use of com.tencent.tinker.loader.TinkerRuntimeException in project tinker by Tencent.
the class DexDiffPatchInternal method patchDexExtractViaDexDiff.
private static boolean patchDexExtractViaDexDiff(Context context, String patchVersionDirectory, String meta, final File patchFile) {
String dir = patchVersionDirectory + "/" + DEX_PATH + "/";
if (!extractDexDiffInternals(context, dir, meta, patchFile, TYPE_DEX)) {
TinkerLog.w(TAG, "patch recover, extractDiffInternals fail");
return false;
}
final Tinker manager = Tinker.with(context);
File dexFiles = new File(dir);
File[] files = dexFiles.listFiles();
optFiles.clear();
if (files != null) {
final String optimizeDexDirectory = patchVersionDirectory + "/" + DEX_OPTIMIZE_PATH + "/";
File optimizeDexDirectoryFile = new File(optimizeDexDirectory);
if (!optimizeDexDirectoryFile.exists() && !optimizeDexDirectoryFile.mkdirs()) {
TinkerLog.w(TAG, "patch recover, make optimizeDexDirectoryFile fail");
return false;
}
// add opt files
for (File file : files) {
String outputPathName = SharePatchFileUtil.optimizedPathFor(file, optimizeDexDirectoryFile);
optFiles.add(new File(outputPathName));
}
TinkerLog.w(TAG, "patch recover, try to optimize dex file count:%d", files.length);
// only use parallel dex optimizer for art
if (ShareTinkerInternals.isVmArt()) {
failOptDexFile.clear();
// try parallel dex optimizer
TinkerParallelDexOptimizer.optimizeAll(files, optimizeDexDirectoryFile, new TinkerParallelDexOptimizer.ResultCallback() {
long startTime;
@Override
public void onStart(File dexFile, File optimizedDir) {
startTime = System.currentTimeMillis();
TinkerLog.i(TAG, "start to parallel optimize dex %s, size: %d", dexFile.getPath(), dexFile.length());
}
@Override
public void onSuccess(File dexFile, File optimizedDir, File optimizedFile) {
// Do nothing.
TinkerLog.i(TAG, "success to parallel optimize dex %s, opt file size: %d, use time %d", dexFile.getPath(), optimizedFile.length(), (System.currentTimeMillis() - startTime));
}
@Override
public void onFailed(File dexFile, File optimizedDir, Throwable thr) {
TinkerLog.i(TAG, "fail to parallel optimize dex %s use time %d", dexFile.getPath(), (System.currentTimeMillis() - startTime));
failOptDexFile.add(dexFile);
}
});
// try again
for (File retryDexFile : failOptDexFile) {
try {
String outputPathName = SharePatchFileUtil.optimizedPathFor(retryDexFile, optimizeDexDirectoryFile);
if (!SharePatchFileUtil.isLegalFile(retryDexFile)) {
manager.getPatchReporter().onPatchDexOptFail(patchFile, retryDexFile, optimizeDexDirectory, retryDexFile.getName(), new TinkerRuntimeException("retry dex optimize file is not exist, name: " + retryDexFile.getName()));
return false;
}
TinkerLog.i(TAG, "try to retry dex optimize file, path: %s, size: %d", retryDexFile.getPath(), retryDexFile.length());
long start = System.currentTimeMillis();
DexFile.loadDex(retryDexFile.getAbsolutePath(), outputPathName, 0);
TinkerLog.i(TAG, "success retry dex optimize file, path: %s, opt file size: %d, use time: %d", retryDexFile.getPath(), new File(outputPathName).length(), (System.currentTimeMillis() - start));
} catch (Throwable e) {
TinkerLog.e(TAG, "retry dex optimize or load failed, path:" + retryDexFile.getPath());
manager.getPatchReporter().onPatchDexOptFail(patchFile, retryDexFile, optimizeDexDirectory, retryDexFile.getName(), e);
return false;
}
}
// for dalvik, machine hardware performance is much worse than art machine
} else {
for (File file : files) {
try {
String outputPathName = SharePatchFileUtil.optimizedPathFor(file, optimizeDexDirectoryFile);
long start = System.currentTimeMillis();
DexFile.loadDex(file.getAbsolutePath(), outputPathName, 0);
TinkerLog.i(TAG, "success single dex optimize file, path: %s, opt file size: %d, use time: %d", file.getPath(), new File(outputPathName).length(), (System.currentTimeMillis() - start));
} catch (Throwable e) {
TinkerLog.e(TAG, "single dex optimize or load failed, path:" + file.getPath());
manager.getPatchReporter().onPatchDexOptFail(patchFile, file, optimizeDexDirectory, file.getName(), e);
return false;
}
}
}
}
return true;
}
use of com.tencent.tinker.loader.TinkerRuntimeException in project tinker by Tencent.
the class BsDiffPatchInternal method extractBsDiffInternals.
private static boolean extractBsDiffInternals(Context context, String dir, String meta, File patchFile, int type) {
// parse
ArrayList<ShareBsDiffPatchInfo> patchList = new ArrayList<>();
ShareBsDiffPatchInfo.parseDiffPatchInfo(meta, patchList);
if (patchList.isEmpty()) {
ShareTinkerLog.w(TAG, "extract patch list is empty! type:%s:", ShareTinkerInternals.getTypeString(type));
return true;
}
File directory = new File(dir);
if (!directory.exists()) {
directory.mkdirs();
}
// I think it is better to extract the raw files from apk
Tinker manager = Tinker.with(context);
ApplicationInfo applicationInfo = context.getApplicationInfo();
if (applicationInfo == null) {
// Looks like running on a test Context, so just return without patching.
ShareTinkerLog.w(TAG, "applicationInfo == null!!!!");
return false;
}
ZipFile apk = null;
ZipFile patch = null;
try {
String apkPath = applicationInfo.sourceDir;
apk = new ZipFile(apkPath);
patch = new ZipFile(patchFile);
for (ShareBsDiffPatchInfo info : patchList) {
long start = System.currentTimeMillis();
final String infoPath = info.path;
String patchRealPath;
if (infoPath.equals("")) {
patchRealPath = info.name;
} else {
patchRealPath = info.path + "/" + info.name;
}
final String fileMd5 = info.md5;
if (!SharePatchFileUtil.checkIfMd5Valid(fileMd5)) {
ShareTinkerLog.w(TAG, "meta file md5 mismatch, type:%s, name: %s, md5: %s", ShareTinkerInternals.getTypeString(type), info.name, info.md5);
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
return false;
}
String middle;
middle = info.path + "/" + info.name;
File extractedFile = new File(dir + middle);
// check file whether already exist
if (extractedFile.exists()) {
if (fileMd5.equals(SharePatchFileUtil.getMD5(extractedFile))) {
// it is ok, just continue
ShareTinkerLog.w(TAG, "bsdiff file %s is already exist, and md5 match, just continue", extractedFile.getPath());
continue;
} else {
ShareTinkerLog.w(TAG, "have a mismatch corrupted dex " + extractedFile.getPath());
extractedFile.delete();
}
} else {
extractedFile.getParentFile().mkdirs();
}
String patchFileMd5 = info.patchMd5;
// it is a new file, just copy
ZipEntry patchFileEntry = patch.getEntry(patchRealPath);
if (patchFileEntry == null) {
ShareTinkerLog.w(TAG, "patch entry is null. path:" + patchRealPath);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);
return false;
}
if (patchFileMd5.equals("0")) {
if (!extract(patch, patchFileEntry, extractedFile, fileMd5, false)) {
ShareTinkerLog.w(TAG, "Failed to extract file " + extractedFile.getPath());
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);
return false;
}
} else {
// we do not check the intermediate files' md5 to save time, use check whether it is 32 length
if (!SharePatchFileUtil.checkIfMd5Valid(patchFileMd5)) {
ShareTinkerLog.w(TAG, "meta file md5 mismatch, type:%s, name: %s, md5: %s", ShareTinkerInternals.getTypeString(type), info.name, patchFileMd5);
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
return false;
}
ZipEntry rawApkFileEntry = apk.getEntry(patchRealPath);
if (rawApkFileEntry == null) {
ShareTinkerLog.w(TAG, "apk entry is null. path:" + patchRealPath);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);
return false;
}
String rawApkCrc = info.rawCrc;
// check source crc instead of md5 for faster
String rawEntryCrc = String.valueOf(rawApkFileEntry.getCrc());
if (!rawEntryCrc.equals(rawApkCrc)) {
ShareTinkerLog.e(TAG, "apk entry %s crc is not equal, expect crc: %s, got crc: %s", patchRealPath, rawApkCrc, rawEntryCrc);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);
return false;
}
InputStream oldStream = null;
InputStream newStream = null;
try {
oldStream = apk.getInputStream(rawApkFileEntry);
newStream = patch.getInputStream(patchFileEntry);
BSPatch.patchFast(oldStream, newStream, extractedFile);
} finally {
IOHelper.closeQuietly(oldStream);
IOHelper.closeQuietly(newStream);
}
// go go go bsdiff get the
if (!SharePatchFileUtil.verifyFileMd5(extractedFile, fileMd5)) {
ShareTinkerLog.w(TAG, "Failed to recover diff file " + extractedFile.getPath());
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);
SharePatchFileUtil.safeDeleteFile(extractedFile);
return false;
}
ShareTinkerLog.w(TAG, "success recover bsdiff file: %s, use time: %d", extractedFile.getPath(), (System.currentTimeMillis() - start));
}
}
} catch (Throwable e) {
throw new TinkerRuntimeException("patch " + ShareTinkerInternals.getTypeString(type) + " extract failed (" + e.getMessage() + ").", e);
} finally {
SharePatchFileUtil.closeZip(apk);
SharePatchFileUtil.closeZip(patch);
}
return true;
}
use of com.tencent.tinker.loader.TinkerRuntimeException in project tinker by Tencent.
the class DexDiffPatchInternal method patchDexFile.
/**
* Generate patched dex file (May wrapped it by a jar if needed.)
*
* @param baseApk OldApk.
* @param patchPkg Patch package, it is also a zip file.
* @param oldDexEntry ZipEntry of old dex.
* @param patchFileEntry ZipEntry of patch file. (also ends with .dex) This could be null.
* @param patchInfo Parsed patch info from package-meta.txt
* @param patchedDexFile Patched dex file, may be a jar.
* <p>
* <b>Notice: patchFileEntry and smallPatchInfoFile cannot both be null.</b>
* @throws IOException
*/
private static void patchDexFile(ZipFile baseApk, ZipFile patchPkg, ZipEntry oldDexEntry, ZipEntry patchFileEntry, ShareDexDiffPatchInfo patchInfo, File patchedDexFile) throws IOException {
InputStream oldDexStream = null;
InputStream patchFileStream = null;
try {
oldDexStream = new BufferedInputStream(baseApk.getInputStream(oldDexEntry));
patchFileStream = (patchFileEntry != null ? new BufferedInputStream(patchPkg.getInputStream(patchFileEntry)) : null);
final boolean isRawDexFile = SharePatchFileUtil.isRawDexFile(patchInfo.rawName);
if (!isRawDexFile || patchInfo.isJarMode) {
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(patchedDexFile)));
zos.putNextEntry(new ZipEntry(ShareConstants.DEX_IN_JAR));
// Old dex is not a raw dex file.
if (!isRawDexFile) {
ZipInputStream zis = null;
try {
zis = new ZipInputStream(oldDexStream);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (ShareConstants.DEX_IN_JAR.equals(entry.getName()))
break;
}
if (entry == null) {
throw new TinkerRuntimeException("can't recognize zip dex format file:" + patchedDexFile.getAbsolutePath());
}
new DexPatchApplier(zis, patchFileStream).executeAndSaveTo(zos);
} finally {
IOHelper.closeQuietly(zis);
}
} else {
new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(zos);
}
zos.closeEntry();
} finally {
IOHelper.closeQuietly(zos);
}
} else {
new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(patchedDexFile);
}
} finally {
IOHelper.closeQuietly(oldDexStream);
IOHelper.closeQuietly(patchFileStream);
}
}
use of com.tencent.tinker.loader.TinkerRuntimeException in project tinker by Tencent.
the class DexDiffPatchInternal method waitAndCheckDexOptFile.
protected static boolean waitAndCheckDexOptFile(File patchFile, Tinker manager) {
if (optFiles.isEmpty()) {
return true;
}
// should use patch list size
int size = patchList.size() * 30;
if (size > MAX_WAIT_COUNT) {
size = MAX_WAIT_COUNT;
}
ShareTinkerLog.i(TAG, "raw dex count: %d, dex opt dex count: %d, final wait times: %d", patchList.size(), optFiles.size(), size);
for (int i = 0; i < size; i++) {
if (!checkAllDexOptFile(optFiles, i + 1)) {
try {
Thread.sleep(WAIT_ASYN_OAT_TIME);
} catch (InterruptedException e) {
ShareTinkerLog.e(TAG, "thread sleep InterruptedException e:" + e);
}
}
}
List<File> failDexFiles = new ArrayList<>();
// check again, if still can't be found, just return
for (File file : optFiles) {
ShareTinkerLog.i(TAG, "check dex optimizer file exist: %s, size %d", file.getPath(), file.length());
if (!SharePatchFileUtil.isLegalFile(file) && !SharePatchFileUtil.shouldAcceptEvenIfIllegal(file)) {
ShareTinkerLog.e(TAG, "final parallel dex optimizer file %s is not exist, return false", file.getName());
failDexFiles.add(file);
}
}
if (!failDexFiles.isEmpty()) {
manager.getPatchReporter().onPatchDexOptFail(patchFile, failDexFiles, new TinkerRuntimeException(ShareConstants.CHECK_DEX_OAT_EXIST_FAIL));
return false;
}
if (Build.VERSION.SDK_INT >= 21) {
Throwable lastThrowable = null;
for (File file : optFiles) {
if (SharePatchFileUtil.shouldAcceptEvenIfIllegal(file)) {
continue;
}
ShareTinkerLog.i(TAG, "check dex optimizer file format: %s, size %d", file.getName(), file.length());
int returnType;
try {
returnType = ShareElfFile.getFileTypeByMagic(file);
} catch (IOException e) {
// read error just continue
continue;
}
if (returnType == ShareElfFile.FILE_TYPE_ELF) {
ShareElfFile elfFile = null;
try {
elfFile = new ShareElfFile(file);
} catch (Throwable e) {
ShareTinkerLog.e(TAG, "final parallel dex optimizer file %s is not elf format, return false", file.getName());
failDexFiles.add(file);
lastThrowable = e;
} finally {
IOHelper.closeQuietly(elfFile);
}
}
}
if (!failDexFiles.isEmpty()) {
Throwable returnThrowable = lastThrowable == null ? new TinkerRuntimeException(ShareConstants.CHECK_DEX_OAT_FORMAT_FAIL) : new TinkerRuntimeException(ShareConstants.CHECK_DEX_OAT_FORMAT_FAIL, lastThrowable);
manager.getPatchReporter().onPatchDexOptFail(patchFile, failDexFiles, returnThrowable);
return false;
}
}
return true;
}
use of com.tencent.tinker.loader.TinkerRuntimeException in project tinker by Tencent.
the class ResDiffPatchInternal method checkAndExtractResourceLargeFile.
private static boolean checkAndExtractResourceLargeFile(Context context, String apkPath, File directory, File tempFileDirtory, File patchFile, ShareResPatchInfo resPatchInfo, int type) {
long start = System.currentTimeMillis();
Tinker manager = Tinker.with(context);
ZipFile apkFile = null;
ZipFile patchZipFile = null;
try {
// recover resources.arsc first
apkFile = new ZipFile(apkPath);
ZipEntry arscEntry = apkFile.getEntry(ShareConstants.RES_ARSC);
File arscFile = new File(directory, ShareConstants.RES_ARSC);
if (arscEntry == null) {
ShareTinkerLog.w(TAG, "resources apk entry is null. path:" + ShareConstants.RES_ARSC);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, arscFile, ShareConstants.RES_ARSC, type);
return false;
}
// use base resources.arsc crc to identify base.apk
String baseArscCrc = String.valueOf(arscEntry.getCrc());
if (!baseArscCrc.equals(resPatchInfo.arscBaseCrc)) {
ShareTinkerLog.e(TAG, "resources.arsc's crc is not equal, expect crc: %s, got crc: %s", resPatchInfo.arscBaseCrc, baseArscCrc);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, arscFile, ShareConstants.RES_ARSC, type);
return false;
}
// resource arsc is not changed, just return true
if (resPatchInfo.largeModRes.isEmpty() && resPatchInfo.storeRes.isEmpty()) {
ShareTinkerLog.i(TAG, "no large modify or store resources, just return");
return true;
}
patchZipFile = new ZipFile(patchFile);
for (String name : resPatchInfo.storeRes.keySet()) {
long storeStart = System.currentTimeMillis();
File destCopy = new File(tempFileDirtory, name);
SharePatchFileUtil.ensureFileDirectory(destCopy);
ZipEntry patchEntry = patchZipFile.getEntry(name);
if (patchEntry == null) {
ShareTinkerLog.w(TAG, "store patch entry is null. path:" + name);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, destCopy, name, type);
return false;
}
extract(patchZipFile, patchEntry, destCopy, null, false);
// fast check, only check size
if (patchEntry.getSize() != destCopy.length()) {
ShareTinkerLog.w(TAG, "resource meta file size mismatch, type:%s, name: %s, patch size: %d, file size; %d", ShareTinkerInternals.getTypeString(type), name, patchEntry.getSize(), destCopy.length());
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
return false;
}
resPatchInfo.storeRes.put(name, destCopy);
ShareTinkerLog.w(TAG, "success recover store file:%s, file size:%d, use time:%d", destCopy.getPath(), destCopy.length(), (System.currentTimeMillis() - storeStart));
}
for (String name : resPatchInfo.largeModRes) {
long largeStart = System.currentTimeMillis();
ShareResPatchInfo.LargeModeInfo largeModeInfo = resPatchInfo.largeModMap.get(name);
if (largeModeInfo == null) {
ShareTinkerLog.w(TAG, "resource not found largeModeInfo, type:%s, name: %s", ShareTinkerInternals.getTypeString(type), name);
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
return false;
}
largeModeInfo.file = new File(tempFileDirtory, name);
SharePatchFileUtil.ensureFileDirectory(largeModeInfo.file);
// we do not check the intermediate files' md5 to save time, use check whether it is 32 length
if (!SharePatchFileUtil.checkIfMd5Valid(largeModeInfo.md5)) {
ShareTinkerLog.w(TAG, "resource meta file md5 mismatch, type:%s, name: %s, md5: %s", ShareTinkerInternals.getTypeString(type), name, largeModeInfo.md5);
manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
return false;
}
ZipEntry patchEntry = patchZipFile.getEntry(name);
if (patchEntry == null) {
ShareTinkerLog.w(TAG, "large mod patch entry is null. path:" + name);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, largeModeInfo.file, name, type);
return false;
}
ZipEntry baseEntry = apkFile.getEntry(name);
if (baseEntry == null) {
ShareTinkerLog.w(TAG, "resources apk entry is null. path:" + name);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, largeModeInfo.file, name, type);
return false;
}
InputStream oldStream = null;
InputStream newStream = null;
try {
oldStream = apkFile.getInputStream(baseEntry);
newStream = patchZipFile.getInputStream(patchEntry);
BSPatch.patchFast(oldStream, newStream, largeModeInfo.file);
} finally {
IOHelper.closeQuietly(oldStream);
IOHelper.closeQuietly(newStream);
}
// go go go bsdiff get the
if (!SharePatchFileUtil.verifyFileMd5(largeModeInfo.file, largeModeInfo.md5)) {
ShareTinkerLog.w(TAG, "Failed to recover large modify file:%s", largeModeInfo.file.getPath());
SharePatchFileUtil.safeDeleteFile(largeModeInfo.file);
manager.getPatchReporter().onPatchTypeExtractFail(patchFile, largeModeInfo.file, name, type);
return false;
}
ShareTinkerLog.w(TAG, "success recover large modify file:%s, file size:%d, use time:%d", largeModeInfo.file.getPath(), largeModeInfo.file.length(), (System.currentTimeMillis() - largeStart));
}
ShareTinkerLog.w(TAG, "success recover all large modify and store resources use time:%d", (System.currentTimeMillis() - start));
} catch (Throwable e) {
throw new TinkerRuntimeException("patch " + ShareTinkerInternals.getTypeString(type) + " extract failed (" + e.getMessage() + ").", e);
} finally {
SharePatchFileUtil.closeZip(apkFile);
SharePatchFileUtil.closeZip(patchZipFile);
}
return true;
}
Aggregations