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