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