Merge "Add wallpaper dimming SystemAPI and implementation for handling dimming set by multiple applications."

This commit is contained in:
Vania Desmonda
2022-01-20 08:55:45 +00:00
committed by Android (Google) Code Review
14 changed files with 543 additions and 29 deletions

View File

@@ -297,6 +297,7 @@ package android {
field public static final String SET_SYSTEM_AUDIO_CAPTION = "android.permission.SET_SYSTEM_AUDIO_CAPTION";
field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
@@ -992,8 +993,10 @@ package android.app {
public class WallpaperManager {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
method public void setDisplayOffset(android.os.IBinder, int, int);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
}
}

View File

@@ -103,6 +103,16 @@ ProtectedMember: android.service.notification.NotificationAssistantService#attac
RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount():
Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount():
Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float):
Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float):
Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):

View File

@@ -204,4 +204,27 @@ interface IWallpaperManager {
* @hide
*/
void notifyGoingToSleep(int x, int y, in Bundle extras);
/**
* Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
* dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
*
* @hide
*/
oneway void setWallpaperDimAmount(float dimAmount);
/**
* Gets the current additional dim amount set on the wallpaper. 0f means no application has
* added any dimming on top of the system default dim amount.
*
* @hide
*/
float getWallpaperDimAmount();
/**
* Whether the lock screen wallpaper is different from the system wallpaper.
*
* @hide
*/
boolean lockScreenWallpaperExists();
}

View File

