WallpaperManagerService.java revision d57d17ca969756e6b9792199e221df475a7537f9
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.app.WallpaperManager.FLAG_SET_SYSTEM;
20import static android.app.WallpaperManager.FLAG_SET_LOCK;
21import static android.os.ParcelFileDescriptor.*;
22
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.AppGlobals;
26import android.app.AppOpsManager;
27import android.app.IUserSwitchObserver;
28import android.app.IWallpaperManager;
29import android.app.IWallpaperManagerCallback;
30import android.app.PendingIntent;
31import android.app.WallpaperInfo;
32import android.app.WallpaperManager;
33import android.app.admin.DevicePolicyManager;
34import android.app.backup.BackupManager;
35import android.app.backup.WallpaperBackupHelper;
36import android.content.BroadcastReceiver;
37import android.content.ComponentName;
38import android.content.Context;
39import android.content.Intent;
40import android.content.IntentFilter;
41import android.content.ServiceConnection;
42import android.content.pm.IPackageManager;
43import android.content.pm.PackageManager;
44import android.content.pm.ResolveInfo;
45import android.content.pm.ServiceInfo;
46import android.content.pm.PackageManager.NameNotFoundException;
47import android.content.pm.UserInfo;
48import android.content.res.Resources;
49import android.graphics.Bitmap;
50import android.graphics.BitmapFactory;
51import android.graphics.BitmapRegionDecoder;
52import android.graphics.Point;
53import android.graphics.Rect;
54import android.os.Binder;
55import android.os.Bundle;
56import android.os.Environment;
57import android.os.FileUtils;
58import android.os.IBinder;
59import android.os.IRemoteCallback;
60import android.os.RemoteException;
61import android.os.FileObserver;
62import android.os.ParcelFileDescriptor;
63import android.os.RemoteCallbackList;
64import android.os.SELinux;
65import android.os.ServiceManager;
66import android.os.SystemClock;
67import android.os.UserHandle;
68import android.os.UserManager;
69import android.service.wallpaper.IWallpaperConnection;
70import android.service.wallpaper.IWallpaperEngine;
71import android.service.wallpaper.IWallpaperService;
72import android.service.wallpaper.WallpaperService;
73import android.util.EventLog;
74import android.util.Slog;
75import android.util.SparseArray;
76import android.util.Xml;
77import android.view.Display;
78import android.view.IWindowManager;
79import android.view.WindowManager;
80
81import java.io.BufferedOutputStream;
82import java.io.FileDescriptor;
83import java.io.IOException;
84import java.io.InputStream;
85import java.io.File;
86import java.io.FileNotFoundException;
87import java.io.FileInputStream;
88import java.io.FileOutputStream;
89import java.io.PrintWriter;
90import java.nio.charset.StandardCharsets;
91import java.util.Arrays;
92import java.util.List;
93
94import org.xmlpull.v1.XmlPullParser;
95import org.xmlpull.v1.XmlPullParserException;
96import org.xmlpull.v1.XmlSerializer;
97
98import com.android.internal.content.PackageMonitor;
99import com.android.internal.util.FastXmlSerializer;
100import com.android.internal.util.JournaledFile;
101import com.android.internal.R;
102import com.android.server.EventLogTags;
103
104import libcore.io.IoUtils;
105
106public class WallpaperManagerService extends IWallpaperManager.Stub {
107    static final String TAG = "WallpaperManagerService";
108    static final boolean DEBUG = false;
109
110    final Object mLock = new Object();
111
112    /**
113     * Minimum time between crashes of a wallpaper service for us to consider
114     * restarting it vs. just reverting to the static wallpaper.
115     */
116    static final long MIN_WALLPAPER_CRASH_TIME = 10000;
117    static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
118    static final String WALLPAPER = "wallpaper_orig";
119    static final String WALLPAPER_CROP = "wallpaper";
120    static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
121    static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
122    static final String WALLPAPER_INFO = "wallpaper_info.xml";
123
124    // All the various per-user state files we need to be aware of
125    static final String[] sPerUserFiles = new String[] {
126        WALLPAPER, WALLPAPER_CROP,
127        WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
128        WALLPAPER_INFO
129    };
130
131    /**
132     * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
133     * that the wallpaper has changed. The CREATE is triggered when there is no
134     * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
135     * everytime the wallpaper is changed.
136     */
137    private class WallpaperObserver extends FileObserver {
138
139        final int mUserId;
140        final WallpaperData mWallpaper;
141        final File mWallpaperDir;
142        final File mWallpaperFile;
143        final File mWallpaperLockFile;
144        final File mWallpaperInfoFile;
145
146        public WallpaperObserver(WallpaperData wallpaper) {
147            super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
148                    CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
149            mUserId = wallpaper.userId;
150            mWallpaperDir = getWallpaperDir(wallpaper.userId);
151            mWallpaper = wallpaper;
152            mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
153            mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
154            mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
155        }
156
157        private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
158            WallpaperData wallpaper = null;
159            synchronized (mLock) {
160                if (lockChanged) {
161                    wallpaper = mLockWallpaperMap.get(mUserId);
162                }
163                if (wallpaper == null) {
164                    // no lock-specific wallpaper exists, or sys case, handled together
165                    wallpaper = mWallpaperMap.get(mUserId);
166                }
167            }
168            return (wallpaper != null) ? wallpaper : mWallpaper;
169        }
170
171        @Override
172        public void onEvent(int event, String path) {
173            if (path == null) {
174                return;
175            }
176            final boolean written = (event == CLOSE_WRITE || event == MOVED_TO);
177            final File changedFile = new File(mWallpaperDir, path);
178
179            // System and system+lock changes happen on the system wallpaper input file;
180            // lock-only changes happen on the dedicated lock wallpaper input file
181            final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
182            final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
183            WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
184
185            if (DEBUG) {
186                Slog.v(TAG, "Wallpaper file change: evt=" + event
187                        + " path=" + path
188                        + " sys=" + sysWallpaperChanged
189                        + " lock=" + lockWallpaperChanged
190                        + " imagePending=" + wallpaper.imageWallpaperPending
191                        + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending)
192                        + " written=" + written);
193            }
194            synchronized (mLock) {
195                if (sysWallpaperChanged || mWallpaperInfoFile.equals(changedFile)) {
196                    // changing the wallpaper means we'll need to back up the new one
197                    long origId = Binder.clearCallingIdentity();
198                    BackupManager bm = new BackupManager(mContext);
199                    bm.dataChanged();
200                    Binder.restoreCallingIdentity(origId);
201                }
202                if (sysWallpaperChanged || lockWallpaperChanged) {
203                    notifyCallbacksLocked(wallpaper);
204                    if (wallpaper.wallpaperComponent == null
205                            || event != CLOSE_WRITE // includes the MOVED_TO case
206                            || wallpaper.imageWallpaperPending) {
207                        if (written) {
208                            // The image source has finished writing the source image,
209                            // so we now produce the crop rect (in the background), and
210                            // only publish the new displayable (sub)image as a result
211                            // of that work.
212                            if (DEBUG) {
213                                Slog.v(TAG, "Wallpaper written; generating crop");
214                            }
215                            generateCrop(wallpaper);
216                            if (DEBUG) {
217                                Slog.v(TAG, "Crop done; invoking completion callback");
218                            }
219                            wallpaper.imageWallpaperPending = false;
220                            if (wallpaper.setComplete != null) {
221                                try {
222                                    wallpaper.setComplete.onWallpaperChanged();
223                                } catch (RemoteException e) {
224                                    // if this fails we don't really care; the setting app may just
225                                    // have crashed and that sort of thing is a fact of life.
226                                }
227                            }
228                            if (sysWallpaperChanged) {
229                                // If this was the system wallpaper, rebind...
230                                bindWallpaperComponentLocked(mImageWallpaper, true,
231                                        false, wallpaper, null);
232                            }
233                            if (lockWallpaperChanged
234                                    || (wallpaper.whichPending & FLAG_SET_LOCK) != 0) {
235                                if (DEBUG) {
236                                    Slog.i(TAG, "Lock-relevant wallpaper changed");
237                                }
238                                // either a lock-only wallpaper commit or a system+lock event.
239                                // if it's system-plus-lock we need to wipe the lock bookkeeping;
240                                // we're falling back to displaying the system wallpaper there.
241                                if (!lockWallpaperChanged) {
242                                    mLockWallpaperMap.remove(wallpaper.userId);
243                                }
244                                // and in any case, tell keyguard about it
245                                final IWallpaperManagerCallback cb = mKeyguardListener;
246                                if (cb != null) {
247                                    try {
248                                        cb.onWallpaperChanged();
249                                    } catch (RemoteException e) {
250                                        // Oh well it went away; no big deal
251                                    }
252                                }
253                            }
254                            saveSettingsLocked(wallpaper.userId);
255                        }
256                    }
257                }
258            }
259        }
260    }
261
262    /**
263     * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
264     * for display.
265     */
266    private void generateCrop(WallpaperData wallpaper) {
267        boolean success = false;
268        boolean needCrop = false;
269        boolean needScale = false;
270
271        if (DEBUG) {
272            Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
273                    + Integer.toHexString(wallpaper.whichPending)
274                    + " to " + wallpaper.cropFile.getName());
275        }
276
277        // Analyse the source; needed in multiple cases
278        BitmapFactory.Options options = new BitmapFactory.Options();
279        options.inJustDecodeBounds = true;
280        BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
281
282        // We'll need to scale if the crop is sufficiently bigger than the display
283
284        // Legacy case uses an empty crop rect here, so we just preserve the
285        // source image verbatim
286        if (!wallpaper.cropHint.isEmpty()) {
287            // ...clamp the crop rect to the measured bounds...
288            wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth);
289            wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight);
290            // ...and don't bother cropping if what we're left with is identity
291            needCrop = (options.outHeight >= wallpaper.cropHint.height()
292                    && options.outWidth >= wallpaper.cropHint.width());
293        }
294
295        if (!needCrop && !needScale) {
296            // Simple case:  the nominal crop is at least as big as the source image,
297            // so we take the whole thing and just copy the image file directly.
298            if (DEBUG) {
299                Slog.v(TAG, "Null crop of new wallpaper; copying");
300            }
301            success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
302            if (!success) {
303                wallpaper.cropFile.delete();
304                // TODO: fall back to default wallpaper in this case
305            }
306        } else {
307            // Fancy case: crop and/or scale
308            FileOutputStream f = null;
309            BufferedOutputStream bos = null;
310            try {
311                BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
312                        wallpaper.wallpaperFile.getAbsolutePath(), false);
313                Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null);
314                decoder.recycle();
315
316                if (cropped == null) {
317                    Slog.e(TAG, "Could not decode new wallpaper");
318                } else {
319                    f = new FileOutputStream(wallpaper.cropFile);
320                    bos = new BufferedOutputStream(f, 32*1024);
321                    cropped.compress(Bitmap.CompressFormat.PNG, 90, bos);
322                    bos.flush();  // don't rely on the implicit flush-at-close when noting success
323                    success = true;
324                }
325            } catch (IOException e) {
326                if (DEBUG) {
327                    Slog.e(TAG, "I/O error decoding crop: " + e.getMessage());
328                }
329            } finally {
330                IoUtils.closeQuietly(bos);
331                IoUtils.closeQuietly(f);
332            }
333        }
334
335        if (!success) {
336            Slog.e(TAG, "Unable to apply new wallpaper");
337            wallpaper.cropFile.delete();
338        }
339
340        if (wallpaper.cropFile.exists()) {
341            boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
342            if (DEBUG) {
343                Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
344            }
345        }
346    }
347
348    final Context mContext;
349    final IWindowManager mIWindowManager;
350    final IPackageManager mIPackageManager;
351    final MyPackageMonitor mMonitor;
352    final AppOpsManager mAppOpsManager;
353    WallpaperData mLastWallpaper;
354    IWallpaperManagerCallback mKeyguardListener;
355
356    /**
357     * ID of the current wallpaper, changed every time anything sets a wallpaper.
358     * This is used for external detection of wallpaper update activity.
359     */
360    int mWallpaperId;
361
362    /**
363     * Name of the component used to display bitmap wallpapers from either the gallery or
364     * built-in wallpapers.
365     */
366    final ComponentName mImageWallpaper;
367
368    final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
369    final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
370
371    int mCurrentUserId;
372
373    static class WallpaperData {
374
375        int userId;
376
377        final File wallpaperFile;   // source image
378        final File cropFile;        // eventual destination
379
380        /**
381         * True while the client is writing a new wallpaper
382         */
383        boolean imageWallpaperPending;
384
385        /**
386         * Which new wallpapers are being written; mirrors the 'which'
387         * selector bit field to setWallpaper().
388         */
389        int whichPending;
390
391        /**
392         * Callback once the set + crop is finished
393         */
394        IWallpaperManagerCallback setComplete;
395
396        /**
397         * Resource name if using a picture from the wallpaper gallery
398         */
399        String name = "";
400
401        /**
402         * The component name of the currently set live wallpaper.
403         */
404        ComponentName wallpaperComponent;
405
406        /**
407         * The component name of the wallpaper that should be set next.
408         */
409        ComponentName nextWallpaperComponent;
410
411        /**
412         * The ID of this wallpaper
413         */
414        int wallpaperId;
415
416        WallpaperConnection connection;
417        long lastDiedTime;
418        boolean wallpaperUpdating;
419        WallpaperObserver wallpaperObserver;
420
421        /**
422         * List of callbacks registered they should each be notified when the wallpaper is changed.
423         */
424        private RemoteCallbackList<IWallpaperManagerCallback> callbacks
425                = new RemoteCallbackList<IWallpaperManagerCallback>();
426
427        int width = -1;
428        int height = -1;
429
430        /**
431         * The crop hint supplied for displaying a subset of the source image
432         */
433        final Rect cropHint = new Rect(0, 0, 0, 0);
434
435        final Rect padding = new Rect(0, 0, 0, 0);
436
437        WallpaperData(int userId, String inputFileName, String cropFileName) {
438            this.userId = userId;
439            final File wallpaperDir = getWallpaperDir(userId);
440            wallpaperFile = new File(wallpaperDir, inputFileName);
441            cropFile = new File(wallpaperDir, cropFileName);
442        }
443
444        // Called during initialization of a given user's wallpaper bookkeeping
445        boolean cropExists() {
446            return cropFile.exists();
447        }
448    }
449
450    int makeWallpaperIdLocked() {
451        do {
452            ++mWallpaperId;
453        } while (mWallpaperId == 0);
454        return mWallpaperId;
455    }
456
457    class WallpaperConnection extends IWallpaperConnection.Stub
458            implements ServiceConnection {
459        final WallpaperInfo mInfo;
460        final Binder mToken = new Binder();
461        IWallpaperService mService;
462        IWallpaperEngine mEngine;
463        WallpaperData mWallpaper;
464        IRemoteCallback mReply;
465
466        boolean mDimensionsChanged = false;
467        boolean mPaddingChanged = false;
468
469        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
470            mInfo = info;
471            mWallpaper = wallpaper;
472        }
473
474        @Override
475        public void onServiceConnected(ComponentName name, IBinder service) {
476            synchronized (mLock) {
477                if (mWallpaper.connection == this) {
478                    mService = IWallpaperService.Stub.asInterface(service);
479                    attachServiceLocked(this, mWallpaper);
480                    // XXX should probably do saveSettingsLocked() later
481                    // when we have an engine, but I'm not sure about
482                    // locking there and anyway we always need to be able to
483                    // recover if there is something wrong.
484                    saveSettingsLocked(mWallpaper.userId);
485                }
486            }
487        }
488
489        @Override
490        public void onServiceDisconnected(ComponentName name) {
491            synchronized (mLock) {
492                mService = null;
493                mEngine = null;
494                if (mWallpaper.connection == this) {
495                    Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
496                    if (!mWallpaper.wallpaperUpdating
497                            && mWallpaper.userId == mCurrentUserId) {
498                        // There is a race condition which causes
499                        // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
500                        // currently updating since the broadcast notifying us is async.
501                        // This race is overcome by the general rule that we only reset the
502                        // wallpaper if its service was shut down twice
503                        // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
504                        if (mWallpaper.lastDiedTime != 0
505                                && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
506                                    > SystemClock.uptimeMillis()) {
507                            Slog.w(TAG, "Reverting to built-in wallpaper!");
508                            clearWallpaperLocked(true, FLAG_SET_SYSTEM, mWallpaper.userId, null);
509                        } else {
510                            mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
511                        }
512                        final String flattened = name.flattenToString();
513                        EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
514                                flattened.substring(0, Math.min(flattened.length(),
515                                        MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
516                    }
517                }
518            }
519        }
520
521        @Override
522        public void attachEngine(IWallpaperEngine engine) {
523            synchronized (mLock) {
524                mEngine = engine;
525                if (mDimensionsChanged) {
526                    try {
527                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
528                    } catch (RemoteException e) {
529                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
530                    }
531                    mDimensionsChanged = false;
532                }
533                if (mPaddingChanged) {
534                    try {
535                        mEngine.setDisplayPadding(mWallpaper.padding);
536                    } catch (RemoteException e) {
537                        Slog.w(TAG, "Failed to set wallpaper padding", e);
538                    }
539                    mPaddingChanged = false;
540                }
541            }
542        }
543
544        @Override
545        public void engineShown(IWallpaperEngine engine) {
546            synchronized (mLock) {
547                if (mReply != null) {
548                    long ident = Binder.clearCallingIdentity();
549                    try {
550                        mReply.sendResult(null);
551                    } catch (RemoteException e) {
552                        Binder.restoreCallingIdentity(ident);
553                    }
554                    mReply = null;
555                }
556            }
557        }
558
559        @Override
560        public ParcelFileDescriptor setWallpaper(String name) {
561            synchronized (mLock) {
562                if (mWallpaper.connection == this) {
563                    return updateWallpaperBitmapLocked(name, mWallpaper, null);
564                }
565                return null;
566            }
567        }
568    }
569
570    class MyPackageMonitor extends PackageMonitor {
571        @Override
572        public void onPackageUpdateFinished(String packageName, int uid) {
573            synchronized (mLock) {
574                if (mCurrentUserId != getChangingUserId()) {
575                    return;
576                }
577                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
578                if (wallpaper != null) {
579                    if (wallpaper.wallpaperComponent != null
580                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
581                        wallpaper.wallpaperUpdating = false;
582                        ComponentName comp = wallpaper.wallpaperComponent;
583                        clearWallpaperComponentLocked(wallpaper);
584                        if (!bindWallpaperComponentLocked(comp, false, false,
585                                wallpaper, null)) {
586                            Slog.w(TAG, "Wallpaper no longer available; reverting to default");
587                            clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
588                        }
589                    }
590                }
591            }
592        }
593
594        @Override
595        public void onPackageModified(String packageName) {
596            synchronized (mLock) {
597                if (mCurrentUserId != getChangingUserId()) {
598                    return;
599                }
600                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
601                if (wallpaper != null) {
602                    if (wallpaper.wallpaperComponent == null
603                            || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
604                        return;
605                    }
606                    doPackagesChangedLocked(true, wallpaper);
607                }
608            }
609        }
610
611        @Override
612        public void onPackageUpdateStarted(String packageName, int uid) {
613            synchronized (mLock) {
614                if (mCurrentUserId != getChangingUserId()) {
615                    return;
616                }
617                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
618                if (wallpaper != null) {
619                    if (wallpaper.wallpaperComponent != null
620                            && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
621                        wallpaper.wallpaperUpdating = true;
622                    }
623                }
624            }
625        }
626
627        @Override
628        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
629            synchronized (mLock) {
630                boolean changed = false;
631                if (mCurrentUserId != getChangingUserId()) {
632                    return false;
633                }
634                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
635                if (wallpaper != null) {
636                    boolean res = doPackagesChangedLocked(doit, wallpaper);
637                    changed |= res;
638                }
639                return changed;
640            }
641        }
642
643        @Override
644        public void onSomePackagesChanged() {
645            synchronized (mLock) {
646                if (mCurrentUserId != getChangingUserId()) {
647                    return;
648                }
649                WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
650                if (wallpaper != null) {
651                    doPackagesChangedLocked(true, wallpaper);
652                }
653            }
654        }
655
656        boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
657            boolean changed = false;
658            if (wallpaper.wallpaperComponent != null) {
659                int change = isPackageDisappearing(wallpaper.wallpaperComponent
660                        .getPackageName());
661                if (change == PACKAGE_PERMANENT_CHANGE
662                        || change == PACKAGE_TEMPORARY_CHANGE) {
663                    changed = true;
664                    if (doit) {
665                        Slog.w(TAG, "Wallpaper uninstalled, removing: "
666                                + wallpaper.wallpaperComponent);
667                        clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
668                    }
669                }
670            }
671            if (wallpaper.nextWallpaperComponent != null) {
672                int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
673                        .getPackageName());
674                if (change == PACKAGE_PERMANENT_CHANGE
675                        || change == PACKAGE_TEMPORARY_CHANGE) {
676                    wallpaper.nextWallpaperComponent = null;
677                }
678            }
679            if (wallpaper.wallpaperComponent != null
680                    && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
681                try {
682                    mContext.getPackageManager().getServiceInfo(
683                            wallpaper.wallpaperComponent, 0);
684                } catch (NameNotFoundException e) {
685                    Slog.w(TAG, "Wallpaper component gone, removing: "
686                            + wallpaper.wallpaperComponent);
687                    clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
688                }
689            }
690            if (wallpaper.nextWallpaperComponent != null
691                    && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
692                try {
693                    mContext.getPackageManager().getServiceInfo(
694                            wallpaper.nextWallpaperComponent, 0);
695                } catch (NameNotFoundException e) {
696                    wallpaper.nextWallpaperComponent = null;
697                }
698            }
699            return changed;
700        }
701    }
702
703    public WallpaperManagerService(Context context) {
704        if (DEBUG) Slog.v(TAG, "WallpaperService startup");
705        mContext = context;
706        mImageWallpaper = ComponentName.unflattenFromString(
707                context.getResources().getString(R.string.image_wallpaper_component));
708        mIWindowManager = IWindowManager.Stub.asInterface(
709                ServiceManager.getService(Context.WINDOW_SERVICE));
710        mIPackageManager = AppGlobals.getPackageManager();
711        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
712        mMonitor = new MyPackageMonitor();
713        mMonitor.register(context, null, UserHandle.ALL, true);
714        getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
715        loadSettingsLocked(UserHandle.USER_SYSTEM);
716    }
717
718    private static File getWallpaperDir(int userId) {
719        return Environment.getUserSystemDirectory(userId);
720    }
721
722    @Override
723    protected void finalize() throws Throwable {
724        super.finalize();
725        for (int i = 0; i < mWallpaperMap.size(); i++) {
726            WallpaperData wallpaper = mWallpaperMap.valueAt(i);
727            wallpaper.wallpaperObserver.stopWatching();
728        }
729    }
730
731    public void systemRunning() {
732        if (DEBUG) Slog.v(TAG, "systemReady");
733        WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
734        // If we think we're going to be using the system image wallpaper imagery, make
735        // sure we have something to render
736        if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
737            // No crop file? Make sure we've finished the processing sequence if necessary
738            if (!wallpaper.cropExists()) {
739                if (DEBUG) {
740                    Slog.i(TAG, "No crop; regenerating from source");
741                }
742                generateCrop(wallpaper);
743            }
744            // Still nothing?  Fall back to default.
745            if (!wallpaper.cropExists()) {
746                if (DEBUG) {
747                    Slog.i(TAG, "Unable to regenerate crop; resetting");
748                }
749                clearWallpaperLocked(false, FLAG_SET_SYSTEM, UserHandle.USER_SYSTEM, null);
750            }
751        } else {
752            if (DEBUG) {
753                Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
754            }
755        }
756        switchWallpaper(wallpaper, null);
757        wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
758        wallpaper.wallpaperObserver.startWatching();
759
760        IntentFilter userFilter = new IntentFilter();
761        userFilter.addAction(Intent.ACTION_USER_REMOVED);
762        userFilter.addAction(Intent.ACTION_USER_STOPPING);
763        mContext.registerReceiver(new BroadcastReceiver() {
764            @Override
765            public void onReceive(Context context, Intent intent) {
766                String action = intent.getAction();
767                if (Intent.ACTION_USER_REMOVED.equals(action)) {
768                    onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
769                            UserHandle.USER_NULL));
770                }
771                // TODO: Race condition causing problems when cleaning up on stopping a user.
772                // Comment this out for now.
773                // else if (Intent.ACTION_USER_STOPPING.equals(action)) {
774                //     onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
775                //             UserHandle.USER_NULL));
776                // }
777            }
778        }, userFilter);
779
780        try {
781            ActivityManagerNative.getDefault().registerUserSwitchObserver(
782                    new IUserSwitchObserver.Stub() {
783                        @Override
784                        public void onUserSwitching(int newUserId, IRemoteCallback reply) {
785                            switchUser(newUserId, reply);
786                        }
787
788                        @Override
789                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
790                        }
791
792                        @Override
793                        public void onForegroundProfileSwitch(int newProfileId) {
794                            // Ignore.
795                        }
796                    });
797        } catch (RemoteException e) {
798            // TODO Auto-generated catch block
799            e.printStackTrace();
800        }
801    }
802
803    /** Called by SystemBackupAgent */
804    public String getName() {
805        // Verify caller is the system
806        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
807            throw new RuntimeException("getName() can only be called from the system process");
808        }
809        synchronized (mLock) {
810            return mWallpaperMap.get(0).name;
811        }
812    }
813
814    void stopObserver(WallpaperData wallpaper) {
815        if (wallpaper != null) {
816            if (wallpaper.wallpaperObserver != null) {
817                wallpaper.wallpaperObserver.stopWatching();
818                wallpaper.wallpaperObserver = null;
819            }
820        }
821    }
822
823    void stopObserversLocked(int userId) {
824        stopObserver(mWallpaperMap.get(userId));
825        stopObserver(mLockWallpaperMap.get(userId));
826        mWallpaperMap.remove(userId);
827        mLockWallpaperMap.remove(userId);
828    }
829
830    void onRemoveUser(int userId) {
831        if (userId < 1) return;
832
833        final File wallpaperDir = getWallpaperDir(userId);
834        synchronized (mLock) {
835            stopObserversLocked(userId);
836            for (String filename : sPerUserFiles) {
837                new File(wallpaperDir, filename).delete();
838            }
839        }
840    }
841
842    void switchUser(int userId, IRemoteCallback reply) {
843        synchronized (mLock) {
844            mCurrentUserId = userId;
845            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
846            // Not started watching yet, in case wallpaper data was loaded for other reasons.
847            if (wallpaper.wallpaperObserver == null) {
848                wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
849                wallpaper.wallpaperObserver.startWatching();
850            }
851            switchWallpaper(wallpaper, reply);
852        }
853    }
854
855    void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
856        synchronized (mLock) {
857            RuntimeException e = null;
858            try {
859                ComponentName cname = wallpaper.wallpaperComponent != null ?
860                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
861                if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
862                    return;
863                }
864            } catch (RuntimeException e1) {
865                e = e1;
866            }
867            Slog.w(TAG, "Failure starting previous wallpaper", e);
868            clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, reply);
869        }
870    }
871
872    @Override
873    public void clearWallpaper(String callingPackage, int which, int userId) {
874        if (DEBUG) Slog.v(TAG, "clearWallpaper");
875        checkPermission(android.Manifest.permission.SET_WALLPAPER);
876        if (!isWallpaperSupported(callingPackage) || !isWallpaperSettingAllowed(callingPackage)) {
877            return;
878        }
879        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
880                Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
881
882        synchronized (mLock) {
883            clearWallpaperLocked(false, which, userId, null);
884        }
885    }
886
887    void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
888        if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
889            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
890        }
891
892        WallpaperData wallpaper = null;
893        if (which == FLAG_SET_LOCK) {
894            wallpaper = mLockWallpaperMap.get(userId);
895            if (wallpaper == null) {
896                // It's already gone; we're done.
897                if (DEBUG) {
898                    Slog.i(TAG, "Lock wallpaper already cleared");
899                }
900                return;
901            }
902        } else {
903            wallpaper = mWallpaperMap.get(userId);
904            if (wallpaper == null) {
905                // Might need to bring it in the first time to establish our rewrite
906                loadSettingsLocked(userId);
907                wallpaper = mWallpaperMap.get(userId);
908            }
909        }
910        if (wallpaper == null) {
911            return;
912        }
913
914        final long ident = Binder.clearCallingIdentity();
915        try {
916            if (wallpaper.wallpaperFile.exists()) {
917                wallpaper.wallpaperFile.delete();
918                wallpaper.cropFile.delete();
919                if (which == FLAG_SET_LOCK) {
920                    mLockWallpaperMap.remove(userId);
921                    final IWallpaperManagerCallback cb = mKeyguardListener;
922                    if (cb != null) {
923                        if (DEBUG) {
924                            Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
925                        }
926                        try {
927                            cb.onWallpaperChanged();
928                        } catch (RemoteException e) {
929                            // Oh well it went away; no big deal
930                        }
931                    }
932                    saveSettingsLocked(userId);
933                    return;
934                }
935            }
936
937            RuntimeException e = null;
938            try {
939                wallpaper.imageWallpaperPending = false;
940                if (userId != mCurrentUserId) return;
941                if (bindWallpaperComponentLocked(defaultFailed
942                        ? mImageWallpaper
943                                : null, true, false, wallpaper, reply)) {
944                    return;
945                }
946            } catch (IllegalArgumentException e1) {
947                e = e1;
948            }
949
950            // This can happen if the default wallpaper component doesn't
951            // exist.  This should be a system configuration problem, but
952            // let's not let it crash the system and just live with no
953            // wallpaper.
954            Slog.e(TAG, "Default wallpaper component not found!", e);
955            clearWallpaperComponentLocked(wallpaper);
956            if (reply != null) {
957                try {
958                    reply.sendResult(null);
959                } catch (RemoteException e1) {
960                }
961            }
962        } finally {
963            Binder.restoreCallingIdentity(ident);
964        }
965    }
966
967    public boolean hasNamedWallpaper(String name) {
968        synchronized (mLock) {
969            List<UserInfo> users;
970            long ident = Binder.clearCallingIdentity();
971            try {
972                users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
973            } finally {
974                Binder.restoreCallingIdentity(ident);
975            }
976            for (UserInfo user: users) {
977                // ignore managed profiles
978                if (user.isManagedProfile()) {
979                    continue;
980                }
981                WallpaperData wd = mWallpaperMap.get(user.id);
982                if (wd == null) {
983                    // User hasn't started yet, so load her settings to peek at the wallpaper
984                    loadSettingsLocked(user.id);
985                    wd = mWallpaperMap.get(user.id);
986                }
987                if (wd != null && name.equals(wd.name)) {
988                    return true;
989                }
990            }
991        }
992        return false;
993    }
994
995    private Point getDefaultDisplaySize() {
996        Point p = new Point();
997        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
998        Display d = wm.getDefaultDisplay();
999        d.getRealSize(p);
1000        return p;
1001    }
1002
1003    public void setDimensionHints(int width, int height, String callingPackage)
1004            throws RemoteException {
1005        checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1006        if (!isWallpaperSupported(callingPackage)) {
1007            return;
1008        }
1009        synchronized (mLock) {
1010            int userId = UserHandle.getCallingUserId();
1011            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
1012            if (width <= 0 || height <= 0) {
1013                throw new IllegalArgumentException("width and height must be > 0");
1014            }
1015            // Make sure it is at least as large as the display.
1016            Point displaySize = getDefaultDisplaySize();
1017            width = Math.max(width, displaySize.x);
1018            height = Math.max(height, displaySize.y);
1019
1020            if (width != wallpaper.width || height != wallpaper.height) {
1021                wallpaper.width = width;
1022                wallpaper.height = height;
1023                saveSettingsLocked(userId);
1024                if (mCurrentUserId != userId) return; // Don't change the properties now
1025                if (wallpaper.connection != null) {
1026                    if (wallpaper.connection.mEngine != null) {
1027                        try {
1028                            wallpaper.connection.mEngine.setDesiredSize(
1029                                    width, height);
1030                        } catch (RemoteException e) {
1031                        }
1032                        notifyCallbacksLocked(wallpaper);
1033                    } else if (wallpaper.connection.mService != null) {
1034                        // We've attached to the service but the engine hasn't attached back to us
1035                        // yet. This means it will be created with the previous dimensions, so we
1036                        // need to update it to the new dimensions once it attaches.
1037                        wallpaper.connection.mDimensionsChanged = true;
1038                    }
1039                }
1040            }
1041        }
1042    }
1043
1044    public int getWidthHint() throws RemoteException {
1045        synchronized (mLock) {
1046            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1047            if (wallpaper != null) {
1048                return wallpaper.width;
1049            } else {
1050                return 0;
1051            }
1052        }
1053    }
1054
1055    public int getHeightHint() throws RemoteException {
1056        synchronized (mLock) {
1057            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1058            if (wallpaper != null) {
1059                return wallpaper.height;
1060            } else {
1061                return 0;
1062            }
1063        }
1064    }
1065
1066    public void setDisplayPadding(Rect padding, String callingPackage) {
1067        checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1068        if (!isWallpaperSupported(callingPackage)) {
1069            return;
1070        }
1071        synchronized (mLock) {
1072            int userId = UserHandle.getCallingUserId();
1073            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
1074            if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
1075                throw new IllegalArgumentException("padding must be positive: " + padding);
1076            }
1077
1078            if (!padding.equals(wallpaper.padding)) {
1079                wallpaper.padding.set(padding);
1080                saveSettingsLocked(userId);
1081                if (mCurrentUserId != userId) return; // Don't change the properties now
1082                if (wallpaper.connection != null) {
1083                    if (wallpaper.connection.mEngine != null) {
1084                        try {
1085                            wallpaper.connection.mEngine.setDisplayPadding(padding);
1086                        } catch (RemoteException e) {
1087                        }
1088                        notifyCallbacksLocked(wallpaper);
1089                    } else if (wallpaper.connection.mService != null) {
1090                        // We've attached to the service but the engine hasn't attached back to us
1091                        // yet. This means it will be created with the previous dimensions, so we
1092                        // need to update it to the new dimensions once it attaches.
1093                        wallpaper.connection.mPaddingChanged = true;
1094                    }
1095                }
1096            }
1097        }
1098    }
1099
1100    @Override
1101    public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, final int which,
1102            Bundle outParams, int wallpaperUserId) {
1103        wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1104                Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
1105
1106        if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
1107            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
1108        }
1109
1110        synchronized (mLock) {
1111            final SparseArray<WallpaperData> whichSet =
1112                    (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1113            WallpaperData wallpaper = whichSet.get(wallpaperUserId);
1114            if (wallpaper == null) {
1115                // common case, this is the first lookup post-boot of the system or
1116                // unified lock, so we bring up the saved state lazily now and recheck.
1117                loadSettingsLocked(wallpaperUserId);
1118                wallpaper = whichSet.get(wallpaperUserId);
1119                if (wallpaper == null) {
1120                    return null;
1121                }
1122            }
1123            try {
1124                if (outParams != null) {
1125                    outParams.putInt("width", wallpaper.width);
1126                    outParams.putInt("height", wallpaper.height);
1127                }
1128                if (cb != null) {
1129                    wallpaper.callbacks.register(cb);
1130                }
1131                if (!wallpaper.cropFile.exists()) {
1132                    return null;
1133                }
1134                return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
1135            } catch (FileNotFoundException e) {
1136                /* Shouldn't happen as we check to see if the file exists */
1137                Slog.w(TAG, "Error getting wallpaper", e);
1138            }
1139            return null;
1140        }
1141    }
1142
1143    @Override
1144    public WallpaperInfo getWallpaperInfo() {
1145        int userId = UserHandle.getCallingUserId();
1146        synchronized (mLock) {
1147            WallpaperData wallpaper = mWallpaperMap.get(userId);
1148            if (wallpaper != null && wallpaper.connection != null) {
1149                return wallpaper.connection.mInfo;
1150            }
1151            return null;
1152        }
1153    }
1154
1155    @Override
1156    public int getWallpaperIdForUser(int which, int userId) {
1157        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1158                Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
1159
1160        if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
1161            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
1162        }
1163
1164        final SparseArray<WallpaperData> map =
1165                (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1166        synchronized (mLock) {
1167            WallpaperData wallpaper = map.get(userId);
1168            if (wallpaper != null) {
1169                return wallpaper.wallpaperId;
1170            }
1171        }
1172        return -1;
1173    }
1174
1175    @Override
1176    public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
1177        checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
1178        synchronized (mLock) {
1179            mKeyguardListener = cb;
1180        }
1181        return true;
1182    }
1183
1184    @Override
1185    public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
1186            Rect cropHint, Bundle extras, int which, IWallpaperManagerCallback completion) {
1187        checkPermission(android.Manifest.permission.SET_WALLPAPER);
1188
1189        if ((which & (FLAG_SET_LOCK|FLAG_SET_SYSTEM)) == 0) {
1190            Slog.e(TAG, "Must specify a valid wallpaper category to set");
1191            return null;
1192        }
1193
1194        if (!isWallpaperSupported(callingPackage) || !isWallpaperSettingAllowed(callingPackage)) {
1195            return null;
1196        }
1197
1198        // "null" means the no-op crop, preserving the full input image
1199        if (cropHint == null) {
1200            cropHint = new Rect(0, 0, 0, 0);
1201        } else {
1202            if (cropHint.isEmpty()
1203                    || cropHint.left < 0
1204                    || cropHint.top < 0) {
1205                return null;
1206            }
1207        }
1208
1209        final int userId = UserHandle.getCallingUserId();
1210
1211        synchronized (mLock) {
1212            if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
1213            WallpaperData wallpaper;
1214
1215            wallpaper = getWallpaperSafeLocked(userId, which);
1216            final long ident = Binder.clearCallingIdentity();
1217            try {
1218                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
1219                if (pfd != null) {
1220                    wallpaper.imageWallpaperPending = true;
1221                    wallpaper.whichPending = which;
1222                    wallpaper.setComplete = completion;
1223                    wallpaper.cropHint.set(cropHint);
1224                }
1225                return pfd;
1226            } finally {
1227                Binder.restoreCallingIdentity(ident);
1228            }
1229        }
1230    }
1231
1232    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
1233            Bundle extras) {
1234        if (name == null) name = "";
1235        try {
1236            File dir = getWallpaperDir(wallpaper.userId);
1237            if (!dir.exists()) {
1238                dir.mkdir();
1239                FileUtils.setPermissions(
1240                        dir.getPath(),
1241                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
1242                        -1, -1);
1243            }
1244            ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
1245                    MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
1246            if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
1247                return null;
1248            }
1249            wallpaper.name = name;
1250            wallpaper.wallpaperId = makeWallpaperIdLocked();
1251            if (extras != null) {
1252                extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
1253            }
1254            if (DEBUG) {
1255                Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
1256                        + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
1257            }
1258            return fd;
1259        } catch (FileNotFoundException e) {
1260            Slog.w(TAG, "Error setting wallpaper", e);
1261        }
1262        return null;
1263    }
1264
1265    @Override
1266    public void setWallpaperComponentChecked(ComponentName name, String callingPackage) {
1267        if (isWallpaperSupported(callingPackage) && isWallpaperSettingAllowed(callingPackage)) {
1268            setWallpaperComponent(name);
1269        }
1270    }
1271
1272    // ToDo: Remove this version of the function
1273    @Override
1274    public void setWallpaperComponent(ComponentName name) {
1275        checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
1276        synchronized (mLock) {
1277            if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
1278            int userId = UserHandle.getCallingUserId();
1279            WallpaperData wallpaper = mWallpaperMap.get(userId);
1280            if (wallpaper == null) {
1281                throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
1282            }
1283            final long ident = Binder.clearCallingIdentity();
1284            try {
1285                wallpaper.imageWallpaperPending = false;
1286                if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
1287                    wallpaper.wallpaperId = makeWallpaperIdLocked();
1288                }
1289            } finally {
1290                Binder.restoreCallingIdentity(ident);
1291            }
1292        }
1293    }
1294
1295    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
1296            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
1297        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
1298        // Has the component changed?
1299        if (!force) {
1300            if (wallpaper.connection != null) {
1301                if (wallpaper.wallpaperComponent == null) {
1302                    if (componentName == null) {
1303                        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
1304                        // Still using default wallpaper.
1305                        return true;
1306                    }
1307                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
1308                    // Changing to same wallpaper.
1309                    if (DEBUG) Slog.v(TAG, "same wallpaper");
1310                    return true;
1311                }
1312            }
1313        }
1314
1315        try {
1316            if (componentName == null) {
1317                componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
1318                if (componentName == null) {
1319                    // Fall back to static image wallpaper
1320                    componentName = mImageWallpaper;
1321                    //clearWallpaperComponentLocked();
1322                    //return;
1323                    if (DEBUG) Slog.v(TAG, "Using image wallpaper");
1324                }
1325            }
1326            int serviceUserId = wallpaper.userId;
1327            ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
1328                    PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
1329            if (si == null) {
1330                // The wallpaper component we're trying to use doesn't exist
1331                Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
1332                return false;
1333            }
1334            if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
1335                String msg = "Selected service does not require "
1336                        + android.Manifest.permission.BIND_WALLPAPER
1337                        + ": " + componentName;
1338                if (fromUser) {
1339                    throw new SecurityException(msg);
1340                }
1341                Slog.w(TAG, msg);
1342                return false;
1343            }
1344
1345            WallpaperInfo wi = null;
1346
1347            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
1348            if (componentName != null && !componentName.equals(mImageWallpaper)) {
1349                // Make sure the selected service is actually a wallpaper service.
1350                List<ResolveInfo> ris =
1351                        mIPackageManager.queryIntentServices(intent,
1352                                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1353                                PackageManager.GET_META_DATA, serviceUserId).getList();
1354                for (int i=0; i<ris.size(); i++) {
1355                    ServiceInfo rsi = ris.get(i).serviceInfo;
1356                    if (rsi.name.equals(si.name) &&
1357                            rsi.packageName.equals(si.packageName)) {
1358                        try {
1359                            wi = new WallpaperInfo(mContext, ris.get(i));
1360                        } catch (XmlPullParserException e) {
1361                            if (fromUser) {
1362                                throw new IllegalArgumentException(e);
1363                            }
1364                            Slog.w(TAG, e);
1365                            return false;
1366                        } catch (IOException e) {
1367                            if (fromUser) {
1368                                throw new IllegalArgumentException(e);
1369                            }
1370                            Slog.w(TAG, e);
1371                            return false;
1372                        }
1373                        break;
1374                    }
1375                }
1376                if (wi == null) {
1377                    String msg = "Selected service is not a wallpaper: "
1378                            + componentName;
1379                    if (fromUser) {
1380                        throw new SecurityException(msg);
1381                    }
1382                    Slog.w(TAG, msg);
1383                    return false;
1384                }
1385            }
1386
1387            // Bind the service!
1388            if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
1389            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
1390            intent.setComponent(componentName);
1391            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1392                    com.android.internal.R.string.wallpaper_binding_label);
1393            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1394                    mContext, 0,
1395                    Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
1396                            mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
1397                    0, null, new UserHandle(serviceUserId)));
1398            if (!mContext.bindServiceAsUser(intent, newConn,
1399                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
1400                            | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
1401                    new UserHandle(serviceUserId))) {
1402                String msg = "Unable to bind service: "
1403                        + componentName;
1404                if (fromUser) {
1405                    throw new IllegalArgumentException(msg);
1406                }
1407                Slog.w(TAG, msg);
1408                return false;
1409            }
1410            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
1411                detachWallpaperLocked(mLastWallpaper);
1412            }
1413            wallpaper.wallpaperComponent = componentName;
1414            wallpaper.connection = newConn;
1415            newConn.mReply = reply;
1416            try {
1417                if (wallpaper.userId == mCurrentUserId) {
1418                    if (DEBUG)
1419                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
1420                    mIWindowManager.addWindowToken(newConn.mToken,
1421                            WindowManager.LayoutParams.TYPE_WALLPAPER);
1422                    mLastWallpaper = wallpaper;
1423                }
1424            } catch (RemoteException e) {
1425            }
1426        } catch (RemoteException e) {
1427            String msg = "Remote exception for " + componentName + "\n" + e;
1428            if (fromUser) {
1429                throw new IllegalArgumentException(msg);
1430            }
1431            Slog.w(TAG, msg);
1432            return false;
1433        }
1434        return true;
1435    }
1436
1437    void detachWallpaperLocked(WallpaperData wallpaper) {
1438        if (wallpaper.connection != null) {
1439            if (wallpaper.connection.mReply != null) {
1440                try {
1441                    wallpaper.connection.mReply.sendResult(null);
1442                } catch (RemoteException e) {
1443                }
1444                wallpaper.connection.mReply = null;
1445            }
1446            if (wallpaper.connection.mEngine != null) {
1447                try {
1448                    wallpaper.connection.mEngine.destroy();
1449                } catch (RemoteException e) {
1450                }
1451            }
1452            mContext.unbindService(wallpaper.connection);
1453            try {
1454                if (DEBUG)
1455                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
1456                mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
1457            } catch (RemoteException e) {
1458            }
1459            wallpaper.connection.mService = null;
1460            wallpaper.connection.mEngine = null;
1461            wallpaper.connection = null;
1462        }
1463    }
1464
1465    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
1466        wallpaper.wallpaperComponent = null;
1467        detachWallpaperLocked(wallpaper);
1468    }
1469
1470    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
1471        try {
1472            conn.mService.attach(conn, conn.mToken,
1473                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
1474                    wallpaper.width, wallpaper.height, wallpaper.padding);
1475        } catch (RemoteException e) {
1476            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
1477            if (!wallpaper.wallpaperUpdating) {
1478                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
1479            }
1480        }
1481    }
1482
1483    private void notifyCallbacksLocked(WallpaperData wallpaper) {
1484        final int n = wallpaper.callbacks.beginBroadcast();
1485        for (int i = 0; i < n; i++) {
1486            try {
1487                wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
1488            } catch (RemoteException e) {
1489
1490                // The RemoteCallbackList will take care of removing
1491                // the dead object for us.
1492            }
1493        }
1494        wallpaper.callbacks.finishBroadcast();
1495        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
1496        mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
1497    }
1498
1499    private void checkPermission(String permission) {
1500        if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
1501            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
1502                    + ", must have permission " + permission);
1503        }
1504    }
1505
1506    /**
1507     * Certain user types do not support wallpapers (e.g. managed profiles). The check is
1508     * implemented through through the OP_WRITE_WALLPAPER AppOp.
1509     */
1510    public boolean isWallpaperSupported(String callingPackage) {
1511        return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
1512                callingPackage) == AppOpsManager.MODE_ALLOWED;
1513    }
1514
1515    @Override
1516    public boolean isWallpaperSettingAllowed(String callingPackage) {
1517        final PackageManager pm = mContext.getPackageManager();
1518        String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
1519        boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
1520        if (!uidMatchPackage) {
1521            return false;   // callingPackage was faked.
1522        }
1523
1524        final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
1525        if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
1526            return true;
1527        }
1528        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1529        return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
1530    }
1531
1532    private static JournaledFile makeJournaledFile(int userId) {
1533        final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
1534        return new JournaledFile(new File(base), new File(base + ".tmp"));
1535    }
1536
1537    private void saveSettingsLocked(int userId) {
1538        JournaledFile journal = makeJournaledFile(userId);
1539        FileOutputStream fstream = null;
1540        BufferedOutputStream stream = null;
1541        try {
1542            XmlSerializer out = new FastXmlSerializer();
1543            fstream = new FileOutputStream(journal.chooseForWrite(), false);
1544            stream = new BufferedOutputStream(fstream);
1545            out.setOutput(stream, StandardCharsets.UTF_8.name());
1546            out.startDocument(null, true);
1547
1548            WallpaperData wallpaper;
1549
1550            wallpaper = mWallpaperMap.get(userId);
1551            if (wallpaper != null) {
1552                writeWallpaperAttributes(out, "wp", wallpaper);
1553            }
1554            wallpaper = mLockWallpaperMap.get(userId);
1555            if (wallpaper != null) {
1556                writeWallpaperAttributes(out, "kwp", wallpaper);
1557            }
1558
1559            out.endDocument();
1560
1561            stream.flush(); // also flushes fstream
1562            FileUtils.sync(fstream);
1563            stream.close(); // also closes fstream
1564            journal.commit();
1565        } catch (IOException e) {
1566            IoUtils.closeQuietly(stream);
1567            journal.rollback();
1568        }
1569    }
1570
1571    private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
1572            throws IllegalArgumentException, IllegalStateException, IOException {
1573        out.startTag(null, tag);
1574        out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
1575        out.attribute(null, "width", Integer.toString(wallpaper.width));
1576        out.attribute(null, "height", Integer.toString(wallpaper.height));
1577
1578        out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
1579        out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
1580        out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
1581        out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
1582
1583        if (wallpaper.padding.left != 0) {
1584            out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
1585        }
1586        if (wallpaper.padding.top != 0) {
1587            out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
1588        }
1589        if (wallpaper.padding.right != 0) {
1590            out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
1591        }
1592        if (wallpaper.padding.bottom != 0) {
1593            out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
1594        }
1595
1596        out.attribute(null, "name", wallpaper.name);
1597        if (wallpaper.wallpaperComponent != null
1598                && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
1599            out.attribute(null, "component",
1600                    wallpaper.wallpaperComponent.flattenToShortString());
1601        }
1602        out.endTag(null, tag);
1603    }
1604
1605    private void migrateFromOld() {
1606        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
1607        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
1608        if (oldWallpaper.exists()) {
1609            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
1610            oldWallpaper.renameTo(newWallpaper);
1611        }
1612        if (oldInfo.exists()) {
1613            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
1614            oldInfo.renameTo(newInfo);
1615        }
1616    }
1617
1618    private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
1619        String value = parser.getAttributeValue(null, name);
1620        if (value == null) {
1621            return defValue;
1622        }
1623        return Integer.parseInt(value);
1624    }
1625
1626    /**
1627     * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
1628     * happen during user switch.  The async user switch observer may not have received
1629     * the event yet.  We use this safe method when we don't care about this ordering and just
1630     * want to update the data.  The data is going to be applied when the user switch observer
1631     * is eventually executed.
1632     */
1633    private WallpaperData getWallpaperSafeLocked(int userId, int which) {
1634        // We're setting either just system (work with the system wallpaper),
1635        // both (also work with the system wallpaper), or just the lock
1636        // wallpaper (update against the existing lock wallpaper if any).
1637        // Combined or just-system operations use the 'system' WallpaperData
1638        // for this use; lock-only operations use the dedicated one.
1639        final SparseArray<WallpaperData> whichSet =
1640                (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1641        WallpaperData wallpaper = whichSet.get(userId);
1642        if (wallpaper == null) {
1643            // common case, this is the first lookup post-boot of the system or
1644            // unified lock, so we bring up the saved state lazily now and recheck.
1645            loadSettingsLocked(userId);
1646            wallpaper = whichSet.get(userId);
1647            // if it's still null here, this is a lock-only operation and there is not
1648            // yet a lock-only wallpaper set for this user, so we need to establish
1649            // it now.
1650            if (wallpaper == null) {
1651                if (which == FLAG_SET_LOCK) {
1652                    wallpaper = new WallpaperData(userId,
1653                            WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1654                    mLockWallpaperMap.put(userId, wallpaper);
1655                } else {
1656                    // sanity fallback: we're in bad shape, but establishing a known
1657                    // valid system+lock WallpaperData will keep us from dying.
1658                    Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
1659                    wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
1660                    mWallpaperMap.put(userId, wallpaper);
1661                }
1662            }
1663        }
1664        return wallpaper;
1665    }
1666
1667    private void loadSettingsLocked(int userId) {
1668        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
1669
1670        JournaledFile journal = makeJournaledFile(userId);
1671        FileInputStream stream = null;
1672        File file = journal.chooseForRead();
1673        if (!file.exists()) {
1674            // This should only happen one time, when upgrading from a legacy system
1675            migrateFromOld();
1676        }
1677        WallpaperData wallpaper = mWallpaperMap.get(userId);
1678        if (wallpaper == null) {
1679            wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
1680            mWallpaperMap.put(userId, wallpaper);
1681            if (!wallpaper.cropExists()) {
1682                generateCrop(wallpaper);
1683            }
1684        }
1685        boolean success = false;
1686        try {
1687            stream = new FileInputStream(file);
1688            XmlPullParser parser = Xml.newPullParser();
1689            parser.setInput(stream, StandardCharsets.UTF_8.name());
1690
1691            int type;
1692            do {
1693                type = parser.next();
1694                if (type == XmlPullParser.START_TAG) {
1695                    String tag = parser.getName();
1696                    if ("wp".equals(tag)) {
1697                        // Common to system + lock wallpapers
1698                        parseWallpaperAttributes(parser, wallpaper);
1699
1700                        // A system wallpaper might also be a live wallpaper
1701                        String comp = parser.getAttributeValue(null, "component");
1702                        wallpaper.nextWallpaperComponent = comp != null
1703                                ? ComponentName.unflattenFromString(comp)
1704                                : null;
1705                        if (wallpaper.nextWallpaperComponent == null
1706                                || "android".equals(wallpaper.nextWallpaperComponent
1707                                        .getPackageName())) {
1708                            wallpaper.nextWallpaperComponent = mImageWallpaper;
1709                        }
1710
1711                        if (DEBUG) {
1712                            Slog.v(TAG, "mWidth:" + wallpaper.width);
1713                            Slog.v(TAG, "mHeight:" + wallpaper.height);
1714                            Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
1715                            Slog.v(TAG, "mName:" + wallpaper.name);
1716                            Slog.v(TAG, "mNextWallpaperComponent:"
1717                                    + wallpaper.nextWallpaperComponent);
1718                        }
1719                    } else if ("kwp".equals(tag)) {
1720                        // keyguard-specific wallpaper for this user
1721                        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
1722                        if (lockWallpaper == null) {
1723                            lockWallpaper = new WallpaperData(userId,
1724                                    WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1725                            mLockWallpaperMap.put(userId, lockWallpaper);
1726                        }
1727                        parseWallpaperAttributes(parser, lockWallpaper);
1728                    }
1729                }
1730            } while (type != XmlPullParser.END_DOCUMENT);
1731            success = true;
1732        } catch (FileNotFoundException e) {
1733            Slog.w(TAG, "no current wallpaper -- first boot?");
1734        } catch (NullPointerException e) {
1735            Slog.w(TAG, "failed parsing " + file + " " + e);
1736        } catch (NumberFormatException e) {
1737            Slog.w(TAG, "failed parsing " + file + " " + e);
1738        } catch (XmlPullParserException e) {
1739            Slog.w(TAG, "failed parsing " + file + " " + e);
1740        } catch (IOException e) {
1741            Slog.w(TAG, "failed parsing " + file + " " + e);
1742        } catch (IndexOutOfBoundsException e) {
1743            Slog.w(TAG, "failed parsing " + file + " " + e);
1744        }
1745        IoUtils.closeQuietly(stream);
1746
1747        if (!success) {
1748            wallpaper.width = -1;
1749            wallpaper.height = -1;
1750            wallpaper.cropHint.set(0, 0, 0, 0);
1751            wallpaper.padding.set(0, 0, 0, 0);
1752            wallpaper.name = "";
1753        } else {
1754            if (wallpaper.wallpaperId <= 0) {
1755                wallpaper.wallpaperId = makeWallpaperIdLocked();
1756                if (DEBUG) {
1757                    Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
1758                            + "); now " + wallpaper.wallpaperId);
1759                }
1760            }
1761        }
1762
1763        // We always want to have some reasonable width hint.
1764        int baseSize = getMaximumSizeDimension();
1765        if (wallpaper.width < baseSize) {
1766            wallpaper.width = baseSize;
1767        }
1768        if (wallpaper.height < baseSize) {
1769            wallpaper.height = baseSize;
1770        }
1771        // and crop, if not previously specified
1772        if (wallpaper.cropHint.width() <= 0
1773                || wallpaper.cropHint.height() <= 0) {
1774            wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
1775        }
1776    }
1777
1778    private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper) {
1779        final String idString = parser.getAttributeValue(null, "id");
1780        if (idString != null) {
1781            final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
1782            if (id > mWallpaperId) {
1783                mWallpaperId = id;
1784            }
1785        } else {
1786            wallpaper.wallpaperId = makeWallpaperIdLocked();
1787        }
1788
1789        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
1790        wallpaper.height = Integer.parseInt(parser
1791                .getAttributeValue(null, "height"));
1792        wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
1793        wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
1794        wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
1795        wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
1796        wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
1797        wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
1798        wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
1799        wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
1800        wallpaper.name = parser.getAttributeValue(null, "name");
1801    }
1802
1803    private int getMaximumSizeDimension() {
1804        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
1805        Display d = wm.getDefaultDisplay();
1806        return d.getMaximumSizeDimension();
1807    }
1808
1809    // Called by SystemBackupAgent after files are restored to disk.
1810    public void settingsRestored() {
1811        // Verify caller is the system
1812        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1813            throw new RuntimeException("settingsRestored() can only be called from the system process");
1814        }
1815        // TODO: If necessary, make it work for secondary users as well. This currently assumes
1816        // restores only to the primary user
1817        if (DEBUG) Slog.v(TAG, "settingsRestored");
1818        WallpaperData wallpaper = null;
1819        boolean success = false;
1820        synchronized (mLock) {
1821            loadSettingsLocked(UserHandle.USER_SYSTEM);
1822            wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1823            wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
1824            if (wallpaper.nextWallpaperComponent != null
1825                    && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
1826                if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
1827                        wallpaper, null)) {
1828                    // No such live wallpaper or other failure; fall back to the default
1829                    // live wallpaper (since the profile being restored indicated that the
1830                    // user had selected a live rather than static one).
1831                    bindWallpaperComponentLocked(null, false, false, wallpaper, null);
1832                }
1833                success = true;
1834            } else {
1835                // If there's a wallpaper name, we use that.  If that can't be loaded, then we
1836                // use the default.
1837                if ("".equals(wallpaper.name)) {
1838                    if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
1839                    success = true;
1840                } else {
1841                    if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
1842                    success = restoreNamedResourceLocked(wallpaper);
1843                }
1844                if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
1845                        + " id=" + wallpaper.wallpaperId);
1846                if (success) {
1847                    generateCrop(wallpaper);    // based on the new image + metadata
1848                    bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
1849                            wallpaper, null);
1850                }
1851            }
1852        }
1853
1854        if (!success) {
1855            Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
1856            wallpaper.name = "";
1857            getWallpaperDir(UserHandle.USER_SYSTEM).delete();
1858        }
1859
1860        synchronized (mLock) {
1861            saveSettingsLocked(UserHandle.USER_SYSTEM);
1862        }
1863    }
1864
1865    // Restore the named resource bitmap to both source + crop files
1866    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
1867        if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
1868            String resName = wallpaper.name.substring(4);
1869
1870            String pkg = null;
1871            int colon = resName.indexOf(':');
1872            if (colon > 0) {
1873                pkg = resName.substring(0, colon);
1874            }
1875
1876            String ident = null;
1877            int slash = resName.lastIndexOf('/');
1878            if (slash > 0) {
1879                ident = resName.substring(slash+1);
1880            }
1881
1882            String type = null;
1883            if (colon > 0 && slash > 0 && (slash-colon) > 1) {
1884                type = resName.substring(colon+1, slash);
1885            }
1886
1887            if (pkg != null && ident != null && type != null) {
1888                int resId = -1;
1889                InputStream res = null;
1890                FileOutputStream fos = null;
1891                FileOutputStream cos = null;
1892                try {
1893                    Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
1894                    Resources r = c.getResources();
1895                    resId = r.getIdentifier(resName, null, null);
1896                    if (resId == 0) {
1897                        Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
1898                                + " ident=" + ident);
1899                        return false;
1900                    }
1901
1902                    res = r.openRawResource(resId);
1903                    if (wallpaper.wallpaperFile.exists()) {
1904                        wallpaper.wallpaperFile.delete();
1905                        wallpaper.cropFile.delete();
1906                    }
1907                    fos = new FileOutputStream(wallpaper.wallpaperFile);
1908                    cos = new FileOutputStream(wallpaper.cropFile);
1909
1910                    byte[] buffer = new byte[32768];
1911                    int amt;
1912                    while ((amt=res.read(buffer)) > 0) {
1913                        fos.write(buffer, 0, amt);
1914                        cos.write(buffer, 0, amt);
1915                    }
1916                    // mWallpaperObserver will notice the close and send the change broadcast
1917
1918                    Slog.v(TAG, "Restored wallpaper: " + resName);
1919                    return true;
1920                } catch (NameNotFoundException e) {
1921                    Slog.e(TAG, "Package name " + pkg + " not found");
1922                } catch (Resources.NotFoundException e) {
1923                    Slog.e(TAG, "Resource not found: " + resId);
1924                } catch (IOException e) {
1925                    Slog.e(TAG, "IOException while restoring wallpaper ", e);
1926                } finally {
1927                    IoUtils.closeQuietly(res);
1928                    if (fos != null) {
1929                        FileUtils.sync(fos);
1930                    }
1931                    if (cos != null) {
1932                        FileUtils.sync(cos);
1933                    }
1934                    IoUtils.closeQuietly(fos);
1935                    IoUtils.closeQuietly(cos);
1936                }
1937            }
1938        }
1939        return false;
1940    }
1941
1942    @Override
1943    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1944        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1945                != PackageManager.PERMISSION_GRANTED) {
1946
1947            pw.println("Permission Denial: can't dump wallpaper service from from pid="
1948                    + Binder.getCallingPid()
1949                    + ", uid=" + Binder.getCallingUid());
1950            return;
1951        }
1952
1953        synchronized (mLock) {
1954            pw.println("Current Wallpaper Service state:");
1955            for (int i = 0; i < mWallpaperMap.size(); i++) {
1956                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1957                pw.print(" User "); pw.print(wallpaper.userId);
1958                pw.print(": id="); pw.println(wallpaper.wallpaperId);
1959                pw.print("  mWidth=");
1960                    pw.print(wallpaper.width);
1961                    pw.print(" mHeight=");
1962                    pw.println(wallpaper.height);
1963                pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
1964                pw.print("  mPadding="); pw.println(wallpaper.padding);
1965                pw.print("  mName=");  pw.println(wallpaper.name);
1966                pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
1967                if (wallpaper.connection != null) {
1968                    WallpaperConnection conn = wallpaper.connection;
1969                    pw.print("  Wallpaper connection ");
1970                    pw.print(conn);
1971                    pw.println(":");
1972                    if (conn.mInfo != null) {
1973                        pw.print("    mInfo.component=");
1974                        pw.println(conn.mInfo.getComponent());
1975                    }
1976                    pw.print("    mToken=");
1977                    pw.println(conn.mToken);
1978                    pw.print("    mService=");
1979                    pw.println(conn.mService);
1980                    pw.print("    mEngine=");
1981                    pw.println(conn.mEngine);
1982                    pw.print("    mLastDiedTime=");
1983                    pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
1984                }
1985            }
1986        }
1987    }
1988}
1989