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