[go: nahoru, domu]

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
17package android.app;
18
19import android.annotation.IntDef;
20import android.annotation.RawRes;
21import android.annotation.SystemApi;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.res.Resources;
29import android.content.res.Resources.NotFoundException;
30import android.graphics.Bitmap;
31import android.graphics.BitmapFactory;
32import android.graphics.BitmapRegionDecoder;
33import android.graphics.Canvas;
34import android.graphics.ColorFilter;
35import android.graphics.Matrix;
36import android.graphics.Paint;
37import android.graphics.PixelFormat;
38import android.graphics.PorterDuff;
39import android.graphics.PorterDuffXfermode;
40import android.graphics.Rect;
41import android.graphics.RectF;
42import android.graphics.drawable.BitmapDrawable;
43import android.graphics.drawable.Drawable;
44import android.net.Uri;
45import android.os.Bundle;
46import android.os.DeadSystemException;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Looper;
50import android.os.ParcelFileDescriptor;
51import android.os.RemoteException;
52import android.os.ServiceManager;
53import android.os.SystemProperties;
54import android.text.TextUtils;
55import android.util.Log;
56import android.view.WindowManagerGlobal;
57
58import libcore.io.IoUtils;
59
60import java.io.BufferedInputStream;
61import java.io.File;
62import java.io.FileInputStream;
63import java.io.FileOutputStream;
64import java.io.IOException;
65import java.io.InputStream;
66import java.lang.annotation.Retention;
67import java.lang.annotation.RetentionPolicy;
68import java.util.List;
69import java.util.concurrent.CountDownLatch;
70import 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 */
82public 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
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
211        public void draw(Canvas canvas) {
212            canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
213        }
214
215        @Override
216        public int getOpacity() {
217            return PixelFormat.OPAQUE;
218        }
219
220        @Override
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
227        public void setAlpha(int alpha) {
228            throw new UnsupportedOperationException("Not supported with this drawable");
229        }
230
231        @Override
232        public void setColorFilter(ColorFilter colorFilter) {
233            throw new UnsupportedOperationException("Not supported with this drawable");
234        }
235
236        @Override
237        public void setDither(boolean dither) {
238            throw new UnsupportedOperationException("Not supported with this drawable");
239        }
240
241        @Override
242        public void setFilterBitmap(boolean filter) {
243            throw new UnsupportedOperationException("Not supported with this drawable");
244        }
245
246        @Override
247        public int getIntrinsicWidth() {
248            return mWidth;
249        }
250
251        @Override
252        public int getIntrinsicHeight() {
253            return mHeight;
254        }
255
256        @Override
257        public int getMinimumWidth() {
258            return mWidth;
259        }
260
261        @Override
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
273        Globals(Looper looper) {
274            IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
275            mService = IWallpaperManager.Stub.asInterface(b);
276            forgetLoadedWallpaper();
277        }
278
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
288        public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
289                @SetWallpaperFlags int which) {
290            return peekWallpaperBitmap(context, returnDefault, which, context.getUserId());
291        }
292
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
329        public void forgetLoadedWallpaper() {
330            synchronized (this) {
331                mCachedWallpaper = null;
332                mCachedWallpaperUserId = 0;
333                mDefaultWallpaper = null;
334            }
335        }
336
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
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
383    static void initGlobals(Looper looper) {
384        synchronized (sSync) {
385            if (sGlobals == null) {
386                sGlobals = new Globals(looper);
387            }
388        }
389    }
390
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     */
399    public static WallpaperManager getInstance(Context context) {
400        return (WallpaperManager)context.getSystemService(
401                Context.WALLPAPER_SERVICE);
402    }
403
404    /** @hide */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
1087    public void setStream(InputStream bitmapData) throws IOException {
1088        setStream(bitmapData, null, true);
1089    }
1090
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
1675        public WallpaperSetCompletion() {
1676            mLatch = new CountDownLatch(1);
1677        }
1678
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
1690        public void onWallpaperChanged() throws RemoteException {
1691            mLatch.countDown();
1692        }
1693    }
1694}
1695