WallpaperManagerService.java revision 0572e1847aa20799cf1e87bc0bfeb1278d2cf423
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                    notifyCallbacksLocked(wallpaper);
1289                }
1290            } finally {
1291                Binder.restoreCallingIdentity(ident);
1292            }
1293        }
1294    }
1295
1296    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
1297            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
1298        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
1299        // Has the component changed?
1300        if (!force) {
1301            if (wallpaper.connection != null) {
1302                if (wallpaper.wallpaperComponent == null) {
1303                    if (componentName == null) {
1304                        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
1305                        // Still using default wallpaper.
1306                        return true;
1307                    }
1308                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
1309                    // Changing to same wallpaper.
1310                    if (DEBUG) Slog.v(TAG, "same wallpaper");
1311                    return true;
1312                }
1313            }
1314        }
1315
1316        try {
1317            if (componentName == null) {
1318                componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
1319                if (componentName == null) {
1320                    // Fall back to static image wallpaper
1321                    componentName = mImageWallpaper;
1322                    //clearWallpaperComponentLocked();
1323                    //return;
1324                    if (DEBUG) Slog.v(TAG, "Using image wallpaper");
1325                }
1326            }
1327            int serviceUserId = wallpaper.userId;
1328            ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
1329                    PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
1330            if (si == null) {
1331                // The wallpaper component we're trying to use doesn't exist
1332                Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
1333                return false;
1334            }
1335            if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
1336                String msg = "Selected service does not require "
1337                        + android.Manifest.permission.BIND_WALLPAPER
1338                        + ": " + componentName;
1339                if (fromUser) {
1340                    throw new SecurityException(msg);
1341                }
1342                Slog.w(TAG, msg);
1343                return false;
1344            }
1345
1346            WallpaperInfo wi = null;
1347
1348            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
1349            if (componentName != null && !componentName.equals(mImageWallpaper)) {
1350                // Make sure the selected service is actually a wallpaper service.
1351                List<ResolveInfo> ris =
1352                        mIPackageManager.queryIntentServices(intent,
1353                                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1354                                PackageManager.GET_META_DATA, serviceUserId).getList();
1355                for (int i=0; i<ris.size(); i++) {
1356                    ServiceInfo rsi = ris.get(i).serviceInfo;
1357                    if (rsi.name.equals(si.name) &&
1358                            rsi.packageName.equals(si.packageName)) {
1359                        try {
1360                            wi = new WallpaperInfo(mContext, ris.get(i));
1361                        } catch (XmlPullParserException e) {
1362                            if (fromUser) {
1363                                throw new IllegalArgumentException(e);
1364                            }
1365                            Slog.w(TAG, e);
1366                            return false;
1367                        } catch (IOException e) {
1368                            if (fromUser) {
1369                                throw new IllegalArgumentException(e);
1370                            }
1371                            Slog.w(TAG, e);
1372                            return false;
1373                        }
1374                        break;
1375                    }
1376                }
1377                if (wi == null) {
1378                    String msg = "Selected service is not a wallpaper: "
1379                            + componentName;
1380                    if (fromUser) {
1381                        throw new SecurityException(msg);
1382                    }
1383                    Slog.w(TAG, msg);
1384                    return false;
1385                }
1386            }
1387
1388            // Bind the service!
1389            if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
1390            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
1391            intent.setComponent(componentName);
1392            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1393                    com.android.internal.R.string.wallpaper_binding_label);
1394            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1395                    mContext, 0,
1396                    Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
1397                            mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
1398                    0, null, new UserHandle(serviceUserId)));
1399            if (!mContext.bindServiceAsUser(intent, newConn,
1400                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
1401                            | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
1402                    new UserHandle(serviceUserId))) {
1403                String msg = "Unable to bind service: "
1404                        + componentName;
1405                if (fromUser) {
1406                    throw new IllegalArgumentException(msg);
1407                }
1408                Slog.w(TAG, msg);
1409                return false;
1410            }
1411            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
1412                detachWallpaperLocked(mLastWallpaper);
1413            }
1414            wallpaper.wallpaperComponent = componentName;
1415            wallpaper.connection = newConn;
1416            newConn.mReply = reply;
1417            try {
1418                if (wallpaper.userId == mCurrentUserId) {
1419                    if (DEBUG)
1420                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
1421                    mIWindowManager.addWindowToken(newConn.mToken,
1422                            WindowManager.LayoutParams.TYPE_WALLPAPER);
1423                    mLastWallpaper = wallpaper;
1424                }
1425            } catch (RemoteException e) {
1426            }
1427        } catch (RemoteException e) {
1428            String msg = "Remote exception for " + componentName + "\n" + e;
1429            if (fromUser) {
1430                throw new IllegalArgumentException(msg);
1431            }
1432            Slog.w(TAG, msg);
1433            return false;
1434        }
1435        return true;
1436    }
1437
1438    void detachWallpaperLocked(WallpaperData wallpaper) {
1439        if (wallpaper.connection != null) {
1440            if (wallpaper.connection.mReply != null) {
1441                try {
1442                    wallpaper.connection.mReply.sendResult(null);
1443                } catch (RemoteException e) {
1444                }
1445                wallpaper.connection.mReply = null;
1446            }
1447            if (wallpaper.connection.mEngine != null) {
1448                try {
1449                    wallpaper.connection.mEngine.destroy();
1450                } catch (RemoteException e) {
1451                }
1452            }
1453            mContext.unbindService(wallpaper.connection);
1454            try {
1455                if (DEBUG)
1456                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
1457                mIWindowManager.removeWindowToken(wallpaper.connection.mToken);
1458            } catch (RemoteException e) {
1459            }
1460            wallpaper.connection.mService = null;
1461            wallpaper.connection.mEngine = null;
1462            wallpaper.connection = null;
1463        }
1464    }
1465
1466    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
1467        wallpaper.wallpaperComponent = null;
1468        detachWallpaperLocked(wallpaper);
1469    }
1470
1471    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
1472        try {
1473            conn.mService.attach(conn, conn.mToken,
1474                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
1475                    wallpaper.width, wallpaper.height, wallpaper.padding);
1476        } catch (RemoteException e) {
1477            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
1478            if (!wallpaper.wallpaperUpdating) {
1479                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
1480            }
1481        }
1482    }
1483
1484    private void notifyCallbacksLocked(WallpaperData wallpaper) {
1485        final int n = wallpaper.callbacks.beginBroadcast();
1486        for (int i = 0; i < n; i++) {
1487            try {
1488                wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
1489            } catch (RemoteException e) {
1490
1491                // The RemoteCallbackList will take care of removing
1492                // the dead object for us.
1493            }
1494        }
1495        wallpaper.callbacks.finishBroadcast();
1496        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
1497        mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
1498    }
1499
1500    private void checkPermission(String permission) {
1501        if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
1502            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
1503                    + ", must have permission " + permission);
1504        }
1505    }
1506
1507    /**
1508     * Certain user types do not support wallpapers (e.g. managed profiles). The check is
1509     * implemented through through the OP_WRITE_WALLPAPER AppOp.
1510     */
1511    public boolean isWallpaperSupported(String callingPackage) {
1512        return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
1513                callingPackage) == AppOpsManager.MODE_ALLOWED;
1514    }
1515
1516    @Override
1517    public boolean isWallpaperSettingAllowed(String callingPackage) {
1518        final PackageManager pm = mContext.getPackageManager();
1519        String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
1520        boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
1521        if (!uidMatchPackage) {
1522            return false;   // callingPackage was faked.
1523        }
1524
1525        final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
1526        if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
1527            return true;
1528        }
1529        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1530        return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
1531    }
1532
1533    private static JournaledFile makeJournaledFile(int userId) {
1534        final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
1535        return new JournaledFile(new File(base), new File(base + ".tmp"));
1536    }
1537
1538    private void saveSettingsLocked(int userId) {
1539        JournaledFile journal = makeJournaledFile(userId);
1540        FileOutputStream fstream = null;
1541        BufferedOutputStream stream = null;
1542        try {
1543            XmlSerializer out = new FastXmlSerializer();
1544            fstream = new FileOutputStream(journal.chooseForWrite(), false);
1545            stream = new BufferedOutputStream(fstream);
1546            out.setOutput(stream, StandardCharsets.UTF_8.name());
1547            out.startDocument(null, true);
1548
1549            WallpaperData wallpaper;
1550
1551            wallpaper = mWallpaperMap.get(userId);
1552            if (wallpaper != null) {
1553                writeWallpaperAttributes(out, "wp", wallpaper);
1554            }
1555            wallpaper = mLockWallpaperMap.get(userId);
1556            if (wallpaper != null) {
1557                writeWallpaperAttributes(out, "kwp", wallpaper);
1558            }
1559
1560            out.endDocument();
1561
1562            stream.flush(); // also flushes fstream
1563            FileUtils.sync(fstream);
1564            stream.close(); // also closes fstream
1565            journal.commit();
1566        } catch (IOException e) {
1567            IoUtils.closeQuietly(stream);
1568            journal.rollback();
1569        }
1570    }
1571
1572    private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
1573            throws IllegalArgumentException, IllegalStateException, IOException {
1574        out.startTag(null, tag);
1575        out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
1576        out.attribute(null, "width", Integer.toString(wallpaper.width));
1577        out.attribute(null, "height", Integer.toString(wallpaper.height));
1578
1579        out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
1580        out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
1581        out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
1582        out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
1583
1584        if (wallpaper.padding.left != 0) {
1585            out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
1586        }
1587        if (wallpaper.padding.top != 0) {
1588            out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
1589        }
1590        if (wallpaper.padding.right != 0) {
1591            out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
1592        }
1593        if (wallpaper.padding.bottom != 0) {
1594            out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
1595        }
1596
1597        out.attribute(null, "name", wallpaper.name);
1598        if (wallpaper.wallpaperComponent != null
1599                && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
1600            out.attribute(null, "component",
1601                    wallpaper.wallpaperComponent.flattenToShortString());
1602        }
1603        out.endTag(null, tag);
1604    }
1605
1606    private void migrateFromOld() {
1607        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
1608        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
1609        if (oldWallpaper.exists()) {
1610            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
1611            oldWallpaper.renameTo(newWallpaper);
1612        }
1613        if (oldInfo.exists()) {
1614            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
1615            oldInfo.renameTo(newInfo);
1616        }
1617    }
1618
1619    private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
1620        String value = parser.getAttributeValue(null, name);
1621        if (value == null) {
1622            return defValue;
1623        }
1624        return Integer.parseInt(value);
1625    }
1626
1627    /**
1628     * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
1629     * happen during user switch.  The async user switch observer may not have received
1630     * the event yet.  We use this safe method when we don't care about this ordering and just
1631     * want to update the data.  The data is going to be applied when the user switch observer
1632     * is eventually executed.
1633     */
1634    private WallpaperData getWallpaperSafeLocked(int userId, int which) {
1635        // We're setting either just system (work with the system wallpaper),
1636        // both (also work with the system wallpaper), or just the lock
1637        // wallpaper (update against the existing lock wallpaper if any).
1638        // Combined or just-system operations use the 'system' WallpaperData
1639        // for this use; lock-only operations use the dedicated one.
1640        final SparseArray<WallpaperData> whichSet =
1641                (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1642        WallpaperData wallpaper = whichSet.get(userId);
1643        if (wallpaper == null) {
1644            // common case, this is the first lookup post-boot of the system or
1645            // unified lock, so we bring up the saved state lazily now and recheck.
1646            loadSettingsLocked(userId);
1647            wallpaper = whichSet.get(userId);
1648            // if it's still null here, this is a lock-only operation and there is not
1649            // yet a lock-only wallpaper set for this user, so we need to establish
1650            // it now.
1651            if (wallpaper == null) {
1652                if (which == FLAG_SET_LOCK) {
1653                    wallpaper = new WallpaperData(userId,
1654                            WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1655                    mLockWallpaperMap.put(userId, wallpaper);
1656                } else {
1657                    // sanity fallback: we're in bad shape, but establishing a known
1658                    // valid system+lock WallpaperData will keep us from dying.
1659                    Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
1660                    wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
1661                    mWallpaperMap.put(userId, wallpaper);
1662                }
1663            }
1664        }
1665        return wallpaper;
1666    }
1667
1668    private void loadSettingsLocked(int userId) {
1669        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
1670
1671        JournaledFile journal = makeJournaledFile(userId);
1672        FileInputStream stream = null;
1673        File file = journal.chooseForRead();
1674        if (!file.exists()) {
1675            // This should only happen one time, when upgrading from a legacy system
1676            migrateFromOld();
1677        }
1678        WallpaperData wallpaper = mWallpaperMap.get(userId);
1679        if (wallpaper == null) {
1680            wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
1681            mWallpaperMap.put(userId, wallpaper);
1682            if (!wallpaper.cropExists()) {
1683                generateCrop(wallpaper);
1684            }
1685        }
1686        boolean success = false;
1687        try {
1688            stream = new FileInputStream(file);
1689            XmlPullParser parser = Xml.newPullParser();
1690            parser.setInput(stream, StandardCharsets.UTF_8.name());
1691
1692            int type;
1693            do {
1694                type = parser.next();
1695                if (type == XmlPullParser.START_TAG) {
1696                    String tag = parser.getName();
1697                    if ("wp".equals(tag)) {
1698                        // Common to system + lock wallpapers
1699                        parseWallpaperAttributes(parser, wallpaper);
1700
1701                        // A system wallpaper might also be a live wallpaper
1702                        String comp = parser.getAttributeValue(null, "component");
1703                        wallpaper.nextWallpaperComponent = comp != null
1704                                ? ComponentName.unflattenFromString(comp)
1705                                : null;
1706                        if (wallpaper.nextWallpaperComponent == null
1707                                || "android".equals(wallpaper.nextWallpaperComponent
1708                                        .getPackageName())) {
1709                            wallpaper.nextWallpaperComponent = mImageWallpaper;
1710                        }
1711
1712                        if (DEBUG) {
1713                            Slog.v(TAG, "mWidth:" + wallpaper.width);
1714                            Slog.v(TAG, "mHeight:" + wallpaper.height);
1715                            Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
1716                            Slog.v(TAG, "mName:" + wallpaper.name);
1717                            Slog.v(TAG, "mNextWallpaperComponent:"
1718                                    + wallpaper.nextWallpaperComponent);
1719                        }
1720                    } else if ("kwp".equals(tag)) {
1721                        // keyguard-specific wallpaper for this user
1722                        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
1723                        if (lockWallpaper == null) {
1724                            lockWallpaper = new WallpaperData(userId,
1725                                    WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1726                            mLockWallpaperMap.put(userId, lockWallpaper);
1727                        }
1728                        parseWallpaperAttributes(parser, lockWallpaper);
1729                    }
1730                }
1731            } while (type != XmlPullParser.END_DOCUMENT);
1732            success = true;
1733        } catch (FileNotFoundException e) {
1734            Slog.w(TAG, "no current wallpaper -- first boot?");
1735        } catch (NullPointerException e) {
1736            Slog.w(TAG, "failed parsing " + file + " " + e);
1737        } catch (NumberFormatException e) {
1738            Slog.w(TAG, "failed parsing " + file + " " + e);
1739        } catch (XmlPullParserException e) {
1740            Slog.w(TAG, "failed parsing " + file + " " + e);
1741        } catch (IOException e) {
1742            Slog.w(TAG, "failed parsing " + file + " " + e);
1743        } catch (IndexOutOfBoundsException e) {
1744            Slog.w(TAG, "failed parsing " + file + " " + e);
1745        }
1746        IoUtils.closeQuietly(stream);
1747
1748        if (!success) {
1749            wallpaper.width = -1;
1750            wallpaper.height = -1;
1751            wallpaper.cropHint.set(0, 0, 0, 0);
1752            wallpaper.padding.set(0, 0, 0, 0);
1753            wallpaper.name = "";
1754        } else {
1755            if (wallpaper.wallpaperId <= 0) {
1756                wallpaper.wallpaperId = makeWallpaperIdLocked();
1757                if (DEBUG) {
1758                    Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
1759                            + "); now " + wallpaper.wallpaperId);
1760                }
1761            }
1762        }
1763
1764        // We always want to have some reasonable width hint.
1765        int baseSize = getMaximumSizeDimension();
1766        if (wallpaper.width < baseSize) {
1767            wallpaper.width = baseSize;
1768        }
1769        if (wallpaper.height < baseSize) {
1770            wallpaper.height = baseSize;
1771        }
1772        // and crop, if not previously specified
1773        if (wallpaper.cropHint.width() <= 0
1774                || wallpaper.cropHint.height() <= 0) {
1775            wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
1776        }
1777    }
1778
1779    private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper) {
1780        final String idString = parser.getAttributeValue(null, "id");
1781        if (idString != null) {
1782            final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
1783            if (id > mWallpaperId) {
1784                mWallpaperId = id;
1785            }
1786        } else {
1787            wallpaper.wallpaperId = makeWallpaperIdLocked();
1788        }
1789
1790        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
1791        wallpaper.height = Integer.parseInt(parser
1792                .getAttributeValue(null, "height"));
1793        wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
1794        wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
1795        wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
1796        wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
1797        wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
1798        wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
1799        wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
1800        wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
1801        wallpaper.name = parser.getAttributeValue(null, "name");
1802    }
1803
1804    private int getMaximumSizeDimension() {
1805        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
1806        Display d = wm.getDefaultDisplay();
1807        return d.getMaximumSizeDimension();
1808    }
1809
1810    // Called by SystemBackupAgent after files are restored to disk.
1811    public void settingsRestored() {
1812        // Verify caller is the system
1813        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1814            throw new RuntimeException("settingsRestored() can only be called from the system process");
1815        }
1816        // TODO: If necessary, make it work for secondary users as well. This currently assumes
1817        // restores only to the primary user
1818        if (DEBUG) Slog.v(TAG, "settingsRestored");
1819        WallpaperData wallpaper = null;
1820        boolean success = false;
1821        synchronized (mLock) {
1822            loadSettingsLocked(UserHandle.USER_SYSTEM);
1823            wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1824            wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
1825            if (wallpaper.nextWallpaperComponent != null
1826                    && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
1827                if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
1828                        wallpaper, null)) {
1829                    // No such live wallpaper or other failure; fall back to the default
1830                    // live wallpaper (since the profile being restored indicated that the
1831                    // user had selected a live rather than static one).
1832                    bindWallpaperComponentLocked(null, false, false, wallpaper, null);
1833                }
1834                success = true;
1835            } else {
1836                // If there's a wallpaper name, we use that.  If that can't be loaded, then we
1837                // use the default.
1838                if ("".equals(wallpaper.name)) {
1839                    if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
1840                    success = true;
1841                } else {
1842                    if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
1843                    success = restoreNamedResourceLocked(wallpaper);
1844                }
1845                if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
1846                        + " id=" + wallpaper.wallpaperId);
1847                if (success) {
1848                    generateCrop(wallpaper);    // based on the new image + metadata
1849                    bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
1850                            wallpaper, null);
1851                }
1852            }
1853        }
1854
1855        if (!success) {
1856            Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
1857            wallpaper.name = "";
1858            getWallpaperDir(UserHandle.USER_SYSTEM).delete();
1859        }
1860
1861        synchronized (mLock) {
1862            saveSettingsLocked(UserHandle.USER_SYSTEM);
1863        }
1864    }
1865
1866    // Restore the named resource bitmap to both source + crop files
1867    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
1868        if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
1869            String resName = wallpaper.name.substring(4);
1870
1871            String pkg = null;
1872            int colon = resName.indexOf(':');
1873            if (colon > 0) {
1874                pkg = resName.substring(0, colon);
1875            }
1876
1877            String ident = null;
1878            int slash = resName.lastIndexOf('/');
1879            if (slash > 0) {
1880                ident = resName.substring(slash+1);
1881            }
1882
1883            String type = null;
1884            if (colon > 0 && slash > 0 && (slash-colon) > 1) {
1885                type = resName.substring(colon+1, slash);
1886            }
1887
1888            if (pkg != null && ident != null && type != null) {
1889                int resId = -1;
1890                InputStream res = null;
1891                FileOutputStream fos = null;
1892                FileOutputStream cos = null;
1893                try {
1894                    Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
1895                    Resources r = c.getResources();
1896                    resId = r.getIdentifier(resName, null, null);
1897                    if (resId == 0) {
1898                        Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
1899                                + " ident=" + ident);
1900                        return false;
1901                    }
1902
1903                    res = r.openRawResource(resId);
1904                    if (wallpaper.wallpaperFile.exists()) {
1905                        wallpaper.wallpaperFile.delete();
1906                        wallpaper.cropFile.delete();
1907                    }
1908                    fos = new FileOutputStream(wallpaper.wallpaperFile);
1909                    cos = new FileOutputStream(wallpaper.cropFile);
1910
1911                    byte[] buffer = new byte[32768];
1912                    int amt;
1913                    while ((amt=res.read(buffer)) > 0) {
1914                        fos.write(buffer, 0, amt);
1915                        cos.write(buffer, 0, amt);
1916                    }
1917                    // mWallpaperObserver will notice the close and send the change broadcast
1918
1919                    Slog.v(TAG, "Restored wallpaper: " + resName);
1920                    return true;
1921                } catch (NameNotFoundException e) {
1922                    Slog.e(TAG, "Package name " + pkg + " not found");
1923                } catch (Resources.NotFoundException e) {
1924                    Slog.e(TAG, "Resource not found: " + resId);
1925                } catch (IOException e) {
1926                    Slog.e(TAG, "IOException while restoring wallpaper ", e);
1927                } finally {
1928                    IoUtils.closeQuietly(res);
1929                    if (fos != null) {
1930                        FileUtils.sync(fos);
1931                    }
1932                    if (cos != null) {
1933                        FileUtils.sync(cos);
1934                    }
1935                    IoUtils.closeQuietly(fos);
1936                    IoUtils.closeQuietly(cos);
1937                }
1938            }
1939        }
1940        return false;
1941    }
1942
1943    @Override
1944    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1945        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1946                != PackageManager.PERMISSION_GRANTED) {
1947
1948            pw.println("Permission Denial: can't dump wallpaper service from from pid="
1949                    + Binder.getCallingPid()
1950                    + ", uid=" + Binder.getCallingUid());
1951            return;
1952        }
1953
1954        synchronized (mLock) {
1955            pw.println("Current Wallpaper Service state:");
1956            for (int i = 0; i < mWallpaperMap.size(); i++) {
1957                WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1958                pw.print(" User "); pw.print(wallpaper.userId);
1959                pw.print(": id="); pw.println(wallpaper.wallpaperId);
1960                pw.print("  mWidth=");
1961                    pw.print(wallpaper.width);
1962                    pw.print(" mHeight=");
1963                    pw.println(wallpaper.height);
1964                pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
1965                pw.print("  mPadding="); pw.println(wallpaper.padding);
1966                pw.print("  mName=");  pw.println(wallpaper.name);
1967                pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
1968                if (wallpaper.connection != null) {
1969                    WallpaperConnection conn = wallpaper.connection;
1970                    pw.print("  Wallpaper connection ");
1971                    pw.print(conn);
1972                    pw.println(":");
1973                    if (conn.mInfo != null) {
1974                        pw.print("    mInfo.component=");
1975                        pw.println(conn.mInfo.getComponent());
1976                    }
1977                    pw.print("    mToken=");
1978                    pw.println(conn.mToken);
1979                    pw.print("    mService=");
1980                    pw.println(conn.mService);
1981                    pw.print("    mEngine=");
1982                    pw.println(conn.mEngine);
1983                    pw.print("    mLastDiedTime=");
1984                    pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
1985                }
1986            }
1987        }
1988    }
1989}
1990