Home | History | Annotate | Download | only in server
      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;
     18 
     19 import static android.os.FileObserver.*;
     20 import static android.os.ParcelFileDescriptor.*;
     21 
     22 import android.app.IWallpaperManager;
     23 import android.app.IWallpaperManagerCallback;
     24 import android.app.PendingIntent;
     25 import android.app.WallpaperInfo;
     26 import android.app.backup.BackupManager;
     27 import android.app.backup.WallpaperBackupHelper;
     28 import android.content.BroadcastReceiver;
     29 import android.content.ComponentName;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.IntentFilter;
     33 import android.content.ServiceConnection;
     34 import android.content.pm.PackageManager;
     35 import android.content.pm.ResolveInfo;
     36 import android.content.pm.ServiceInfo;
     37 import android.content.pm.PackageManager.NameNotFoundException;
     38 import android.content.res.Resources;
     39 import android.os.Binder;
     40 import android.os.Bundle;
     41 import android.os.Environment;
     42 import android.os.FileUtils;
     43 import android.os.IBinder;
     44 import android.os.RemoteException;
     45 import android.os.FileObserver;
     46 import android.os.ParcelFileDescriptor;
     47 import android.os.RemoteCallbackList;
     48 import android.os.ServiceManager;
     49 import android.os.SystemClock;
     50 import android.os.UserId;
     51 import android.service.wallpaper.IWallpaperConnection;
     52 import android.service.wallpaper.IWallpaperEngine;
     53 import android.service.wallpaper.IWallpaperService;
     54 import android.service.wallpaper.WallpaperService;
     55 import android.util.Slog;
     56 import android.util.SparseArray;
     57 import android.util.Xml;
     58 import android.view.Display;
     59 import android.view.IWindowManager;
     60 import android.view.WindowManager;
     61 
     62 import java.io.FileDescriptor;
     63 import java.io.IOException;
     64 import java.io.InputStream;
     65 import java.io.File;
     66 import java.io.FileNotFoundException;
     67 import java.io.FileInputStream;
     68 import java.io.FileOutputStream;
     69 import java.io.PrintWriter;
     70 import java.util.List;
     71 
     72 import org.xmlpull.v1.XmlPullParser;
     73 import org.xmlpull.v1.XmlPullParserException;
     74 import org.xmlpull.v1.XmlSerializer;
     75 
     76 import com.android.internal.content.PackageMonitor;
     77 import com.android.internal.util.FastXmlSerializer;
     78 import com.android.internal.util.JournaledFile;
     79 import com.android.server.am.ActivityManagerService;
     80 
     81 class WallpaperManagerService extends IWallpaperManager.Stub {
     82     static final String TAG = "WallpaperService";
     83     static final boolean DEBUG = false;
     84 
     85     final Object mLock = new Object[0];
     86 
     87     /**
     88      * Minimum time between crashes of a wallpaper service for us to consider
     89      * restarting it vs. just reverting to the static wallpaper.
     90      */
     91     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
     92 
     93     static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
     94     static final String WALLPAPER = "wallpaper";
     95     static final String WALLPAPER_INFO = "wallpaper_info.xml";
     96 
     97     /**
     98      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
     99      * that the wallpaper has changed. The CREATE is triggered when there is no
    100      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
    101      * everytime the wallpaper is changed.
    102      */
    103     private class WallpaperObserver extends FileObserver {
    104 
    105         final WallpaperData mWallpaper;
    106         final File mWallpaperDir;
    107         final File mWallpaperFile;
    108 
    109         public WallpaperObserver(WallpaperData wallpaper) {
    110             super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
    111                     CLOSE_WRITE | DELETE | DELETE_SELF);
    112             mWallpaperDir = getWallpaperDir(wallpaper.userId);
    113             mWallpaper = wallpaper;
    114             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
    115         }
    116 
    117         @Override
    118         public void onEvent(int event, String path) {
    119             if (path == null) {
    120                 return;
    121             }
    122             synchronized (mLock) {
    123                 // changing the wallpaper means we'll need to back up the new one
    124                 long origId = Binder.clearCallingIdentity();
    125                 BackupManager bm = new BackupManager(mContext);
    126                 bm.dataChanged();
    127                 Binder.restoreCallingIdentity(origId);
    128 
    129                 File changedFile = new File(mWallpaperDir, path);
    130                 if (mWallpaperFile.equals(changedFile)) {
    131                     notifyCallbacksLocked(mWallpaper);
    132                     if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE
    133                             || mWallpaper.imageWallpaperPending) {
    134                         if (event == CLOSE_WRITE) {
    135                             mWallpaper.imageWallpaperPending = false;
    136                         }
    137                         bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
    138                                 false, mWallpaper);
    139                         saveSettingsLocked(mWallpaper);
    140                     }
    141                 }
    142             }
    143         }
    144     }
    145 
    146     final Context mContext;
    147     final IWindowManager mIWindowManager;
    148     final MyPackageMonitor mMonitor;
    149     WallpaperData mLastWallpaper;
    150 
    151     SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
    152 
    153     int mCurrentUserId;
    154 
    155     static class WallpaperData {
    156 
    157         int userId;
    158 
    159         File wallpaperFile;
    160 
    161         /**
    162          * Client is currently writing a new image wallpaper.
    163          */
    164         boolean imageWallpaperPending;
    165 
    166         /**
    167          * Resource name if using a picture from the wallpaper gallery
    168          */
    169         String name = "";
    170 
    171         /**
    172          * The component name of the currently set live wallpaper.
    173          */
    174         ComponentName wallpaperComponent;
    175 
    176         /**
    177          * The component name of the wallpaper that should be set next.
    178          */
    179         ComponentName nextWallpaperComponent;
    180 
    181         /**
    182          * Name of the component used to display bitmap wallpapers from either the gallery or
    183          * built-in wallpapers.
    184          */
    185         ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui",
    186                 "com.android.systemui.ImageWallpaper");
    187 
    188         WallpaperConnection connection;
    189         long lastDiedTime;
    190         boolean wallpaperUpdating;
    191         WallpaperObserver wallpaperObserver;
    192 
    193         /**
    194          * List of callbacks registered they should each be notified when the wallpaper is changed.
    195          */
    196         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
    197                 = new RemoteCallbackList<IWallpaperManagerCallback>();
    198 
    199         int width = -1;
    200         int height = -1;
    201 
    202         WallpaperData(int userId) {
    203             this.userId = userId;
    204             wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
    205         }
    206     }
    207 
    208     class WallpaperConnection extends IWallpaperConnection.Stub
    209             implements ServiceConnection {
    210         final WallpaperInfo mInfo;
    211         final Binder mToken = new Binder();
    212         IWallpaperService mService;
    213         IWallpaperEngine mEngine;
    214         WallpaperData mWallpaper;
    215 
    216         public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
    217             mInfo = info;
    218             mWallpaper = wallpaper;
    219         }
    220 
    221         public void onServiceConnected(ComponentName name, IBinder service) {
    222             synchronized (mLock) {
    223                 if (mWallpaper.connection == this) {
    224                     mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
    225                     mService = IWallpaperService.Stub.asInterface(service);
    226                     attachServiceLocked(this, mWallpaper);
    227                     // XXX should probably do saveSettingsLocked() later
    228                     // when we have an engine, but I'm not sure about
    229                     // locking there and anyway we always need to be able to
    230                     // recover if there is something wrong.
    231                     saveSettingsLocked(mWallpaper);
    232                 }
    233             }
    234         }
    235 
    236         public void onServiceDisconnected(ComponentName name) {
    237             synchronized (mLock) {
    238                 mService = null;
    239                 mEngine = null;
    240                 if (mWallpaper.connection == this) {
    241                     Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
    242                     if (!mWallpaper.wallpaperUpdating
    243                             && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME)
    244                                 > SystemClock.uptimeMillis()
    245                             && mWallpaper.userId == mCurrentUserId) {
    246                         Slog.w(TAG, "Reverting to built-in wallpaper!");
    247                         clearWallpaperLocked(true, mWallpaper.userId);
    248                     }
    249                 }
    250             }
    251         }
    252 
    253         public void attachEngine(IWallpaperEngine engine) {
    254             mEngine = engine;
    255         }
    256 
    257         public ParcelFileDescriptor setWallpaper(String name) {
    258             synchronized (mLock) {
    259                 if (mWallpaper.connection == this) {
    260                     return updateWallpaperBitmapLocked(name, mWallpaper);
    261                 }
    262                 return null;
    263             }
    264         }
    265     }
    266 
    267     class MyPackageMonitor extends PackageMonitor {
    268         @Override
    269         public void onPackageUpdateFinished(String packageName, int uid) {
    270             synchronized (mLock) {
    271                 for (int i = 0; i < mWallpaperMap.size(); i++) {
    272                     WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    273                     if (wallpaper.wallpaperComponent != null
    274                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    275                         wallpaper.wallpaperUpdating = false;
    276                         ComponentName comp = wallpaper.wallpaperComponent;
    277                         clearWallpaperComponentLocked(wallpaper);
    278                         // Do this only for the current user's wallpaper
    279                         if (wallpaper.userId == mCurrentUserId
    280                                 && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) {
    281                             Slog.w(TAG, "Wallpaper no longer available; reverting to default");
    282                             clearWallpaperLocked(false, wallpaper.userId);
    283                         }
    284                     }
    285                 }
    286             }
    287         }
    288 
    289         @Override
    290         public void onPackageModified(String packageName) {
    291             synchronized (mLock) {
    292                 for (int i = 0; i < mWallpaperMap.size(); i++) {
    293                     WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    294                     if (wallpaper.wallpaperComponent == null
    295                             || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    296                         continue;
    297                     }
    298                     doPackagesChangedLocked(true, wallpaper);
    299                 }
    300             }
    301         }
    302 
    303         @Override
    304         public void onPackageUpdateStarted(String packageName, int uid) {
    305             synchronized (mLock) {
    306                 for (int i = 0; i < mWallpaperMap.size(); i++) {
    307                     WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    308                     if (wallpaper.wallpaperComponent != null
    309                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
    310                         wallpaper.wallpaperUpdating = true;
    311                     }
    312                 }
    313             }
    314         }
    315 
    316         @Override
    317         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
    318             synchronized (mLock) {
    319                 boolean changed = false;
    320                 for (int i = 0; i < mWallpaperMap.size(); i++) {
    321                     WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    322                     boolean res = doPackagesChangedLocked(doit, wallpaper);
    323                     changed |= res;
    324                 }
    325                 return changed;
    326             }
    327         }
    328 
    329         @Override
    330         public void onSomePackagesChanged() {
    331             synchronized (mLock) {
    332                 for (int i = 0; i < mWallpaperMap.size(); i++) {
    333                     WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    334                     doPackagesChangedLocked(true, wallpaper);
    335                 }
    336             }
    337         }
    338 
    339         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
    340             boolean changed = false;
    341             if (wallpaper.wallpaperComponent != null) {
    342                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
    343                         .getPackageName());
    344                 if (change == PACKAGE_PERMANENT_CHANGE
    345                         || change == PACKAGE_TEMPORARY_CHANGE) {
    346                     changed = true;
    347                     if (doit) {
    348                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
    349                                 + wallpaper.wallpaperComponent);
    350                         clearWallpaperLocked(false, wallpaper.userId);
    351                     }
    352                 }
    353             }
    354             if (wallpaper.nextWallpaperComponent != null) {
    355                 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
    356                         .getPackageName());
    357                 if (change == PACKAGE_PERMANENT_CHANGE
    358                         || change == PACKAGE_TEMPORARY_CHANGE) {
    359                     wallpaper.nextWallpaperComponent = null;
    360                 }
    361             }
    362             if (wallpaper.wallpaperComponent != null
    363                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
    364                 try {
    365                     mContext.getPackageManager().getServiceInfo(
    366                             wallpaper.wallpaperComponent, 0);
    367                 } catch (NameNotFoundException e) {
    368                     Slog.w(TAG, "Wallpaper component gone, removing: "
    369                             + wallpaper.wallpaperComponent);
    370                     clearWallpaperLocked(false, wallpaper.userId);
    371                 }
    372             }
    373             if (wallpaper.nextWallpaperComponent != null
    374                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
    375                 try {
    376                     mContext.getPackageManager().getServiceInfo(
    377                             wallpaper.nextWallpaperComponent, 0);
    378                 } catch (NameNotFoundException e) {
    379                     wallpaper.nextWallpaperComponent = null;
    380                 }
    381             }
    382             return changed;
    383         }
    384     }
    385 
    386     public WallpaperManagerService(Context context) {
    387         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
    388         mContext = context;
    389         mIWindowManager = IWindowManager.Stub.asInterface(
    390                 ServiceManager.getService(Context.WINDOW_SERVICE));
    391         mMonitor = new MyPackageMonitor();
    392         mMonitor.register(context, null, true);
    393         WALLPAPER_BASE_DIR.mkdirs();
    394         loadSettingsLocked(0);
    395     }
    396 
    397     private static File getWallpaperDir(int userId) {
    398         return new File(WALLPAPER_BASE_DIR + "/" + userId);
    399     }
    400 
    401     @Override
    402     protected void finalize() throws Throwable {
    403         super.finalize();
    404         for (int i = 0; i < mWallpaperMap.size(); i++) {
    405             WallpaperData wallpaper = mWallpaperMap.valueAt(i);
    406             wallpaper.wallpaperObserver.stopWatching();
    407         }
    408     }
    409 
    410     public void systemReady() {
    411         if (DEBUG) Slog.v(TAG, "systemReady");
    412         WallpaperData wallpaper = mWallpaperMap.get(0);
    413         switchWallpaper(wallpaper);
    414         wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
    415         wallpaper.wallpaperObserver.startWatching();
    416 
    417         IntentFilter userFilter = new IntentFilter();
    418         userFilter.addAction(Intent.ACTION_USER_SWITCHED);
    419         userFilter.addAction(Intent.ACTION_USER_REMOVED);
    420         mContext.registerReceiver(new BroadcastReceiver() {
    421             @Override
    422             public void onReceive(Context context, Intent intent) {
    423                 String action = intent.getAction();
    424                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
    425                     switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
    426                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
    427                     removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
    428                 }
    429             }
    430         }, userFilter);
    431     }
    432 
    433     String getName() {
    434         return mWallpaperMap.get(0).name;
    435     }
    436 
    437     void removeUser(int userId) {
    438         synchronized (mLock) {
    439             WallpaperData wallpaper = mWallpaperMap.get(userId);
    440             if (wallpaper != null) {
    441                 wallpaper.wallpaperObserver.stopWatching();
    442                 mWallpaperMap.remove(userId);
    443             }
    444             File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
    445             wallpaperFile.delete();
    446             File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
    447             wallpaperInfoFile.delete();
    448         }
    449     }
    450 
    451     void switchUser(int userId) {
    452         synchronized (mLock) {
    453             mCurrentUserId = userId;
    454             WallpaperData wallpaper = mWallpaperMap.get(userId);
    455             if (wallpaper == null) {
    456                 wallpaper = new WallpaperData(userId);
    457                 mWallpaperMap.put(userId, wallpaper);
    458                 loadSettingsLocked(userId);
    459                 wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
    460                 wallpaper.wallpaperObserver.startWatching();
    461             }
    462             switchWallpaper(wallpaper);
    463         }
    464     }
    465 
    466     void switchWallpaper(WallpaperData wallpaper) {
    467         synchronized (mLock) {
    468             RuntimeException e = null;
    469             try {
    470                 ComponentName cname = wallpaper.wallpaperComponent != null ?
    471                         wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
    472                 if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) {
    473                     return;
    474                 }
    475             } catch (RuntimeException e1) {
    476                 e = e1;
    477             }
    478             Slog.w(TAG, "Failure starting previous wallpaper", e);
    479             clearWallpaperLocked(false, wallpaper.userId);
    480         }
    481     }
    482 
    483     public void clearWallpaper() {
    484         if (DEBUG) Slog.v(TAG, "clearWallpaper");
    485         synchronized (mLock) {
    486             clearWallpaperLocked(false, UserId.getCallingUserId());
    487         }
    488     }
    489 
    490     void clearWallpaperLocked(boolean defaultFailed, int userId) {
    491         WallpaperData wallpaper = mWallpaperMap.get(userId);
    492         File f = new File(getWallpaperDir(userId), WALLPAPER);
    493         if (f.exists()) {
    494             f.delete();
    495         }
    496         final long ident = Binder.clearCallingIdentity();
    497         RuntimeException e = null;
    498         try {
    499             wallpaper.imageWallpaperPending = false;
    500             if (userId != mCurrentUserId) return;
    501             if (bindWallpaperComponentLocked(defaultFailed
    502                     ? wallpaper.imageWallpaperComponent
    503                     : null, true, false, wallpaper)) {
    504                 return;
    505             }
    506         } catch (IllegalArgumentException e1) {
    507             e = e1;
    508         } finally {
    509             Binder.restoreCallingIdentity(ident);
    510         }
    511 
    512         // This can happen if the default wallpaper component doesn't
    513         // exist.  This should be a system configuration problem, but
    514         // let's not let it crash the system and just live with no
    515         // wallpaper.
    516         Slog.e(TAG, "Default wallpaper component not found!", e);
    517         clearWallpaperComponentLocked(wallpaper);
    518     }
    519 
    520     public void setDimensionHints(int width, int height) throws RemoteException {
    521         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
    522 
    523         int userId = UserId.getCallingUserId();
    524         WallpaperData wallpaper = mWallpaperMap.get(userId);
    525         if (wallpaper == null) {
    526             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
    527         }
    528         if (width <= 0 || height <= 0) {
    529             throw new IllegalArgumentException("width and height must be > 0");
    530         }
    531 
    532         synchronized (mLock) {
    533             if (width != wallpaper.width || height != wallpaper.height) {
    534                 wallpaper.width = width;
    535                 wallpaper.height = height;
    536                 saveSettingsLocked(wallpaper);
    537                 if (mCurrentUserId != userId) return; // Don't change the properties now
    538                 if (wallpaper.connection != null) {
    539                     if (wallpaper.connection.mEngine != null) {
    540                         try {
    541                             wallpaper.connection.mEngine.setDesiredSize(
    542                                     width, height);
    543                         } catch (RemoteException e) {
    544                         }
    545                         notifyCallbacksLocked(wallpaper);
    546                     }
    547                 }
    548             }
    549         }
    550     }
    551 
    552     public int getWidthHint() throws RemoteException {
    553         synchronized (mLock) {
    554             WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
    555             return wallpaper.width;
    556         }
    557     }
    558 
    559     public int getHeightHint() throws RemoteException {
    560         synchronized (mLock) {
    561             WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
    562             return wallpaper.height;
    563         }
    564     }
    565 
    566     public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
    567             Bundle outParams) {
    568         synchronized (mLock) {
    569             // This returns the current user's wallpaper, if called by a system service. Else it
    570             // returns the wallpaper for the calling user.
    571             int callingUid = Binder.getCallingUid();
    572             int wallpaperUserId = 0;
    573             if (callingUid == android.os.Process.SYSTEM_UID) {
    574                 wallpaperUserId = mCurrentUserId;
    575             } else {
    576                 wallpaperUserId = UserId.getUserId(callingUid);
    577             }
    578             WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
    579             try {
    580                 if (outParams != null) {
    581                     outParams.putInt("width", wallpaper.width);
    582                     outParams.putInt("height", wallpaper.height);
    583                 }
    584                 wallpaper.callbacks.register(cb);
    585                 File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
    586                 if (!f.exists()) {
    587                     return null;
    588                 }
    589                 return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
    590             } catch (FileNotFoundException e) {
    591                 /* Shouldn't happen as we check to see if the file exists */
    592                 Slog.w(TAG, "Error getting wallpaper", e);
    593             }
    594             return null;
    595         }
    596     }
    597 
    598     public WallpaperInfo getWallpaperInfo() {
    599         int userId = UserId.getCallingUserId();
    600         synchronized (mLock) {
    601             WallpaperData wallpaper = mWallpaperMap.get(userId);
    602             if (wallpaper.connection != null) {
    603                 return wallpaper.connection.mInfo;
    604             }
    605             return null;
    606         }
    607     }
    608 
    609     public ParcelFileDescriptor setWallpaper(String name) {
    610         if (DEBUG) Slog.v(TAG, "setWallpaper");
    611         int userId = UserId.getCallingUserId();
    612         WallpaperData wallpaper = mWallpaperMap.get(userId);
    613         if (wallpaper == null) {
    614             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
    615         }
    616         checkPermission(android.Manifest.permission.SET_WALLPAPER);
    617         synchronized (mLock) {
    618             final long ident = Binder.clearCallingIdentity();
    619             try {
    620                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
    621                 if (pfd != null) {
    622                     wallpaper.imageWallpaperPending = true;
    623                 }
    624                 return pfd;
    625             } finally {
    626                 Binder.restoreCallingIdentity(ident);
    627             }
    628         }
    629     }
    630 
    631     ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
    632         if (name == null) name = "";
    633         try {
    634             File dir = getWallpaperDir(wallpaper.userId);
    635             if (!dir.exists()) {
    636                 dir.mkdir();
    637                 FileUtils.setPermissions(
    638                         dir.getPath(),
    639                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
    640                         -1, -1);
    641             }
    642             ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER),
    643                     MODE_CREATE|MODE_READ_WRITE);
    644             wallpaper.name = name;
    645             return fd;
    646         } catch (FileNotFoundException e) {
    647             Slog.w(TAG, "Error setting wallpaper", e);
    648         }
    649         return null;
    650     }
    651 
    652     public void setWallpaperComponent(ComponentName name) {
    653         if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
    654         int userId = UserId.getCallingUserId();
    655         WallpaperData wallpaper = mWallpaperMap.get(userId);
    656         if (wallpaper == null) {
    657             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
    658         }
    659         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
    660         synchronized (mLock) {
    661             final long ident = Binder.clearCallingIdentity();
    662             try {
    663                 wallpaper.imageWallpaperPending = false;
    664                 bindWallpaperComponentLocked(name, false, true, wallpaper);
    665             } finally {
    666                 Binder.restoreCallingIdentity(ident);
    667             }
    668         }
    669     }
    670 
    671     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
    672             boolean fromUser, WallpaperData wallpaper) {
    673         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
    674         // Has the component changed?
    675         if (!force) {
    676             if (wallpaper.connection != null) {
    677                 if (wallpaper.wallpaperComponent == null) {
    678                     if (componentName == null) {
    679                         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
    680                         // Still using default wallpaper.
    681                         return true;
    682                     }
    683                 } else if (wallpaper.wallpaperComponent.equals(componentName)) {
    684                     // Changing to same wallpaper.
    685                     if (DEBUG) Slog.v(TAG, "same wallpaper");
    686                     return true;
    687                 }
    688             }
    689         }
    690 
    691         try {
    692             if (componentName == null) {
    693                 String defaultComponent =
    694                     mContext.getString(com.android.internal.R.string.default_wallpaper_component);
    695                 if (defaultComponent != null) {
    696                     // See if there is a default wallpaper component specified
    697                     componentName = ComponentName.unflattenFromString(defaultComponent);
    698                     if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName);
    699                 }
    700                 if (componentName == null) {
    701                     // Fall back to static image wallpaper
    702                     componentName = wallpaper.imageWallpaperComponent;
    703                     //clearWallpaperComponentLocked();
    704                     //return;
    705                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
    706                 }
    707             }
    708             ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
    709                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
    710             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
    711                 String msg = "Selected service does not require "
    712                         + android.Manifest.permission.BIND_WALLPAPER
    713                         + ": " + componentName;
    714                 if (fromUser) {
    715                     throw new SecurityException(msg);
    716                 }
    717                 Slog.w(TAG, msg);
    718                 return false;
    719             }
    720 
    721             WallpaperInfo wi = null;
    722 
    723             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
    724             if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
    725                 // Make sure the selected service is actually a wallpaper service.
    726                 List<ResolveInfo> ris = mContext.getPackageManager()
    727                         .queryIntentServices(intent, PackageManager.GET_META_DATA);
    728                 for (int i=0; i<ris.size(); i++) {
    729                     ServiceInfo rsi = ris.get(i).serviceInfo;
    730                     if (rsi.name.equals(si.name) &&
    731                             rsi.packageName.equals(si.packageName)) {
    732                         try {
    733                             wi = new WallpaperInfo(mContext, ris.get(i));
    734                         } catch (XmlPullParserException e) {
    735                             if (fromUser) {
    736                                 throw new IllegalArgumentException(e);
    737                             }
    738                             Slog.w(TAG, e);
    739                             return false;
    740                         } catch (IOException e) {
    741                             if (fromUser) {
    742                                 throw new IllegalArgumentException(e);
    743                             }
    744                             Slog.w(TAG, e);
    745                             return false;
    746                         }
    747                         break;
    748                     }
    749                 }
    750                 if (wi == null) {
    751                     String msg = "Selected service is not a wallpaper: "
    752                             + componentName;
    753                     if (fromUser) {
    754                         throw new SecurityException(msg);
    755                     }
    756                     Slog.w(TAG, msg);
    757                     return false;
    758                 }
    759             }
    760 
    761             // Bind the service!
    762             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
    763             WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
    764             intent.setComponent(componentName);
    765             int serviceUserId = wallpaper.userId;
    766             // Because the image wallpaper is running in the system ui
    767             if (componentName.equals(wallpaper.imageWallpaperComponent)) {
    768                 serviceUserId = 0;
    769             }
    770             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
    771                     com.android.internal.R.string.wallpaper_binding_label);
    772             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
    773                     mContext, 0,
    774                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
    775                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
    776                             0));
    777             if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
    778                 String msg = "Unable to bind service: "
    779                         + componentName;
    780                 if (fromUser) {
    781                     throw new IllegalArgumentException(msg);
    782                 }
    783                 Slog.w(TAG, msg);
    784                 return false;
    785             }
    786             if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
    787                 detachWallpaperLocked(mLastWallpaper);
    788             }
    789             wallpaper.wallpaperComponent = componentName;
    790             wallpaper.connection = newConn;
    791             wallpaper.lastDiedTime = SystemClock.uptimeMillis();
    792             try {
    793                 if (wallpaper.userId == mCurrentUserId) {
    794                     if (DEBUG)
    795                         Slog.v(TAG, "Adding window token: " + newConn.mToken);
    796                     mIWindowManager.addWindowToken(newConn.mToken,
    797                             WindowManager.LayoutParams.TYPE_WALLPAPER);
    798                     mLastWallpaper = wallpaper;
    799                 }
    800             } catch (RemoteException e) {
    801             }
    802         } catch (PackageManager.NameNotFoundException e) {
    803             String msg = "Unknown component " + componentName;
    804             if (fromUser) {
    805                 throw new IllegalArgumentException(msg);
    806             }
    807             Slog.w(TAG, msg);
    808             return false;
    809         }
    810         return true;
    811     }
    812 
    813     void detachWallpaperLocked(WallpaperData wallpaper) {
    814         if (wallpaper.connection != null) {
    815             if (wallpaper.connection.mEngine != null) {
    816                 try {
    817                     wallpaper.connection.mEngine.destroy();
    818                 } catch (RemoteException e) {
    819                 }
    820             }
    821             mContext.unbindService(wallpaper.connection);
    822             try {
    823                 if (DEBUG)
    824                     Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
    825                 mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
    826             } catch (RemoteException e) {
    827             }
    828             wallpaper.connection.mService = null;
    829             wallpaper.connection.mEngine = null;
    830             wallpaper.connection = null;
    831         }
    832     }
    833 
    834     void clearWallpaperComponentLocked(WallpaperData wallpaper) {
    835         wallpaper.wallpaperComponent = null;
    836         detachWallpaperLocked(wallpaper);
    837     }
    838 
    839     void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
    840         try {
    841             conn.mService.attach(conn, conn.mToken,
    842                     WindowManager.LayoutParams.TYPE_WALLPAPER, false,
    843                     wallpaper.width, wallpaper.height);
    844         } catch (RemoteException e) {
    845             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
    846             if (!wallpaper.wallpaperUpdating) {
    847                 bindWallpaperComponentLocked(null, false, false, wallpaper);
    848             }
    849         }
    850     }
    851 
    852     private void notifyCallbacksLocked(WallpaperData wallpaper) {
    853         final int n = wallpaper.callbacks.beginBroadcast();
    854         for (int i = 0; i < n; i++) {
    855             try {
    856                 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
    857             } catch (RemoteException e) {
    858 
    859                 // The RemoteCallbackList will take care of removing
    860                 // the dead object for us.
    861             }
    862         }
    863         wallpaper.callbacks.finishBroadcast();
    864         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
    865         mContext.sendBroadcast(intent);
    866     }
    867 
    868     private void checkPermission(String permission) {
    869         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
    870             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
    871                     + ", must have permission " + permission);
    872         }
    873     }
    874 
    875     private static JournaledFile makeJournaledFile(int userId) {
    876         final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
    877         return new JournaledFile(new File(base), new File(base + ".tmp"));
    878     }
    879 
    880     private void saveSettingsLocked(WallpaperData wallpaper) {
    881         JournaledFile journal = makeJournaledFile(wallpaper.userId);
    882         FileOutputStream stream = null;
    883         try {
    884             stream = new FileOutputStream(journal.chooseForWrite(), false);
    885             XmlSerializer out = new FastXmlSerializer();
    886             out.setOutput(stream, "utf-8");
    887             out.startDocument(null, true);
    888 
    889             out.startTag(null, "wp");
    890             out.attribute(null, "width", Integer.toString(wallpaper.width));
    891             out.attribute(null, "height", Integer.toString(wallpaper.height));
    892             out.attribute(null, "name", wallpaper.name);
    893             if (wallpaper.wallpaperComponent != null
    894                     && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
    895                 out.attribute(null, "component",
    896                         wallpaper.wallpaperComponent.flattenToShortString());
    897             }
    898             out.endTag(null, "wp");
    899 
    900             out.endDocument();
    901             stream.close();
    902             journal.commit();
    903         } catch (IOException e) {
    904             try {
    905                 if (stream != null) {
    906                     stream.close();
    907                 }
    908             } catch (IOException ex) {
    909                 // Ignore
    910             }
    911             journal.rollback();
    912         }
    913     }
    914 
    915     private void migrateFromOld() {
    916         File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
    917         File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
    918         if (oldWallpaper.exists()) {
    919             File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
    920             oldWallpaper.renameTo(newWallpaper);
    921         }
    922         if (oldInfo.exists()) {
    923             File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
    924             oldInfo.renameTo(newInfo);
    925         }
    926     }
    927 
    928     private void loadSettingsLocked(int userId) {
    929         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
    930 
    931         JournaledFile journal = makeJournaledFile(userId);
    932         FileInputStream stream = null;
    933         File file = journal.chooseForRead();
    934         if (!file.exists()) {
    935             // This should only happen one time, when upgrading from a legacy system
    936             migrateFromOld();
    937         }
    938         WallpaperData wallpaper = mWallpaperMap.get(userId);
    939         if (wallpaper == null) {
    940             wallpaper = new WallpaperData(userId);
    941             mWallpaperMap.put(userId, wallpaper);
    942         }
    943         boolean success = false;
    944         try {
    945             stream = new FileInputStream(file);
    946             XmlPullParser parser = Xml.newPullParser();
    947             parser.setInput(stream, null);
    948 
    949             int type;
    950             do {
    951                 type = parser.next();
    952                 if (type == XmlPullParser.START_TAG) {
    953                     String tag = parser.getName();
    954                     if ("wp".equals(tag)) {
    955                         wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
    956                         wallpaper.height = Integer.parseInt(parser
    957                                 .getAttributeValue(null, "height"));
    958                         wallpaper.name = parser.getAttributeValue(null, "name");
    959                         String comp = parser.getAttributeValue(null, "component");
    960                         wallpaper.nextWallpaperComponent = comp != null
    961                                 ? ComponentName.unflattenFromString(comp)
    962                                 : null;
    963                         if (wallpaper.nextWallpaperComponent == null
    964                                 || "android".equals(wallpaper.nextWallpaperComponent
    965                                         .getPackageName())) {
    966                             wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent;
    967                         }
    968 
    969                         if (DEBUG) {
    970                             Slog.v(TAG, "mWidth:" + wallpaper.width);
    971                             Slog.v(TAG, "mHeight:" + wallpaper.height);
    972                             Slog.v(TAG, "mName:" + wallpaper.name);
    973                             Slog.v(TAG, "mNextWallpaperComponent:"
    974                                     + wallpaper.nextWallpaperComponent);
    975                         }
    976                     }
    977                 }
    978             } while (type != XmlPullParser.END_DOCUMENT);
    979             success = true;
    980         } catch (NullPointerException e) {
    981             Slog.w(TAG, "failed parsing " + file + " " + e);
    982         } catch (NumberFormatException e) {
    983             Slog.w(TAG, "failed parsing " + file + " " + e);
    984         } catch (XmlPullParserException e) {
    985             Slog.w(TAG, "failed parsing " + file + " " + e);
    986         } catch (IOException e) {
    987             Slog.w(TAG, "failed parsing " + file + " " + e);
    988         } catch (IndexOutOfBoundsException e) {
    989             Slog.w(TAG, "failed parsing " + file + " " + e);
    990         }
    991         try {
    992             if (stream != null) {
    993                 stream.close();
    994             }
    995         } catch (IOException e) {
    996             // Ignore
    997         }
    998 
    999         if (!success) {
   1000             wallpaper.width = -1;
   1001             wallpaper.height = -1;
   1002             wallpaper.name = "";
   1003         }
   1004 
   1005         // We always want to have some reasonable width hint.
   1006         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
   1007         Display d = wm.getDefaultDisplay();
   1008         int baseSize = d.getMaximumSizeDimension();
   1009         if (wallpaper.width < baseSize) {
   1010             wallpaper.width = baseSize;
   1011         }
   1012         if (wallpaper.height < baseSize) {
   1013             wallpaper.height = baseSize;
   1014         }
   1015     }
   1016 
   1017     // Called by SystemBackupAgent after files are restored to disk.
   1018     void settingsRestored() {
   1019         // TODO: If necessary, make it work for secondary users as well. This currently assumes
   1020         // restores only to the primary user
   1021         if (DEBUG) Slog.v(TAG, "settingsRestored");
   1022         WallpaperData wallpaper = null;
   1023         boolean success = false;
   1024         synchronized (mLock) {
   1025             loadSettingsLocked(0);
   1026             wallpaper = mWallpaperMap.get(0);
   1027             if (wallpaper.nextWallpaperComponent != null
   1028                     && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) {
   1029                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
   1030                         wallpaper)) {
   1031                     // No such live wallpaper or other failure; fall back to the default
   1032                     // live wallpaper (since the profile being restored indicated that the
   1033                     // user had selected a live rather than static one).
   1034                     bindWallpaperComponentLocked(null, false, false, wallpaper);
   1035                 }
   1036                 success = true;
   1037             } else {
   1038                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
   1039                 // use the default.
   1040                 if ("".equals(wallpaper.name)) {
   1041                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
   1042                     success = true;
   1043                 } else {
   1044                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
   1045                     success = restoreNamedResourceLocked(wallpaper);
   1046                 }
   1047                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
   1048                 if (success) {
   1049                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
   1050                             wallpaper);
   1051                 }
   1052             }
   1053         }
   1054 
   1055         if (!success) {
   1056             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
   1057             wallpaper.name = "";
   1058             getWallpaperDir(0).delete();
   1059         }
   1060 
   1061         synchronized (mLock) {
   1062             saveSettingsLocked(wallpaper);
   1063         }
   1064     }
   1065 
   1066     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
   1067         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
   1068             String resName = wallpaper.name.substring(4);
   1069 
   1070             String pkg = null;
   1071             int colon = resName.indexOf(':');
   1072             if (colon > 0) {
   1073                 pkg = resName.substring(0, colon);
   1074             }
   1075 
   1076             String ident = null;
   1077             int slash = resName.lastIndexOf('/');
   1078             if (slash > 0) {
   1079                 ident = resName.substring(slash+1);
   1080             }
   1081 
   1082             String type = null;
   1083             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
   1084                 type = resName.substring(colon+1, slash);
   1085             }
   1086 
   1087             if (pkg != null && ident != null && type != null) {
   1088                 int resId = -1;
   1089                 InputStream res = null;
   1090                 FileOutputStream fos = null;
   1091                 try {
   1092                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
   1093                     Resources r = c.getResources();
   1094                     resId = r.getIdentifier(resName, null, null);
   1095                     if (resId == 0) {
   1096                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
   1097                                 + " ident=" + ident);
   1098                         return false;
   1099                     }
   1100 
   1101                     res = r.openRawResource(resId);
   1102                     if (wallpaper.wallpaperFile.exists()) {
   1103                         wallpaper.wallpaperFile.delete();
   1104                     }
   1105                     fos = new FileOutputStream(wallpaper.wallpaperFile);
   1106 
   1107                     byte[] buffer = new byte[32768];
   1108                     int amt;
   1109                     while ((amt=res.read(buffer)) > 0) {
   1110                         fos.write(buffer, 0, amt);
   1111                     }
   1112                     // mWallpaperObserver will notice the close and send the change broadcast
   1113 
   1114                     Slog.v(TAG, "Restored wallpaper: " + resName);
   1115                     return true;
   1116                 } catch (NameNotFoundException e) {
   1117                     Slog.e(TAG, "Package name " + pkg + " not found");
   1118                 } catch (Resources.NotFoundException e) {
   1119                     Slog.e(TAG, "Resource not found: " + resId);
   1120                 } catch (IOException e) {
   1121                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
   1122                 } finally {
   1123                     if (res != null) {
   1124                         try {
   1125                             res.close();
   1126                         } catch (IOException ex) {}
   1127                     }
   1128                     if (fos != null) {
   1129                         FileUtils.sync(fos);
   1130                         try {
   1131                             fos.close();
   1132                         } catch (IOException ex) {}
   1133                     }
   1134                 }
   1135             }
   1136         }
   1137         return false;
   1138     }
   1139 
   1140     @Override
   1141     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1142         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
   1143                 != PackageManager.PERMISSION_GRANTED) {
   1144 
   1145             pw.println("Permission Denial: can't dump wallpaper service from from pid="
   1146                     + Binder.getCallingPid()
   1147                     + ", uid=" + Binder.getCallingUid());
   1148             return;
   1149         }
   1150 
   1151         synchronized (mLock) {
   1152             pw.println("Current Wallpaper Service state:");
   1153             for (int i = 0; i < mWallpaperMap.size(); i++) {
   1154                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
   1155                 pw.println(" User " + wallpaper.userId + ":");
   1156                 pw.print("  mWidth=");
   1157                 pw.print(wallpaper.width);
   1158                 pw.print(" mHeight=");
   1159                 pw.println(wallpaper.height);
   1160                 pw.print("  mName=");
   1161                 pw.println(wallpaper.name);
   1162                 pw.print("  mWallpaperComponent=");
   1163                 pw.println(wallpaper.wallpaperComponent);
   1164                 if (wallpaper.connection != null) {
   1165                     WallpaperConnection conn = wallpaper.connection;
   1166                     pw.print("  Wallpaper connection ");
   1167                     pw.print(conn);
   1168                     pw.println(":");
   1169                     if (conn.mInfo != null) {
   1170                         pw.print("    mInfo.component=");
   1171                         pw.println(conn.mInfo.getComponent());
   1172                     }
   1173                     pw.print("    mToken=");
   1174                     pw.println(conn.mToken);
   1175                     pw.print("    mService=");
   1176                     pw.println(conn.mService);
   1177                     pw.print("    mEngine=");
   1178                     pw.println(conn.mEngine);
   1179                     pw.print("    mLastDiedTime=");
   1180                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
   1181                 }
   1182             }
   1183         }
   1184     }
   1185 }
   1186