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