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

Commit fd201a12 authored by Chris Göllner's avatar Chris Göllner
Browse files

ApplicationPackageManager: use display aware Configuration for Resources

When calling getResourcesForApplication from an external display, the
returned Resources should have the same Configuration as the Resources
provided by the Context associated with the Display.

If we don't specify the specific Configuration, the Configuration from
the default display will be used, which may lead to wrong qualifiers
being used, due to different display dimensions.

Test: Unable to add unit test, as it is not possible to instantiate
      ApplicationPackageManager with ContextImpl in tests.
Test: Connect an external display with different dimensions to the
      default display, and see that the system status bar icons display
      correctly.
Bug: 420905041
Flag: com.android.graphics.flags.use_resources_from_context_to_create_drawable_icons
Change-Id: I51e935971a8b67b20f17d8807bb2dfc66292c9fa
parent 10eefec8
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ import android.util.LauncherIcons;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import android.window.DesktopExperienceFlags;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -2162,8 +2163,16 @@ public class ApplicationPackageManager extends PackageManager {
    @Override
    public Resources getResourcesForApplication(@NonNull ApplicationInfo app)
            throws NameNotFoundException {
        if (DesktopExperienceFlags.USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS.isTrue()) {
            // To support multiple displays, we need to use the Configuration of the Resources
            // associated with the Display of the current Context.
            // Otherwise, we will be using wrong resource qualifiers due to different display
            // configurations.
            return getResourcesForApplication(app, mContext.getResources().getConfiguration());
        } else {
            return getResourcesForApplication(app, null);
        }
    }

    @Override
    public Resources getResourcesForApplication(@NonNull ApplicationInfo app,
+139 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.os.storage.VolumeInfo.STATE_MOUNTED;
import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;

import static com.android.graphics.flags.Flags.FLAG_USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -47,21 +49,35 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;

import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.internal.annotations.VisibleForTesting;

import junit.framework.TestCase;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
@@ -73,7 +89,13 @@ import java.util.List;

@Presubmit
@LargeTest
@RunWith(AndroidJUnit4.class)
public class ApplicationPackageManagerTest extends TestCase {

    @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule(
            com.android.graphics.flags.Flags.class);
    @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();

    private static final String sInternalVolPath = "/data";
    private static final String sAdoptedVolPath = "/mnt/expand/123";
    private static final String sPublicVolPath = "/emulated";
@@ -204,6 +226,7 @@ public class ApplicationPackageManagerTest extends TestCase {
        }
    }

    @Test
    public void testGetCandidateVolumes_systemApp() throws Exception {
        ApplicationInfo sysAppInfo = new ApplicationInfo();
        sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
@@ -235,6 +258,7 @@ public class ApplicationPackageManagerTest extends TestCase {
        verifyReturnedVolumes(candidates, sInternalVol);
    }

    @Test
    public void testGetCandidateVolumes_3rdParty_internalOnly() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        StorageManager storageManager = getMockedStorageManager();
@@ -264,6 +288,7 @@ public class ApplicationPackageManagerTest extends TestCase {
        }
    }

    @Test
    public void testGetCandidateVolumes_3rdParty_auto() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        StorageManager storageManager = getMockedStorageManager();
@@ -302,12 +327,14 @@ public class ApplicationPackageManagerTest extends TestCase {
        }
    }

    @Test
    public void testExtractPackageItemInfoAttributes_noServiceInfo() {
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
        assertThat(appPkgMgr.extractPackageItemInfoAttributes(null, null, null,
                new int[]{})).isNull();
    }

    @Test
    public void testExtractPackageItemInfoAttributes_noMetaData() {
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
@@ -315,6 +342,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                new int[]{})).isNull();
    }

    @Test
    public void testExtractPackageItemInfoAttributes_noParser() {
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
@@ -324,6 +352,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                new int[]{})).isNull();
    }

    @Test
    public void testExtractPackageItemInfoAttributes_noMetaDataXml() {
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
@@ -334,6 +363,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                new int[]{})).isNull();
    }

    @Test
    public void testExtractPackageItemInfoAttributes_nonMatchingRootTag() throws Exception {
        final String rootTag = "rootTag";
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -350,6 +380,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                new int[]{})).isNull();
    }

    @Test
    public void testExtractPackageItemInfoAttributes_successfulExtraction() throws Exception {
        final String rootTag = "rootTag";
        final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -371,6 +402,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                new int[]{})).isEqualTo(attributes);
    }

    @Test
    public void testGetLaunchIntentForPackage_categoryInfoActivity_returnsIt() throws Exception {
        String pkg = "com.some.package";
        int userId = 42;
@@ -406,6 +438,7 @@ public class ApplicationPackageManagerTest extends TestCase {
                eq(userId));
    }

    @Test
    public void testGetLaunchIntentForPackage_categoryLauncherActivity_returnsIt() {
        String pkg = "com.some.package";
        int userId = 42;
@@ -441,6 +474,7 @@ public class ApplicationPackageManagerTest extends TestCase {
        assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
    }

    @Test
    public void testGetLaunchIntentForPackage_noSuitableActivity_returnsNull() throws Exception {
        String pkg = "com.some.package";
        int userId = 42;
@@ -458,6 +492,111 @@ public class ApplicationPackageManagerTest extends TestCase {
        assertThat(intent).isNull();
    }

    @DisableFlags(FLAG_USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS)
    @Test
    public void getResourcesForApplication_flagDisabled_nonSystemPackage_returnsDefaultResources()
            throws Exception {
        ApplicationInfo appInfo = createApplicationInfoForPackage("com.example");
        Context defaultDisplayContext = InstrumentationRegistry.getInstrumentation().getContext();
        int defaultDisplayDensityDpi =
                defaultDisplayContext.getResources().getConfiguration().densityDpi;
        Context newDisplayContext =
                defaultDisplayContext.createDisplayContext(createSecondaryDisplay());

        ApplicationPackageManager packageManager =
                (ApplicationPackageManager) newDisplayContext.getPackageManager();

        Resources resourcesForApplication = packageManager.getResourcesForApplication(appInfo);

        assertThat(resourcesForApplication.getConfiguration().densityDpi)
                .isEqualTo(defaultDisplayDensityDpi);
    }

    @DisableFlags(FLAG_USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS)
    @Test
    public void getResourcesForApplication_flagDisabled_systemPackage_returnsDefaultResources()
            throws Exception {
        ApplicationInfo appInfo = createApplicationInfoForPackage("system");
        Context defaultDisplayContext = InstrumentationRegistry.getInstrumentation().getContext();
        int defaultDisplayDensityDpi =
                defaultDisplayContext.getResources().getConfiguration().densityDpi;
        Context newDisplayContext =
                defaultDisplayContext.createDisplayContext(createSecondaryDisplay());

        ApplicationPackageManager packageManager =
                (ApplicationPackageManager) newDisplayContext.getPackageManager();

        Resources resourcesForApplication = packageManager.getResourcesForApplication(appInfo);

        assertThat(resourcesForApplication.getConfiguration().densityDpi)
                .isEqualTo(defaultDisplayDensityDpi);
    }

    @EnableFlags(FLAG_USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS)
    @Test
    public void getResourcesForApplication_flagEnabled_nonSystemPackage_returnsNewDisplayResources()
            throws Exception {
        ApplicationInfo appInfo = createApplicationInfoForPackage("com.example");
        Context defaultDisplayContext = InstrumentationRegistry.getInstrumentation().getContext();
        Context newDisplayContext =
                defaultDisplayContext.createDisplayContext(createSecondaryDisplay());
        int newDisplayDensityDpi = newDisplayContext.getResources().getConfiguration().densityDpi;

        final ApplicationPackageManager pm =
                (ApplicationPackageManager) newDisplayContext.getPackageManager();

        Resources resourcesForApplication = pm.getResourcesForApplication(appInfo);
        assertThat(resourcesForApplication.getConfiguration().densityDpi)
                .isEqualTo(newDisplayDensityDpi);
    }

    @EnableFlags(FLAG_USE_RESOURCES_FROM_CONTEXT_TO_CREATE_DRAWABLE_ICONS)
    @Test
    public void getResourcesForApplication_flagEnabled_systemPackage_returnsNewDisplayResources()
            throws Exception {
        ApplicationInfo appInfo = createApplicationInfoForPackage("system");
        Context defaultDisplayContext = InstrumentationRegistry.getInstrumentation().getContext();
        Context newDisplayContext =
                defaultDisplayContext.createDisplayContext(createSecondaryDisplay());
        int newDisplayDensityDpi = newDisplayContext.getResources().getConfiguration().densityDpi;

        final ApplicationPackageManager pm =
                (ApplicationPackageManager) newDisplayContext.getPackageManager();

        Resources resourcesForApplication = pm.getResourcesForApplication(appInfo);
        assertThat(resourcesForApplication.getConfiguration().densityDpi)
                .isEqualTo(newDisplayDensityDpi);
    }

    private static ApplicationInfo createApplicationInfoForPackage(String packageName) {
        final ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = packageName;
        return appInfo;
    }

    /**
     * Creates a new {@link Display} object representing a secondary display.
     *
     * <p>The returned display has a different density than the default display.
     */
    private Display createSecondaryDisplay() {
        Context context = InstrumentationRegistry.getInstrumentation().getContext();
        Resources resources = context.getResources();
        DisplayMetrics displayMetrics = new DisplayMetrics();
        displayMetrics.densityDpi = resources.getConfiguration().densityDpi + 100;
        Configuration configuration = new Configuration();
        configuration.densityDpi = displayMetrics.densityDpi;
        Resources newResources =
                new Resources(context.getAssets(), displayMetrics, configuration);
        DisplayInfo displayInfo = new DisplayInfo();
        displayInfo.displayId = context.getDisplayId() + 1;
        return new Display(
                DisplayManagerGlobal.getInstance(),
                displayInfo.displayId,
                displayInfo,
                newResources);
    }

    /** Equality check for intents -- ignoring extras */
    private static Intent eqIntent(Intent wanted) {
        return argThat(