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