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