From 70b4c60d0cb3c48a72a01434ae4ded36b15099f1 Mon Sep 17 00:00:00 2001 From: tangyinsheng Date: Thu, 20 Oct 2022 15:24:25 +0800 Subject: [PATCH 1/5] [tinker] Add lost modifications: remove old patch async on patch loading. --- .../main/java/com/tencent/tinker/loader/TinkerLoader.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerLoader.java b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerLoader.java index be750466..099dc767 100644 --- a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerLoader.java +++ b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerLoader.java @@ -92,7 +92,6 @@ private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultInten //tinker/patch.info File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectoryPath); - //check patch info file whether exist if (!patchInfoFile.exists()) { ShareTinkerLog.w(TAG, "tryLoadPatchFiles:patch info not exist:" + patchInfoFile.getAbsolutePath()); @@ -102,7 +101,6 @@ private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultInten //old = 641e634c5b8f1649c75caf73794acbdf //new = 2c150d8560334966952678930ba67fa8 File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectoryPath); - patchInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile); if (patchInfo == null) { ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED); @@ -173,7 +171,7 @@ private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultInten String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName; ShareTinkerInternals.killProcessExceptMain(app); - SharePatchFileUtil.deleteDir(patchVersionDirFullPath); + SharePatchFileUtil.deleteDirAsync(patchVersionDirFullPath); } } else { patchInfo.versionToRemove = ""; @@ -244,7 +242,6 @@ private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultInten } ShareSecurityCheck securityCheck = new ShareSecurityCheck(app); - int returnCode = ShareTinkerInternals.checkTinkerPackage(app, tinkerFlag, patchVersionFile, securityCheck); if (returnCode != ShareConstants.ERROR_PACKAGE_CHECK_OK) { ShareTinkerLog.w(TAG, "tryLoadPatchFiles:checkTinkerPackage"); From 1a7200ed89a2f5887fac25104f2635aa56a30959 Mon Sep 17 00:00:00 2001 From: nalecyxu Date: Mon, 31 Oct 2022 23:11:38 +0800 Subject: [PATCH 2/5] [customdiff] fix npe --- .../build/gradle/extension/TinkerPatchExtension.groovy | 7 +++++++ .../build/gradle/task/TinkerPatchSchemaTask.groovy | 1 + .../com/tencent/tinker/build/patch/Configuration.java | 1 + .../com/tencent/tinker/build/patch/InputParam.java | 10 ++++++++++ .../java/com/tencent/tinker/build/util/CustomDiff.java | 2 +- 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/extension/TinkerPatchExtension.groovy b/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/extension/TinkerPatchExtension.groovy index 6a39ad95..f97e7df8 100644 --- a/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/extension/TinkerPatchExtension.groovy +++ b/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/extension/TinkerPatchExtension.groovy @@ -102,6 +102,12 @@ public class TinkerPatchExtension { */ String customPath + /** + * customDiffPathArgs + * default: null + */ + String customDiffPathArgs + public TinkerPatchExtension() { oldApk = "" outputFolder = "" @@ -112,6 +118,7 @@ public class TinkerPatchExtension { useSign = true tinkerEnable = true customPath = null + customDiffPathArgs = null } void checkParameter() { diff --git a/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/task/TinkerPatchSchemaTask.groovy b/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/task/TinkerPatchSchemaTask.groovy index 7d81395d..8a29a039 100644 --- a/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/task/TinkerPatchSchemaTask.groovy +++ b/tinker-build/tinker-patch-gradle-plugin/src/main/groovy/com/tencent/tinker/build/gradle/task/TinkerPatchSchemaTask.groovy @@ -138,6 +138,7 @@ public class TinkerPatchSchemaTask extends DefaultTask { .setIgnoreWarning(configuration.ignoreWarning) .setAllowLoaderInAnyDex(configuration.allowLoaderInAnyDex) .setCustomDiffPath(configuration.customPath) + .setCustomDiffPathArgs(configuration.customDiffPathArgs) .setRemoveLoaderForAllDex(configuration.removeLoaderForAllDex) .setDexFilePattern(new ArrayList(configuration.dex.pattern)) .setIsProtectedApp(configuration.buildConfig.isProtectedApp) diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java index 87e63247..9db035c2 100644 --- a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java @@ -268,6 +268,7 @@ public Configuration(InputParam param) throws IOException, TinkerPatchException mUseSignAPk = param.useSign; mCustomDiffPath = param.customDiffPath; + mCustomDiffPathArgs = param.customDiffPathArgs; setSignData(param.signFile, param.keypass, param.storealias, param.storepass); FileOperation.cleanDir(new File(mOutFolder)); diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/InputParam.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/InputParam.java index 98d40cb4..61074cf0 100644 --- a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/InputParam.java +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/InputParam.java @@ -35,6 +35,7 @@ public class InputParam { public final String storealias; public final String storepass; public final String customDiffPath; + public final String customDiffPathArgs; public final boolean ignoreWarning; public final boolean allowLoaderInAnyDex; public final boolean removeLoaderForAllDex; @@ -98,6 +99,7 @@ private InputParam( String storealias, String storepass, String customDiffPath, + String customDiffPathArgs, boolean ignoreWarning, boolean allowLoaderInAnyDex, boolean removeLoaderForAllDex, @@ -130,6 +132,7 @@ private InputParam( this.storealias = storealias; this.storepass = storepass; this.customDiffPath = customDiffPath; + this.customDiffPathArgs = customDiffPathArgs; this.ignoreWarning = ignoreWarning; this.allowLoaderInAnyDex = allowLoaderInAnyDex; this.removeLoaderForAllDex = removeLoaderForAllDex; @@ -168,6 +171,7 @@ public static class Builder { private String storealias; private String storepass; private String customDiffPath; + private String customDiffPathArgs; private boolean ignoreWarning; private boolean allowLoaderInAnyDex; private boolean removeLoaderForAllDex; @@ -311,6 +315,11 @@ public Builder setCustomDiffPath(String path) { return this; } + public Builder setCustomDiffPathArgs(String args) { + this.customDiffPathArgs = args; + return this; + } + public Builder setRemoveLoaderForAllDex(boolean removeLoaderForAllDex){ this.removeLoaderForAllDex = removeLoaderForAllDex; return this; @@ -376,6 +385,7 @@ public InputParam create() { storealias, storepass, customDiffPath, + customDiffPathArgs, ignoreWarning, allowLoaderInAnyDex, removeLoaderForAllDex, diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/util/CustomDiff.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/util/CustomDiff.java index 319bc5d9..b5cbbf84 100644 --- a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/util/CustomDiff.java +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/util/CustomDiff.java @@ -15,7 +15,7 @@ public class CustomDiff { public static boolean checkHasCustomDiff(Configuration config) { - return config.mCustomDiffPath != null && !config.mCustomDiffPath.trim().isEmpty(); + return config.mCustomDiffPath != null && !config.mCustomDiffPath.trim().isEmpty() && config.mCustomDiffPathArgs != null && !config.mCustomDiffPathArgs.isEmpty(); } public static void diffFile(String mCustomDiffPath,String mCustomDiffPathArgs, File oldFile, File newFile, File diffFile) throws IOException { From fc46af09022b6a8d3c420009be785d854bd340fd Mon Sep 17 00:00:00 2001 From: tomystang Date: Sun, 11 Jun 2023 11:29:25 +0800 Subject: [PATCH 3/5] [tinker] Try to fix resource not found on api 27 and newer devices. --- .../tinker/loader/NewClassLoaderInjector.java | 23 ++++---- .../tinker/loader/TinkerResourcePatcher.java | 56 ++++++++++++++++--- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/NewClassLoaderInjector.java b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/NewClassLoaderInjector.java index b5b975f0..db9cdb19 100644 --- a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/NewClassLoaderInjector.java +++ b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/NewClassLoaderInjector.java @@ -139,18 +139,19 @@ private static void doInject(Application app, ClassLoader classLoader) throws Th final Object basePackageInfo = findField(baseContext.getClass(), "mPackageInfo").get(baseContext); findField(basePackageInfo.getClass(), "mClassLoader").set(basePackageInfo, classLoader); - if (Build.VERSION.SDK_INT < 27) { - final Resources res = app.getResources(); - try { - findField(res.getClass(), "mClassLoader").set(res, classLoader); - - final Object drawableInflater = findField(res.getClass(), "mDrawableInflater").get(res); - if (drawableInflater != null) { - findField(drawableInflater.getClass(), "mClassLoader").set(drawableInflater, classLoader); - } - } catch (Throwable ignored) { - // Ignored. + final Resources res = app.getResources(); + try { + findField(res.getClass(), "mClassLoader").set(res, classLoader); + } catch (Throwable ignored) { + // Ignored. + } + try { + final Object drawableInflater = findField(res.getClass(), "mDrawableInflater").get(res); + if (drawableInflater != null) { + findField(drawableInflater.getClass(), "mClassLoader").set(drawableInflater, classLoader); } + } catch (Throwable ignored) { + // Ignored. } } diff --git a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerResourcePatcher.java b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerResourcePatcher.java index 0cc310f0..32f69554 100644 --- a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerResourcePatcher.java +++ b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerResourcePatcher.java @@ -58,6 +58,8 @@ class TinkerResourcePatcher { // original object private static Collection> references = null; + + private static Map> resourceImpls = null; private static Object currentActivityThread = null; private static AssetManager newAssetManager = null; @@ -71,6 +73,7 @@ class TinkerResourcePatcher { private static Field assetsFiled = null; private static Field resourcesImplFiled = null; private static Field resDir = null; + private static Field resources = null; private static Field packagesFiled = null; private static Field resourcePackagesFiled = null; private static Field publicSourceDirField = null; @@ -78,6 +81,10 @@ class TinkerResourcePatcher { private static long storedPatchedResModifiedTime = 0L; + private static Context packageContext = null; + + private static Context packageResContext = null; + @SuppressWarnings("unchecked") public static void isResourceCanPatch(Context context) throws Throwable { // - Replace mResDir to point to the external resource file instead of the .apk. This is @@ -97,9 +104,18 @@ public static void isResourceCanPatch(Context context) throws Throwable { } resDir = findField(loadedApkClass, "mResDir"); + try { + resources = findField(loadedApkClass, "mResources"); + } catch (Throwable thr) { + ShareTinkerLog.printErrStackTrace(TAG, thr, "Fail to get LoadedApk.mResources field."); + resources = null; + } packagesFiled = findField(activityThread, "mPackages"); - if (Build.VERSION.SDK_INT < 27) { + try { resourcePackagesFiled = findField(activityThread, "mResourcePackages"); + } catch (Throwable thr) { + ShareTinkerLog.printErrStackTrace(TAG, thr, "Fail to get mResourcePackages field."); + resourcePackagesFiled = null; } // Create a new AssetManager instance and point it to the resources @@ -139,6 +155,13 @@ public static void isResourceCanPatch(Context context) throws Throwable { // N moved the resources to mResourceReferences final Field mResourceReferences = findField(resourcesManagerClass, "mResourceReferences"); references = (Collection>) mResourceReferences.get(resourcesManager); + + try { + final Field mResourceImplsField = findField(resourcesManagerClass, "mResourceImpls"); + resourceImpls = (Map>) mResourceImplsField.get(resourcesManager); + } catch (Throwable ignored) { + resourceImpls = null; + } } } else { final Field fMActiveResources = findField(activityThread, "mActiveResources"); @@ -186,13 +209,15 @@ public static void monkeyPatchExistingResources(Context context, String external final ApplicationInfo appInfo = context.getApplicationInfo(); - final Field[] packagesFields; - if (Build.VERSION.SDK_INT < 27) { - packagesFields = new Field[]{packagesFiled, resourcePackagesFiled}; - } else { - packagesFields = new Field[]{packagesFiled}; - } + // Prevent cached LoadedApk being recycled. + packageContext = context.createPackageContext(context.getPackageName(), Context.CONTEXT_INCLUDE_CODE); + packageResContext = context.createPackageContext(context.getPackageName(), 0); + + final Field[] packagesFields = new Field[]{packagesFiled, resourcePackagesFiled}; for (Field field : packagesFields) { + if (field == null) { + continue; + } final Object value = field.get(currentActivityThread); for (Map.Entry> entry @@ -204,6 +229,9 @@ public static void monkeyPatchExistingResources(Context context, String external final String resDirPath = (String) resDir.get(loadedApk); if (appInfo.sourceDir.equals(resDirPath)) { resDir.set(loadedApk, externalResourceFile); + if (resources != null) { + resources.set(loadedApk, null); + } } } } @@ -264,6 +292,20 @@ public static void monkeyPatchExistingResources(Context context, String external resources.updateConfiguration(resources.getConfiguration(), resources.getDisplayMetrics()); } + try { + if (resourceImpls != null) { + for (WeakReference wr : resourceImpls.values()) { + final Object resourceImpl = wr.get(); + if (resourceImpl != null) { + final Field implAssets = findField(resourceImpl, "mAssets"); + implAssets.set(resourceImpl, newAssetManager); + } + } + } + } catch (Throwable ignored) { + // Ignored. + } + // Handle issues caused by WebView on Android N. // Issue: On Android N, if an activity contains a webview, when screen rotates // our resource patch may lost effects. From 9a782b27dca435cc966844fedbe0f194bf490f8b Mon Sep 17 00:00:00 2001 From: tomystang Date: Mon, 12 Jun 2023 15:00:28 +0800 Subject: [PATCH 4/5] [tinker] Add emergency mode to trigger dex2oat asynchronously for fast patch applying. --- .../lib/listener/DefaultPatchListener.java | 4 ++ .../tinker/lib/patch/AbstractPatch.java | 2 +- .../tinker/lib/patch/UpgradePatch.java | 2 +- .../lib/listener/DefaultPatchListener.java | 20 +++++++-- .../tinker/lib/patch/AbstractPatch.java | 2 +- .../lib/patch/DexDiffPatchInternal.java | 18 +++++--- .../tinker/lib/patch/UpgradePatch.java | 4 +- .../tinker/lib/service/PatchResult.java | 3 ++ .../lib/service/TinkerPatchService.java | 18 +++++++- .../tinker/loader/TinkerDexLoader.java | 2 +- .../tinker/loader/TinkerDexOptimizer.java | 42 ++++++++++++------- 11 files changed, 87 insertions(+), 30 deletions(-) diff --git a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java index 42eabbb0..52752ce0 100644 --- a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java +++ b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java @@ -35,6 +35,10 @@ public DefaultPatchListener(Context context) { @Override public int onPatchReceived(String path) { + return checkPackageAndRunPatchService(path, false); + } + + protected int checkPackageAndRunPatchService(String path, boolean useEmergencyMode) { final int returnCode = patchCheck(path, null); Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode); return returnCode; diff --git a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java index 1cbe4025..c28ed422 100644 --- a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java +++ b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java @@ -25,5 +25,5 @@ */ public abstract class AbstractPatch { - public abstract boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult); + public abstract boolean tryPatch(Context context, String tempPatchPath, boolean useEmergencyMode, PatchResult patchResult); } diff --git a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java index 7dfa2f9b..7e2b2a8f 100644 --- a/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java +++ b/tinker-android/tinker-android-lib-no-op/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java @@ -31,7 +31,7 @@ public class UpgradePatch extends AbstractPatch { private static final String TAG = "Tinker.UpgradePatch"; @Override - public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) { + public boolean tryPatch(Context context, String tempPatchPath, boolean useEmergencyMode, PatchResult patchResult) { ShareTinkerLog.e(TAG, "[-] Ignore this invocation since I'm no-op version."); return false; } diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java index e48317a9..cf8bb228 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/listener/DefaultPatchListener.java @@ -58,19 +58,34 @@ public DefaultPatchListener(Context context) { */ @Override public int onPatchReceived(String path) { + return checkPackageAndRunPatchService(path, false); + } + + /** + * Check patch package then start patch service to generate patched artifacts. + * @param path + * Path to your patch package. + * @param useEmergencyMode + * true for using emergency mode, otherwise false. + * + * By using emergency mode, dex2oat triggering procedure will be done asynchronously on Android Q and newer + * system to save costs. If your app lives too short to wait for generating patch artifacts, this mode should + * help. **However, the performance of your patched app will become terribly worse since odex of patched dex(es) + * may not be generated before loading patched artifacts in this mode.** + */ + protected int checkPackageAndRunPatchService(String path, boolean useEmergencyMode) { final File patchFile = new File(path); final String patchMD5 = SharePatchFileUtil.getMD5(patchFile); final int returnCode = patchCheck(path, patchMD5); if (returnCode == ShareConstants.ERROR_PATCH_OK) { runForgService(); - TinkerPatchService.runPatchService(context, path); + TinkerPatchService.runPatchService(context, path, useEmergencyMode); } else { Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode); } return returnCode; } - private void runForgService() { try { connection = new ServiceConnection() { @@ -156,5 +171,4 @@ protected int patchCheck(String path, String patchMd5) { return ShareConstants.ERROR_PATCH_OK; } - } diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java index 1cbe4025..c28ed422 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/AbstractPatch.java @@ -25,5 +25,5 @@ */ public abstract class AbstractPatch { - public abstract boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult); + public abstract boolean tryPatch(Context context, String tempPatchPath, boolean useEmergencyMode, PatchResult patchResult); } diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java index 49623e77..210551aa 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java @@ -71,7 +71,8 @@ public class DexDiffPatchInternal extends BasePatchInternal { protected static boolean tryRecoverDexFiles(Tinker manager, ShareSecurityCheck checker, Context context, - String patchVersionDirectory, File patchFile, PatchResult patchResult) { + String patchVersionDirectory, File patchFile, boolean useEmergencyMode, + PatchResult patchResult) { if (!manager.isEnabledForDex()) { ShareTinkerLog.w(TAG, "patch recover, dex is not enabled"); return true; @@ -84,7 +85,8 @@ protected static boolean tryRecoverDexFiles(Tinker manager, ShareSecurityCheck c } long begin = SystemClock.elapsedRealtime(); - boolean result = patchDexExtractViaDexDiff(context, patchVersionDirectory, dexMeta, patchFile, patchResult); + boolean result = patchDexExtractViaDexDiff(context, patchVersionDirectory, dexMeta, patchFile, + useEmergencyMode, patchResult); long cost = SystemClock.elapsedRealtime() - begin; patchResult.dexCostTime = cost; ShareTinkerLog.i(TAG, "recover dex result:%b, cost:%d", result, cost); @@ -166,7 +168,9 @@ protected static boolean waitAndCheckDexOptFile(File patchFile, Tinker manager) return true; } - private static boolean patchDexExtractViaDexDiff(Context context, String patchVersionDirectory, String meta, final File patchFile, PatchResult patchResult) { + private static boolean patchDexExtractViaDexDiff(Context context, String patchVersionDirectory, String meta, + final File patchFile, boolean useEmergencyMode, + PatchResult patchResult) { String dir = patchVersionDirectory + "/" + DEX_PATH + "/"; if (!extractDexDiffInternals(context, dir, meta, patchFile, TYPE_DEX)) { @@ -194,7 +198,7 @@ private static boolean patchDexExtractViaDexDiff(Context context, String patchVe ShareTinkerLog.i(TAG, "legal files to do dexopt: " + legalFiles); final String optimizeDexDirectory = patchVersionDirectory + "/" + DEX_OPTIMIZE_PATH + "/"; - return dexOptimizeDexFiles(context, legalFiles, optimizeDexDirectory, patchFile, patchResult); + return dexOptimizeDexFiles(context, legalFiles, optimizeDexDirectory, patchFile, useEmergencyMode, patchResult); } @@ -347,7 +351,9 @@ private static boolean mergeClassNDexFiles(final Context context, final File pat return result; } - private static boolean dexOptimizeDexFiles(Context context, List dexFiles, String optimizeDexDirectory, final File patchFile, final PatchResult patchResult) { + private static boolean dexOptimizeDexFiles(Context context, List dexFiles, String optimizeDexDirectory, + final File patchFile, boolean useEmergencyMode, + final PatchResult patchResult) { final Tinker manager = Tinker.with(context); optFiles.clear(); @@ -381,7 +387,7 @@ private static boolean dexOptimizeDexFiles(Context context, List dexFiles, // try parallel dex optimizer TinkerDexOptimizer.optimizeAll( context, dexFiles, optimizeDexDirectoryFile, - useDLC, + useDLC, useEmergencyMode, new TinkerDexOptimizer.ResultCallback() { long startTime; diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java index e46dad17..c492098e 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/UpgradePatch.java @@ -42,7 +42,7 @@ public class UpgradePatch extends AbstractPatch { private static final String TAG = "Tinker.UpgradePatch"; @Override - public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) { + public boolean tryPatch(Context context, String tempPatchPath, boolean useEmergencyMode, PatchResult patchResult) { Tinker manager = Tinker.with(context); final File patchFile = new File(tempPatchPath); @@ -169,7 +169,7 @@ public boolean tryPatch(Context context, String tempPatchPath, PatchResult patch } //we use destPatchFile instead of patchFile, because patchFile may be deleted during the patch process - if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile, patchResult)) { + if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile, useEmergencyMode, patchResult)) { ShareTinkerLog.e(TAG, "UpgradePatch tryPatch:new patch recover, try patch dex failed"); return false; } diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/PatchResult.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/PatchResult.java index 74f91d2a..933f54c4 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/PatchResult.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/PatchResult.java @@ -30,6 +30,8 @@ public class PatchResult implements Serializable { public String rawPatchFilePath; + public boolean useEmergencyMode; + public long totalCostTime; public long dexCostTime; @@ -55,6 +57,7 @@ public String toString() { sb.append("\nPatchResult: \n"); sb.append("isSuccess:" + isSuccess + "\n"); sb.append("rawPatchFilePath:" + rawPatchFilePath + "\n"); + sb.append("useEmergencyMode:" + useEmergencyMode + "\n"); sb.append("costTime:" + totalCostTime + "\n"); sb.append("dexoptTriggerTime:" + dexoptTriggerTime + "\n"); sb.append("isOatGenerated:" + isOatGenerated + "\n"); diff --git a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/TinkerPatchService.java b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/TinkerPatchService.java index f311782a..b44c04cd 100644 --- a/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/TinkerPatchService.java +++ b/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/service/TinkerPatchService.java @@ -47,6 +47,7 @@ public class TinkerPatchService extends IntentService { private static final String TAG = "Tinker.TinkerPatchService"; private static final String PATCH_PATH_EXTRA = "patch_path_extra"; + private static final String PATCH_USE_EMERGENCY_MODE = "patch_use_emergency_mode"; private static final String RESULT_CLASS_EXTRA = "patch_result_class"; private static AbstractPatch upgradePatchProcessor = null; @@ -59,9 +60,14 @@ public TinkerPatchService() { } public static void runPatchService(final Context context, final String path) { + runPatchService(context, path, false); + } + + public static void runPatchService(final Context context, final String path, boolean useEmergencyMode) { ShareTinkerLog.i(TAG, "run patch service..."); Intent intent = new Intent(context, TinkerPatchService.class); intent.putExtra(PATCH_PATH_EXTRA, path); + intent.putExtra(PATCH_USE_EMERGENCY_MODE, useEmergencyMode); intent.putExtra(RESULT_CLASS_EXTRA, resultServiceClass.getName()); try { context.startService(intent); @@ -88,6 +94,13 @@ public static String getPatchPathExtra(Intent intent) { return ShareIntentUtil.getStringExtra(intent, PATCH_PATH_EXTRA); } + public static boolean getPatchUseEmergencyMode(Intent intent) { + if (intent == null) { + throw new TinkerRuntimeException("getPatchUseEmergencyMode, but intent is null"); + } + return ShareIntentUtil.getBooleanExtra(intent, PATCH_USE_EMERGENCY_MODE, false); + } + public static String getPatchResultExtra(Intent intent) { if (intent == null) { throw new TinkerRuntimeException("getPatchResultExtra, but intent is null"); @@ -210,6 +223,8 @@ private static void doApplyPatch(Context context, Intent intent) { } File patchFile = new File(path); + final boolean useEmergencyMode = getPatchUseEmergencyMode(intent); + long begin = SystemClock.elapsedRealtime(); boolean result; long cost; @@ -220,7 +235,7 @@ private static void doApplyPatch(Context context, Intent intent) { if (upgradePatchProcessor == null) { throw new TinkerRuntimeException("upgradePatchProcessor is null."); } - result = upgradePatchProcessor.tryPatch(context, path, patchResult); + result = upgradePatchProcessor.tryPatch(context, path, useEmergencyMode, patchResult); } catch (Throwable throwable) { e = throwable; result = false; @@ -233,6 +248,7 @@ private static void doApplyPatch(Context context, Intent intent) { patchResult.isSuccess = result; patchResult.rawPatchFilePath = path; + patchResult.useEmergencyMode = useEmergencyMode; patchResult.totalCostTime = cost; patchResult.type = tinker.getCustomPatcher() == null ? PatchResult.PATCH_TYPE_BSDIFF : PatchResult.PATCH_TYPE_CUSTOM; patchResult.e = e; diff --git a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexLoader.java b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexLoader.java index 7d2d4f1d..58214a03 100644 --- a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexLoader.java +++ b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexLoader.java @@ -153,7 +153,7 @@ public static boolean loadTinkerJars(final TinkerApplication application, String TinkerDexOptimizer.optimizeAll( application, legalFiles, optimizeDir, true, - application.isUseDelegateLastClassLoader(), targetISA, + application.isUseDelegateLastClassLoader(), targetISA, false, new TinkerDexOptimizer.ResultCallback() { long start; diff --git a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java index 299c864b..2f764d93 100644 --- a/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java +++ b/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java @@ -79,14 +79,14 @@ public final class TinkerDexOptimizer { * @return If all dexes are optimized successfully, return true. Otherwise return false. */ public static boolean optimizeAll(Context context, Collection dexFiles, File optimizedDir, - boolean useDLC, ResultCallback cb) { + boolean useDLC, boolean useEmergencyMode, ResultCallback cb) { final String targetISA = ShareTinkerInternals.getCurrentInstructionSet(); - return optimizeAll(context, dexFiles, optimizedDir, false, useDLC, targetISA, cb); + return optimizeAll(context, dexFiles, optimizedDir, false, useDLC, targetISA, useEmergencyMode, cb); } public static boolean optimizeAll(Context context, Collection dexFiles, File optimizedDir, boolean useInterpretMode, boolean useDLC, - String targetISA, ResultCallback cb) { + String targetISA, boolean useEmergencyMode, ResultCallback cb) { ArrayList sortList = new ArrayList<>(dexFiles); // sort input dexFiles with its file length in reverse order. Collections.sort(sortList, new Comparator() { @@ -105,7 +105,7 @@ public int compare(File lhs, File rhs) { }); for (File dexFile : sortList) { OptimizeWorker worker = new OptimizeWorker(context, dexFile, optimizedDir, useInterpretMode, - useDLC, targetISA, cb); + useDLC, targetISA, useEmergencyMode, cb); if (!worker.run()) { return false; } @@ -129,10 +129,11 @@ private static class OptimizeWorker { private final File optimizedDir; private final boolean useInterpretMode; private final boolean useDLC; + private final boolean useEmergencyMode; private final ResultCallback callback; OptimizeWorker(Context context, File dexFile, File optimizedDir, boolean useInterpretMode, - boolean useDLC, String targetISA, ResultCallback cb) { + boolean useDLC, String targetISA, boolean useEmergencyMode, ResultCallback cb) { this.context = context; this.dexFile = dexFile; this.optimizedDir = optimizedDir; @@ -140,6 +141,7 @@ private static class OptimizeWorker { this.useDLC = useDLC; this.callback = cb; this.targetISA = targetISA; + this.useEmergencyMode = useEmergencyMode; } boolean run() { @@ -177,15 +179,27 @@ boolean run() { createFakeODexPathStructureOnDemand(optimizedPath); patchClassLoaderStrongRef = NewClassLoaderInjector.triggerDex2Oat(context, optimizedDir, useDLC, dexFile.getAbsolutePath()); - try { - triggerPMDexOptOnDemand(context, dexFile.getAbsolutePath(), optimizedPath); - } catch (Throwable thr) { - ShareTinkerLog.printErrStackTrace(TAG, thr, - "Fail to call triggerPMDexOptAsyncOnDemand."); - } finally { - final String vdexPath = optimizedPath.substring(0, - optimizedPath.lastIndexOf(ODEX_SUFFIX)) + VDEX_SUFFIX; - waitUntilFileGeneratedOrTimeout(context, vdexPath); + final Runnable task = new Runnable() { + @Override + public void run() { + try { + triggerPMDexOptOnDemand(context, dexFile.getAbsolutePath(), optimizedPath); + } catch (Throwable thr) { + ShareTinkerLog.printErrStackTrace(TAG, thr, + "Fail to call triggerPMDexOptAsyncOnDemand."); + } finally { + if (!useEmergencyMode) { + final String vdexPath = optimizedPath.substring(0, + optimizedPath.lastIndexOf(ODEX_SUFFIX)) + VDEX_SUFFIX; + waitUntilFileGeneratedOrTimeout(context, vdexPath); + } + } + } + }; + if (useEmergencyMode) { + new Thread(task, "TinkerDex2oatTrigger").start(); + } else { + task.run(); } } else { patchClassLoaderStrongRef = NewClassLoaderInjector.triggerDex2Oat(context, optimizedDir, From 6ed897d4717db11f1cc2fd39bd9f3bb20d99420b Mon Sep 17 00:00:00 2001 From: tangyinsheng Date: Fri, 28 Oct 2022 16:06:27 +0800 Subject: [PATCH 5/5] [tinker] Update version to 1.9.14.25.2. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1724259c..33afa1c4 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ ext { javaVersion = JavaVersion.VERSION_1_8 GROUP = 'com.tencent.tinker' - VERSION_NAME = '1.9.14.25.1' + VERSION_NAME = '1.9.14.25.2' POM_DESCRIPTION = 'Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling apk.' POM_URL = 'https://github.com/Tencent/tinker'