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