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
This commit is contained in:
Pierre Barbier de Reuille
2021-10-07 12:27:32 +00:00
parent 7fc0216e35
commit acfcc90ebd
4 changed files with 47 additions and 7 deletions

View File

@@ -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<String> 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);
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -408,7 +408,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
}
@Override
protected Context getRemoteContext() {
protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
return null;
}