1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; 20 import static android.Manifest.permission.READ_WALLPAPER_INTERNAL; 21 import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT; 22 import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; 23 import static android.app.Flags.enableConnectedDisplaysWallpaper; 24 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 25 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; 26 27 import static com.android.window.flags.Flags.FLAG_MULTI_CROP; 28 import static com.android.window.flags.Flags.multiCrop; 29 30 import android.Manifest; 31 import android.annotation.FlaggedApi; 32 import android.annotation.FloatRange; 33 import android.annotation.IntDef; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.RawRes; 37 import android.annotation.RequiresPermission; 38 import android.annotation.SdkConstant; 39 import android.annotation.SdkConstant.SdkConstantType; 40 import android.annotation.SystemApi; 41 import android.annotation.SystemService; 42 import android.annotation.TestApi; 43 import android.annotation.UiContext; 44 import android.app.compat.CompatChanges; 45 import android.app.wallpaper.WallpaperDescription; 46 import android.app.wallpaper.WallpaperInstance; 47 import android.compat.annotation.ChangeId; 48 import android.compat.annotation.EnabledSince; 49 import android.compat.annotation.UnsupportedAppUsage; 50 import android.content.ComponentName; 51 import android.content.ContentResolver; 52 import android.content.Context; 53 import android.content.Intent; 54 import android.content.pm.PackageManager; 55 import android.content.pm.ResolveInfo; 56 import android.content.res.Configuration; 57 import android.content.res.Resources; 58 import android.content.res.Resources.NotFoundException; 59 import android.graphics.Bitmap; 60 import android.graphics.BitmapFactory; 61 import android.graphics.BitmapRegionDecoder; 62 import android.graphics.Canvas; 63 import android.graphics.ColorFilter; 64 import android.graphics.ColorSpace; 65 import android.graphics.ImageDecoder; 66 import android.graphics.Matrix; 67 import android.graphics.Paint; 68 import android.graphics.PixelFormat; 69 import android.graphics.Point; 70 import android.graphics.PorterDuff; 71 import android.graphics.PorterDuffXfermode; 72 import android.graphics.Rect; 73 import android.graphics.RectF; 74 import android.graphics.drawable.BitmapDrawable; 75 import android.graphics.drawable.Drawable; 76 import android.net.Uri; 77 import android.os.Build; 78 import android.os.Bundle; 79 import android.os.DeadSystemException; 80 import android.os.Environment; 81 import android.os.FileUtils; 82 import android.os.Handler; 83 import android.os.IBinder; 84 import android.os.Looper; 85 import android.os.ParcelFileDescriptor; 86 import android.os.RemoteException; 87 import android.os.StrictMode; 88 import android.os.SystemProperties; 89 import android.os.Trace; 90 import android.text.TextUtils; 91 import android.util.ArrayMap; 92 import android.util.ArraySet; 93 import android.util.Log; 94 import android.util.MathUtils; 95 import android.util.Pair; 96 import android.util.SparseArray; 97 import android.view.Display; 98 import android.view.WindowManagerGlobal; 99 100 import com.android.internal.R; 101 import com.android.internal.annotations.Keep; 102 103 import libcore.io.IoUtils; 104 105 import java.io.BufferedInputStream; 106 import java.io.File; 107 import java.io.FileInputStream; 108 import java.io.FileNotFoundException; 109 import java.io.FileOutputStream; 110 import java.io.IOException; 111 import java.io.InputStream; 112 import java.lang.annotation.Retention; 113 import java.lang.annotation.RetentionPolicy; 114 import java.util.ArrayList; 115 import java.util.Arrays; 116 import java.util.HashSet; 117 import java.util.List; 118 import java.util.Map; 119 import java.util.Set; 120 import java.util.concurrent.CountDownLatch; 121 import java.util.concurrent.TimeUnit; 122 123 /** 124 * Provides access to the system wallpaper. With WallpaperManager, you can 125 * get the current wallpaper, get the desired dimensions for the wallpaper, set 126 * the wallpaper, and more. 127 * 128 * <p> An app can check whether wallpapers are supported for the current user, by calling 129 * {@link #isWallpaperSupported()}, and whether setting of wallpapers is allowed, by calling 130 * {@link #isSetWallpaperAllowed()}. 131 * Any public APIs added to WallpaperManager should have a corresponding stub in 132 * {@link DisabledWallpaperManager}. 133 */ 134 @SystemService(Context.WALLPAPER_SERVICE) 135 public class WallpaperManager { 136 137 private static String TAG = "WallpaperManager"; 138 private static final boolean DEBUG = false; 139 140 /** 141 * Trying to read the wallpaper file or bitmap in T will return 142 * the default wallpaper bitmap/file instead of throwing a SecurityException. 143 */ 144 @ChangeId 145 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) 146 static final long RETURN_DEFAULT_ON_SECURITY_EXCEPTION = 239784307L; 147 148 /** 149 * In U and later, attempting to read the wallpaper file or bitmap will throw an exception, 150 * (except with the READ_WALLPAPER_INTERNAL permission). 151 */ 152 @ChangeId 153 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 154 static final long THROW_ON_SECURITY_EXCEPTION = 237508058L; 155 156 private float mWallpaperXStep = -1; 157 private float mWallpaperYStep = -1; 158 private static final @NonNull RectF LOCAL_COLOR_BOUNDS = 159 new RectF(0, 0, 1, 1); 160 161 /** {@hide} */ 162 private static final String PROP_WALLPAPER = "ro.config.wallpaper"; 163 /** {@hide} */ 164 private static final String PROP_LOCK_WALLPAPER = "ro.config.lock_wallpaper"; 165 /** {@hide} */ 166 private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component"; 167 /** {@hide} */ 168 private static final String VALUE_CMF_COLOR = 169 android.os.SystemProperties.get("ro.boot.hardware.color"); 170 /** {@hide} */ 171 private static final String WALLPAPER_CMF_PATH = "/wallpaper/image/"; 172 173 /** 174 * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct 175 * an intent; instead, use {@link #getCropAndSetWallpaperIntent}. 176 * <p>Input: {@link Intent#getData} is the URI of the image to crop and set as wallpaper. 177 * <p>Output: RESULT_OK if user decided to crop/set the wallpaper, RESULT_CANCEL otherwise 178 * Activities that support this intent should specify a MIME filter of "image/*" 179 */ 180 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 181 public static final String ACTION_CROP_AND_SET_WALLPAPER = 182 "android.service.wallpaper.CROP_AND_SET_WALLPAPER"; 183 184 /** 185 * Launch an activity for the user to pick the current global live 186 * wallpaper. 187 */ 188 public static final String ACTION_LIVE_WALLPAPER_CHOOSER 189 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER"; 190 191 /** 192 * Directly launch live wallpaper preview, allowing the user to immediately 193 * confirm to switch to a specific live wallpaper. You must specify 194 * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of 195 * a live wallpaper component that is to be shown. 196 */ 197 public static final String ACTION_CHANGE_LIVE_WALLPAPER 198 = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER"; 199 200 /** 201 * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the 202 * ComponentName of a live wallpaper that should be shown as a preview, 203 * for the user to confirm. 204 */ 205 public static final String EXTRA_LIVE_WALLPAPER_COMPONENT 206 = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT"; 207 208 /** 209 * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER} 210 * which allows them to provide a custom large icon associated with this action. 211 */ 212 public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview"; 213 214 /** 215 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 216 * host when the user taps on an empty area (not performing an action 217 * in the host). The x and y arguments are the location of the tap in 218 * screen coordinates. 219 */ 220 public static final String COMMAND_TAP = "android.wallpaper.tap"; 221 222 /** 223 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 224 * host when the user releases a secondary pointer on an empty area 225 * (not performing an action in the host). The x and y arguments are 226 * the location of the secondary tap in screen coordinates. 227 */ 228 public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap"; 229 230 /** 231 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 232 * host when the user drops an object into an area of the host. The x 233 * and y arguments are the location of the drop. 234 */ 235 public static final String COMMAND_DROP = "android.home.drop"; 236 237 /** 238 * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is waking 239 * up. The x and y arguments are a location (possibly very roughly) corresponding to the action 240 * that caused the device to wake up. For example, if the power button was pressed, this will be 241 * the location on the screen nearest the power button. 242 * 243 * If the location is unknown or not applicable, x and y will be -1. 244 * 245 * @hide 246 */ 247 public static final String COMMAND_WAKING_UP = "android.wallpaper.wakingup"; 248 249 /** 250 * Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard 251 * starts going away. 252 * <p> 253 * This command is triggered by {@link android.app.IActivityTaskManager#keyguardGoingAway(int)} 254 * or by {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}. 255 * 256 * @hide 257 */ 258 public static final String COMMAND_KEYGUARD_GOING_AWAY = 259 "android.wallpaper.keyguardgoingaway"; 260 261 /** 262 * Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard 263 * starts going away. 264 * 265 * <p>This command is triggered by 266 * {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}. 267 * 268 * @hide 269 */ 270 public static final String COMMAND_KEYGUARD_APPEARING = 271 "android.wallpaper.keyguardappearing"; 272 273 /** 274 * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is going to 275 * sleep. The x and y arguments are a location (possibly very roughly) corresponding to the 276 * action that caused the device to go to sleep. For example, if the power button was pressed, 277 * this will be the location on the screen nearest the power button. 278 * 279 * If the location is unknown or not applicable, x and y will be -1. 280 * 281 * @hide 282 */ 283 public static final String COMMAND_GOING_TO_SLEEP = "android.wallpaper.goingtosleep"; 284 285 /** 286 * Command for {@link #sendWallpaperCommand}: reported when the wallpaper that was already 287 * set is re-applied by the user. 288 * @hide 289 */ 290 public static final String COMMAND_REAPPLY = "android.wallpaper.reapply"; 291 292 /** 293 * Command for {@link #sendWallpaperCommand}: reported when the live wallpaper needs to be 294 * frozen. 295 * @hide 296 */ 297 public static final String COMMAND_FREEZE = "android.wallpaper.freeze"; 298 299 /** 300 * Command for {@link #sendWallpaperCommand}: reported when the live wallapper doesn't need 301 * to be frozen anymore. 302 * @hide 303 */ 304 public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze"; 305 306 /** 307 * Command for {@link #sendWallpaperCommand}: in sendWallpaperCommand put extra to this command 308 * to give the bounds of space between the bottom of notifications and the top of shortcuts 309 * @hide 310 */ 311 public static final String COMMAND_LOCKSCREEN_LAYOUT_CHANGED = 312 "android.wallpaper.lockscreen_layout_changed"; 313 314 /** 315 * Command for {@link #sendWallpaperCommand}: Include the tap position within the wallpaper 316 * focal area.The x and y arguments are the absolute tap coordinates, already scaled to match 317 * the wallpaper's dimensions. 318 * 319 * @hide 320 */ 321 public static final String COMMAND_LOCKSCREEN_TAP = 322 "android.wallpaper.lockscreen_tap"; 323 324 /** 325 * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID. 326 * @hide 327 */ 328 public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID"; 329 330 /** 331 * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from 332 * a foreground app. 333 * @hide 334 */ 335 public static final String EXTRA_FROM_FOREGROUND_APP = 336 "android.service.wallpaper.extra.FROM_FOREGROUND_APP"; 337 338 /** 339 * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from 340 * a foreground app. 341 * @hide 342 */ 343 public static final String EXTRA_WHICH_WALLPAPER_CHANGED = 344 "android.service.wallpaper.extra.WHICH_WALLPAPER_CHANGED"; 345 346 /** 347 * The different screen orientations. {@link #getOrientation} provides their exact definition. 348 * This is only used internally by the framework and the WallpaperBackupAgent. 349 * @hide 350 */ 351 @IntDef(prefix = { "ORIENTATION_" }, value = { 352 ORIENTATION_UNKNOWN, 353 ORIENTATION_PORTRAIT, 354 ORIENTATION_LANDSCAPE, 355 ORIENTATION_SQUARE_PORTRAIT, 356 ORIENTATION_SQUARE_LANDSCAPE, 357 }) 358 @Retention(RetentionPolicy.SOURCE) 359 public @interface ScreenOrientation {} 360 361 /** 362 * @hide 363 */ 364 public static final int ORIENTATION_UNKNOWN = -1; 365 366 /** 367 * Portrait orientation of most screens 368 * @hide 369 */ 370 public static final int ORIENTATION_PORTRAIT = 0; 371 372 /** 373 * Landscape orientation of most screens 374 * @hide 375 */ 376 public static final int ORIENTATION_LANDSCAPE = 1; 377 378 /** 379 * Portrait orientation with similar width and height (e.g. the inner screen of a foldable) 380 * @hide 381 */ 382 public static final int ORIENTATION_SQUARE_PORTRAIT = 2; 383 384 /** 385 * Landscape orientation with similar width and height (e.g. the inner screen of a foldable) 386 * @hide 387 */ 388 public static final int ORIENTATION_SQUARE_LANDSCAPE = 3; 389 390 /** 391 * Converts a (width, height) screen size to a {@link ScreenOrientation}. 392 * @param screenSize the dimensions of a screen 393 * @return the corresponding {@link ScreenOrientation}. 394 * @hide 395 */ getOrientation(@onNull Point screenSize)396 public static @ScreenOrientation int getOrientation(@NonNull Point screenSize) { 397 float ratio = ((float) screenSize.x) / screenSize.y; 398 // ratios between 3/4 and 4/3 are considered square 399 return ratio >= 4 / 3f ? ORIENTATION_LANDSCAPE 400 : ratio > 1f ? ORIENTATION_SQUARE_LANDSCAPE 401 : ratio > 3 / 4f ? ORIENTATION_SQUARE_PORTRAIT 402 : ORIENTATION_PORTRAIT; 403 } 404 405 /** 406 * Get the 90° rotation of a given orientation 407 * @hide 408 */ getRotatedOrientation(@creenOrientation int orientation)409 public static @ScreenOrientation int getRotatedOrientation(@ScreenOrientation int orientation) { 410 switch (orientation) { 411 case ORIENTATION_PORTRAIT: return ORIENTATION_LANDSCAPE; 412 case ORIENTATION_LANDSCAPE: return ORIENTATION_PORTRAIT; 413 case ORIENTATION_SQUARE_PORTRAIT: return ORIENTATION_SQUARE_LANDSCAPE; 414 case ORIENTATION_SQUARE_LANDSCAPE: return ORIENTATION_SQUARE_PORTRAIT; 415 default: return ORIENTATION_UNKNOWN; 416 } 417 } 418 419 // flags for which kind of wallpaper to act on 420 421 /** @hide */ 422 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 423 FLAG_SYSTEM, 424 FLAG_LOCK 425 }) 426 @Retention(RetentionPolicy.SOURCE) 427 public @interface SetWallpaperFlags {} 428 429 /** 430 * Flag: set or retrieve the general system wallpaper. 431 */ 432 public static final int FLAG_SYSTEM = 1 << 0; 433 434 /** 435 * Flag: set or retrieve the lock-screen-specific wallpaper. 436 */ 437 public static final int FLAG_LOCK = 1 << 1; 438 439 private static final Object sSync = new Object[0]; 440 @UnsupportedAppUsage 441 private static Globals sGlobals; 442 private final Context mContext; 443 private final boolean mWcgEnabled; 444 private final ColorManagementProxy mCmProxy; 445 private static Boolean sIsMultiCropEnabled = null; 446 447 /** 448 * Special drawable that draws a wallpaper as fast as possible. Assumes 449 * no scaling or placement off (0,0) of the wallpaper (this should be done 450 * at the time the bitmap is loaded). 451 */ 452 static class FastBitmapDrawable extends Drawable { 453 private final Bitmap mBitmap; 454 private final int mWidth; 455 private final int mHeight; 456 private int mDrawLeft; 457 private int mDrawTop; 458 private final Paint mPaint; 459 FastBitmapDrawable(Bitmap bitmap)460 private FastBitmapDrawable(Bitmap bitmap) { 461 mBitmap = bitmap; 462 mWidth = bitmap.getWidth(); 463 mHeight = bitmap.getHeight(); 464 465 setBounds(0, 0, mWidth, mHeight); 466 467 mPaint = new Paint(); 468 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 469 } 470 471 @Override draw(Canvas canvas)472 public void draw(Canvas canvas) { 473 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint); 474 } 475 476 @Override getOpacity()477 public int getOpacity() { 478 return PixelFormat.OPAQUE; 479 } 480 481 @Override setBounds(int left, int top, int right, int bottom)482 public void setBounds(int left, int top, int right, int bottom) { 483 mDrawLeft = left + (right-left - mWidth) / 2; 484 mDrawTop = top + (bottom-top - mHeight) / 2; 485 } 486 487 @Override setAlpha(int alpha)488 public void setAlpha(int alpha) { 489 throw new UnsupportedOperationException("Not supported with this drawable"); 490 } 491 492 @Override setColorFilter(ColorFilter colorFilter)493 public void setColorFilter(ColorFilter colorFilter) { 494 throw new UnsupportedOperationException("Not supported with this drawable"); 495 } 496 497 @Override setDither(boolean dither)498 public void setDither(boolean dither) { 499 throw new UnsupportedOperationException("Not supported with this drawable"); 500 } 501 502 @Override setFilterBitmap(boolean filter)503 public void setFilterBitmap(boolean filter) { 504 throw new UnsupportedOperationException("Not supported with this drawable"); 505 } 506 507 @Override getIntrinsicWidth()508 public int getIntrinsicWidth() { 509 return mWidth; 510 } 511 512 @Override getIntrinsicHeight()513 public int getIntrinsicHeight() { 514 return mHeight; 515 } 516 517 @Override getMinimumWidth()518 public int getMinimumWidth() { 519 return mWidth; 520 } 521 522 @Override getMinimumHeight()523 public int getMinimumHeight() { 524 return mHeight; 525 } 526 } 527 528 /** 529 * Convenience class representing a cached wallpaper bitmap and associated data. 530 */ 531 private static class CachedWallpaper { 532 final Bitmap mCachedWallpaper; 533 final int mCachedWallpaperUserId; 534 @SetWallpaperFlags final int mWhich; 535 CachedWallpaper(Bitmap cachedWallpaper, int cachedWallpaperUserId, @SetWallpaperFlags int which)536 CachedWallpaper(Bitmap cachedWallpaper, int cachedWallpaperUserId, 537 @SetWallpaperFlags int which) { 538 mCachedWallpaper = cachedWallpaper; 539 mCachedWallpaperUserId = cachedWallpaperUserId; 540 mWhich = which; 541 } 542 543 /** 544 * Returns true if this object represents a valid cached bitmap for the given parameters, 545 * otherwise false. 546 */ isValid(int userId, @SetWallpaperFlags int which)547 boolean isValid(int userId, @SetWallpaperFlags int which) { 548 return userId == mCachedWallpaperUserId && which == mWhich 549 && !mCachedWallpaper.isRecycled(); 550 } 551 } 552 553 private static class Globals extends IWallpaperManagerCallback.Stub { 554 private final IWallpaperManager mService; 555 private boolean mColorCallbackRegistered; 556 private final ArrayList<Pair<OnColorsChangedListener, Handler>> mColorListeners = 557 new ArrayList<>(); 558 private CachedWallpaper mCachedWallpaper; 559 private Bitmap mDefaultWallpaper; 560 private Handler mMainLooperHandler; 561 private ArrayMap<LocalWallpaperColorConsumer, ArraySet<RectF>> mLocalColorCallbackAreas = 562 new ArrayMap<>(); 563 private ILocalWallpaperColorConsumer mLocalColorCallback = 564 new ILocalWallpaperColorConsumer.Stub() { 565 @Override 566 public void onColorsChanged(RectF area, WallpaperColors colors) { 567 for (LocalWallpaperColorConsumer callback : 568 mLocalColorCallbackAreas.keySet()) { 569 ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback); 570 if (areas != null && areas.contains(area)) { 571 callback.onColorsChanged(area, colors); 572 } 573 } 574 } 575 }; 576 Globals(IWallpaperManager service, Looper looper)577 Globals(IWallpaperManager service, Looper looper) { 578 mService = service; 579 mMainLooperHandler = new Handler(looper); 580 forgetLoadedWallpaper(); 581 } 582 onWallpaperChanged()583 public void onWallpaperChanged() { 584 /* The wallpaper has changed but we shouldn't eagerly load the 585 * wallpaper as that would be inefficient. Reset the cached wallpaper 586 * to null so if the user requests the wallpaper again then we'll 587 * fetch it. 588 */ 589 forgetLoadedWallpaper(); 590 } 591 592 /** 593 * Start listening to wallpaper color events. 594 * Will be called whenever someone changes their wallpaper or if a live wallpaper 595 * changes its colors. 596 * @param callback Listener 597 * @param handler Thread to call it from. Main thread if null. 598 * @param userId Owner of the wallpaper or UserHandle.USER_ALL 599 * @param displayId Caller comes from which display 600 */ addOnColorsChangedListener(@onNull OnColorsChangedListener callback, @Nullable Handler handler, int userId, int displayId)601 public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback, 602 @Nullable Handler handler, int userId, int displayId) { 603 synchronized (this) { 604 if (!mColorCallbackRegistered) { 605 try { 606 mService.registerWallpaperColorsCallback(this, userId, displayId); 607 mColorCallbackRegistered = true; 608 } catch (RemoteException e) { 609 // Failed, service is gone 610 Log.w(TAG, "Can't register for color updates", e); 611 } 612 } 613 mColorListeners.add(new Pair<>(callback, handler)); 614 } 615 } 616 addOnColorsChangedListener( @onNull LocalWallpaperColorConsumer callback, @NonNull List<RectF> regions, int which, int userId, int displayId)617 public void addOnColorsChangedListener( 618 @NonNull LocalWallpaperColorConsumer callback, 619 @NonNull List<RectF> regions, int which, int userId, int displayId) { 620 synchronized (this) { 621 for (RectF area : regions) { 622 ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback); 623 if (areas == null) { 624 areas = new ArraySet<>(); 625 mLocalColorCallbackAreas.put(callback, areas); 626 } 627 areas.add(area); 628 } 629 try { 630 // one way returns immediately 631 mService.addOnLocalColorsChangedListener(mLocalColorCallback, regions, which, 632 userId, displayId); 633 } catch (RemoteException e) { 634 // Can't get colors, connection lost. 635 Log.e(TAG, "Can't register for local color updates", e); 636 } 637 } 638 } 639 removeOnColorsChangedListener( @onNull LocalWallpaperColorConsumer callback, int which, int userId, int displayId)640 public void removeOnColorsChangedListener( 641 @NonNull LocalWallpaperColorConsumer callback, int which, int userId, 642 int displayId) { 643 synchronized (this) { 644 final ArraySet<RectF> removeAreas = mLocalColorCallbackAreas.remove(callback); 645 if (removeAreas == null || removeAreas.size() == 0) { 646 return; 647 } 648 for (LocalWallpaperColorConsumer cb : mLocalColorCallbackAreas.keySet()) { 649 ArraySet<RectF> areas = mLocalColorCallbackAreas.get(cb); 650 if (areas != null && cb != callback) removeAreas.removeAll(areas); 651 } 652 try { 653 if (removeAreas.size() > 0) { 654 // one way returns immediately 655 mService.removeOnLocalColorsChangedListener( 656 mLocalColorCallback, new ArrayList(removeAreas), which, userId, 657 displayId); 658 } 659 } catch (RemoteException e) { 660 // Can't get colors, connection lost. 661 Log.e(TAG, "Can't unregister for local color updates", e); 662 } 663 } 664 } 665 666 /** 667 * Stop listening to wallpaper color events. 668 * 669 * @param callback listener 670 * @param userId Owner of the wallpaper or UserHandle.USER_ALL 671 * @param displayId Which display is interested 672 */ removeOnColorsChangedListener(@onNull OnColorsChangedListener callback, int userId, int displayId)673 public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback, 674 int userId, int displayId) { 675 synchronized (this) { 676 mColorListeners.removeIf(pair -> pair.first == callback); 677 678 if (mColorListeners.size() == 0 && mColorCallbackRegistered) { 679 mColorCallbackRegistered = false; 680 try { 681 mService.unregisterWallpaperColorsCallback(this, userId, displayId); 682 } catch (RemoteException e) { 683 // Failed, service is gone 684 Log.w(TAG, "Can't unregister color updates", e); 685 } 686 } 687 } 688 } 689 690 @Override onWallpaperColorsChanged(WallpaperColors colors, int which, int userId)691 public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) { 692 synchronized (this) { 693 for (Pair<OnColorsChangedListener, Handler> listener : mColorListeners) { 694 Handler handler = listener.second; 695 if (listener.second == null) { 696 handler = mMainLooperHandler; 697 } 698 handler.post(() -> { 699 // Dealing with race conditions between posting a callback and 700 // removeOnColorsChangedListener being called. 701 boolean stillExists; 702 synchronized (sGlobals) { 703 stillExists = mColorListeners.contains(listener); 704 } 705 if (stillExists) { 706 listener.first.onColorsChanged(colors, which, userId); 707 } 708 }); 709 } 710 } 711 } 712 getWallpaperColors(int which, int userId, int displayId)713 WallpaperColors getWallpaperColors(int which, int userId, int displayId) { 714 checkExactlyOneWallpaperFlagSet(which); 715 716 try { 717 return mService.getWallpaperColors(which, userId, displayId); 718 } catch (RemoteException e) { 719 // Can't get colors, connection lost. 720 } 721 return null; 722 } 723 peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, ColorManagementProxy cmProxy)724 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, 725 @SetWallpaperFlags int which, ColorManagementProxy cmProxy) { 726 return peekWallpaperBitmap(context, returnDefault, which, context.getUserId(), 727 false /* hardware */, cmProxy); 728 } 729 730 /** 731 * Retrieves the current wallpaper Bitmap, caching the result. If this fails and 732 * `returnDefault` is set, returns the Bitmap for the default wallpaper; otherwise returns 733 * null. 734 * 735 * More sophisticated caching might a) store and compare the wallpaper ID so that 736 * consecutive calls for FLAG_SYSTEM and FLAG_LOCK could return the cached wallpaper if 737 * no lock screen wallpaper is set, or b) separately cache home and lock screen wallpaper. 738 */ peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId, boolean hardware, ColorManagementProxy cmProxy)739 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, 740 @SetWallpaperFlags int which, int userId, boolean hardware, 741 ColorManagementProxy cmProxy) { 742 if (mService != null) { 743 try { 744 Trace.beginSection("WPMS.isWallpaperSupported"); 745 if (!mService.isWallpaperSupported(context.getOpPackageName())) { 746 return null; 747 } 748 } catch (RemoteException e) { 749 throw e.rethrowFromSystemServer(); 750 } finally { 751 Trace.endSection(); 752 } 753 } 754 synchronized (this) { 755 if (mCachedWallpaper != null && mCachedWallpaper.isValid(userId, which) && context 756 .checkSelfPermission(READ_WALLPAPER_INTERNAL) == PERMISSION_GRANTED) { 757 return mCachedWallpaper.mCachedWallpaper; 758 } 759 mCachedWallpaper = null; 760 Bitmap currentWallpaper = null; 761 try { 762 Trace.beginSection("WPMS.getCurrentWallpaperLocked"); 763 currentWallpaper = getCurrentWallpaperLocked( 764 context, which, userId, hardware, cmProxy); 765 } catch (OutOfMemoryError e) { 766 Log.w(TAG, "Out of memory loading the current wallpaper: " + e); 767 } catch (SecurityException e) { 768 /* 769 * Apps with target SDK <= S can still access the wallpaper through 770 * READ_EXTERNAL_STORAGE. In T however, app that previously had access to the 771 * wallpaper via READ_EXTERNAL_STORAGE will get a SecurityException here. 772 * Thus, in T specifically, return the default wallpaper instead of crashing. 773 */ 774 if (CompatChanges.isChangeEnabled(RETURN_DEFAULT_ON_SECURITY_EXCEPTION) 775 && !CompatChanges.isChangeEnabled(THROW_ON_SECURITY_EXCEPTION)) { 776 Log.w(TAG, "No permission to access wallpaper, returning default" 777 + " wallpaper to avoid crashing legacy app."); 778 return getDefaultWallpaper(context, FLAG_SYSTEM); 779 } 780 781 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) { 782 Log.w(TAG, "No permission to access wallpaper, suppressing" 783 + " exception to avoid crashing legacy app."); 784 } else { 785 // Post-O apps really most sincerely need the permission. 786 throw e; 787 } 788 } finally { 789 Trace.endSection(); 790 } 791 if (currentWallpaper != null) { 792 mCachedWallpaper = new CachedWallpaper(currentWallpaper, userId, which); 793 return currentWallpaper; 794 } 795 } 796 if (returnDefault || (which == FLAG_LOCK && isStaticWallpaper(FLAG_LOCK))) { 797 return getDefaultWallpaper(context, which); 798 } 799 return null; 800 } 801 802 @Nullable peekWallpaperDimensions(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId)803 public Rect peekWallpaperDimensions(Context context, boolean returnDefault, 804 @SetWallpaperFlags int which, int userId) { 805 if (mService != null) { 806 try { 807 if (!mService.isWallpaperSupported(context.getOpPackageName())) { 808 return new Rect(); 809 } 810 } catch (RemoteException e) { 811 throw e.rethrowFromSystemServer(); 812 } 813 } 814 815 Rect dimensions = null; 816 synchronized (this) { 817 Bundle params = new Bundle(); 818 try (ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( 819 context.getOpPackageName(), context.getAttributionTag(), this, which, 820 params, userId, /* getCropped = */ true)) { 821 // Let's peek user wallpaper first. 822 if (pfd != null) { 823 BitmapFactory.Options options = new BitmapFactory.Options(); 824 options.inJustDecodeBounds = true; 825 BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, options); 826 dimensions = new Rect(0, 0, options.outWidth, options.outHeight); 827 } 828 } catch (RemoteException ex) { 829 Log.w(TAG, "peek wallpaper dimensions failed", ex); 830 } catch (IOException ignored) { 831 // This is only thrown on close and can be safely ignored. 832 } 833 } 834 // If user wallpaper is unavailable, may be the default one instead. 835 if ((dimensions == null || dimensions.width() == 0 || dimensions.height() == 0) 836 && (returnDefault || (which == FLAG_LOCK && isStaticWallpaper(FLAG_LOCK)))) { 837 InputStream is = openDefaultWallpaper(context, which); 838 if (is != null) { 839 try { 840 BitmapFactory.Options options = new BitmapFactory.Options(); 841 options.inJustDecodeBounds = true; 842 BitmapFactory.decodeStream(is, null, options); 843 dimensions = new Rect(0, 0, options.outWidth, options.outHeight); 844 } finally { 845 IoUtils.closeQuietly(is); 846 } 847 } 848 } 849 return dimensions; 850 } 851 forgetLoadedWallpaper()852 void forgetLoadedWallpaper() { 853 synchronized (this) { 854 mCachedWallpaper = null; 855 mDefaultWallpaper = null; 856 } 857 } 858 getCurrentWallpaperLocked(Context context, @SetWallpaperFlags int which, int userId, boolean hardware, ColorManagementProxy cmProxy)859 private Bitmap getCurrentWallpaperLocked(Context context, @SetWallpaperFlags int which, 860 int userId, boolean hardware, ColorManagementProxy cmProxy) { 861 if (mService == null) { 862 Log.w(TAG, "WallpaperService not running"); 863 return null; 864 } 865 866 try { 867 Bundle params = new Bundle(); 868 Trace.beginSection("WPMS.getWallpaperWithFeature_" + which); 869 ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( 870 context.getOpPackageName(), context.getAttributionTag(), this, which, 871 params, userId, /* getCropped = */ true); 872 Trace.endSection(); 873 874 if (pfd == null) { 875 return null; 876 } 877 try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { 878 ImageDecoder.Source src; 879 if (enableConnectedDisplaysWallpaper()) { 880 src = ImageDecoder.createSource(context.getResources(), is, 881 /* density= */ 0); 882 } else { 883 src = ImageDecoder.createSource(context.getResources(), is); 884 } 885 return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> { 886 // Mutable and hardware config can't be set at the same time. 887 decoder.setMutableRequired(!hardware); 888 // Let's do color management 889 if (cmProxy != null) { 890 cmProxy.doColorManagement(decoder, info); 891 } 892 })); 893 } catch (OutOfMemoryError | IOException e) { 894 Log.w(TAG, "Can't decode file", e); 895 } 896 } catch (RemoteException e) { 897 throw e.rethrowFromSystemServer(); 898 } 899 return null; 900 } 901 getDefaultWallpaper(Context context, @SetWallpaperFlags int which)902 private Bitmap getDefaultWallpaper(Context context, @SetWallpaperFlags int which) { 903 Trace.beginSection("WPMS.getDefaultWallpaper_" + which); 904 Bitmap defaultWallpaper = mDefaultWallpaper; 905 if (defaultWallpaper == null || defaultWallpaper.isRecycled()) { 906 defaultWallpaper = null; 907 Trace.beginSection("WPMS.openDefaultWallpaper"); 908 try (InputStream is = openDefaultWallpaper(context, which)) { 909 Trace.endSection(); 910 if (is != null) { 911 BitmapFactory.Options options = new BitmapFactory.Options(); 912 Trace.beginSection("WPMS.decodeStream"); 913 defaultWallpaper = BitmapFactory.decodeStream(is, null, options); 914 Trace.endSection(); 915 } 916 } catch (OutOfMemoryError | IOException e) { 917 Log.w(TAG, "Can't decode stream", e); 918 } 919 } 920 synchronized (this) { 921 mDefaultWallpaper = defaultWallpaper; 922 } 923 Trace.endSection(); 924 return defaultWallpaper; 925 } 926 927 /** 928 * Return true if there is a static wallpaper on the specified screen. 929 * With {@code which=}{@link #FLAG_LOCK}, always return false if the lockscreen doesn't run 930 * its own wallpaper engine. 931 */ isStaticWallpaper(@etWallpaperFlags int which)932 private boolean isStaticWallpaper(@SetWallpaperFlags int which) { 933 if (mService == null) { 934 Log.w(TAG, "WallpaperService not running"); 935 throw new RuntimeException(new DeadSystemException()); 936 } 937 try { 938 return mService.isStaticWallpaper(which); 939 } catch (RemoteException e) { 940 throw e.rethrowFromSystemServer(); 941 } 942 } 943 } 944 initGlobals(IWallpaperManager service, Looper looper)945 static void initGlobals(IWallpaperManager service, Looper looper) { 946 synchronized (sSync) { 947 if (sGlobals == null) { 948 sGlobals = new Globals(service, looper); 949 } 950 } 951 } 952 WallpaperManager(IWallpaperManager service, @UiContext Context context, Handler handler)953 /*package*/ WallpaperManager(IWallpaperManager service, @UiContext Context context, 954 Handler handler) { 955 mContext = context; 956 if (service != null) { 957 initGlobals(service, context.getMainLooper()); 958 } 959 // Check if supports mixed color spaces composition in hardware. 960 mWcgEnabled = context.getResources().getConfiguration().isScreenWideColorGamut() 961 && context.getResources().getBoolean(R.bool.config_enableWcgMode); 962 mCmProxy = new ColorManagementProxy(context); 963 } 964 965 // no-op constructor called just by DisabledWallpaperManager WallpaperManager()966 /*package*/ WallpaperManager() { 967 mContext = null; 968 mCmProxy = null; 969 mWcgEnabled = false; 970 } 971 972 /** 973 * Retrieve a WallpaperManager associated with the given Context. 974 */ getInstance(Context context)975 public static WallpaperManager getInstance(Context context) { 976 return (WallpaperManager)context.getSystemService( 977 Context.WALLPAPER_SERVICE); 978 } 979 980 /** @hide */ 981 @UnsupportedAppUsage getIWallpaperManager()982 public IWallpaperManager getIWallpaperManager() { 983 return sGlobals.mService; 984 } 985 986 /** 987 * TODO (b/305908217) remove 988 * Temporary method for project b/197814683. 989 * @return true if the lockscreen wallpaper always uses a wallpaperService, not a static image 990 * @hide 991 */ 992 @TestApi isLockscreenLiveWallpaperEnabled()993 public boolean isLockscreenLiveWallpaperEnabled() { 994 return true; 995 } 996 997 /** 998 * Temporary method for project b/270726737 999 * @return true if the wallpaper supports different crops for different display dimensions 1000 * @hide 1001 */ isMultiCropEnabled()1002 public static boolean isMultiCropEnabled() { 1003 if (sIsMultiCropEnabled == null) { 1004 sIsMultiCropEnabled = multiCrop(); 1005 } 1006 return sIsMultiCropEnabled; 1007 } 1008 1009 /** 1010 * Indicate whether wcg (Wide Color Gamut) should be enabled. 1011 * <p> 1012 * Some devices lack of capability of mixed color spaces composition, 1013 * enable wcg on such devices might cause memory or battery concern. 1014 * <p> 1015 * Therefore, in addition to {@link Configuration#isScreenWideColorGamut()}, 1016 * we also take mixed color spaces composition (config_enableWcgMode) into account. 1017 * 1018 * @see Configuration#isScreenWideColorGamut() 1019 * @return True if wcg should be enabled for this device. 1020 * @hide 1021 */ 1022 @TestApi shouldEnableWideColorGamut()1023 public boolean shouldEnableWideColorGamut() { 1024 return mWcgEnabled; 1025 } 1026 1027 /** 1028 * <strong> Important note: </strong> 1029 * <ul> 1030 * <li>Up to Android 12, this method requires the 1031 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> 1032 * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, 1033 * instead the default system wallpaper is returned 1034 * (some versions of Android 13 may throw a {@code SecurityException}).</li> 1035 * <li>From Android 14, this method should not be used 1036 * and will always throw a {@code SecurityException}.</li> 1037 * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 1038 * can still access the real wallpaper on all versions. </li> 1039 * </ul> 1040 * 1041 * <p> 1042 * Equivalent to {@link #getDrawable(int)} with {@code which=}{@link #FLAG_SYSTEM}. 1043 * </p> 1044 * 1045 * @return A Drawable object for the requested wallpaper. 1046 * 1047 * @see #getDrawable(int) 1048 * 1049 * @throws SecurityException as described in the note 1050 */ 1051 @Nullable 1052 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) getDrawable()1053 public Drawable getDrawable() { 1054 return getDrawable(FLAG_SYSTEM); 1055 } 1056 1057 /** 1058 * <strong> Important note: </strong> only apps with 1059 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method. 1060 * Otherwise, a {@code SecurityException} will be thrown. 1061 * 1062 * <p> 1063 * Retrieve the requested wallpaper for the specified wallpaper type if the wallpaper is not 1064 * a live wallpaper. This method should not be used to display the user wallpaper on an app: 1065 * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WALLPAPER} should be used instead. 1066 * </p> 1067 * <p> 1068 * When called with {@code which=}{@link #FLAG_SYSTEM}, 1069 * if there is a live wallpaper on home screen, the built-in default wallpaper is returned. 1070 * </p> 1071 * <p> 1072 * When called with {@code which=}{@link #FLAG_LOCK}, if there is a live wallpaper 1073 * on lock screen, or if the lock screen and home screen share the same wallpaper engine, 1074 * {@code null} is returned. 1075 * </p> 1076 * <p> 1077 * {@link #getWallpaperInfo(int)} can be used to determine whether there is a live wallpaper 1078 * on a specified screen type. 1079 * </p> 1080 * 1081 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1082 * IllegalArgumentException if an invalid wallpaper is requested. 1083 * @return A Drawable object for the requested wallpaper. 1084 * 1085 * @throws SecurityException as described in the note 1086 */ 1087 @Nullable 1088 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) getDrawable(@etWallpaperFlags int which)1089 public Drawable getDrawable(@SetWallpaperFlags int which) { 1090 final ColorManagementProxy cmProxy = getColorManagementProxy(); 1091 boolean returnDefault = which != FLAG_LOCK; 1092 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, returnDefault, which, cmProxy); 1093 if (bm != null) { 1094 Drawable dr = new BitmapDrawable(mContext.getResources(), bm); 1095 dr.setDither(false); 1096 return dr; 1097 } 1098 return null; 1099 } 1100 1101 /** 1102 * Obtain a drawable for the built-in static system wallpaper. 1103 */ getBuiltInDrawable()1104 public Drawable getBuiltInDrawable() { 1105 return getBuiltInDrawable(0, 0, false, 0, 0, FLAG_SYSTEM); 1106 } 1107 1108 /** 1109 * Obtain a drawable for the specified built-in static system wallpaper. 1110 * 1111 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1112 * IllegalArgumentException if an invalid wallpaper is requested. 1113 * @return A Drawable presenting the specified wallpaper image, or {@code null} 1114 * if no built-in default image for that wallpaper type exists. 1115 */ getBuiltInDrawable(@etWallpaperFlags int which)1116 public Drawable getBuiltInDrawable(@SetWallpaperFlags int which) { 1117 return getBuiltInDrawable(0, 0, false, 0, 0, which); 1118 } 1119 1120 /** 1121 * Returns a drawable for the system built-in static wallpaper. Based on the parameters, the 1122 * drawable can be cropped and scaled 1123 * 1124 * @param outWidth The width of the returned drawable 1125 * @param outWidth The height of the returned drawable 1126 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it 1127 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image; 1128 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned 1129 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image; 1130 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned 1131 * @return A Drawable presenting the built-in default system wallpaper image, 1132 * or {@code null} if no such default image is defined on this device. 1133 */ getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment)1134 public Drawable getBuiltInDrawable(int outWidth, int outHeight, 1135 boolean scaleToFit, float horizontalAlignment, float verticalAlignment) { 1136 return getBuiltInDrawable(outWidth, outHeight, scaleToFit, 1137 horizontalAlignment, verticalAlignment, FLAG_SYSTEM); 1138 } 1139 1140 /** 1141 * Returns a drawable for the built-in static wallpaper of the specified type. Based on the 1142 * parameters, the drawable can be cropped and scaled. 1143 * 1144 * @param outWidth The width of the returned drawable 1145 * @param outWidth The height of the returned drawable 1146 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it 1147 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image; 1148 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned 1149 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image; 1150 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned 1151 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1152 * IllegalArgumentException if an invalid wallpaper is requested. 1153 * @return A Drawable presenting the built-in default wallpaper image of the given type, 1154 * or {@code null} if no default image of that type is defined on this device. 1155 */ getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which)1156 public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, 1157 float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which) { 1158 if (sGlobals.mService == null) { 1159 Log.w(TAG, "WallpaperService not running"); 1160 throw new RuntimeException(new DeadSystemException()); 1161 } 1162 1163 checkExactlyOneWallpaperFlagSet(which); 1164 1165 Resources resources = mContext.getResources(); 1166 horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment)); 1167 verticalAlignment = Math.max(0, Math.min(1, verticalAlignment)); 1168 1169 InputStream wpStream = openDefaultWallpaper(mContext, which); 1170 if (wpStream == null) { 1171 if (DEBUG) { 1172 Log.w(TAG, "default wallpaper stream " + which + " is null"); 1173 } 1174 return null; 1175 } else { 1176 InputStream is = new BufferedInputStream(wpStream); 1177 if (outWidth <= 0 || outHeight <= 0) { 1178 Bitmap fullSize = BitmapFactory.decodeStream(is, null, null); 1179 return new BitmapDrawable(resources, fullSize); 1180 } else { 1181 int inWidth; 1182 int inHeight; 1183 // Just measure this time through... 1184 { 1185 BitmapFactory.Options options = new BitmapFactory.Options(); 1186 options.inJustDecodeBounds = true; 1187 BitmapFactory.decodeStream(is, null, options); 1188 if (options.outWidth != 0 && options.outHeight != 0) { 1189 inWidth = options.outWidth; 1190 inHeight = options.outHeight; 1191 } else { 1192 Log.e(TAG, "default wallpaper dimensions are 0"); 1193 return null; 1194 } 1195 } 1196 1197 // Reopen the stream to do the full decode. We know at this point 1198 // that openDefaultWallpaper() will return non-null. 1199 is = new BufferedInputStream(openDefaultWallpaper(mContext, which)); 1200 1201 RectF cropRectF; 1202 1203 outWidth = Math.min(inWidth, outWidth); 1204 outHeight = Math.min(inHeight, outHeight); 1205 if (scaleToFit) { 1206 cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight, 1207 horizontalAlignment, verticalAlignment); 1208 } else { 1209 float left = (inWidth - outWidth) * horizontalAlignment; 1210 float right = left + outWidth; 1211 float top = (inHeight - outHeight) * verticalAlignment; 1212 float bottom = top + outHeight; 1213 cropRectF = new RectF(left, top, right, bottom); 1214 } 1215 Rect roundedTrueCrop = new Rect(); 1216 cropRectF.roundOut(roundedTrueCrop); 1217 1218 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 1219 Log.w(TAG, "crop has bad values for full size image"); 1220 return null; 1221 } 1222 1223 // See how much we're reducing the size of the image 1224 int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth, 1225 roundedTrueCrop.height() / outHeight); 1226 1227 // Attempt to open a region decoder 1228 BitmapRegionDecoder decoder = null; 1229 try { 1230 decoder = BitmapRegionDecoder.newInstance(is, true); 1231 } catch (IOException e) { 1232 Log.w(TAG, "cannot open region decoder for default wallpaper"); 1233 } 1234 1235 Bitmap crop = null; 1236 if (decoder != null) { 1237 // Do region decoding to get crop bitmap 1238 BitmapFactory.Options options = new BitmapFactory.Options(); 1239 if (scaleDownSampleSize > 1) { 1240 options.inSampleSize = scaleDownSampleSize; 1241 } 1242 crop = decoder.decodeRegion(roundedTrueCrop, options); 1243 decoder.recycle(); 1244 } 1245 1246 if (crop == null) { 1247 // BitmapRegionDecoder has failed, try to crop in-memory. We know at 1248 // this point that openDefaultWallpaper() will return non-null. 1249 is = new BufferedInputStream(openDefaultWallpaper(mContext, which)); 1250 Bitmap fullSize = null; 1251 BitmapFactory.Options options = new BitmapFactory.Options(); 1252 if (scaleDownSampleSize > 1) { 1253 options.inSampleSize = scaleDownSampleSize; 1254 } 1255 fullSize = BitmapFactory.decodeStream(is, null, options); 1256 if (fullSize != null) { 1257 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 1258 roundedTrueCrop.top, roundedTrueCrop.width(), 1259 roundedTrueCrop.height()); 1260 } 1261 } 1262 1263 if (crop == null) { 1264 Log.w(TAG, "cannot decode default wallpaper"); 1265 return null; 1266 } 1267 1268 // Scale down if necessary 1269 if (outWidth > 0 && outHeight > 0 && 1270 (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) { 1271 Matrix m = new Matrix(); 1272 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); 1273 RectF returnRect = new RectF(0, 0, outWidth, outHeight); 1274 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 1275 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 1276 (int) returnRect.height(), Bitmap.Config.ARGB_8888); 1277 if (tmp != null) { 1278 Canvas c = new Canvas(tmp); 1279 Paint p = new Paint(); 1280 p.setFilterBitmap(true); 1281 c.drawBitmap(crop, m, p); 1282 crop = tmp; 1283 } 1284 } 1285 1286 return new BitmapDrawable(resources, crop); 1287 } 1288 } 1289 } 1290 getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight, float horizontalAlignment, float verticalAlignment)1291 private static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight, 1292 float horizontalAlignment, float verticalAlignment) { 1293 RectF cropRect = new RectF(); 1294 // Get a crop rect that will fit this 1295 if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 1296 cropRect.top = 0; 1297 cropRect.bottom = inHeight; 1298 float cropWidth = outWidth * (inHeight / (float) outHeight); 1299 cropRect.left = (inWidth - cropWidth) * horizontalAlignment; 1300 cropRect.right = cropRect.left + cropWidth; 1301 } else { 1302 cropRect.left = 0; 1303 cropRect.right = inWidth; 1304 float cropHeight = outHeight * (inWidth / (float) outWidth); 1305 cropRect.top = (inHeight - cropHeight) * verticalAlignment; 1306 cropRect.bottom = cropRect.top + cropHeight; 1307 } 1308 return cropRect; 1309 } 1310 1311 /** 1312 * <strong> Important note: </strong> 1313 * <ul> 1314 * <li>Up to Android 12, this method requires the 1315 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> 1316 * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, 1317 * instead the default system wallpaper is returned 1318 * (some versions of Android 13 may throw a {@code SecurityException}).</li> 1319 * <li>From Android 14, this method should not be used 1320 * and will always throw a {@code SecurityException}.</li> 1321 * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 1322 * can still access the real wallpaper on all versions. </li> 1323 * </ul> 1324 * 1325 * <p> 1326 * Equivalent to {@link #getDrawable()}. 1327 * </p> 1328 * 1329 * @return A Drawable object for the requested wallpaper. 1330 * 1331 * @see #getDrawable() 1332 * 1333 * @throws SecurityException as described in the note 1334 */ 1335 @Nullable 1336 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) peekDrawable()1337 public Drawable peekDrawable() { 1338 return peekDrawable(FLAG_SYSTEM); 1339 } 1340 1341 /** 1342 * <strong> Important note: </strong> only apps with 1343 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method. 1344 * Otherwise, a {@code SecurityException} will be thrown. 1345 * 1346 * <p> 1347 * Equivalent to {@link #getDrawable(int)}. 1348 * </p> 1349 * 1350 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1351 * IllegalArgumentException if an invalid wallpaper is requested. 1352 * @return A Drawable object for the requested wallpaper. 1353 * 1354 * @see #getDrawable(int) 1355 * 1356 * @throws SecurityException as described in the note 1357 */ 1358 @Nullable 1359 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) peekDrawable(@etWallpaperFlags int which)1360 public Drawable peekDrawable(@SetWallpaperFlags int which) { 1361 return getDrawable(which); 1362 } 1363 1364 /** 1365 * <strong> Important note: </strong> 1366 * <ul> 1367 * <li>Up to Android 12, this method requires the 1368 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> 1369 * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, 1370 * instead the default wallpaper is returned 1371 * (some versions of Android 13 may throw a {@code SecurityException}).</li> 1372 * <li>From Android 14, this method should not be used 1373 * and will always throw a {@code SecurityException}.</li> 1374 * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 1375 * can still access the real wallpaper on all versions. </li> 1376 * </ul> 1377 * 1378 * <p> 1379 * Equivalent to {@link #getFastDrawable(int)} with {@code which=}{@link #FLAG_SYSTEM}. 1380 * </p> 1381 * 1382 * @return A Drawable object for the requested wallpaper. 1383 * 1384 * @see #getFastDrawable(int) 1385 * 1386 * @throws SecurityException as described in the note 1387 */ 1388 @Nullable 1389 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) getFastDrawable()1390 public Drawable getFastDrawable() { 1391 return getFastDrawable(FLAG_SYSTEM); 1392 } 1393 1394 /** 1395 * <strong> Important note: </strong> only apps with 1396 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method. 1397 * Otherwise, a {@code SecurityException} will be thrown. 1398 * 1399 * Like {@link #getDrawable(int)}, but the returned Drawable has a number 1400 * of limitations to reduce its overhead as much as possible. It will 1401 * never scale the wallpaper (only centering it if the requested bounds 1402 * do match the bitmap bounds, which should not be typical), doesn't 1403 * allow setting an alpha, color filter, or other attributes, etc. The 1404 * bounds of the returned drawable will be initialized to the same bounds 1405 * as the wallpaper, so normally you will not need to touch it. The 1406 * drawable also assumes that it will be used in a context running in 1407 * the same density as the screen (not in density compatibility mode). 1408 * 1409 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1410 * IllegalArgumentException if an invalid wallpaper is requested. 1411 * @return An optimized Drawable object for the requested wallpaper, or {@code null} 1412 * in some cases as specified in {@link #getDrawable(int)}. 1413 * 1414 * @throws SecurityException as described in the note 1415 */ 1416 @Nullable 1417 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) getFastDrawable(@etWallpaperFlags int which)1418 public Drawable getFastDrawable(@SetWallpaperFlags int which) { 1419 final ColorManagementProxy cmProxy = getColorManagementProxy(); 1420 boolean returnDefault = which != FLAG_LOCK; 1421 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, returnDefault, which, cmProxy); 1422 if (bm != null) { 1423 return new FastBitmapDrawable(bm); 1424 } 1425 return null; 1426 } 1427 1428 /** 1429 * <strong> Important note: </strong> only apps with 1430 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method. 1431 * Otherwise, a {@code SecurityException} will be thrown. 1432 * 1433 * <p> 1434 * Equivalent to {@link #getFastDrawable()}. 1435 * </p> 1436 * 1437 * @return An optimized Drawable object for the requested wallpaper. 1438 * 1439 * @see #getFastDrawable() 1440 * 1441 * @throws SecurityException as described in the note 1442 */ 1443 @Nullable 1444 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) peekFastDrawable()1445 public Drawable peekFastDrawable() { 1446 return peekFastDrawable(FLAG_SYSTEM); 1447 } 1448 1449 /** 1450 * <strong> Important note: </strong> only apps with 1451 * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 1452 * should use this method. Otherwise, a {@code SecurityException} will be thrown. 1453 * 1454 * <p> 1455 * Equivalent to {@link #getFastDrawable(int)}. 1456 * </p> 1457 * 1458 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 1459 * IllegalArgumentException if an invalid wallpaper is requested. 1460 * @return An optimized Drawable object for the requested wallpaper. 1461 * 1462 * @throws SecurityException as described in the note 1463 */ 1464 @Nullable 1465 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) peekFastDrawable(@etWallpaperFlags int which)1466 public Drawable peekFastDrawable(@SetWallpaperFlags int which) { 1467 return getFastDrawable(which); 1468 } 1469 1470 /** 1471 * Whether the wallpaper supports Wide Color Gamut or not. This is only meant to be used by 1472 * ImageWallpaper, and will always return false if the wallpaper for the specified screen 1473 * is not an ImageWallpaper. This will also return false when called with {@link #FLAG_LOCK} if 1474 * the lock and home screen share the same wallpaper engine. 1475 * 1476 * @param which The wallpaper whose image file is to be retrieved. Must be a single 1477 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. 1478 * @return true when supported. 1479 * 1480 * @see #FLAG_LOCK 1481 * @see #FLAG_SYSTEM 1482 * @hide 1483 */ 1484 @TestApi wallpaperSupportsWcg(int which)1485 public boolean wallpaperSupportsWcg(int which) { 1486 if (!shouldEnableWideColorGamut()) { 1487 return false; 1488 } 1489 final ColorManagementProxy cmProxy = getColorManagementProxy(); 1490 Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, cmProxy); 1491 return bitmap != null && bitmap.getColorSpace() != null 1492 && bitmap.getColorSpace() != ColorSpace.get(ColorSpace.Named.SRGB) 1493 && cmProxy.isSupportedColorSpace(bitmap.getColorSpace()); 1494 } 1495 1496 /** 1497 * Like {@link #getDrawable()} but returns a Bitmap with default {@link Bitmap.Config}. 1498 * 1499 * @hide 1500 */ 1501 @TestApi 1502 @Nullable 1503 @UnsupportedAppUsage getBitmap()1504 public Bitmap getBitmap() { 1505 return getBitmap(false); 1506 } 1507 1508 /** 1509 * Like {@link #getDrawable()} but returns a Bitmap. 1510 * 1511 * @param hardware Asks for a hardware backed bitmap. 1512 * @see Bitmap.Config#HARDWARE 1513 * @hide 1514 */ 1515 @UnsupportedAppUsage getBitmap(boolean hardware)1516 public Bitmap getBitmap(boolean hardware) { 1517 return getBitmapAsUser(mContext.getUserId(), hardware); 1518 } 1519 1520 /** 1521 * Like {@link #getDrawable(int)} but returns a Bitmap. 1522 * 1523 * @param hardware Asks for a hardware backed bitmap. 1524 * @param which Specifies home or lock screen 1525 * @see Bitmap.Config#HARDWARE 1526 * @hide 1527 */ 1528 @Nullable getBitmap(boolean hardware, @SetWallpaperFlags int which)1529 public Bitmap getBitmap(boolean hardware, @SetWallpaperFlags int which) { 1530 return getBitmapAsUser(mContext.getUserId(), hardware, which); 1531 } 1532 1533 /** 1534 * Like {@link #getDrawable()} but returns a Bitmap for the provided user. 1535 * 1536 * @hide 1537 */ getBitmapAsUser(int userId, boolean hardware)1538 public Bitmap getBitmapAsUser(int userId, boolean hardware) { 1539 final ColorManagementProxy cmProxy = getColorManagementProxy(); 1540 return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware, cmProxy); 1541 } 1542 1543 /** 1544 * Like {@link #getDrawable(int)} but returns a Bitmap for the provided user. 1545 * 1546 * @param which Specifies home or lock screen 1547 * @hide 1548 */ 1549 @TestApi 1550 @Nullable getBitmapAsUser(int userId, boolean hardware, @SetWallpaperFlags int which)1551 public Bitmap getBitmapAsUser(int userId, boolean hardware, @SetWallpaperFlags int which) { 1552 boolean returnDefault = which != FLAG_LOCK; 1553 return getBitmapAsUser(userId, hardware, which, returnDefault); 1554 } 1555 1556 /** 1557 * Overload of {@link #getBitmapAsUser(int, boolean, int)} with a returnDefault argument. 1558 * 1559 * @param returnDefault If true, return the default static wallpaper if no custom static 1560 * wallpaper is set on the specified screen. 1561 * If false, return {@code null} in that case. 1562 * @hide 1563 */ 1564 @Nullable getBitmapAsUser(int userId, boolean hardware, @SetWallpaperFlags int which, boolean returnDefault)1565 public Bitmap getBitmapAsUser(int userId, boolean hardware, 1566 @SetWallpaperFlags int which, boolean returnDefault) { 1567 final ColorManagementProxy cmProxy = getColorManagementProxy(); 1568 return sGlobals.peekWallpaperBitmap(mContext, returnDefault, 1569 which, userId, hardware, cmProxy); 1570 } 1571 1572 /** 1573 * Peek the dimensions of system wallpaper of the user without decoding it. 1574 * Equivalent to {@link #peekBitmapDimensions(int)} with {@code which=}{@link #FLAG_SYSTEM}. 1575 * 1576 * @return the dimensions of system wallpaper 1577 * @hide 1578 */ 1579 @TestApi 1580 @Nullable peekBitmapDimensions()1581 public Rect peekBitmapDimensions() { 1582 return peekBitmapDimensions(FLAG_SYSTEM); 1583 } 1584 1585 /** 1586 * Peek the dimensions of given wallpaper of the user without decoding it. 1587 * 1588 * <p> 1589 * When called with {@code which=}{@link #FLAG_SYSTEM}, if there is a live wallpaper on 1590 * home screen, the built-in default wallpaper dimensions are returned. 1591 * </p> 1592 * <p> 1593 * When called with {@code which=}{@link #FLAG_LOCK}, if there is a live wallpaper 1594 * on lock screen, or if the lock screen and home screen share the same wallpaper engine, 1595 * {@code null} is returned. 1596 * </p> 1597 * <p> 1598 * {@link #getWallpaperInfo(int)} can be used to determine whether there is a live wallpaper 1599 * on a specified screen type. 1600 * </p> 1601 * 1602 * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. 1603 * @return the dimensions of specified wallpaper 1604 * @hide 1605 */ 1606 @TestApi 1607 @Nullable peekBitmapDimensions(@etWallpaperFlags int which)1608 public Rect peekBitmapDimensions(@SetWallpaperFlags int which) { 1609 boolean returnDefault = which != FLAG_LOCK; 1610 return peekBitmapDimensions(which, returnDefault); 1611 } 1612 1613 /** 1614 * Overload of {@link #peekBitmapDimensions(int)} with a returnDefault argument. 1615 * 1616 * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. 1617 * @param returnDefault If true, always return the default static wallpaper dimensions 1618 * if no custom static wallpaper is set on the specified screen. 1619 * If false, always return {@code null} in that case. 1620 * @return the dimensions of specified wallpaper 1621 * @hide 1622 */ 1623 @Nullable peekBitmapDimensions(@etWallpaperFlags int which, boolean returnDefault)1624 public Rect peekBitmapDimensions(@SetWallpaperFlags int which, boolean returnDefault) { 1625 if (multiCrop()) { 1626 return peekBitmapDimensionsAsUser(which, returnDefault, mContext.getUserId()); 1627 } 1628 checkExactlyOneWallpaperFlagSet(which); 1629 return sGlobals.peekWallpaperDimensions(mContext, returnDefault, which, 1630 mContext.getUserId()); 1631 } 1632 1633 /** 1634 * Overload of {@link #peekBitmapDimensions(int, boolean)} with a userId argument. 1635 * TODO(b/360120606): remove the SuppressWarnings 1636 * @hide 1637 */ 1638 @SuppressWarnings("AndroidFrameworkContextUserId") 1639 @FlaggedApi(FLAG_MULTI_CROP) 1640 @Nullable peekBitmapDimensionsAsUser(@etWallpaperFlags int which, boolean returnDefault, int userId)1641 public Rect peekBitmapDimensionsAsUser(@SetWallpaperFlags int which, boolean returnDefault, 1642 int userId) { 1643 checkExactlyOneWallpaperFlagSet(which); 1644 return sGlobals.peekWallpaperDimensions(mContext, returnDefault, which, userId); 1645 } 1646 1647 /** 1648 * For the current user, given a list of display sizes, return a list of rectangles representing 1649 * the area of the current wallpaper that would be shown for each of these sizes. 1650 * 1651 * @param displaySizes the display sizes. 1652 * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. 1653 * @param originalBitmap If true, return areas relative to the original bitmap. 1654 * If false, return areas relative to the cropped bitmap. 1655 * @return A List of Rect where the Rect is within the cropped/original bitmap, and corresponds 1656 * to what is displayed. The Rect may have a larger width/height ratio than the screen 1657 * due to parallax. Return an empty list if the wallpaper is not an ImageWallpaper. 1658 * Also return an empty list when called with which={@link #FLAG_LOCK} if there is a 1659 * shared home + lock wallpaper. 1660 * @hide 1661 */ 1662 @FlaggedApi(FLAG_MULTI_CROP) 1663 @TestApi 1664 @RequiresPermission(READ_WALLPAPER_INTERNAL) 1665 @NonNull getBitmapCrops(@onNull List<Point> displaySizes, @SetWallpaperFlags int which, boolean originalBitmap)1666 public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes, 1667 @SetWallpaperFlags int which, boolean originalBitmap) { 1668 checkExactlyOneWallpaperFlagSet(which); 1669 try { 1670 List<Rect> result = sGlobals.mService.getBitmapCrops( 1671 displaySizes, which, originalBitmap, mContext.getUserId()); 1672 if (result != null) return result; 1673 // mService.getBitmapCrops returns null if the requested wallpaper is an ImageWallpaper, 1674 // but there are no crop hints and the bitmap size is unknown to the service (this 1675 // mostly happens for the default wallpaper). In that case, fetch the bitmap dimensions 1676 // and use the other getBitmapCrops API with no cropHints to figure out the crops. 1677 Rect bitmapDimensions = peekBitmapDimensions(which, true); 1678 if (bitmapDimensions == null) return List.of(); 1679 Point bitmapSize = new Point(bitmapDimensions.width(), bitmapDimensions.height()); 1680 return getBitmapCrops(bitmapSize, displaySizes, null); 1681 1682 } catch (RemoteException e) { 1683 throw e.rethrowFromSystemServer(); 1684 } 1685 } 1686 1687 /** 1688 * For preview purposes. 1689 * Return how a bitmap of a given size would be cropped for a given list of display sizes, if 1690 * it was set as wallpaper via {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or 1691 * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. 1692 * 1693 * @return A List of Rect where the Rect is within the bitmap, and corresponds to what is 1694 * displayed for each display size. The Rect may have a larger width/height ratio than 1695 * the display due to parallax. 1696 * @hide 1697 */ 1698 @FlaggedApi(FLAG_MULTI_CROP) 1699 @TestApi 1700 @NonNull getBitmapCrops(@onNull Point bitmapSize, @NonNull List<Point> displaySizes, @Nullable Map<Point, Rect> cropHints)1701 public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes, 1702 @Nullable Map<Point, Rect> cropHints) { 1703 try { 1704 if (cropHints == null) cropHints = Map.of(); 1705 Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); 1706 int[] screenOrientations = entries.stream().mapToInt(entry -> 1707 getOrientation(entry.getKey())).toArray(); 1708 List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); 1709 return sGlobals.mService.getFutureBitmapCrops(bitmapSize, displaySizes, 1710 screenOrientations, crops); 1711 } catch (RemoteException e) { 1712 throw e.rethrowFromSystemServer(); 1713 } 1714 } 1715 1716 /** 1717 * For preview purposes. 1718 * Compute the wallpaper colors of the given bitmap, if it was set as wallpaper via 1719 * {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or 1720 * {@link #setStreamWithCrops(InputStream, Map, boolean, int)}. 1721 * Return {@code null} if an error occurred and the colors could not be computed. 1722 * 1723 * @hide 1724 */ 1725 @FlaggedApi(FLAG_MULTI_CROP) 1726 @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) 1727 @Nullable getWallpaperColors(@onNull Bitmap bitmap, @Nullable Map<Point, Rect> cropHints)1728 public WallpaperColors getWallpaperColors(@NonNull Bitmap bitmap, 1729 @Nullable Map<Point, Rect> cropHints) { 1730 if (sGlobals.mService == null) { 1731 Log.w(TAG, "WallpaperService not running"); 1732 throw new RuntimeException(new DeadSystemException()); 1733 } 1734 try { 1735 if (cropHints == null) cropHints = Map.of(); 1736 Set<Map.Entry<Point, Rect>> entries = cropHints.entrySet(); 1737 int[] screenOrientations = entries.stream().mapToInt(entry -> 1738 getOrientation(entry.getKey())).toArray(); 1739 List<Rect> crops = entries.stream().map(Map.Entry::getValue).toList(); 1740 Point bitmapSize = new Point(bitmap.getWidth(), bitmap.getHeight()); 1741 Rect crop = sGlobals.mService.getBitmapCrop(bitmapSize, screenOrientations, crops); 1742 float dimAmount = getWallpaperDimAmount(); 1743 Bitmap croppedBitmap = Bitmap.createBitmap( 1744 bitmap, crop.left, crop.top, crop.width(), crop.height()); 1745 WallpaperColors result = WallpaperColors.fromBitmap(croppedBitmap, dimAmount); 1746 return result; 1747 } catch (RemoteException e) { 1748 throw e.rethrowFromSystemServer(); 1749 } 1750 } 1751 1752 /** 1753 * <strong> Important note: </strong> 1754 * <ul> 1755 * <li>Up to Android 12, this method requires the 1756 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li> 1757 * <li>Starting in Android 13, directly accessing the wallpaper is not possible anymore, 1758 * instead the default system wallpaper is returned 1759 * (some versions of Android 13 may throw a {@code SecurityException}).</li> 1760 * <li>From Android 14, this method should not be used 1761 * and will always throw a {@code SecurityException}.</li> 1762 * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} 1763 * can still access the real wallpaper on all versions. </li> 1764 * </ul> 1765 * <br> 1766 * 1767 * Get an open, readable file descriptor to the given wallpaper image file. 1768 * The caller is responsible for closing the file descriptor when done ingesting the file. 1769 * 1770 * <p>If no lock-specific wallpaper has been configured for the given user, then 1771 * this method will return {@code null} when requesting {@link #FLAG_LOCK} rather than 1772 * returning the system wallpaper's image file. 1773 * 1774 * @param which The wallpaper whose image file is to be retrieved. Must be a single 1775 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 1776 * {@link #FLAG_LOCK}. 1777 * @return An open, readable file descriptor to the requested wallpaper image file; 1778 * or {@code null} if no such wallpaper is configured or if the calling app does 1779 * not have permission to read the current wallpaper. 1780 * 1781 * @see #FLAG_LOCK 1782 * @see #FLAG_SYSTEM 1783 * 1784 * @throws SecurityException as described in the note 1785 */ 1786 @Nullable 1787 @RequiresPermission(anyOf = {MANAGE_EXTERNAL_STORAGE, READ_WALLPAPER_INTERNAL}) getWallpaperFile(@etWallpaperFlags int which)1788 public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which) { 1789 return getWallpaperFile(which, mContext.getUserId()); 1790 } 1791 1792 /** 1793 * Registers a listener to get notified when the wallpaper colors change. 1794 * @param listener A listener to register 1795 * @param handler Where to call it from. Will be called from the main thread 1796 * if null. 1797 */ addOnColorsChangedListener(@onNull OnColorsChangedListener listener, @NonNull Handler handler)1798 public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener, 1799 @NonNull Handler handler) { 1800 addOnColorsChangedListener(listener, handler, mContext.getUserId()); 1801 } 1802 1803 /** 1804 * Registers a listener to get notified when the wallpaper colors change 1805 * @param listener A listener to register 1806 * @param handler Where to call it from. Will be called from the main thread 1807 * if null. 1808 * @param userId Owner of the wallpaper or UserHandle.USER_ALL. 1809 * @hide 1810 */ 1811 @UnsupportedAppUsage addOnColorsChangedListener(@onNull OnColorsChangedListener listener, @NonNull Handler handler, int userId)1812 public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener, 1813 @NonNull Handler handler, int userId) { 1814 sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId()); 1815 } 1816 1817 /** 1818 * Stop listening to color updates. 1819 * @param callback A callback to unsubscribe. 1820 */ removeOnColorsChangedListener(@onNull OnColorsChangedListener callback)1821 public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) { 1822 removeOnColorsChangedListener(callback, mContext.getUserId()); 1823 } 1824 1825 /** 1826 * Stop listening to color updates. 1827 * @param callback A callback to unsubscribe. 1828 * @param userId Owner of the wallpaper or UserHandle.USER_ALL. 1829 * @hide 1830 */ removeOnColorsChangedListener(@onNull OnColorsChangedListener callback, int userId)1831 public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback, 1832 int userId) { 1833 sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId()); 1834 } 1835 1836 /** 1837 * Get the primary colors of a wallpaper. 1838 * 1839 * <p>This method can return {@code null} when: 1840 * <ul> 1841 * <li>Colors are still being processed by the system.</li> 1842 * <li>The user has chosen to use a live wallpaper: live wallpapers might not 1843 * implement 1844 * {@link android.service.wallpaper.WallpaperService.Engine#onComputeColors() 1845 * WallpaperService.Engine#onComputeColors()}.</li> 1846 * </ul> 1847 * <p>Please note that this API will go through IPC and may take some time to 1848 * calculate the wallpaper color, which could block the caller thread, so it is 1849 * not recommended to call this in the UI thread.</p> 1850 * 1851 * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or 1852 * {@link #FLAG_LOCK}. 1853 * @return Current {@link WallpaperColors} or null if colors are unknown. 1854 * @see #addOnColorsChangedListener(OnColorsChangedListener, Handler) 1855 */ getWallpaperColors(int which)1856 public @Nullable WallpaperColors getWallpaperColors(int which) { 1857 return getWallpaperColors(which, mContext.getUserId()); 1858 } 1859 1860 // TODO(b/181083333): add multiple root display area support on this API. 1861 /** 1862 * Get the primary colors of the wallpaper configured in the given user. 1863 * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or 1864 * {@link #FLAG_LOCK} 1865 * @param userId Owner of the wallpaper. 1866 * @return {@link WallpaperColors} or null if colors are unknown. 1867 * @hide 1868 */ 1869 @UnsupportedAppUsage getWallpaperColors(int which, int userId)1870 public @Nullable WallpaperColors getWallpaperColors(int which, int userId) { 1871 StrictMode.assertUiContext(mContext, "getWallpaperColors"); 1872 return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId()); 1873 } 1874 1875 /** 1876 * @hide 1877 */ addOnColorsChangedListener(@onNull LocalWallpaperColorConsumer callback, List<RectF> regions, int which)1878 public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, 1879 List<RectF> regions, int which) throws IllegalArgumentException { 1880 for (RectF region : regions) { 1881 if (!LOCAL_COLOR_BOUNDS.contains(region)) { 1882 throw new IllegalArgumentException("Regions must be within bounds " 1883 + LOCAL_COLOR_BOUNDS); 1884 } 1885 } 1886 sGlobals.addOnColorsChangedListener(callback, regions, which, 1887 mContext.getUserId(), mContext.getDisplayId()); 1888 } 1889 1890 /** 1891 * @hide 1892 */ removeOnColorsChangedListener(@onNull LocalWallpaperColorConsumer callback)1893 public void removeOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback) { 1894 sGlobals.removeOnColorsChangedListener(callback, FLAG_SYSTEM, mContext.getUserId(), 1895 mContext.getDisplayId()); 1896 } 1897 1898 /** 1899 * Version of {@link #getWallpaperFile(int)} that can access the wallpaper data 1900 * for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL 1901 * permission to access another user's wallpaper data. 1902 * 1903 * @param which The wallpaper whose image file is to be retrieved. Must be a single 1904 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 1905 * {@link #FLAG_LOCK}. 1906 * @param userId The user or profile whose imagery is to be retrieved 1907 * 1908 * @see #FLAG_LOCK 1909 * @see #FLAG_SYSTEM 1910 * 1911 * @hide 1912 */ 1913 @UnsupportedAppUsage getWallpaperFile(@etWallpaperFlags int which, int userId)1914 public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, int userId) { 1915 return getWallpaperFile(which, userId, /* getCropped = */ true); 1916 } 1917 1918 /** 1919 * Version of {@link #getWallpaperFile(int)} that allows specifying whether to get the 1920 * cropped version of the wallpaper file or the original. 1921 * 1922 * @param which The wallpaper whose image file is to be retrieved. Must be a single 1923 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. 1924 * @param getCropped If true the cropped file will be retrieved, if false the original will 1925 * be retrieved. 1926 * @return A ParcelFileDescriptor for the wallpaper bitmap of the given destination, if it's an 1927 * ImageWallpaper wallpaper. Return {@code null} if the wallpaper is not an 1928 * ImageWallpaper. Also return {@code null} when called with 1929 * which={@link #FLAG_LOCK} if there is a shared home + lock wallpaper. 1930 * @hide 1931 */ 1932 @Nullable getWallpaperFile(@etWallpaperFlags int which, boolean getCropped)1933 public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, boolean getCropped) { 1934 return getWallpaperFile(which, mContext.getUserId(), getCropped); 1935 } 1936 getWallpaperFile(@etWallpaperFlags int which, int userId, boolean getCropped)1937 private ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, int userId, 1938 boolean getCropped) { 1939 checkExactlyOneWallpaperFlagSet(which); 1940 1941 if (sGlobals.mService == null) { 1942 Log.w(TAG, "WallpaperService not running"); 1943 throw new RuntimeException(new DeadSystemException()); 1944 } else { 1945 try { 1946 Bundle outParams = new Bundle(); 1947 return sGlobals.mService.getWallpaperWithFeature(mContext.getOpPackageName(), 1948 mContext.getAttributionTag(), null, which, outParams, 1949 userId, getCropped); 1950 } catch (RemoteException e) { 1951 throw e.rethrowFromSystemServer(); 1952 } catch (SecurityException e) { 1953 if (CompatChanges.isChangeEnabled(RETURN_DEFAULT_ON_SECURITY_EXCEPTION) 1954 && !CompatChanges.isChangeEnabled(THROW_ON_SECURITY_EXCEPTION)) { 1955 Log.w(TAG, "No permission to access wallpaper, returning default" 1956 + " wallpaper file to avoid crashing legacy app."); 1957 return getDefaultSystemWallpaperFile(); 1958 } 1959 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) { 1960 Log.w(TAG, "No permission to access wallpaper, suppressing" 1961 + " exception to avoid crashing legacy app."); 1962 return null; 1963 } 1964 throw e; 1965 } 1966 } 1967 } 1968 1969 /** 1970 * Remove all internal references to the last loaded wallpaper. Useful 1971 * for apps that want to reduce memory usage when they only temporarily 1972 * need to have the wallpaper. After calling, the next request for the 1973 * wallpaper will require reloading it again from disk. 1974 */ forgetLoadedWallpaper()1975 public void forgetLoadedWallpaper() { 1976 sGlobals.forgetLoadedWallpaper(); 1977 } 1978 1979 /** 1980 * Returns the information about the home screen wallpaper if its current wallpaper is a live 1981 * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, or if the 1982 * caller doesn't have the appropriate permissions, this returns {@code null}. 1983 * 1984 * <p> 1985 * For devices running Android 13 or earlier, this method requires the 1986 * {@link android.Manifest.permission#QUERY_ALL_PACKAGES} permission. 1987 * </p> 1988 * 1989 * <p> 1990 * For devices running Android 14 or later, in order to use this, apps should declare a 1991 * {@code <queries>} tag with the action {@code "android.service.wallpaper.WallpaperService"}. 1992 * Otherwise, this method will return {@code null} if the caller doesn't otherwise have 1993 * <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package. 1994 * </p> 1995 */ 1996 @RequiresPermission(value = "QUERY_ALL_PACKAGES", conditional = true) getWallpaperInfo()1997 public WallpaperInfo getWallpaperInfo() { 1998 return getWallpaperInfoForUser(mContext.getUserId()); 1999 } 2000 2001 /** 2002 * Returns the information about the home screen wallpaper if its current wallpaper is a live 2003 * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null. 2004 * 2005 * @param userId Owner of the wallpaper. 2006 * @hide 2007 */ getWallpaperInfoForUser(int userId)2008 public WallpaperInfo getWallpaperInfoForUser(int userId) { 2009 return getWallpaperInfo(FLAG_SYSTEM, userId); 2010 } 2011 2012 /** 2013 * Returns the information about the designated wallpaper if its current wallpaper is a live 2014 * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, or if 2015 * the caller doesn't have the appropriate permissions, this returns {@code null}. 2016 * 2017 * <p> 2018 * In order to use this, apps should declare a {@code <queries>} tag with the action 2019 * {@code "android.service.wallpaper.WallpaperService"}. Otherwise, this method will return 2020 * {@code null} if the caller doesn't otherwise have 2021 * <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package. 2022 * </p> 2023 * 2024 * @param which Specifies wallpaper to request (home or lock). 2025 * @throws IllegalArgumentException if {@code which} is not exactly one of 2026 * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}. 2027 */ 2028 @Nullable getWallpaperInfo(@etWallpaperFlags int which)2029 public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) { 2030 return getWallpaperInfo(which, mContext.getUserId()); 2031 } 2032 2033 /** 2034 * Returns the information about the designated wallpaper if its current wallpaper is a live 2035 * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, or if the 2036 * caller doesn't have the appropriate permissions, this returns {@code null}. 2037 * 2038 * <p> 2039 * In order to use this, apps should declare a {@code <queries>} tag 2040 * with the action {@code "android.service.wallpaper.WallpaperService"}. Otherwise, 2041 * this method will return {@code null} if the caller doesn't otherwise have 2042 * <a href="{@docRoot}training/package-visibility">visibility</a> of the wallpaper package. 2043 * </p> 2044 * 2045 * @param which Specifies wallpaper to request (home or lock). 2046 * @param userId Owner of the wallpaper. 2047 * @throws IllegalArgumentException if {@code which} is not exactly one of 2048 * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}. 2049 * @hide 2050 */ getWallpaperInfo(@etWallpaperFlags int which, int userId)2051 public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) { 2052 checkExactlyOneWallpaperFlagSet(which); 2053 try { 2054 if (sGlobals.mService == null) { 2055 Log.w(TAG, "WallpaperService not running"); 2056 throw new RuntimeException(new DeadSystemException()); 2057 } else { 2058 return sGlobals.mService.getWallpaperInfoWithFlags(which, userId); 2059 } 2060 } catch (RemoteException e) { 2061 throw e.rethrowFromSystemServer(); 2062 } 2063 } 2064 2065 /** 2066 * Returns the description of the designated wallpaper. Returns null if the lock screen 2067 * wallpaper is requested and lock screen wallpaper is not set. 2068 2069 * @param which Specifies wallpaper to request (home or lock). 2070 * @throws IllegalArgumentException if {@code which} is not exactly one of 2071 * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}. 2072 * 2073 * @hide 2074 */ 2075 @Nullable 2076 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 2077 @RequiresPermission(READ_WALLPAPER_INTERNAL) 2078 @SystemApi getWallpaperInstance(@etWallpaperFlags int which)2079 public WallpaperInstance getWallpaperInstance(@SetWallpaperFlags int which) { 2080 checkExactlyOneWallpaperFlagSet(which); 2081 try { 2082 if (sGlobals.mService == null) { 2083 Log.w(TAG, "WallpaperService not running"); 2084 throw new RuntimeException(new DeadSystemException()); 2085 } else { 2086 return sGlobals.mService.getWallpaperInstance(which, mContext.getUserId()); 2087 } 2088 } catch (RemoteException e) { 2089 throw e.rethrowFromSystemServer(); 2090 } 2091 } 2092 2093 /** 2094 * Get an open, readable file descriptor for the file that contains metadata about the 2095 * context user's wallpaper. 2096 * 2097 * The caller is responsible for closing the file descriptor when done ingesting the file. 2098 * 2099 * @hide 2100 */ 2101 @Nullable getWallpaperInfoFile()2102 public ParcelFileDescriptor getWallpaperInfoFile() { 2103 if (sGlobals.mService == null) { 2104 Log.w(TAG, "WallpaperService not running"); 2105 throw new RuntimeException(new DeadSystemException()); 2106 } else { 2107 try { 2108 return sGlobals.mService.getWallpaperInfoFile(mContext.getUserId()); 2109 } catch (RemoteException e) { 2110 throw e.rethrowFromSystemServer(); 2111 } 2112 } 2113 } 2114 2115 /** 2116 * Get the ID of the current wallpaper of the given kind. If there is no 2117 * such wallpaper configured, returns a negative number. 2118 * 2119 * <p>Every time the wallpaper image is set, a new ID is assigned to it. 2120 * This method allows the caller to determine whether the wallpaper imagery 2121 * has changed, regardless of how that change happened. 2122 * 2123 * @param which The wallpaper whose ID is to be returned. Must be a single 2124 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 2125 * {@link #FLAG_LOCK}. 2126 * @return The positive numeric ID of the current wallpaper of the given kind, 2127 * or a negative value if no such wallpaper is configured. 2128 */ getWallpaperId(@etWallpaperFlags int which)2129 public int getWallpaperId(@SetWallpaperFlags int which) { 2130 return getWallpaperIdForUser(which, mContext.getUserId()); 2131 } 2132 2133 /** 2134 * Get the ID of the given user's current wallpaper of the given kind. If there 2135 * is no such wallpaper configured, returns a negative number. 2136 * @hide 2137 */ getWallpaperIdForUser(@etWallpaperFlags int which, int userId)2138 public int getWallpaperIdForUser(@SetWallpaperFlags int which, int userId) { 2139 try { 2140 if (sGlobals.mService == null) { 2141 Log.w(TAG, "WallpaperService not running"); 2142 throw new RuntimeException(new DeadSystemException()); 2143 } else { 2144 return sGlobals.mService.getWallpaperIdForUser(which, userId); 2145 } 2146 } catch (RemoteException e) { 2147 throw e.rethrowFromSystemServer(); 2148 } 2149 } 2150 2151 /** 2152 * Gets an Intent that will launch an activity that crops the given 2153 * image and sets the device's wallpaper. If there is a default HOME activity 2154 * that supports cropping wallpapers, it will be preferred as the default. 2155 * Use this method instead of directly creating a {@link #ACTION_CROP_AND_SET_WALLPAPER} 2156 * intent. 2157 * 2158 * @param imageUri The image URI that will be set in the intent. The must be a content 2159 * URI and its provider must resolve its type to "image/*" 2160 * 2161 * @throws IllegalArgumentException if the URI is not a content URI or its MIME type is 2162 * not "image/*" 2163 */ getCropAndSetWallpaperIntent(Uri imageUri)2164 public Intent getCropAndSetWallpaperIntent(Uri imageUri) { 2165 if (imageUri == null) { 2166 throw new IllegalArgumentException("Image URI must not be null"); 2167 } 2168 2169 if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) { 2170 throw new IllegalArgumentException("Image URI must be of the " 2171 + ContentResolver.SCHEME_CONTENT + " scheme type"); 2172 } 2173 2174 final PackageManager packageManager = mContext.getPackageManager(); 2175 Intent cropAndSetWallpaperIntent = 2176 new Intent(ACTION_CROP_AND_SET_WALLPAPER, imageUri); 2177 cropAndSetWallpaperIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 2178 2179 // Find out if the default HOME activity supports CROP_AND_SET_WALLPAPER 2180 Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); 2181 ResolveInfo resolvedHome = packageManager.resolveActivity(homeIntent, 2182 PackageManager.MATCH_DEFAULT_ONLY); 2183 if (resolvedHome != null) { 2184 cropAndSetWallpaperIntent.setPackage(resolvedHome.activityInfo.packageName); 2185 2186 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 2187 cropAndSetWallpaperIntent, 0); 2188 if (cropAppList.size() > 0) { 2189 return cropAndSetWallpaperIntent; 2190 } 2191 } 2192 2193 // fallback crop activity 2194 final String cropperPackage = mContext.getString( 2195 com.android.internal.R.string.config_wallpaperCropperPackage); 2196 cropAndSetWallpaperIntent.setPackage(cropperPackage); 2197 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 2198 cropAndSetWallpaperIntent, 0); 2199 if (cropAppList.size() > 0) { 2200 return cropAndSetWallpaperIntent; 2201 } 2202 // If the URI is not of the right type, or for some reason the system wallpaper 2203 // cropper doesn't exist, return null 2204 throw new IllegalArgumentException("Cannot use passed URI to set wallpaper; " + 2205 "check that the type returned by ContentProvider matches image/*"); 2206 } 2207 2208 /** 2209 * Change the current system wallpaper to the bitmap in the given resource. 2210 * The resource is opened as a raw data stream and copied into the 2211 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent 2212 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 2213 * 2214 * <p>This method requires the caller to hold the permission 2215 * {@link android.Manifest.permission#SET_WALLPAPER}. 2216 * 2217 * @param resid The resource ID of the bitmap to be used as the wallpaper image 2218 * 2219 * @throws IOException If an error occurs reverting to the built-in 2220 * wallpaper. 2221 */ 2222 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setResource(@awRes int resid)2223 public void setResource(@RawRes int resid) throws IOException { 2224 setResource(resid, FLAG_SYSTEM | FLAG_LOCK); 2225 } 2226 2227 /** 2228 * Version of {@link #setResource(int)} that allows the caller to specify which 2229 * of the supported wallpaper categories to set. 2230 * 2231 * @param resid The resource ID of the bitmap to be used as the wallpaper image 2232 * @param which Flags indicating which wallpaper(s) to configure with the new imagery 2233 * 2234 * @see #FLAG_LOCK 2235 * @see #FLAG_SYSTEM 2236 * 2237 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2238 * 2239 * @throws IOException 2240 */ 2241 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setResource(@awRes int resid, @SetWallpaperFlags int which)2242 public int setResource(@RawRes int resid, @SetWallpaperFlags int which) 2243 throws IOException { 2244 if (sGlobals.mService == null) { 2245 Log.w(TAG, "WallpaperService not running"); 2246 throw new RuntimeException(new DeadSystemException()); 2247 } 2248 final Bundle result = new Bundle(); 2249 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 2250 try { 2251 Resources resources = mContext.getResources(); 2252 /* Set the wallpaper to the default values */ 2253 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( 2254 "res:" + resources.getResourceName(resid), 2255 mContext.getOpPackageName(), null, null, false, result, which, completion, 2256 mContext.getUserId()); 2257 if (fd != null) { 2258 FileOutputStream fos = null; 2259 try { 2260 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 2261 copyStreamToWallpaperFile(resources.openRawResource(resid), fos); 2262 // The 'close()' is the trigger for any server-side image manipulation, 2263 // so we must do that before waiting for completion. 2264 fos.close(); 2265 completion.waitForCompletion(); 2266 } finally { 2267 // Might be redundant but completion shouldn't wait unless the write 2268 // succeeded; this is a fallback if it threw past the close+wait. 2269 IoUtils.closeQuietly(fos); 2270 } 2271 } 2272 } catch (RemoteException e) { 2273 throw e.rethrowFromSystemServer(); 2274 } 2275 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 2276 } 2277 2278 /** 2279 * Change the current system wallpaper to a bitmap. The given bitmap is 2280 * converted to a PNG and stored as the wallpaper. On success, the intent 2281 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 2282 * 2283 * <p>This method is equivalent to calling 2284 * {@link #setBitmap(Bitmap, Rect, boolean)} and passing {@code null} for the 2285 * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup} 2286 * parameter. 2287 * 2288 * <p>This method requires the caller to hold the permission 2289 * {@link android.Manifest.permission#SET_WALLPAPER}. 2290 * 2291 * @param bitmap The bitmap to be used as the new system wallpaper. 2292 * 2293 * @throws IOException If an error occurs when attempting to set the wallpaper 2294 * to the provided image. 2295 */ 2296 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmap(Bitmap bitmap)2297 public void setBitmap(Bitmap bitmap) throws IOException { 2298 setBitmap(bitmap, null, true); 2299 } 2300 2301 /** 2302 * Change the current system wallpaper to a bitmap, specifying a hint about 2303 * which subrectangle of the full image is to be visible. The OS will then 2304 * try to best present the given portion of the full image as the static system 2305 * wallpaper image. On success, the intent 2306 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 2307 * 2308 * <p>Passing {@code null} as the {@code visibleHint} parameter is equivalent to 2309 * passing (0, 0, {@code fullImage.getWidth()}, {@code fullImage.getHeight()}). 2310 * 2311 * <p>This method requires the caller to hold the permission 2312 * {@link android.Manifest.permission#SET_WALLPAPER}. 2313 * 2314 * @param fullImage A bitmap that will supply the wallpaper imagery. 2315 * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be 2316 * displayed as wallpaper. Passing {@code null} for this parameter means that 2317 * the full image should be displayed if possible given the image's and device's 2318 * aspect ratios, etc. 2319 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2320 * image for restore to a future device; {@code false} otherwise. 2321 * 2322 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2323 * 2324 * @throws IOException If an error occurs when attempting to set the wallpaper 2325 * to the provided image. 2326 * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is 2327 * empty or invalid. 2328 */ 2329 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)2330 public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup) 2331 throws IOException { 2332 return setBitmap(fullImage, visibleCropHint, allowBackup, FLAG_SYSTEM | FLAG_LOCK); 2333 } 2334 2335 /** 2336 * Version of {@link #setBitmap(Bitmap, Rect, boolean)} that allows the caller 2337 * to specify which of the supported wallpaper categories to set. 2338 * 2339 * @param fullImage A bitmap that will supply the wallpaper imagery. 2340 * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be 2341 * displayed as wallpaper. Passing {@code null} for this parameter means that 2342 * the full image should be displayed if possible given the image's and device's 2343 * aspect ratios, etc. 2344 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2345 * image for restore to a future device; {@code false} otherwise. 2346 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 2347 * 2348 * @see #FLAG_LOCK 2349 * @see #FLAG_SYSTEM 2350 * 2351 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2352 * 2353 * @throws IOException 2354 */ 2355 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which)2356 public int setBitmap(Bitmap fullImage, Rect visibleCropHint, 2357 boolean allowBackup, @SetWallpaperFlags int which) 2358 throws IOException { 2359 return setBitmap(fullImage, visibleCropHint, allowBackup, which, 2360 mContext.getUserId()); 2361 } 2362 2363 /** 2364 * Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user 2365 * id. If the user id doesn't match the user id the process is running under, calling this 2366 * requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. 2367 * @hide 2368 */ 2369 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which, int userId)2370 public int setBitmap(Bitmap fullImage, Rect visibleCropHint, 2371 boolean allowBackup, @SetWallpaperFlags int which, int userId) 2372 throws IOException { 2373 if (multiCrop()) { 2374 SparseArray<Rect> cropMap = new SparseArray<>(); 2375 if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); 2376 return setBitmapWithCrops(fullImage, cropMap, allowBackup, which, userId); 2377 } 2378 validateRect(visibleCropHint); 2379 if (sGlobals.mService == null) { 2380 Log.w(TAG, "WallpaperService not running"); 2381 throw new RuntimeException(new DeadSystemException()); 2382 } 2383 final Bundle result = new Bundle(); 2384 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 2385 final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); 2386 try { 2387 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 2388 mContext.getOpPackageName(), null, crops, allowBackup, result, which, 2389 completion, userId); 2390 if (fd != null) { 2391 FileOutputStream fos = null; 2392 try { 2393 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 2394 fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); 2395 fos.close(); 2396 completion.waitForCompletion(); 2397 } finally { 2398 IoUtils.closeQuietly(fos); 2399 } 2400 } 2401 } catch (RemoteException e) { 2402 throw e.rethrowFromSystemServer(); 2403 } 2404 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 2405 } 2406 2407 /** 2408 * Version of setBitmap that defines how the wallpaper will be positioned for different 2409 * display sizes. 2410 * @param cropHints map from screen dimensions to a sub-region of the image to display for those 2411 * dimensions. The {@code Rect} sub-region may have a larger width/height ratio 2412 * than the screen dimensions to apply a horizontal parallax effect. If the 2413 * map is empty or some entries are missing, the system will apply a default 2414 * strategy to position the wallpaper for any unspecified screen dimensions. 2415 * @hide 2416 */ 2417 @FlaggedApi(FLAG_MULTI_CROP) 2418 @TestApi 2419 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmapWithCrops(@ullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which)2420 public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints, 2421 boolean allowBackup, @SetWallpaperFlags int which) throws IOException { 2422 SparseArray<Rect> crops = new SparseArray<>(); 2423 cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); 2424 return setBitmapWithCrops(fullImage, crops, allowBackup, which, mContext.getUserId()); 2425 } 2426 2427 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmapWithCrops(@ullable Bitmap fullImage, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which, int userId)2428 private int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull SparseArray<Rect> cropHints, 2429 boolean allowBackup, @SetWallpaperFlags int which, int userId) throws IOException { 2430 if (sGlobals.mService == null) { 2431 Log.w(TAG, "WallpaperService not running"); 2432 throw new RuntimeException(new DeadSystemException()); 2433 } 2434 int size = cropHints.size(); 2435 int[] screenOrientations = new int[size]; 2436 List<Rect> crops = new ArrayList<>(size); 2437 for (int i = 0; i < size; i++) { 2438 screenOrientations[i] = cropHints.keyAt(i); 2439 Rect cropHint = cropHints.valueAt(i); 2440 validateRect(cropHint); 2441 crops.add(cropHint); 2442 } 2443 final Bundle result = new Bundle(); 2444 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 2445 try { 2446 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 2447 mContext.getOpPackageName(), screenOrientations, crops, allowBackup, 2448 result, which, completion, userId); 2449 if (fd != null) { 2450 FileOutputStream fos = null; 2451 try { 2452 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 2453 fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); 2454 fos.close(); 2455 completion.waitForCompletion(); 2456 } finally { 2457 IoUtils.closeQuietly(fos); 2458 } 2459 } 2460 } catch (RemoteException e) { 2461 throw e.rethrowFromSystemServer(); 2462 } 2463 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 2464 } 2465 2466 /** 2467 * Version of setBitmap that allows specification of wallpaper metadata including how the 2468 * wallpaper will be positioned for different display sizes. 2469 * 2470 * @param fullImage A bitmap that will supply the wallpaper imagery. 2471 * @param description Wallpaper metadata including desired cropping 2472 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2473 * image for restore to a future device; {@code false} otherwise. 2474 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 2475 * @hide 2476 */ 2477 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 2478 @TestApi 2479 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setBitmapWithDescription(@ullable Bitmap fullImage, @NonNull WallpaperDescription description, boolean allowBackup, @SetWallpaperFlags int which)2480 public int setBitmapWithDescription(@Nullable Bitmap fullImage, 2481 @NonNull WallpaperDescription description, boolean allowBackup, 2482 @SetWallpaperFlags int which) throws IOException { 2483 return setBitmapWithCrops(fullImage, description.getCropHints(), allowBackup, which, 2484 mContext.getUserId()); 2485 } 2486 validateRect(Rect rect)2487 private final void validateRect(Rect rect) { 2488 if (rect != null && rect.isEmpty()) { 2489 throw new IllegalArgumentException("visibleCrop rectangle must be valid and non-empty"); 2490 } 2491 } 2492 2493 /** 2494 * Change the current system wallpaper to a specific byte stream. The 2495 * give InputStream is copied into persistent storage and will now be 2496 * used as the wallpaper. Currently it must be either a JPEG or PNG 2497 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 2498 * is broadcast. 2499 * 2500 * <p>This method is equivalent to calling 2501 * {@link #setStream(InputStream, Rect, boolean)} and passing {@code null} for the 2502 * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup} 2503 * parameter. 2504 * 2505 * <p>This method requires the caller to hold the permission 2506 * {@link android.Manifest.permission#SET_WALLPAPER}. 2507 * 2508 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 2509 * data can be in any format handled by {@link BitmapRegionDecoder}. 2510 * 2511 * @throws IOException If an error occurs when attempting to set the wallpaper 2512 * based on the provided image data. 2513 */ 2514 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStream(InputStream bitmapData)2515 public void setStream(InputStream bitmapData) throws IOException { 2516 setStream(bitmapData, null, true); 2517 } 2518 copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)2519 private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos) 2520 throws IOException { 2521 FileUtils.copy(data, fos); 2522 } 2523 2524 /** 2525 * Change the current system wallpaper to a specific byte stream, specifying a 2526 * hint about which subrectangle of the full image is to be visible. The OS will 2527 * then try to best present the given portion of the full image as the static system 2528 * wallpaper image. The data from the given InputStream is copied into persistent 2529 * storage and will then be used as the system wallpaper. Currently the data must 2530 * be either a JPEG or PNG image. On success, the intent 2531 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 2532 * 2533 * <p>This method requires the caller to hold the permission 2534 * {@link android.Manifest.permission#SET_WALLPAPER}. 2535 * 2536 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 2537 * data can be in any format handled by {@link BitmapRegionDecoder}. 2538 * @param visibleCropHint The rectangular subregion of the streamed image that should be 2539 * displayed as wallpaper. Passing {@code null} for this parameter means that 2540 * the full image should be displayed if possible given the image's and device's 2541 * aspect ratios, etc. 2542 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2543 * image for restore to a future device; {@code false} otherwise. 2544 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2545 * 2546 * @see #getWallpaperId(int) 2547 * 2548 * @throws IOException If an error occurs when attempting to set the wallpaper 2549 * based on the provided image data. 2550 * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is 2551 * empty or invalid. 2552 */ 2553 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)2554 public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup) 2555 throws IOException { 2556 return setStream(bitmapData, visibleCropHint, allowBackup, FLAG_SYSTEM | FLAG_LOCK); 2557 } 2558 2559 /** 2560 * Version of {@link #setStream(InputStream, Rect, boolean)} that allows the caller 2561 * to specify which of the supported wallpaper categories to set. 2562 * 2563 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 2564 * data can be in any format handled by {@link BitmapRegionDecoder}. 2565 * @param visibleCropHint The rectangular subregion of the streamed image that should be 2566 * displayed as wallpaper. Passing {@code null} for this parameter means that 2567 * the full image should be displayed if possible given the image's and device's 2568 * aspect ratios, etc. 2569 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2570 * image for restore to a future device; {@code false} otherwise. 2571 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 2572 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2573 * 2574 * @see #getWallpaperId(int) 2575 * @see #FLAG_LOCK 2576 * @see #FLAG_SYSTEM 2577 * 2578 * @throws IOException 2579 */ 2580 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which)2581 public int setStream(InputStream bitmapData, Rect visibleCropHint, 2582 boolean allowBackup, @SetWallpaperFlags int which) 2583 throws IOException { 2584 if (multiCrop()) { 2585 SparseArray<Rect> cropMap = new SparseArray<>(); 2586 if (visibleCropHint != null) cropMap.put(ORIENTATION_UNKNOWN, visibleCropHint); 2587 return setStreamWithCrops(bitmapData, cropMap, allowBackup, which); 2588 } 2589 validateRect(visibleCropHint); 2590 if (sGlobals.mService == null) { 2591 Log.w(TAG, "WallpaperService not running"); 2592 throw new RuntimeException(new DeadSystemException()); 2593 } 2594 final Bundle result = new Bundle(); 2595 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 2596 final List<Rect> crops = visibleCropHint == null ? null : List.of(visibleCropHint); 2597 try { 2598 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 2599 mContext.getOpPackageName(), null, crops, allowBackup, result, which, 2600 completion, mContext.getUserId()); 2601 if (fd != null) { 2602 FileOutputStream fos = null; 2603 try { 2604 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 2605 copyStreamToWallpaperFile(bitmapData, fos); 2606 fos.close(); 2607 completion.waitForCompletion(); 2608 } finally { 2609 IoUtils.closeQuietly(fos); 2610 } 2611 } 2612 } catch (RemoteException e) { 2613 throw e.rethrowFromSystemServer(); 2614 } 2615 2616 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 2617 } 2618 2619 /** 2620 * Version of setStream that defines how the wallpaper will be positioned for different 2621 * display sizes. 2622 * @param cropHints map from screen dimensions to a sub-region of the image to display for those 2623 * dimensions. The {@code Rect} sub-region may have a larger width/height ratio 2624 * than the screen dimensions to apply a horizontal parallax effect. If the 2625 * map is empty or some entries are missing, the system will apply a default 2626 * strategy to position the wallpaper for any unspecified screen dimensions. 2627 * @hide 2628 */ 2629 @FlaggedApi(FLAG_MULTI_CROP) 2630 @TestApi 2631 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStreamWithCrops(@onNull InputStream bitmapData, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which)2632 public int setStreamWithCrops(@NonNull InputStream bitmapData, 2633 @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) 2634 throws IOException { 2635 SparseArray<Rect> crops = new SparseArray<>(); 2636 cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); 2637 return setStreamWithCrops(bitmapData, crops, allowBackup, which); 2638 } 2639 2640 /** 2641 * Similar to {@link #setStreamWithCrops(InputStream, Map, boolean, int)}, but using 2642 * {@link ScreenOrientation} as keys of the cropHints map. Used for backup & restore, since 2643 * WallpaperBackupAgent stores orientations rather than the exact display size. 2644 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 2645 * data can be in any format handled by {@link BitmapRegionDecoder}. 2646 * @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to display 2647 * for that screen orientation. 2648 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2649 * image for restore to a future device; {@code false} otherwise. 2650 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 2651 * @hide 2652 */ 2653 @FlaggedApi(FLAG_MULTI_CROP) 2654 @SystemApi 2655 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStreamWithCrops(@onNull InputStream bitmapData, @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which)2656 public int setStreamWithCrops(@NonNull InputStream bitmapData, 2657 @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) 2658 throws IOException { 2659 if (sGlobals.mService == null) { 2660 Log.w(TAG, "WallpaperService not running"); 2661 throw new RuntimeException(new DeadSystemException()); 2662 } 2663 int size = cropHints.size(); 2664 int[] screenOrientations = new int[size]; 2665 List<Rect> crops = new ArrayList<>(size); 2666 for (int i = 0; i < size; i++) { 2667 screenOrientations[i] = cropHints.keyAt(i); 2668 Rect cropHint = cropHints.valueAt(i); 2669 validateRect(cropHint); 2670 crops.add(cropHint); 2671 } 2672 final Bundle result = new Bundle(); 2673 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 2674 try { 2675 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 2676 mContext.getOpPackageName(), screenOrientations, crops, allowBackup, 2677 result, which, completion, mContext.getUserId()); 2678 if (fd != null) { 2679 FileOutputStream fos = null; 2680 try { 2681 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 2682 copyStreamToWallpaperFile(bitmapData, fos); 2683 fos.close(); 2684 completion.waitForCompletion(); 2685 } finally { 2686 IoUtils.closeQuietly(fos); 2687 } 2688 } 2689 } catch (RemoteException e) { 2690 throw e.rethrowFromSystemServer(); 2691 } 2692 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 2693 } 2694 2695 /** 2696 * Version of setStream that allows specification of wallpaper metadata including how the 2697 * wallpaper will be positioned for different display sizes. 2698 * 2699 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 2700 * data can be in any format handled by {@link BitmapRegionDecoder}. 2701 * @param description Wallpaper metadata including desired cropping 2702 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 2703 * image for restore to a future device; {@code false} otherwise. 2704 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 2705 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 2706 * @hide 2707 */ 2708 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 2709 @SystemApi 2710 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) setStreamWithDescription(@onNull InputStream bitmapData, @NonNull WallpaperDescription description, boolean allowBackup, @SetWallpaperFlags int which)2711 public int setStreamWithDescription(@NonNull InputStream bitmapData, 2712 @NonNull WallpaperDescription description, boolean allowBackup, 2713 @SetWallpaperFlags int which) throws IOException { 2714 return setStreamWithCrops(bitmapData, description.getCropHints(), allowBackup, which); 2715 } 2716 2717 /** 2718 * Return whether any users are currently set to use the wallpaper 2719 * with the given resource ID. That is, their wallpaper has been 2720 * set through {@link #setResource(int)} with the same resource id. 2721 */ hasResourceWallpaper(@awRes int resid)2722 public boolean hasResourceWallpaper(@RawRes int resid) { 2723 if (sGlobals.mService == null) { 2724 Log.w(TAG, "WallpaperService not running"); 2725 throw new RuntimeException(new DeadSystemException()); 2726 } 2727 try { 2728 Resources resources = mContext.getResources(); 2729 String name = "res:" + resources.getResourceName(resid); 2730 return sGlobals.mService.hasNamedWallpaper(name); 2731 } catch (RemoteException e) { 2732 throw e.rethrowFromSystemServer(); 2733 } 2734 } 2735 2736 // TODO(b/181083333): add multiple root display area support on this API. 2737 /** 2738 * Returns the desired minimum width for the wallpaper. Callers of 2739 * {@link #setBitmap(android.graphics.Bitmap)} or 2740 * {@link #setStream(java.io.InputStream)} should check this value 2741 * beforehand to make sure the supplied wallpaper respects the desired 2742 * minimum width. 2743 * 2744 * If the returned value is <= 0, the caller should use the width of 2745 * the default display instead. 2746 * 2747 * @return The desired minimum width for the wallpaper. This value should 2748 * be honored by applications that set the wallpaper but it is not 2749 * mandatory. 2750 * 2751 * @see #getDesiredMinimumHeight() 2752 */ getDesiredMinimumWidth()2753 public int getDesiredMinimumWidth() { 2754 StrictMode.assertUiContext(mContext, "getDesiredMinimumWidth"); 2755 if (sGlobals.mService == null) { 2756 Log.w(TAG, "WallpaperService not running"); 2757 throw new RuntimeException(new DeadSystemException()); 2758 } 2759 try { 2760 return sGlobals.mService.getWidthHint(mContext.getDisplayId()); 2761 } catch (RemoteException e) { 2762 throw e.rethrowFromSystemServer(); 2763 } 2764 } 2765 2766 // TODO(b/181083333): add multiple root display area support on this API. 2767 /** 2768 * Returns the desired minimum height for the wallpaper. Callers of 2769 * {@link #setBitmap(android.graphics.Bitmap)} or 2770 * {@link #setStream(java.io.InputStream)} should check this value 2771 * beforehand to make sure the supplied wallpaper respects the desired 2772 * minimum height. 2773 * 2774 * If the returned value is <= 0, the caller should use the height of 2775 * the default display instead. 2776 * 2777 * @return The desired minimum height for the wallpaper. This value should 2778 * be honored by applications that set the wallpaper but it is not 2779 * mandatory. 2780 * 2781 * @see #getDesiredMinimumWidth() 2782 */ getDesiredMinimumHeight()2783 public int getDesiredMinimumHeight() { 2784 StrictMode.assertUiContext(mContext, "getDesiredMinimumHeight"); 2785 if (sGlobals.mService == null) { 2786 Log.w(TAG, "WallpaperService not running"); 2787 throw new RuntimeException(new DeadSystemException()); 2788 } 2789 try { 2790 return sGlobals.mService.getHeightHint(mContext.getDisplayId()); 2791 } catch (RemoteException e) { 2792 throw e.rethrowFromSystemServer(); 2793 } 2794 } 2795 2796 // TODO(b/181083333): add multiple root display area support on this API. 2797 /** 2798 * For use only by the current home application, to specify the size of 2799 * wallpaper it would like to use. This allows such applications to have 2800 * a virtual wallpaper that is larger than the physical screen, matching 2801 * the size of their workspace. 2802 * 2803 * <p class="note">Calling this method from apps other than the active 2804 * home app is not guaranteed to work properly. Other apps that supply 2805 * wallpaper imagery should use {@link #getDesiredMinimumWidth()} and 2806 * {@link #getDesiredMinimumHeight()} and construct a wallpaper that 2807 * matches those dimensions. 2808 * 2809 * <p>This method requires the caller to hold the permission 2810 * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}. 2811 * 2812 * @param minimumWidth Desired minimum width 2813 * @param minimumHeight Desired minimum height 2814 */ suggestDesiredDimensions(int minimumWidth, int minimumHeight)2815 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { 2816 StrictMode.assertUiContext(mContext, "suggestDesiredDimensions"); 2817 try { 2818 /** 2819 * The framework makes no attempt to limit the window size 2820 * to the maximum texture size. Any window larger than this 2821 * cannot be composited. 2822 * 2823 * Read maximum texture size from system property and scale down 2824 * minimumWidth and minimumHeight accordingly. 2825 */ 2826 int maximumTextureSize; 2827 try { 2828 maximumTextureSize = SystemProperties.getInt("sys.max_texture_size", 0); 2829 } catch (Exception e) { 2830 maximumTextureSize = 0; 2831 } 2832 2833 if (maximumTextureSize > 0) { 2834 if ((minimumWidth > maximumTextureSize) || 2835 (minimumHeight > maximumTextureSize)) { 2836 float aspect = (float)minimumHeight / (float)minimumWidth; 2837 if (minimumWidth > minimumHeight) { 2838 minimumWidth = maximumTextureSize; 2839 minimumHeight = (int)((minimumWidth * aspect) + 0.5); 2840 } else { 2841 minimumHeight = maximumTextureSize; 2842 minimumWidth = (int)((minimumHeight / aspect) + 0.5); 2843 } 2844 } 2845 } 2846 2847 if (sGlobals.mService == null) { 2848 Log.w(TAG, "WallpaperService not running"); 2849 throw new RuntimeException(new DeadSystemException()); 2850 } else { 2851 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight, 2852 mContext.getOpPackageName(), mContext.getDisplayId()); 2853 } 2854 } catch (RemoteException e) { 2855 throw e.rethrowFromSystemServer(); 2856 } 2857 } 2858 2859 // TODO(b/181083333): add multiple root display area support on this API. 2860 /** 2861 * Specify extra padding that the wallpaper should have outside of the display. 2862 * That is, the given padding supplies additional pixels the wallpaper should extend 2863 * outside of the display itself. 2864 * 2865 * <p>This method requires the caller to hold the permission 2866 * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}. 2867 * 2868 * @param padding The number of pixels the wallpaper should extend beyond the display, 2869 * on its left, top, right, and bottom sides. 2870 */ 2871 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS) setDisplayPadding(Rect padding)2872 public void setDisplayPadding(Rect padding) { 2873 StrictMode.assertUiContext(mContext, "setDisplayPadding"); 2874 try { 2875 if (sGlobals.mService == null) { 2876 Log.w(TAG, "WallpaperService not running"); 2877 throw new RuntimeException(new DeadSystemException()); 2878 } else { 2879 sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(), 2880 mContext.getDisplayId()); 2881 } 2882 } catch (RemoteException e) { 2883 throw e.rethrowFromSystemServer(); 2884 } 2885 } 2886 2887 /** 2888 * Apply a raw offset to the wallpaper window. Should only be used in 2889 * combination with {@link #setDisplayPadding(android.graphics.Rect)} when you 2890 * have ensured that the wallpaper will extend outside of the display area so that 2891 * it can be moved without leaving part of the display uncovered. 2892 * @param x The offset, in pixels, to apply to the left edge. 2893 * @param y The offset, in pixels, to apply to the top edge. 2894 * @hide 2895 */ 2896 @SystemApi setDisplayOffset(IBinder windowToken, int x, int y)2897 public void setDisplayOffset(IBinder windowToken, int x, int y) { 2898 try { 2899 //Log.v(TAG, "Sending new wallpaper display offsets from app..."); 2900 WindowManagerGlobal.getWindowSession().setWallpaperDisplayOffset( 2901 windowToken, x, y); 2902 //Log.v(TAG, "...app returning after sending display offset!"); 2903 } catch (RemoteException e) { 2904 throw e.rethrowFromSystemServer(); 2905 } 2906 } 2907 2908 /** 2909 * Equivalent to {@link #clear()}. 2910 * @see #clear() 2911 */ 2912 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) clearWallpaper()2913 public void clearWallpaper() { 2914 clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId()); 2915 } 2916 2917 /** 2918 * Clear the wallpaper for a specific user. 2919 * <ul> 2920 * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper. 2921 * The home screen wallpaper will become visible on the lock screen. </li> 2922 * 2923 * <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen 2924 * wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous 2925 * wallpaper was shared between home and lock screen, it will become lock screen only. </li> 2926 * 2927 * <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the 2928 * default wallpaper on both home and lock screen, removing any user defined wallpaper.</li> 2929 * </ul> 2930 * </p> 2931 * 2932 * The caller must hold the 2933 * INTERACT_ACROSS_USERS_FULL permission to clear another user's 2934 * wallpaper, and must hold the SET_WALLPAPER permission in all 2935 * circumstances. 2936 * @hide 2937 */ 2938 @SystemApi 2939 @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) clearWallpaper(@etWallpaperFlags int which, int userId)2940 public void clearWallpaper(@SetWallpaperFlags int which, int userId) { 2941 if (sGlobals.mService == null) { 2942 Log.w(TAG, "WallpaperService not running"); 2943 throw new RuntimeException(new DeadSystemException()); 2944 } 2945 try { 2946 sGlobals.mService.clearWallpaper(mContext.getOpPackageName(), which, userId); 2947 } catch (RemoteException e) { 2948 throw e.rethrowFromSystemServer(); 2949 } 2950 } 2951 2952 /** 2953 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 2954 * wallpaper, usually in order to set a live wallpaper. 2955 * 2956 * @param name Name of the component to use. 2957 * 2958 * @hide 2959 */ 2960 @SystemApi 2961 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) setWallpaperComponent(ComponentName name)2962 public boolean setWallpaperComponent(ComponentName name) { 2963 return setWallpaperComponent(name, mContext.getUserId()); 2964 } 2965 2966 /** 2967 * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default 2968 * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black. 2969 * 2970 * @hide 2971 */ 2972 @SystemApi 2973 @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) setWallpaperDimAmount(@loatRange from = 0f, to = 1f) float dimAmount)2974 public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) { 2975 if (sGlobals.mService == null) { 2976 Log.w(TAG, "WallpaperService not running"); 2977 throw new RuntimeException(new DeadSystemException()); 2978 } 2979 try { 2980 sGlobals.mService.setWallpaperDimAmount(MathUtils.saturate(dimAmount)); 2981 } catch (RemoteException e) { 2982 throw e.rethrowFromSystemServer(); 2983 } 2984 } 2985 2986 /** 2987 * Gets the current additional dim amount set on the wallpaper. 0f means no application has 2988 * added any dimming on top of the system default dim amount. 2989 * 2990 * @hide 2991 */ 2992 @SystemApi 2993 @RequiresPermission(SET_WALLPAPER_DIM_AMOUNT) getWallpaperDimAmount()2994 public @FloatRange (from = 0f, to = 1f) float getWallpaperDimAmount() { 2995 if (sGlobals.mService == null) { 2996 Log.w(TAG, "WallpaperService not running"); 2997 throw new RuntimeException(new DeadSystemException()); 2998 } 2999 try { 3000 return sGlobals.mService.getWallpaperDimAmount(); 3001 } catch (RemoteException e) { 3002 throw e.rethrowFromSystemServer(); 3003 } 3004 } 3005 3006 /** 3007 * Whether the lock screen wallpaper is different from the system wallpaper. 3008 * 3009 * @hide 3010 */ lockScreenWallpaperExists()3011 public boolean lockScreenWallpaperExists() { 3012 if (sGlobals.mService == null) { 3013 Log.w(TAG, "WallpaperService not running"); 3014 throw new RuntimeException(new DeadSystemException()); 3015 } 3016 try { 3017 return sGlobals.mService.lockScreenWallpaperExists(); 3018 } catch (RemoteException e) { 3019 throw e.rethrowFromSystemServer(); 3020 } 3021 } 3022 3023 /** 3024 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 3025 * wallpaper, usually in order to set a live wallpaper. 3026 * 3027 * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 3028 * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change 3029 * another user's wallpaper. 3030 * 3031 * @param name Name of the component to use. 3032 * @param userId User for whom the component should be set. 3033 * 3034 * @hide 3035 */ 3036 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) 3037 @UnsupportedAppUsage setWallpaperComponent(ComponentName name, int userId)3038 public boolean setWallpaperComponent(ComponentName name, int userId) { 3039 return setWallpaperComponentWithFlags(name, FLAG_SYSTEM | FLAG_LOCK, userId); 3040 } 3041 3042 /** 3043 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 3044 * wallpaper, usually in order to set a live wallpaper, for a given wallpaper destination. 3045 * 3046 * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 3047 * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change 3048 * another user's wallpaper. 3049 * 3050 * @param name Name of the component to use. 3051 * @param which Specifies wallpaper destination (home and/or lock). 3052 * 3053 * @hide 3054 */ 3055 @SystemApi 3056 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) setWallpaperComponentWithFlags(@onNull ComponentName name, @SetWallpaperFlags int which)3057 public boolean setWallpaperComponentWithFlags(@NonNull ComponentName name, 3058 @SetWallpaperFlags int which) { 3059 return setWallpaperComponentWithFlags(name, which, mContext.getUserId()); 3060 } 3061 3062 /** 3063 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 3064 * wallpaper, usually in order to set a live wallpaper, for a given wallpaper destination. 3065 * 3066 * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 3067 * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change 3068 * another user's wallpaper. 3069 * 3070 * @param name Name of the component to use. 3071 * @param which Specifies wallpaper destination (home and/or lock). 3072 * @param userId User for whom the component should be set. 3073 * 3074 * @hide 3075 */ 3076 @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT, 3077 Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true) setWallpaperComponentWithFlags(@onNull ComponentName name, @SetWallpaperFlags int which, int userId)3078 public boolean setWallpaperComponentWithFlags(@NonNull ComponentName name, 3079 @SetWallpaperFlags int which, int userId) { 3080 WallpaperDescription description = new WallpaperDescription.Builder().setComponent( 3081 name).build(); 3082 return setWallpaperComponentWithDescription(description, which, userId); 3083 } 3084 3085 /** 3086 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 3087 * wallpaper, along with associated metadata. 3088 * 3089 * <p>This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 3090 * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change 3091 * another user's wallpaper. 3092 * </p> 3093 * 3094 * @param description wallpaper component and metadata to set 3095 * @param which Specifies wallpaper destination (home and/or lock). 3096 * @return true on success, otherwise false 3097 * 3098 * @hide 3099 */ 3100 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 3101 @SystemApi 3102 @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT, 3103 Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true) setWallpaperComponentWithDescription(@onNull WallpaperDescription description, @SetWallpaperFlags int which)3104 public boolean setWallpaperComponentWithDescription(@NonNull WallpaperDescription description, 3105 @SetWallpaperFlags int which) { 3106 return setWallpaperComponentWithDescription(description, which, mContext.getUserId()); 3107 } 3108 3109 /** 3110 * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render 3111 * wallpaper, along with associated metadata. 3112 * 3113 * <p>This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 3114 * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change 3115 * another user's wallpaper. 3116 * </p> 3117 * 3118 * @param description wallpaper component and metadata to set 3119 * @param which Specifies wallpaper destination (home and/or lock). 3120 * @param userId User for whom the component should be set. 3121 * @return true on success, otherwise false 3122 * 3123 * @hide 3124 */ 3125 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 3126 @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT, 3127 Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true) setWallpaperComponentWithDescription(@onNull WallpaperDescription description, @SetWallpaperFlags int which, int userId)3128 public boolean setWallpaperComponentWithDescription(@NonNull WallpaperDescription description, 3129 @SetWallpaperFlags int which, int userId) { 3130 if (sGlobals.mService == null) { 3131 Log.w(TAG, "WallpaperManagerService not running"); 3132 throw new RuntimeException(new DeadSystemException()); 3133 } 3134 try { 3135 sGlobals.mService.setWallpaperComponentChecked(description, mContext.getOpPackageName(), 3136 which, userId); 3137 return true; 3138 } catch (RemoteException e) { 3139 throw e.rethrowFromSystemServer(); 3140 } 3141 } 3142 3143 /** 3144 * Set the display position of the current wallpaper within any larger space, when 3145 * that wallpaper is visible behind the given window. The X and Y offsets 3146 * are floating point numbers ranging from 0 to 1, representing where the 3147 * wallpaper should be positioned within the screen space. These only 3148 * make sense when the wallpaper is larger than the display. 3149 * 3150 * @param windowToken The window who these offsets should be associated 3151 * with, as returned by {@link android.view.View#getWindowToken() 3152 * View.getWindowToken()}. 3153 * @param xOffset The offset along the X dimension, from 0 to 1. 3154 * @param yOffset The offset along the Y dimension, from 0 to 1. 3155 */ setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset)3156 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { 3157 try { 3158 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 3159 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 3160 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); 3161 //Log.v(TAG, "...app returning after sending offsets!"); 3162 } catch (RemoteException e) { 3163 throw e.rethrowFromSystemServer(); 3164 } 3165 } 3166 3167 /** 3168 * For applications that use multiple virtual screens showing a wallpaper, 3169 * specify the step size between virtual screens. For example, if the 3170 * launcher has 3 virtual screens, it would specify an xStep of 0.5, 3171 * since the X offset for those screens are 0.0, 0.5 and 1.0 3172 * @param xStep The X offset delta from one screen to the next one 3173 * @param yStep The Y offset delta from one screen to the next one 3174 */ setWallpaperOffsetSteps(float xStep, float yStep)3175 public void setWallpaperOffsetSteps(float xStep, float yStep) { 3176 mWallpaperXStep = xStep; 3177 mWallpaperYStep = yStep; 3178 } 3179 3180 /** 3181 * Send an arbitrary command to the current active wallpaper. 3182 * 3183 * @param windowToken The window who these offsets should be associated 3184 * with, as returned by {@link android.view.View#getWindowToken() 3185 * View.getWindowToken()}. 3186 * @param action Name of the command to perform. This must be a scoped 3187 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT". 3188 * @param x Arbitrary integer argument based on command. 3189 * @param y Arbitrary integer argument based on command. 3190 * @param z Arbitrary integer argument based on command. 3191 * @param extras Optional additional information for the command, or null. 3192 */ 3193 @RequiresPermission(value = android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER, 3194 conditional = true) sendWallpaperCommand(IBinder windowToken, String action, int x, int y, int z, Bundle extras)3195 public void sendWallpaperCommand(IBinder windowToken, String action, 3196 int x, int y, int z, Bundle extras) { 3197 try { 3198 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 3199 WindowManagerGlobal.getWindowSession().sendWallpaperCommand( 3200 windowToken, action, x, y, z, extras, false); 3201 //Log.v(TAG, "...app returning after sending offsets!"); 3202 } catch (RemoteException e) { 3203 throw e.rethrowFromSystemServer(); 3204 } 3205 } 3206 3207 /** 3208 * Set the current zoom out level of the wallpaper. 3209 * 3210 * @param windowToken window requesting wallpaper zoom. Zoom level will only be applier while 3211 * such window is visible. 3212 * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in 3213 * 3214 * @hide 3215 */ 3216 @Keep 3217 @TestApi setWallpaperZoomOut(@onNull IBinder windowToken, float zoom)3218 public void setWallpaperZoomOut(@NonNull IBinder windowToken, float zoom) { 3219 if (zoom < 0 || zoom > 1f) { 3220 throw new IllegalArgumentException("zoom must be between 0 and 1: " + zoom); 3221 } 3222 if (windowToken == null) { 3223 throw new IllegalArgumentException("windowToken must not be null"); 3224 } 3225 try { 3226 WindowManagerGlobal.getWindowSession().setWallpaperZoomOut(windowToken, zoom); 3227 } catch (RemoteException e) { 3228 throw e.rethrowFromSystemServer(); 3229 } 3230 } 3231 3232 /** 3233 * Returns whether wallpapers are supported for the calling user. If this function returns 3234 * {@code false}, any attempts to changing the wallpaper will have no effect, 3235 * and any attempt to obtain of the wallpaper will return {@code null}. 3236 */ isWallpaperSupported()3237 public boolean isWallpaperSupported() { 3238 if (sGlobals.mService == null) { 3239 Log.w(TAG, "WallpaperService not running"); 3240 throw new RuntimeException(new DeadSystemException()); 3241 } else { 3242 try { 3243 return sGlobals.mService.isWallpaperSupported(mContext.getOpPackageName()); 3244 } catch (RemoteException e) { 3245 throw e.rethrowFromSystemServer(); 3246 } 3247 } 3248 } 3249 3250 /** 3251 * Returns whether the calling package is allowed to set the wallpaper for the calling user. 3252 * If this function returns {@code false}, any attempts to change the wallpaper will have 3253 * no effect. Always returns {@code true} for device owner and profile owner. 3254 * 3255 * @see android.os.UserManager#DISALLOW_SET_WALLPAPER 3256 */ isSetWallpaperAllowed()3257 public boolean isSetWallpaperAllowed() { 3258 if (sGlobals.mService == null) { 3259 Log.w(TAG, "WallpaperService not running"); 3260 throw new RuntimeException(new DeadSystemException()); 3261 } else { 3262 try { 3263 return sGlobals.mService.isSetWallpaperAllowed(mContext.getOpPackageName()); 3264 } catch (RemoteException e) { 3265 throw e.rethrowFromSystemServer(); 3266 } 3267 } 3268 } 3269 3270 /** 3271 * Clear the offsets previously associated with this window through 3272 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts 3273 * the window to its default state, where it does not cause the wallpaper 3274 * to scroll from whatever its last offsets were. 3275 * 3276 * @param windowToken The window who these offsets should be associated 3277 * with, as returned by {@link android.view.View#getWindowToken() 3278 * View.getWindowToken()}. 3279 */ clearWallpaperOffsets(IBinder windowToken)3280 public void clearWallpaperOffsets(IBinder windowToken) { 3281 try { 3282 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 3283 windowToken, -1, -1, -1, -1); 3284 } catch (RemoteException e) { 3285 throw e.rethrowFromSystemServer(); 3286 } 3287 } 3288 3289 /** 3290 * Remove any currently set system wallpaper, reverting to the system's built-in 3291 * wallpaper. 3292 * On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 3293 * 3294 * <p>This method requires the caller to hold the permission 3295 * {@link android.Manifest.permission#SET_WALLPAPER}. 3296 * 3297 * @throws IOException If an error occurs reverting to the built-in 3298 * wallpaper. 3299 */ 3300 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) clear()3301 public void clear() throws IOException { 3302 clear(FLAG_SYSTEM | FLAG_LOCK); 3303 } 3304 3305 /** 3306 * Remove one or more currently set wallpapers, reverting to the system default 3307 * display for each one. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 3308 * is broadcast. 3309 * <ul> 3310 * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper. 3311 * The home screen wallpaper will become visible on the lock screen. </li> 3312 * 3313 * <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen 3314 * wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous 3315 * wallpaper was shared between home and lock screen, it will become lock screen only. </li> 3316 * 3317 * <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the 3318 * default wallpaper on both home and lock screen, removing any user defined wallpaper.</li> 3319 * </ul> 3320 * 3321 * @param which A bitwise combination of {@link #FLAG_SYSTEM} or 3322 * {@link #FLAG_LOCK} 3323 * @throws IOException If an error occurs reverting to the built-in wallpaper. 3324 */ 3325 @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) clear(@etWallpaperFlags int which)3326 public void clear(@SetWallpaperFlags int which) throws IOException { 3327 clearWallpaper(which, mContext.getUserId()); 3328 } 3329 3330 /** 3331 * Open stream representing the default static image wallpaper. 3332 * 3333 * If the device defines no default wallpaper of the requested kind, 3334 * {@code null} is returned. 3335 * 3336 * @hide 3337 */ 3338 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) openDefaultWallpaper(Context context, @SetWallpaperFlags int which)3339 public static InputStream openDefaultWallpaper(Context context, @SetWallpaperFlags int which) { 3340 final String whichProp; 3341 final int defaultResId; 3342 /* Factory-default lock wallpapers are not yet supported. 3343 whichProp = which == FLAG_LOCK ? PROP_LOCK_WALLPAPER : PROP_WALLPAPER; 3344 defaultResId = which == FLAG_LOCK ? R.drawable.default_lock_wallpaper : .... 3345 */ 3346 whichProp = PROP_WALLPAPER; 3347 defaultResId = R.drawable.default_wallpaper; 3348 final String path = SystemProperties.get(whichProp); 3349 final InputStream wallpaperInputStream = getWallpaperInputStream(path); 3350 if (wallpaperInputStream != null) { 3351 return wallpaperInputStream; 3352 } 3353 final String cmfPath = getCmfWallpaperPath(); 3354 final InputStream cmfWallpaperInputStream = getWallpaperInputStream(cmfPath); 3355 if (cmfWallpaperInputStream != null) { 3356 return cmfWallpaperInputStream; 3357 } 3358 try { 3359 return context.getResources().openRawResource(defaultResId); 3360 } catch (NotFoundException e) { 3361 // no default defined for this device; this is not a failure 3362 } 3363 return null; 3364 } 3365 3366 /** 3367 * util used in T to return a default system wallpaper file 3368 * when third party apps attempt to read the wallpaper with {@link #getWallpaperFile} 3369 */ getDefaultSystemWallpaperFile()3370 private static ParcelFileDescriptor getDefaultSystemWallpaperFile() { 3371 for (String path: getDefaultSystemWallpaperPaths()) { 3372 File file = new File(path); 3373 if (file.exists()) { 3374 try { 3375 return ParcelFileDescriptor.open(file, MODE_READ_ONLY); 3376 } catch (FileNotFoundException e) { 3377 // continue; default wallpaper file not found on this path 3378 } 3379 } 3380 } 3381 return null; 3382 } 3383 getWallpaperInputStream(String path)3384 private static InputStream getWallpaperInputStream(String path) { 3385 if (!TextUtils.isEmpty(path)) { 3386 final File file = new File(path); 3387 if (file.exists()) { 3388 try { 3389 return new FileInputStream(file); 3390 } catch (IOException e) { 3391 // Ignored, fall back to platform default 3392 } 3393 } 3394 } 3395 return null; 3396 } 3397 3398 /** 3399 * @return a list of paths to the system default wallpapers, in order of priority: 3400 * if the file exists for the first path of this list, the first path should be used. 3401 */ getDefaultSystemWallpaperPaths()3402 private static List<String> getDefaultSystemWallpaperPaths() { 3403 return List.of(SystemProperties.get(PROP_WALLPAPER), getCmfWallpaperPath()); 3404 } 3405 getCmfWallpaperPath()3406 private static String getCmfWallpaperPath() { 3407 return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_" 3408 + VALUE_CMF_COLOR; 3409 } 3410 3411 /** 3412 * Return {@link ComponentName} of the default live wallpaper, or 3413 * {@code null} if none is defined. 3414 * 3415 * @hide 3416 */ getDefaultWallpaperComponent(Context context)3417 public static ComponentName getDefaultWallpaperComponent(Context context) { 3418 ComponentName cn = null; 3419 3420 String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT); 3421 if (!TextUtils.isEmpty(flat)) { 3422 cn = ComponentName.unflattenFromString(flat); 3423 } 3424 3425 if (cn == null) { 3426 flat = context.getString(com.android.internal.R.string.default_wallpaper_component); 3427 if (!TextUtils.isEmpty(flat)) { 3428 cn = ComponentName.unflattenFromString(flat); 3429 } 3430 } 3431 3432 if (!isComponentExist(context, cn)) { 3433 cn = null; 3434 } 3435 3436 return cn; 3437 } 3438 3439 /** 3440 * Return {@link ComponentName} of the CMF default wallpaper, or 3441 * {@link #getDefaultWallpaperComponent(Context)} if none is defined. 3442 * 3443 * @hide 3444 */ getCmfDefaultWallpaperComponent(Context context)3445 public static ComponentName getCmfDefaultWallpaperComponent(Context context) { 3446 ComponentName cn = null; 3447 String[] cmfWallpaperMap = context.getResources().getStringArray( 3448 com.android.internal.R.array.default_wallpaper_component_per_device_color); 3449 if (cmfWallpaperMap != null && cmfWallpaperMap.length > 0) { 3450 for (String entry : cmfWallpaperMap) { 3451 String[] cmfWallpaper; 3452 if (!TextUtils.isEmpty(entry)) { 3453 cmfWallpaper = entry.split(","); 3454 if (cmfWallpaper != null && cmfWallpaper.length == 2 && VALUE_CMF_COLOR.equals( 3455 cmfWallpaper[0]) && !TextUtils.isEmpty(cmfWallpaper[1])) { 3456 cn = ComponentName.unflattenFromString(cmfWallpaper[1]); 3457 break; 3458 } 3459 } 3460 } 3461 } 3462 3463 if (!isComponentExist(context, cn)) { 3464 cn = null; 3465 } 3466 3467 return cn == null ? getDefaultWallpaperComponent(context) : cn; 3468 } 3469 isComponentExist(Context context, ComponentName cn)3470 private static boolean isComponentExist(Context context, ComponentName cn) { 3471 if (cn == null) { 3472 return false; 3473 } 3474 try { 3475 final PackageManager packageManager = context.getPackageManager(); 3476 packageManager.getPackageInfo(cn.getPackageName(), 3477 PackageManager.MATCH_DIRECT_BOOT_AWARE 3478 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 3479 } catch (PackageManager.NameNotFoundException e) { 3480 return false; 3481 } 3482 return true; 3483 } 3484 3485 /** 3486 * Is the current system wallpaper eligible for backup? 3487 * 3488 * Only the OS itself may use this method. 3489 * @hide 3490 */ isWallpaperBackupEligible(int which)3491 public boolean isWallpaperBackupEligible(int which) { 3492 if (sGlobals.mService == null) { 3493 Log.w(TAG, "WallpaperService not running"); 3494 throw new RuntimeException(new DeadSystemException()); 3495 } 3496 try { 3497 return sGlobals.mService.isWallpaperBackupEligible(which, mContext.getUserId()); 3498 } catch (RemoteException e) { 3499 Log.e(TAG, "Exception querying wallpaper backup eligibility: " + e.getMessage()); 3500 } 3501 return false; 3502 } 3503 3504 /** 3505 * Get the instance of {@link ColorManagementProxy}. 3506 * 3507 * @return instance of {@link ColorManagementProxy}. 3508 * @hide 3509 */ getColorManagementProxy()3510 public ColorManagementProxy getColorManagementProxy() { 3511 return mCmProxy; 3512 } 3513 checkExactlyOneWallpaperFlagSet(@etWallpaperFlags int which)3514 private static void checkExactlyOneWallpaperFlagSet(@SetWallpaperFlags int which) { 3515 if (which == FLAG_SYSTEM || which == FLAG_LOCK) { 3516 return; 3517 } 3518 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper"); 3519 } 3520 3521 /** 3522 * A hidden class to help {@link Globals#getCurrentWallpaperLocked} handle color management. 3523 * @hide 3524 */ 3525 public static class ColorManagementProxy { 3526 private final Set<ColorSpace> mSupportedColorSpaces = new HashSet<>(); 3527 ColorManagementProxy(@onNull Context context)3528 public ColorManagementProxy(@NonNull Context context) { 3529 // Get a list of supported wide gamut color spaces. 3530 Display display = context.getDisplayNoVerify(); 3531 if (display != null) { 3532 mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut())); 3533 } 3534 } 3535 3536 @NonNull getSupportedColorSpaces()3537 public Set<ColorSpace> getSupportedColorSpaces() { 3538 return mSupportedColorSpaces; 3539 } 3540 isSupportedColorSpace(ColorSpace colorSpace)3541 boolean isSupportedColorSpace(ColorSpace colorSpace) { 3542 return colorSpace != null && (colorSpace == ColorSpace.get(ColorSpace.Named.SRGB) 3543 || getSupportedColorSpaces().contains(colorSpace)); 3544 } 3545 doColorManagement(ImageDecoder decoder, ImageDecoder.ImageInfo info)3546 void doColorManagement(ImageDecoder decoder, ImageDecoder.ImageInfo info) { 3547 if (!isSupportedColorSpace(info.getColorSpace())) { 3548 decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); 3549 Log.w(TAG, "Not supported color space: " + info.getColorSpace()); 3550 } 3551 } 3552 } 3553 3554 // Private completion callback for setWallpaper() synchronization 3555 private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub { 3556 final CountDownLatch mLatch; 3557 WallpaperSetCompletion()3558 public WallpaperSetCompletion() { 3559 mLatch = new CountDownLatch(1); 3560 } 3561 waitForCompletion()3562 public void waitForCompletion() { 3563 try { 3564 final boolean completed = mLatch.await(30, TimeUnit.SECONDS); 3565 if (completed) { 3566 Log.d(TAG, "Wallpaper set completion."); 3567 } else { 3568 Log.d(TAG, "Timeout waiting for wallpaper set completion!"); 3569 } 3570 } catch (InterruptedException e) { 3571 // This might be legit: the crop may take a very long time. Don't sweat 3572 // it in that case; we are okay with display lagging behind in order to 3573 // keep the caller from locking up indeterminately. 3574 } 3575 } 3576 3577 @Override onWallpaperChanged()3578 public void onWallpaperChanged() throws RemoteException { 3579 mLatch.countDown(); 3580 } 3581 3582 @Override onWallpaperColorsChanged(WallpaperColors colors, int which, int userId)3583 public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) 3584 throws RemoteException { 3585 sGlobals.onWallpaperColorsChanged(colors, which, userId); 3586 } 3587 } 3588 3589 /** 3590 * Interface definition for a callback to be invoked when colors change on a wallpaper. 3591 */ 3592 public interface OnColorsChangedListener { 3593 /** 3594 * Called when colors change. 3595 * A {@link android.app.WallpaperColors} object containing a simplified 3596 * color histogram will be given. 3597 * 3598 * @param colors Wallpaper color info, {@code null} when not available. 3599 * @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM} 3600 * @see android.service.wallpaper.WallpaperService.Engine#onComputeColors() 3601 */ onColorsChanged(@ullable WallpaperColors colors, int which)3602 void onColorsChanged(@Nullable WallpaperColors colors, int which); 3603 3604 /** 3605 * Called when colors change. 3606 * A {@link android.app.WallpaperColors} object containing a simplified 3607 * color histogram will be given. 3608 * 3609 * @param colors Wallpaper color info, {@code null} when not available. 3610 * @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM} 3611 * @param userId Owner of the wallpaper 3612 * @see android.service.wallpaper.WallpaperService.Engine#onComputeColors() 3613 * @hide 3614 */ onColorsChanged(@ullable WallpaperColors colors, int which, int userId)3615 default void onColorsChanged(@Nullable WallpaperColors colors, int which, int userId) { 3616 onColorsChanged(colors, which); 3617 } 3618 } 3619 3620 /** 3621 * Callback to update a consumer with a local color change 3622 * @hide 3623 */ 3624 public interface LocalWallpaperColorConsumer { 3625 3626 /** 3627 * Gets called when a color of an area gets updated 3628 * @param area 3629 * @param colors 3630 */ onColorsChanged(RectF area, WallpaperColors colors)3631 void onColorsChanged(RectF area, WallpaperColors colors); 3632 } 3633 } 3634