Home | History | Annotate | Download | only in wallpaper
      1 /*
      2  * Copyright (C) 2008 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 com.android.server.wallpaper;
     18 
     19 import static android.app.WallpaperManager.FLAG_LOCK;
     20 import static android.app.WallpaperManager.FLAG_SYSTEM;
     21 import static android.os.ParcelFileDescriptor.MODE_CREATE;
     22 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
     23 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
     24 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
     25 
     26 import android.app.ActivityManager;
     27 import android.app.ActivityManagerNative;
     28 import android.app.AppGlobals;
     29 import android.app.AppOpsManager;
     30 import android.app.IUserSwitchObserver;
     31 import android.app.IWallpaperManager;
     32 import android.app.IWallpaperManagerCallback;
     33 import android.app.PendingIntent;
     34 import android.app.WallpaperInfo;
     35 import android.app.WallpaperManager;
     36 import android.app.admin.DevicePolicyManager;
     37 import android.app.backup.WallpaperBackupHelper;
     38 import android.content.BroadcastReceiver;
     39 import android.content.ComponentName;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.ServiceConnection;
     44 import android.content.pm.IPackageManager;
     45 import android.content.pm.PackageManager;
     46 import android.content.pm.PackageManager.NameNotFoundException;
     47 import android.content.pm.ResolveInfo;
     48 import android.content.pm.ServiceInfo;
     49 import android.content.pm.UserInfo;
     50 import android.content.res.Resources;
     51 import android.graphics.Bitmap;
     52 import android.graphics.BitmapFactory;
     53 import android.graphics.BitmapRegionDecoder;
     54 import android.graphics.Point;
     55 import android.graphics.Rect;
     56 import android.os.Binder;
     57 import android.os.Bundle;
     58 import android.os.Environment;
     59 import android.os.FileObserver;
     60 import android.os.FileUtils;
     61 import android.os.IBinder;
     62 import android.os.IRemoteCallback;
     63 import android.os.Process;
     64 import android.os.ParcelFileDescriptor;
     65 import android.os.RemoteCallbackList;
     66 import android.os.RemoteException;
     67 import android.os.SELinux;
     68 import android.os.ServiceManager;
     69 import android.os.SystemClock;
     70 import android.os.UserHandle;
     71 import android.os.UserManager;
     72 import android.service.wallpaper.IWallpaperConnection;
     73 import android.service.wallpaper.IWallpaperEngine;
     74 import android.service.wallpaper.IWallpaperService;
     75 import android.service.wallpaper.WallpaperService;
     76 import android.system.ErrnoException;
     77 import android.system.Os;
     78 import android.util.EventLog;
     79 import android.util.Slog;
     80 import android.util.SparseArray;
     81 import android.util.Xml;
     82 import android.view.Display;
     83 import android.view.IWindowManager;
     84 import android.view.WindowManager;
     85 
     86 import com.android.internal.R;
     87 import com.android.internal.content.PackageMonitor;
     88 import com.android.internal.util.FastXmlSerializer;
     89 import com.android.internal.util.JournaledFile;
     90 import com.android.server.EventLogTags;
     91 import com.android.server.SystemService;
     92 
     93 import libcore.io.IoUtils;
     94 
     95 import org.xmlpull.v1.XmlPullParser;
     96 import org.xmlpull.v1.XmlPullParserException;
     97 import org.xmlpull.v1.XmlSerializer;
     98 
     99 import java.io.BufferedOutputStream;
    100 import java.io.File;
    101 import java.io.FileDescriptor;
    102 import java.io.FileInputStream;
    103 import java.io.FileNotFoundException;
    104 import java.io.FileOutputStream;
    105 import java.io.IOException;
    106 import java.io.InputStream;
    107 import java.io.PrintWriter;
    108 import java.nio.charset.StandardCharsets;
    109 import java.util.Arrays;
    110 import java.util.List;
    111 
    112 public class WallpaperManagerService extends IWallpaperManager.Stub {
    113     static final String TAG = "WallpaperManagerService";
    114     static final boolean DEBUG = false;
    115 
    116     public static class Lifecycle extends SystemService {
    117         private WallpaperManagerService mService;
    118 
    119         public Lifecycle(Context context) {
    120             super(context);
    121         }
    122 
    123         @Override
    124         public void onStart() {
    125             mService = new WallpaperManagerService(getContext());
    126             publishBinderService(Context.WALLPAPER_SERVICE, mService);
    127         }
    128 
    129         @Override
    130         public void onBootPhase(int phase) {
    131             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
    132                 mService.systemReady();
    133             } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
    134                 mService.switchUser(UserHandle.USER_SYSTEM, null);
    135             }
    136         }
    137 
    138         @Override
    139         public void onUnlockUser(int userHandle) {
    140             mService.onUnlockUser(userHandle);
    141         }
    142     }
    143 
    144     final Object mLock = new Object();
    145 
    146     /**
    147      * Minimum time between crashes of a wallpaper service for us to consider
    148      * restarting it vs. just reverting to the static wallpaper.
    149      */
    150     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
    151     static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
    152     static final String WALLPAPER = "wallpaper_orig";
    153     static final String WALLPAPER_CROP = "wallpaper";
    154     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
    155     static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
    156     static final String WALLPAPER_INFO = "wallpaper_info.xml";
    157 
    158     // All the various per-user state files we need to be aware of
    159     static final String[] sPerUserFiles = new String[] {
    160         WALLPAPER, WALLPAPER_CROP,
    161         WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
    162         WALLPAPER_INFO
    163     };
    164 
    165     /**
    166      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
    167      * that the wallpaper has changed. The CREATE is triggered when there is no
    168      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
    169      * everytime the wallpaper is changed.
    170      */
    171     private class WallpaperObserver extends FileObserver {
    172 
    173         final int mUserId;
    174         final WallpaperData mWallpaper;
    175         final File mWallpaperDir;
    176         final File mWallpaperFile;
    177         final File mWallpaperLockFile;
    178         final File mWallpaperInfoFile;
    179 
    180         public WallpaperObserver(WallpaperData wallpaper) {
    181             super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
    182                     CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
    183             mUserId = wallpaper.userId;
    184             mWallpaperDir = getWallpaperDir(wallpaper.userId);
    185             mWallpaper = wallpaper;
    186             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
    187             mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
    188             mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
    189         }
    190 
    191         private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
    192             WallpaperData wallpaper = null;
    193             synchronized (mLock) {
    194                 if (lockChanged) {
    195                     wallpaper = mLockWallpaperMap.get(mUserId);
    196                 }
    197                 if (wallpaper == null) {
    198                     // no lock-specific wallpaper exists, or sys case, handled together
    199                     wallpaper = mWallpaperMap.get(mUserId);
    200                 }
    201             }
    202             return (wallpaper != null) ? wallpaper : mWallpaper;
    203         }
    204 
    205         @Override
    206         public void onEvent(int event, String path) {
    207             if (path == null) {
    208                 return;
    209             }
    210             final boolean moved = (event == MOVED_TO);
    211             final boolean written = (event == CLOSE_WRITE || moved);
    212             final File changedFile = new File(mWallpaperDir, path);
    213 
    214             // System and system+lock changes happen on the system wallpaper input file;
    215             // lock-only changes happen on the dedicated lock wallpaper input file
    216             final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
    217             final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
    218             WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
    219 
    220             if (DEBUG) {
    221                 Slog.v(TAG, "Wallpaper file change: evt=" + event
    222                         + " path=" + path
    223                         + " sys=" + sysWallpaperChanged
    224                         + " lock=" + lockWallpaperChanged
    225                         + " imagePending=" + wallpaper.imageWallpaperPending
    226                         + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending)
    227                         + " written=" + written);
    228             }
    229 
    230             if (moved && lockWallpaperChanged) {
    231                 // We just migrated sys -> lock to preserve imagery for an impending
    232                 // new system-only wallpaper.  Tell keyguard about it and make sure it
    233                 // has the right SELinux label.
    234                 if (DEBUG) {
    235                     Slog.i(TAG, "Sys -> lock MOVED_TO");
    236                 }
    237                 SELinux.restorecon(changedFile);
    238                 notifyLockWallpaperChanged();
    239                 return;
    240             }
    241 
    242             synchronized (mLock) {
    243                 if (sysWallpaperChanged || lockWallpaperChanged) {
    244                     notifyCallbacksLocked(wallpaper);
    245                     if (wallpaper.wallpaperComponent == null
    246                             || event != CLOSE_WRITE // includes the MOVED_TO case
    247                             || wallpaper.imageWallpaperPending) {
    248                         if (written) {
    249                             // The image source has finished writing the source image,
    250                             // so we now produce the crop rect (in the background), and
    251                             // only publish the new displayable (sub)image as a result
    252                             // of that work.
    253                             if (DEBUG) {
    254                                 Slog.v(TAG, "Wallpaper written; generating crop");
    255                             }
    256                             if (moved) {
    257                                 // This is a restore, so generate the crop using any just-restored new
    258                                 // crop guidelines, making sure to preserve our local dimension hints.
    259                                 // We also make sure to reapply the correct SELinux label.
    260                                 if (DEBUG) {
    261                                     Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
    262                                 }
    263                                 SELinux.restorecon(changedFile);
    264                                 loadSettingsLocked(wallpaper.userId, true);
    265                             }
    266                             generateCrop(wallpaper);
    267                             if (DEBUG) {
    268                                 Slog.v(TAG, "Crop done; invoking completion callback");
    269                             }
    270                             wallpaper.imageWallpaperPending = false;
    271                             if (wallpaper.setComplete != null) {
    272                                 try {
    273                                     wallpaper.setComplete.onWallpaperChanged();
    274                                 } catch (RemoteException e) {
    275                                     // if this fails we don't really care; the setting app may just
    276                                     // have crashed and that sort of thing is a fact of life.
    277                                 }
    278                             }
    279                             if (sysWallpaperChanged) {
    280                                 // If this was the system wallpaper, rebind...
    281                                 bindWallpaperComponentLocked(mImageWallpaper, true,
    282                                         false, wallpaper, null);
    283                             }
    284                             if (lockWallpaperChanged
    285                                     || (wallpaper.whichPending & FLAG_LOCK) != 0) {
    286                                 if (DEBUG) {
    287                                     Slog.i(TAG, "Lock-relevant wallpaper changed");
    288                                 }
    289                                 // either a lock-only wallpaper commit or a system+lock event.
    290                                 // if it's system-plus-lock we need to wipe the lock bookkeeping;
    291                                 // we're falling back to displaying the system wallpaper there.
    292                                 if (!lockWallpaperChanged) {
    293                                     mLockWallpaperMap.remove(wallpaper.userId);
    294                                 }
    295                                 // and in any case, tell keyguard about it
    296                                 notifyLockWallpaperChanged();
    297                             }
    298                             saveSettingsLocked(wallpaper.userId);
    299                         }
    300                     }
    301                 }
    302             }
    303         }
    304     }
    305 
    306     void notifyLockWallpaperChanged() {
    307         final IWallpaperManagerCallback cb = mKeyguardListener;
    308         if (cb != null) {
    309             try {
    310                 cb.onWallpaperChanged();
    311             } catch (RemoteException e) {
    312                 // Oh well it went away; no big deal
    313             }
    314         }
    315     }
    316 
    317     /**
    318      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
    319      * for display.
    320      */
    321     private void generateCrop(WallpaperData wallpaper) {
    322         boolean success = false;
    323 
    324         Rect cropHint = new Rect(wallpaper.cropHint);
    325 
    326         if (DEBUG) {
    327             Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
    328                     + Integer.toHexString(wallpaper.whichPending)
    329                     + " to " + wallpaper.cropFile.getName()
    330                     + " crop=(" + cropHint.width() + 'x' + cropHint.height()
    331                     + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
    332         }
    333 
    334         // Analyse the source; needed in multiple cases
    335         BitmapFactory.Options options = new BitmapFactory.Options();
    336         options.inJustDecodeBounds = true;
    337         BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
    338         if (options.outWidth <= 0 || options.outHeight <= 0) {
    339             Slog.e(TAG, "Invalid wallpaper data");
    340             success = false;
    341         } else {
    342             boolean needCrop = false;
    343             boolean needScale = false;
    344 
    345             // Empty crop means use the full image
    346             if (cropHint.isEmpty()) {
    347                 cropHint.left = cropHint.top = 0;
    348                 cropHint.right = options.outWidth;
    349                 cropHint.bottom = options.outHeight;
    350             } else {
    351                 // force the crop rect to lie within the measured bounds
    352                 cropHint.offset(
    353                         (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
    354                         (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
    355 
    356                 // Don't bother cropping if what we're left with is identity
    357                 needCrop = (options.outHeight >= cropHint.height()
    358                         && options.outWidth >= cropHint.width());
    359             }
    360 
    361             // scale if the crop height winds up not matching the recommended metrics
    362             needScale = (wallpaper.height != cropHint.height());
    363 
    364             if (DEBUG) {
    365                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
    366                 Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
    367                 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
    368                 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
    369             }
    370 
    371             if (!needCrop && !needScale) {
    372                 // Simple case:  the nominal crop fits what we want, so we take
    373                 // the whole thing and just copy the image file directly.
    374                 if (DEBUG) {
    375                     Slog.v(TAG, "Null crop of new wallpaper; copying");
    376                 }
    377                 success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
    378                 if (!success) {
    379                     wallpaper.cropFile.delete();
    380                     // TODO: fall back to default wallpaper in this case
    381                 }
    382             } else {
    383                 // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
    384                 FileOutputStream f = null;
    385                 BufferedOutputStream bos = null;
    386                 try {
    387                     BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
    388                             wallpaper.wallpaperFile.getAbsolutePath(), false);
    389 
    390                     // This actually downsamples only by powers of two, but that's okay; we do
    391                     // a proper scaling blit later.  This is to minimize transient RAM use.
    392                     // We calculate the largest power-of-two under the actual ratio rather than
    393                     // just let the decode take care of it because we also want to remap where the
    394                     // cropHint rectangle lies in the decoded [super]rect.
    395                     final BitmapFactory.Options scaler;
    396                     final int actualScale = cropHint.height() / wallpaper.height;
    397                     int scale = 1;
    398                     while (2*scale < actualScale) {
    399                         scale *= 2;
    400                     }
    401                     if (scale > 1) {
    402                         scaler = new BitmapFactory.Options();
    403                         scaler.inSampleSize = scale;
    404                         if (DEBUG) {
    405                             Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
    406                         }
    407                     } else {
    408                         scaler = null;
    409                     }
    410                     Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
    411                     decoder.recycle();
    412 
    413                     if (cropped == null) {
    414                         Slog.e(TAG, "Could not decode new wallpaper");
    415                     } else {
    416                         // We've got the extracted crop; now we want to scale it properly to
    417                         // the desired rectangle.  That's a height-biased operation: make it
    418                         // fit the hinted height, and accept whatever width we end up with.
    419                         cropHint.offsetTo(0, 0);
    420                         cropHint.right /= scale;    // adjust by downsampling factor
    421                         cropHint.bottom /= scale;
    422                         final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
    423                         if (DEBUG) {
    424                             Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
    425                         }
    426                         final int destWidth = (int)(cropHint.width() * heightR);
    427                         final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
    428                                 destWidth, wallpaper.height, true);
    429                         if (DEBUG) {
    430                             Slog.v(TAG, "Final extract:");
    431                             Slog.v(TAG, "  dims: w=" + wallpaper.width
    432                                     + " h=" + wallpaper.height);
    433                             Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
    434                                     + " h=" + finalCrop.getHeight());
    435                         }
    436 
    437                         f = new FileOutputStream(wallpaper.cropFile);
    438                         bos = new BufferedOutputStream(f, 32*1024);
    439                         finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
    440                         bos.flush();  // don't rely on the implicit flush-at-close when noting success
    441                         success = true;
    442                     }
    443                 } catch (Exception e) {
    444                     if (DEBUG) {
    445                         Slog.e(TAG, "Error decoding crop", e);
    446                     }
    447                 } finally {
    448                     IoUtils.closeQuietly(bos);
    449                     IoUtils.closeQuietly(f);
    450                 }
    451             }
    452         }
    453 
    454         if (!success) {
    455             Slog.e(TAG, "Unable to apply new wallpaper");
    456             wallpaper.cropFile.delete();
    457         }
    458 
    459         if (wallpaper.cropFile.exists()) {
    460             boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
    461             if (DEBUG) {
    462                 Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
    463             }
    464         }
    465     }
    466 
    467     final Context mContext;
    468     final IWindowManager mIWindowManager;
    469     final IPackageManager mIPackageManager;
    470     final MyPackageMonitor mMonitor;
    471     final AppOpsManager mAppOpsManager;
    472     WallpaperData mLastWallpaper;
    473     IWallpaperManagerCallback mKeyguardListener;
    474     boolean mWaitingForUnlock;
    475 
    476     /**
    477      * ID of the current wallpaper, changed every time anything sets a wallpaper.
    478      * This is used for external detection of wallpaper update activity.
    479      */
    480     int mWallpaperId;
    481 
    482     /**
    483      * Name of the component used to display bitmap wallpapers from either the gallery or
    484      * built-in wallpapers.
    485      */
    486     final ComponentName mImageWallpaper;
    487 
    488     final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
    489     final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
    490 
    491     int mCurrentUserId;
    492 
    493     static class WallpaperData {
    494 
    495         int userId;
    496 
    497         final File wallpaperFile;   // source image
    498         final File cropFile;        // eventual destination
    499 
    500         /**
    501          * True while the client is writing a new wallpaper
    502          */
    503         boolean imageWallpaperPending;
    504 
    505         /**
    506          * Which new wallpapers are being written; mirrors the 'which'
    507          * selector bit field to setWallpaper().
    508          */
    509         int whichPending;
    510 
    511         /**
    512          * Callback once the set + crop is finished
    513          */
    514         IWallpaperManagerCallback setComplete;
    515 
    516         /**
    517          * Is the OS allowed to back up this wallpaper imagery?
    518          */
    519         boolean allowBackup;
    520 
    521         /**
    522          * Resource name if using a picture from the wallpaper gallery
    523          */
    524         String name = "";
    525 
    526         /**
    527          * The component name of the currently set live wallpaper.
    528          */
    529         ComponentName wallpaperComponent;
    530 
    531         /**
    532          * The component name of the wallpaper that should be set next.
    533          */
    534         ComponentName nextWallpaperComponent;
    535 
    536         /**
    537          * The ID of this wallpaper
    538          */
    539         int wallpaperId;
    540 
    541         WallpaperConnection connection;
    542         long lastDiedTime;
    543         boolean wallpaperUpdating;
    544         WallpaperObserver wallpaperObserver;
    545 
    546         /**
    547          * List of callbacks registered they should each be notified when the wallpaper is changed.
    548          */
    549         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
    550                 = new RemoteCallbackList<IWallpaperManagerCallback>();
    551 
    552         int width = -1;
    553         int height = -1;
    554 
    555         /**
    556          * The crop hint supplied for displaying a subset of the source image
    557          */
    558         final Rect cropHint = new Rect(0, 0, 0, 0);
    559 
    560         final Rect padding = new Rect(0, 0, 0, 0);
    561 
    562         WallpaperData(int userId, String inputFileName, String cropFileName) {
    563             this.userId = userId;
    564             final File wallpaperDir = getWallpaperDir(userId);
    565             wallpaperFile = new File(wallpaperDir, inputFileName);
    566             cropFile = new File(wallpaperDir, cropFileName);
    567         }
    568 
    569         // Called during initialization of a given user's wallpaper bookkeeping
    570         boolean cropExists() {
    571             return cropFile.exists();
    572         }
    573     }
    574 
    575     int makeWallpaperIdLocked() {
    576         do {
    577             ++mWallpaperId;
    578         } while (mWallpaperId == 0);
    579         return mWallpaperId;
    580     }
    581 
    582     class WallpaperConnection extends IWallpaperConnection.Stub
    583             implements ServiceConnection {
    584         final WallpaperInfo mInfo;
    585         final Binder mToken = new Binder();
    586         IWallpaperService mService;
    587         IWallpaperEngine mEngine;
    588         WallpaperData mWallpaper;
    589         IRemoteCallback mReply;
    590 
    591         boolean mDimensionsChanged = false;
    592         boolean mPaddingChanged = false;
    593 
    594         public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
    595             mInfo = info;
    596             mWallpaper = wallpaper;
    597         }
    598 
    599         @Override
    600         public void onServiceConnected(ComponentName name, IBinder service) {
    601             synchronized (mLock) {
    602                 if (mWallpaper.connection == this) {
    603                     mService = IWallpaperService.Stub.asInterface(service);
    604                     attachServiceLocked(this, mWallpaper);
    605                     // XXX should probably do saveSettingsLocked() later
    606                     // when we have an engine, but I'm not sure about
    607                     // locking there and anyway we always need to be able to
    608                     // recover if there is something wrong.
    609                     saveSettingsLocked(mWallpaper.userId);
    610                 }
    611             }
    612         }
    613 
    614         @Override
    615         public void onServiceDisconnected(ComponentName name) {
    616             synchronized (mLock) {
    617                 mService = null;
    618                 mEngine = null;
    619                 if (mWallpaper.connection == this) {
    620                     Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
    621                     if (!mWallpaper.wallpaperUpdating
    622                             && mWallpaper.userId == mCurrentUserId) {
    623                         // There is a race condition which causes
    624                         // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
    625                         // currently updating since the broadcast notifying us is async.
    626                         // This race is overcome by the general rule that we only reset the
    627                         // wallpaper if its service was shut down twice
    628                         // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
    629                         if (mWallpaper.lastDiedTime != 0
    630                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
    631                                     > SystemClock.uptimeMillis()) {
    632                             Slog.w(TAG, "Reverting to built-in wallpaper!");
    633                             clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
    634                         } else {
    635                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
    636                         }
    637                         final String flattened = name.flattenToString();
    638                         EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
    639                                 flattened.substring(0, Math.min(flattened.length(),
    640                                         MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
    641                     }
    642                 }
    643             }
    644         }
    645 
    646         @Override
    647         public void attachEngine(IWallpaperEngine engine) {
    648             synchronized (mLock) {
    649                 mEngine = engine;
    650                 if (mDimensionsChanged) {
    651                     try {
    652                         mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
    653                     } catch (RemoteException e) {
    654                         Slog.w(TAG, "Failed to set wallpaper dimensions", e);
    655                     }
    656                     mDimensionsChanged = false;
    657                 }
    658                 if (mPaddingChanged) {
    659                     try {
    660                         mEngine.setDisplayPadding(mWallpaper.padding);
    661                     } catch (RemoteException e) {
    662                         Slog.w(TAG, "Failed to set wallpaper padding", e);
    663                     }
    664                     mPaddingChanged = false;
    665                 }
    666             }
    667         }
    668 
    669         @Override
    670         public void engineShown(IWallpaperEngine engine) {
    671             synchronized (mLock) {
    672                 if (mReply != null) {
    673                     long ident = Binder.clearCallingIdentity();
    674                     try {
    675                         mReply.sendResult(null);
    676                     } catch (RemoteException e) {
    677                         Binder.restoreCallingIdentity(ident);
    678                     }
    679                     mReply = null;
    680                 }
    681             }
    682         }
    683 
    684         @Override
    685         public ParcelFileDescriptor setWallpaper(String name) {
    686             synchronized (mLock) {
    687                 if (mWallpaper.connection == this) {
    688                     return updateWallpaperBitmapLocked(name, mWallpaper, null);
    689                 }
    690                 return null;
    691             }
    692         }
    693     }
    694 
    695     class MyPackageMonitor extends PackageMonitor {
    696         @Override
    697         public void onPackageUpdateFinished(String packageName, int uid) {
    698             synchronized (mLock) {
    699                 if (mCurrentUserId != getChangingUserId()) {
    700                     return;
    701                 }
    702                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
    703                 if (wallpaper != null) {
    704                     if (wallpaper.wallpaperComponent != null
    705                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    706                         wallpaper.wallpaperUpdating = false;
    707                         ComponentName comp = wallpaper.wallpaperComponent;
    708                         clearWallpaperComponentLocked(wallpaper);
    709                         if (!bindWallpaperComponentLocked(comp, false, false,
    710                                 wallpaper, null)) {
    711                             Slog.w(TAG, "Wallpaper no longer available; reverting to default");
    712                             clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
    713                         }
    714                     }
    715                 }
    716             }
    717         }
    718 
    719         @Override
    720         public void onPackageModified(String packageName) {
    721             synchronized (mLock) {
    722                 if (mCurrentUserId != getChangingUserId()) {
    723                     return;
    724                 }
    725                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
    726                 if (wallpaper != null) {
    727                     if (wallpaper.wallpaperComponent == null
    728                             || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    729                         return;
    730                     }
    731                     doPackagesChangedLocked(true, wallpaper);
    732                 }
    733             }
    734         }
    735 
    736         @Override
    737         public void onPackageUpdateStarted(String packageName, int uid) {
    738             synchronized (mLock) {
    739                 if (mCurrentUserId != getChangingUserId()) {
    740                     return;
    741                 }
    742                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
    743                 if (wallpaper != null) {
    744                     if (wallpaper.wallpaperComponent != null
    745                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    746                         wallpaper.wallpaperUpdating = true;
    747                     }
    748                 }
    749             }
    750         }
    751 
    752         @Override
    753         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
    754             synchronized (mLock) {
    755                 boolean changed = false;
    756                 if (mCurrentUserId != getChangingUserId()) {
    757                     return false;
    758                 }
    759                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
    760                 if (wallpaper != null) {
    761                     boolean res = doPackagesChangedLocked(doit, wallpaper);
    762                     changed |= res;
    763                 }
    764                 return changed;
    765             }
    766         }
    767 
    768         @Override
    769         public void onSomePackagesChanged() {
    770             synchronized (mLock) {
    771                 if (mCurrentUserId != getChangingUserId()) {
    772                     return;
    773                 }
    774                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
    775                 if (wallpaper != null) {
    776                     doPackagesChangedLocked(true, wallpaper);
    777                 }
    778             }
    779         }
    780 
    781         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
    782             boolean changed = false;
    783             if (wallpaper.wallpaperComponent != null) {
    784                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
    785                         .getPackageName());
    786                 if (change == PACKAGE_PERMANENT_CHANGE
    787                         || change == PACKAGE_TEMPORARY_CHANGE) {
    788                     changed = true;
    789                     if (doit) {
    790                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
    791                                 + wallpaper.wallpaperComponent);
    792                         clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
    793                     }
    794                 }
    795             }
    796             if (wallpaper.nextWallpaperComponent != null) {
    797                 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
    798                         .getPackageName());
    799                 if (change == PACKAGE_PERMANENT_CHANGE
    800                         || change == PACKAGE_TEMPORARY_CHANGE) {
    801                     wallpaper.nextWallpaperComponent = null;
    802                 }
    803             }
    804             if (wallpaper.wallpaperComponent != null
    805                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
    806                 try {
    807                     mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
    808                             PackageManager.MATCH_DIRECT_BOOT_AWARE
    809                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
    810                 } catch (NameNotFoundException e) {
    811                     Slog.w(TAG, "Wallpaper component gone, removing: "
    812                             + wallpaper.wallpaperComponent);
    813                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
    814                 }
    815             }
    816             if (wallpaper.nextWallpaperComponent != null
    817                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
    818                 try {
    819                     mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
    820                             PackageManager.MATCH_DIRECT_BOOT_AWARE
    821                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
    822                 } catch (NameNotFoundException e) {
    823                     wallpaper.nextWallpaperComponent = null;
    824                 }
    825             }
    826             return changed;
    827         }
    828     }
    829 
    830     public WallpaperManagerService(Context context) {
    831         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
    832         mContext = context;
    833         mImageWallpaper = ComponentName.unflattenFromString(
    834                 context.getResources().getString(R.string.image_wallpaper_component));
    835         mIWindowManager = IWindowManager.Stub.asInterface(
    836                 ServiceManager.getService(Context.WINDOW_SERVICE));
    837         mIPackageManager = AppGlobals.getPackageManager();
    838         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
    839         mMonitor = new MyPackageMonitor();
    840         mMonitor.register(context, null, UserHandle.ALL, true);
    841         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
    842         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
    843     }
    844 
    845     private static File getWallpaperDir(int userId) {
    846         return Environment.getUserSystemDirectory(userId);
    847     }
    848 
    849     @Override
    850     protected void finalize() throws Throwable {
    851         super.finalize();
    852         for (int i = 0; i < mWallpaperMap.size(); i++) {
    853             WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    854             wallpaper.wallpaperObserver.stopWatching();
    855         }
    856     }
    857 
    858     void systemReady() {
    859         if (DEBUG) Slog.v(TAG, "systemReady");
    860         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
    861         // If we think we're going to be using the system image wallpaper imagery, make
    862         // sure we have something to render
    863         if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
    864             // No crop file? Make sure we've finished the processing sequence if necessary
    865             if (!wallpaper.cropExists()) {
    866                 if (DEBUG) {
    867                     Slog.i(TAG, "No crop; regenerating from source");
    868                 }
    869                 generateCrop(wallpaper);
    870             }
    871             // Still nothing?  Fall back to default.
    872             if (!wallpaper.cropExists()) {
    873                 if (DEBUG) {
    874                     Slog.i(TAG, "Unable to regenerate crop; resetting");
    875                 }
    876                 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
    877             }
    878         } else {
    879             if (DEBUG) {
    880                 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
    881             }
    882         }
    883 
    884         IntentFilter userFilter = new IntentFilter();
    885         userFilter.addAction(Intent.ACTION_USER_REMOVED);
    886         mContext.registerReceiver(new BroadcastReceiver() {
    887             @Override
    888             public void onReceive(Context context, Intent intent) {
    889                 final String action = intent.getAction();
    890                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
    891                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
    892                             UserHandle.USER_NULL));
    893                 }
    894             }
    895         }, userFilter);
    896 
    897         try {
    898             ActivityManagerNative.getDefault().registerUserSwitchObserver(
    899                     new IUserSwitchObserver.Stub() {
    900                         @Override
    901                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
    902                             switchUser(newUserId, reply);
    903                         }
    904 
    905                         @Override
    906                         public void onUserSwitchComplete(int newUserId) throws RemoteException {
    907                         }
    908 
    909                         @Override
    910                         public void onForegroundProfileSwitch(int newProfileId) {
    911                             // Ignore.
    912                         }
    913                     });
    914         } catch (RemoteException e) {
    915             // TODO Auto-generated catch block
    916             e.printStackTrace();
    917         }
    918     }
    919 
    920     /** Called by SystemBackupAgent */
    921     public String getName() {
    922         // Verify caller is the system
    923         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
    924             throw new RuntimeException("getName() can only be called from the system process");
    925         }
    926         synchronized (mLock) {
    927             return mWallpaperMap.get(0).name;
    928         }
    929     }
    930 
    931     void stopObserver(WallpaperData wallpaper) {
    932         if (wallpaper != null) {
    933             if (wallpaper.wallpaperObserver != null) {
    934                 wallpaper.wallpaperObserver.stopWatching();
    935                 wallpaper.wallpaperObserver = null;
    936             }
    937         }
    938     }
    939 
    940     void stopObserversLocked(int userId) {
    941         stopObserver(mWallpaperMap.get(userId));
    942         stopObserver(mLockWallpaperMap.get(userId));
    943         mWallpaperMap.remove(userId);
    944         mLockWallpaperMap.remove(userId);
    945     }
    946 
    947     void onUnlockUser(int userId) {
    948         synchronized (mLock) {
    949             if (mCurrentUserId == userId && mWaitingForUnlock) {
    950                 switchUser(userId, null);
    951             }
    952         }
    953     }
    954 
    955     void onRemoveUser(int userId) {
    956         if (userId < 1) return;
    957 
    958         final File wallpaperDir = getWallpaperDir(userId);
    959         synchronized (mLock) {
    960             stopObserversLocked(userId);
    961             for (String filename : sPerUserFiles) {
    962                 new File(wallpaperDir, filename).delete();
    963             }
    964         }
    965     }
    966 
    967     void switchUser(int userId, IRemoteCallback reply) {
    968         synchronized (mLock) {
    969             mCurrentUserId = userId;
    970             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
    971             // Not started watching yet, in case wallpaper data was loaded for other reasons.
    972             if (wallpaper.wallpaperObserver == null) {
    973                 wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
    974                 wallpaper.wallpaperObserver.startWatching();
    975             }
    976             switchWallpaper(wallpaper, reply);
    977         }
    978     }
    979 
    980     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
    981         synchronized (mLock) {
    982             mWaitingForUnlock = false;
    983             final ComponentName cname = wallpaper.wallpaperComponent != null ?
    984                     wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
    985             if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
    986                 // We failed to bind the desired wallpaper, but that might
    987                 // happen if the wallpaper isn't direct-boot aware
    988                 ServiceInfo si = null;
    989                 try {
    990                     si = mIPackageManager.getServiceInfo(cname,
    991                             PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
    992                 } catch (RemoteException ignored) {
    993                 }
    994 
    995                 if (si == null) {
    996                     Slog.w(TAG, "Failure starting previous wallpaper; clearing");
    997                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
    998                 } else {
    999                     Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
   1000                     // We might end up persisting the current wallpaper data
   1001                     // while locked, so pretend like the component was actually
   1002                     // bound into place
   1003                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
   1004                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
   1005                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
   1006                     ensureSaneWallpaperData(fallback);
   1007                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
   1008                     mWaitingForUnlock = true;
   1009                 }
   1010             }
   1011         }
   1012     }
   1013 
   1014     @Override
   1015     public void clearWallpaper(String callingPackage, int which, int userId) {
   1016         if (DEBUG) Slog.v(TAG, "clearWallpaper");
   1017         checkPermission(android.Manifest.permission.SET_WALLPAPER);
   1018         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
   1019             return;
   1020         }
   1021         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1022                 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
   1023 
   1024         synchronized (mLock) {
   1025             clearWallpaperLocked(false, which, userId, null);
   1026         }
   1027     }
   1028 
   1029     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
   1030         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
   1031             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
   1032         }
   1033 
   1034         WallpaperData wallpaper = null;
   1035         if (which == FLAG_LOCK) {
   1036             wallpaper = mLockWallpaperMap.get(userId);
   1037             if (wallpaper == null) {
   1038                 // It's already gone; we're done.
   1039                 if (DEBUG) {
   1040                     Slog.i(TAG, "Lock wallpaper already cleared");
   1041                 }
   1042                 return;
   1043             }
   1044         } else {
   1045             wallpaper = mWallpaperMap.get(userId);
   1046             if (wallpaper == null) {
   1047                 // Might need to bring it in the first time to establish our rewrite
   1048                 loadSettingsLocked(userId, false);
   1049                 wallpaper = mWallpaperMap.get(userId);
   1050             }
   1051         }
   1052         if (wallpaper == null) {
   1053             return;
   1054         }
   1055 
   1056         final long ident = Binder.clearCallingIdentity();
   1057         try {
   1058             if (wallpaper.wallpaperFile.exists()) {
   1059                 wallpaper.wallpaperFile.delete();
   1060                 wallpaper.cropFile.delete();
   1061                 if (which == FLAG_LOCK) {
   1062                     mLockWallpaperMap.remove(userId);
   1063                     final IWallpaperManagerCallback cb = mKeyguardListener;
   1064                     if (cb != null) {
   1065                         if (DEBUG) {
   1066                             Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
   1067                         }
   1068                         try {
   1069                             cb.onWallpaperChanged();
   1070                         } catch (RemoteException e) {
   1071                             // Oh well it went away; no big deal
   1072                         }
   1073                     }
   1074                     saveSettingsLocked(userId);
   1075                     return;
   1076                 }
   1077             }
   1078 
   1079             RuntimeException e = null;
   1080             try {
   1081                 wallpaper.imageWallpaperPending = false;
   1082                 if (userId != mCurrentUserId) return;
   1083                 if (bindWallpaperComponentLocked(defaultFailed
   1084                         ? mImageWallpaper
   1085                                 : null, true, false, wallpaper, reply)) {
   1086                     return;
   1087                 }
   1088             } catch (IllegalArgumentException e1) {
   1089                 e = e1;
   1090             }
   1091 
   1092             // This can happen if the default wallpaper component doesn't
   1093             // exist.  This should be a system configuration problem, but
   1094             // let's not let it crash the system and just live with no
   1095             // wallpaper.
   1096             Slog.e(TAG, "Default wallpaper component not found!", e);
   1097             clearWallpaperComponentLocked(wallpaper);
   1098             if (reply != null) {
   1099                 try {
   1100                     reply.sendResult(null);
   1101                 } catch (RemoteException e1) {
   1102                 }
   1103             }
   1104         } finally {
   1105             Binder.restoreCallingIdentity(ident);
   1106         }
   1107     }
   1108 
   1109     public boolean hasNamedWallpaper(String name) {
   1110         synchronized (mLock) {
   1111             List<UserInfo> users;
   1112             long ident = Binder.clearCallingIdentity();
   1113             try {
   1114                 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
   1115             } finally {
   1116                 Binder.restoreCallingIdentity(ident);
   1117             }
   1118             for (UserInfo user: users) {
   1119                 // ignore managed profiles
   1120                 if (user.isManagedProfile()) {
   1121                     continue;
   1122                 }
   1123                 WallpaperData wd = mWallpaperMap.get(user.id);
   1124                 if (wd == null) {
   1125                     // User hasn't started yet, so load her settings to peek at the wallpaper
   1126                     loadSettingsLocked(user.id, false);
   1127                     wd = mWallpaperMap.get(user.id);
   1128                 }
   1129                 if (wd != null && name.equals(wd.name)) {
   1130                     return true;
   1131                 }
   1132             }
   1133         }
   1134         return false;
   1135     }
   1136 
   1137     private Point getDefaultDisplaySize() {
   1138         Point p = new Point();
   1139         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
   1140         Display d = wm.getDefaultDisplay();
   1141         d.getRealSize(p);
   1142         return p;
   1143     }
   1144 
   1145     public void setDimensionHints(int width, int height, String callingPackage)
   1146             throws RemoteException {
   1147         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
   1148         if (!isWallpaperSupported(callingPackage)) {
   1149             return;
   1150         }
   1151         synchronized (mLock) {
   1152             int userId = UserHandle.getCallingUserId();
   1153             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
   1154             if (width <= 0 || height <= 0) {
   1155                 throw new IllegalArgumentException("width and height must be > 0");
   1156             }
   1157             // Make sure it is at least as large as the display.
   1158             Point displaySize = getDefaultDisplaySize();
   1159             width = Math.max(width, displaySize.x);
   1160             height = Math.max(height, displaySize.y);
   1161 
   1162             if (width != wallpaper.width || height != wallpaper.height) {
   1163                 wallpaper.width = width;
   1164                 wallpaper.height = height;
   1165                 saveSettingsLocked(userId);
   1166                 if (mCurrentUserId != userId) return; // Don't change the properties now
   1167                 if (wallpaper.connection != null) {
   1168                     if (wallpaper.connection.mEngine != null) {
   1169                         try {
   1170                             wallpaper.connection.mEngine.setDesiredSize(
   1171                                     width, height);
   1172                         } catch (RemoteException e) {
   1173                         }
   1174                         notifyCallbacksLocked(wallpaper);
   1175                     } else if (wallpaper.connection.mService != null) {
   1176                         // We've attached to the service but the engine hasn't attached back to us
   1177                         // yet. This means it will be created with the previous dimensions, so we
   1178                         // need to update it to the new dimensions once it attaches.
   1179                         wallpaper.connection.mDimensionsChanged = true;
   1180                     }
   1181                 }
   1182             }
   1183         }
   1184     }
   1185 
   1186     public int getWidthHint() throws RemoteException {
   1187         synchronized (mLock) {
   1188             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
   1189             if (wallpaper != null) {
   1190                 return wallpaper.width;
   1191             } else {
   1192                 return 0;
   1193             }
   1194         }
   1195     }
   1196 
   1197     public int getHeightHint() throws RemoteException {
   1198         synchronized (mLock) {
   1199             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
   1200             if (wallpaper != null) {
   1201                 return wallpaper.height;
   1202             } else {
   1203                 return 0;
   1204             }
   1205         }
   1206     }
   1207 
   1208     public void setDisplayPadding(Rect padding, String callingPackage) {
   1209         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
   1210         if (!isWallpaperSupported(callingPackage)) {
   1211             return;
   1212         }
   1213         synchronized (mLock) {
   1214             int userId = UserHandle.getCallingUserId();
   1215             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
   1216             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
   1217                 throw new IllegalArgumentException("padding must be positive: " + padding);
   1218             }
   1219 
   1220             if (!padding.equals(wallpaper.padding)) {
   1221                 wallpaper.padding.set(padding);
   1222                 saveSettingsLocked(userId);
   1223                 if (mCurrentUserId != userId) return; // Don't change the properties now
   1224                 if (wallpaper.connection != null) {
   1225                     if (wallpaper.connection.mEngine != null) {
   1226                         try {
   1227                             wallpaper.connection.mEngine.setDisplayPadding(padding);
   1228                         } catch (RemoteException e) {
   1229                         }
   1230                         notifyCallbacksLocked(wallpaper);
   1231                     } else if (wallpaper.connection.mService != null) {
   1232                         // We've attached to the service but the engine hasn't attached back to us
   1233                         // yet. This means it will be created with the previous dimensions, so we
   1234                         // need to update it to the new dimensions once it attaches.
   1235                         wallpaper.connection.mPaddingChanged = true;
   1236                     }
   1237                 }
   1238             }
   1239         }
   1240     }
   1241 
   1242     @Override
   1243     public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, final int which,
   1244             Bundle outParams, int wallpaperUserId) {
   1245         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1246                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
   1247 
   1248         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
   1249             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
   1250         }
   1251 
   1252         synchronized (mLock) {
   1253             final SparseArray<WallpaperData> whichSet =
   1254                     (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
   1255             WallpaperData wallpaper = whichSet.get(wallpaperUserId);
   1256             if (wallpaper == null) {
   1257                 // common case, this is the first lookup post-boot of the system or
   1258                 // unified lock, so we bring up the saved state lazily now and recheck.
   1259                 loadSettingsLocked(wallpaperUserId, false);
   1260                 wallpaper = whichSet.get(wallpaperUserId);
   1261                 if (wallpaper == null) {
   1262                     return null;
   1263                 }
   1264             }
   1265             try {
   1266                 if (outParams != null) {
   1267                     outParams.putInt("width", wallpaper.width);
   1268                     outParams.putInt("height", wallpaper.height);
   1269                 }
   1270                 if (cb != null) {
   1271                     wallpaper.callbacks.register(cb);
   1272                 }
   1273                 if (!wallpaper.cropFile.exists()) {
   1274                     return null;
   1275                 }
   1276                 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
   1277             } catch (FileNotFoundException e) {
   1278                 /* Shouldn't happen as we check to see if the file exists */
   1279                 Slog.w(TAG, "Error getting wallpaper", e);
   1280             }
   1281             return null;
   1282         }
   1283     }
   1284 
   1285     @Override
   1286     public WallpaperInfo getWallpaperInfo() {
   1287         int userId = UserHandle.getCallingUserId();
   1288         synchronized (mLock) {
   1289             WallpaperData wallpaper = mWallpaperMap.get(userId);
   1290             if (wallpaper != null && wallpaper.connection != null) {
   1291                 return wallpaper.connection.mInfo;
   1292             }
   1293             return null;
   1294         }
   1295     }
   1296 
   1297     @Override
   1298     public int getWallpaperIdForUser(int which, int userId) {
   1299         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
   1300                 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
   1301 
   1302         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
   1303             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
   1304         }
   1305 
   1306         final SparseArray<WallpaperData> map =
   1307                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
   1308         synchronized (mLock) {
   1309             WallpaperData wallpaper = map.get(userId);
   1310             if (wallpaper != null) {
   1311                 return wallpaper.wallpaperId;
   1312             }
   1313         }
   1314         return -1;
   1315     }
   1316 
   1317     @Override
   1318     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
   1319         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
   1320         synchronized (mLock) {
   1321             mKeyguardListener = cb;
   1322         }
   1323         return true;
   1324     }
   1325 
   1326     @Override
   1327     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
   1328             Rect cropHint, boolean allowBackup, Bundle extras, int which,
   1329             IWallpaperManagerCallback completion) {
   1330         checkPermission(android.Manifest.permission.SET_WALLPAPER);
   1331 
   1332         if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
   1333             final String msg = "Must specify a valid wallpaper category to set";
   1334             Slog.e(TAG, msg);
   1335             throw new IllegalArgumentException(msg);
   1336         }
   1337 
   1338         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
   1339             return null;
   1340         }
   1341 
   1342         // "null" means the no-op crop, preserving the full input image
   1343         if (cropHint == null) {
   1344             cropHint = new Rect(0, 0, 0, 0);
   1345         } else {
   1346             if (cropHint.isEmpty()
   1347                     || cropHint.left < 0
   1348                     || cropHint.top < 0) {
   1349                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
   1350             }
   1351         }
   1352 
   1353         final int userId = UserHandle.getCallingUserId();
   1354 
   1355         synchronized (mLock) {
   1356             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
   1357             WallpaperData wallpaper;
   1358 
   1359             /* If we're setting system but not lock, and lock is currently sharing the system
   1360              * wallpaper, we need to migrate that image over to being lock-only before
   1361              * the caller here writes new bitmap data.
   1362              */
   1363             if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
   1364                 if (DEBUG) {
   1365                     Slog.i(TAG, "Migrating system->lock to preserve");
   1366                 }
   1367                 migrateSystemToLockWallpaperLocked(userId);
   1368             }
   1369 
   1370             wallpaper = getWallpaperSafeLocked(userId, which);
   1371             final long ident = Binder.clearCallingIdentity();
   1372             try {
   1373                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
   1374                 if (pfd != null) {
   1375                     wallpaper.imageWallpaperPending = true;
   1376                     wallpaper.whichPending = which;
   1377                     wallpaper.setComplete = completion;
   1378                     wallpaper.cropHint.set(cropHint);
   1379                     if ((which & FLAG_SYSTEM) != 0) {
   1380                         wallpaper.allowBackup = allowBackup;
   1381                     }
   1382                 }
   1383                 return pfd;
   1384             } finally {
   1385                 Binder.restoreCallingIdentity(ident);
   1386             }
   1387         }
   1388     }
   1389 
   1390     private void migrateSystemToLockWallpaperLocked(int userId) {
   1391         WallpaperData sysWP = mWallpaperMap.get(userId);
   1392         if (sysWP == null) {
   1393             if (DEBUG) {
   1394                 Slog.i(TAG, "No system wallpaper?  Not tracking for lock-only");
   1395             }
   1396             return;
   1397         }
   1398 
   1399         // We know a-priori that there is no lock-only wallpaper currently
   1400         WallpaperData lockWP = new WallpaperData(userId,
   1401                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
   1402         lockWP.wallpaperId = sysWP.wallpaperId;
   1403         lockWP.cropHint.set(sysWP.cropHint);
   1404         lockWP.width = sysWP.width;
   1405         lockWP.height = sysWP.height;
   1406         lockWP.allowBackup = false;
   1407 
   1408         // Migrate the bitmap files outright; no need to copy
   1409         try {
   1410             Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
   1411             Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
   1412         } catch (ErrnoException e) {
   1413             Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
   1414             lockWP.wallpaperFile.delete();
   1415             lockWP.cropFile.delete();
   1416             return;
   1417         }
   1418 
   1419         mLockWallpaperMap.put(userId, lockWP);
   1420     }
   1421 
   1422     ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
   1423             Bundle extras) {
   1424         if (name == null) name = "";
   1425         try {
   1426             File dir = getWallpaperDir(wallpaper.userId);
   1427             if (!dir.exists()) {
   1428                 dir.mkdir();
   1429                 FileUtils.setPermissions(
   1430                         dir.getPath(),
   1431                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
   1432                         -1, -1);
   1433             }
   1434             ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
   1435                     MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
   1436             if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
   1437                 return null;
   1438             }
   1439             wallpaper.name = name;
   1440             wallpaper.wallpaperId = makeWallpaperIdLocked();
   1441             if (extras != null) {
   1442                 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
   1443             }
   1444             if (DEBUG) {
   1445                 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
   1446                         + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
   1447             }
   1448             return fd;
   1449         } catch (FileNotFoundException e) {
   1450             Slog.w(TAG, "Error setting wallpaper", e);
   1451         }
   1452         return null;
   1453     }
   1454 
   1455     @Override
   1456     public void setWallpaperComponentChecked(ComponentName name, String callingPackage) {
   1457         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
   1458             setWallpaperComponent(name);
   1459         }
   1460     }
   1461 
   1462     // ToDo: Remove this version of the function
   1463     @Override
   1464     public void setWallpaperComponent(ComponentName name) {
   1465         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
   1466         synchronized (mLock) {
   1467             if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
   1468             int userId = UserHandle.getCallingUserId();
   1469             WallpaperData wallpaper = mWallpaperMap.get(userId);
   1470             if (wallpaper == null) {
   1471                 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
   1472             }
   1473             final long ident = Binder.clearCallingIdentity();
   1474             try {
   1475                 wallpaper.imageWallpaperPending = false;
   1476                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
   1477                     wallpaper.wallpaperId = makeWallpaperIdLocked();
   1478                     notifyCallbacksLocked(wallpaper);
   1479                 }
   1480             } finally {
   1481                 Binder.restoreCallingIdentity(ident);
   1482             }
   1483         }
   1484     }
   1485 
   1486     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
   1487             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
   1488         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
   1489         // Has the component changed?
   1490         if (!force) {
   1491             if (wallpaper.connection != null) {
   1492                 if (wallpaper.wallpaperComponent == null) {
   1493                     if (componentName == null) {
   1494                         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
   1495                         // Still using default wallpaper.
   1496                         return true;
   1497                     }
   1498                 } else if (wallpaper.wallpaperComponent.equals(componentName)) {
   1499                     // Changing to same wallpaper.
   1500                     if (DEBUG) Slog.v(TAG, "same wallpaper");
   1501                     return true;
   1502                 }
   1503             }
   1504         }
   1505 
   1506         try {
   1507             if (componentName == null) {
   1508                 componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
   1509                 if (componentName == null) {
   1510                     // Fall back to static image wallpaper
   1511                     componentName = mImageWallpaper;
   1512                     //clearWallpaperComponentLocked();
   1513                     //return;
   1514                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
   1515                 }
   1516             }
   1517             int serviceUserId = wallpaper.userId;
   1518             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
   1519                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
   1520             if (si == null) {
   1521                 // The wallpaper component we're trying to use doesn't exist
   1522                 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
   1523                 return false;
   1524             }
   1525             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
   1526                 String msg = "Selected service does not require "
   1527                         + android.Manifest.permission.BIND_WALLPAPER
   1528                         + ": " + componentName;
   1529                 if (fromUser) {
   1530                     throw new SecurityException(msg);
   1531                 }
   1532                 Slog.w(TAG, msg);
   1533                 return false;
   1534             }
   1535 
   1536             WallpaperInfo wi = null;
   1537 
   1538             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
   1539             if (componentName != null && !componentName.equals(mImageWallpaper)) {
   1540                 // Make sure the selected service is actually a wallpaper service.
   1541                 List<ResolveInfo> ris =
   1542                         mIPackageManager.queryIntentServices(intent,
   1543                                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
   1544                                 PackageManager.GET_META_DATA, serviceUserId).getList();
   1545                 for (int i=0; i<ris.size(); i++) {
   1546                     ServiceInfo rsi = ris.get(i).serviceInfo;
   1547                     if (rsi.name.equals(si.name) &&
   1548                             rsi.packageName.equals(si.packageName)) {
   1549                         try {
   1550                             wi = new WallpaperInfo(mContext, ris.get(i));
   1551                         } catch (XmlPullParserException e) {
   1552                             if (fromUser) {
   1553                                 throw new IllegalArgumentException(e);
   1554                             }
   1555                             Slog.w(TAG, e);
   1556                             return false;
   1557                         } catch (IOException e) {
   1558                             if (fromUser) {
   1559                                 throw new IllegalArgumentException(e);
   1560                             }
   1561                             Slog.w(TAG, e);
   1562                             return false;
   1563                         }
   1564                         break;
   1565                     }
   1566                 }
   1567                 if (wi == null) {
   1568                     String msg = "Selected service is not a wallpaper: "
   1569                             + componentName;
   1570                     if (fromUser) {
   1571                         throw new SecurityException(msg);
   1572                     }
   1573                     Slog.w(TAG, msg);
   1574                     return false;
   1575                 }
   1576             }
   1577 
   1578             // Bind the service!
   1579             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
   1580             WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
   1581             intent.setComponent(componentName);
   1582             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
   1583                     com.android.internal.R.string.wallpaper_binding_label);
   1584             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
   1585                     mContext, 0,
   1586                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
   1587                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
   1588                     0, null, new UserHandle(serviceUserId)));
   1589             if (!mContext.bindServiceAsUser(intent, newConn,
   1590                     Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
   1591                             | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
   1592                     new UserHandle(serviceUserId))) {
   1593                 String msg = "Unable to bind service: "
   1594                         + componentName;
   1595                 if (fromUser) {
   1596                     throw new IllegalArgumentException(msg);
   1597                 }
   1598                 Slog.w(TAG, msg);
   1599                 return false;
   1600             }
   1601             if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
   1602                 detachWallpaperLocked(mLastWallpaper);
   1603             }
   1604             wallpaper.wallpaperComponent = componentName;
   1605             wallpaper.connection = newConn;
   1606             newConn.mReply = reply;
   1607             try {
   1608                 if (wallpaper.userId == mCurrentUserId) {
   1609                     if (DEBUG)
   1610                         Slog.v(TAG, "Adding window token: " + newConn.mToken);
   1611                     mIWindowManager.addWindowToken(newConn.mToken,
   1612                             WindowManager.LayoutParams.TYPE_WALLPAPER);
   1613                     mLastWallpaper = wallpaper;
   1614                 }
   1615             } catch (RemoteException e) {
   1616             }
   1617         } catch (RemoteException e) {
   1618             String msg = "Remote exception for " + componentName + "\n" + e;
   1619             if (fromUser) {
   1620                 throw new IllegalArgumentException(msg);
   1621             }
   1622             Slog.w(TAG, msg);
   1623             return false;
   1624         }
   1625         return true;
   1626     }
   1627 
   1628     void detachWallpaperLocked(WallpaperData wallpaper) {
   1629         if (wallpaper.connection != null) {
   1630             if (wallpaper.connection.mReply != null) {
   1631                 try {
   1632                     wallpaper.connection.mReply.sendResult(null);
   1633                 } catch (RemoteException e) {
   1634                 }
   1635                 wallpaper.connection.mReply = null;
   1636             }
   1637             if (wallpaper.connection.mEngine != null) {
   1638                 try {
   1639                     wallpaper.connection.mEngine.destroy();
   1640                 } catch (RemoteException e) {
   1641                 }
   1642             }
   1643             mContext.unbindService(wallpaper.connection);
   1644             try {
   1645                 if (DEBUG)
   1646                     Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
   1647                 mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
   1648             } catch (RemoteException e) {
   1649             }
   1650             wallpaper.connection.mService = null;
   1651             wallpaper.connection.mEngine = null;
   1652             wallpaper.connection = null;
   1653         }
   1654     }
   1655 
   1656     void clearWallpaperComponentLocked(WallpaperData wallpaper) {
   1657         wallpaper.wallpaperComponent = null;
   1658         detachWallpaperLocked(wallpaper);
   1659     }
   1660 
   1661     void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
   1662         try {
   1663             conn.mService.attach(conn, conn.mToken,
   1664                     WindowManager.LayoutParams.TYPE_WALLPAPER, false,
   1665                     wallpaper.width, wallpaper.height, wallpaper.padding);
   1666         } catch (RemoteException e) {
   1667             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
   1668             if (!wallpaper.wallpaperUpdating) {
   1669                 bindWallpaperComponentLocked(null, false, false, wallpaper, null);
   1670             }
   1671         }
   1672     }
   1673 
   1674     private void notifyCallbacksLocked(WallpaperData wallpaper) {
   1675         final int n = wallpaper.callbacks.beginBroadcast();
   1676         for (int i = 0; i < n; i++) {
   1677             try {
   1678                 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
   1679             } catch (RemoteException e) {
   1680 
   1681                 // The RemoteCallbackList will take care of removing
   1682                 // the dead object for us.
   1683             }
   1684         }
   1685         wallpaper.callbacks.finishBroadcast();
   1686         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
   1687         mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
   1688     }
   1689 
   1690     private void checkPermission(String permission) {
   1691         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
   1692             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
   1693                     + ", must have permission " + permission);
   1694         }
   1695     }
   1696 
   1697     /**
   1698      * Certain user types do not support wallpapers (e.g. managed profiles). The check is
   1699      * implemented through through the OP_WRITE_WALLPAPER AppOp.
   1700      */
   1701     public boolean isWallpaperSupported(String callingPackage) {
   1702         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
   1703                 callingPackage) == AppOpsManager.MODE_ALLOWED;
   1704     }
   1705 
   1706     @Override
   1707     public boolean isSetWallpaperAllowed(String callingPackage) {
   1708         final PackageManager pm = mContext.getPackageManager();
   1709         String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
   1710         boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
   1711         if (!uidMatchPackage) {
   1712             return false;   // callingPackage was faked.
   1713         }
   1714 
   1715         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
   1716         if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
   1717             return true;
   1718         }
   1719         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
   1720         return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
   1721     }
   1722 
   1723     @Override
   1724     public boolean isWallpaperBackupEligible(int userId) {
   1725         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
   1726             throw new SecurityException("Only the system may call isWallpaperBackupEligible");
   1727         }
   1728 
   1729         WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
   1730         return (wallpaper != null) ? wallpaper.allowBackup : false;
   1731     }
   1732 
   1733     private static JournaledFile makeJournaledFile(int userId) {
   1734         final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
   1735         return new JournaledFile(new File(base), new File(base + ".tmp"));
   1736     }
   1737 
   1738     private void saveSettingsLocked(int userId) {
   1739         JournaledFile journal = makeJournaledFile(userId);
   1740         FileOutputStream fstream = null;
   1741         BufferedOutputStream stream = null;
   1742         try {
   1743             XmlSerializer out = new FastXmlSerializer();
   1744             fstream = new FileOutputStream(journal.chooseForWrite(), false);
   1745             stream = new BufferedOutputStream(fstream);
   1746             out.setOutput(stream, StandardCharsets.UTF_8.name());
   1747             out.startDocument(null, true);
   1748 
   1749             WallpaperData wallpaper;
   1750 
   1751             wallpaper = mWallpaperMap.get(userId);
   1752             if (wallpaper != null) {
   1753                 writeWallpaperAttributes(out, "wp", wallpaper);
   1754             }
   1755             wallpaper = mLockWallpaperMap.get(userId);
   1756             if (wallpaper != null) {
   1757                 writeWallpaperAttributes(out, "kwp", wallpaper);
   1758             }
   1759 
   1760             out.endDocument();
   1761 
   1762             stream.flush(); // also flushes fstream
   1763             FileUtils.sync(fstream);
   1764             stream.close(); // also closes fstream
   1765             journal.commit();
   1766         } catch (IOException e) {
   1767             IoUtils.closeQuietly(stream);
   1768             journal.rollback();
   1769         }
   1770     }
   1771 
   1772     private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
   1773             throws IllegalArgumentException, IllegalStateException, IOException {
   1774         out.startTag(null, tag);
   1775         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
   1776         out.attribute(null, "width", Integer.toString(wallpaper.width));
   1777         out.attribute(null, "height", Integer.toString(wallpaper.height));
   1778 
   1779         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
   1780         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
   1781         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
   1782         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
   1783 
   1784         if (wallpaper.padding.left != 0) {
   1785             out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
   1786         }
   1787         if (wallpaper.padding.top != 0) {
   1788             out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
   1789         }
   1790         if (wallpaper.padding.right != 0) {
   1791             out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
   1792         }
   1793         if (wallpaper.padding.bottom != 0) {
   1794             out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
   1795         }
   1796 
   1797         out.attribute(null, "name", wallpaper.name);
   1798         if (wallpaper.wallpaperComponent != null
   1799                 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
   1800             out.attribute(null, "component",
   1801                     wallpaper.wallpaperComponent.flattenToShortString());
   1802         }
   1803 
   1804         if (wallpaper.allowBackup) {
   1805             out.attribute(null, "backup", "true");
   1806         }
   1807 
   1808         out.endTag(null, tag);
   1809     }
   1810 
   1811     private void migrateFromOld() {
   1812         File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
   1813         File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
   1814         if (oldWallpaper.exists()) {
   1815             File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
   1816             oldWallpaper.renameTo(newWallpaper);
   1817         }
   1818         if (oldInfo.exists()) {
   1819             File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
   1820             oldInfo.renameTo(newInfo);
   1821         }
   1822     }
   1823 
   1824     private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
   1825         String value = parser.getAttributeValue(null, name);
   1826         if (value == null) {
   1827             return defValue;
   1828         }
   1829         return Integer.parseInt(value);
   1830     }
   1831 
   1832     /**
   1833      * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
   1834      * happen during user switch.  The async user switch observer may not have received
   1835      * the event yet.  We use this safe method when we don't care about this ordering and just
   1836      * want to update the data.  The data is going to be applied when the user switch observer
   1837      * is eventually executed.
   1838      */
   1839     private WallpaperData getWallpaperSafeLocked(int userId, int which) {
   1840         // We're setting either just system (work with the system wallpaper),
   1841         // both (also work with the system wallpaper), or just the lock
   1842         // wallpaper (update against the existing lock wallpaper if any).
   1843         // Combined or just-system operations use the 'system' WallpaperData
   1844         // for this use; lock-only operations use the dedicated one.
   1845         final SparseArray<WallpaperData> whichSet =
   1846                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
   1847         WallpaperData wallpaper = whichSet.get(userId);
   1848         if (wallpaper == null) {
   1849             // common case, this is the first lookup post-boot of the system or
   1850             // unified lock, so we bring up the saved state lazily now and recheck.
   1851             loadSettingsLocked(userId, false);
   1852             wallpaper = whichSet.get(userId);
   1853             // if it's still null here, this is a lock-only operation and there is not
   1854             // yet a lock-only wallpaper set for this user, so we need to establish
   1855             // it now.
   1856             if (wallpaper == null) {
   1857                 if (which == FLAG_LOCK) {
   1858                     wallpaper = new WallpaperData(userId,
   1859                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
   1860                     mLockWallpaperMap.put(userId, wallpaper);
   1861                     ensureSaneWallpaperData(wallpaper);
   1862                 } else {
   1863                     // sanity fallback: we're in bad shape, but establishing a known
   1864                     // valid system+lock WallpaperData will keep us from dying.
   1865                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
   1866                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
   1867                     mWallpaperMap.put(userId, wallpaper);
   1868                     ensureSaneWallpaperData(wallpaper);
   1869                 }
   1870             }
   1871         }
   1872         return wallpaper;
   1873     }
   1874 
   1875     private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
   1876         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
   1877 
   1878         JournaledFile journal = makeJournaledFile(userId);
   1879         FileInputStream stream = null;
   1880         File file = journal.chooseForRead();
   1881         if (!file.exists()) {
   1882             // This should only happen one time, when upgrading from a legacy system
   1883             migrateFromOld();
   1884         }
   1885         WallpaperData wallpaper = mWallpaperMap.get(userId);
   1886         if (wallpaper == null) {
   1887             wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
   1888             wallpaper.allowBackup = true;
   1889             mWallpaperMap.put(userId, wallpaper);
   1890             if (!wallpaper.cropExists()) {
   1891                 generateCrop(wallpaper);
   1892             }
   1893         }
   1894         boolean success = false;
   1895         try {
   1896             stream = new FileInputStream(file);
   1897             XmlPullParser parser = Xml.newPullParser();
   1898             parser.setInput(stream, StandardCharsets.UTF_8.name());
   1899 
   1900             int type;
   1901             do {
   1902                 type = parser.next();
   1903                 if (type == XmlPullParser.START_TAG) {
   1904                     String tag = parser.getName();
   1905                     if ("wp".equals(tag)) {
   1906                         // Common to system + lock wallpapers
   1907                         parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
   1908 
   1909                         // A system wallpaper might also be a live wallpaper
   1910                         String comp = parser.getAttributeValue(null, "component");
   1911                         wallpaper.nextWallpaperComponent = comp != null
   1912                                 ? ComponentName.unflattenFromString(comp)
   1913                                 : null;
   1914                         if (wallpaper.nextWallpaperComponent == null
   1915                                 || "android".equals(wallpaper.nextWallpaperComponent
   1916                                         .getPackageName())) {
   1917                             wallpaper.nextWallpaperComponent = mImageWallpaper;
   1918                         }
   1919 
   1920                         if (DEBUG) {
   1921                             Slog.v(TAG, "mWidth:" + wallpaper.width);
   1922                             Slog.v(TAG, "mHeight:" + wallpaper.height);
   1923                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
   1924                             Slog.v(TAG, "mName:" + wallpaper.name);
   1925                             Slog.v(TAG, "mNextWallpaperComponent:"
   1926                                     + wallpaper.nextWallpaperComponent);
   1927                         }
   1928                     } else if ("kwp".equals(tag)) {
   1929                         // keyguard-specific wallpaper for this user
   1930                         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
   1931                         if (lockWallpaper == null) {
   1932                             lockWallpaper = new WallpaperData(userId,
   1933                                     WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
   1934                             mLockWallpaperMap.put(userId, lockWallpaper);
   1935                         }
   1936                         parseWallpaperAttributes(parser, lockWallpaper, false);
   1937                     }
   1938                 }
   1939             } while (type != XmlPullParser.END_DOCUMENT);
   1940             success = true;
   1941         } catch (FileNotFoundException e) {
   1942             Slog.w(TAG, "no current wallpaper -- first boot?");
   1943         } catch (NullPointerException e) {
   1944             Slog.w(TAG, "failed parsing " + file + " " + e);
   1945         } catch (NumberFormatException e) {
   1946             Slog.w(TAG, "failed parsing " + file + " " + e);
   1947         } catch (XmlPullParserException e) {
   1948             Slog.w(TAG, "failed parsing " + file + " " + e);
   1949         } catch (IOException e) {
   1950             Slog.w(TAG, "failed parsing " + file + " " + e);
   1951         } catch (IndexOutOfBoundsException e) {
   1952             Slog.w(TAG, "failed parsing " + file + " " + e);
   1953         }
   1954         IoUtils.closeQuietly(stream);
   1955 
   1956         if (!success) {
   1957             wallpaper.width = -1;
   1958             wallpaper.height = -1;
   1959             wallpaper.cropHint.set(0, 0, 0, 0);
   1960             wallpaper.padding.set(0, 0, 0, 0);
   1961             wallpaper.name = "";
   1962 
   1963             mLockWallpaperMap.remove(userId);
   1964         } else {
   1965             if (wallpaper.wallpaperId <= 0) {
   1966                 wallpaper.wallpaperId = makeWallpaperIdLocked();
   1967                 if (DEBUG) {
   1968                     Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
   1969                             + "); now " + wallpaper.wallpaperId);
   1970                 }
   1971             }
   1972         }
   1973 
   1974         ensureSaneWallpaperData(wallpaper);
   1975         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
   1976         if (lockWallpaper != null) {
   1977             ensureSaneWallpaperData(lockWallpaper);
   1978         }
   1979     }
   1980 
   1981     private void ensureSaneWallpaperData(WallpaperData wallpaper) {
   1982         // We always want to have some reasonable width hint.
   1983         int baseSize = getMaximumSizeDimension();
   1984         if (wallpaper.width < baseSize) {
   1985             wallpaper.width = baseSize;
   1986         }
   1987         if (wallpaper.height < baseSize) {
   1988             wallpaper.height = baseSize;
   1989         }
   1990         // and crop, if not previously specified
   1991         if (wallpaper.cropHint.width() <= 0
   1992                 || wallpaper.cropHint.height() <= 0) {
   1993             wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
   1994         }
   1995     }
   1996 
   1997     private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
   1998             boolean keepDimensionHints) {
   1999         final String idString = parser.getAttributeValue(null, "id");
   2000         if (idString != null) {
   2001             final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
   2002             if (id > mWallpaperId) {
   2003                 mWallpaperId = id;
   2004             }
   2005         } else {
   2006             wallpaper.wallpaperId = makeWallpaperIdLocked();
   2007         }
   2008 
   2009         if (!keepDimensionHints) {
   2010             wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
   2011             wallpaper.height = Integer.parseInt(parser
   2012                     .getAttributeValue(null, "height"));
   2013         }
   2014         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
   2015         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
   2016         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
   2017         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
   2018         wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
   2019         wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
   2020         wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
   2021         wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
   2022         wallpaper.name = parser.getAttributeValue(null, "name");
   2023         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
   2024     }
   2025 
   2026     private int getMaximumSizeDimension() {
   2027         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
   2028         Display d = wm.getDefaultDisplay();
   2029         return d.getMaximumSizeDimension();
   2030     }
   2031 
   2032     // Called by SystemBackupAgent after files are restored to disk.
   2033     public void settingsRestored() {
   2034         // Verify caller is the system
   2035         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
   2036             throw new RuntimeException("settingsRestored() can only be called from the system process");
   2037         }
   2038         // TODO: If necessary, make it work for secondary users as well. This currently assumes
   2039         // restores only to the primary user
   2040         if (DEBUG) Slog.v(TAG, "settingsRestored");
   2041         WallpaperData wallpaper = null;
   2042         boolean success = false;
   2043         synchronized (mLock) {
   2044             loadSettingsLocked(UserHandle.USER_SYSTEM, false);
   2045             wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
   2046             wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
   2047             wallpaper.allowBackup = true;   // by definition if it was restored
   2048             if (wallpaper.nextWallpaperComponent != null
   2049                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
   2050                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
   2051                         wallpaper, null)) {
   2052                     // No such live wallpaper or other failure; fall back to the default
   2053                     // live wallpaper (since the profile being restored indicated that the
   2054                     // user had selected a live rather than static one).
   2055                     bindWallpaperComponentLocked(null, false, false, wallpaper, null);
   2056                 }
   2057                 success = true;
   2058             } else {
   2059                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
   2060                 // use the default.
   2061                 if ("".equals(wallpaper.name)) {
   2062                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
   2063                     success = true;
   2064                 } else {
   2065                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
   2066                     success = restoreNamedResourceLocked(wallpaper);
   2067                 }
   2068                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
   2069                         + " id=" + wallpaper.wallpaperId);
   2070                 if (success) {
   2071                     generateCrop(wallpaper);    // based on the new image + metadata
   2072                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
   2073                             wallpaper, null);
   2074                 }
   2075             }
   2076         }
   2077 
   2078         if (!success) {
   2079             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
   2080             wallpaper.name = "";
   2081             getWallpaperDir(UserHandle.USER_SYSTEM).delete();
   2082         }
   2083 
   2084         synchronized (mLock) {
   2085             saveSettingsLocked(UserHandle.USER_SYSTEM);
   2086         }
   2087     }
   2088 
   2089     // Restore the named resource bitmap to both source + crop files
   2090     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
   2091         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
   2092             String resName = wallpaper.name.substring(4);
   2093 
   2094             String pkg = null;
   2095             int colon = resName.indexOf(':');
   2096             if (colon > 0) {
   2097                 pkg = resName.substring(0, colon);
   2098             }
   2099 
   2100             String ident = null;
   2101             int slash = resName.lastIndexOf('/');
   2102             if (slash > 0) {
   2103                 ident = resName.substring(slash+1);
   2104             }
   2105 
   2106             String type = null;
   2107             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
   2108                 type = resName.substring(colon+1, slash);
   2109             }
   2110 
   2111             if (pkg != null && ident != null && type != null) {
   2112                 int resId = -1;
   2113                 InputStream res = null;
   2114                 FileOutputStream fos = null;
   2115                 FileOutputStream cos = null;
   2116                 try {
   2117                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
   2118                     Resources r = c.getResources();
   2119                     resId = r.getIdentifier(resName, null, null);
   2120                     if (resId == 0) {
   2121                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
   2122                                 + " ident=" + ident);
   2123                         return false;
   2124                     }
   2125 
   2126                     res = r.openRawResource(resId);
   2127                     if (wallpaper.wallpaperFile.exists()) {
   2128                         wallpaper.wallpaperFile.delete();
   2129                         wallpaper.cropFile.delete();
   2130                     }
   2131                     fos = new FileOutputStream(wallpaper.wallpaperFile);
   2132                     cos = new FileOutputStream(wallpaper.cropFile);
   2133 
   2134                     byte[] buffer = new byte[32768];
   2135                     int amt;
   2136                     while ((amt=res.read(buffer)) > 0) {
   2137                         fos.write(buffer, 0, amt);
   2138                         cos.write(buffer, 0, amt);
   2139                     }
   2140                     // mWallpaperObserver will notice the close and send the change broadcast
   2141 
   2142                     Slog.v(TAG, "Restored wallpaper: " + resName);
   2143                     return true;
   2144                 } catch (NameNotFoundException e) {
   2145                     Slog.e(TAG, "Package name " + pkg + " not found");
   2146                 } catch (Resources.NotFoundException e) {
   2147                     Slog.e(TAG, "Resource not found: " + resId);
   2148                 } catch (IOException e) {
   2149                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
   2150                 } finally {
   2151                     IoUtils.closeQuietly(res);
   2152                     if (fos != null) {
   2153                         FileUtils.sync(fos);
   2154                     }
   2155                     if (cos != null) {
   2156                         FileUtils.sync(cos);
   2157                     }
   2158                     IoUtils.closeQuietly(fos);
   2159                     IoUtils.closeQuietly(cos);
   2160                 }
   2161             }
   2162         }
   2163         return false;
   2164     }
   2165 
   2166     @Override
   2167     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   2168         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   2169                 != PackageManager.PERMISSION_GRANTED) {
   2170 
   2171             pw.println("Permission Denial: can't dump wallpaper service from from pid="
   2172                     + Binder.getCallingPid()
   2173                     + ", uid=" + Binder.getCallingUid());
   2174             return;
   2175         }
   2176 
   2177         synchronized (mLock) {
   2178             pw.println("System wallpaper state:");
   2179             for (int i = 0; i < mWallpaperMap.size(); i++) {
   2180                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
   2181                 pw.print(" User "); pw.print(wallpaper.userId);
   2182                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
   2183                 pw.print("  mWidth=");
   2184                     pw.print(wallpaper.width);
   2185                     pw.print(" mHeight=");
   2186                     pw.println(wallpaper.height);
   2187                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
   2188                 pw.print("  mPadding="); pw.println(wallpaper.padding);
   2189                 pw.print("  mName=");  pw.println(wallpaper.name);
   2190                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
   2191                 if (wallpaper.connection != null) {
   2192                     WallpaperConnection conn = wallpaper.connection;
   2193                     pw.print("  Wallpaper connection ");
   2194                     pw.print(conn);
   2195                     pw.println(":");
   2196                     if (conn.mInfo != null) {
   2197                         pw.print("    mInfo.component=");
   2198                         pw.println(conn.mInfo.getComponent());
   2199                     }
   2200                     pw.print("    mToken=");
   2201                     pw.println(conn.mToken);
   2202                     pw.print("    mService=");
   2203                     pw.println(conn.mService);
   2204                     pw.print("    mEngine=");
   2205                     pw.println(conn.mEngine);
   2206                     pw.print("    mLastDiedTime=");
   2207                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
   2208                 }
   2209             }
   2210             pw.println("Lock wallpaper state:");
   2211             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
   2212                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
   2213                 pw.print(" User "); pw.print(wallpaper.userId);
   2214                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
   2215                 pw.print("  mWidth="); pw.print(wallpaper.width);
   2216                     pw.print(" mHeight="); pw.println(wallpaper.height);
   2217                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
   2218                 pw.print("  mPadding="); pw.println(wallpaper.padding);
   2219                 pw.print("  mName=");  pw.println(wallpaper.name);
   2220             }
   2221 
   2222         }
   2223     }
   2224 }
   2225