Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acfcc90e authored by Pierre Barbier de Reuille's avatar Pierre Barbier de Reuille
Browse files

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
parent 7fc0216e
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -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);
    }

}
+7 −4
Original line number Diff line number Diff line
@@ -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);
+5 −2
Original line number Diff line number Diff line
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -408,7 +408,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
        }

        @Override
        protected Context getRemoteContext() {
        protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
            return null;
        }