From acfcc90ebd40be13b5058529d2b77dd57cbe2c8f Mon Sep 17 00:00:00 2001 From: Pierre Barbier de Reuille Date: Thu, 7 Oct 2021 12:27:32 +0000 Subject: [PATCH] Check if APK paths are valid right before creating the context. The check has to be done in RemoteViews and in AppWidgetHostView right before creating the context used to inflate the app widget. Further, the APK is cached potentially in two places: in the APK with code and the APK without codes, so both places are updated if present. Test: manual, see bug for details Fix: 202369942 Change-Id: I5718f67711a3332a942d3c037eef7f30379549a4 --- core/java/android/app/LoadedApk.java | 34 +++++++++++++++++++ .../android/appwidget/AppWidgetHostView.java | 11 +++--- core/java/android/widget/RemoteViews.java | 7 ++-- .../android/widget/RemoteViewsAdapter.java | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index a2c9795204ada..74208c3a4aff6 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -2134,4 +2134,38 @@ public final class LoadedApk { final IBinder mService; } } + + /** + * Check if the Apk paths in the cache are correct, and update them if they are not. + * @hide + */ + public static void checkAndUpdateApkPaths(ApplicationInfo expectedAppInfo) { + // Get the LoadedApk from the cache + ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread == null) { + Log.e(TAG, "Cannot find activity thread"); + return; + } + checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ true); + checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ false); + } + + private static void checkAndUpdateApkPaths(ActivityThread activityThread, + ApplicationInfo expectedAppInfo, boolean cacheWithCode) { + String expectedCodePath = expectedAppInfo.getCodePath(); + LoadedApk loadedApk = activityThread.peekPackageInfo( + expectedAppInfo.packageName, /* includeCode= */ cacheWithCode); + // If there is load apk cached, or if the cache is valid, don't do anything. + if (loadedApk == null || loadedApk.getApplicationInfo() == null + || loadedApk.getApplicationInfo().getCodePath().equals(expectedCodePath)) { + return; + } + // Duplicate framework logic + List oldPaths = new ArrayList<>(); + LoadedApk.makePaths(activityThread, expectedAppInfo, oldPaths); + + // Force update the LoadedApk instance, which should update the reference in the cache + loadedApk.updateApplicationInfo(expectedAppInfo, oldPaths); + } + } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index dd147cc26e592..eb10f097f37f1 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityOptions; +import android.app.LoadedApk; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -554,7 +555,7 @@ public class AppWidgetHostView extends FrameLayout { } // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. - mRemoteContext = getRemoteContext(); + mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath(); int layoutId = rvToApply.getLayoutId(); if (rvToApply.canRecycleView(mView)) { @@ -616,7 +617,7 @@ public class AppWidgetHostView extends FrameLayout { private void inflateAsync(@NonNull RemoteViews remoteViews) { // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. - mRemoteContext = getRemoteContext(); + mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath(); int layoutId = remoteViews.getLayoutId(); if (mLastExecutionSignal != null) { @@ -718,8 +719,10 @@ public class AppWidgetHostView extends FrameLayout { * purposes of reading remote resources. * @hide */ - protected Context getRemoteContext() { + protected Context getRemoteContextEnsuringCorrectCachedApkPath() { try { + ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo; + LoadedApk.checkAndUpdateApkPaths(expectedAppInfo); // Return if cloned successfully, otherwise default Context newContext = mContext.createApplicationContext( mInfo.providerInfo.applicationInfo, @@ -765,7 +768,7 @@ public class AppWidgetHostView extends FrameLayout { try { if (mInfo != null) { - Context theirContext = getRemoteContext(); + Context theirContext = getRemoteContextEnsuringCorrectCachedApkPath(); mRemoteContext = theirContext; LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index fe5eb085dc5c0..0f309f10c2b79 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -34,6 +34,7 @@ import android.app.Activity; import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application; +import android.app.LoadedApk; import android.app.PendingIntent; import android.app.RemoteInput; import android.appwidget.AppWidgetHostView; @@ -5475,7 +5476,8 @@ public class RemoteViews implements Parcelable, Filter { // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. - final Context contextForResources = getContextForResources(context); + final Context contextForResources = + getContextForResourcesEnsuringCorrectCachedApkPaths(context); if (colorResources != null) { colorResources.apply(contextForResources); } @@ -5853,13 +5855,14 @@ public class RemoteViews implements Parcelable, Filter { } } - private Context getContextForResources(Context context) { + private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) { if (mApplication != null) { if (context.getUserId() == UserHandle.getUserId(mApplication.uid) && context.getPackageName().equals(mApplication.packageName)) { return context; } try { + LoadedApk.checkAndUpdateApkPaths(mApplication); return context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 6b33428d7fe49..8e293f4b356dc 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -408,7 +408,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } @Override - protected Context getRemoteContext() { + protected Context getRemoteContextEnsuringCorrectCachedApkPath() { return null; }