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