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