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