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