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