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.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
22 import static android.os.ParcelFileDescriptor.MODE_CREATE;
23 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
24 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
25 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
26 import static android.view.Display.DEFAULT_DISPLAY;
27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
28 
29 import android.annotation.NonNull;
30 import android.app.ActivityManager;
31 import android.app.AppGlobals;
32 import android.app.AppOpsManager;
33 import android.app.IWallpaperManager;
34 import android.app.IWallpaperManagerCallback;
35 import android.app.PendingIntent;
36 import android.app.UserSwitchObserver;
37 import android.app.WallpaperColors;
38 import android.app.WallpaperInfo;
39 import android.app.WallpaperManager;
40 import android.app.admin.DevicePolicyManager;
41 import android.app.backup.WallpaperBackupHelper;
42 import android.content.BroadcastReceiver;
43 import android.content.ComponentName;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.ServiceConnection;
48 import android.content.pm.IPackageManager;
49 import android.content.pm.PackageManager;
50 import android.content.pm.PackageManager.NameNotFoundException;
51 import android.content.pm.ResolveInfo;
52 import android.content.pm.ServiceInfo;
53 import android.content.pm.UserInfo;
54 import android.content.res.Resources;
55 import android.graphics.Bitmap;
56 import android.graphics.BitmapFactory;
57 import android.graphics.BitmapRegionDecoder;
58 import android.graphics.Color;
59 import android.graphics.Rect;
60 import android.hardware.display.DisplayManager;
61 import android.os.Binder;
62 import android.os.Bundle;
63 import android.os.Debug;
64 import android.os.Environment;
65 import android.os.FileObserver;
66 import android.os.FileUtils;
67 import android.os.Handler;
68 import android.os.IBinder;
69 import android.os.IInterface;
70 import android.os.IRemoteCallback;
71 import android.os.ParcelFileDescriptor;
72 import android.os.Process;
73 import android.os.RemoteCallbackList;
74 import android.os.RemoteException;
75 import android.os.SELinux;
76 import android.os.ServiceManager;
77 import android.os.SystemClock;
78 import android.os.UserHandle;
79 import android.os.UserManager;
80 import android.os.storage.StorageManager;
81 import android.service.wallpaper.IWallpaperConnection;
82 import android.service.wallpaper.IWallpaperEngine;
83 import android.service.wallpaper.IWallpaperService;
84 import android.service.wallpaper.WallpaperService;
85 import android.system.ErrnoException;
86 import android.system.Os;
87 import android.util.EventLog;
88 import android.util.Slog;
89 import android.util.SparseArray;
90 import android.util.SparseBooleanArray;
91 import android.util.Xml;
92 import android.view.Display;
93 import android.view.DisplayInfo;
94 import android.view.IWindowManager;
95 
96 import com.android.internal.R;
97 import com.android.internal.content.PackageMonitor;
98 import com.android.internal.os.BackgroundThread;
99 import com.android.internal.util.DumpUtils;
100 import com.android.internal.util.FastXmlSerializer;
101 import com.android.internal.util.JournaledFile;
102 import com.android.server.EventLogTags;
103 import com.android.server.FgThread;
104 import com.android.server.LocalServices;
105 import com.android.server.SystemService;
106 import com.android.server.wm.WindowManagerInternal;
107 
108 import libcore.io.IoUtils;
109 
110 import org.xmlpull.v1.XmlPullParser;
111 import org.xmlpull.v1.XmlPullParserException;
112 import org.xmlpull.v1.XmlSerializer;
113 
114 import java.io.BufferedOutputStream;
115 import java.io.File;
116 import java.io.FileDescriptor;
117 import java.io.FileInputStream;
118 import java.io.FileNotFoundException;
119 import java.io.FileOutputStream;
120 import java.io.IOException;
121 import java.io.InputStream;
122 import java.io.PrintWriter;
123 import java.nio.charset.StandardCharsets;
124 import java.util.ArrayList;
125 import java.util.Arrays;
126 import java.util.List;
127 import java.util.Objects;
128 import java.util.function.Consumer;
129 import java.util.function.Predicate;
130 
131 public class WallpaperManagerService extends IWallpaperManager.Stub
132         implements IWallpaperManagerService {
133     private static final String TAG = "WallpaperManagerService";
134     private static final boolean DEBUG = false;
135     private static final boolean DEBUG_LIVE = true;
136 
137     public static class Lifecycle extends SystemService {
138         private IWallpaperManagerService mService;
139 
Lifecycle(Context context)140         public Lifecycle(Context context) {
141             super(context);
142         }
143 
144         @Override
onStart()145         public void onStart() {
146             try {
147                 final Class<? extends IWallpaperManagerService> klass =
148                         (Class<? extends IWallpaperManagerService>)Class.forName(
149                                 getContext().getResources().getString(
150                                         R.string.config_wallpaperManagerServiceName));
151                 mService = klass.getConstructor(Context.class).newInstance(getContext());
152                 publishBinderService(Context.WALLPAPER_SERVICE, mService);
153             } catch (Exception exp) {
154                 Slog.wtf(TAG, "Failed to instantiate WallpaperManagerService", exp);
155             }
156         }
157 
158         @Override
onBootPhase(int phase)159         public void onBootPhase(int phase) {
160             if (mService != null) {
161                 mService.onBootPhase(phase);
162             }
163         }
164 
165         @Override
onUnlockUser(int userHandle)166         public void onUnlockUser(int userHandle) {
167             if (mService != null) {
168                 mService.onUnlockUser(userHandle);
169             }
170         }
171     }
172 
173     private final Object mLock = new Object();
174 
175     /**
176      * Minimum time between crashes of a wallpaper service for us to consider
177      * restarting it vs. just reverting to the static wallpaper.
178      */
179     private static final long MIN_WALLPAPER_CRASH_TIME = 10000;
180     private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
181     static final String WALLPAPER = "wallpaper_orig";
182     static final String WALLPAPER_CROP = "wallpaper";
183     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
184     static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
185     static final String WALLPAPER_INFO = "wallpaper_info.xml";
186 
187     // All the various per-user state files we need to be aware of
188     private static final String[] sPerUserFiles = new String[] {
189         WALLPAPER, WALLPAPER_CROP,
190         WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
191         WALLPAPER_INFO
192     };
193 
194     /**
195      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
196      * that the wallpaper has changed. The CREATE is triggered when there is no
197      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
198      * every time the wallpaper is changed.
199      */
200     private class WallpaperObserver extends FileObserver {
201 
202         final int mUserId;
203         final WallpaperData mWallpaper;
204         final File mWallpaperDir;
205         final File mWallpaperFile;
206         final File mWallpaperLockFile;
207 
WallpaperObserver(WallpaperData wallpaper)208         public WallpaperObserver(WallpaperData wallpaper) {
209             super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
210                     CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
211             mUserId = wallpaper.userId;
212             mWallpaperDir = getWallpaperDir(wallpaper.userId);
213             mWallpaper = wallpaper;
214             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
215             mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
216         }
217 
dataForEvent(boolean sysChanged, boolean lockChanged)218         private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
219             WallpaperData wallpaper = null;
220             synchronized (mLock) {
221                 if (lockChanged) {
222                     wallpaper = mLockWallpaperMap.get(mUserId);
223                 }
224                 if (wallpaper == null) {
225                     // no lock-specific wallpaper exists, or sys case, handled together
226                     wallpaper = mWallpaperMap.get(mUserId);
227                 }
228             }
229             return (wallpaper != null) ? wallpaper : mWallpaper;
230         }
231 
232         @Override
onEvent(int event, String path)233         public void onEvent(int event, String path) {
234             if (path == null) {
235                 return;
236             }
237             final boolean moved = (event == MOVED_TO);
238             final boolean written = (event == CLOSE_WRITE || moved);
239             final File changedFile = new File(mWallpaperDir, path);
240 
241             // System and system+lock changes happen on the system wallpaper input file;
242             // lock-only changes happen on the dedicated lock wallpaper input file
243             final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
244             final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
245             int notifyColorsWhich = 0;
246             WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
247 
248             if (DEBUG) {
249                 Slog.v(TAG, "Wallpaper file change: evt=" + event
250                         + " path=" + path
251                         + " sys=" + sysWallpaperChanged
252                         + " lock=" + lockWallpaperChanged
253                         + " imagePending=" + wallpaper.imageWallpaperPending
254                         + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending)
255                         + " written=" + written);
256             }
257 
258             if (moved && lockWallpaperChanged) {
259                 // We just migrated sys -> lock to preserve imagery for an impending
260                 // new system-only wallpaper.  Tell keyguard about it and make sure it
261                 // has the right SELinux label.
262                 if (DEBUG) {
263                     Slog.i(TAG, "Sys -> lock MOVED_TO");
264                 }
265                 SELinux.restorecon(changedFile);
266                 notifyLockWallpaperChanged();
267                 notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);
268                 return;
269             }
270 
271             synchronized (mLock) {
272                 if (sysWallpaperChanged || lockWallpaperChanged) {
273                     notifyCallbacksLocked(wallpaper);
274                     if (wallpaper.wallpaperComponent == null
275                             || event != CLOSE_WRITE // includes the MOVED_TO case
276                             || wallpaper.imageWallpaperPending) {
277                         if (written) {
278                             // The image source has finished writing the source image,
279                             // so we now produce the crop rect (in the background), and
280                             // only publish the new displayable (sub)image as a result
281                             // of that work.
282                             if (DEBUG) {
283                                 Slog.v(TAG, "Wallpaper written; generating crop");
284                             }
285                             SELinux.restorecon(changedFile);
286                             if (moved) {
287                                 // This is a restore, so generate the crop using any just-restored new
288                                 // crop guidelines, making sure to preserve our local dimension hints.
289                                 // We also make sure to reapply the correct SELinux label.
290                                 if (DEBUG) {
291                                     Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
292                                 }
293                                 loadSettingsLocked(wallpaper.userId, true);
294                             }
295                             generateCrop(wallpaper);
296                             if (DEBUG) {
297                                 Slog.v(TAG, "Crop done; invoking completion callback");
298                             }
299                             wallpaper.imageWallpaperPending = false;
300                             if (sysWallpaperChanged) {
301                                 // If this was the system wallpaper, rebind...
302                                 bindWallpaperComponentLocked(mImageWallpaper, true,
303                                         false, wallpaper, null);
304                                 notifyColorsWhich |= FLAG_SYSTEM;
305                             }
306                             if (lockWallpaperChanged
307                                     || (wallpaper.whichPending & FLAG_LOCK) != 0) {
308                                 if (DEBUG) {
309                                     Slog.i(TAG, "Lock-relevant wallpaper changed");
310                                 }
311                                 // either a lock-only wallpaper commit or a system+lock event.
312                                 // if it's system-plus-lock we need to wipe the lock bookkeeping;
313                                 // we're falling back to displaying the system wallpaper there.
314                                 if (!lockWallpaperChanged) {
315                                     mLockWallpaperMap.remove(wallpaper.userId);
316                                 }
317                                 // and in any case, tell keyguard about it
318                                 notifyLockWallpaperChanged();
319                                 notifyColorsWhich |= FLAG_LOCK;
320                             }
321 
322                             saveSettingsLocked(wallpaper.userId);
323 
324                             // Publish completion *after* we've persisted the changes
325                             if (wallpaper.setComplete != null) {
326                                 try {
327                                     wallpaper.setComplete.onWallpaperChanged();
328                                 } catch (RemoteException e) {
329                                     // if this fails we don't really care; the setting app may just
330                                     // have crashed and that sort of thing is a fact of life.
331                                 }
332                             }
333                         }
334                     }
335                 }
336             }
337 
338             // Outside of the lock since it will synchronize itself
339             if (notifyColorsWhich != 0) {
340                 notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
341             }
342         }
343     }
344 
notifyLockWallpaperChanged()345     private void notifyLockWallpaperChanged() {
346         final IWallpaperManagerCallback cb = mKeyguardListener;
347         if (cb != null) {
348             try {
349                 cb.onWallpaperChanged();
350             } catch (RemoteException e) {
351                 // Oh well it went away; no big deal
352             }
353         }
354     }
355 
notifyWallpaperColorsChanged(@onNull WallpaperData wallpaper, int which)356     private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
357         if (wallpaper.connection != null) {
358             wallpaper.connection.forEachDisplayConnector(connector -> {
359                 notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
360             });
361         } else { // Lock wallpaper does not have WallpaperConnection.
362             notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY);
363         }
364     }
365 
getWallpaperCallbacks(int userId, int displayId)366     private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId,
367             int displayId) {
368         RemoteCallbackList<IWallpaperManagerCallback> listeners = null;
369         final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners =
370                 mColorsChangedListeners.get(userId);
371         if (displayListeners != null) {
372             listeners = displayListeners.get(displayId);
373         }
374         return listeners;
375     }
376 
notifyWallpaperColorsChangedOnDisplay(@onNull WallpaperData wallpaper, int which, int displayId)377     private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which,
378             int displayId) {
379         boolean needsExtraction;
380         synchronized (mLock) {
381             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
382                     getWallpaperCallbacks(wallpaper.userId, displayId);
383             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
384                     getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
385             // No-op until someone is listening to it.
386             if (emptyCallbackList(currentUserColorListeners)  &&
387                     emptyCallbackList(userAllColorListeners)) {
388                 return;
389             }
390 
391             if (DEBUG) {
392                 Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
393             }
394 
395             needsExtraction = wallpaper.primaryColors == null;
396         }
397 
398         // Let's notify the current values, it's fine if it's null, it just means
399         // that we don't know yet.
400         notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
401 
402         if (needsExtraction) {
403             extractColors(wallpaper);
404             synchronized (mLock) {
405                 // Don't need to notify if nothing changed.
406                 if (wallpaper.primaryColors == null) {
407                     return;
408                 }
409             }
410             notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
411         }
412     }
413 
emptyCallbackList(RemoteCallbackList<T> list)414     private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
415         return (list == null || list.getRegisteredCallbackCount() == 0);
416     }
417 
notifyColorListeners(@onNull WallpaperColors wallpaperColors, int which, int userId, int displayId)418     private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
419             int userId, int displayId) {
420         final IWallpaperManagerCallback keyguardListener;
421         final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
422         synchronized (mLock) {
423             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
424                     getWallpaperCallbacks(userId, displayId);
425             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
426                     getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
427             keyguardListener = mKeyguardListener;
428 
429             if (currentUserColorListeners != null) {
430                 final int count = currentUserColorListeners.beginBroadcast();
431                 for (int i = 0; i < count; i++) {
432                     colorListeners.add(currentUserColorListeners.getBroadcastItem(i));
433                 }
434                 currentUserColorListeners.finishBroadcast();
435             }
436 
437             if (userAllColorListeners != null) {
438                 final int count = userAllColorListeners.beginBroadcast();
439                 for (int i = 0; i < count; i++) {
440                     colorListeners.add(userAllColorListeners.getBroadcastItem(i));
441                 }
442                 userAllColorListeners.finishBroadcast();
443             }
444         }
445 
446         final int count = colorListeners.size();
447         for (int i = 0; i < count; i++) {
448             try {
449                 colorListeners.get(i).onWallpaperColorsChanged(wallpaperColors, which, userId);
450             } catch (RemoteException e) {
451                 // Callback is gone, it's not necessary to unregister it since
452                 // RemoteCallbackList#getBroadcastItem will take care of it.
453             }
454         }
455 
456         // Only shows Keyguard on default display
457         if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
458             try {
459                 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
460             } catch (RemoteException e) {
461                 // Oh well it went away; no big deal
462             }
463         }
464     }
465 
466     /**
467      * We can easily extract colors from an ImageWallpaper since it's only a bitmap.
468      * In this case, using the crop is more than enough. Live wallpapers are just ignored.
469      *
470      * @param wallpaper a wallpaper representation
471      */
extractColors(WallpaperData wallpaper)472     private void extractColors(WallpaperData wallpaper) {
473         String cropFile = null;
474         boolean defaultImageWallpaper = false;
475         int wallpaperId;
476 
477         if (wallpaper.equals(mFallbackWallpaper)) {
478             synchronized (mLock) {
479                 if (mFallbackWallpaper.primaryColors != null) return;
480             }
481             final WallpaperColors colors = extractDefaultImageWallpaperColors();
482             synchronized (mLock) {
483                 mFallbackWallpaper.primaryColors = colors;
484             }
485             return;
486         }
487 
488         synchronized (mLock) {
489             // Not having a wallpaperComponent means it's a lock screen wallpaper.
490             final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
491                     || wallpaper.wallpaperComponent == null;
492             if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) {
493                 cropFile = wallpaper.cropFile.getAbsolutePath();
494             } else if (imageWallpaper && !wallpaper.cropExists() && !wallpaper.sourceExists()) {
495                 defaultImageWallpaper = true;
496             }
497             wallpaperId = wallpaper.wallpaperId;
498         }
499 
500         WallpaperColors colors = null;
501         if (cropFile != null) {
502             Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
503             if (bitmap != null) {
504                 colors = WallpaperColors.fromBitmap(bitmap);
505                 bitmap.recycle();
506             }
507         } else if (defaultImageWallpaper) {
508             // There is no crop and source file because this is default image wallpaper.
509             colors = extractDefaultImageWallpaperColors();
510         }
511 
512         if (colors == null) {
513             Slog.w(TAG, "Cannot extract colors because wallpaper could not be read.");
514             return;
515         }
516 
517         synchronized (mLock) {
518             if (wallpaper.wallpaperId == wallpaperId) {
519                 wallpaper.primaryColors = colors;
520                 // Now that we have the colors, let's save them into the xml
521                 // to avoid having to run this again.
522                 saveSettingsLocked(wallpaper.userId);
523             } else {
524                 Slog.w(TAG, "Not setting primary colors since wallpaper changed");
525             }
526         }
527     }
528 
extractDefaultImageWallpaperColors()529     private WallpaperColors extractDefaultImageWallpaperColors() {
530         if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
531 
532         synchronized (mLock) {
533             if (mCacheDefaultImageWallpaperColors != null) return mCacheDefaultImageWallpaperColors;
534         }
535 
536         WallpaperColors colors = null;
537         try (InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM)) {
538             if (is == null) {
539                 Slog.w(TAG, "Can't open default wallpaper stream");
540                 return null;
541             }
542 
543             final BitmapFactory.Options options = new BitmapFactory.Options();
544             final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
545             if (bitmap != null) {
546                 colors = WallpaperColors.fromBitmap(bitmap);
547                 bitmap.recycle();
548             }
549         } catch (OutOfMemoryError e) {
550             Slog.w(TAG, "Can't decode default wallpaper stream", e);
551         } catch (IOException e) {
552             Slog.w(TAG, "Can't close default wallpaper stream", e);
553         }
554 
555         if (colors == null) {
556             Slog.e(TAG, "Extract default image wallpaper colors failed");
557         } else {
558             synchronized (mLock) {
559                 mCacheDefaultImageWallpaperColors = colors;
560             }
561         }
562 
563         return colors;
564     }
565 
566     /**
567      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
568      * for display.
569      */
generateCrop(WallpaperData wallpaper)570     private void generateCrop(WallpaperData wallpaper) {
571         boolean success = false;
572 
573         // Only generate crop for default display.
574         final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
575         Rect cropHint = new Rect(wallpaper.cropHint);
576 
577         if (DEBUG) {
578             Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
579                     + Integer.toHexString(wallpaper.whichPending)
580                     + " to " + wallpaper.cropFile.getName()
581                     + " crop=(" + cropHint.width() + 'x' + cropHint.height()
582                     + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
583         }
584 
585         // Analyse the source; needed in multiple cases
586         BitmapFactory.Options options = new BitmapFactory.Options();
587         options.inJustDecodeBounds = true;
588         BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
589         if (options.outWidth <= 0 || options.outHeight <= 0) {
590             Slog.w(TAG, "Invalid wallpaper data");
591             success = false;
592         } else {
593             boolean needCrop = false;
594             boolean needScale = false;
595 
596             // Empty crop means use the full image
597             if (cropHint.isEmpty()) {
598                 cropHint.left = cropHint.top = 0;
599                 cropHint.right = options.outWidth;
600                 cropHint.bottom = options.outHeight;
601             } else {
602                 // force the crop rect to lie within the measured bounds
603                 cropHint.offset(
604                         (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
605                         (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
606 
607                 // If the crop hint was larger than the image we just overshot. Patch things up.
608                 if (cropHint.left < 0) {
609                     cropHint.left = 0;
610                 }
611                 if (cropHint.top < 0) {
612                     cropHint.top = 0;
613                 }
614 
615                 // Don't bother cropping if what we're left with is identity
616                 needCrop = (options.outHeight > cropHint.height()
617                         || options.outWidth > cropHint.width());
618             }
619 
620             // scale if the crop height winds up not matching the recommended metrics
621             needScale = (wpData.mHeight != cropHint.height());
622 
623             //make sure screen aspect ratio is preserved if width is scaled under screen size
624             if (needScale) {
625                 final DisplayInfo displayInfo = new DisplayInfo();
626                 mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
627                 final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
628                 final int newWidth = (int) (cropHint.width() * scaleByHeight);
629                 if (newWidth < displayInfo.logicalWidth) {
630                     final float screenAspectRatio =
631                             (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
632                     cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
633                     needCrop = true;
634                 }
635             }
636 
637             if (DEBUG) {
638                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
639                 Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
640                 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
641                 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
642             }
643 
644             if (!needCrop && !needScale) {
645                 // Simple case:  the nominal crop fits what we want, so we take
646                 // the whole thing and just copy the image file directly.
647                 if (DEBUG) {
648                     Slog.v(TAG, "Null crop of new wallpaper; copying");
649                 }
650                 success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
651                 if (!success) {
652                     wallpaper.cropFile.delete();
653                     // TODO: fall back to default wallpaper in this case
654                 }
655             } else {
656                 // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
657                 FileOutputStream f = null;
658                 BufferedOutputStream bos = null;
659                 try {
660                     BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
661                             wallpaper.wallpaperFile.getAbsolutePath(), false);
662 
663                     // This actually downsamples only by powers of two, but that's okay; we do
664                     // a proper scaling blit later.  This is to minimize transient RAM use.
665                     // We calculate the largest power-of-two under the actual ratio rather than
666                     // just let the decode take care of it because we also want to remap where the
667                     // cropHint rectangle lies in the decoded [super]rect.
668                     final BitmapFactory.Options scaler;
669                     final int actualScale = cropHint.height() / wpData.mHeight;
670                     int scale = 1;
671                     while (2*scale < actualScale) {
672                         scale *= 2;
673                     }
674                     if (scale > 1) {
675                         scaler = new BitmapFactory.Options();
676                         scaler.inSampleSize = scale;
677                         if (DEBUG) {
678                             Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
679                         }
680                     } else {
681                         scaler = null;
682                     }
683                     Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
684                     decoder.recycle();
685 
686                     if (cropped == null) {
687                         Slog.e(TAG, "Could not decode new wallpaper");
688                     } else {
689                         // We've got the extracted crop; now we want to scale it properly to
690                         // the desired rectangle.  That's a height-biased operation: make it
691                         // fit the hinted height, and accept whatever width we end up with.
692                         cropHint.offsetTo(0, 0);
693                         cropHint.right /= scale;    // adjust by downsampling factor
694                         cropHint.bottom /= scale;
695                         final float heightR =
696                                 ((float) wpData.mHeight) / ((float) cropHint.height());
697                         if (DEBUG) {
698                             Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
699                         }
700                         final int destWidth = (int)(cropHint.width() * heightR);
701                         final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
702                                 destWidth, wpData.mHeight, true);
703                         if (DEBUG) {
704                             Slog.v(TAG, "Final extract:");
705                             Slog.v(TAG, "  dims: w=" + wpData.mWidth
706                                     + " h=" + wpData.mHeight);
707                             Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
708                                     + " h=" + finalCrop.getHeight());
709                         }
710 
711                         f = new FileOutputStream(wallpaper.cropFile);
712                         bos = new BufferedOutputStream(f, 32*1024);
713                         finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
714                         bos.flush();  // don't rely on the implicit flush-at-close when noting success
715                         success = true;
716                     }
717                 } catch (Exception e) {
718                     if (DEBUG) {
719                         Slog.e(TAG, "Error decoding crop", e);
720                     }
721                 } finally {
722                     IoUtils.closeQuietly(bos);
723                     IoUtils.closeQuietly(f);
724                 }
725             }
726         }
727 
728         if (!success) {
729             Slog.e(TAG, "Unable to apply new wallpaper");
730             wallpaper.cropFile.delete();
731         }
732 
733         if (wallpaper.cropFile.exists()) {
734             boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
735             if (DEBUG) {
736                 Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
737             }
738         }
739     }
740 
741     private final Context mContext;
742     private final IWindowManager mIWindowManager;
743     private final WindowManagerInternal mWindowManagerInternal;
744     private final IPackageManager mIPackageManager;
745     private final MyPackageMonitor mMonitor;
746     private final AppOpsManager mAppOpsManager;
747 
748     private final DisplayManager mDisplayManager;
749     private final DisplayManager.DisplayListener mDisplayListener =
750             new DisplayManager.DisplayListener() {
751 
752         @Override
753         public void onDisplayAdded(int displayId) {
754         }
755 
756         @Override
757         public void onDisplayRemoved(int displayId) {
758             synchronized (mLock) {
759                 if (mLastWallpaper != null) {
760                     WallpaperData targetWallpaper = null;
761                     if (mLastWallpaper.connection.containsDisplay(displayId)) {
762                         targetWallpaper = mLastWallpaper;
763                     } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
764                         targetWallpaper = mFallbackWallpaper;
765                     }
766                     if (targetWallpaper == null) return;
767                     WallpaperConnection.DisplayConnector connector =
768                             targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
769                     if (connector == null) return;
770                     connector.disconnectLocked();
771                     targetWallpaper.connection.removeDisplayConnector(displayId);
772                     removeDisplayData(displayId);
773                 }
774                 for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
775                     final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
776                             mColorsChangedListeners.valueAt(i);
777                     callbacks.delete(displayId);
778                 }
779             }
780         }
781 
782         @Override
783         public void onDisplayChanged(int displayId) {
784         }
785     };
786 
787     /**
788      * Map of color listeners per user id.
789      * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
790      * The secondary key will be the display id, which means which display the listener is
791      * interested in.
792      */
793     private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
794             mColorsChangedListeners;
795     private WallpaperData mLastWallpaper;
796     private IWallpaperManagerCallback mKeyguardListener;
797     private boolean mWaitingForUnlock;
798     private boolean mShuttingDown;
799 
800     /**
801      * ID of the current wallpaper, changed every time anything sets a wallpaper.
802      * This is used for external detection of wallpaper update activity.
803      */
804     private int mWallpaperId;
805 
806     /**
807      * Name of the component used to display bitmap wallpapers from either the gallery or
808      * built-in wallpapers.
809      */
810     private final ComponentName mImageWallpaper;
811 
812     /**
813      * Default image wallpaper shall never changed after system service started, caching it when we
814      * first read the image file.
815      */
816     private WallpaperColors mCacheDefaultImageWallpaperColors;
817 
818     /**
819      * Name of the default wallpaper component; might be different from mImageWallpaper
820      */
821     private final ComponentName mDefaultWallpaperComponent;
822 
823     private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
824     private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
825 
826     private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
827 
828     private WallpaperData mFallbackWallpaper;
829 
830     private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
831     private int mCurrentUserId = UserHandle.USER_NULL;
832     private boolean mInAmbientMode;
833 
834     static class WallpaperData {
835 
836         int userId;
837 
838         final File wallpaperFile;   // source image
839         final File cropFile;        // eventual destination
840 
841         /**
842          * True while the client is writing a new wallpaper
843          */
844         boolean imageWallpaperPending;
845 
846         /**
847          * Which new wallpapers are being written; mirrors the 'which'
848          * selector bit field to setWallpaper().
849          */
850         int whichPending;
851 
852         /**
853          * Callback once the set + crop is finished
854          */
855         IWallpaperManagerCallback setComplete;
856 
857         /**
858          * Is the OS allowed to back up this wallpaper imagery?
859          */
860         boolean allowBackup;
861 
862         /**
863          * Resource name if using a picture from the wallpaper gallery
864          */
865         String name = "";
866 
867         /**
868          * The component name of the currently set live wallpaper.
869          */
870         ComponentName wallpaperComponent;
871 
872         /**
873          * The component name of the wallpaper that should be set next.
874          */
875         ComponentName nextWallpaperComponent;
876 
877         /**
878          * The ID of this wallpaper
879          */
880         int wallpaperId;
881 
882         /**
883          * Primary colors histogram
884          */
885         WallpaperColors primaryColors;
886 
887         WallpaperConnection connection;
888         long lastDiedTime;
889         boolean wallpaperUpdating;
890         WallpaperObserver wallpaperObserver;
891 
892         /**
893          * List of callbacks registered they should each be notified when the wallpaper is changed.
894          */
895         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
896                 = new RemoteCallbackList<IWallpaperManagerCallback>();
897 
898         /**
899          * The crop hint supplied for displaying a subset of the source image
900          */
901         final Rect cropHint = new Rect(0, 0, 0, 0);
902 
WallpaperData(int userId, String inputFileName, String cropFileName)903         WallpaperData(int userId, String inputFileName, String cropFileName) {
904             this.userId = userId;
905             final File wallpaperDir = getWallpaperDir(userId);
906             wallpaperFile = new File(wallpaperDir, inputFileName);
907             cropFile = new File(wallpaperDir, cropFileName);
908         }
909 
910         // Called during initialization of a given user's wallpaper bookkeeping
cropExists()911         boolean cropExists() {
912             return cropFile.exists();
913         }
914 
sourceExists()915         boolean sourceExists() {
916             return wallpaperFile.exists();
917         }
918     }
919 
920     private static final class DisplayData {
921         int mWidth = -1;
922         int mHeight = -1;
923         final Rect mPadding = new Rect(0, 0, 0, 0);
924         final int mDisplayId;
925 
DisplayData(int displayId)926         DisplayData(int displayId) {
927             mDisplayId = displayId;
928         }
929     }
930 
removeDisplayData(int displayId)931     private void removeDisplayData(int displayId) {
932         mDisplayDatas.remove(displayId);
933     }
934 
getDisplayDataOrCreate(int displayId)935     private DisplayData getDisplayDataOrCreate(int displayId) {
936         DisplayData wpdData = mDisplayDatas.get(displayId);
937         if (wpdData == null) {
938             wpdData = new DisplayData(displayId);
939             ensureSaneWallpaperDisplaySize(wpdData, displayId);
940             mDisplayDatas.append(displayId, wpdData);
941         }
942         return wpdData;
943     }
944 
ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId)945     private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
946         // We always want to have some reasonable width hint.
947         final int baseSize = getMaximumSizeDimension(displayId);
948         if (wpdData.mWidth < baseSize) {
949             wpdData.mWidth = baseSize;
950         }
951         if (wpdData.mHeight < baseSize) {
952             wpdData.mHeight = baseSize;
953         }
954     }
955 
getMaximumSizeDimension(int displayId)956     private int getMaximumSizeDimension(int displayId) {
957         Display display = mDisplayManager.getDisplay(displayId);
958         if (display == null) {
959             Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
960             display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
961         }
962         return display.getMaximumSizeDimension();
963     }
964 
forEachDisplayData(Consumer<DisplayData> action)965     void forEachDisplayData(Consumer<DisplayData> action) {
966         for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
967             final DisplayData wpdData = mDisplayDatas.valueAt(i);
968             action.accept(wpdData);
969         }
970     }
971 
makeWallpaperIdLocked()972     int makeWallpaperIdLocked() {
973         do {
974             ++mWallpaperId;
975         } while (mWallpaperId == 0);
976         return mWallpaperId;
977     }
978 
supportsMultiDisplay(WallpaperConnection connection)979     private boolean supportsMultiDisplay(WallpaperConnection connection) {
980         if (connection != null) {
981             return connection.mInfo == null // This is image wallpaper
982                     || connection.mInfo.supportsMultipleDisplays();
983         }
984         return false;
985     }
986 
updateFallbackConnection()987     private void updateFallbackConnection() {
988         if (mLastWallpaper == null || mFallbackWallpaper == null) return;
989         final WallpaperConnection systemConnection = mLastWallpaper.connection;
990         final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
991         if (fallbackConnection == null) {
992             Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
993             return;
994         }
995         if (supportsMultiDisplay(systemConnection)) {
996             if (fallbackConnection.mDisplayConnector.size() != 0) {
997                 fallbackConnection.forEachDisplayConnector(connector -> {
998                     if (connector.mEngine != null) {
999                         connector.disconnectLocked();
1000                     }
1001                 });
1002                 fallbackConnection.mDisplayConnector.clear();
1003             }
1004         } else {
1005             fallbackConnection.appendConnectorWithCondition(display ->
1006                     fallbackConnection.isUsableDisplay(display)
1007                             && display.getDisplayId() != DEFAULT_DISPLAY
1008                             && !fallbackConnection.containsDisplay(display.getDisplayId()));
1009             fallbackConnection.forEachDisplayConnector(connector -> {
1010                 if (connector.mEngine == null) {
1011                     connector.connectLocked(fallbackConnection, mFallbackWallpaper);
1012                 }
1013             });
1014         }
1015     }
1016 
1017     class WallpaperConnection extends IWallpaperConnection.Stub
1018             implements ServiceConnection {
1019 
1020         /**
1021          * Collect needed info for a display.
1022          */
1023         private final class DisplayConnector {
1024             final int mDisplayId;
1025             final Binder mToken = new Binder();
1026             IWallpaperEngine mEngine;
1027             boolean mDimensionsChanged;
1028             boolean mPaddingChanged;
1029 
DisplayConnector(int displayId)1030             DisplayConnector(int displayId) {
1031                 mDisplayId = displayId;
1032             }
1033 
ensureStatusHandled()1034             void ensureStatusHandled() {
1035                 final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
1036                 if (mDimensionsChanged) {
1037                     try {
1038                         mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
1039                     } catch (RemoteException e) {
1040                         Slog.w(TAG, "Failed to set wallpaper dimensions", e);
1041                     }
1042                     mDimensionsChanged = false;
1043                 }
1044                 if (mPaddingChanged) {
1045                     try {
1046                         mEngine.setDisplayPadding(wpdData.mPadding);
1047                     } catch (RemoteException e) {
1048                         Slog.w(TAG, "Failed to set wallpaper padding", e);
1049                     }
1050                     mPaddingChanged = false;
1051                 }
1052             }
1053 
connectLocked(WallpaperConnection connection, WallpaperData wallpaper)1054             void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
1055                 if (connection.mService == null) {
1056                     Slog.w(TAG, "WallpaperService is not connected yet");
1057                     return;
1058                 }
1059                 if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
1060                 try {
1061                     mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
1062                 } catch (RemoteException e) {
1063                     Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
1064                     return;
1065                 }
1066 
1067                 final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
1068                 try {
1069                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
1070                             wpdData.mWidth, wpdData.mHeight,
1071                             wpdData.mPadding, mDisplayId);
1072                 } catch (RemoteException e) {
1073                     Slog.w(TAG, "Failed attaching wallpaper on display", e);
1074                     if (wallpaper != null && !wallpaper.wallpaperUpdating
1075                             && connection.getConnectedEngineSize() == 0) {
1076                         bindWallpaperComponentLocked(null /* componentName */, false /* force */,
1077                                 false /* fromUser */, wallpaper, null /* reply */);
1078                     }
1079                 }
1080             }
1081 
disconnectLocked()1082             void disconnectLocked() {
1083                 if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
1084                 try {
1085                     mIWindowManager.removeWindowToken(mToken, mDisplayId);
1086                 } catch (RemoteException e) {
1087                 }
1088                 try {
1089                     if (mEngine != null) {
1090                         mEngine.destroy();
1091                     }
1092                 } catch (RemoteException e) {
1093                 }
1094                 mEngine = null;
1095             }
1096         }
1097 
1098         /**
1099          * A map for each display.
1100          * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
1101          */
1102         private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
1103 
1104         /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
1105          *  middle of an update). If exceeded, the wallpaper gets reset to the system default. */
1106         private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
1107 
1108         final WallpaperInfo mInfo;
1109         IWallpaperService mService;
1110         WallpaperData mWallpaper;
1111         final int mClientUid;
1112         IRemoteCallback mReply;
1113 
1114         private Runnable mResetRunnable = () -> {
1115             synchronized (mLock) {
1116                 if (mShuttingDown) {
1117                     // Don't expect wallpaper services to relaunch during shutdown
1118                     if (DEBUG_LIVE) {
1119                         Slog.i(TAG, "Ignoring relaunch timeout during shutdown");
1120                     }
1121                     return;
1122                 }
1123 
1124                 if (!mWallpaper.wallpaperUpdating
1125                         && mWallpaper.userId == mCurrentUserId) {
1126                     Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
1127                             + ", reverting to built-in wallpaper!");
1128                     clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
1129                             null);
1130                 }
1131             }
1132         };
1133 
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid)1134         WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
1135             mInfo = info;
1136             mWallpaper = wallpaper;
1137             mClientUid = clientUid;
1138             initDisplayState();
1139         }
1140 
initDisplayState()1141         private void initDisplayState() {
1142             // Do not initialize fallback wallpaper
1143             if (!mWallpaper.equals(mFallbackWallpaper)) {
1144                 if (supportsMultiDisplay(this)) {
1145                     // The system wallpaper is image wallpaper or it can supports multiple displays.
1146                     appendConnectorWithCondition(this::isUsableDisplay);
1147                 } else {
1148                     // The system wallpaper does not support multiple displays, so just attach it on
1149                     // default display.
1150                     mDisplayConnector.append(DEFAULT_DISPLAY,
1151                             new DisplayConnector(DEFAULT_DISPLAY));
1152                 }
1153             }
1154         }
1155 
appendConnectorWithCondition(Predicate<Display> tester)1156         private void appendConnectorWithCondition(Predicate<Display> tester) {
1157             final Display[] displays = mDisplayManager.getDisplays();
1158             for (Display display : displays) {
1159                 if (tester.test(display)) {
1160                     final int displayId = display.getDisplayId();
1161                     final DisplayConnector connector = mDisplayConnector.get(displayId);
1162                     if (connector == null) {
1163                         mDisplayConnector.append(displayId,
1164                                 new DisplayConnector(displayId));
1165                     }
1166                 }
1167             }
1168         }
1169 
isUsableDisplay(Display display)1170         private boolean isUsableDisplay(Display display) {
1171             if (display == null || !display.hasAccess(mClientUid)) {
1172                 return false;
1173             }
1174             final int displayId = display.getDisplayId();
1175             if (displayId == DEFAULT_DISPLAY) {
1176                 return true;
1177             }
1178 
1179             final long ident = Binder.clearCallingIdentity();
1180             try {
1181                 return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
1182             } finally {
1183                 Binder.restoreCallingIdentity(ident);
1184             }
1185         }
1186 
forEachDisplayConnector(Consumer<DisplayConnector> action)1187         void forEachDisplayConnector(Consumer<DisplayConnector> action) {
1188             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
1189                 final DisplayConnector connector = mDisplayConnector.valueAt(i);
1190                 action.accept(connector);
1191             }
1192         }
1193 
getConnectedEngineSize()1194         int getConnectedEngineSize() {
1195             int engineSize = 0;
1196             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
1197                 final DisplayConnector connector = mDisplayConnector.valueAt(i);
1198                 if (connector.mEngine != null) engineSize++;
1199             }
1200             return engineSize;
1201         }
1202 
getDisplayConnectorOrCreate(int displayId)1203         DisplayConnector getDisplayConnectorOrCreate(int displayId) {
1204             DisplayConnector connector = mDisplayConnector.get(displayId);
1205             if (connector == null) {
1206                 final Display display = mDisplayManager.getDisplay(displayId);
1207                 if (isUsableDisplay(display)) {
1208                     connector = new DisplayConnector(displayId);
1209                     mDisplayConnector.append(displayId, connector);
1210                 }
1211             }
1212             return connector;
1213         }
1214 
containsDisplay(int displayId)1215         boolean containsDisplay(int displayId) {
1216             return mDisplayConnector.get(displayId) != null;
1217         }
1218 
removeDisplayConnector(int displayId)1219         void removeDisplayConnector(int displayId) {
1220             final DisplayConnector connector = mDisplayConnector.get(displayId);
1221             if (connector != null) {
1222                 mDisplayConnector.remove(displayId);
1223             }
1224         }
1225 
1226         @Override
onServiceConnected(ComponentName name, IBinder service)1227         public void onServiceConnected(ComponentName name, IBinder service) {
1228             synchronized (mLock) {
1229                 if (mWallpaper.connection == this) {
1230                     mService = IWallpaperService.Stub.asInterface(service);
1231                     attachServiceLocked(this, mWallpaper);
1232                     // XXX should probably do saveSettingsLocked() later
1233                     // when we have an engine, but I'm not sure about
1234                     // locking there and anyway we always need to be able to
1235                     // recover if there is something wrong.
1236                     if (!mWallpaper.equals(mFallbackWallpaper)) {
1237                         saveSettingsLocked(mWallpaper.userId);
1238                     }
1239                     FgThread.getHandler().removeCallbacks(mResetRunnable);
1240                 }
1241             }
1242         }
1243 
1244         @Override
onServiceDisconnected(ComponentName name)1245         public void onServiceDisconnected(ComponentName name) {
1246             synchronized (mLock) {
1247                 Slog.w(TAG, "Wallpaper service gone: " + name);
1248                 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
1249                     Slog.e(TAG, "Does not match expected wallpaper component "
1250                             + mWallpaper.wallpaperComponent);
1251                 }
1252                 mService = null;
1253                 forEachDisplayConnector(connector -> connector.mEngine = null);
1254                 if (mWallpaper.connection == this) {
1255                     // There is an inherent ordering race between this callback and the
1256                     // package monitor that receives notice that a package is being updated,
1257                     // so we cannot quite trust at this moment that we know for sure that
1258                     // this is not an update.  If we think this is a genuine non-update
1259                     // wallpaper outage, we do our "wait for reset" work as a continuation,
1260                     // a short time in the future, specifically to allow any pending package
1261                     // update message on this same looper thread to be processed.
1262                     if (!mWallpaper.wallpaperUpdating) {
1263                         mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
1264                                 1000);
1265                     }
1266                 }
1267             }
1268         }
1269 
scheduleTimeoutLocked()1270         public void scheduleTimeoutLocked() {
1271             // If we didn't reset it right away, do so after we couldn't connect to
1272             // it for an extended amount of time to avoid having a black wallpaper.
1273             final Handler fgHandler = FgThread.getHandler();
1274             fgHandler.removeCallbacks(mResetRunnable);
1275             fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
1276             if (DEBUG_LIVE) {
1277                 Slog.i(TAG,
1278                         "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
1279             }
1280         }
1281 
processDisconnect(final ServiceConnection connection)1282         private void processDisconnect(final ServiceConnection connection) {
1283             synchronized (mLock) {
1284                 // The wallpaper disappeared.  If this isn't a system-default one, track
1285                 // crashes and fall back to default if it continues to misbehave.
1286                 if (connection == mWallpaper.connection) {
1287                     final ComponentName wpService = mWallpaper.wallpaperComponent;
1288                     if (!mWallpaper.wallpaperUpdating
1289                             && mWallpaper.userId == mCurrentUserId
1290                             && !Objects.equals(mDefaultWallpaperComponent, wpService)
1291                             && !Objects.equals(mImageWallpaper, wpService)) {
1292                         // There is a race condition which causes
1293                         // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
1294                         // currently updating since the broadcast notifying us is async.
1295                         // This race is overcome by the general rule that we only reset the
1296                         // wallpaper if its service was shut down twice
1297                         // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
1298                         if (mWallpaper.lastDiedTime != 0
1299                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
1300                                 > SystemClock.uptimeMillis()) {
1301                             Slog.w(TAG, "Reverting to built-in wallpaper!");
1302                             clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
1303                         } else {
1304                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
1305 
1306                             clearWallpaperComponentLocked(mWallpaper);
1307                             if (bindWallpaperComponentLocked(
1308                                     wpService, false, false, mWallpaper, null)) {
1309                                 mWallpaper.connection.scheduleTimeoutLocked();
1310                             } else {
1311                                 Slog.w(TAG, "Reverting to built-in wallpaper!");
1312                                 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
1313                             }
1314                         }
1315                         final String flattened = wpService.flattenToString();
1316                         EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
1317                                 flattened.substring(0, Math.min(flattened.length(),
1318                                         MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
1319                     }
1320                 } else {
1321                     if (DEBUG_LIVE) {
1322                         Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
1323                     }
1324                 }
1325             }
1326         }
1327 
1328         /**
1329          * Called by a live wallpaper if its colors have changed.
1330          * @param primaryColors representation of wallpaper primary colors
1331          * @param displayId for which display
1332          */
1333         @Override
onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId)1334         public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
1335             int which;
1336             synchronized (mLock) {
1337                 // Do not broadcast changes on ImageWallpaper since it's handled
1338                 // internally by this class.
1339                 if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
1340                     return;
1341                 }
1342 
1343                 mWallpaper.primaryColors = primaryColors;
1344 
1345                 // Live wallpapers always are system wallpapers.
1346                 which = FLAG_SYSTEM;
1347                 // It's also the lock screen wallpaper when we don't have a bitmap in there.
1348                 if (displayId == DEFAULT_DISPLAY) {
1349                     final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
1350                     if (lockedWallpaper == null) {
1351                         which |= FLAG_LOCK;
1352                     }
1353                 }
1354             }
1355             if (which != 0) {
1356                 notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
1357             }
1358         }
1359 
1360         @Override
attachEngine(IWallpaperEngine engine, int displayId)1361         public void attachEngine(IWallpaperEngine engine, int displayId) {
1362             synchronized (mLock) {
1363                 final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
1364                 if (connector == null) {
1365                     try {
1366                         engine.destroy();
1367                     } catch (RemoteException e) {
1368                         Slog.w(TAG, "Failed to destroy engine", e);
1369                     }
1370                     return;
1371                 }
1372                 connector.mEngine = engine;
1373                 connector.ensureStatusHandled();
1374 
1375                 // TODO(multi-display) TBD.
1376                 if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
1377                     try {
1378                         connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
1379                     } catch (RemoteException e) {
1380                         Slog.w(TAG, "Failed to set ambient mode state", e);
1381                     }
1382                 }
1383                 try {
1384                     // This will trigger onComputeColors in the wallpaper engine.
1385                     // It's fine to be locked in here since the binder is oneway.
1386                     connector.mEngine.requestWallpaperColors();
1387                 } catch (RemoteException e) {
1388                     Slog.w(TAG, "Failed to request wallpaper colors", e);
1389                 }
1390             }
1391         }
1392 
1393         @Override
engineShown(IWallpaperEngine engine)1394         public void engineShown(IWallpaperEngine engine) {
1395             synchronized (mLock) {
1396                 if (mReply != null) {
1397                     long ident = Binder.clearCallingIdentity();
1398                     try {
1399                         mReply.sendResult(null);
1400                     } catch (RemoteException e) {
1401                         Binder.restoreCallingIdentity(ident);
1402                     }
1403                     mReply = null;
1404                 }
1405             }
1406         }
1407 
1408         @Override
setWallpaper(String name)1409         public ParcelFileDescriptor setWallpaper(String name) {
1410             synchronized (mLock) {
1411                 if (mWallpaper.connection == this) {
1412                     return updateWallpaperBitmapLocked(name, mWallpaper, null);
1413                 }
1414                 return null;
1415             }
1416         }
1417     }
1418 
1419     class MyPackageMonitor extends PackageMonitor {
1420         @Override
onPackageUpdateFinished(String packageName, int uid)1421         public void onPackageUpdateFinished(String packageName, int uid) {
1422             synchronized (mLock) {
1423                 if (mCurrentUserId != getChangingUserId()) {
1424                     return;
1425                 }
1426                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1427                 if (wallpaper != null) {
1428                     final ComponentName wpService = wallpaper.wallpaperComponent;
1429                     if (wpService != null && wpService.getPackageName().equals(packageName)) {
1430                         if (DEBUG_LIVE) {
1431                             Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
1432                         }
1433                         wallpaper.wallpaperUpdating = false;
1434                         clearWallpaperComponentLocked(wallpaper);
1435                         if (!bindWallpaperComponentLocked(wpService, false, false,
1436                                 wallpaper, null)) {
1437                             Slog.w(TAG, "Wallpaper " + wpService
1438                                     + " no longer available; reverting to default");
1439                             clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1440                         }
1441                     }
1442                 }
1443             }
1444         }
1445 
1446         @Override
onPackageModified(String packageName)1447         public void onPackageModified(String packageName) {
1448             synchronized (mLock) {
1449                 if (mCurrentUserId != getChangingUserId()) {
1450                     return;
1451                 }
1452                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1453                 if (wallpaper != null) {
1454                     if (wallpaper.wallpaperComponent == null
1455                             || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1456                         return;
1457                     }
1458                     doPackagesChangedLocked(true, wallpaper);
1459                 }
1460             }
1461         }
1462 
1463         @Override
onPackageUpdateStarted(String packageName, int uid)1464         public void onPackageUpdateStarted(String packageName, int uid) {
1465             synchronized (mLock) {
1466                 if (mCurrentUserId != getChangingUserId()) {
1467                     return;
1468                 }
1469                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1470                 if (wallpaper != null) {
1471                     if (wallpaper.wallpaperComponent != null
1472                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1473                         if (DEBUG_LIVE) {
1474                             Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent
1475                                     + " is updating");
1476                         }
1477                         wallpaper.wallpaperUpdating = true;
1478                         if (wallpaper.connection != null) {
1479                             FgThread.getHandler().removeCallbacks(
1480                                     wallpaper.connection.mResetRunnable);
1481                         }
1482                     }
1483                 }
1484             }
1485         }
1486 
1487         @Override
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)1488         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1489             synchronized (mLock) {
1490                 boolean changed = false;
1491                 if (mCurrentUserId != getChangingUserId()) {
1492                     return false;
1493                 }
1494                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1495                 if (wallpaper != null) {
1496                     boolean res = doPackagesChangedLocked(doit, wallpaper);
1497                     changed |= res;
1498                 }
1499                 return changed;
1500             }
1501         }
1502 
1503         @Override
onSomePackagesChanged()1504         public void onSomePackagesChanged() {
1505             synchronized (mLock) {
1506                 if (mCurrentUserId != getChangingUserId()) {
1507                     return;
1508                 }
1509                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1510                 if (wallpaper != null) {
1511                     doPackagesChangedLocked(true, wallpaper);
1512                 }
1513             }
1514         }
1515 
doPackagesChangedLocked(boolean doit, WallpaperData wallpaper)1516         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
1517             boolean changed = false;
1518             if (wallpaper.wallpaperComponent != null) {
1519                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
1520                         .getPackageName());
1521                 if (change == PACKAGE_PERMANENT_CHANGE
1522                         || change == PACKAGE_TEMPORARY_CHANGE) {
1523                     changed = true;
1524                     if (doit) {
1525                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
1526                                 + wallpaper.wallpaperComponent);
1527                         clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1528                     }
1529                 }
1530             }
1531             if (wallpaper.nextWallpaperComponent != null) {
1532                 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
1533                         .getPackageName());
1534                 if (change == PACKAGE_PERMANENT_CHANGE
1535                         || change == PACKAGE_TEMPORARY_CHANGE) {
1536                     wallpaper.nextWallpaperComponent = null;
1537                 }
1538             }
1539             if (wallpaper.wallpaperComponent != null
1540                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
1541                 try {
1542                     mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
1543                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1544                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1545                 } catch (NameNotFoundException e) {
1546                     Slog.w(TAG, "Wallpaper component gone, removing: "
1547                             + wallpaper.wallpaperComponent);
1548                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1549                 }
1550             }
1551             if (wallpaper.nextWallpaperComponent != null
1552                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
1553                 try {
1554                     mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
1555                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1556                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1557                 } catch (NameNotFoundException e) {
1558                     wallpaper.nextWallpaperComponent = null;
1559                 }
1560             }
1561             return changed;
1562         }
1563     }
1564 
WallpaperManagerService(Context context)1565     public WallpaperManagerService(Context context) {
1566         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
1567         mContext = context;
1568         mShuttingDown = false;
1569         mImageWallpaper = ComponentName.unflattenFromString(
1570                 context.getResources().getString(R.string.image_wallpaper_component));
1571         mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
1572         mIWindowManager = IWindowManager.Stub.asInterface(
1573                 ServiceManager.getService(Context.WINDOW_SERVICE));
1574         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1575         mIPackageManager = AppGlobals.getPackageManager();
1576         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1577         mDisplayManager = mContext.getSystemService(DisplayManager.class);
1578         mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
1579         mMonitor = new MyPackageMonitor();
1580         mColorsChangedListeners = new SparseArray<>();
1581 
1582         LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
1583     }
1584 
1585     private final class LocalService extends WallpaperManagerInternal {
1586         @Override
onDisplayReady(int displayId)1587         public void onDisplayReady(int displayId) {
1588             onDisplayReadyInternal(displayId);
1589         }
1590     }
1591 
initialize()1592     void initialize() {
1593         mMonitor.register(mContext, null, UserHandle.ALL, true);
1594         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
1595 
1596         // Initialize state from the persistent store, then guarantee that the
1597         // WallpaperData for the system imagery is instantiated & active, creating
1598         // it from defaults if necessary.
1599         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
1600         getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
1601     }
1602 
getWallpaperDir(int userId)1603     private static File getWallpaperDir(int userId) {
1604         return Environment.getUserSystemDirectory(userId);
1605     }
1606 
1607     @Override
finalize()1608     protected void finalize() throws Throwable {
1609         super.finalize();
1610         for (int i = 0; i < mWallpaperMap.size(); i++) {
1611             WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1612             wallpaper.wallpaperObserver.stopWatching();
1613         }
1614     }
1615 
systemReady()1616     void systemReady() {
1617         if (DEBUG) Slog.v(TAG, "systemReady");
1618         initialize();
1619 
1620         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1621         // If we think we're going to be using the system image wallpaper imagery, make
1622         // sure we have something to render
1623         if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
1624             // No crop file? Make sure we've finished the processing sequence if necessary
1625             if (!wallpaper.cropExists()) {
1626                 if (DEBUG) {
1627                     Slog.i(TAG, "No crop; regenerating from source");
1628                 }
1629                 generateCrop(wallpaper);
1630             }
1631             // Still nothing?  Fall back to default.
1632             if (!wallpaper.cropExists()) {
1633                 if (DEBUG) {
1634                     Slog.i(TAG, "Unable to regenerate crop; resetting");
1635                 }
1636                 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
1637             }
1638         } else {
1639             if (DEBUG) {
1640                 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
1641             }
1642         }
1643 
1644         IntentFilter userFilter = new IntentFilter();
1645         userFilter.addAction(Intent.ACTION_USER_REMOVED);
1646         mContext.registerReceiver(new BroadcastReceiver() {
1647             @Override
1648             public void onReceive(Context context, Intent intent) {
1649                 final String action = intent.getAction();
1650                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
1651                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
1652                             UserHandle.USER_NULL));
1653                 }
1654             }
1655         }, userFilter);
1656 
1657         final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
1658         mContext.registerReceiver(new BroadcastReceiver() {
1659             @Override
1660             public void onReceive(Context context, Intent intent) {
1661                 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
1662                     if (DEBUG) {
1663                         Slog.i(TAG, "Shutting down");
1664                     }
1665                     synchronized (mLock) {
1666                         mShuttingDown = true;
1667                     }
1668                 }
1669             }
1670         }, shutdownFilter);
1671 
1672         try {
1673             ActivityManager.getService().registerUserSwitchObserver(
1674                     new UserSwitchObserver() {
1675                         @Override
1676                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
1677                             switchUser(newUserId, reply);
1678                         }
1679                     }, TAG);
1680         } catch (RemoteException e) {
1681             e.rethrowAsRuntimeException();
1682         }
1683     }
1684 
1685     /** Called by SystemBackupAgent */
getName()1686     public String getName() {
1687         // Verify caller is the system
1688         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1689             throw new RuntimeException("getName() can only be called from the system process");
1690         }
1691         synchronized (mLock) {
1692             return mWallpaperMap.get(0).name;
1693         }
1694     }
1695 
stopObserver(WallpaperData wallpaper)1696     void stopObserver(WallpaperData wallpaper) {
1697         if (wallpaper != null) {
1698             if (wallpaper.wallpaperObserver != null) {
1699                 wallpaper.wallpaperObserver.stopWatching();
1700                 wallpaper.wallpaperObserver = null;
1701             }
1702         }
1703     }
1704 
stopObserversLocked(int userId)1705     void stopObserversLocked(int userId) {
1706         stopObserver(mWallpaperMap.get(userId));
1707         stopObserver(mLockWallpaperMap.get(userId));
1708         mWallpaperMap.remove(userId);
1709         mLockWallpaperMap.remove(userId);
1710     }
1711 
1712     @Override
onBootPhase(int phase)1713     public void onBootPhase(int phase) {
1714         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
1715             systemReady();
1716         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1717             switchUser(UserHandle.USER_SYSTEM, null);
1718         }
1719     }
1720 
1721     @Override
onUnlockUser(final int userId)1722     public void onUnlockUser(final int userId) {
1723         synchronized (mLock) {
1724             if (mCurrentUserId == userId) {
1725                 if (mWaitingForUnlock) {
1726                     // the desired wallpaper is not direct-boot aware, load it now
1727                     final WallpaperData systemWallpaper =
1728                             getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1729                     switchWallpaper(systemWallpaper, null);
1730                     notifyCallbacksLocked(systemWallpaper);
1731                 }
1732 
1733                 // Make sure that the SELinux labeling of all the relevant files is correct.
1734                 // This corrects for mislabeling bugs that might have arisen from move-to
1735                 // operations involving the wallpaper files.  This isn't timing-critical,
1736                 // so we do it in the background to avoid holding up the user unlock operation.
1737                 if (!mUserRestorecon.get(userId)) {
1738                     mUserRestorecon.put(userId, true);
1739                     Runnable relabeler = new Runnable() {
1740                         @Override
1741                         public void run() {
1742                             final File wallpaperDir = getWallpaperDir(userId);
1743                             for (String filename : sPerUserFiles) {
1744                                 File f = new File(wallpaperDir, filename);
1745                                 if (f.exists()) {
1746                                     SELinux.restorecon(f);
1747                                 }
1748                             }
1749                         }
1750                     };
1751                     BackgroundThread.getHandler().post(relabeler);
1752                 }
1753             }
1754         }
1755     }
1756 
onRemoveUser(int userId)1757     void onRemoveUser(int userId) {
1758         if (userId < 1) return;
1759 
1760         final File wallpaperDir = getWallpaperDir(userId);
1761         synchronized (mLock) {
1762             stopObserversLocked(userId);
1763             for (String filename : sPerUserFiles) {
1764                 new File(wallpaperDir, filename).delete();
1765             }
1766             mUserRestorecon.delete(userId);
1767         }
1768     }
1769 
switchUser(int userId, IRemoteCallback reply)1770     void switchUser(int userId, IRemoteCallback reply) {
1771         final WallpaperData systemWallpaper;
1772         final WallpaperData lockWallpaper;
1773         synchronized (mLock) {
1774             if (mCurrentUserId == userId) {
1775                 return;
1776             }
1777             mCurrentUserId = userId;
1778             systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1779             final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
1780             lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
1781             // Not started watching yet, in case wallpaper data was loaded for other reasons.
1782             if (systemWallpaper.wallpaperObserver == null) {
1783                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
1784                 systemWallpaper.wallpaperObserver.startWatching();
1785             }
1786             switchWallpaper(systemWallpaper, reply);
1787         }
1788 
1789         // Offload color extraction to another thread since switchUser will be called
1790         // from the main thread.
1791         FgThread.getHandler().post(() -> {
1792             notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
1793             notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
1794             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
1795         });
1796     }
1797 
switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply)1798     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
1799         synchronized (mLock) {
1800             mWaitingForUnlock = false;
1801             final ComponentName cname = wallpaper.wallpaperComponent != null ?
1802                     wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
1803             if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
1804                 // We failed to bind the desired wallpaper, but that might
1805                 // happen if the wallpaper isn't direct-boot aware
1806                 ServiceInfo si = null;
1807                 try {
1808                     si = mIPackageManager.getServiceInfo(cname,
1809                             PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
1810                 } catch (RemoteException ignored) {
1811                 }
1812 
1813                 if (si == null) {
1814                     Slog.w(TAG, "Failure starting previous wallpaper; clearing");
1815                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
1816                 } else {
1817                     Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
1818                     // We might end up persisting the current wallpaper data
1819                     // while locked, so pretend like the component was actually
1820                     // bound into place
1821                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
1822                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
1823                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1824                     ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
1825                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
1826                     mWaitingForUnlock = true;
1827                 }
1828             }
1829         }
1830     }
1831 
1832     @Override
clearWallpaper(String callingPackage, int which, int userId)1833     public void clearWallpaper(String callingPackage, int which, int userId) {
1834         if (DEBUG) Slog.v(TAG, "clearWallpaper");
1835         checkPermission(android.Manifest.permission.SET_WALLPAPER);
1836         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1837             return;
1838         }
1839         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1840                 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
1841 
1842         WallpaperData data = null;
1843         synchronized (mLock) {
1844             clearWallpaperLocked(false, which, userId, null);
1845 
1846             if (which == FLAG_LOCK) {
1847                 data = mLockWallpaperMap.get(userId);
1848             }
1849             if (which == FLAG_SYSTEM || data == null) {
1850                 data = mWallpaperMap.get(userId);
1851             }
1852         }
1853 
1854         // When clearing a wallpaper, broadcast new valid colors
1855         if (data != null) {
1856             notifyWallpaperColorsChanged(data, which);
1857             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
1858         }
1859     }
1860 
clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply)1861     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
1862         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1863             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
1864         }
1865 
1866         WallpaperData wallpaper = null;
1867         if (which == FLAG_LOCK) {
1868             wallpaper = mLockWallpaperMap.get(userId);
1869             if (wallpaper == null) {
1870                 // It's already gone; we're done.
1871                 if (DEBUG) {
1872                     Slog.i(TAG, "Lock wallpaper already cleared");
1873                 }
1874                 return;
1875             }
1876         } else {
1877             wallpaper = mWallpaperMap.get(userId);
1878             if (wallpaper == null) {
1879                 // Might need to bring it in the first time to establish our rewrite
1880                 loadSettingsLocked(userId, false);
1881                 wallpaper = mWallpaperMap.get(userId);
1882             }
1883         }
1884         if (wallpaper == null) {
1885             return;
1886         }
1887 
1888         final long ident = Binder.clearCallingIdentity();
1889         try {
1890             if (wallpaper.wallpaperFile.exists()) {
1891                 wallpaper.wallpaperFile.delete();
1892                 wallpaper.cropFile.delete();
1893                 if (which == FLAG_LOCK) {
1894                     mLockWallpaperMap.remove(userId);
1895                     final IWallpaperManagerCallback cb = mKeyguardListener;
1896                     if (cb != null) {
1897                         if (DEBUG) {
1898                             Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
1899                         }
1900                         try {
1901                             cb.onWallpaperChanged();
1902                         } catch (RemoteException e) {
1903                             // Oh well it went away; no big deal
1904                         }
1905                     }
1906                     saveSettingsLocked(userId);
1907                     return;
1908                 }
1909             }
1910 
1911             RuntimeException e = null;
1912             try {
1913                 wallpaper.primaryColors = null;
1914                 wallpaper.imageWallpaperPending = false;
1915                 if (userId != mCurrentUserId) return;
1916                 if (bindWallpaperComponentLocked(defaultFailed
1917                         ? mImageWallpaper
1918                                 : null, true, false, wallpaper, reply)) {
1919                     return;
1920                 }
1921             } catch (IllegalArgumentException e1) {
1922                 e = e1;
1923             }
1924 
1925             // This can happen if the default wallpaper component doesn't
1926             // exist.  This should be a system configuration problem, but
1927             // let's not let it crash the system and just live with no
1928             // wallpaper.
1929             Slog.e(TAG, "Default wallpaper component not found!", e);
1930             clearWallpaperComponentLocked(wallpaper);
1931             if (reply != null) {
1932                 try {
1933                     reply.sendResult(null);
1934                 } catch (RemoteException e1) {
1935                 }
1936             }
1937         } finally {
1938             Binder.restoreCallingIdentity(ident);
1939         }
1940     }
1941 
hasNamedWallpaper(String name)1942     public boolean hasNamedWallpaper(String name) {
1943         synchronized (mLock) {
1944             List<UserInfo> users;
1945             long ident = Binder.clearCallingIdentity();
1946             try {
1947                 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
1948             } finally {
1949                 Binder.restoreCallingIdentity(ident);
1950             }
1951             for (UserInfo user: users) {
1952                 // ignore managed profiles
1953                 if (user.isManagedProfile()) {
1954                     continue;
1955                 }
1956                 WallpaperData wd = mWallpaperMap.get(user.id);
1957                 if (wd == null) {
1958                     // User hasn't started yet, so load her settings to peek at the wallpaper
1959                     loadSettingsLocked(user.id, false);
1960                     wd = mWallpaperMap.get(user.id);
1961                 }
1962                 if (wd != null && name.equals(wd.name)) {
1963                     return true;
1964                 }
1965             }
1966         }
1967         return false;
1968     }
1969 
isValidDisplay(int displayId)1970     private boolean isValidDisplay(int displayId) {
1971         return mDisplayManager.getDisplay(displayId) != null;
1972     }
1973 
1974     /**
1975      * Sets the dimension hint for the wallpaper. These hints indicate the desired
1976      * minimum width and height for the wallpaper in a particular display.
1977      */
setDimensionHints(int width, int height, String callingPackage, int displayId)1978     public void setDimensionHints(int width, int height, String callingPackage, int displayId)
1979             throws RemoteException {
1980         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1981         if (!isWallpaperSupported(callingPackage)) {
1982             return;
1983         }
1984         synchronized (mLock) {
1985             int userId = UserHandle.getCallingUserId();
1986             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1987             if (width <= 0 || height <= 0) {
1988                 throw new IllegalArgumentException("width and height must be > 0");
1989             }
1990 
1991             if (!isValidDisplay(displayId)) {
1992                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
1993             }
1994 
1995             final DisplayData wpdData = getDisplayDataOrCreate(displayId);
1996             if (width != wpdData.mWidth || height != wpdData.mHeight) {
1997                 wpdData.mWidth = width;
1998                 wpdData.mHeight = height;
1999                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
2000                 if (mCurrentUserId != userId) return; // Don't change the properties now
2001                 if (wallpaper.connection != null) {
2002                     final WallpaperConnection.DisplayConnector connector = wallpaper.connection
2003                             .getDisplayConnectorOrCreate(displayId);
2004                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
2005                     if (engine != null) {
2006                         try {
2007                             engine.setDesiredSize(width, height);
2008                         } catch (RemoteException e) {
2009                         }
2010                         notifyCallbacksLocked(wallpaper);
2011                     } else if (wallpaper.connection.mService != null && connector != null) {
2012                         // We've attached to the service but the engine hasn't attached back to us
2013                         // yet. This means it will be created with the previous dimensions, so we
2014                         // need to update it to the new dimensions once it attaches.
2015                         connector.mDimensionsChanged = true;
2016                     }
2017                 }
2018             }
2019         }
2020     }
2021 
2022     /**
2023      * Returns the desired minimum width for the wallpaper in a particular display.
2024      */
getWidthHint(int displayId)2025     public int getWidthHint(int displayId) throws RemoteException {
2026         synchronized (mLock) {
2027             if (!isValidDisplay(displayId)) {
2028                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2029             }
2030             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
2031             if (wallpaper != null) {
2032                 final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2033                 return wpdData.mWidth;
2034             } else {
2035                 return 0;
2036             }
2037         }
2038     }
2039 
2040     /**
2041      * Returns the desired minimum height for the wallpaper in a particular display.
2042      */
getHeightHint(int displayId)2043     public int getHeightHint(int displayId) throws RemoteException {
2044         synchronized (mLock) {
2045             if (!isValidDisplay(displayId)) {
2046                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2047             }
2048             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
2049             if (wallpaper != null) {
2050                 final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2051                 return wpdData.mHeight;
2052             } else {
2053                 return 0;
2054             }
2055         }
2056     }
2057 
2058     /**
2059      * Sets extra padding that we would like the wallpaper to have outside of the display.
2060      */
setDisplayPadding(Rect padding, String callingPackage, int displayId)2061     public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
2062         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
2063         if (!isWallpaperSupported(callingPackage)) {
2064             return;
2065         }
2066         synchronized (mLock) {
2067             if (!isValidDisplay(displayId)) {
2068                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2069             }
2070             int userId = UserHandle.getCallingUserId();
2071             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
2072             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
2073                 throw new IllegalArgumentException("padding must be positive: " + padding);
2074             }
2075 
2076             final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2077             if (!padding.equals(wpdData.mPadding)) {
2078                 wpdData.mPadding.set(padding);
2079                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
2080                 if (mCurrentUserId != userId) return; // Don't change the properties now
2081                 if (wallpaper.connection != null) {
2082                     final WallpaperConnection.DisplayConnector connector = wallpaper.connection
2083                             .getDisplayConnectorOrCreate(displayId);
2084                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
2085                     if (engine != null) {
2086                         try {
2087                             engine.setDisplayPadding(padding);
2088                         } catch (RemoteException e) {
2089                         }
2090                         notifyCallbacksLocked(wallpaper);
2091                     } else if (wallpaper.connection.mService != null && connector != null) {
2092                         // We've attached to the service but the engine hasn't attached back to us
2093                         // yet. This means it will be created with the previous dimensions, so we
2094                         // need to update it to the new dimensions once it attaches.
2095                         connector.mPaddingChanged = true;
2096                     }
2097                 }
2098             }
2099         }
2100     }
2101 
2102     @Override
getWallpaper(String callingPkg, IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId)2103     public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
2104             final int which, Bundle outParams, int wallpaperUserId) {
2105         final int hasPrivilege = mContext.checkCallingOrSelfPermission(
2106                 android.Manifest.permission.READ_WALLPAPER_INTERNAL);
2107         if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
2108             mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
2109                     Binder.getCallingPid(), Binder.getCallingUid(), callingPkg);
2110         }
2111 
2112         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2113                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
2114 
2115         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
2116             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
2117         }
2118 
2119         synchronized (mLock) {
2120             final SparseArray<WallpaperData> whichSet =
2121                     (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2122             WallpaperData wallpaper = whichSet.get(wallpaperUserId);
2123             if (wallpaper == null) {
2124                 // There is no established wallpaper imagery of this type (expected
2125                 // only for lock wallpapers; a system WallpaperData is established at
2126                 // user switch)
2127                 return null;
2128             }
2129             // Only for default display.
2130             final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
2131             try {
2132                 if (outParams != null) {
2133                     outParams.putInt("width", wpdData.mWidth);
2134                     outParams.putInt("height", wpdData.mHeight);
2135                 }
2136                 if (cb != null) {
2137                     wallpaper.callbacks.register(cb);
2138                 }
2139                 if (!wallpaper.cropFile.exists()) {
2140                     return null;
2141                 }
2142                 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
2143             } catch (FileNotFoundException e) {
2144                 /* Shouldn't happen as we check to see if the file exists */
2145                 Slog.w(TAG, "Error getting wallpaper", e);
2146             }
2147             return null;
2148         }
2149     }
2150 
2151     @Override
getWallpaperInfo(int userId)2152     public WallpaperInfo getWallpaperInfo(int userId) {
2153         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2154                 Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
2155         synchronized (mLock) {
2156             WallpaperData wallpaper = mWallpaperMap.get(userId);
2157             if (wallpaper != null && wallpaper.connection != null) {
2158                 return wallpaper.connection.mInfo;
2159             }
2160             return null;
2161         }
2162     }
2163 
2164     @Override
getWallpaperIdForUser(int which, int userId)2165     public int getWallpaperIdForUser(int which, int userId) {
2166         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2167                 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
2168 
2169         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
2170             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
2171         }
2172 
2173         final SparseArray<WallpaperData> map =
2174                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2175         synchronized (mLock) {
2176             WallpaperData wallpaper = map.get(userId);
2177             if (wallpaper != null) {
2178                 return wallpaper.wallpaperId;
2179             }
2180         }
2181         return -1;
2182     }
2183 
2184     @Override
registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2185     public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
2186             int displayId) {
2187         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2188                 userId, true, true, "registerWallpaperColorsCallback", null);
2189         synchronized (mLock) {
2190             SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
2191                     userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
2192             if (userDisplayColorsChangedListeners == null) {
2193                 userDisplayColorsChangedListeners = new SparseArray<>();
2194                 mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
2195             }
2196             RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
2197                     userDisplayColorsChangedListeners.get(displayId);
2198             if (displayChangedListeners == null) {
2199                 displayChangedListeners = new RemoteCallbackList<>();
2200                 userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
2201             }
2202             displayChangedListeners.register(cb);
2203         }
2204     }
2205 
2206     @Override
unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2207     public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
2208             int displayId) {
2209         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2210                 userId, true, true, "unregisterWallpaperColorsCallback", null);
2211         synchronized (mLock) {
2212             SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
2213                     userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
2214             if (userDisplayColorsChangedListeners != null) {
2215                 RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
2216                         userDisplayColorsChangedListeners.get(displayId);
2217                 if (displayChangedListeners != null) {
2218                     displayChangedListeners.unregister(cb);
2219                 }
2220             }
2221         }
2222     }
2223 
2224     /**
2225      * TODO(multi-display) Extends this method with specific display.
2226      * Propagate ambient state to wallpaper engine.
2227      *
2228      * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
2229      * @param animationDuration Duration of the animation, or 0 when immediate.
2230      */
setInAmbientMode(boolean inAmbientMode, long animationDuration)2231     public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
2232         final IWallpaperEngine engine;
2233         synchronized (mLock) {
2234             mInAmbientMode = inAmbientMode;
2235             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
2236             // The wallpaper info is null for image wallpaper, also use the engine in this case.
2237             if (data != null && data.connection != null && (data.connection.mInfo == null
2238                     || data.connection.mInfo.supportsAmbientMode())) {
2239                 // TODO(multi-display) Extends this method with specific display.
2240                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
2241             } else {
2242                 engine = null;
2243             }
2244         }
2245 
2246         if (engine != null) {
2247             try {
2248                 engine.setInAmbientMode(inAmbientMode, animationDuration);
2249             } catch (RemoteException e) {
2250                 // Cannot talk to wallpaper engine.
2251             }
2252         }
2253     }
2254 
2255     @Override
setLockWallpaperCallback(IWallpaperManagerCallback cb)2256     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
2257         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
2258         synchronized (mLock) {
2259             mKeyguardListener = cb;
2260         }
2261         return true;
2262     }
2263 
2264     @Override
getWallpaperColors(int which, int userId, int displayId)2265     public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
2266             throws RemoteException {
2267         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
2268             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
2269         }
2270         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2271                 userId, false, true, "getWallpaperColors", null);
2272 
2273         WallpaperData wallpaperData = null;
2274         boolean shouldExtract;
2275 
2276         synchronized (mLock) {
2277             if (which == FLAG_LOCK) {
2278                 wallpaperData = mLockWallpaperMap.get(userId);
2279             }
2280 
2281             // Try to get the system wallpaper anyway since it might
2282             // also be the lock screen wallpaper
2283             if (wallpaperData == null) {
2284                 wallpaperData = findWallpaperAtDisplay(userId, displayId);
2285             }
2286 
2287             if (wallpaperData == null) {
2288                 return null;
2289             }
2290             shouldExtract = wallpaperData.primaryColors == null;
2291         }
2292 
2293         if (shouldExtract) {
2294             extractColors(wallpaperData);
2295         }
2296 
2297         synchronized (mLock) {
2298             return wallpaperData.primaryColors;
2299         }
2300     }
2301 
findWallpaperAtDisplay(int userId, int displayId)2302     private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
2303         if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
2304                 && mFallbackWallpaper.connection.containsDisplay(displayId)) {
2305             return mFallbackWallpaper;
2306         } else {
2307             return mWallpaperMap.get(userId);
2308         }
2309     }
2310 
2311     @Override
setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, IWallpaperManagerCallback completion, int userId)2312     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
2313             Rect cropHint, boolean allowBackup, Bundle extras, int which,
2314             IWallpaperManagerCallback completion, int userId) {
2315         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
2316                 false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
2317         checkPermission(android.Manifest.permission.SET_WALLPAPER);
2318 
2319         if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
2320             final String msg = "Must specify a valid wallpaper category to set";
2321             Slog.e(TAG, msg);
2322             throw new IllegalArgumentException(msg);
2323         }
2324 
2325         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
2326             return null;
2327         }
2328 
2329         // "null" means the no-op crop, preserving the full input image
2330         if (cropHint == null) {
2331             cropHint = new Rect(0, 0, 0, 0);
2332         } else {
2333             if (cropHint.isEmpty()
2334                     || cropHint.left < 0
2335                     || cropHint.top < 0) {
2336                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
2337             }
2338         }
2339 
2340         synchronized (mLock) {
2341             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
2342             WallpaperData wallpaper;
2343 
2344             /* If we're setting system but not lock, and lock is currently sharing the system
2345              * wallpaper, we need to migrate that image over to being lock-only before
2346              * the caller here writes new bitmap data.
2347              */
2348             if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
2349                 if (DEBUG) {
2350                     Slog.i(TAG, "Migrating system->lock to preserve");
2351                 }
2352                 migrateSystemToLockWallpaperLocked(userId);
2353             }
2354 
2355             wallpaper = getWallpaperSafeLocked(userId, which);
2356             final long ident = Binder.clearCallingIdentity();
2357             try {
2358                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
2359                 if (pfd != null) {
2360                     wallpaper.imageWallpaperPending = true;
2361                     wallpaper.whichPending = which;
2362                     wallpaper.setComplete = completion;
2363                     wallpaper.cropHint.set(cropHint);
2364                     wallpaper.allowBackup = allowBackup;
2365                 }
2366                 return pfd;
2367             } finally {
2368                 Binder.restoreCallingIdentity(ident);
2369             }
2370         }
2371     }
2372 
migrateSystemToLockWallpaperLocked(int userId)2373     private void migrateSystemToLockWallpaperLocked(int userId) {
2374         WallpaperData sysWP = mWallpaperMap.get(userId);
2375         if (sysWP == null) {
2376             if (DEBUG) {
2377                 Slog.i(TAG, "No system wallpaper?  Not tracking for lock-only");
2378             }
2379             return;
2380         }
2381 
2382         // We know a-priori that there is no lock-only wallpaper currently
2383         WallpaperData lockWP = new WallpaperData(userId,
2384                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2385         lockWP.wallpaperId = sysWP.wallpaperId;
2386         lockWP.cropHint.set(sysWP.cropHint);
2387         lockWP.allowBackup = sysWP.allowBackup;
2388         lockWP.primaryColors = sysWP.primaryColors;
2389 
2390         // Migrate the bitmap files outright; no need to copy
2391         try {
2392             Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
2393             Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
2394         } catch (ErrnoException e) {
2395             Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
2396             lockWP.wallpaperFile.delete();
2397             lockWP.cropFile.delete();
2398             return;
2399         }
2400 
2401         mLockWallpaperMap.put(userId, lockWP);
2402     }
2403 
updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, Bundle extras)2404     ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
2405             Bundle extras) {
2406         if (name == null) name = "";
2407         try {
2408             File dir = getWallpaperDir(wallpaper.userId);
2409             if (!dir.exists()) {
2410                 dir.mkdir();
2411                 FileUtils.setPermissions(
2412                         dir.getPath(),
2413                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
2414                         -1, -1);
2415             }
2416             ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
2417                     MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
2418             if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
2419                 return null;
2420             }
2421             wallpaper.name = name;
2422             wallpaper.wallpaperId = makeWallpaperIdLocked();
2423             if (extras != null) {
2424                 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
2425             }
2426             // Nullify field to require new computation
2427             wallpaper.primaryColors = null;
2428             if (DEBUG) {
2429                 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
2430                         + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
2431             }
2432             return fd;
2433         } catch (FileNotFoundException e) {
2434             Slog.w(TAG, "Error setting wallpaper", e);
2435         }
2436         return null;
2437     }
2438 
2439     @Override
setWallpaperComponentChecked(ComponentName name, String callingPackage, int userId)2440     public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
2441             int userId) {
2442 
2443         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
2444             setWallpaperComponent(name, userId);
2445         }
2446     }
2447 
2448     // ToDo: Remove this version of the function
2449     @Override
setWallpaperComponent(ComponentName name)2450     public void setWallpaperComponent(ComponentName name) {
2451         setWallpaperComponent(name, UserHandle.getCallingUserId());
2452     }
2453 
setWallpaperComponent(ComponentName name, int userId)2454     private void setWallpaperComponent(ComponentName name, int userId) {
2455         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
2456                 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
2457         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
2458 
2459         int which = FLAG_SYSTEM;
2460         boolean shouldNotifyColors = false;
2461         WallpaperData wallpaper;
2462 
2463         synchronized (mLock) {
2464             if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
2465             wallpaper = mWallpaperMap.get(userId);
2466             if (wallpaper == null) {
2467                 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
2468             }
2469             final long ident = Binder.clearCallingIdentity();
2470 
2471             // Live wallpapers can't be specified for keyguard.  If we're using a static
2472             // system+lock image currently, migrate the system wallpaper to be a lock-only
2473             // image as part of making a different live component active as the system
2474             // wallpaper.
2475             if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
2476                 if (mLockWallpaperMap.get(userId) == null) {
2477                     // We're using the static imagery and there is no lock-specific image in place,
2478                     // therefore it's a shared system+lock image that we need to migrate.
2479                     migrateSystemToLockWallpaperLocked(userId);
2480                 }
2481             }
2482 
2483             // New live wallpaper is also a lock wallpaper if nothing is set
2484             if (mLockWallpaperMap.get(userId) == null) {
2485                 which |= FLAG_LOCK;
2486             }
2487 
2488             try {
2489                 wallpaper.imageWallpaperPending = false;
2490                 boolean same = changingToSame(name, wallpaper);
2491                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
2492                     if (!same) {
2493                         wallpaper.primaryColors = null;
2494                     }
2495                     wallpaper.wallpaperId = makeWallpaperIdLocked();
2496                     notifyCallbacksLocked(wallpaper);
2497                     shouldNotifyColors = true;
2498                 }
2499             } finally {
2500                 Binder.restoreCallingIdentity(ident);
2501             }
2502         }
2503 
2504         if (shouldNotifyColors) {
2505             notifyWallpaperColorsChanged(wallpaper, which);
2506             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
2507         }
2508     }
2509 
changingToSame(ComponentName componentName, WallpaperData wallpaper)2510     private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
2511         if (wallpaper.connection != null) {
2512             if (wallpaper.wallpaperComponent == null) {
2513                 if (componentName == null) {
2514                     if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
2515                     // Still using default wallpaper.
2516                     return true;
2517                 }
2518             } else if (wallpaper.wallpaperComponent.equals(componentName)) {
2519                 // Changing to same wallpaper.
2520                 if (DEBUG) Slog.v(TAG, "same wallpaper");
2521                 return true;
2522             }
2523         }
2524         return false;
2525     }
2526 
bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply)2527     private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
2528             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
2529         if (DEBUG_LIVE) {
2530             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
2531         }
2532         // Has the component changed?
2533         if (!force && changingToSame(componentName, wallpaper)) {
2534             return true;
2535         }
2536 
2537         try {
2538             if (componentName == null) {
2539                 componentName = mDefaultWallpaperComponent;
2540                 if (componentName == null) {
2541                     // Fall back to static image wallpaper
2542                     componentName = mImageWallpaper;
2543                     //clearWallpaperComponentLocked();
2544                     //return;
2545                     if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
2546                 }
2547             }
2548             int serviceUserId = wallpaper.userId;
2549             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
2550                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
2551             if (si == null) {
2552                 // The wallpaper component we're trying to use doesn't exist
2553                 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
2554                 return false;
2555             }
2556             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
2557                 String msg = "Selected service does not have "
2558                         + android.Manifest.permission.BIND_WALLPAPER
2559                         + ": " + componentName;
2560                 if (fromUser) {
2561                     throw new SecurityException(msg);
2562                 }
2563                 Slog.w(TAG, msg);
2564                 return false;
2565             }
2566 
2567             WallpaperInfo wi = null;
2568 
2569             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
2570             if (componentName != null && !componentName.equals(mImageWallpaper)) {
2571                 // Make sure the selected service is actually a wallpaper service.
2572                 List<ResolveInfo> ris =
2573                         mIPackageManager.queryIntentServices(intent,
2574                                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2575                                 PackageManager.GET_META_DATA, serviceUserId).getList();
2576                 for (int i=0; i<ris.size(); i++) {
2577                     ServiceInfo rsi = ris.get(i).serviceInfo;
2578                     if (rsi.name.equals(si.name) &&
2579                             rsi.packageName.equals(si.packageName)) {
2580                         try {
2581                             wi = new WallpaperInfo(mContext, ris.get(i));
2582                         } catch (XmlPullParserException e) {
2583                             if (fromUser) {
2584                                 throw new IllegalArgumentException(e);
2585                             }
2586                             Slog.w(TAG, e);
2587                             return false;
2588                         } catch (IOException e) {
2589                             if (fromUser) {
2590                                 throw new IllegalArgumentException(e);
2591                             }
2592                             Slog.w(TAG, e);
2593                             return false;
2594                         }
2595                         break;
2596                     }
2597                 }
2598                 if (wi == null) {
2599                     String msg = "Selected service is not a wallpaper: "
2600                             + componentName;
2601                     if (fromUser) {
2602                         throw new SecurityException(msg);
2603                     }
2604                     Slog.w(TAG, msg);
2605                     return false;
2606                 }
2607             }
2608 
2609             if (wi != null && wi.supportsAmbientMode()) {
2610                 final int hasPrivilege = mIPackageManager.checkPermission(
2611                         android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
2612                         serviceUserId);
2613                 if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
2614                     String msg = "Selected service does not have "
2615                             + android.Manifest.permission.AMBIENT_WALLPAPER
2616                             + ": " + componentName;
2617                     if (fromUser) {
2618                         throw new SecurityException(msg);
2619                     }
2620                     Slog.w(TAG, msg);
2621                     return false;
2622                 }
2623             }
2624 
2625             // Bind the service!
2626             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
2627             final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
2628                     MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
2629             WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
2630             intent.setComponent(componentName);
2631             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
2632                     com.android.internal.R.string.wallpaper_binding_label);
2633             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
2634                     mContext, 0,
2635                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
2636                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
2637                     0, null, new UserHandle(serviceUserId)));
2638             if (!mContext.bindServiceAsUser(intent, newConn,
2639                     Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
2640                             | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
2641                             | Context.BIND_INCLUDE_CAPABILITIES,
2642                     new UserHandle(serviceUserId))) {
2643                 String msg = "Unable to bind service: "
2644                         + componentName;
2645                 if (fromUser) {
2646                     throw new IllegalArgumentException(msg);
2647                 }
2648                 Slog.w(TAG, msg);
2649                 return false;
2650             }
2651             if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
2652                     && !wallpaper.equals(mFallbackWallpaper)) {
2653                 detachWallpaperLocked(mLastWallpaper);
2654             }
2655             wallpaper.wallpaperComponent = componentName;
2656             wallpaper.connection = newConn;
2657             newConn.mReply = reply;
2658             if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
2659                 mLastWallpaper = wallpaper;
2660             }
2661             updateFallbackConnection();
2662         } catch (RemoteException e) {
2663             String msg = "Remote exception for " + componentName + "\n" + e;
2664             if (fromUser) {
2665                 throw new IllegalArgumentException(msg);
2666             }
2667             Slog.w(TAG, msg);
2668             return false;
2669         }
2670         return true;
2671     }
2672 
detachWallpaperLocked(WallpaperData wallpaper)2673     private void detachWallpaperLocked(WallpaperData wallpaper) {
2674         if (wallpaper.connection != null) {
2675             if (wallpaper.connection.mReply != null) {
2676                 try {
2677                     wallpaper.connection.mReply.sendResult(null);
2678                 } catch (RemoteException e) {
2679                 }
2680                 wallpaper.connection.mReply = null;
2681             }
2682             try {
2683                 // It can be null if user switching happens before service connection.
2684                 if (wallpaper.connection.mService != null) {
2685                     wallpaper.connection.mService.detach();
2686                 }
2687             } catch (RemoteException e) {
2688                 Slog.w(TAG, "Failed detaching wallpaper service ", e);
2689             }
2690             mContext.unbindService(wallpaper.connection);
2691             wallpaper.connection.forEachDisplayConnector(
2692                     WallpaperConnection.DisplayConnector::disconnectLocked);
2693             wallpaper.connection.mService = null;
2694             wallpaper.connection.mDisplayConnector.clear();
2695             wallpaper.connection = null;
2696             if (wallpaper == mLastWallpaper) mLastWallpaper = null;
2697         }
2698     }
2699 
clearWallpaperComponentLocked(WallpaperData wallpaper)2700     private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
2701         wallpaper.wallpaperComponent = null;
2702         detachWallpaperLocked(wallpaper);
2703     }
2704 
attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper)2705     private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
2706         conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
2707     }
2708 
notifyCallbacksLocked(WallpaperData wallpaper)2709     private void notifyCallbacksLocked(WallpaperData wallpaper) {
2710         final int n = wallpaper.callbacks.beginBroadcast();
2711         for (int i = 0; i < n; i++) {
2712             try {
2713                 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
2714             } catch (RemoteException e) {
2715 
2716                 // The RemoteCallbackList will take care of removing
2717                 // the dead object for us.
2718             }
2719         }
2720         wallpaper.callbacks.finishBroadcast();
2721 
2722         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
2723         mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
2724     }
2725 
checkPermission(String permission)2726     private void checkPermission(String permission) {
2727         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
2728             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
2729                     + ", must have permission " + permission);
2730         }
2731     }
2732 
2733     /**
2734      * Certain user types do not support wallpapers (e.g. managed profiles). The check is
2735      * implemented through through the OP_WRITE_WALLPAPER AppOp.
2736      */
isWallpaperSupported(String callingPackage)2737     public boolean isWallpaperSupported(String callingPackage) {
2738         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
2739                 callingPackage) == AppOpsManager.MODE_ALLOWED;
2740     }
2741 
2742     @Override
isSetWallpaperAllowed(String callingPackage)2743     public boolean isSetWallpaperAllowed(String callingPackage) {
2744         final PackageManager pm = mContext.getPackageManager();
2745         String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
2746         boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
2747         if (!uidMatchPackage) {
2748             return false;   // callingPackage was faked.
2749         }
2750 
2751         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
2752         if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
2753             return true;
2754         }
2755         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
2756         return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
2757     }
2758 
2759     @Override
isWallpaperBackupEligible(int which, int userId)2760     public boolean isWallpaperBackupEligible(int which, int userId) {
2761         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2762             throw new SecurityException("Only the system may call isWallpaperBackupEligible");
2763         }
2764 
2765         WallpaperData wallpaper = (which == FLAG_LOCK)
2766                 ? mLockWallpaperMap.get(userId)
2767                 : mWallpaperMap.get(userId);
2768         return (wallpaper != null) ? wallpaper.allowBackup : false;
2769     }
2770 
onDisplayReadyInternal(int displayId)2771     private void onDisplayReadyInternal(int displayId) {
2772         synchronized (mLock) {
2773             if (mLastWallpaper == null) {
2774                 return;
2775             }
2776             if (supportsMultiDisplay(mLastWallpaper.connection)) {
2777                 final WallpaperConnection.DisplayConnector connector =
2778                         mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
2779                 if (connector == null) return;
2780                 connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
2781                 return;
2782             }
2783             // System wallpaper does not support multiple displays, attach this display to
2784             // the fallback wallpaper.
2785             if (mFallbackWallpaper != null) {
2786                 final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
2787                         .connection.getDisplayConnectorOrCreate(displayId);
2788                 if (connector == null) return;
2789                 connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
2790             } else {
2791                 Slog.w(TAG, "No wallpaper can be added to the new display");
2792             }
2793         }
2794     }
2795 
makeJournaledFile(int userId)2796     private static JournaledFile makeJournaledFile(int userId) {
2797         final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
2798         return new JournaledFile(new File(base), new File(base + ".tmp"));
2799     }
2800 
saveSettingsLocked(int userId)2801     private void saveSettingsLocked(int userId) {
2802         JournaledFile journal = makeJournaledFile(userId);
2803         FileOutputStream fstream = null;
2804         BufferedOutputStream stream = null;
2805         try {
2806             XmlSerializer out = new FastXmlSerializer();
2807             fstream = new FileOutputStream(journal.chooseForWrite(), false);
2808             stream = new BufferedOutputStream(fstream);
2809             out.setOutput(stream, StandardCharsets.UTF_8.name());
2810             out.startDocument(null, true);
2811 
2812             WallpaperData wallpaper;
2813 
2814             wallpaper = mWallpaperMap.get(userId);
2815             if (wallpaper != null) {
2816                 writeWallpaperAttributes(out, "wp", wallpaper);
2817             }
2818             wallpaper = mLockWallpaperMap.get(userId);
2819             if (wallpaper != null) {
2820                 writeWallpaperAttributes(out, "kwp", wallpaper);
2821             }
2822 
2823             out.endDocument();
2824 
2825             stream.flush(); // also flushes fstream
2826             FileUtils.sync(fstream);
2827             stream.close(); // also closes fstream
2828             journal.commit();
2829         } catch (IOException e) {
2830             IoUtils.closeQuietly(stream);
2831             journal.rollback();
2832         }
2833     }
2834 
writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)2835     private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
2836             throws IllegalArgumentException, IllegalStateException, IOException {
2837         if (DEBUG) {
2838             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
2839         }
2840         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
2841         out.startTag(null, tag);
2842         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
2843         out.attribute(null, "width", Integer.toString(wpdData.mWidth));
2844         out.attribute(null, "height", Integer.toString(wpdData.mHeight));
2845 
2846         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
2847         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
2848         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
2849         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
2850 
2851         if (wpdData.mPadding.left != 0) {
2852             out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
2853         }
2854         if (wpdData.mPadding.top != 0) {
2855             out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
2856         }
2857         if (wpdData.mPadding.right != 0) {
2858             out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
2859         }
2860         if (wpdData.mPadding.bottom != 0) {
2861             out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
2862         }
2863 
2864         if (wallpaper.primaryColors != null) {
2865             int colorsCount = wallpaper.primaryColors.getMainColors().size();
2866             out.attribute(null, "colorsCount", Integer.toString(colorsCount));
2867             if (colorsCount > 0) {
2868                 for (int i = 0; i < colorsCount; i++) {
2869                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
2870                     out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
2871                 }
2872             }
2873             out.attribute(null, "colorHints",
2874                     Integer.toString(wallpaper.primaryColors.getColorHints()));
2875         }
2876 
2877         out.attribute(null, "name", wallpaper.name);
2878         if (wallpaper.wallpaperComponent != null
2879                 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
2880             out.attribute(null, "component",
2881                     wallpaper.wallpaperComponent.flattenToShortString());
2882         }
2883 
2884         if (wallpaper.allowBackup) {
2885             out.attribute(null, "backup", "true");
2886         }
2887 
2888         out.endTag(null, tag);
2889     }
2890 
migrateFromOld()2891     private void migrateFromOld() {
2892         // Pre-N, what existed is the one we're now using as the display crop
2893         File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
2894         // In the very-long-ago, imagery lived with the settings app
2895         File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
2896         File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
2897 
2898         // Migrations from earlier wallpaper image storage schemas
2899         if (preNWallpaper.exists()) {
2900             if (!newWallpaper.exists()) {
2901                 // we've got the 'wallpaper' crop file but not the nominal source image,
2902                 // so do the simple "just take everything" straight copy of legacy data
2903                 if (DEBUG) {
2904                     Slog.i(TAG, "Migrating wallpaper schema");
2905                 }
2906                 FileUtils.copyFile(preNWallpaper, newWallpaper);
2907             } // else we're in the usual modern case: both source & crop exist
2908         } else if (originalWallpaper.exists()) {
2909             // VERY old schema; make sure things exist and are in the right place
2910             if (DEBUG) {
2911                 Slog.i(TAG, "Migrating antique wallpaper schema");
2912             }
2913             File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
2914             if (oldInfo.exists()) {
2915                 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
2916                 oldInfo.renameTo(newInfo);
2917             }
2918 
2919             FileUtils.copyFile(originalWallpaper, preNWallpaper);
2920             originalWallpaper.renameTo(newWallpaper);
2921         }
2922     }
2923 
getAttributeInt(XmlPullParser parser, String name, int defValue)2924     private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
2925         String value = parser.getAttributeValue(null, name);
2926         if (value == null) {
2927             return defValue;
2928         }
2929         return Integer.parseInt(value);
2930     }
2931 
2932     /**
2933      * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
2934      * happen during user switch.  The async user switch observer may not have received
2935      * the event yet.  We use this safe method when we don't care about this ordering and just
2936      * want to update the data.  The data is going to be applied when the user switch observer
2937      * is eventually executed.
2938      *
2939      * Important: this method loads settings to initialize the given user's wallpaper data if
2940      * there is no current in-memory state.
2941      */
getWallpaperSafeLocked(int userId, int which)2942     private WallpaperData getWallpaperSafeLocked(int userId, int which) {
2943         // We're setting either just system (work with the system wallpaper),
2944         // both (also work with the system wallpaper), or just the lock
2945         // wallpaper (update against the existing lock wallpaper if any).
2946         // Combined or just-system operations use the 'system' WallpaperData
2947         // for this use; lock-only operations use the dedicated one.
2948         final SparseArray<WallpaperData> whichSet =
2949                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2950         WallpaperData wallpaper = whichSet.get(userId);
2951         if (wallpaper == null) {
2952             // common case, this is the first lookup post-boot of the system or
2953             // unified lock, so we bring up the saved state lazily now and recheck.
2954             loadSettingsLocked(userId, false);
2955             wallpaper = whichSet.get(userId);
2956             // if it's still null here, this is a lock-only operation and there is not
2957             // yet a lock-only wallpaper set for this user, so we need to establish
2958             // it now.
2959             if (wallpaper == null) {
2960                 if (which == FLAG_LOCK) {
2961                     wallpaper = new WallpaperData(userId,
2962                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2963                     mLockWallpaperMap.put(userId, wallpaper);
2964                     ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
2965                 } else {
2966                     // sanity fallback: we're in bad shape, but establishing a known
2967                     // valid system+lock WallpaperData will keep us from dying.
2968                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
2969                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2970                     mWallpaperMap.put(userId, wallpaper);
2971                     ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
2972                 }
2973             }
2974         }
2975         return wallpaper;
2976     }
2977 
loadSettingsLocked(int userId, boolean keepDimensionHints)2978     private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
2979         JournaledFile journal = makeJournaledFile(userId);
2980         FileInputStream stream = null;
2981         File file = journal.chooseForRead();
2982 
2983         WallpaperData wallpaper = mWallpaperMap.get(userId);
2984         if (wallpaper == null) {
2985             // Do this once per boot
2986             migrateFromOld();
2987 
2988             wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2989             wallpaper.allowBackup = true;
2990             mWallpaperMap.put(userId, wallpaper);
2991             if (!wallpaper.cropExists()) {
2992                 if (wallpaper.sourceExists()) {
2993                     generateCrop(wallpaper);
2994                 } else {
2995                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
2996                 }
2997             }
2998             initializeFallbackWallpaper();
2999         }
3000         boolean success = false;
3001         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
3002         try {
3003             stream = new FileInputStream(file);
3004             XmlPullParser parser = Xml.newPullParser();
3005             parser.setInput(stream, StandardCharsets.UTF_8.name());
3006 
3007             int type;
3008             do {
3009                 type = parser.next();
3010                 if (type == XmlPullParser.START_TAG) {
3011                     String tag = parser.getName();
3012                     if ("wp".equals(tag)) {
3013                         // Common to system + lock wallpapers
3014                         parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
3015 
3016                         // A system wallpaper might also be a live wallpaper
3017                         String comp = parser.getAttributeValue(null, "component");
3018                         wallpaper.nextWallpaperComponent = comp != null
3019                                 ? ComponentName.unflattenFromString(comp)
3020                                 : null;
3021                         if (wallpaper.nextWallpaperComponent == null
3022                                 || "android".equals(wallpaper.nextWallpaperComponent
3023                                         .getPackageName())) {
3024                             wallpaper.nextWallpaperComponent = mImageWallpaper;
3025                         }
3026 
3027                         if (DEBUG) {
3028                             Slog.v(TAG, "mWidth:" + wpdData.mWidth);
3029                             Slog.v(TAG, "mHeight:" + wpdData.mHeight);
3030                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
3031                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
3032                             Slog.v(TAG, "mName:" + wallpaper.name);
3033                             Slog.v(TAG, "mNextWallpaperComponent:"
3034                                     + wallpaper.nextWallpaperComponent);
3035                         }
3036                     } else if ("kwp".equals(tag)) {
3037                         // keyguard-specific wallpaper for this user
3038                         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
3039                         if (lockWallpaper == null) {
3040                             lockWallpaper = new WallpaperData(userId,
3041                                     WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
3042                             mLockWallpaperMap.put(userId, lockWallpaper);
3043                         }
3044                         parseWallpaperAttributes(parser, lockWallpaper, false);
3045                     }
3046                 }
3047             } while (type != XmlPullParser.END_DOCUMENT);
3048             success = true;
3049         } catch (FileNotFoundException e) {
3050             Slog.w(TAG, "no current wallpaper -- first boot?");
3051         } catch (NullPointerException e) {
3052             Slog.w(TAG, "failed parsing " + file + " " + e);
3053         } catch (NumberFormatException e) {
3054             Slog.w(TAG, "failed parsing " + file + " " + e);
3055         } catch (XmlPullParserException e) {
3056             Slog.w(TAG, "failed parsing " + file + " " + e);
3057         } catch (IOException e) {
3058             Slog.w(TAG, "failed parsing " + file + " " + e);
3059         } catch (IndexOutOfBoundsException e) {
3060             Slog.w(TAG, "failed parsing " + file + " " + e);
3061         }
3062         IoUtils.closeQuietly(stream);
3063 
3064         if (!success) {
3065             wallpaper.cropHint.set(0, 0, 0, 0);
3066             wpdData.mPadding.set(0, 0, 0, 0);
3067             wallpaper.name = "";
3068 
3069             mLockWallpaperMap.remove(userId);
3070         } else {
3071             if (wallpaper.wallpaperId <= 0) {
3072                 wallpaper.wallpaperId = makeWallpaperIdLocked();
3073                 if (DEBUG) {
3074                     Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
3075                             + "); now " + wallpaper.wallpaperId);
3076                 }
3077             }
3078         }
3079 
3080         ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
3081         ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
3082         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
3083         if (lockWallpaper != null) {
3084             ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
3085         }
3086     }
3087 
initializeFallbackWallpaper()3088     private void initializeFallbackWallpaper() {
3089         if (mFallbackWallpaper == null) {
3090             if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
3091             mFallbackWallpaper = new WallpaperData(
3092                     UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
3093             mFallbackWallpaper.allowBackup = false;
3094             mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
3095             bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
3096         }
3097     }
3098 
ensureSaneWallpaperData(WallpaperData wallpaper, int displayId)3099     private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
3100         final DisplayData size = getDisplayDataOrCreate(displayId);
3101 
3102         if (displayId == DEFAULT_DISPLAY) {
3103             // crop, if not previously specified
3104             if (wallpaper.cropHint.width() <= 0
3105                     || wallpaper.cropHint.height() <= 0) {
3106                 wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
3107             }
3108         }
3109     }
3110 
parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)3111     private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
3112             boolean keepDimensionHints) {
3113         final String idString = parser.getAttributeValue(null, "id");
3114         if (idString != null) {
3115             final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
3116             if (id > mWallpaperId) {
3117                 mWallpaperId = id;
3118             }
3119         } else {
3120             wallpaper.wallpaperId = makeWallpaperIdLocked();
3121         }
3122 
3123         final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
3124 
3125         if (!keepDimensionHints) {
3126             wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
3127             wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
3128         }
3129         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
3130         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
3131         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
3132         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
3133         wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
3134         wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
3135         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
3136         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
3137         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
3138         if (colorsCount > 0) {
3139             Color primary = null, secondary = null, tertiary = null;
3140             for (int i = 0; i < colorsCount; i++) {
3141                 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
3142                 if (i == 0) {
3143                     primary = color;
3144                 } else if (i == 1) {
3145                     secondary = color;
3146                 } else if (i == 2) {
3147                     tertiary = color;
3148                 } else {
3149                     break;
3150                 }
3151             }
3152             int colorHints = getAttributeInt(parser, "colorHints", 0);
3153             wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
3154         }
3155         wallpaper.name = parser.getAttributeValue(null, "name");
3156         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
3157     }
3158 
3159     // Called by SystemBackupAgent after files are restored to disk.
settingsRestored()3160     public void settingsRestored() {
3161         // Verify caller is the system
3162         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
3163             throw new RuntimeException("settingsRestored() can only be called from the system process");
3164         }
3165         // TODO: If necessary, make it work for secondary users as well. This currently assumes
3166         // restores only to the primary user
3167         if (DEBUG) Slog.v(TAG, "settingsRestored");
3168         WallpaperData wallpaper = null;
3169         boolean success = false;
3170         synchronized (mLock) {
3171             loadSettingsLocked(UserHandle.USER_SYSTEM, false);
3172             wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
3173             wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
3174             wallpaper.allowBackup = true;   // by definition if it was restored
3175             if (wallpaper.nextWallpaperComponent != null
3176                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
3177                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
3178                         wallpaper, null)) {
3179                     // No such live wallpaper or other failure; fall back to the default
3180                     // live wallpaper (since the profile being restored indicated that the
3181                     // user had selected a live rather than static one).
3182                     bindWallpaperComponentLocked(null, false, false, wallpaper, null);
3183                 }
3184                 success = true;
3185             } else {
3186                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
3187                 // use the default.
3188                 if ("".equals(wallpaper.name)) {
3189                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
3190                     success = true;
3191                 } else {
3192                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
3193                     success = restoreNamedResourceLocked(wallpaper);
3194                 }
3195                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
3196                         + " id=" + wallpaper.wallpaperId);
3197                 if (success) {
3198                     generateCrop(wallpaper); // based on the new image + metadata
3199                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
3200                             wallpaper, null);
3201                 }
3202             }
3203         }
3204 
3205         if (!success) {
3206             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
3207             wallpaper.name = "";
3208             getWallpaperDir(UserHandle.USER_SYSTEM).delete();
3209         }
3210 
3211         synchronized (mLock) {
3212             saveSettingsLocked(UserHandle.USER_SYSTEM);
3213         }
3214     }
3215 
3216     // Restore the named resource bitmap to both source + crop files
restoreNamedResourceLocked(WallpaperData wallpaper)3217     private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
3218         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
3219             String resName = wallpaper.name.substring(4);
3220 
3221             String pkg = null;
3222             int colon = resName.indexOf(':');
3223             if (colon > 0) {
3224                 pkg = resName.substring(0, colon);
3225             }
3226 
3227             String ident = null;
3228             int slash = resName.lastIndexOf('/');
3229             if (slash > 0) {
3230                 ident = resName.substring(slash+1);
3231             }
3232 
3233             String type = null;
3234             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
3235                 type = resName.substring(colon+1, slash);
3236             }
3237 
3238             if (pkg != null && ident != null && type != null) {
3239                 int resId = -1;
3240                 InputStream res = null;
3241                 FileOutputStream fos = null;
3242                 FileOutputStream cos = null;
3243                 try {
3244                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
3245                     Resources r = c.getResources();
3246                     resId = r.getIdentifier(resName, null, null);
3247                     if (resId == 0) {
3248                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
3249                                 + " ident=" + ident);
3250                         return false;
3251                     }
3252 
3253                     res = r.openRawResource(resId);
3254                     if (wallpaper.wallpaperFile.exists()) {
3255                         wallpaper.wallpaperFile.delete();
3256                         wallpaper.cropFile.delete();
3257                     }
3258                     fos = new FileOutputStream(wallpaper.wallpaperFile);
3259                     cos = new FileOutputStream(wallpaper.cropFile);
3260 
3261                     byte[] buffer = new byte[32768];
3262                     int amt;
3263                     while ((amt=res.read(buffer)) > 0) {
3264                         fos.write(buffer, 0, amt);
3265                         cos.write(buffer, 0, amt);
3266                     }
3267                     // mWallpaperObserver will notice the close and send the change broadcast
3268 
3269                     Slog.v(TAG, "Restored wallpaper: " + resName);
3270                     return true;
3271                 } catch (NameNotFoundException e) {
3272                     Slog.e(TAG, "Package name " + pkg + " not found");
3273                 } catch (Resources.NotFoundException e) {
3274                     Slog.e(TAG, "Resource not found: " + resId);
3275                 } catch (IOException e) {
3276                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
3277                 } finally {
3278                     IoUtils.closeQuietly(res);
3279                     if (fos != null) {
3280                         FileUtils.sync(fos);
3281                     }
3282                     if (cos != null) {
3283                         FileUtils.sync(cos);
3284                     }
3285                     IoUtils.closeQuietly(fos);
3286                     IoUtils.closeQuietly(cos);
3287                 }
3288             }
3289         }
3290         return false;
3291     }
3292 
3293     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)3294     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3295         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
3296 
3297         synchronized (mLock) {
3298             pw.println("System wallpaper state:");
3299             for (int i = 0; i < mWallpaperMap.size(); i++) {
3300                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
3301                 pw.print(" User "); pw.print(wallpaper.userId);
3302                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
3303                 pw.println(" Display state:");
3304                 forEachDisplayData(wpSize -> {
3305                     pw.print("  displayId=");
3306                     pw.println(wpSize.mDisplayId);
3307                     pw.print("  mWidth=");
3308                     pw.print(wpSize.mWidth);
3309                     pw.print("  mHeight=");
3310                     pw.println(wpSize.mHeight);
3311                     pw.print("  mPadding="); pw.println(wpSize.mPadding);
3312                 });
3313                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
3314                 pw.print("  mName=");  pw.println(wallpaper.name);
3315                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
3316                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
3317                 if (wallpaper.connection != null) {
3318                     WallpaperConnection conn = wallpaper.connection;
3319                     pw.print("  Wallpaper connection ");
3320                     pw.print(conn);
3321                     pw.println(":");
3322                     if (conn.mInfo != null) {
3323                         pw.print("    mInfo.component=");
3324                         pw.println(conn.mInfo.getComponent());
3325                     }
3326                     conn.forEachDisplayConnector(connector -> {
3327                         pw.print("     mDisplayId=");
3328                         pw.println(connector.mDisplayId);
3329                         pw.print("     mToken=");
3330                         pw.println(connector.mToken);
3331                         pw.print("     mEngine=");
3332                         pw.println(connector.mEngine);
3333                     });
3334                     pw.print("    mService=");
3335                     pw.println(conn.mService);
3336                     pw.print("    mLastDiedTime=");
3337                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
3338                 }
3339             }
3340             pw.println("Lock wallpaper state:");
3341             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
3342                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
3343                 pw.print(" User "); pw.print(wallpaper.userId);
3344                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
3345                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
3346                 pw.print("  mName=");  pw.println(wallpaper.name);
3347                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
3348             }
3349             pw.println("Fallback wallpaper state:");
3350             pw.print(" User "); pw.print(mFallbackWallpaper.userId);
3351             pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
3352             pw.print("  mCropHint="); pw.println(mFallbackWallpaper.cropHint);
3353             pw.print("  mName=");  pw.println(mFallbackWallpaper.name);
3354             pw.print("  mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
3355             if (mFallbackWallpaper.connection != null) {
3356                 WallpaperConnection conn = mFallbackWallpaper.connection;
3357                 pw.print("  Fallback Wallpaper connection ");
3358                 pw.print(conn);
3359                 pw.println(":");
3360                 if (conn.mInfo != null) {
3361                     pw.print("    mInfo.component=");
3362                     pw.println(conn.mInfo.getComponent());
3363                 }
3364                 conn.forEachDisplayConnector(connector -> {
3365                     pw.print("     mDisplayId=");
3366                     pw.println(connector.mDisplayId);
3367                     pw.print("     mToken=");
3368                     pw.println(connector.mToken);
3369                     pw.print("     mEngine=");
3370                     pw.println(connector.mEngine);
3371                 });
3372                 pw.print("    mService=");
3373                 pw.println(conn.mService);
3374                 pw.print("    mLastDiedTime=");
3375                 pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
3376             }
3377         }
3378     }
3379 }
3380