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