• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WallpaperObserver(WallpaperData wallpaper)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
onEvent(int event, String path)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 
WallpaperData(int userId)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 
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper)216         public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
217             mInfo = info;
218             mWallpaper = wallpaper;
219         }
220 
onServiceConnected(ComponentName name, IBinder service)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 
onServiceDisconnected(ComponentName name)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 
attachEngine(IWallpaperEngine engine)253         public void attachEngine(IWallpaperEngine engine) {
254             mEngine = engine;
255         }
256 
setWallpaper(String name)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
onPackageUpdateFinished(String packageName, int uid)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
onPackageModified(String packageName)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
onPackageUpdateStarted(String packageName, int uid)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
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)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
onSomePackagesChanged()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 
doPackagesChangedLocked(boolean doit, WallpaperData wallpaper)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 
WallpaperManagerService(Context context)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 
getWallpaperDir(int userId)397     private static File getWallpaperDir(int userId) {
398         return new File(WALLPAPER_BASE_DIR + "/" + userId);
399     }
400 
401     @Override
finalize()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 
systemReady()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 
getName()433     String getName() {
434         return mWallpaperMap.get(0).name;
435     }
436 
removeUser(int userId)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 
switchUser(int userId)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 
switchWallpaper(WallpaperData wallpaper)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 
clearWallpaper()483     public void clearWallpaper() {
484         if (DEBUG) Slog.v(TAG, "clearWallpaper");
485         synchronized (mLock) {
486             clearWallpaperLocked(false, UserId.getCallingUserId());
487         }
488     }
489 
clearWallpaperLocked(boolean defaultFailed, int userId)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 
setDimensionHints(int width, int height)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 
getWidthHint()552     public int getWidthHint() throws RemoteException {
553         synchronized (mLock) {
554             WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
555             return wallpaper.width;
556         }
557     }
558 
getHeightHint()559     public int getHeightHint() throws RemoteException {
560         synchronized (mLock) {
561             WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
562             return wallpaper.height;
563         }
564     }
565 
getWallpaper(IWallpaperManagerCallback cb, Bundle outParams)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 
getWallpaperInfo()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 
setWallpaper(String name)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 
updateWallpaperBitmapLocked(String name, WallpaperData wallpaper)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 
setWallpaperComponent(ComponentName name)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 
bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper)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 
detachWallpaperLocked(WallpaperData wallpaper)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 
clearWallpaperComponentLocked(WallpaperData wallpaper)834     void clearWallpaperComponentLocked(WallpaperData wallpaper) {
835         wallpaper.wallpaperComponent = null;
836         detachWallpaperLocked(wallpaper);
837     }
838 
attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper)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 
notifyCallbacksLocked(WallpaperData wallpaper)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 
checkPermission(String permission)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 
makeJournaledFile(int userId)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 
saveSettingsLocked(WallpaperData wallpaper)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 
migrateFromOld()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 
loadSettingsLocked(int userId)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.
settingsRestored()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 
restoreNamedResourceLocked(WallpaperData wallpaper)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
dump(FileDescriptor fd, PrintWriter pw, String[] args)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