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
This commit is contained in:
James O'Leary
2021-07-12 10:10:19 -04:00
parent 103bec65a2
commit edb7c22c0d
2 changed files with 54 additions and 7 deletions

View File

@@ -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 {

View File

@@ -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);