@@ -16,6 +16,7 @@
package android.app;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
import com.android.internal.graphics.ColorUtils;
@@ -44,6 +46,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@@ -173,6 +176,22 @@ public final class WallpaperColors implements Parcelable {
if (bitmap == null) {
throw new IllegalArgumentException("Bitmap can't be null");
}
return fromBitmap(bitmap, 0f /* dimAmount */);
}
/**
* Constructs {@link WallpaperColors} from a bitmap with dimming applied.
* <p>
* Main colors will be extracted from the bitmap with dimming taken into account when
* calculating dark hints.
*
* @param bitmap Source where to extract from.
* @param dimAmount Wallpaper dim amount
* @hide
*/
public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
@FloatRange (from = 0f, to = 1f) float dimAmount) {
Objects.requireNonNull(bitmap, "Bitmap can't be null");
final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
boolean shouldRecycle = false;
@@ -211,7 +230,7 @@ public final class WallpaperColors implements Parcelable {
}
int hints = calculateDarkHints(bitmap);
int hints = calculateDarkHints(bitmap, dimAmount);
if (shouldRecycle) {
bitmap.recycle();
@@ -507,13 +526,15 @@ public final class WallpaperColors implements Parcelable {
* Checks if image is bright and clean enough to support light text.
*
* @param source What to read.
* @param dimAmount How much wallpaper dim amount was applied.
* @return Whether image supports dark text or not.
*/
private static int calculateDarkHints(Bitmap source) {
private static int calculateDarkHints(Bitmap source, float dimAmount) {
if (source == null) {
return 0;
}
dimAmount = MathUtils.saturate(dimAmount);
int[] pixels = new int[source.getWidth() * source.getHeight()];
double totalLuminance = 0;
final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
@@ -521,24 +542,37 @@ public final class WallpaperColors implements Parcelable {
source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
source.getWidth(), source.getHeight());
// Create a new black layer with dimAmount as the alpha to be accounted for when computing
// the luminance.
int dimmingLayerAlpha = (int) (255 * dimAmount);
int blackTransparent = ColorUtils.setAlphaComponent(Color.BLACK, dimmingLayerAlpha);
// This bitmap was already resized to fit the maximum allowed area.
// Let's just loop through the pixels, no sweat!
float[] tmpHsl = new float[3];
for (int i = 0; i < pixels.length; i++) {
ColorUtils.colorToHSL(pixels[i], tmpHsl);
final float luminance = tmpHsl[2];
final int alpha = Color.alpha(pixels[i]);
int pixelColor = pixels[i];
ColorUtils.colorToHSL(pixelColor, tmpHsl);
final int alpha = Color.alpha(pixelColor);
// Apply composite colors where the foreground is a black layer with an alpha value of
// the dim amount and the background is the wallpaper pixel color.
int compositeColors = ColorUtils.compositeColors(blackTransparent, pixelColor);
// Calculate the adjusted luminance of the dimmed wallpaper pixel color.
double adjustedLuminance = ColorUtils.calculateLuminance(compositeColors);
// Make sure we don't have a dark pixel mass that will
// make text illegible.
final boolean satisfiesTextContrast = ContrastColorUtil
.calculateContrast(pixels[i], Color.BLACK) > DARK_PIXEL_CONTRAST;
.calculateContrast(pixelColor, Color.BLACK) > DARK_PIXEL_CONTRAST;
if (!satisfiesTextContrast && alpha != 0) {
darkPixels++;
if (DEBUG_DARK_PIXELS) {
pixels[i] = Color.RED;
}
}
totalLuminance += luminance;
totalLuminance += adjustedLuminance;
}
int hints = 0;

View File

@@ -16,6 +16,7 @@
package android.app;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -71,6 +72,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
import android.view.Display;
import android.view.WindowManagerGlobal;
@@ -1993,6 +1995,63 @@ public class WallpaperManager {
return setWallpaperComponent(name, mContext.getUserId());
}
/**
* Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
* dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
sGlobals.mService.setWallpaperDimAmount(MathUtils.saturate(dimAmount));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Gets the current additional dim amount set on the wallpaper. 0f means no application has
* added any dimming on top of the system default dim amount.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
public float getWallpaperDimAmount() {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
return sGlobals.mService.getWallpaperDimAmount();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Whether the lock screen wallpaper is different from the system wallpaper.
*
* @hide
*/
public boolean lockScreenWallpaperExists() {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
return sGlobals.mService.lockScreenWallpaperExists();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set the live wallpaper.
*

View File

@@ -46,4 +46,5 @@ interface IWallpaperEngine {
oneway void removeLocalColorsAreas(in List<RectF> regions);
oneway void addLocalColorsAreas(in List<RectF> regions);
SurfaceControl mirrorSurfaceControl();
oneway void applyDimming(float dimAmount);
}

View File

@@ -26,6 +26,7 @@ import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.animation.ValueAnimator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -159,6 +160,7 @@ public abstract class WallpaperService extends Service {
private static final int MSG_ZOOM = 10100;
private static final int MSG_SCALE_PREVIEW = 10110;
private static final int MSG_REPORT_SHOWN = 10150;
private static final int MSG_UPDATE_DIMMING = 10200;
private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY);
@@ -167,6 +169,8 @@ public abstract class WallpaperService extends Service {
private static final boolean ENABLE_WALLPAPER_DIMMING =
SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
private static final long DIMMING_ANIMATION_DURATION_MS = 300L;
private final ArrayList<Engine> mActiveEngines
= new ArrayList<Engine>();
@@ -221,6 +225,9 @@ public abstract class WallpaperService extends Service {
boolean mOffsetsChanged;
boolean mFixedSizeAllowed;
boolean mShouldDim;
// Whether the wallpaper should be dimmed by default (when no additional dimming is applied)
// based on its color hints
boolean mShouldDimByDefault;
int mWidth;
int mHeight;
int mFormat;
@@ -272,6 +279,8 @@ public abstract class WallpaperService extends Service {
private Context mDisplayContext;
private int mDisplayState;
private float mWallpaperDimAmount = 0.05f;
private float mPreviousWallpaperDimAmount = mWallpaperDimAmount;
private float mDefaultDimAmount = mWallpaperDimAmount;
SurfaceControl mSurfaceControl = new SurfaceControl();
SurfaceControl mBbqSurfaceControl;
@@ -861,15 +870,34 @@ public abstract class WallpaperService extends Service {
return;
}
int colorHints = colors.getColorHints();
boolean shouldDim = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
mShouldDimByDefault = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
&& (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) == 0);
if (shouldDim != mShouldDim) {
mShouldDim = shouldDim;
// If default dimming value changes and no additional dimming is applied
if (mShouldDimByDefault != mShouldDim && mWallpaperDimAmount == 0f) {
mShouldDim = mShouldDimByDefault;
updateSurfaceDimming();
updateSurface(false, false, true);
}
}
/**
* Update the dim amount of the wallpaper by updating the surface.
*
* @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper.
*/
private void updateWallpaperDimming(float dimAmount) {
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
// Custom dim amount cannot be less than the default dim amount.
mWallpaperDimAmount = Math.max(mDefaultDimAmount, dimAmount);
// If dim amount is 0f (additional dimming is removed), then the wallpaper should dim
// based on its default wallpaper color hints.
mShouldDim = dimAmount != 0f || mShouldDimByDefault;
updateSurfaceDimming();
updateSurface(false, false, true);
}
private void updateSurfaceDimming() {
if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) {
return;
@@ -878,9 +906,21 @@ public abstract class WallpaperService extends Service {
// preview mode.
if (!isPreview() && mShouldDim) {
Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
new SurfaceControl.Transaction()
.setAlpha(mBbqSurfaceControl, 1 - mWallpaperDimAmount)
.apply();
SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction();
// Animate dimming to gradually change the wallpaper alpha from the previous
// dim amount to the new amount only if the dim amount changed.
ValueAnimator animator = ValueAnimator.ofFloat(
mPreviousWallpaperDimAmount, mWallpaperDimAmount);
animator.setDuration(mPreviousWallpaperDimAmount == mWallpaperDimAmount
? 0 : DIMMING_ANIMATION_DURATION_MS);
animator.addUpdateListener((ValueAnimator va) -> {
final float dimValue = (float) va.getAnimatedValue();
surfaceControl
.setAlpha(mBbqSurfaceControl, 1 - dimValue)
.apply();
});
animator.start();
} else {
Log.v(TAG, "Setting wallpaper dimming: " + 0);
new SurfaceControl.Transaction()
@@ -1332,8 +1372,10 @@ public abstract class WallpaperService extends Service {
// Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
mDisplayContext = createDisplayContext(mDisplay)
.createWindowContext(TYPE_WALLPAPER, null /* options */);
mWallpaperDimAmount = mDisplayContext.getResources().getFloat(
mDefaultDimAmount = mDisplayContext.getResources().getFloat(
com.android.internal.R.dimen.config_wallpaperDimAmount);
mWallpaperDimAmount = mDefaultDimAmount;
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1647,7 +1689,7 @@ public abstract class WallpaperService extends Service {
Log.e(TAG, "Error creating page local color bitmap", e);
continue;
}
WallpaperColors color = WallpaperColors.fromBitmap(target);
WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
target.recycle();
WallpaperColors currentColor = page.getColors(area);
@@ -2175,6 +2217,12 @@ public abstract class WallpaperService extends Service {
mDetached.set(true);
}
public void applyDimming(float dimAmount) throws RemoteException {
Message msg = mCaller.obtainMessageI(MSG_UPDATE_DIMMING,
Float.floatToIntBits(dimAmount));
mCaller.sendMessage(msg);
}
public void scalePreview(Rect position) {
Message msg = mCaller.obtainMessageO(MSG_SCALE_PREVIEW, position);
mCaller.sendMessage(msg);
@@ -2245,6 +2293,9 @@ public abstract class WallpaperService extends Service {
case MSG_ZOOM:
mEngine.setZoom(Float.intBitsToFloat(message.arg1));
break;
case MSG_UPDATE_DIMMING:
mEngine.updateWallpaperDimming(Float.intBitsToFloat(message.arg1));
break;
case MSG_SCALE_PREVIEW:
mEngine.scalePreview((Rect) message.obj);
break;

View File

@@ -62,7 +62,10 @@ public final class ColorUtils {
return Color.argb(a, r, g, b);
}
private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
/**
* Returns the composite alpha of the given foreground and background alpha.
*/
public static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
}

View File

@@ -5128,6 +5128,11 @@
<permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to set the wallpaper dim amount.
@hide. -->
<permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows applications to read dream settings and dream state.
@hide -->
<permission android:name="android.permission.READ_DREAM_STATE"

View File

@@ -182,6 +182,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
private float mAdditionalScrimBehindAlphaKeyguard = 0f;
// Combined scrim behind keyguard alpha of default scrim + additional scrim
// (if wallpaper dimming is applied).
private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
private final float mDefaultScrimAlpha;
@@ -437,7 +440,35 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
return mState;
}
protected void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
/**
* Sets the additional scrim behind alpha keyguard that would be blended with the default scrim
* by applying alpha composition on both values.
*
* @param additionalScrimAlpha alpha value of additional scrim behind alpha keyguard.
*/
protected void setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha) {
mAdditionalScrimBehindAlphaKeyguard = additionalScrimAlpha;
}
/**
* Applies alpha composition to the default scrim behind alpha keyguard and the additional
* scrim alpha, and sets this value to the scrim behind alpha keyguard.
* This is used to apply additional keyguard dimming on top of the default scrim alpha value.
*/
protected void applyCompositeAlphaOnScrimBehindKeyguard() {
int compositeAlpha = ColorUtils.compositeAlpha(
(int) (255 * mAdditionalScrimBehindAlphaKeyguard),
(int) (255 * KEYGUARD_SCRIM_ALPHA));
float keyguardScrimAlpha = (float) compositeAlpha / 255;
setScrimBehindValues(keyguardScrimAlpha);
}
/**
* Sets the scrim behind alpha keyguard values. This is how much the keyguard will be dimmed.
*
* @param scrimBehindAlphaKeyguard alpha value of the scrim behind
*/
private void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
@@ -732,7 +763,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
// We're unoccluding the keyguard and don't want to have a bright flash.
mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
mNotificationsAlpha = mScrimBehindAlphaKeyguard;
mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
}
}

View File

@@ -3168,16 +3168,29 @@ public class StatusBar extends CoreStartable implements
* Switches theme from light to dark and vice-versa.
*/
protected void updateTheme() {
// Set additional scrim only if the lock and system wallpaper are different to prevent
// applying the dimming effect twice.
mUiBgExecutor.execute(() -> {
float dimAmount = 0f;
if (mWallpaperManager.lockScreenWallpaperExists()) {
dimAmount = mWallpaperManager.getWallpaperDimAmount();
}
final float scrimDimAmount = dimAmount;
mMainExecutor.execute(() -> {
mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount);
mScrimController.applyCompositeAlphaOnScrimBehindKeyguard();
});
});
// Lock wallpaper defines the color of the majority of the views, hence we'll use it
// to set our default theme.
final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper
: R.style.Theme_SystemUI;
if (mContext.getThemeResId() == themeResId) {
return;
if (mContext.getThemeResId() != themeResId) {
mContext.setTheme(themeResId);
mConfigurationController.notifyThemeChanged();
}
mContext.setTheme(themeResId);
mConfigurationController.notifyThemeChanged();
}
private void updateDozingState() {

View File

@@ -81,7 +81,9 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -92,6 +94,7 @@ import android.service.wallpaper.IWallpaperService;
import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -420,7 +423,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
}
needsExtraction = wallpaper.primaryColors == null;
needsExtraction = wallpaper.primaryColors == null || wallpaper.mIsColorExtractedFromDim;
}
if (needsExtraction) {
@@ -491,12 +494,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
String cropFile = null;
boolean defaultImageWallpaper = false;
int wallpaperId;
float dimAmount;
synchronized (mLock) {
wallpaper.mIsColorExtractedFromDim = false;
}
if (wallpaper.equals(mFallbackWallpaper)) {
synchronized (mLock) {
if (mFallbackWallpaper.primaryColors != null) return;
}
final WallpaperColors colors = extractDefaultImageWallpaperColors();
final WallpaperColors colors = extractDefaultImageWallpaperColors(wallpaper);
synchronized (mLock) {
mFallbackWallpaper.primaryColors = colors;
}
@@ -513,18 +521,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
defaultImageWallpaper = true;
}
wallpaperId = wallpaper.wallpaperId;
dimAmount = wallpaper.mWallpaperDimAmount;
}
WallpaperColors colors = null;
if (cropFile != null) {
Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
if (bitmap != null) {
colors = WallpaperColors.fromBitmap(bitmap);
colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
bitmap.recycle();
}
} else if (defaultImageWallpaper) {
// There is no crop and source file because this is default image wallpaper.
colors = extractDefaultImageWallpaperColors();
colors = extractDefaultImageWallpaperColors(wallpaper);
}
if (colors == null) {
@@ -544,11 +553,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
private WallpaperColors extractDefaultImageWallpaperColors() {
private WallpaperColors extractDefaultImageWallpaperColors(WallpaperData wallpaper) {
if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
float dimAmount;
synchronized (mLock) {
if (mCacheDefaultImageWallpaperColors != null) return mCacheDefaultImageWallpaperColors;
dimAmount = wallpaper.mWallpaperDimAmount;
}
WallpaperColors colors = null;
@@ -561,7 +572,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
if (bitmap != null) {
colors = WallpaperColors.fromBitmap(bitmap);
colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
bitmap.recycle();
}
} catch (OutOfMemoryError e) {
@@ -947,6 +958,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean wallpaperUpdating;
WallpaperObserver wallpaperObserver;
/**
* The dim amount to be applied to the wallpaper.
*/
float mWallpaperDimAmount = 0.0f;
/**
* A map to keep track of the dimming set by different applications. The key is the calling
* UID and the value is the dim amount.
*/
ArrayMap<Integer, Float> mUidToDimAmount = new ArrayMap<>();
/**
* Whether we need to extract the wallpaper colors again to calculate the dark hints
* after dimming is applied.
*/
boolean mIsColorExtractedFromDim;
/**
* List of callbacks registered they should each be notified when the wallpaper is changed.
*/
@@ -1487,6 +1515,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, "Failed to register local colors areas", e);
}
}
if (mWallpaper.mWallpaperDimAmount != 0f) {
try {
connector.mEngine.applyDimming(mWallpaper.mWallpaperDimAmount);
notifyWallpaperColorsChanged(mWallpaper, FLAG_SYSTEM);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to dim wallpaper", e);
}
}
}
}
@@ -2536,6 +2573,98 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (purgeAreas.size() > 0) engine.removeLocalColorsAreas(purgeAreas);
}
/**
* Returns true if the lock screen wallpaper exists (different wallpaper from the system)
*/
@Override
public boolean lockScreenWallpaperExists() {
synchronized (mLock) {
return mLockWallpaperMap.get(mCurrentUserId) != null;
}
}
/**
* Sets wallpaper dim amount for the calling UID. This only applies to FLAG_SYSTEM wallpaper as
* the lock screen does not have a wallpaper component, so we use mWallpaperMap.
*
* @param dimAmount Dim amount which would be blended with the system default dimming.
*/
@Override
public void setWallpaperDimAmount(float dimAmount) throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
if (dimAmount == 0.0f) {
wallpaper.mUidToDimAmount.remove(uid);
} else {
wallpaper.mUidToDimAmount.put(uid, dimAmount);
}
float maxDimAmount = getHighestDimAmountFromMap(wallpaper.mUidToDimAmount);
wallpaper.mWallpaperDimAmount = maxDimAmount;
// Also set the dim amount to the lock screen wallpaper if the lock and home screen
// do not share the same wallpaper
if (lockWallpaper != null) {
lockWallpaper.mWallpaperDimAmount = maxDimAmount;
}
if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(connector -> {
if (connector.mEngine != null) {
try {
connector.mEngine.applyDimming(maxDimAmount);
} catch (RemoteException e) {
Slog.w(TAG,
"Can't apply dimming on wallpaper display connector", e);
}
}
});
// Need to extract colors again to re-calculate dark hints after
// applying dimming.
wallpaper.mIsColorExtractedFromDim = true;
notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
if (lockWallpaper != null) {
lockWallpaper.mIsColorExtractedFromDim = true;
notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
}
saveSettingsLocked(wallpaper.userId);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public float getWallpaperDimAmount() {
checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
synchronized (mLock) {
WallpaperData data = mWallpaperMap.get(mCurrentUserId);
return data.mWallpaperDimAmount;
}
}
/**
* Gets the highest dim amount among all the calling UIDs that set the wallpaper dim amount.
* Return 0f as default value to indicate no application has dimmed the wallpaper.
*
* @param uidToDimAmountMap Map of UIDs to dim amounts
*/
private float getHighestDimAmountFromMap(ArrayMap<Integer, Float> uidToDimAmountMap) {
float maxDimAmount = 0.0f;
for (Map.Entry<Integer, Float> entry : uidToDimAmountMap.entrySet()) {
if (entry.getValue() > maxDimAmount) {
maxDimAmount = entry.getValue();
}
}
return maxDimAmount;
}
@Override
public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
throws RemoteException {
@@ -2562,7 +2691,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (wallpaperData == null) {
return null;
}
shouldExtract = wallpaperData.primaryColors == null;
shouldExtract = wallpaperData.primaryColors == null
|| wallpaperData.mIsColorExtractedFromDim;
}
if (shouldExtract) {
@@ -2664,6 +2794,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
lockWP.cropHint.set(sysWP.cropHint);
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
lockWP.mWallpaperDimAmount = sysWP.mWallpaperDimAmount;
// Migrate the bitmap files outright; no need to copy
try {
@@ -3191,6 +3322,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
}
out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
int dimAmountsCount = wallpaper.mUidToDimAmount.size();
out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
if (dimAmountsCount > 0) {
int index = 0;
for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
out.attributeInt(null, "dimUID" + index, entry.getKey());
out.attributeFloat(null, "dimValue" + index, entry.getValue());
index++;
}
}
if (wallpaper.primaryColors != null) {
int colorsCount = wallpaper.primaryColors.getMainColors().size();
out.attributeInt(null, "colorsCount", colorsCount);
@@ -3267,6 +3410,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return parser.getAttributeInt(null, name, defValue);
}
private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
return parser.getAttributeFloat(null, name, defValue);
}
/**
* Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could
* happen during user switch. The async user switch observer may not have received
@@ -3471,6 +3618,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
if (dimAmountsCount > 0) {
ArrayMap<Integer, Float> allDimAmounts = new ArrayMap<>(dimAmountsCount);
for (int i = 0; i < dimAmountsCount; i++) {
int uid = getAttributeInt(parser, "dimUID" + i, 0);
float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
allDimAmounts.put(uid, dimValue);
}
wallpaper.mUidToDimAmount = allDimAmounts;
}
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
int allColorsCount = getAttributeInt(parser, "allColorsCount", 0);
if (allColorsCount > 0) {
@@ -3637,6 +3795,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return false;
}
@Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
new WallpaperManagerShellCommand(WallpaperManagerService.this).exec(this, in, out, err,
args, callback, resultReceiver);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -3664,6 +3830,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
pw.print(" isColorExtracted="); pw.println(wallpaper.mIsColorExtractedFromDim);
pw.println(" mUidToDimAmount:");
for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
pw.print(" UID="); pw.print(entry.getKey());
pw.print(" dimAmount="); pw.println(entry.getValue());
}
if (wallpaper.connection != null) {
WallpaperConnection conn = wallpaper.connection;
pw.print(" Wallpaper connection ");
@@ -3695,6 +3868,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
}
pw.println("Fallback wallpaper state:");
pw.print(" User "); pw.print(mFallbackWallpaper.userId);

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wallpaper;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.util.Log;
import java.io.PrintWriter;
/**
* Shell Command class to run adb commands on the wallpaper service
*/
public class WallpaperManagerShellCommand extends ShellCommand {
private static final String TAG = "WallpaperManagerShellCommand";
private final WallpaperManagerService mService;
public WallpaperManagerShellCommand(WallpaperManagerService service) {
mService = service;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
onHelp();
return 1;
}
switch(cmd) {
case "set-dim-amount":
return setWallpaperDimAmount();
case "get-dim-amount":
return getWallpaperDimAmount();
case "-h":
case "help":
onHelp();
return 0;
default:
return handleDefaultCommands(cmd);
}
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println("Wallpaper manager commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
pw.println(" set-dim-amount DIMMING");
pw.println(" Sets the current dimming value to DIMMING (a number between 0 and 1).");
pw.println();
pw.println(" get-dim-amount");
pw.println(" Get the current wallpaper dim amount.");
}
/**
* Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
* dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
*/
private int setWallpaperDimAmount() {
float dimAmount = Float.parseFloat(getNextArgRequired());
try {
mService.setWallpaperDimAmount(dimAmount);
} catch (RemoteException e) {
Log.e(TAG, "Can't set wallpaper dim amount");
}
getOutPrintWriter().println("Dimming the wallpaper to: " + dimAmount);
return 0;
}
/**
* Gets the current additional dim amount set on the wallpaper. 0f means no application has
* added any dimming on top of the system default dim amount.
*/
private int getWallpaperDimAmount() {
float dimAmount = mService.getWallpaperDimAmount();
getOutPrintWriter().println("The current wallpaper dim amount is: " + dimAmount);
return 0;
}
}

View File

@@ -153,6 +153,9 @@ public class WallpaperManagerServiceTests {
sContext.getTestablePermissions().setPermission(
android.Manifest.permission.SET_WALLPAPER,
PackageManager.PERMISSION_GRANTED);
sContext.getTestablePermissions().setPermission(
android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT,
PackageManager.PERMISSION_GRANTED);
doNothing().when(sContext).sendBroadcastAsUser(any(), any());
//Wallpaper components
@@ -433,6 +436,15 @@ public class WallpaperManagerServiceTests {
assertTrue(timestamps[1] > timestamps[0]);
}
@Test
public void testSetWallpaperDimAmount() throws RemoteException {
mService.switchUser(USER_SYSTEM, null);
float dimAmount = 0.7f;
mService.setWallpaperDimAmount(dimAmount);
assertEquals("Getting dim amount should match after setting the dim amount",
mService.getWallpaperDimAmount(), dimAmount, 0.0);
}
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {