Search in sources :

Example 1 with ShareResPatchInfo

use of com.tencent.tinker.loader.shareutil.ShareResPatchInfo in project tinker by Tencent.

the class ResDiffPatchInternal method checkAndExtractResourceLargeFile.

private static boolean checkAndExtractResourceLargeFile(Context context, String apkPath, File directory, 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) {
            TinkerLog.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)) {
            TinkerLog.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()) {
            TinkerLog.i(TAG, "no large modify resources, just return");
            return true;
        }
        for (String name : resPatchInfo.largeModRes) {
            long largeStart = System.currentTimeMillis();
            ShareResPatchInfo.LargeModeInfo largeModeInfo = resPatchInfo.largeModMap.get(name);
            if (largeModeInfo == null) {
                TinkerLog.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(directory, 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)) {
                TinkerLog.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;
            }
            patchZipFile = new ZipFile(patchFile);
            ZipEntry patchEntry = patchZipFile.getEntry(name);
            if (patchEntry == null) {
                TinkerLog.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) {
                TinkerLog.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 {
                SharePatchFileUtil.closeQuietly(oldStream);
                SharePatchFileUtil.closeQuietly(newStream);
            }
            //go go go bsdiff get the
            if (!SharePatchFileUtil.verifyFileMd5(largeModeInfo.file, largeModeInfo.md5)) {
                TinkerLog.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;
            }
            TinkerLog.w(TAG, "success recover large modify file:%s, file size:%d, use time:%d", largeModeInfo.file.getPath(), largeModeInfo.file.length(), (System.currentTimeMillis() - largeStart));
        }
        TinkerLog.w(TAG, "success recover all large modify use time:%d", (System.currentTimeMillis() - start));
    } catch (Throwable e) {
        //            e.printStackTrace();
        throw new TinkerRuntimeException("patch " + ShareTinkerInternals.getTypeString(type) + " extract failed (" + e.getMessage() + ").", e);
    } finally {
        SharePatchFileUtil.closeZip(apkFile);
        SharePatchFileUtil.closeZip(patchZipFile);
    }
    return true;
}
Also used : TinkerRuntimeException(com.tencent.tinker.loader.TinkerRuntimeException) TinkerZipFile(com.tencent.tinker.commons.ziputil.TinkerZipFile) ZipFile(java.util.zip.ZipFile) ShareResPatchInfo(com.tencent.tinker.loader.shareutil.ShareResPatchInfo) InputStream(java.io.InputStream) TinkerZipEntry(com.tencent.tinker.commons.ziputil.TinkerZipEntry) ZipEntry(java.util.zip.ZipEntry) Tinker(com.tencent.tinker.lib.tinker.Tinker) TinkerZipFile(com.tencent.tinker.commons.ziputil.TinkerZipFile) File(java.io.File) ZipFile(java.util.zip.ZipFile)

Example 2 with ShareResPatchInfo

use of com.tencent.tinker.loader.shareutil.ShareResPatchInfo in project tinker by Tencent.

the class ResDiffPatchInternal method extractResourceDiffInternals.

