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