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