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

Commit 500a2bb4 authored by Brandon Liu's avatar Brandon Liu
Browse files

Fix Amazon Prime Video sign in failure caused by register_resource_paths

feature flag.

Amazon Prime Video got sign in failures consistently after
register_resource_paths enabled in trunkfood. This is because the app
itself created some Resources instances from Resources constructor
directly and are not managed by ResourcesManager, and also will not
append proper asset paths after shared library registered. To fix this,
this CL introduced another list to collect references of all Resources
and another redirect function to help to reload ResourcesImpl only for
shared library asset paths update.

Bug: b/330286700
Test: Added and verified all affected tests pass, also passed manual
test.

Change-Id: Ie22b2ebf8708535af3f6cfbec4209f9f99b48ff2
parent e8053c5e
Loading
Loading
Loading
Loading
+62 −1
Original line number Diff line number Diff line
@@ -119,6 +119,17 @@ public class ResourcesManager {
    private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
    private final ReferenceQueue<Resources> mResourcesReferencesQueue = new ReferenceQueue<>();

    /**
     * A list of Resources references for all Resources instances created through Resources public
     * constructor, only system Resources created by the private constructor are excluded.
     * This addition is necessary due to certain Application Resources created by constructor
     * directly which are not managed by ResourcesManager, hence we require a comprehensive
     * collection of all Resources references to help with asset paths appending tasks when shared
     * libraries are registered.
     */
    private final ArrayList<WeakReference<Resources>> mAllResourceReferences = new ArrayList<>();
    private final ReferenceQueue<Resources> mAllResourceReferencesQueue = new ReferenceQueue<>();

    /**
     * The localeConfig of the app.
     */
@@ -1568,7 +1579,7 @@ public class ResourcesManager {
                }
            }

            redirectResourcesToNewImplLocked(updatedResourceKeys);
            redirectAllResourcesToNewImplLocked(updatedResourceKeys);
        }
    }

@@ -1707,6 +1718,43 @@ public class ResourcesManager {
        }
    }

    // Another redirect function which will loop through all Resources and reload ResourcesImpl
    // if it needs a shared library asset paths update.
    private void redirectAllResourcesToNewImplLocked(
            @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
        cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue);

        // Update any references to ResourcesImpl that require reloading.
        final int resourcesCount = mAllResourceReferences.size();
        for (int i = 0; i < resourcesCount; i++) {
            final WeakReference<Resources> ref = mAllResourceReferences.get(i);
            final Resources r = ref != null ? ref.get() : null;
            if (r != null) {
                final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
                if (key != null) {
                    final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
                    if (impl == null) {
                        throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
                    }
                    r.setImpl(impl);
                } else {
                    // ResourcesKey is null which means the ResourcesImpl could belong to a
                    // Resources created by application through Resources constructor and was not
                    // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
                    // have shared library asset paths appended if there are any.
                    if (r.getImpl() != null) {
                        final ResourcesImpl oldImpl = r.getImpl();
                        // ResourcesImpl constructor will help to append shared library asset paths.
                        final ResourcesImpl newImpl = new ResourcesImpl(oldImpl.getAssets(),
                                oldImpl.getMetrics(), oldImpl.getConfiguration(),
                                oldImpl.getDisplayAdjustments());
                        r.setImpl(newImpl);
                    }
                }
            }
        }
    }

    /**
     * Returns the LocaleConfig current set
     */
@@ -1827,4 +1875,17 @@ public class ResourcesManager {
    public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() {
        return new ArrayMap<>(mSharedLibAssetsMap);
    }

    /**
     * Add all resources references to the list which is designed to help to append shared library
     * asset paths. This is invoked in Resources constructor to include all Resources instances.
     */
    public void registerAllResourcesReference(@NonNull Resources resources) {
        if (android.content.res.Flags.registerResourcePaths()) {
            synchronized (mLock) {
                mAllResourceReferences.add(
                        new WeakReference<>(resources, mAllResourceReferencesQueue));
            }
        }
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -339,14 +339,17 @@ public class Resources {
    public Resources(@Nullable ClassLoader classLoader) {
        mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
        sResourcesHistory.add(this);
        ResourcesManager.getInstance().registerAllResourcesReference(this);
    }

    /**
     * Only for creating the System resources.
     * Only for creating the System resources. This is the only constructor that doesn't add
     * Resources itself to the ResourcesManager list of all Resources references.
     */
    @UnsupportedAppUsage
    private Resources() {
        this(null);
        mClassLoader = ClassLoader.getSystemClassLoader();
        sResourcesHistory.add(this);

        final DisplayMetrics metrics = new DisplayMetrics();
        metrics.setToDefaults();
+7 −1
Original line number Diff line number Diff line
@@ -228,6 +228,11 @@ public class ResourcesImpl {
        return mAssets;
    }

    @UnsupportedAppUsage
    public DisplayMetrics getMetrics() {
        return mMetrics;
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    DisplayMetrics getDisplayMetrics() {
        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
@@ -235,7 +240,8 @@ public class ResourcesImpl {
        return mMetrics;
    }

    Configuration getConfiguration() {
    @UnsupportedAppUsage
    public Configuration getConfiguration() {
        return mConfiguration;
    }

+38 −0
Original line number Diff line number Diff line
@@ -421,6 +421,44 @@ public class ResourcesManagerTest extends TestCase {
        ResourcesManager.setInstance(oriResourcesManager);
    }

    @Test
    @SmallTest
    @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
    public void testExistingResourcesCreatedByConstructorAfterResourcePathsRegistration()
            throws PackageManager.NameNotFoundException {
        // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
        // the static method can interact with this test smoothly.
        ResourcesManager oriResourcesManager = ResourcesManager.getInstance();
        ResourcesManager.setInstance(mResourcesManager);

        // Create a Resources through constructor directly before register resources' paths.
        final DisplayMetrics metrics = new DisplayMetrics();
        metrics.setToDefaults();
        final Configuration config = new Configuration();
        config.setToDefaults();
        Resources resources = new Resources(new AssetManager(), metrics, config);
        assertNotNull(resources);

        ResourcesImpl oriResImpl = resources.getImpl();

        ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
        Resources.registerResourcePaths(TEST_LIB, appInfo);

        assertNotSame(oriResImpl, resources.getImpl());

        String[] resourcePaths = appInfo.getAllApkPaths();
        resourcePaths = removeDuplicates(resourcePaths);
        ApkAssets[] loadedAssets = resources.getAssets().getApkAssets();
        assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));

        // Package resources' paths should be cached in ResourcesManager.
        assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
                .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));

        // Revert the ResourcesManager instance back.
        ResourcesManager.setInstance(oriResourcesManager);
    }

    @Test
    @SmallTest
    @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)