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
This commit is contained in:
Lucas Dupin
2021-11-05 17:30:49 -07:00
parent 3379ab4158
commit 6fd464985a
4 changed files with 63 additions and 5 deletions

View File

@@ -240,6 +240,14 @@ public class WallpaperManager {
*/ */
public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID"; 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 // flags for which kind of wallpaper to act on
/** @hide */ /** @hide */

View File

@@ -259,8 +259,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
reevaluateSystemTheme(true /* forceReload */); reevaluateSystemTheme(true /* forceReload */);
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) { } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
if (intent.getBooleanExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, false)) {
mAcceptColorEvents = true; mAcceptColorEvents = true;
Log.i(TAG, "Allowing color events again"); Log.i(TAG, "Wallpaper changed, allowing color events again");
} else {
Log.i(TAG, "Wallpaper changed from background app, "
+ "keep deferring color events. Accepting: " + mAcceptColorEvents);
}
} }
} }
}; };

View File

@@ -152,7 +152,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
} }
@Test @Test
public void onWallpaperColorsChanged_setsTheme() { public void onWallpaperColorsChanged_setsTheme_whenForeground() {
// Should ask for a new theme when wallpaper colors change // Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null); Color.valueOf(Color.BLUE), null);
@@ -180,12 +180,42 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
// But should change theme after changing wallpapers // But should change theme after changing wallpapers
clearInvocations(mThemeOverlayApplier); 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), mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
null, null), WallpaperManager.FLAG_SYSTEM); null, null), WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); 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<Map<String, OverlayIdentifier>> 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 @Test
public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() { public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
// Should ask for a new theme when wallpaper colors change // 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 // Regression test: null events should not reset the internal state and allow colors to be
// applied again. // applied again.
clearInvocations(mThemeOverlayApplier); 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); mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
any()); any());

View File

@@ -17,6 +17,7 @@
package com.android.server.wallpaper; package com.android.server.wallpaper;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 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.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -791,6 +792,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private final Context mContext; private final Context mContext;
private final WindowManagerInternal mWindowManagerInternal; private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager; private final IPackageManager mIPackageManager;
private final ActivityManager mActivityManager;
private final MyPackageMonitor mMonitor; private final MyPackageMonitor mMonitor;
private final AppOpsManager mAppOpsManager; private final AppOpsManager mAppOpsManager;
@@ -939,6 +941,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*/ */
WallpaperColors primaryColors; WallpaperColors primaryColors;
/**
* If the wallpaper was set from a foreground app (instead of from a background service).
*/
public boolean fromForegroundApp;
WallpaperConnection connection; WallpaperConnection connection;
long lastDiedTime; long lastDiedTime;
boolean wallpaperUpdating; boolean wallpaperUpdating;
@@ -1688,6 +1695,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mDisplayManager = mContext.getSystemService(DisplayManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */); mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
mActivityManager = mContext.getSystemService(ActivityManager.class);
mMonitor = new MyPackageMonitor(); mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>(); 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) { synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which)); if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper; WallpaperData wallpaper;
@@ -2670,6 +2681,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.imageWallpaperPending = true; wallpaper.imageWallpaperPending = true;
wallpaper.whichPending = which; wallpaper.whichPending = which;
wallpaper.setComplete = completion; wallpaper.setComplete = completion;
wallpaper.fromForegroundApp = fromForegroundApp;
wallpaper.cropHint.set(cropHint); wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup; wallpaper.allowBackup = allowBackup;
} }
@@ -3052,6 +3064,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.callbacks.finishBroadcast(); wallpaper.callbacks.finishBroadcast();
final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, wallpaper.fromForegroundApp);
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
} }