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