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

Commit d605e2d6 authored by Winson's avatar Winson
Browse files

Propagate base code path and split dir changes to Resources objects

When split APK loading is deferred, like a dynamic feature module
or delayed install, the ResourcesImpl for old Resources objects
weren't being updated.

This is because the stable key used was "mResDir", which
corresponds to a path with a random hash ID suffixed to the end
of the APKs, like com.example.android-RANDOMHASH/base.apk.

Because RANDOMHASH changes every time a package is updated
(like installing a split), the stable key is no longer valid for
old Resources objects. That means they would never get notified
that the package resource directories could have changed.

There was a partial solution added 2 years ago to solve this case
for refreshing overlays. This extends the solution to replace base
code paths and split dirs, as well as force updates to Resources
with the old code paths.

The original breakage started from P, but I was unable to narrow
down what caused it. This bug should've existed in O, but maybe
a separate, unrelated resources behavior allowed it to work. And
given that, I don't know of a workaround for current instant apps.

Bug: 112392906
Bug: 116167993

Test: manual test Dotloop IA in b/112392906
Test: manual test Hollar IA in b/116167993

Change-Id: I73491fa2b9397a489765ddac8f0a07a797f02028
parent 02aefee5
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -5434,19 +5434,25 @@ public final class ActivityThread extends ClientTransactionHandler {
            ref = mResourcePackages.get(ai.packageName);
            resApk = ref != null ? ref.get() : null;
        }

        final String[] oldResDirs = new String[2];

        if (apk != null) {
            oldResDirs[0] = apk.getResDir();
            final ArrayList<String> oldPaths = new ArrayList<>();
            LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
            apk.updateApplicationInfo(ai, oldPaths);
        }
        if (resApk != null) {
            oldResDirs[1] = resApk.getResDir();
            final ArrayList<String> oldPaths = new ArrayList<>();
            LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
            resApk.updateApplicationInfo(ai, oldPaths);
        }

        synchronized (mResourcesManager) {
            // Update all affected Resources objects to use new ResourcesImpl
            mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
            mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
        }

        ApplicationPackageManager.configurationChanged();
@@ -5699,9 +5705,17 @@ public final class ActivityThread extends ClientTransactionHandler {
                                        }
                                    }
                                }

                                final String[] oldResDirs = { pkgInfo.getResDir() };

                                final ArrayList<String> oldPaths = new ArrayList<>();
                                LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths);
                                pkgInfo.updateApplicationInfo(aInfo, oldPaths);

                                synchronized (mResourcesManager) {
                                    // Update affected Resources objects to use new ResourcesImpl
                                    mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
                                }
                            } catch (RemoteException e) {
                            }
                        }
+28 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.CompatResources;
@@ -32,6 +33,7 @@ import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -1136,27 +1138,46 @@ public class ResourcesManager {
    }

    // TODO(adamlesinski): Make this accept more than just overlay directories.
    final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
            @Nullable final String[] newResourceDirs) {
    final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
            @Nullable final String[] oldPaths) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                    "ResourcesManager#applyNewResourceDirsLocked");

            String baseCodePath = appInfo.getBaseCodePath();

            final int myUid = Process.myUid();
            String[] newSplitDirs = appInfo.uid == myUid
                    ? appInfo.splitSourceDirs
                    : appInfo.splitPublicSourceDirs;

            // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
            String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
            String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);

            final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
            final int implCount = mResourceImpls.size();
            for (int i = 0; i < implCount; i++) {
                final ResourcesKey key = mResourceImpls.keyAt(i);
                final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
                final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
                if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) {

                if (impl == null) {
                    continue;
                }

                if (key.mResDir == null
                        || key.mResDir.equals(baseCodePath)
                        || ArrayUtils.contains(oldPaths, key.mResDir)) {
                    updatedResourceKeys.put(impl, new ResourcesKey(
                            key.mResDir,
                            key.mSplitResDirs,
                            newResourceDirs,
                            baseCodePath,
                            copiedSplitDirs,
                            copiedResourceDirs,
                            key.mLibDirs,
                            key.mDisplayId,
                            key.mOverrideConfiguration,
                            key.mCompatInfo));
                            key.mCompatInfo
                    ));
                }
            }

+7 −0
Original line number Diff line number Diff line
@@ -527,6 +527,13 @@ public class ArrayUtils {
        return (array != null) ? array.clone() : null;
    }

    /**
     * Clones an array or returns null if the array is null.
     */
    public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) {
        return (array != null) ? array.clone() : null;
    }

    public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
        return (array != null) ? new ArraySet<T>(array) : null;
    }