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

Commit edb7c22c authored by James O'Leary's avatar James O'Leary
Browse files

Fix WallpaperPicker not being able to assign colors

- ThemeOverlayController intentionally chooses transparent as the theme
color if there aren't any wallpaper colors (ex. a Live Wallpaper that
doesn't send WallpaperColors objects to WallpaperManagerService)
- Rather than refactor old code, we patched by ensuring any attempts to
use less-than-opaque colors for a theme are replaced by using a default
color (ag/15193364)
- However, WallpaperPicker sends transparent colors when setting a new
theme color
- WallpaperPicker's ColorUtils.toColorString turns a color into a 6
characters hex code when transmitting it to framework via JSON stored in
a setting.
- Six characters can only contain red, green, and blue channels, not
alpha.
- When the six character string is parsed by Integer.parseInt(), the
resulting color has 0 alpha, and the new theme color is rejected in
favor of the default color
- Using Color.parseColor ensures that alpha is correctly set, whether
callers use a 6 or 8 character hex code, and ensuring the input to
Color.parseColor starts with a # sign ensures it will work properly

Test: Add logs to WallpaperPicker, verifying points at which it
manipulates hex codes, ensuring WallpaperPicker is receiving colors
with a full alpha channel and merely losing it upon converting it to
a 6 digit string. Make this patch to framework. Check implementation of
Color.parseColor, ensure we meet its requirements for arguments by
sanitizing inputs from callers (i.e. add a # sign in front). Uninstall
patches wallpaper picker, reverting to system image, and verify theme
colors are assigned properly.
Bug: 193044118

Change-Id: Idcdf5d41ee422d5a1b12cec415da9198b8405a5a
parent 103bec65
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -450,19 +450,23 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
        OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
        if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
            try {
                int color = Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
                String colorString =  systemPalette.getPackageName().toLowerCase();
                if (!colorString.startsWith("#")) {
                    colorString = "#" + colorString;
                }
                int color = Color.parseColor(colorString);
                mNeutralOverlay = getOverlay(color, NEUTRAL);
                mNeedsOverlayCreation = true;
                categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
            } catch (NumberFormatException e) {
                Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName());
            } catch (Exception e) {
                // Color.parseColor doesn't catch any exceptions from the calls it makes
                Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e);
            }
        } else if (!mIsMonetEnabled && systemPalette != null) {
            try {
                // It's possible that we flipped the flag off and still have a @ColorInt in the
                // setting. We need to sanitize the input, otherwise the overlay transaction will
                // fail.
                Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
                categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
            } catch (NumberFormatException e) {
                // This is a package name. All good, let's continue
@@ -473,12 +477,17 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
        OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR);
        if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
            try {
                int color = Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
                String colorString =  accentPalette.getPackageName().toLowerCase();
                if (!colorString.startsWith("#")) {
                    colorString = "#" + colorString;
                }
                int color = Color.parseColor(colorString);
                mSecondaryOverlay = getOverlay(color, ACCENT);
                mNeedsOverlayCreation = true;
                categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
            } catch (NumberFormatException e) {
                Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName());
            } catch (Exception e) {
                // Color.parseColor doesn't catch any exceptions from the calls it makes
                Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e);
            }
        } else if (!mIsMonetEnabled && accentPalette != null) {
            try {
+38 −0
Original line number Diff line number Diff line
@@ -465,6 +465,44 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
                any());
    }

    @Test
    public void catchException_whenPackageNameIsOverlayName() {
        mDeviceProvisionedController = mock(DeviceProvisionedController.class);
        mThemeOverlayApplier = mock(ThemeOverlayApplier.class);
        mWallpaperManager = mock(WallpaperManager.class);

        // Assume we have some wallpaper colors at boot.
        when(mWallpaperManager.getWallpaperColors(anyInt()))
                .thenReturn(new WallpaperColors(Color.valueOf(Color.GRAY), null, null));

        Executor executor = MoreExecutors.directExecutor();

        mThemeOverlayController = new ThemeOverlayController(null /* context */,
                mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
            @Nullable
            @Override
            protected FabricatedOverlay getOverlay(int color, int type) {
                FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                when(overlay.getIdentifier())
                        .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));

                return overlay;
            }

        };
        mThemeOverlayController.start();

        verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
                eq(UserHandle.USER_ALL));
        verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());

        // Colors were applied during controller initialization.
        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
        clearInvocations(mThemeOverlayApplier);
    }

    @Test
    public void onWallpaperColorsChanged_defersUntilSetupIsCompleted_ifHasColors() {
        mDeviceProvisionedController = mock(DeviceProvisionedController.class);