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