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

Commit 814b239b authored by Yurii Zubrytskyi's avatar Yurii Zubrytskyi
Browse files

[res] Fix registerResourcePaths crash with bad path

Make sure that when we're adding new paths to existing Resources
objects this doesn't throw an unexpected exception to the
API caller. Instead, it's better to skip updating any existing
objects that fail loading - those can't be used correctly
anymore anyway
(the usual issue is when there's an object that hasn't been
collected yet, so it is irrelevant to the app correctness)

+ Add a unit test for this exact scenario

Bug: 405913316
Flag: EXEMPT small bugfix
Test: atest ResourcesManagerTest

Change-Id: I0fc33770dbf2932c2946ed702ed7d5b2c3dde04b
parent 946719a8
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -1886,10 +1886,12 @@ public class ResourcesManager {
            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");
                }
                if (impl != null) {
                    r.setImpl(impl);
                } else {
                    Slog.w(TAG, "failed to redirect ResourcesImpl, left untouched, for a key "
                            + key);
                }
            } else {
                // ResourcesKey is null which means the ResourcesImpl could belong to a
                // Resources created by application through Resources constructor and was not
+46 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;

import android.annotation.NonNull;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -43,11 +44,16 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

@@ -123,8 +129,12 @@ public class ResourcesManagerTest {
        ResourcesManager.setInstance(mOldResourcesManager);
    }

    private Context getContext() {
        return InstrumentationRegistry.getInstrumentation().getContext();
    }

    private PackageManager getPackageManager() {
        return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
        return getContext().getPackageManager();
    }

    @Test
@@ -543,6 +553,41 @@ public class ResourcesManagerTest {
        assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
    }

    @Test
    @SmallTest
    @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
    public void testRegisterPathWithExistingResourcesWithInvalidPath()
            throws PackageManager.NameNotFoundException, IOException {
        File filesDir = getContext().getFilesDir();
        File tmpName = new File(filesDir, "tmp.apk");
        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(TEST_LIB, 0);
        try {
            // This test requires a proper setup:
            // 1. Create a temp location for an apk with resources. Make sure the file is a
            //    separate copy, not a link, so we can remove it completely later.
            Files.copy(Path.of(appInfo.sourceDir), tmpName.toPath());

            // 2. Load those resources using the real resource manager object
            ResourcesManager.setInstance(mOldResourcesManager);
            Resources tmpResources = ResourcesManager.getInstance().getResources(null,
                    tmpName.toString(),
                    null, null, null, null, null, null,
                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
            assertNotNull(tmpResources);

            // 3. Now remove the temporary apk - simulate an app uninstallation
            Files.delete(tmpName.toPath());

            // 4. Here comes the test - registering resources path is going to update all objects
            //    and it should not fail because our temporary resources backing file is gone
            Resources.registerResourcePaths(TEST_LIB, appInfo);
        } catch (Exception e) {
            Assert.fail("Shouldn't throw, but did: " + e);
        } finally {
            Files.deleteIfExists(tmpName.toPath());
        }
    }

    private static boolean containsPath(String substring, ApkAssets[] assets) {
        for (final var asset : assets) {
            if (asset.getAssetPath().contains(substring)) {