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