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