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