private static boolean extractResourceDiffInternals(Context context, String dir, String meta, File patchFile, int type) {
    ShareResPatchInfo resPatchInfo = new ShareResPatchInfo();
    ShareResPatchInfo.parseAllResPatchInfo(meta, resPatchInfo);
    TinkerLog.i(TAG, "res dir: %s, meta: %s", dir, resPatchInfo.toString());
    Tinker manager = Tinker.with(context);
    if (!SharePatchFileUtil.checkIfMd5Valid(resPatchInfo.resArscMd5)) {
        TinkerLog.w(TAG, "resource meta file md5 mismatch, type:%s, md5: %s", ShareTinkerInternals.getTypeString(type), resPatchInfo.resArscMd5);
        manager.getPatchReporter().onPatchPackageCheckFail(patchFile, BasePatchInternal.getMetaCorruptedCode(type));
        return false;
    }
    File directory = new File(dir);
    File resOutput = new File(directory, ShareConstants.RES_NAME);
    //check result file whether already exist
    if (resOutput.exists()) {
        if (SharePatchFileUtil.checkResourceArscMd5(resOutput, resPatchInfo.resArscMd5)) {
            //it is ok, just continue
            TinkerLog.w(TAG, "resource file %s is already exist, and md5 match, just return true", resOutput.getPath());
            return true;
        } else {
            TinkerLog.w(TAG, "have a mismatch corrupted resource " + resOutput.getPath());
            resOutput.delete();
        }
    } else {
        resOutput.getParentFile().mkdirs();
    }
    try {
        ApplicationInfo applicationInfo = context.getApplicationInfo();
        if (applicationInfo == null) {
            //Looks like running on a test Context, so just return without patching.
            TinkerLog.w(TAG, "applicationInfo == null!!!!");
            return false;
        }
        String apkPath = applicationInfo.sourceDir;
        if (!checkAndExtractResourceLargeFile(context, apkPath, directory, patchFile, resPatchInfo, type)) {
            return false;
        }
        TinkerZipOutputStream out = null;
        TinkerZipFile oldApk = null;
        TinkerZipFile newApk = null;
        int totalEntryCount = 0;
        try {
            out = new TinkerZipOutputStream(new BufferedOutputStream(new FileOutputStream(resOutput)));
            oldApk = new TinkerZipFile(apkPath);
            newApk = new TinkerZipFile(patchFile);
            final Enumeration<? extends TinkerZipEntry> entries = oldApk.entries();
            while (entries.hasMoreElements()) {
                TinkerZipEntry zipEntry = entries.nextElement();
                if (zipEntry == null) {
                    throw new TinkerRuntimeException("zipEntry is null when get from oldApk");
                }
                String name = zipEntry.getName();
                if (name.contains("../")) {
                    continue;
                }
                if (ShareResPatchInfo.checkFileInPattern(resPatchInfo.patterns, name)) {
                    //won't contain in add set.
                    if (!resPatchInfo.deleteRes.contains(name) && !resPatchInfo.modRes.contains(name) && !resPatchInfo.largeModRes.contains(name) && !name.equals(ShareConstants.RES_MANIFEST)) {
                        ResUtil.extractTinkerEntry(oldApk, zipEntry, out);
                        totalEntryCount++;
                    }
                }
            }
            //process manifest
            TinkerZipEntry manifestZipEntry = oldApk.getEntry(ShareConstants.RES_MANIFEST);
            if (manifestZipEntry == null) {
                TinkerLog.w(TAG, "manifest patch entry is null. path:" + ShareConstants.RES_MANIFEST);
                manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, ShareConstants.RES_MANIFEST, type);
                return false;
            }
            ResUtil.extractTinkerEntry(oldApk, manifestZipEntry, out);
            totalEntryCount++;
            for (String name : resPatchInfo.largeModRes) {
                TinkerZipEntry largeZipEntry = oldApk.getEntry(name);
                if (largeZipEntry == null) {
                    TinkerLog.w(TAG, "large patch entry is null. path:" + name);
                    manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, name, type);
                    return false;
                }
                ShareResPatchInfo.LargeModeInfo largeModeInfo = resPatchInfo.largeModMap.get(name);
                ResUtil.extractLargeModifyFile(largeZipEntry, largeModeInfo.file, largeModeInfo.crc, out);
                totalEntryCount++;
            }
            for (String name : resPatchInfo.addRes) {
                TinkerZipEntry addZipEntry = newApk.getEntry(name);
                if (addZipEntry == null) {
                    TinkerLog.w(TAG, "add patch entry is null. path:" + name);
                    manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, name, type);
                    return false;
                }
                ResUtil.extractTinkerEntry(newApk, addZipEntry, out);
                totalEntryCount++;
            }
            for (String name : resPatchInfo.modRes) {
                TinkerZipEntry modZipEntry = newApk.getEntry(name);
                if (modZipEntry == null) {
                    TinkerLog.w(TAG, "mod patch entry is null. path:" + name);
                    manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, name, type);
                    return false;
                }
                ResUtil.extractTinkerEntry(newApk, modZipEntry, out);
                totalEntryCount++;
            }
        } finally {
            if (out != null) {
                out.close();
            }
            if (oldApk != null) {
                oldApk.close();
            }
            if (newApk != null) {
                newApk.close();
            }
            //delete temp files
            for (ShareResPatchInfo.LargeModeInfo largeModeInfo : resPatchInfo.largeModMap.values()) {
                SharePatchFileUtil.safeDeleteFile(largeModeInfo.file);
            }
        }
        boolean result = SharePatchFileUtil.checkResourceArscMd5(resOutput, resPatchInfo.resArscMd5);
        if (!result) {
            TinkerLog.i(TAG, "check final new resource file fail path:%s, entry count:%d, size:%d", resOutput.getAbsolutePath(), totalEntryCount, resOutput.length());
            SharePatchFileUtil.safeDeleteFile(resOutput);
            manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, ShareConstants.RES_NAME, type);
            return false;
        }
        TinkerLog.i(TAG, "final new resource file:%s, entry count:%d, size:%d", resOutput.getAbsolutePath(), totalEntryCount, resOutput.length());
    } catch (Throwable e) {
        //            e.printStackTrace();
        throw new TinkerRuntimeException("patch " + ShareTinkerInternals.getTypeString(type) + " extract failed (" + e.getMessage() + ").", e);
    }
    return true;
}
Also used : TinkerRuntimeException(com.tencent.tinker.loader.TinkerRuntimeException) ApplicationInfo(android.content.pm.ApplicationInfo) TinkerZipEntry(com.tencent.tinker.commons.ziputil.TinkerZipEntry) Tinker(com.tencent.tinker.lib.tinker.Tinker) TinkerZipOutputStream(com.tencent.tinker.commons.ziputil.TinkerZipOutputStream) ShareResPatchInfo(com.tencent.tinker.loader.shareutil.ShareResPatchInfo) FileOutputStream(java.io.FileOutputStream) TinkerZipFile(com.tencent.tinker.commons.ziputil.TinkerZipFile) File(java.io.File) ZipFile(java.util.zip.ZipFile) BufferedOutputStream(java.io.BufferedOutputStream) TinkerZipFile(com.tencent.tinker.commons.ziputil.TinkerZipFile)

Aggregations

TinkerZipEntry (com.tencent.tinker.commons.ziputil.TinkerZipEntry)2 TinkerZipFile (com.tencent.tinker.commons.ziputil.TinkerZipFile)2 Tinker (com.tencent.tinker.lib.tinker.Tinker)2 TinkerRuntimeException (com.tencent.tinker.loader.TinkerRuntimeException)2 ShareResPatchInfo (com.tencent.tinker.loader.shareutil.ShareResPatchInfo)2 File (java.io.File)2 ZipFile (java.util.zip.ZipFile)2 ApplicationInfo (android.content.pm.ApplicationInfo)1 TinkerZipOutputStream (com.tencent.tinker.commons.ziputil.TinkerZipOutputStream)1 BufferedOutputStream (java.io.BufferedOutputStream)1 FileOutputStream (java.io.FileOutputStream)1 InputStream (java.io.InputStream)1 ZipEntry (java.util.zip.ZipEntry)1