From d33ef5665159212e190d2cccda55e77b19779e75 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 19 Jan 2017 14:49:59 -0800 Subject: [PATCH] ActivityThread: Only update code paths that have been added When an ApplicationInfo object is updated and we want to update code paths without restarting the app's process, we need to make sure that we only update the paths that have changed. This means diffing between the old paths and the new paths. Test: watch /proc//maps for dex entries before and after running adb exec-out am update-appinfos all Change-Id: I6855d860478ade3184bbb578a5483d8548396daa --- core/java/android/app/ActivityThread.java | 13 +++++++++++-- core/java/android/app/LoadedApk.java | 12 +++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 74614cc8a7036..d814ddcc7f27d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4919,18 +4919,27 @@ public final class ActivityThread { } void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { + // Updates triggered by package installation go through a package update + // receiver. Here we try to capture ApplicationInfo changes that are + // caused by other sources, such as overlays. That means we want to be as conservative + // about code changes as possible. Take the diff of the old ApplicationInfo and the new + // to see if anything needs to change. synchronized (mResourcesManager) { // Update all affected loaded packages with new package information WeakReference ref = mPackages.get(ai.packageName); LoadedApk apk = ref != null ? ref.get() : null; if (apk != null) { - apk.updateApplicationInfo(ai, null); + final ArrayList oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths, null /*outLibPaths*/); + apk.updateApplicationInfo(ai, oldPaths); } ref = mResourcePackages.get(ai.packageName); apk = ref != null ? ref.get() : null; if (apk != null) { - apk.updateApplicationInfo(ai, null); + final ArrayList oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths, null /*outLibPaths*/); + apk.updateApplicationInfo(ai, oldPaths); } // Update all affected Resources objects to use new ResourcesImpl diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index db1162a3cbb3a..4ab07432ad1f0 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -262,7 +264,15 @@ public final class LoadedApk { return ai.sharedLibraryFiles; } - public void updateApplicationInfo(ApplicationInfo aInfo, List oldPaths) { + /** + * Update the ApplicationInfo for an app. If oldPaths is null, all the paths are considered + * new. + * @param aInfo The new ApplicationInfo to use for this LoadedApk + * @param oldPaths The code paths for the old ApplicationInfo object. null means no paths can + * be reused. + */ + public void updateApplicationInfo(@NonNull ApplicationInfo aInfo, + @Nullable List oldPaths) { setApplicationInfo(aInfo); final List newPaths = new ArrayList<>();