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