From 6fd464985a0f53025976f78f3e7a684fe95c8fd1 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Fri, 5 Nov 2021 17:30:49 -0700 Subject: [PATCH] Don't allow background apps to change theme A background app that changes the wallpaper, was able to change the device theme. This change will apply the theme if the app is on the foreground, like WallpaperPicker would be, but would defer the event until the next power button cycles, like we do with Live Wallpapers, to avoid the same type of DoS. Test: manual Test: atest ThemeOverlayControllerTest Fixes: 205140487 Change-Id: I2086b29ef9bf3bb6fb7d9ebba6e7b8db9392e459 Merged-In: I2086b29ef9bf3bb6fb7d9ebba6e7b8db9392e459 --- core/java/android/app/WallpaperManager.java | 8 ++++ .../theme/ThemeOverlayController.java | 9 ++++- .../theme/ThemeOverlayControllerTest.java | 38 +++++++++++++++++-- .../wallpaper/WallpaperManagerService.java | 13 +++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 11c01e61911c3..22b1c03fba508 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -240,6 +240,14 @@ public class WallpaperManager { */ public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID"; + /** + * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from + * a foreground app. + * @hide + */ + public static final String EXTRA_FROM_FOREGROUND_APP = + "android.service.wallpaper.extra.FROM_FOREGROUND_APP"; + // flags for which kind of wallpaper to act on /** @hide */ diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index cd5865f58c85c..426bc91a606d0 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -259,8 +259,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); reevaluateSystemTheme(true /* forceReload */); } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) { - mAcceptColorEvents = true; - Log.i(TAG, "Allowing color events again"); + if (intent.getBooleanExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, false)) { + mAcceptColorEvents = true; + Log.i(TAG, "Wallpaper changed, allowing color events again"); + } else { + Log.i(TAG, "Wallpaper changed from background app, " + + "keep deferring color events. Accepting: " + mAcceptColorEvents); + } } } }; diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index f89bbe898e35a..766471b9a695d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -152,7 +152,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } @Test - public void onWallpaperColorsChanged_setsTheme() { + public void onWallpaperColorsChanged_setsTheme_whenForeground() { // Should ask for a new theme when wallpaper colors change WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), Color.valueOf(Color.BLUE), null); @@ -180,12 +180,42 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { // But should change theme after changing wallpapers clearInvocations(mThemeOverlayApplier); - mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED)); + Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); + intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true); + mBroadcastReceiver.getValue().onReceive(null, intent); mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null), WallpaperManager.FLAG_SYSTEM); verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); } + @Test + public void onWallpaperColorsChanged_setsTheme_skipWhenBackground() { + // Should ask for a new theme when wallpaper colors change + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), + Color.valueOf(Color.BLUE), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + ArgumentCaptor> themeOverlays = + ArgumentCaptor.forClass(Map.class); + + verify(mThemeOverlayApplier) + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); + + // Assert that we received the colors that we were expecting + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) + .isEqualTo(new OverlayIdentifier("ffff0000")); + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR)) + .isEqualTo(new OverlayIdentifier("ffff0000")); + + // Should not change theme after changing wallpapers, if intent doesn't have + // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true. + clearInvocations(mThemeOverlayApplier); + mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED)); + mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), + null, null), WallpaperManager.FLAG_SYSTEM); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + } + @Test public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() { // Should ask for a new theme when wallpaper colors change @@ -455,7 +485,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { // Regression test: null events should not reset the internal state and allow colors to be // applied again. clearInvocations(mThemeOverlayApplier); - mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED)); + Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); + intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true); + mBroadcastReceiver.getValue().onReceive(null, intent); mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM); verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), any()); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index a51ed09790a42..2f353d19b5dfc 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wallpaper; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.WallpaperManager.COMMAND_REAPPLY; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; @@ -791,6 +792,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private final Context mContext; private final WindowManagerInternal mWindowManagerInternal; private final IPackageManager mIPackageManager; + private final ActivityManager mActivityManager; private final MyPackageMonitor mMonitor; private final AppOpsManager mAppOpsManager; @@ -939,6 +941,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ WallpaperColors primaryColors; + /** + * If the wallpaper was set from a foreground app (instead of from a background service). + */ + public boolean fromForegroundApp; + WallpaperConnection connection; long lastDiedTime; boolean wallpaperUpdating; @@ -1688,6 +1695,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mDisplayManager = mContext.getSystemService(DisplayManager.class); mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */); + mActivityManager = mContext.getSystemService(ActivityManager.class); mMonitor = new MyPackageMonitor(); mColorsChangedListeners = new SparseArray<>(); @@ -2648,6 +2656,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + final boolean fromForegroundApp = Binder.withCleanCallingIdentity(() -> + mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND); + synchronized (mLock) { if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which)); WallpaperData wallpaper; @@ -2670,6 +2681,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.imageWallpaperPending = true; wallpaper.whichPending = which; wallpaper.setComplete = completion; + wallpaper.fromForegroundApp = fromForegroundApp; wallpaper.cropHint.set(cropHint); wallpaper.allowBackup = allowBackup; } @@ -3052,6 +3064,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.callbacks.finishBroadcast(); final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); + intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, wallpaper.fromForegroundApp); mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); }