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