Loading core/java/android/app/IWallpaperManager.aidl +37 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; Loading @@ -26,6 +27,8 @@ import android.app.WallpaperInfo; import android.content.ComponentName; import android.app.WallpaperColors; import java.util.List; /** @hide */ interface IWallpaperManager { Loading @@ -39,15 +42,21 @@ interface IWallpaperManager { * FLAG_SET_SYSTEM * FLAG_SET_LOCK * * A 'null' cropHint rectangle is explicitly permitted as a sentinel for "whatever * the source image's bounding rect is." * 'screenOrientations' and 'crops' define how the wallpaper will be positioned for * different screen orientations. If some screen orientations are missing, crops for these * orientations will be added by the system. * * If 'screenOrientations' is null, 'crops' can be null or a singleton list. The system will * fit the provided crop (or the whole image, if 'crops' is 'null') for the current device * orientation, and add crops for the missing orientations. * * The completion callback's "onWallpaperChanged()" method is invoked when the * new wallpaper content is ready to display. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER)") ParcelFileDescriptor setWallpaper(String name, in String callingPackage, in Rect cropHint, boolean allowBackup, out Bundle extras, int which, IWallpaperManagerCallback completion, int userId); in int[] screenOrientations, in List<Rect> crops, boolean allowBackup, out Bundle extras, int which, IWallpaperManagerCallback completion, int userId); /** * Set the live wallpaper. Loading Loading @@ -77,6 +86,30 @@ interface IWallpaperManager { IWallpaperManagerCallback cb, int which, out Bundle outParams, int userId, boolean getCropped); /** * For a given user and a list of display sizes, get a list of Rect representing the * area of the current wallpaper that is displayed for each display size. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL)") @SuppressWarnings(value={"untyped-collection"}) List getBitmapCrops(in List<Point> displaySizes, int which, boolean originalBitmap, int userId); /** * Return how a bitmap of a given size would be cropped for a given list of display sizes when * set with the given suggested crops. * @hide */ @SuppressWarnings(value={"untyped-collection"}) List getFutureBitmapCrops(in Point bitmapSize, in List<Point> displaySizes, in int[] screenOrientations, in List<Rect> crops); /** * Return how a bitmap of a given size would be cropped when set with the given suggested crops. * @hide */ @SuppressWarnings(value={"untyped-collection"}) Rect getBitmapCrop(in Point bitmapSize, in int[] screenOrientations, in List<Rect> crops); /** * Retrieve the given user's current wallpaper ID of the given kind. */ Loading Loading @@ -245,11 +278,4 @@ interface IWallpaperManager { * @hide */ boolean isStaticWallpaper(int which); /** * Temporary method for project b/270726737. * Return true if the wallpaper supports different crops for different display dimensions. * @hide */ boolean isMultiCropEnabled(); } core/java/android/app/WallpaperManager.java +319 −14 Original line number Diff line number Diff line Loading @@ -18,11 +18,14 @@ package android.app; import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_WALLPAPER_INTERNAL; import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static com.android.window.flags.Flags.FLAG_MULTI_CROP; import static com.android.window.flags.Flags.multiCrop; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; Loading Loading @@ -58,6 +61,7 @@ import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; Loading @@ -84,6 +88,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.Pair; import android.util.SparseArray; import android.view.Display; import android.view.WindowManagerGlobal; Loading @@ -104,6 +109,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading Loading @@ -289,6 +295,79 @@ public class WallpaperManager { public static final String EXTRA_FROM_FOREGROUND_APP = "android.service.wallpaper.extra.FROM_FOREGROUND_APP"; /** * The different screen orientations. {@link #getOrientation} provides their exact definition. * This is only used internally by the framework and the WallpaperBackupAgent. * @hide */ @IntDef(value = { ORIENTATION_UNKNOWN, PORTRAIT, LANDSCAPE, SQUARE_PORTRAIT, SQUARE_LANDSCAPE, }) @Retention(RetentionPolicy.SOURCE) public @interface ScreenOrientation {} /** * @hide */ public static final int ORIENTATION_UNKNOWN = -1; /** * Portrait orientation of most screens * @hide */ public static final int PORTRAIT = 0; /** * Landscape orientation of most screens * @hide */ public static final int LANDSCAPE = 1; /** * Portrait orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ public static final int SQUARE_PORTRAIT = 2; /** * Landscape orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ public static final int SQUARE_LANDSCAPE = 3; /** * Converts a (width, height) screen size to a {@link ScreenOrientation}. * @param screenSize the dimensions of a screen * @return the corresponding {@link ScreenOrientation}. * @hide */ public static @ScreenOrientation int getOrientation(Point screenSize) { float ratio = ((float) screenSize.x) / screenSize.y; // ratios between 3/4 and 4/3 are considered square return ratio >= 4 / 3f ? LANDSCAPE : ratio > 1f ? SQUARE_LANDSCAPE : ratio > 3 / 4f ? SQUARE_PORTRAIT : PORTRAIT; } /** * Get the 90° rotation of a given orientation * @hide */ public static @ScreenOrientation int getRotatedOrientation(@ScreenOrientation int orientation) { switch (orientation) { case PORTRAIT: return LANDSCAPE; case LANDSCAPE: return PORTRAIT; case SQUARE_PORTRAIT: return SQUARE_LANDSCAPE; case SQUARE_LANDSCAPE: return SQUARE_PORTRAIT; default: return ORIENTATION_UNKNOWN; } } // flags for which kind of wallpaper to act on /** @hide */ Loading Loading @@ -867,15 +946,8 @@ public class WallpaperManager { * @hide */ public static boolean isMultiCropEnabled() { if (sGlobals == null) { sIsMultiCropEnabled = multiCrop(); } if (sIsMultiCropEnabled == null) { try { sIsMultiCropEnabled = sGlobals.mService.isMultiCropEnabled(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } sIsMultiCropEnabled = multiCrop(); } return sIsMultiCropEnabled; } Loading Loading @@ -1501,6 +1573,99 @@ public class WallpaperManager { mContext.getUserId()); } /** * For the current user, given a list of display sizes, return a list of rectangles representing * the area of the current wallpaper that would be shown for each of these sizes. * * @param displaySizes the display sizes. * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. * @param originalBitmap If true, return areas relative to the original bitmap. * If false, return areas relative to the cropped bitmap. * @return A List of Rect where the Rect is within the cropped/original bitmap, and corresponds * to what is displayed. The Rect may have a larger width/height ratio than the screen * due to parallax. Return {@code null} if the wallpaper is not an ImageWallpaper. * Also return {@code null} when called with which={@link #FLAG_LOCK} if there is a * shared home + lock wallpaper. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(READ_WALLPAPER_INTERNAL) @Nullable public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes, @SetWallpaperFlags int which, boolean originalBitmap) { checkExactlyOneWallpaperFlagSet(which); try { return sGlobals.mService.getBitmapCrops(displaySizes, which, originalBitmap, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * For preview purposes. * Return how a bitmap of a given size would be cropped for a given list of display sizes, if * it was set as wallpaper via {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. * * @return A List of Rect where the Rect is within the bitmap, and corresponds to what is * displayed for each display size. The Rect may have a larger width/height ratio than * the display due to parallax. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @Nullable public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes, @Nullable Map<Point, Rect> cropHints) { try { if (cropHints == null) cropHints = Map.of(); Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); int[] screenOrientations = entries.stream().mapToInt(entry -> getOrientation(entry.getKey())).toArray(); List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); return sGlobals.mService.getFutureBitmapCrops(bitmapSize, displaySizes, screenOrientations, crops); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * For preview purposes. * Compute the wallpaper colors of the given bitmap, if it was set as wallpaper via * {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. * Return {@code null} if an error occurred and the colors could not be computed. * * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) @Nullable public WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap, @Nullable Map<Point, Rect> cropHints) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } try { if (cropHints == null) cropHints = Map.of(); Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); int[] screenOrientations = entries.stream().mapToInt(entry -> getOrientation(entry.getKey())).toArray(); List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); Point bitmapSize = new Point(bitmap.getWidth(), bitmap.getHeight()); Rect crop = sGlobals.mService.getBitmapCrop(bitmapSize, screenOrientations, crops); float dimAmount = getWallpaperDimAmount(); Bitmap croppedBitmap = Bitmap.createBitmap( bitmap, crop.left, crop.top, crop.width(), crop.height()); WallpaperColors result = WallpaperColors.fromBitmap(croppedBitmap, dimAmount); return result; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * <strong> Important note: </strong> * <ul> Loading Loading @@ -1971,7 +2136,7 @@ public class WallpaperManager { /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), mContext.getOpPackageName(), null, false, result, which, completion, mContext.getOpPackageName(), null, null, false, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; Loading Loading @@ -2089,6 +2254,11 @@ public class WallpaperManager { public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which, int userId) throws IOException { if (multiCrop()) { SparseArray<Rect> cropMap = new SparseArray<>(); if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); return setBitmapWithCrops(fullImage, cropMap, allowBackup, which, userId); } validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2096,9 +2266,69 @@ public class WallpaperManager { } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), null, crops, allowBackup, result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Version of setBitmap that defines how the wallpaper will be positioned for different * display sizes. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from screen dimensions to a sub-region of the image to display for those * dimensions. The {@code Rect} sub-region may have a larger width/height ratio * than the screen dimensions to apply a horizontal parallax effect. If the * map is empty or some entries are missing, the system will apply a default * strategy to position the wallpaper for any unspecified screen dimensions. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { SparseArray<Rect> crops = new SparseArray<>(); cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); return setBitmapWithCrops(fullImage, crops, allowBackup, which, mContext.getUserId()); } @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) private int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which, int userId) throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } int size = cropHints.size(); int[] screenOrientations = new int[size]; List<Rect> crops = new ArrayList<>(size); for (int i = 0; i < size; i++) { screenOrientations[i] = cropHints.keyAt(i); Rect cropHint = cropHints.valueAt(i); validateRect(cropHint); crops.add(cropHint); } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, mContext.getOpPackageName(), screenOrientations, crops, allowBackup, result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; Loading Loading @@ -2214,6 +2444,11 @@ public class WallpaperManager { public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { if (multiCrop()) { SparseArray<Rect> cropMap = new SparseArray<>(); if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); return setStreamWithCrops(bitmapData, cropMap, allowBackup, which); } validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2221,10 +2456,11 @@ public class WallpaperManager { } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, result, which, completion, mContext.getUserId()); mContext.getOpPackageName(), null, crops, allowBackup, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; try { Loading @@ -2243,6 +2479,75 @@ public class WallpaperManager { return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Version of setStream that defines how the wallpaper will be positioned for different * display sizes. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from screen dimensions to a sub-region of the image to display for those * dimensions. The {@code Rect} sub-region may have a larger width/height ratio * than the screen dimensions to apply a horizontal parallax effect. If the * map is empty or some entries are missing, the system will apply a default * strategy to position the wallpaper for any unspecified screen dimensions. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(InputStream bitmapData, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { SparseArray<Rect> crops = new SparseArray<>(); cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); return setStreamWithCrops(bitmapData, crops, allowBackup, which); } /** * Similar to {@link #setStreamWithCrops(InputStream, Map, boolean, int)}, but using * {@link ScreenOrientation} as keys of the cropHints map. Used for backup & restore, since * WallpaperBackupAgent stores orientations rather than the exact display size. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to display * for that screen orientation. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } int size = cropHints.size(); int[] screenOrientations = new int[size]; List<Rect> crops = new ArrayList<>(size); for (int i = 0; i < size; i++) { screenOrientations[i] = cropHints.keyAt(i); Rect cropHint = cropHints.valueAt(i); validateRect(cropHint); crops.add(cropHint); } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), screenOrientations, crops, allowBackup, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(bitmapData, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Return whether any users are currently set to use the wallpaper * with the given resource ID. That is, their wallpaper has been Loading Loading @@ -2499,7 +2804,7 @@ public class WallpaperManager { * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2519,7 +2824,7 @@ public class WallpaperManager { * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) public @FloatRange (from = 0f, to = 1f) float getWallpaperDimAmount() { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading data/etc/services.core.protolog.json +6 −0 Original line number Diff line number Diff line Loading @@ -4333,6 +4333,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, "1810872941": { "message": "setWallpaperCropHints: non-existent wallpaper token: %s", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, "1820873642": { "message": "SyncGroup %d: Unfinished dependencies: %s", "level": "VERBOSE", Loading packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +67 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +9 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOC import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_SYSTEM; import static com.android.window.flags.Flags.multiCrop; import static com.google.common.truth.Truth.assertThat; Loading Loading @@ -60,6 +61,7 @@ import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.service.wallpaper.WallpaperService; import android.util.SparseArray; import android.util.Xml; import androidx.test.InstrumentationRegistry; Loading Loading @@ -711,8 +713,13 @@ public class WallpaperBackupAgentTest { @Test public void testOnRestore_throwsException_logsErrors() throws Exception { when(mWallpaperManager.setStream(any(), any(), anyBoolean(), anyInt())).thenThrow( new RuntimeException()); if (!multiCrop()) { when(mWallpaperManager.setStream(any(), any(), anyBoolean(), anyInt())) .thenThrow(new RuntimeException()); } else { when(mWallpaperManager.setStreamWithCrops(any(), any(SparseArray.class), anyBoolean(), anyInt())).thenThrow(new RuntimeException()); } mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE); mockStagedWallpaperFile(WALLPAPER_INFO_STAGE); mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD, Loading Loading
core/java/android/app/IWallpaperManager.aidl +37 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; Loading @@ -26,6 +27,8 @@ import android.app.WallpaperInfo; import android.content.ComponentName; import android.app.WallpaperColors; import java.util.List; /** @hide */ interface IWallpaperManager { Loading @@ -39,15 +42,21 @@ interface IWallpaperManager { * FLAG_SET_SYSTEM * FLAG_SET_LOCK * * A 'null' cropHint rectangle is explicitly permitted as a sentinel for "whatever * the source image's bounding rect is." * 'screenOrientations' and 'crops' define how the wallpaper will be positioned for * different screen orientations. If some screen orientations are missing, crops for these * orientations will be added by the system. * * If 'screenOrientations' is null, 'crops' can be null or a singleton list. The system will * fit the provided crop (or the whole image, if 'crops' is 'null') for the current device * orientation, and add crops for the missing orientations. * * The completion callback's "onWallpaperChanged()" method is invoked when the * new wallpaper content is ready to display. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_WALLPAPER)") ParcelFileDescriptor setWallpaper(String name, in String callingPackage, in Rect cropHint, boolean allowBackup, out Bundle extras, int which, IWallpaperManagerCallback completion, int userId); in int[] screenOrientations, in List<Rect> crops, boolean allowBackup, out Bundle extras, int which, IWallpaperManagerCallback completion, int userId); /** * Set the live wallpaper. Loading Loading @@ -77,6 +86,30 @@ interface IWallpaperManager { IWallpaperManagerCallback cb, int which, out Bundle outParams, int userId, boolean getCropped); /** * For a given user and a list of display sizes, get a list of Rect representing the * area of the current wallpaper that is displayed for each display size. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL)") @SuppressWarnings(value={"untyped-collection"}) List getBitmapCrops(in List<Point> displaySizes, int which, boolean originalBitmap, int userId); /** * Return how a bitmap of a given size would be cropped for a given list of display sizes when * set with the given suggested crops. * @hide */ @SuppressWarnings(value={"untyped-collection"}) List getFutureBitmapCrops(in Point bitmapSize, in List<Point> displaySizes, in int[] screenOrientations, in List<Rect> crops); /** * Return how a bitmap of a given size would be cropped when set with the given suggested crops. * @hide */ @SuppressWarnings(value={"untyped-collection"}) Rect getBitmapCrop(in Point bitmapSize, in int[] screenOrientations, in List<Rect> crops); /** * Retrieve the given user's current wallpaper ID of the given kind. */ Loading Loading @@ -245,11 +278,4 @@ interface IWallpaperManager { * @hide */ boolean isStaticWallpaper(int which); /** * Temporary method for project b/270726737. * Return true if the wallpaper supports different crops for different display dimensions. * @hide */ boolean isMultiCropEnabled(); }
core/java/android/app/WallpaperManager.java +319 −14 Original line number Diff line number Diff line Loading @@ -18,11 +18,14 @@ package android.app; import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_WALLPAPER_INTERNAL; import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static com.android.window.flags.Flags.FLAG_MULTI_CROP; import static com.android.window.flags.Flags.multiCrop; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; Loading Loading @@ -58,6 +61,7 @@ import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; Loading @@ -84,6 +88,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.Pair; import android.util.SparseArray; import android.view.Display; import android.view.WindowManagerGlobal; Loading @@ -104,6 +109,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; Loading Loading @@ -289,6 +295,79 @@ public class WallpaperManager { public static final String EXTRA_FROM_FOREGROUND_APP = "android.service.wallpaper.extra.FROM_FOREGROUND_APP"; /** * The different screen orientations. {@link #getOrientation} provides their exact definition. * This is only used internally by the framework and the WallpaperBackupAgent. * @hide */ @IntDef(value = { ORIENTATION_UNKNOWN, PORTRAIT, LANDSCAPE, SQUARE_PORTRAIT, SQUARE_LANDSCAPE, }) @Retention(RetentionPolicy.SOURCE) public @interface ScreenOrientation {} /** * @hide */ public static final int ORIENTATION_UNKNOWN = -1; /** * Portrait orientation of most screens * @hide */ public static final int PORTRAIT = 0; /** * Landscape orientation of most screens * @hide */ public static final int LANDSCAPE = 1; /** * Portrait orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ public static final int SQUARE_PORTRAIT = 2; /** * Landscape orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ public static final int SQUARE_LANDSCAPE = 3; /** * Converts a (width, height) screen size to a {@link ScreenOrientation}. * @param screenSize the dimensions of a screen * @return the corresponding {@link ScreenOrientation}. * @hide */ public static @ScreenOrientation int getOrientation(Point screenSize) { float ratio = ((float) screenSize.x) / screenSize.y; // ratios between 3/4 and 4/3 are considered square return ratio >= 4 / 3f ? LANDSCAPE : ratio > 1f ? SQUARE_LANDSCAPE : ratio > 3 / 4f ? SQUARE_PORTRAIT : PORTRAIT; } /** * Get the 90° rotation of a given orientation * @hide */ public static @ScreenOrientation int getRotatedOrientation(@ScreenOrientation int orientation) { switch (orientation) { case PORTRAIT: return LANDSCAPE; case LANDSCAPE: return PORTRAIT; case SQUARE_PORTRAIT: return SQUARE_LANDSCAPE; case SQUARE_LANDSCAPE: return SQUARE_PORTRAIT; default: return ORIENTATION_UNKNOWN; } } // flags for which kind of wallpaper to act on /** @hide */ Loading Loading @@ -867,15 +946,8 @@ public class WallpaperManager { * @hide */ public static boolean isMultiCropEnabled() { if (sGlobals == null) { sIsMultiCropEnabled = multiCrop(); } if (sIsMultiCropEnabled == null) { try { sIsMultiCropEnabled = sGlobals.mService.isMultiCropEnabled(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } sIsMultiCropEnabled = multiCrop(); } return sIsMultiCropEnabled; } Loading Loading @@ -1501,6 +1573,99 @@ public class WallpaperManager { mContext.getUserId()); } /** * For the current user, given a list of display sizes, return a list of rectangles representing * the area of the current wallpaper that would be shown for each of these sizes. * * @param displaySizes the display sizes. * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. * @param originalBitmap If true, return areas relative to the original bitmap. * If false, return areas relative to the cropped bitmap. * @return A List of Rect where the Rect is within the cropped/original bitmap, and corresponds * to what is displayed. The Rect may have a larger width/height ratio than the screen * due to parallax. Return {@code null} if the wallpaper is not an ImageWallpaper. * Also return {@code null} when called with which={@link #FLAG_LOCK} if there is a * shared home + lock wallpaper. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(READ_WALLPAPER_INTERNAL) @Nullable public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes, @SetWallpaperFlags int which, boolean originalBitmap) { checkExactlyOneWallpaperFlagSet(which); try { return sGlobals.mService.getBitmapCrops(displaySizes, which, originalBitmap, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * For preview purposes. * Return how a bitmap of a given size would be cropped for a given list of display sizes, if * it was set as wallpaper via {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. * * @return A List of Rect where the Rect is within the bitmap, and corresponds to what is * displayed for each display size. The Rect may have a larger width/height ratio than * the display due to parallax. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @Nullable public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes, @Nullable Map<Point, Rect> cropHints) { try { if (cropHints == null) cropHints = Map.of(); Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); int[] screenOrientations = entries.stream().mapToInt(entry -> getOrientation(entry.getKey())).toArray(); List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); return sGlobals.mService.getFutureBitmapCrops(bitmapSize, displaySizes, screenOrientations, crops); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * For preview purposes. * Compute the wallpaper colors of the given bitmap, if it was set as wallpaper via * {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. * Return {@code null} if an error occurred and the colors could not be computed. * * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) @Nullable public WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap, @Nullable Map<Point, Rect> cropHints) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } try { if (cropHints == null) cropHints = Map.of(); Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); int[] screenOrientations = entries.stream().mapToInt(entry -> getOrientation(entry.getKey())).toArray(); List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); Point bitmapSize = new Point(bitmap.getWidth(), bitmap.getHeight()); Rect crop = sGlobals.mService.getBitmapCrop(bitmapSize, screenOrientations, crops); float dimAmount = getWallpaperDimAmount(); Bitmap croppedBitmap = Bitmap.createBitmap( bitmap, crop.left, crop.top, crop.width(), crop.height()); WallpaperColors result = WallpaperColors.fromBitmap(croppedBitmap, dimAmount); return result; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * <strong> Important note: </strong> * <ul> Loading Loading @@ -1971,7 +2136,7 @@ public class WallpaperManager { /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), mContext.getOpPackageName(), null, false, result, which, completion, mContext.getOpPackageName(), null, null, false, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; Loading Loading @@ -2089,6 +2254,11 @@ public class WallpaperManager { public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which, int userId) throws IOException { if (multiCrop()) { SparseArray<Rect> cropMap = new SparseArray<>(); if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); return setBitmapWithCrops(fullImage, cropMap, allowBackup, which, userId); } validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2096,9 +2266,69 @@ public class WallpaperManager { } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), null, crops, allowBackup, result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Version of setBitmap that defines how the wallpaper will be positioned for different * display sizes. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from screen dimensions to a sub-region of the image to display for those * dimensions. The {@code Rect} sub-region may have a larger width/height ratio * than the screen dimensions to apply a horizontal parallax effect. If the * map is empty or some entries are missing, the system will apply a default * strategy to position the wallpaper for any unspecified screen dimensions. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { SparseArray<Rect> crops = new SparseArray<>(); cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); return setBitmapWithCrops(fullImage, crops, allowBackup, which, mContext.getUserId()); } @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) private int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which, int userId) throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } int size = cropHints.size(); int[] screenOrientations = new int[size]; List<Rect> crops = new ArrayList<>(size); for (int i = 0; i < size; i++) { screenOrientations[i] = cropHints.keyAt(i); Rect cropHint = cropHints.valueAt(i); validateRect(cropHint); crops.add(cropHint); } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, mContext.getOpPackageName(), screenOrientations, crops, allowBackup, result, which, completion, userId); if (fd != null) { FileOutputStream fos = null; Loading Loading @@ -2214,6 +2444,11 @@ public class WallpaperManager { public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { if (multiCrop()) { SparseArray<Rect> cropMap = new SparseArray<>(); if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); return setStreamWithCrops(bitmapData, cropMap, allowBackup, which); } validateRect(visibleCropHint); if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2221,10 +2456,11 @@ public class WallpaperManager { } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), visibleCropHint, allowBackup, result, which, completion, mContext.getUserId()); mContext.getOpPackageName(), null, crops, allowBackup, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; try { Loading @@ -2243,6 +2479,75 @@ public class WallpaperManager { return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Version of setStream that defines how the wallpaper will be positioned for different * display sizes. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from screen dimensions to a sub-region of the image to display for those * dimensions. The {@code Rect} sub-region may have a larger width/height ratio * than the screen dimensions to apply a horizontal parallax effect. If the * map is empty or some entries are missing, the system will apply a default * strategy to position the wallpaper for any unspecified screen dimensions. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(InputStream bitmapData, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { SparseArray<Rect> crops = new SparseArray<>(); cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); return setStreamWithCrops(bitmapData, crops, allowBackup, which); } /** * Similar to {@link #setStreamWithCrops(InputStream, Map, boolean, int)}, but using * {@link ScreenOrientation} as keys of the cropHints map. Used for backup & restore, since * WallpaperBackupAgent stores orientations rather than the exact display size. * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to display * for that screen orientation. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } int size = cropHints.size(); int[] screenOrientations = new int[size]; List<Rect> crops = new ArrayList<>(size); for (int i = 0; i < size; i++) { screenOrientations[i] = cropHints.keyAt(i); Rect cropHint = cropHints.valueAt(i); validateRect(cropHint); crops.add(cropHint); } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), screenOrientations, crops, allowBackup, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(bitmapData, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); } /** * Return whether any users are currently set to use the wallpaper * with the given resource ID. That is, their wallpaper has been Loading Loading @@ -2499,7 +2804,7 @@ public class WallpaperManager { * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading @@ -2519,7 +2824,7 @@ public class WallpaperManager { * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) public @FloatRange (from = 0f, to = 1f) float getWallpaperDimAmount() { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); Loading
data/etc/services.core.protolog.json +6 −0 Original line number Diff line number Diff line Loading @@ -4333,6 +4333,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, "1810872941": { "message": "setWallpaperCropHints: non-existent wallpaper token: %s", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, "1820873642": { "message": "SyncGroup %d: Unfinished dependencies: %s", "level": "VERBOSE", Loading
packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +67 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +9 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOC import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_SYSTEM; import static com.android.window.flags.Flags.multiCrop; import static com.google.common.truth.Truth.assertThat; Loading Loading @@ -60,6 +61,7 @@ import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.service.wallpaper.WallpaperService; import android.util.SparseArray; import android.util.Xml; import androidx.test.InstrumentationRegistry; Loading Loading @@ -711,8 +713,13 @@ public class WallpaperBackupAgentTest { @Test public void testOnRestore_throwsException_logsErrors() throws Exception { when(mWallpaperManager.setStream(any(), any(), anyBoolean(), anyInt())).thenThrow( new RuntimeException()); if (!multiCrop()) { when(mWallpaperManager.setStream(any(), any(), anyBoolean(), anyInt())) .thenThrow(new RuntimeException()); } else { when(mWallpaperManager.setStreamWithCrops(any(), any(SparseArray.class), anyBoolean(), anyInt())).thenThrow(new RuntimeException()); } mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE); mockStagedWallpaperFile(WALLPAPER_INFO_STAGE); mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD, Loading