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             ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
    480                     MODE_CREATE|MODE_READ_WRITE);
    481             mName = name;
    482             return fd;
    483         } catch (FileNotFoundException e) {
    484             Slog.w(TAG, "Error setting wallpaper", e);
    485         }
    486         return null;
    487     }
    488 
    489     public void setWallpaperComponent(ComponentName name) {
    490         if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
    491         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
    492         synchronized (mLock) {
    493             final long ident = Binder.clearCallingIdentity();
    494             try {
    495                 mImageWallpaperPending = false;
    496                 bindWallpaperComponentLocked(name, false, true);
    497             } finally {
    498                 Binder.restoreCallingIdentity(ident);
    499             }
    500         }
    501     }
    502 
    503     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser) {
    504         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
    505 
    506         // Has the component changed?
    507         if (!force) {
    508             if (mWallpaperConnection != null) {
    509                 if (mWallpaperComponent == null) {
    510                     if (componentName == null) {
    511                         if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
    512                         // Still using default wallpaper.
    513                         return true;
    514                     }
    515                 } else if (mWallpaperComponent.equals(componentName)) {
    516                     // Changing to same wallpaper.
    517                     if (DEBUG) Slog.v(TAG, "same wallpaper");
    518                     return true;
    519                 }
    520             }
    521         }
    522 
    523         try {
    524             if (componentName == null) {
    525                 String defaultComponent =
    526                     mContext.getString(com.android.internal.R.string.default_wallpaper_component);
    527                 if (defaultComponent != null) {
    528                     // See if there is a default wallpaper component specified
    529                     componentName = ComponentName.unflattenFromString(defaultComponent);
    530                     if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName);
    531                 }
    532                 if (componentName == null) {
    533                     // Fall back to static image wallpaper
    534                     componentName = mImageWallpaperComponent;
    535                     //clearWallpaperComponentLocked();
    536                     //return;
    537                     if (DEBUG) Slog.v(TAG, "Using image wallpaper");
    538                 }
    539             }
    540             ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
    541                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
    542             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
    543                 String msg = "Selected service does not require "
    544                         + android.Manifest.permission.BIND_WALLPAPER
    545                         + ": " + componentName;
    546                 if (fromUser) {
    547                     throw new SecurityException(msg);
    548                 }
    549                 Slog.w(TAG, msg);
    550                 return false;
    551             }
    552 
    553             WallpaperInfo wi = null;
    554 
    555             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
    556             if (componentName != null && !componentName.equals(mImageWallpaperComponent)) {
    557                 // Make sure the selected service is actually a wallpaper service.
    558                 List<ResolveInfo> ris = mContext.getPackageManager()
    559                         .queryIntentServices(intent, PackageManager.GET_META_DATA);
    560                 for (int i=0; i<ris.size(); i++) {
    561                     ServiceInfo rsi = ris.get(i).serviceInfo;
    562                     if (rsi.name.equals(si.name) &&
    563                             rsi.packageName.equals(si.packageName)) {
    564                         try {
    565                             wi = new WallpaperInfo(mContext, ris.get(i));
    566                         } catch (XmlPullParserException e) {
    567                             if (fromUser) {
    568                                 throw new IllegalArgumentException(e);
    569                             }
    570                             Slog.w(TAG, e);
    571                             return false;
    572                         } catch (IOException e) {
    573                             if (fromUser) {
    574                                 throw new IllegalArgumentException(e);
    575                             }
    576                             Slog.w(TAG, e);
    577                             return false;
    578                         }
    579                         break;
    580                     }
    581                 }
    582                 if (wi == null) {
    583                     String msg = "Selected service is not a wallpaper: "
    584                             + componentName;
    585                     if (fromUser) {
    586                         throw new SecurityException(msg);
    587                     }
    588                     Slog.w(TAG, msg);
    589                     return false;
    590                 }
    591             }
    592 
    593             // Bind the service!
    594             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
    595             WallpaperConnection newConn = new WallpaperConnection(wi);
    596             intent.setComponent(componentName);
    597             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
    598                     com.android.internal.R.string.wallpaper_binding_label);
    599             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
    600                     mContext, 0,
    601                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
    602                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
    603                             0));
    604             if (!mContext.bindService(intent, newConn,
    605                     Context.BIND_AUTO_CREATE)) {
    606                 String msg = "Unable to bind service: "
    607                         + componentName;
    608                 if (fromUser) {
    609                     throw new IllegalArgumentException(msg);
    610                 }
    611                 Slog.w(TAG, msg);
    612                 return false;
    613             }
    614 
    615             clearWallpaperComponentLocked();
    616             mWallpaperComponent = componentName;
    617             mWallpaperConnection = newConn;
    618             mLastDiedTime = SystemClock.uptimeMillis();
    619             try {
    620                 if (DEBUG) Slog.v(TAG, "Adding window token: " + newConn.mToken);
    621                 mIWindowManager.addWindowToken(newConn.mToken,
    622                         WindowManager.LayoutParams.TYPE_WALLPAPER);
    623             } catch (RemoteException e) {
    624             }
    625 
    626         } catch (PackageManager.NameNotFoundException e) {
    627             String msg = "Unknown component " + componentName;
    628             if (fromUser) {
    629                 throw new IllegalArgumentException(msg);
    630             }
    631             Slog.w(TAG, msg);
    632             return false;
    633         }
    634         return true;
    635     }
    636 
    637     void clearWallpaperComponentLocked() {
    638         mWallpaperComponent = null;
    639         if (mWallpaperConnection != null) {
    640             if (mWallpaperConnection.mEngine != null) {
    641                 try {
    642                     mWallpaperConnection.mEngine.destroy();
    643                 } catch (RemoteException e) {
    644                 }
    645             }
    646             mContext.unbindService(mWallpaperConnection);
    647             try {
    648                 if (DEBUG) Slog.v(TAG, "Removing window token: "
    649                         + mWallpaperConnection.mToken);
    650                 mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
    651             } catch (RemoteException e) {
    652             }
    653             mWallpaperConnection.mService = null;
    654             mWallpaperConnection.mEngine = null;
    655             mWallpaperConnection = null;
    656         }
    657     }
    658 
    659     void attachServiceLocked(WallpaperConnection conn) {
    660         try {
    661             conn.mService.attach(conn, conn.mToken,
    662                     WindowManager.LayoutParams.TYPE_WALLPAPER, false,
    663                     mWidth, mHeight);
    664         } catch (RemoteException e) {
    665             Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
    666             if (!mWallpaperUpdating) {
    667                 bindWallpaperComponentLocked(null, false, false);
    668             }
    669         }
    670     }
    671 
    672     private void notifyCallbacksLocked() {
    673         final int n = mCallbacks.beginBroadcast();
    674         for (int i = 0; i < n; i++) {
    675             try {
    676                 mCallbacks.getBroadcastItem(i).onWallpaperChanged();
    677             } catch (RemoteException e) {
    678 
    679                 // The RemoteCallbackList will take care of removing
    680                 // the dead object for us.
    681             }
    682         }
    683         mCallbacks.finishBroadcast();
    684         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
    685         mContext.sendBroadcast(intent);
    686     }
    687 
    688     private void checkPermission(String permission) {
    689         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
    690             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
    691                     + ", must have permission " + permission);
    692         }
    693     }
    694 
    695     private static JournaledFile makeJournaledFile() {
    696         final String base = "/data/system/wallpaper_info.xml";
    697         return new JournaledFile(new File(base), new File(base + ".tmp"));
    698     }
    699 
    700     private void saveSettingsLocked() {
    701         JournaledFile journal = makeJournaledFile();
    702         FileOutputStream stream = null;
    703         try {
    704             stream = new FileOutputStream(journal.chooseForWrite(), false);
    705             XmlSerializer out = new FastXmlSerializer();
    706             out.setOutput(stream, "utf-8");
    707             out.startDocument(null, true);
    708 
    709             out.startTag(null, "wp");
    710             out.attribute(null, "width", Integer.toString(mWidth));
    711             out.attribute(null, "height", Integer.toString(mHeight));
    712             out.attribute(null, "name", mName);
    713             if (mWallpaperComponent != null &&
    714                     !mWallpaperComponent.equals(mImageWallpaperComponent)) {
    715                 out.attribute(null, "component",
    716                         mWallpaperComponent.flattenToShortString());
    717             }
    718             out.endTag(null, "wp");
    719 
    720             out.endDocument();
    721             stream.close();
    722             journal.commit();
    723         } catch (IOException e) {
    724             try {
    725                 if (stream != null) {
    726                     stream.close();
    727                 }
    728             } catch (IOException ex) {
    729                 // Ignore
    730             }
    731             journal.rollback();
    732         }
    733     }
    734 
    735     private void loadSettingsLocked() {
    736         if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
    737 
    738         JournaledFile journal = makeJournaledFile();
    739         FileInputStream stream = null;
    740         File file = journal.chooseForRead();
    741         boolean success = false;
    742         try {
    743             stream = new FileInputStream(file);
    744             XmlPullParser parser = Xml.newPullParser();
    745             parser.setInput(stream, null);
    746 
    747             int type;
    748             do {
    749                 type = parser.next();
    750                 if (type == XmlPullParser.START_TAG) {
    751                     String tag = parser.getName();
    752                     if ("wp".equals(tag)) {
    753                         mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
    754                         mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
    755                         mName = parser.getAttributeValue(null, "name");
    756                         String comp = parser.getAttributeValue(null, "component");
    757                         mNextWallpaperComponent = comp != null
    758                                 ? ComponentName.unflattenFromString(comp)
    759                                 : null;
    760                         if (mNextWallpaperComponent == null ||
    761                                 "android".equals(mNextWallpaperComponent.getPackageName())) {
    762                             mNextWallpaperComponent = mImageWallpaperComponent;
    763                         }
    764 
    765                         if (DEBUG) {
    766                             Slog.v(TAG, "mWidth:" + mWidth);
    767                             Slog.v(TAG, "mHeight:" + mHeight);
    768                             Slog.v(TAG, "mName:" + mName);
    769                             Slog.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent);
    770                         }
    771                     }
    772                 }
    773             } while (type != XmlPullParser.END_DOCUMENT);
    774             success = true;
    775         } catch (NullPointerException e) {
    776             Slog.w(TAG, "failed parsing " + file + " " + e);
    777         } catch (NumberFormatException e) {
    778             Slog.w(TAG, "failed parsing " + file + " " + e);
    779         } catch (XmlPullParserException e) {
    780             Slog.w(TAG, "failed parsing " + file + " " + e);
    781         } catch (IOException e) {
    782             Slog.w(TAG, "failed parsing " + file + " " + e);
    783         } catch (IndexOutOfBoundsException e) {
    784             Slog.w(TAG, "failed parsing " + file + " " + e);
    785         }
    786         try {
    787             if (stream != null) {
    788                 stream.close();
    789             }
    790         } catch (IOException e) {
    791             // Ignore
    792         }
    793 
    794         if (!success) {
    795             mWidth = -1;
    796             mHeight = -1;
    797             mName = "";
    798         }
    799 
    800         // We always want to have some reasonable width hint.
    801         WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    802         Display d = wm.getDefaultDisplay();
    803         int baseSize = d.getMaximumSizeDimension();
    804         if (mWidth < baseSize) {
    805             mWidth = baseSize;
    806         }
    807         if (mHeight < baseSize) {
    808             mHeight = baseSize;
    809         }
    810     }
    811 
    812     // Called by SystemBackupAgent after files are restored to disk.
    813     void settingsRestored() {
    814         if (DEBUG) Slog.v(TAG, "settingsRestored");
    815 
    816         boolean success = false;
    817         synchronized (mLock) {
    818             loadSettingsLocked();
    819             if (mNextWallpaperComponent != null &&
    820                     !mNextWallpaperComponent.equals(mImageWallpaperComponent)) {
    821                 if (!bindWallpaperComponentLocked(mNextWallpaperComponent, false, false)) {
    822                     // No such live wallpaper or other failure; fall back to the default
    823                     // live wallpaper (since the profile being restored indicated that the
    824                     // user had selected a live rather than static one).
    825                     bindWallpaperComponentLocked(null, false, false);
    826                 }
    827                 success = true;
    828             } else {
    829                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
    830                 // use the default.
    831                 if ("".equals(mName)) {
    832                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
    833                     success = true;
    834                 } else {
    835                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
    836                     success = restoreNamedResourceLocked();
    837                 }
    838                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
    839                 if (success) {
    840                     bindWallpaperComponentLocked(mNextWallpaperComponent, false, false);
    841                 }
    842             }
    843         }
    844 
    845         if (!success) {
    846             Slog.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
    847             mName = "";
    848             WALLPAPER_FILE.delete();
    849         }
    850 
    851         synchronized (mLock) {
    852             saveSettingsLocked();
    853         }
    854     }
    855 
    856     boolean restoreNamedResourceLocked() {
    857         if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
    858             String resName = mName.substring(4);
    859 
    860             String pkg = null;
    861             int colon = resName.indexOf(':');
    862             if (colon > 0) {
    863                 pkg = resName.substring(0, colon);
    864             }
    865 
    866             String ident = null;
    867             int slash = resName.lastIndexOf('/');
    868             if (slash > 0) {
    869                 ident = resName.substring(slash+1);
    870             }
    871 
    872             String type = null;
    873             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
    874                 type = resName.substring(colon+1, slash);
    875             }
    876 
    877             if (pkg != null && ident != null && type != null) {
    878                 int resId = -1;
    879                 InputStream res = null;
    880                 FileOutputStream fos = null;
    881                 try {
    882                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
    883                     Resources r = c.getResources();
    884                     resId = r.getIdentifier(resName, null, null);
    885                     if (resId == 0) {
    886                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
    887                                 + " ident=" + ident);
    888                         return false;
    889                     }
    890 
    891                     res = r.openRawResource(resId);
    892                     if (WALLPAPER_FILE.exists()) {
    893                         WALLPAPER_FILE.delete();
    894                     }
    895                     fos = new FileOutputStream(WALLPAPER_FILE);
    896 
    897                     byte[] buffer = new byte[32768];
    898                     int amt;
    899                     while ((amt=res.read(buffer)) > 0) {
    900                         fos.write(buffer, 0, amt);
    901                     }
    902                     // mWallpaperObserver will notice the close and send the change broadcast
    903 
    904                     Slog.v(TAG, "Restored wallpaper: " + resName);
    905                     return true;
    906                 } catch (NameNotFoundException e) {
    907                     Slog.e(TAG, "Package name " + pkg + " not found");
    908                 } catch (Resources.NotFoundException e) {
    909                     Slog.e(TAG, "Resource not found: " + resId);
    910                 } catch (IOException e) {
    911                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
    912                 } finally {
    913                     if (res != null) {
    914                         try {
    915                             res.close();
    916                         } catch (IOException ex) {}
    917                     }
    918                     if (fos != null) {
    919                         FileUtils.sync(fos);
    920                         try {
    921                             fos.close();
    922                         } catch (IOException ex) {}
    923                     }
    924                 }
    925             }
    926         }
    927         return false;
    928     }
    929 
    930     @Override
    931     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    932         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    933                 != PackageManager.PERMISSION_GRANTED) {
    934 
    935             pw.println("Permission Denial: can't dump wallpaper service from from pid="
    936                     + Binder.getCallingPid()
    937                     + ", uid=" + Binder.getCallingUid());
    938             return;
    939         }
    940 
    941         synchronized (mLock) {
    942             pw.println("Current Wallpaper Service state:");
    943             pw.print("  mWidth="); pw.print(mWidth);
    944                     pw.print(" mHeight="); pw.println(mHeight);
    945             pw.print("  mName="); pw.println(mName);
    946             pw.print("  mWallpaperComponent="); pw.println(mWallpaperComponent);
    947             if (mWallpaperConnection != null) {
    948                 WallpaperConnection conn = mWallpaperConnection;
    949                 pw.print("  Wallpaper connection ");
    950                         pw.print(conn); pw.println(":");
    951                 pw.print("    mInfo.component="); pw.println(conn.mInfo.getComponent());
    952                 pw.print("    mToken="); pw.println(conn.mToken);
    953                 pw.print("    mService="); pw.println(conn.mService);
    954                 pw.print("    mEngine="); pw.println(conn.mEngine);
    955                 pw.print("    mLastDiedTime=");
    956                         pw.println(mLastDiedTime - SystemClock.uptimeMillis());
    957             }
    958         }
    959     }
    960 }
    961