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 
1183         private Runnable mTryToRebindRunnable = () -> {
1184             tryToRebind();
1185         };
1186 
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid)1187         WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
1188             mInfo = info;
1189             mWallpaper = wallpaper;
1190             mClientUid = clientUid;
1191             initDisplayState();
1192         }
1193 
initDisplayState()1194         private void initDisplayState() {
1195             // Do not initialize fallback wallpaper
1196             if (!mWallpaper.equals(mFallbackWallpaper)) {
1197                 if (supportsMultiDisplay(this)) {
1198                     // The system wallpaper is image wallpaper or it can supports multiple displays.
1199                     appendConnectorWithCondition(this::isUsableDisplay);
1200                 } else {
1201                     // The system wallpaper does not support multiple displays, so just attach it on
1202                     // default display.
1203                     mDisplayConnector.append(DEFAULT_DISPLAY,
1204                             new DisplayConnector(DEFAULT_DISPLAY));
1205                 }
1206             }
1207         }
1208 
appendConnectorWithCondition(Predicate<Display> tester)1209         private void appendConnectorWithCondition(Predicate<Display> tester) {
1210             final Display[] displays = mDisplayManager.getDisplays();
1211             for (Display display : displays) {
1212                 if (tester.test(display)) {
1213                     final int displayId = display.getDisplayId();
1214                     final DisplayConnector connector = mDisplayConnector.get(displayId);
1215                     if (connector == null) {
1216                         mDisplayConnector.append(displayId,
1217                                 new DisplayConnector(displayId));
1218                     }
1219                 }
1220             }
1221         }
1222 
isUsableDisplay(Display display)1223         private boolean isUsableDisplay(Display display) {
1224             if (display == null || !display.hasAccess(mClientUid)) {
1225                 return false;
1226             }
1227             final int displayId = display.getDisplayId();
1228             if (displayId == DEFAULT_DISPLAY) {
1229                 return true;
1230             }
1231 
1232             final long ident = Binder.clearCallingIdentity();
1233             try {
1234                 return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
1235             } finally {
1236                 Binder.restoreCallingIdentity(ident);
1237             }
1238         }
1239 
forEachDisplayConnector(Consumer<DisplayConnector> action)1240         void forEachDisplayConnector(Consumer<DisplayConnector> action) {
1241             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
1242                 final DisplayConnector connector = mDisplayConnector.valueAt(i);
1243                 action.accept(connector);
1244             }
1245         }
1246 
getConnectedEngineSize()1247         int getConnectedEngineSize() {
1248             int engineSize = 0;
1249             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
1250                 final DisplayConnector connector = mDisplayConnector.valueAt(i);
1251                 if (connector.mEngine != null) engineSize++;
1252             }
1253             return engineSize;
1254         }
1255 
getDisplayConnectorOrCreate(int displayId)1256         DisplayConnector getDisplayConnectorOrCreate(int displayId) {
1257             DisplayConnector connector = mDisplayConnector.get(displayId);
1258             if (connector == null) {
1259                 final Display display = mDisplayManager.getDisplay(displayId);
1260                 if (isUsableDisplay(display)) {
1261                     connector = new DisplayConnector(displayId);
1262                     mDisplayConnector.append(displayId, connector);
1263                 }
1264             }
1265             return connector;
1266         }
1267 
containsDisplay(int displayId)1268         boolean containsDisplay(int displayId) {
1269             return mDisplayConnector.get(displayId) != null;
1270         }
1271 
removeDisplayConnector(int displayId)1272         void removeDisplayConnector(int displayId) {
1273             final DisplayConnector connector = mDisplayConnector.get(displayId);
1274             if (connector != null) {
1275                 mDisplayConnector.remove(displayId);
1276             }
1277         }
1278 
1279         @Override
onServiceConnected(ComponentName name, IBinder service)1280         public void onServiceConnected(ComponentName name, IBinder service) {
1281             synchronized (mLock) {
1282                 if (mWallpaper.connection == this) {
1283                     mService = IWallpaperService.Stub.asInterface(service);
1284                     attachServiceLocked(this, mWallpaper);
1285                     // XXX should probably do saveSettingsLocked() later
1286                     // when we have an engine, but I'm not sure about
1287                     // locking there and anyway we always need to be able to
1288                     // recover if there is something wrong.
1289                     if (!mWallpaper.equals(mFallbackWallpaper)) {
1290                         saveSettingsLocked(mWallpaper.userId);
1291                     }
1292                     FgThread.getHandler().removeCallbacks(mResetRunnable);
1293                     mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable);
1294                 }
1295             }
1296         }
1297 
1298         @Override
onServiceDisconnected(ComponentName name)1299         public void onServiceDisconnected(ComponentName name) {
1300             synchronized (mLock) {
1301                 Slog.w(TAG, "Wallpaper service gone: " + name);
1302                 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
1303                     Slog.e(TAG, "Does not match expected wallpaper component "
1304                             + mWallpaper.wallpaperComponent);
1305                 }
1306                 mService = null;
1307                 forEachDisplayConnector(connector -> connector.mEngine = null);
1308                 if (mWallpaper.connection == this) {
1309                     // There is an inherent ordering race between this callback and the
1310                     // package monitor that receives notice that a package is being updated,
1311                     // so we cannot quite trust at this moment that we know for sure that
1312                     // this is not an update.  If we think this is a genuine non-update
1313                     // wallpaper outage, we do our "wait for reset" work as a continuation,
1314                     // a short time in the future, specifically to allow any pending package
1315                     // update message on this same looper thread to be processed.
1316                     if (!mWallpaper.wallpaperUpdating) {
1317                         mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
1318                                 1000);
1319                     }
1320                 }
1321             }
1322         }
1323 
scheduleTimeoutLocked()1324         public void scheduleTimeoutLocked() {
1325             // If we didn't reset it right away, do so after we couldn't connect to
1326             // it for an extended amount of time to avoid having a black wallpaper.
1327             final Handler fgHandler = FgThread.getHandler();
1328             fgHandler.removeCallbacks(mResetRunnable);
1329             fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
1330             if (DEBUG_LIVE) {
1331                 Slog.i(TAG,
1332                         "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
1333             }
1334         }
1335 
tryToRebind()1336         private void tryToRebind() {
1337             synchronized (mLock) {
1338                 if (mWallpaper.wallpaperUpdating) {
1339                     return;
1340                 }
1341                 final ComponentName wpService = mWallpaper.wallpaperComponent;
1342                 // The broadcast of package update could be delayed after service disconnected. Try
1343                 // to re-bind the service for 10 seconds.
1344                 if (bindWallpaperComponentLocked(
1345                         wpService, true, false, mWallpaper, null)) {
1346                     mWallpaper.connection.scheduleTimeoutLocked();
1347                 } else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime
1348                         < WALLPAPER_RECONNECT_TIMEOUT_MS) {
1349                     // Bind fail without timeout, schedule rebind
1350                     Slog.w(TAG, "Rebind fail! Try again later");
1351                     mContext.getMainThreadHandler().postDelayed(mTryToRebindRunnable, 1000);
1352                 } else {
1353                     // Timeout
1354                     Slog.w(TAG, "Reverting to built-in wallpaper!");
1355                     clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
1356                     final String flattened = wpService.flattenToString();
1357                     EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
1358                             flattened.substring(0, Math.min(flattened.length(),
1359                                     MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
1360                 }
1361             }
1362         }
1363 
processDisconnect(final ServiceConnection connection)1364         private void processDisconnect(final ServiceConnection connection) {
1365             synchronized (mLock) {
1366                 // The wallpaper disappeared.  If this isn't a system-default one, track
1367                 // crashes and fall back to default if it continues to misbehave.
1368                 if (connection == mWallpaper.connection) {
1369                     final ComponentName wpService = mWallpaper.wallpaperComponent;
1370                     if (!mWallpaper.wallpaperUpdating
1371                             && mWallpaper.userId == mCurrentUserId
1372                             && !Objects.equals(mDefaultWallpaperComponent, wpService)
1373                             && !Objects.equals(mImageWallpaper, wpService)) {
1374                         // There is a race condition which causes
1375                         // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
1376                         // currently updating since the broadcast notifying us is async.
1377                         // This race is overcome by the general rule that we only reset the
1378                         // wallpaper if its service was shut down twice
1379                         // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
1380                         if (mWallpaper.lastDiedTime != 0
1381                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
1382                                 > SystemClock.uptimeMillis()) {
1383                             Slog.w(TAG, "Reverting to built-in wallpaper!");
1384                             clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
1385                         } else {
1386                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
1387                             tryToRebind();
1388                         }
1389                     }
1390                 } else {
1391                     if (DEBUG_LIVE) {
1392                         Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
1393                     }
1394                 }
1395             }
1396         }
1397 
1398         /**
1399          * Called by a live wallpaper if its colors have changed.
1400          * @param primaryColors representation of wallpaper primary colors
1401          * @param displayId for which display
1402          */
1403         @Override
onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId)1404         public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
1405             int which;
1406             synchronized (mLock) {
1407                 // Do not broadcast changes on ImageWallpaper since it's handled
1408                 // internally by this class.
1409                 if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
1410                     return;
1411                 }
1412 
1413                 mWallpaper.primaryColors = primaryColors;
1414 
1415                 // Live wallpapers always are system wallpapers.
1416                 which = FLAG_SYSTEM;
1417                 // It's also the lock screen wallpaper when we don't have a bitmap in there.
1418                 if (displayId == DEFAULT_DISPLAY) {
1419                     final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
1420                     if (lockedWallpaper == null) {
1421                         which |= FLAG_LOCK;
1422                     }
1423                 }
1424             }
1425             if (which != 0) {
1426                 notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
1427             }
1428         }
1429 
1430         @Override
attachEngine(IWallpaperEngine engine, int displayId)1431         public void attachEngine(IWallpaperEngine engine, int displayId) {
1432             synchronized (mLock) {
1433                 final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
1434                 if (connector == null) {
1435                     try {
1436                         engine.destroy();
1437                     } catch (RemoteException e) {
1438                         Slog.w(TAG, "Failed to destroy engine", e);
1439                     }
1440                     return;
1441                 }
1442                 connector.mEngine = engine;
1443                 connector.ensureStatusHandled();
1444 
1445                 // TODO(multi-display) TBD.
1446                 if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
1447                     try {
1448                         connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
1449                     } catch (RemoteException e) {
1450                         Slog.w(TAG, "Failed to set ambient mode state", e);
1451                     }
1452                 }
1453                 try {
1454                     // This will trigger onComputeColors in the wallpaper engine.
1455                     // It's fine to be locked in here since the binder is oneway.
1456                     connector.mEngine.requestWallpaperColors();
1457                 } catch (RemoteException e) {
1458                     Slog.w(TAG, "Failed to request wallpaper colors", e);
1459                 }
1460             }
1461         }
1462 
1463         @Override
engineShown(IWallpaperEngine engine)1464         public void engineShown(IWallpaperEngine engine) {
1465             synchronized (mLock) {
1466                 if (mReply != null) {
1467                     long ident = Binder.clearCallingIdentity();
1468                     try {
1469                         mReply.sendResult(null);
1470                     } catch (RemoteException e) {
1471                         Binder.restoreCallingIdentity(ident);
1472                     }
1473                     mReply = null;
1474                 }
1475             }
1476         }
1477 
1478         @Override
setWallpaper(String name)1479         public ParcelFileDescriptor setWallpaper(String name) {
1480             synchronized (mLock) {
1481                 if (mWallpaper.connection == this) {
1482                     return updateWallpaperBitmapLocked(name, mWallpaper, null);
1483                 }
1484                 return null;
1485             }
1486         }
1487     }
1488 
1489     class MyPackageMonitor extends PackageMonitor {
1490         @Override
onPackageUpdateFinished(String packageName, int uid)1491         public void onPackageUpdateFinished(String packageName, int uid) {
1492             synchronized (mLock) {
1493                 if (mCurrentUserId != getChangingUserId()) {
1494                     return;
1495                 }
1496                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1497                 if (wallpaper != null) {
1498                     final ComponentName wpService = wallpaper.wallpaperComponent;
1499                     if (wpService != null && wpService.getPackageName().equals(packageName)) {
1500                         if (DEBUG_LIVE) {
1501                             Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
1502                         }
1503                         wallpaper.wallpaperUpdating = false;
1504                         clearWallpaperComponentLocked(wallpaper);
1505                         if (!bindWallpaperComponentLocked(wpService, false, false,
1506                                 wallpaper, null)) {
1507                             Slog.w(TAG, "Wallpaper " + wpService
1508                                     + " no longer available; reverting to default");
1509                             clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1510                         }
1511                     }
1512                 }
1513             }
1514         }
1515 
1516         @Override
onPackageModified(String packageName)1517         public void onPackageModified(String packageName) {
1518             synchronized (mLock) {
1519                 if (mCurrentUserId != getChangingUserId()) {
1520                     return;
1521                 }
1522                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1523                 if (wallpaper != null) {
1524                     if (wallpaper.wallpaperComponent == null
1525                             || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1526                         return;
1527                     }
1528                     doPackagesChangedLocked(true, wallpaper);
1529                 }
1530             }
1531         }
1532 
1533         @Override
onPackageUpdateStarted(String packageName, int uid)1534         public void onPackageUpdateStarted(String packageName, int uid) {
1535             synchronized (mLock) {
1536                 if (mCurrentUserId != getChangingUserId()) {
1537                     return;
1538                 }
1539                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1540                 if (wallpaper != null) {
1541                     if (wallpaper.wallpaperComponent != null
1542                             && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1543                         if (DEBUG_LIVE) {
1544                             Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent
1545                                     + " is updating");
1546                         }
1547                         wallpaper.wallpaperUpdating = true;
1548                         if (wallpaper.connection != null) {
1549                             FgThread.getHandler().removeCallbacks(
1550                                     wallpaper.connection.mResetRunnable);
1551                         }
1552                     }
1553                 }
1554             }
1555         }
1556 
1557         @Override
onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)1558         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1559             synchronized (mLock) {
1560                 boolean changed = false;
1561                 if (mCurrentUserId != getChangingUserId()) {
1562                     return false;
1563                 }
1564                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1565                 if (wallpaper != null) {
1566                     boolean res = doPackagesChangedLocked(doit, wallpaper);
1567                     changed |= res;
1568                 }
1569                 return changed;
1570             }
1571         }
1572 
1573         @Override
onSomePackagesChanged()1574         public void onSomePackagesChanged() {
1575             synchronized (mLock) {
1576                 if (mCurrentUserId != getChangingUserId()) {
1577                     return;
1578                 }
1579                 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1580                 if (wallpaper != null) {
1581                     doPackagesChangedLocked(true, wallpaper);
1582                 }
1583             }
1584         }
1585 
doPackagesChangedLocked(boolean doit, WallpaperData wallpaper)1586         boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
1587             boolean changed = false;
1588             if (wallpaper.wallpaperComponent != null) {
1589                 int change = isPackageDisappearing(wallpaper.wallpaperComponent
1590                         .getPackageName());
1591                 if (change == PACKAGE_PERMANENT_CHANGE
1592                         || change == PACKAGE_TEMPORARY_CHANGE) {
1593                     changed = true;
1594                     if (doit) {
1595                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
1596                                 + wallpaper.wallpaperComponent);
1597                         clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1598                     }
1599                 }
1600             }
1601             if (wallpaper.nextWallpaperComponent != null) {
1602                 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
1603                         .getPackageName());
1604                 if (change == PACKAGE_PERMANENT_CHANGE
1605                         || change == PACKAGE_TEMPORARY_CHANGE) {
1606                     wallpaper.nextWallpaperComponent = null;
1607                 }
1608             }
1609             if (wallpaper.wallpaperComponent != null
1610                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
1611                 try {
1612                     mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
1613                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1614                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1615                 } catch (NameNotFoundException e) {
1616                     Slog.w(TAG, "Wallpaper component gone, removing: "
1617                             + wallpaper.wallpaperComponent);
1618                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1619                 }
1620             }
1621             if (wallpaper.nextWallpaperComponent != null
1622                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
1623                 try {
1624                     mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
1625                             PackageManager.MATCH_DIRECT_BOOT_AWARE
1626                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1627                 } catch (NameNotFoundException e) {
1628                     wallpaper.nextWallpaperComponent = null;
1629                 }
1630             }
1631             return changed;
1632         }
1633     }
1634 
WallpaperManagerService(Context context)1635     public WallpaperManagerService(Context context) {
1636         if (DEBUG) Slog.v(TAG, "WallpaperService startup");
1637         mContext = context;
1638         mShuttingDown = false;
1639         mImageWallpaper = ComponentName.unflattenFromString(
1640                 context.getResources().getString(R.string.image_wallpaper_component));
1641         mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
1642         mIWindowManager = IWindowManager.Stub.asInterface(
1643                 ServiceManager.getService(Context.WINDOW_SERVICE));
1644         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1645         mIPackageManager = AppGlobals.getPackageManager();
1646         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1647         mDisplayManager = mContext.getSystemService(DisplayManager.class);
1648         mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
1649         mMonitor = new MyPackageMonitor();
1650         mColorsChangedListeners = new SparseArray<>();
1651 
1652         LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
1653     }
1654 
1655     private final class LocalService extends WallpaperManagerInternal {
1656         @Override
onDisplayReady(int displayId)1657         public void onDisplayReady(int displayId) {
1658             onDisplayReadyInternal(displayId);
1659         }
1660     }
1661 
initialize()1662     void initialize() {
1663         mMonitor.register(mContext, null, UserHandle.ALL, true);
1664         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
1665 
1666         // Initialize state from the persistent store, then guarantee that the
1667         // WallpaperData for the system imagery is instantiated & active, creating
1668         // it from defaults if necessary.
1669         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
1670         getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
1671     }
1672 
getWallpaperDir(int userId)1673     private static File getWallpaperDir(int userId) {
1674         return Environment.getUserSystemDirectory(userId);
1675     }
1676 
1677     @Override
finalize()1678     protected void finalize() throws Throwable {
1679         super.finalize();
1680         for (int i = 0; i < mWallpaperMap.size(); i++) {
1681             WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1682             wallpaper.wallpaperObserver.stopWatching();
1683         }
1684     }
1685 
systemReady()1686     void systemReady() {
1687         if (DEBUG) Slog.v(TAG, "systemReady");
1688         initialize();
1689 
1690         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1691         // If we think we're going to be using the system image wallpaper imagery, make
1692         // sure we have something to render
1693         if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
1694             // No crop file? Make sure we've finished the processing sequence if necessary
1695             if (!wallpaper.cropExists()) {
1696                 if (DEBUG) {
1697                     Slog.i(TAG, "No crop; regenerating from source");
1698                 }
1699                 generateCrop(wallpaper);
1700             }
1701             // Still nothing?  Fall back to default.
1702             if (!wallpaper.cropExists()) {
1703                 if (DEBUG) {
1704                     Slog.i(TAG, "Unable to regenerate crop; resetting");
1705                 }
1706                 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
1707             }
1708         } else {
1709             if (DEBUG) {
1710                 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
1711             }
1712         }
1713 
1714         IntentFilter userFilter = new IntentFilter();
1715         userFilter.addAction(Intent.ACTION_USER_REMOVED);
1716         mContext.registerReceiver(new BroadcastReceiver() {
1717             @Override
1718             public void onReceive(Context context, Intent intent) {
1719                 final String action = intent.getAction();
1720                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
1721                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
1722                             UserHandle.USER_NULL));
1723                 }
1724             }
1725         }, userFilter);
1726 
1727         final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
1728         mContext.registerReceiver(new BroadcastReceiver() {
1729             @Override
1730             public void onReceive(Context context, Intent intent) {
1731                 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
1732                     if (DEBUG) {
1733                         Slog.i(TAG, "Shutting down");
1734                     }
1735                     synchronized (mLock) {
1736                         mShuttingDown = true;
1737                     }
1738                 }
1739             }
1740         }, shutdownFilter);
1741 
1742         try {
1743             ActivityManager.getService().registerUserSwitchObserver(
1744                     new UserSwitchObserver() {
1745                         @Override
1746                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
1747                             switchUser(newUserId, reply);
1748                         }
1749                     }, TAG);
1750         } catch (RemoteException e) {
1751             e.rethrowAsRuntimeException();
1752         }
1753     }
1754 
1755     /** Called by SystemBackupAgent */
getName()1756     public String getName() {
1757         // Verify caller is the system
1758         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1759             throw new RuntimeException("getName() can only be called from the system process");
1760         }
1761         synchronized (mLock) {
1762             return mWallpaperMap.get(0).name;
1763         }
1764     }
1765 
stopObserver(WallpaperData wallpaper)1766     void stopObserver(WallpaperData wallpaper) {
1767         if (wallpaper != null) {
1768             if (wallpaper.wallpaperObserver != null) {
1769                 wallpaper.wallpaperObserver.stopWatching();
1770                 wallpaper.wallpaperObserver = null;
1771             }
1772         }
1773     }
1774 
stopObserversLocked(int userId)1775     void stopObserversLocked(int userId) {
1776         stopObserver(mWallpaperMap.get(userId));
1777         stopObserver(mLockWallpaperMap.get(userId));
1778         mWallpaperMap.remove(userId);
1779         mLockWallpaperMap.remove(userId);
1780     }
1781 
1782     @Override
onBootPhase(int phase)1783     public void onBootPhase(int phase) {
1784         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
1785             systemReady();
1786         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1787             switchUser(UserHandle.USER_SYSTEM, null);
1788         }
1789     }
1790 
1791     @Override
onUnlockUser(final int userId)1792     public void onUnlockUser(final int userId) {
1793         synchronized (mLock) {
1794             if (mCurrentUserId == userId) {
1795                 if (mWaitingForUnlock) {
1796                     // the desired wallpaper is not direct-boot aware, load it now
1797                     final WallpaperData systemWallpaper =
1798                             getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1799                     switchWallpaper(systemWallpaper, null);
1800                     notifyCallbacksLocked(systemWallpaper);
1801                 }
1802 
1803                 // Make sure that the SELinux labeling of all the relevant files is correct.
1804                 // This corrects for mislabeling bugs that might have arisen from move-to
1805                 // operations involving the wallpaper files.  This isn't timing-critical,
1806                 // so we do it in the background to avoid holding up the user unlock operation.
1807                 if (!mUserRestorecon.get(userId)) {
1808                     mUserRestorecon.put(userId, true);
1809                     Runnable relabeler = new Runnable() {
1810                         @Override
1811                         public void run() {
1812                             final File wallpaperDir = getWallpaperDir(userId);
1813                             for (String filename : sPerUserFiles) {
1814                                 File f = new File(wallpaperDir, filename);
1815                                 if (f.exists()) {
1816                                     SELinux.restorecon(f);
1817                                 }
1818                             }
1819                         }
1820                     };
1821                     BackgroundThread.getHandler().post(relabeler);
1822                 }
1823             }
1824         }
1825     }
1826 
onRemoveUser(int userId)1827     void onRemoveUser(int userId) {
1828         if (userId < 1) return;
1829 
1830         final File wallpaperDir = getWallpaperDir(userId);
1831         synchronized (mLock) {
1832             stopObserversLocked(userId);
1833             for (String filename : sPerUserFiles) {
1834                 new File(wallpaperDir, filename).delete();
1835             }
1836             mUserRestorecon.delete(userId);
1837         }
1838     }
1839 
switchUser(int userId, IRemoteCallback reply)1840     void switchUser(int userId, IRemoteCallback reply) {
1841         final WallpaperData systemWallpaper;
1842         final WallpaperData lockWallpaper;
1843         synchronized (mLock) {
1844             if (mCurrentUserId == userId) {
1845                 return;
1846             }
1847             mCurrentUserId = userId;
1848             systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1849             final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
1850             lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
1851             // Not started watching yet, in case wallpaper data was loaded for other reasons.
1852             if (systemWallpaper.wallpaperObserver == null) {
1853                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
1854                 systemWallpaper.wallpaperObserver.startWatching();
1855             }
1856             switchWallpaper(systemWallpaper, reply);
1857         }
1858 
1859         // Offload color extraction to another thread since switchUser will be called
1860         // from the main thread.
1861         FgThread.getHandler().post(() -> {
1862             notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
1863             notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
1864             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
1865         });
1866     }
1867 
switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply)1868     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
1869         synchronized (mLock) {
1870             mWaitingForUnlock = false;
1871             final ComponentName cname = wallpaper.wallpaperComponent != null ?
1872                     wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
1873             if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
1874                 // We failed to bind the desired wallpaper, but that might
1875                 // happen if the wallpaper isn't direct-boot aware
1876                 ServiceInfo si = null;
1877                 try {
1878                     si = mIPackageManager.getServiceInfo(cname,
1879                             PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
1880                 } catch (RemoteException ignored) {
1881                 }
1882 
1883                 if (si == null) {
1884                     Slog.w(TAG, "Failure starting previous wallpaper; clearing");
1885                     clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
1886                 } else {
1887                     Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
1888                     // We might end up persisting the current wallpaper data
1889                     // while locked, so pretend like the component was actually
1890                     // bound into place
1891                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
1892                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
1893                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1894                     ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
1895                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
1896                     mWaitingForUnlock = true;
1897                 }
1898             }
1899         }
1900     }
1901 
1902     @Override
clearWallpaper(String callingPackage, int which, int userId)1903     public void clearWallpaper(String callingPackage, int which, int userId) {
1904         if (DEBUG) Slog.v(TAG, "clearWallpaper");
1905         checkPermission(android.Manifest.permission.SET_WALLPAPER);
1906         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1907             return;
1908         }
1909         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1910                 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
1911 
1912         WallpaperData data = null;
1913         synchronized (mLock) {
1914             clearWallpaperLocked(false, which, userId, null);
1915 
1916             if (which == FLAG_LOCK) {
1917                 data = mLockWallpaperMap.get(userId);
1918             }
1919             if (which == FLAG_SYSTEM || data == null) {
1920                 data = mWallpaperMap.get(userId);
1921             }
1922         }
1923 
1924         // When clearing a wallpaper, broadcast new valid colors
1925         if (data != null) {
1926             notifyWallpaperColorsChanged(data, which);
1927             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
1928         }
1929     }
1930 
clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply)1931     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
1932         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1933             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
1934         }
1935 
1936         WallpaperData wallpaper = null;
1937         if (which == FLAG_LOCK) {
1938             wallpaper = mLockWallpaperMap.get(userId);
1939             if (wallpaper == null) {
1940                 // It's already gone; we're done.
1941                 if (DEBUG) {
1942                     Slog.i(TAG, "Lock wallpaper already cleared");
1943                 }
1944                 return;
1945             }
1946         } else {
1947             wallpaper = mWallpaperMap.get(userId);
1948             if (wallpaper == null) {
1949                 // Might need to bring it in the first time to establish our rewrite
1950                 loadSettingsLocked(userId, false);
1951                 wallpaper = mWallpaperMap.get(userId);
1952             }
1953         }
1954         if (wallpaper == null) {
1955             return;
1956         }
1957 
1958         final long ident = Binder.clearCallingIdentity();
1959         try {
1960             if (wallpaper.wallpaperFile.exists()) {
1961                 wallpaper.wallpaperFile.delete();
1962                 wallpaper.cropFile.delete();
1963                 if (which == FLAG_LOCK) {
1964                     mLockWallpaperMap.remove(userId);
1965                     final IWallpaperManagerCallback cb = mKeyguardListener;
1966                     if (cb != null) {
1967                         if (DEBUG) {
1968                             Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
1969                         }
1970                         try {
1971                             cb.onWallpaperChanged();
1972                         } catch (RemoteException e) {
1973                             // Oh well it went away; no big deal
1974                         }
1975                     }
1976                     saveSettingsLocked(userId);
1977                     return;
1978                 }
1979             }
1980 
1981             RuntimeException e = null;
1982             try {
1983                 wallpaper.primaryColors = null;
1984                 wallpaper.imageWallpaperPending = false;
1985                 if (userId != mCurrentUserId) return;
1986                 if (bindWallpaperComponentLocked(defaultFailed
1987                         ? mImageWallpaper
1988                                 : null, true, false, wallpaper, reply)) {
1989                     return;
1990                 }
1991             } catch (IllegalArgumentException e1) {
1992                 e = e1;
1993             }
1994 
1995             // This can happen if the default wallpaper component doesn't
1996             // exist.  This should be a system configuration problem, but
1997             // let's not let it crash the system and just live with no
1998             // wallpaper.
1999             Slog.e(TAG, "Default wallpaper component not found!", e);
2000             clearWallpaperComponentLocked(wallpaper);
2001             if (reply != null) {
2002                 try {
2003                     reply.sendResult(null);
2004                 } catch (RemoteException e1) {
2005                 }
2006             }
2007         } finally {
2008             Binder.restoreCallingIdentity(ident);
2009         }
2010     }
2011 
hasNamedWallpaper(String name)2012     public boolean hasNamedWallpaper(String name) {
2013         synchronized (mLock) {
2014             List<UserInfo> users;
2015             long ident = Binder.clearCallingIdentity();
2016             try {
2017                 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
2018             } finally {
2019                 Binder.restoreCallingIdentity(ident);
2020             }
2021             for (UserInfo user: users) {
2022                 // ignore managed profiles
2023                 if (user.isManagedProfile()) {
2024                     continue;
2025                 }
2026                 WallpaperData wd = mWallpaperMap.get(user.id);
2027                 if (wd == null) {
2028                     // User hasn't started yet, so load her settings to peek at the wallpaper
2029                     loadSettingsLocked(user.id, false);
2030                     wd = mWallpaperMap.get(user.id);
2031                 }
2032                 if (wd != null && name.equals(wd.name)) {
2033                     return true;
2034                 }
2035             }
2036         }
2037         return false;
2038     }
2039 
isValidDisplay(int displayId)2040     private boolean isValidDisplay(int displayId) {
2041         return mDisplayManager.getDisplay(displayId) != null;
2042     }
2043 
2044     /**
2045      * Sets the dimension hint for the wallpaper. These hints indicate the desired
2046      * minimum width and height for the wallpaper in a particular display.
2047      */
setDimensionHints(int width, int height, String callingPackage, int displayId)2048     public void setDimensionHints(int width, int height, String callingPackage, int displayId)
2049             throws RemoteException {
2050         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
2051         if (!isWallpaperSupported(callingPackage)) {
2052             return;
2053         }
2054 
2055         // Make sure both width and height are not larger than max texture size.
2056         width = Math.min(width, GLHelper.getMaxTextureSize());
2057         height = Math.min(height, GLHelper.getMaxTextureSize());
2058 
2059         synchronized (mLock) {
2060             int userId = UserHandle.getCallingUserId();
2061             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
2062             if (width <= 0 || height <= 0) {
2063                 throw new IllegalArgumentException("width and height must be > 0");
2064             }
2065 
2066             if (!isValidDisplay(displayId)) {
2067                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2068             }
2069 
2070             final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2071             if (width != wpdData.mWidth || height != wpdData.mHeight) {
2072                 wpdData.mWidth = width;
2073                 wpdData.mHeight = height;
2074                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
2075                 if (mCurrentUserId != userId) return; // Don't change the properties now
2076                 if (wallpaper.connection != null) {
2077                     final WallpaperConnection.DisplayConnector connector = wallpaper.connection
2078                             .getDisplayConnectorOrCreate(displayId);
2079                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
2080                     if (engine != null) {
2081                         try {
2082                             engine.setDesiredSize(width, height);
2083                         } catch (RemoteException e) {
2084                         }
2085                         notifyCallbacksLocked(wallpaper);
2086                     } else if (wallpaper.connection.mService != null && connector != null) {
2087                         // We've attached to the service but the engine hasn't attached back to us
2088                         // yet. This means it will be created with the previous dimensions, so we
2089                         // need to update it to the new dimensions once it attaches.
2090                         connector.mDimensionsChanged = true;
2091                     }
2092                 }
2093             }
2094         }
2095     }
2096 
2097     /**
2098      * Returns the desired minimum width for the wallpaper in a particular display.
2099      */
getWidthHint(int displayId)2100     public int getWidthHint(int displayId) throws RemoteException {
2101         synchronized (mLock) {
2102             if (!isValidDisplay(displayId)) {
2103                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2104             }
2105             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
2106             if (wallpaper != null) {
2107                 final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2108                 return wpdData.mWidth;
2109             } else {
2110                 return 0;
2111             }
2112         }
2113     }
2114 
2115     /**
2116      * Returns the desired minimum height for the wallpaper in a particular display.
2117      */
getHeightHint(int displayId)2118     public int getHeightHint(int displayId) throws RemoteException {
2119         synchronized (mLock) {
2120             if (!isValidDisplay(displayId)) {
2121                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2122             }
2123             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
2124             if (wallpaper != null) {
2125                 final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2126                 return wpdData.mHeight;
2127             } else {
2128                 return 0;
2129             }
2130         }
2131     }
2132 
2133     /**
2134      * Sets extra padding that we would like the wallpaper to have outside of the display.
2135      */
setDisplayPadding(Rect padding, String callingPackage, int displayId)2136     public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
2137         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
2138         if (!isWallpaperSupported(callingPackage)) {
2139             return;
2140         }
2141         synchronized (mLock) {
2142             if (!isValidDisplay(displayId)) {
2143                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
2144             }
2145             int userId = UserHandle.getCallingUserId();
2146             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
2147             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
2148                 throw new IllegalArgumentException("padding must be positive: " + padding);
2149             }
2150 
2151             final DisplayData wpdData = getDisplayDataOrCreate(displayId);
2152             if (!padding.equals(wpdData.mPadding)) {
2153                 wpdData.mPadding.set(padding);
2154                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
2155                 if (mCurrentUserId != userId) return; // Don't change the properties now
2156                 if (wallpaper.connection != null) {
2157                     final WallpaperConnection.DisplayConnector connector = wallpaper.connection
2158                             .getDisplayConnectorOrCreate(displayId);
2159                     final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
2160                     if (engine != null) {
2161                         try {
2162                             engine.setDisplayPadding(padding);
2163                         } catch (RemoteException e) {
2164                         }
2165                         notifyCallbacksLocked(wallpaper);
2166                     } else if (wallpaper.connection.mService != null && connector != null) {
2167                         // We've attached to the service but the engine hasn't attached back to us
2168                         // yet. This means it will be created with the previous dimensions, so we
2169                         // need to update it to the new dimensions once it attaches.
2170                         connector.mPaddingChanged = true;
2171                     }
2172                 }
2173             }
2174         }
2175     }
2176 
2177     @Override
getWallpaper(String callingPkg, IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId)2178     public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
2179             final int which, Bundle outParams, int wallpaperUserId) {
2180         final int hasPrivilege = mContext.checkCallingOrSelfPermission(
2181                 android.Manifest.permission.READ_WALLPAPER_INTERNAL);
2182         if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
2183             mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
2184                     Binder.getCallingPid(), Binder.getCallingUid(), callingPkg);
2185         }
2186 
2187         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2188                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
2189 
2190         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
2191             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
2192         }
2193 
2194         synchronized (mLock) {
2195             final SparseArray<WallpaperData> whichSet =
2196                     (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2197             WallpaperData wallpaper = whichSet.get(wallpaperUserId);
2198             if (wallpaper == null) {
2199                 // There is no established wallpaper imagery of this type (expected
2200                 // only for lock wallpapers; a system WallpaperData is established at
2201                 // user switch)
2202                 return null;
2203             }
2204             // Only for default display.
2205             final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
2206             try {
2207                 if (outParams != null) {
2208                     outParams.putInt("width", wpdData.mWidth);
2209                     outParams.putInt("height", wpdData.mHeight);
2210                 }
2211                 if (cb != null) {
2212                     wallpaper.callbacks.register(cb);
2213                 }
2214                 if (!wallpaper.cropFile.exists()) {
2215                     return null;
2216                 }
2217                 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
2218             } catch (FileNotFoundException e) {
2219                 /* Shouldn't happen as we check to see if the file exists */
2220                 Slog.w(TAG, "Error getting wallpaper", e);
2221             }
2222             return null;
2223         }
2224     }
2225 
2226     @Override
getWallpaperInfo(int userId)2227     public WallpaperInfo getWallpaperInfo(int userId) {
2228         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2229                 Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
2230         synchronized (mLock) {
2231             WallpaperData wallpaper = mWallpaperMap.get(userId);
2232             if (wallpaper != null && wallpaper.connection != null) {
2233                 return wallpaper.connection.mInfo;
2234             }
2235             return null;
2236         }
2237     }
2238 
2239     @Override
getWallpaperIdForUser(int which, int userId)2240     public int getWallpaperIdForUser(int which, int userId) {
2241         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2242                 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
2243 
2244         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
2245             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
2246         }
2247 
2248         final SparseArray<WallpaperData> map =
2249                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2250         synchronized (mLock) {
2251             WallpaperData wallpaper = map.get(userId);
2252             if (wallpaper != null) {
2253                 return wallpaper.wallpaperId;
2254             }
2255         }
2256         return -1;
2257     }
2258 
2259     @Override
registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2260     public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
2261             int displayId) {
2262         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2263                 userId, true, true, "registerWallpaperColorsCallback", null);
2264         synchronized (mLock) {
2265             SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
2266                     userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
2267             if (userDisplayColorsChangedListeners == null) {
2268                 userDisplayColorsChangedListeners = new SparseArray<>();
2269                 mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
2270             }
2271             RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
2272                     userDisplayColorsChangedListeners.get(displayId);
2273             if (displayChangedListeners == null) {
2274                 displayChangedListeners = new RemoteCallbackList<>();
2275                 userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
2276             }
2277             displayChangedListeners.register(cb);
2278         }
2279     }
2280 
2281     @Override
unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2282     public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
2283             int displayId) {
2284         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2285                 userId, true, true, "unregisterWallpaperColorsCallback", null);
2286         synchronized (mLock) {
2287             SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
2288                     userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
2289             if (userDisplayColorsChangedListeners != null) {
2290                 RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
2291                         userDisplayColorsChangedListeners.get(displayId);
2292                 if (displayChangedListeners != null) {
2293                     displayChangedListeners.unregister(cb);
2294                 }
2295             }
2296         }
2297     }
2298 
2299     /**
2300      * TODO(multi-display) Extends this method with specific display.
2301      * Propagate ambient state to wallpaper engine.
2302      *
2303      * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
2304      * @param animationDuration Duration of the animation, or 0 when immediate.
2305      */
setInAmbientMode(boolean inAmbientMode, long animationDuration)2306     public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
2307         final IWallpaperEngine engine;
2308         synchronized (mLock) {
2309             mInAmbientMode = inAmbientMode;
2310             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
2311             // The wallpaper info is null for image wallpaper, also use the engine in this case.
2312             if (data != null && data.connection != null && (data.connection.mInfo == null
2313                     || data.connection.mInfo.supportsAmbientMode())) {
2314                 // TODO(multi-display) Extends this method with specific display.
2315                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
2316             } else {
2317                 engine = null;
2318             }
2319         }
2320 
2321         if (engine != null) {
2322             try {
2323                 engine.setInAmbientMode(inAmbientMode, animationDuration);
2324             } catch (RemoteException e) {
2325                 // Cannot talk to wallpaper engine.
2326             }
2327         }
2328     }
2329 
2330     @Override
setLockWallpaperCallback(IWallpaperManagerCallback cb)2331     public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
2332         checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
2333         synchronized (mLock) {
2334             mKeyguardListener = cb;
2335         }
2336         return true;
2337     }
2338 
2339     @Override
getWallpaperColors(int which, int userId, int displayId)2340     public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
2341             throws RemoteException {
2342         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
2343             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
2344         }
2345         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
2346                 userId, false, true, "getWallpaperColors", null);
2347 
2348         WallpaperData wallpaperData = null;
2349         boolean shouldExtract;
2350 
2351         synchronized (mLock) {
2352             if (which == FLAG_LOCK) {
2353                 wallpaperData = mLockWallpaperMap.get(userId);
2354             }
2355 
2356             // Try to get the system wallpaper anyway since it might
2357             // also be the lock screen wallpaper
2358             if (wallpaperData == null) {
2359                 wallpaperData = findWallpaperAtDisplay(userId, displayId);
2360             }
2361 
2362             if (wallpaperData == null) {
2363                 return null;
2364             }
2365             shouldExtract = wallpaperData.primaryColors == null;
2366         }
2367 
2368         if (shouldExtract) {
2369             extractColors(wallpaperData);
2370         }
2371 
2372         synchronized (mLock) {
2373             return wallpaperData.primaryColors;
2374         }
2375     }
2376 
findWallpaperAtDisplay(int userId, int displayId)2377     private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
2378         if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
2379                 && mFallbackWallpaper.connection.containsDisplay(displayId)) {
2380             return mFallbackWallpaper;
2381         } else {
2382             return mWallpaperMap.get(userId);
2383         }
2384     }
2385 
2386     @Override
setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, IWallpaperManagerCallback completion, int userId)2387     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
2388             Rect cropHint, boolean allowBackup, Bundle extras, int which,
2389             IWallpaperManagerCallback completion, int userId) {
2390         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
2391                 false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
2392         checkPermission(android.Manifest.permission.SET_WALLPAPER);
2393 
2394         if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
2395             final String msg = "Must specify a valid wallpaper category to set";
2396             Slog.e(TAG, msg);
2397             throw new IllegalArgumentException(msg);
2398         }
2399 
2400         if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
2401             return null;
2402         }
2403 
2404         // "null" means the no-op crop, preserving the full input image
2405         if (cropHint == null) {
2406             cropHint = new Rect(0, 0, 0, 0);
2407         } else {
2408             if (cropHint.isEmpty()
2409                     || cropHint.left < 0
2410                     || cropHint.top < 0) {
2411                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
2412             }
2413         }
2414 
2415         synchronized (mLock) {
2416             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
2417             WallpaperData wallpaper;
2418 
2419             /* If we're setting system but not lock, and lock is currently sharing the system
2420              * wallpaper, we need to migrate that image over to being lock-only before
2421              * the caller here writes new bitmap data.
2422              */
2423             if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
2424                 if (DEBUG) {
2425                     Slog.i(TAG, "Migrating system->lock to preserve");
2426                 }
2427                 migrateSystemToLockWallpaperLocked(userId);
2428             }
2429 
2430             wallpaper = getWallpaperSafeLocked(userId, which);
2431             final long ident = Binder.clearCallingIdentity();
2432             try {
2433                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
2434                 if (pfd != null) {
2435                     wallpaper.imageWallpaperPending = true;
2436                     wallpaper.whichPending = which;
2437                     wallpaper.setComplete = completion;
2438                     wallpaper.cropHint.set(cropHint);
2439                     wallpaper.allowBackup = allowBackup;
2440                 }
2441                 return pfd;
2442             } finally {
2443                 Binder.restoreCallingIdentity(ident);
2444             }
2445         }
2446     }
2447 
migrateSystemToLockWallpaperLocked(int userId)2448     private void migrateSystemToLockWallpaperLocked(int userId) {
2449         WallpaperData sysWP = mWallpaperMap.get(userId);
2450         if (sysWP == null) {
2451             if (DEBUG) {
2452                 Slog.i(TAG, "No system wallpaper?  Not tracking for lock-only");
2453             }
2454             return;
2455         }
2456 
2457         // We know a-priori that there is no lock-only wallpaper currently
2458         WallpaperData lockWP = new WallpaperData(userId,
2459                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2460         lockWP.wallpaperId = sysWP.wallpaperId;
2461         lockWP.cropHint.set(sysWP.cropHint);
2462         lockWP.allowBackup = sysWP.allowBackup;
2463         lockWP.primaryColors = sysWP.primaryColors;
2464 
2465         // Migrate the bitmap files outright; no need to copy
2466         try {
2467             Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
2468             Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
2469         } catch (ErrnoException e) {
2470             Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
2471             lockWP.wallpaperFile.delete();
2472             lockWP.cropFile.delete();
2473             return;
2474         }
2475 
2476         mLockWallpaperMap.put(userId, lockWP);
2477     }
2478 
updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, Bundle extras)2479     ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
2480             Bundle extras) {
2481         if (name == null) name = "";
2482         try {
2483             File dir = getWallpaperDir(wallpaper.userId);
2484             if (!dir.exists()) {
2485                 dir.mkdir();
2486                 FileUtils.setPermissions(
2487                         dir.getPath(),
2488                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
2489                         -1, -1);
2490             }
2491             ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
2492                     MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
2493             if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
2494                 return null;
2495             }
2496             wallpaper.name = name;
2497             wallpaper.wallpaperId = makeWallpaperIdLocked();
2498             if (extras != null) {
2499                 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
2500             }
2501             // Nullify field to require new computation
2502             wallpaper.primaryColors = null;
2503             if (DEBUG) {
2504                 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
2505                         + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
2506             }
2507             return fd;
2508         } catch (FileNotFoundException e) {
2509             Slog.w(TAG, "Error setting wallpaper", e);
2510         }
2511         return null;
2512     }
2513 
2514     @Override
setWallpaperComponentChecked(ComponentName name, String callingPackage, int userId)2515     public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
2516             int userId) {
2517 
2518         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
2519             setWallpaperComponent(name, userId);
2520         }
2521     }
2522 
2523     // ToDo: Remove this version of the function
2524     @Override
setWallpaperComponent(ComponentName name)2525     public void setWallpaperComponent(ComponentName name) {
2526         setWallpaperComponent(name, UserHandle.getCallingUserId());
2527     }
2528 
setWallpaperComponent(ComponentName name, int userId)2529     private void setWallpaperComponent(ComponentName name, int userId) {
2530         userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
2531                 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
2532         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
2533 
2534         int which = FLAG_SYSTEM;
2535         boolean shouldNotifyColors = false;
2536         WallpaperData wallpaper;
2537 
2538         synchronized (mLock) {
2539             if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
2540             wallpaper = mWallpaperMap.get(userId);
2541             if (wallpaper == null) {
2542                 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
2543             }
2544             final long ident = Binder.clearCallingIdentity();
2545 
2546             // Live wallpapers can't be specified for keyguard.  If we're using a static
2547             // system+lock image currently, migrate the system wallpaper to be a lock-only
2548             // image as part of making a different live component active as the system
2549             // wallpaper.
2550             if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
2551                 if (mLockWallpaperMap.get(userId) == null) {
2552                     // We're using the static imagery and there is no lock-specific image in place,
2553                     // therefore it's a shared system+lock image that we need to migrate.
2554                     migrateSystemToLockWallpaperLocked(userId);
2555                 }
2556             }
2557 
2558             // New live wallpaper is also a lock wallpaper if nothing is set
2559             if (mLockWallpaperMap.get(userId) == null) {
2560                 which |= FLAG_LOCK;
2561             }
2562 
2563             try {
2564                 wallpaper.imageWallpaperPending = false;
2565                 boolean same = changingToSame(name, wallpaper);
2566                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
2567                     if (!same) {
2568                         wallpaper.primaryColors = null;
2569                     }
2570                     wallpaper.wallpaperId = makeWallpaperIdLocked();
2571                     notifyCallbacksLocked(wallpaper);
2572                     shouldNotifyColors = true;
2573                 }
2574             } finally {
2575                 Binder.restoreCallingIdentity(ident);
2576             }
2577         }
2578 
2579         if (shouldNotifyColors) {
2580             notifyWallpaperColorsChanged(wallpaper, which);
2581             notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
2582         }
2583     }
2584 
changingToSame(ComponentName componentName, WallpaperData wallpaper)2585     private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
2586         if (wallpaper.connection != null) {
2587             if (wallpaper.wallpaperComponent == null) {
2588                 if (componentName == null) {
2589                     if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
2590                     // Still using default wallpaper.
2591                     return true;
2592                 }
2593             } else if (wallpaper.wallpaperComponent.equals(componentName)) {
2594                 // Changing to same wallpaper.
2595                 if (DEBUG) Slog.v(TAG, "same wallpaper");
2596                 return true;
2597             }
2598         }
2599         return false;
2600     }
2601 
bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply)2602     private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
2603             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
2604         if (DEBUG_LIVE) {
2605             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
2606         }
2607         // Has the component changed?
2608         if (!force && changingToSame(componentName, wallpaper)) {
2609             return true;
2610         }
2611 
2612         try {
2613             if (componentName == null) {
2614                 componentName = mDefaultWallpaperComponent;
2615                 if (componentName == null) {
2616                     // Fall back to static image wallpaper
2617                     componentName = mImageWallpaper;
2618                     //clearWallpaperComponentLocked();
2619                     //return;
2620                     if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
2621                 }
2622             }
2623             int serviceUserId = wallpaper.userId;
2624             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
2625                     PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
2626             if (si == null) {
2627                 // The wallpaper component we're trying to use doesn't exist
2628                 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
2629                 return false;
2630             }
2631             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
2632                 String msg = "Selected service does not have "
2633                         + android.Manifest.permission.BIND_WALLPAPER
2634                         + ": " + componentName;
2635                 if (fromUser) {
2636                     throw new SecurityException(msg);
2637                 }
2638                 Slog.w(TAG, msg);
2639                 return false;
2640             }
2641 
2642             WallpaperInfo wi = null;
2643 
2644             Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
2645             if (componentName != null && !componentName.equals(mImageWallpaper)) {
2646                 // Make sure the selected service is actually a wallpaper service.
2647                 List<ResolveInfo> ris =
2648                         mIPackageManager.queryIntentServices(intent,
2649                                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2650                                 PackageManager.GET_META_DATA, serviceUserId).getList();
2651                 for (int i=0; i<ris.size(); i++) {
2652                     ServiceInfo rsi = ris.get(i).serviceInfo;
2653                     if (rsi.name.equals(si.name) &&
2654                             rsi.packageName.equals(si.packageName)) {
2655                         try {
2656                             wi = new WallpaperInfo(mContext, ris.get(i));
2657                         } catch (XmlPullParserException e) {
2658                             if (fromUser) {
2659                                 throw new IllegalArgumentException(e);
2660                             }
2661                             Slog.w(TAG, e);
2662                             return false;
2663                         } catch (IOException e) {
2664                             if (fromUser) {
2665                                 throw new IllegalArgumentException(e);
2666                             }
2667                             Slog.w(TAG, e);
2668                             return false;
2669                         }
2670                         break;
2671                     }
2672                 }
2673                 if (wi == null) {
2674                     String msg = "Selected service is not a wallpaper: "
2675                             + componentName;
2676                     if (fromUser) {
2677                         throw new SecurityException(msg);
2678                     }
2679                     Slog.w(TAG, msg);
2680                     return false;
2681                 }
2682             }
2683 
2684             if (wi != null && wi.supportsAmbientMode()) {
2685                 final int hasPrivilege = mIPackageManager.checkPermission(
2686                         android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
2687                         serviceUserId);
2688                 if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
2689                     String msg = "Selected service does not have "
2690                             + android.Manifest.permission.AMBIENT_WALLPAPER
2691                             + ": " + componentName;
2692                     if (fromUser) {
2693                         throw new SecurityException(msg);
2694                     }
2695                     Slog.w(TAG, msg);
2696                     return false;
2697                 }
2698             }
2699 
2700             // Bind the service!
2701             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
2702             final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
2703                     MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
2704             WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
2705             intent.setComponent(componentName);
2706             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
2707                     com.android.internal.R.string.wallpaper_binding_label);
2708             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
2709                     mContext, 0,
2710                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
2711                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
2712                     0, null, new UserHandle(serviceUserId)));
2713             if (!mContext.bindServiceAsUser(intent, newConn,
2714                     Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
2715                             | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
2716                             | Context.BIND_INCLUDE_CAPABILITIES,
2717                     new UserHandle(serviceUserId))) {
2718                 String msg = "Unable to bind service: "
2719                         + componentName;
2720                 if (fromUser) {
2721                     throw new IllegalArgumentException(msg);
2722                 }
2723                 Slog.w(TAG, msg);
2724                 return false;
2725             }
2726             if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
2727                     && !wallpaper.equals(mFallbackWallpaper)) {
2728                 detachWallpaperLocked(mLastWallpaper);
2729             }
2730             wallpaper.wallpaperComponent = componentName;
2731             wallpaper.connection = newConn;
2732             newConn.mReply = reply;
2733             if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
2734                 mLastWallpaper = wallpaper;
2735             }
2736             updateFallbackConnection();
2737         } catch (RemoteException e) {
2738             String msg = "Remote exception for " + componentName + "\n" + e;
2739             if (fromUser) {
2740                 throw new IllegalArgumentException(msg);
2741             }
2742             Slog.w(TAG, msg);
2743             return false;
2744         }
2745         return true;
2746     }
2747 
detachWallpaperLocked(WallpaperData wallpaper)2748     private void detachWallpaperLocked(WallpaperData wallpaper) {
2749         if (wallpaper.connection != null) {
2750             if (wallpaper.connection.mReply != null) {
2751                 try {
2752                     wallpaper.connection.mReply.sendResult(null);
2753                 } catch (RemoteException e) {
2754                 }
2755                 wallpaper.connection.mReply = null;
2756             }
2757             try {
2758                 // It can be null if user switching happens before service connection.
2759                 if (wallpaper.connection.mService != null) {
2760                     wallpaper.connection.mService.detach();
2761                 }
2762             } catch (RemoteException e) {
2763                 Slog.w(TAG, "Failed detaching wallpaper service ", e);
2764             }
2765             mContext.unbindService(wallpaper.connection);
2766             wallpaper.connection.forEachDisplayConnector(
2767                     WallpaperConnection.DisplayConnector::disconnectLocked);
2768             wallpaper.connection.mService = null;
2769             wallpaper.connection.mDisplayConnector.clear();
2770             wallpaper.connection = null;
2771             if (wallpaper == mLastWallpaper) mLastWallpaper = null;
2772         }
2773     }
2774 
clearWallpaperComponentLocked(WallpaperData wallpaper)2775     private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
2776         wallpaper.wallpaperComponent = null;
2777         detachWallpaperLocked(wallpaper);
2778     }
2779 
attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper)2780     private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
2781         conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
2782     }
2783 
notifyCallbacksLocked(WallpaperData wallpaper)2784     private void notifyCallbacksLocked(WallpaperData wallpaper) {
2785         final int n = wallpaper.callbacks.beginBroadcast();
2786         for (int i = 0; i < n; i++) {
2787             try {
2788                 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
2789             } catch (RemoteException e) {
2790 
2791                 // The RemoteCallbackList will take care of removing
2792                 // the dead object for us.
2793             }
2794         }
2795         wallpaper.callbacks.finishBroadcast();
2796 
2797         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
2798         mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
2799     }
2800 
checkPermission(String permission)2801     private void checkPermission(String permission) {
2802         if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
2803             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
2804                     + ", must have permission " + permission);
2805         }
2806     }
2807 
2808     /**
2809      * Certain user types do not support wallpapers (e.g. managed profiles). The check is
2810      * implemented through through the OP_WRITE_WALLPAPER AppOp.
2811      */
isWallpaperSupported(String callingPackage)2812     public boolean isWallpaperSupported(String callingPackage) {
2813         return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
2814                 callingPackage) == AppOpsManager.MODE_ALLOWED;
2815     }
2816 
2817     @Override
isSetWallpaperAllowed(String callingPackage)2818     public boolean isSetWallpaperAllowed(String callingPackage) {
2819         final PackageManager pm = mContext.getPackageManager();
2820         String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
2821         boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
2822         if (!uidMatchPackage) {
2823             return false;   // callingPackage was faked.
2824         }
2825 
2826         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
2827         if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
2828             return true;
2829         }
2830         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
2831         return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
2832     }
2833 
2834     @Override
isWallpaperBackupEligible(int which, int userId)2835     public boolean isWallpaperBackupEligible(int which, int userId) {
2836         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2837             throw new SecurityException("Only the system may call isWallpaperBackupEligible");
2838         }
2839 
2840         WallpaperData wallpaper = (which == FLAG_LOCK)
2841                 ? mLockWallpaperMap.get(userId)
2842                 : mWallpaperMap.get(userId);
2843         return (wallpaper != null) ? wallpaper.allowBackup : false;
2844     }
2845 
onDisplayReadyInternal(int displayId)2846     private void onDisplayReadyInternal(int displayId) {
2847         synchronized (mLock) {
2848             if (mLastWallpaper == null) {
2849                 return;
2850             }
2851             if (supportsMultiDisplay(mLastWallpaper.connection)) {
2852                 final WallpaperConnection.DisplayConnector connector =
2853                         mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
2854                 if (connector == null) return;
2855                 connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
2856                 return;
2857             }
2858             // System wallpaper does not support multiple displays, attach this display to
2859             // the fallback wallpaper.
2860             if (mFallbackWallpaper != null) {
2861                 final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
2862                         .connection.getDisplayConnectorOrCreate(displayId);
2863                 if (connector == null) return;
2864                 connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
2865             } else {
2866                 Slog.w(TAG, "No wallpaper can be added to the new display");
2867             }
2868         }
2869     }
2870 
makeJournaledFile(int userId)2871     private static JournaledFile makeJournaledFile(int userId) {
2872         final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
2873         return new JournaledFile(new File(base), new File(base + ".tmp"));
2874     }
2875 
saveSettingsLocked(int userId)2876     private void saveSettingsLocked(int userId) {
2877         JournaledFile journal = makeJournaledFile(userId);
2878         FileOutputStream fstream = null;
2879         BufferedOutputStream stream = null;
2880         try {
2881             XmlSerializer out = new FastXmlSerializer();
2882             fstream = new FileOutputStream(journal.chooseForWrite(), false);
2883             stream = new BufferedOutputStream(fstream);
2884             out.setOutput(stream, StandardCharsets.UTF_8.name());
2885             out.startDocument(null, true);
2886 
2887             WallpaperData wallpaper;
2888 
2889             wallpaper = mWallpaperMap.get(userId);
2890             if (wallpaper != null) {
2891                 writeWallpaperAttributes(out, "wp", wallpaper);
2892             }
2893             wallpaper = mLockWallpaperMap.get(userId);
2894             if (wallpaper != null) {
2895                 writeWallpaperAttributes(out, "kwp", wallpaper);
2896             }
2897 
2898             out.endDocument();
2899 
2900             stream.flush(); // also flushes fstream
2901             FileUtils.sync(fstream);
2902             stream.close(); // also closes fstream
2903             journal.commit();
2904         } catch (IOException e) {
2905             IoUtils.closeQuietly(stream);
2906             journal.rollback();
2907         }
2908     }
2909 
writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)2910     private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
2911             throws IllegalArgumentException, IllegalStateException, IOException {
2912         if (DEBUG) {
2913             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
2914         }
2915         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
2916         out.startTag(null, tag);
2917         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
2918         out.attribute(null, "width", Integer.toString(wpdData.mWidth));
2919         out.attribute(null, "height", Integer.toString(wpdData.mHeight));
2920 
2921         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
2922         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
2923         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
2924         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
2925 
2926         if (wpdData.mPadding.left != 0) {
2927             out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
2928         }
2929         if (wpdData.mPadding.top != 0) {
2930             out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
2931         }
2932         if (wpdData.mPadding.right != 0) {
2933             out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
2934         }
2935         if (wpdData.mPadding.bottom != 0) {
2936             out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
2937         }
2938 
2939         if (wallpaper.primaryColors != null) {
2940             int colorsCount = wallpaper.primaryColors.getMainColors().size();
2941             out.attribute(null, "colorsCount", Integer.toString(colorsCount));
2942             if (colorsCount > 0) {
2943                 for (int i = 0; i < colorsCount; i++) {
2944                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
2945                     out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
2946                 }
2947             }
2948             out.attribute(null, "colorHints",
2949                     Integer.toString(wallpaper.primaryColors.getColorHints()));
2950         }
2951 
2952         out.attribute(null, "name", wallpaper.name);
2953         if (wallpaper.wallpaperComponent != null
2954                 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
2955             out.attribute(null, "component",
2956                     wallpaper.wallpaperComponent.flattenToShortString());
2957         }
2958 
2959         if (wallpaper.allowBackup) {
2960             out.attribute(null, "backup", "true");
2961         }
2962 
2963         out.endTag(null, tag);
2964     }
2965 
migrateFromOld()2966     private void migrateFromOld() {
2967         // Pre-N, what existed is the one we're now using as the display crop
2968         File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
2969         // In the very-long-ago, imagery lived with the settings app
2970         File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
2971         File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
2972 
2973         // Migrations from earlier wallpaper image storage schemas
2974         if (preNWallpaper.exists()) {
2975             if (!newWallpaper.exists()) {
2976                 // we've got the 'wallpaper' crop file but not the nominal source image,
2977                 // so do the simple "just take everything" straight copy of legacy data
2978                 if (DEBUG) {
2979                     Slog.i(TAG, "Migrating wallpaper schema");
2980                 }
2981                 FileUtils.copyFile(preNWallpaper, newWallpaper);
2982             } // else we're in the usual modern case: both source & crop exist
2983         } else if (originalWallpaper.exists()) {
2984             // VERY old schema; make sure things exist and are in the right place
2985             if (DEBUG) {
2986                 Slog.i(TAG, "Migrating antique wallpaper schema");
2987             }
2988             File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
2989             if (oldInfo.exists()) {
2990                 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
2991                 oldInfo.renameTo(newInfo);
2992             }
2993 
2994             FileUtils.copyFile(originalWallpaper, preNWallpaper);
2995             originalWallpaper.renameTo(newWallpaper);
2996         }
2997     }
2998 
getAttributeInt(XmlPullParser parser, String name, int defValue)2999     private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
3000         String value = parser.getAttributeValue(null, name);
3001         if (value == null) {
3002             return defValue;
3003         }
3004         return Integer.parseInt(value);
3005     }
3006 
3007     /**
3008      * Sometimes it is expected the wallpaper map may not have a user's data.  E.g. This could
3009      * happen during user switch.  The async user switch observer may not have received
3010      * the event yet.  We use this safe method when we don't care about this ordering and just
3011      * want to update the data.  The data is going to be applied when the user switch observer
3012      * is eventually executed.
3013      *
3014      * Important: this method loads settings to initialize the given user's wallpaper data if
3015      * there is no current in-memory state.
3016      */
getWallpaperSafeLocked(int userId, int which)3017     private WallpaperData getWallpaperSafeLocked(int userId, int which) {
3018         // We're setting either just system (work with the system wallpaper),
3019         // both (also work with the system wallpaper), or just the lock
3020         // wallpaper (update against the existing lock wallpaper if any).
3021         // Combined or just-system operations use the 'system' WallpaperData
3022         // for this use; lock-only operations use the dedicated one.
3023         final SparseArray<WallpaperData> whichSet =
3024                 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
3025         WallpaperData wallpaper = whichSet.get(userId);
3026         if (wallpaper == null) {
3027             // common case, this is the first lookup post-boot of the system or
3028             // unified lock, so we bring up the saved state lazily now and recheck.
3029             loadSettingsLocked(userId, false);
3030             wallpaper = whichSet.get(userId);
3031             // if it's still null here, this is a lock-only operation and there is not
3032             // yet a lock-only wallpaper set for this user, so we need to establish
3033             // it now.
3034             if (wallpaper == null) {
3035                 if (which == FLAG_LOCK) {
3036                     wallpaper = new WallpaperData(userId,
3037                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
3038                     mLockWallpaperMap.put(userId, wallpaper);
3039                     ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
3040                 } else {
3041                     // sanity fallback: we're in bad shape, but establishing a known
3042                     // valid system+lock WallpaperData will keep us from dying.
3043                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
3044                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
3045                     mWallpaperMap.put(userId, wallpaper);
3046                     ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
3047                 }
3048             }
3049         }
3050         return wallpaper;
3051     }
3052 
loadSettingsLocked(int userId, boolean keepDimensionHints)3053     private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
3054         JournaledFile journal = makeJournaledFile(userId);
3055         FileInputStream stream = null;
3056         File file = journal.chooseForRead();
3057 
3058         WallpaperData wallpaper = mWallpaperMap.get(userId);
3059         if (wallpaper == null) {
3060             // Do this once per boot
3061             migrateFromOld();
3062 
3063             wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
3064             wallpaper.allowBackup = true;
3065             mWallpaperMap.put(userId, wallpaper);
3066             if (!wallpaper.cropExists()) {
3067                 if (wallpaper.sourceExists()) {
3068                     generateCrop(wallpaper);
3069                 } else {
3070                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
3071                 }
3072             }
3073             initializeFallbackWallpaper();
3074         }
3075         boolean success = false;
3076         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
3077         try {
3078             stream = new FileInputStream(file);
3079             XmlPullParser parser = Xml.newPullParser();
3080             parser.setInput(stream, StandardCharsets.UTF_8.name());
3081 
3082             int type;
3083             do {
3084                 type = parser.next();
3085                 if (type == XmlPullParser.START_TAG) {
3086                     String tag = parser.getName();
3087                     if ("wp".equals(tag)) {
3088                         // Common to system + lock wallpapers
3089                         parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
3090 
3091                         // A system wallpaper might also be a live wallpaper
3092                         String comp = parser.getAttributeValue(null, "component");
3093                         wallpaper.nextWallpaperComponent = comp != null
3094                                 ? ComponentName.unflattenFromString(comp)
3095                                 : null;
3096                         if (wallpaper.nextWallpaperComponent == null
3097                                 || "android".equals(wallpaper.nextWallpaperComponent
3098                                         .getPackageName())) {
3099                             wallpaper.nextWallpaperComponent = mImageWallpaper;
3100                         }
3101 
3102                         if (DEBUG) {
3103                             Slog.v(TAG, "mWidth:" + wpdData.mWidth);
3104                             Slog.v(TAG, "mHeight:" + wpdData.mHeight);
3105                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
3106                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
3107                             Slog.v(TAG, "mName:" + wallpaper.name);
3108                             Slog.v(TAG, "mNextWallpaperComponent:"
3109                                     + wallpaper.nextWallpaperComponent);
3110                         }
3111                     } else if ("kwp".equals(tag)) {
3112                         // keyguard-specific wallpaper for this user
3113                         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
3114                         if (lockWallpaper == null) {
3115                             lockWallpaper = new WallpaperData(userId,
3116                                     WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
3117                             mLockWallpaperMap.put(userId, lockWallpaper);
3118                         }
3119                         parseWallpaperAttributes(parser, lockWallpaper, false);
3120                     }
3121                 }
3122             } while (type != XmlPullParser.END_DOCUMENT);
3123             success = true;
3124         } catch (FileNotFoundException e) {
3125             Slog.w(TAG, "no current wallpaper -- first boot?");
3126         } catch (NullPointerException e) {
3127             Slog.w(TAG, "failed parsing " + file + " " + e);
3128         } catch (NumberFormatException e) {
3129             Slog.w(TAG, "failed parsing " + file + " " + e);
3130         } catch (XmlPullParserException e) {
3131             Slog.w(TAG, "failed parsing " + file + " " + e);
3132         } catch (IOException e) {
3133             Slog.w(TAG, "failed parsing " + file + " " + e);
3134         } catch (IndexOutOfBoundsException e) {
3135             Slog.w(TAG, "failed parsing " + file + " " + e);
3136         }
3137         IoUtils.closeQuietly(stream);
3138 
3139         if (!success) {
3140             wallpaper.cropHint.set(0, 0, 0, 0);
3141             wpdData.mPadding.set(0, 0, 0, 0);
3142             wallpaper.name = "";
3143 
3144             mLockWallpaperMap.remove(userId);
3145         } else {
3146             if (wallpaper.wallpaperId <= 0) {
3147                 wallpaper.wallpaperId = makeWallpaperIdLocked();
3148                 if (DEBUG) {
3149                     Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
3150                             + "); now " + wallpaper.wallpaperId);
3151                 }
3152             }
3153         }
3154 
3155         ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
3156         ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
3157         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
3158         if (lockWallpaper != null) {
3159             ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
3160         }
3161     }
3162 
initializeFallbackWallpaper()3163     private void initializeFallbackWallpaper() {
3164         if (mFallbackWallpaper == null) {
3165             if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
3166             mFallbackWallpaper = new WallpaperData(
3167                     UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
3168             mFallbackWallpaper.allowBackup = false;
3169             mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
3170             bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
3171         }
3172     }
3173 
ensureSaneWallpaperData(WallpaperData wallpaper, int displayId)3174     private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
3175         final DisplayData size = getDisplayDataOrCreate(displayId);
3176 
3177         if (displayId == DEFAULT_DISPLAY) {
3178             // crop, if not previously specified
3179             if (wallpaper.cropHint.width() <= 0
3180                     || wallpaper.cropHint.height() <= 0) {
3181                 wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
3182             }
3183         }
3184     }
3185 
parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)3186     private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
3187             boolean keepDimensionHints) {
3188         final String idString = parser.getAttributeValue(null, "id");
3189         if (idString != null) {
3190             final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
3191             if (id > mWallpaperId) {
3192                 mWallpaperId = id;
3193             }
3194         } else {
3195             wallpaper.wallpaperId = makeWallpaperIdLocked();
3196         }
3197 
3198         final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
3199 
3200         if (!keepDimensionHints) {
3201             wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
3202             wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
3203         }
3204         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
3205         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
3206         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
3207         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
3208         wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
3209         wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
3210         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
3211         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
3212         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
3213         if (colorsCount > 0) {
3214             Color primary = null, secondary = null, tertiary = null;
3215             for (int i = 0; i < colorsCount; i++) {
3216                 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
3217                 if (i == 0) {
3218                     primary = color;
3219                 } else if (i == 1) {
3220                     secondary = color;
3221                 } else if (i == 2) {
3222                     tertiary = color;
3223                 } else {
3224                     break;
3225                 }
3226             }
3227             int colorHints = getAttributeInt(parser, "colorHints", 0);
3228             wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
3229         }
3230         wallpaper.name = parser.getAttributeValue(null, "name");
3231         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
3232     }
3233 
3234     // Called by SystemBackupAgent after files are restored to disk.
settingsRestored()3235     public void settingsRestored() {
3236         // Verify caller is the system
3237         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
3238             throw new RuntimeException("settingsRestored() can only be called from the system process");
3239         }
3240         // TODO: If necessary, make it work for secondary users as well. This currently assumes
3241         // restores only to the primary user
3242         if (DEBUG) Slog.v(TAG, "settingsRestored");
3243         WallpaperData wallpaper = null;
3244         boolean success = false;
3245         synchronized (mLock) {
3246             loadSettingsLocked(UserHandle.USER_SYSTEM, false);
3247             wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
3248             wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
3249             wallpaper.allowBackup = true;   // by definition if it was restored
3250             if (wallpaper.nextWallpaperComponent != null
3251                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
3252                 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
3253                         wallpaper, null)) {
3254                     // No such live wallpaper or other failure; fall back to the default
3255                     // live wallpaper (since the profile being restored indicated that the
3256                     // user had selected a live rather than static one).
3257                     bindWallpaperComponentLocked(null, false, false, wallpaper, null);
3258                 }
3259                 success = true;
3260             } else {
3261                 // If there's a wallpaper name, we use that.  If that can't be loaded, then we
3262                 // use the default.
3263                 if ("".equals(wallpaper.name)) {
3264                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
3265                     success = true;
3266                 } else {
3267                     if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
3268                     success = restoreNamedResourceLocked(wallpaper);
3269                 }
3270                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
3271                         + " id=" + wallpaper.wallpaperId);
3272                 if (success) {
3273                     generateCrop(wallpaper); // based on the new image + metadata
3274                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
3275                             wallpaper, null);
3276                 }
3277             }
3278         }
3279 
3280         if (!success) {
3281             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
3282             wallpaper.name = "";
3283             getWallpaperDir(UserHandle.USER_SYSTEM).delete();
3284         }
3285 
3286         synchronized (mLock) {
3287             saveSettingsLocked(UserHandle.USER_SYSTEM);
3288         }
3289     }
3290 
3291     // Restore the named resource bitmap to both source + crop files
restoreNamedResourceLocked(WallpaperData wallpaper)3292     private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
3293         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
3294             String resName = wallpaper.name.substring(4);
3295 
3296             String pkg = null;
3297             int colon = resName.indexOf(':');
3298             if (colon > 0) {
3299                 pkg = resName.substring(0, colon);
3300             }
3301 
3302             String ident = null;
3303             int slash = resName.lastIndexOf('/');
3304             if (slash > 0) {
3305                 ident = resName.substring(slash+1);
3306             }
3307 
3308             String type = null;
3309             if (colon > 0 && slash > 0 && (slash-colon) > 1) {
3310                 type = resName.substring(colon+1, slash);
3311             }
3312 
3313             if (pkg != null && ident != null && type != null) {
3314                 int resId = -1;
3315                 InputStream res = null;
3316                 FileOutputStream fos = null;
3317                 FileOutputStream cos = null;
3318                 try {
3319                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
3320                     Resources r = c.getResources();
3321                     resId = r.getIdentifier(resName, null, null);
3322                     if (resId == 0) {
3323                         Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
3324                                 + " ident=" + ident);
3325                         return false;
3326                     }
3327 
3328                     res = r.openRawResource(resId);
3329                     if (wallpaper.wallpaperFile.exists()) {
3330                         wallpaper.wallpaperFile.delete();
3331                         wallpaper.cropFile.delete();
3332                     }
3333                     fos = new FileOutputStream(wallpaper.wallpaperFile);
3334                     cos = new FileOutputStream(wallpaper.cropFile);
3335 
3336                     byte[] buffer = new byte[32768];
3337                     int amt;
3338                     while ((amt=res.read(buffer)) > 0) {
3339                         fos.write(buffer, 0, amt);
3340                         cos.write(buffer, 0, amt);
3341                     }
3342                     // mWallpaperObserver will notice the close and send the change broadcast
3343 
3344                     Slog.v(TAG, "Restored wallpaper: " + resName);
3345                     return true;
3346                 } catch (NameNotFoundException e) {
3347                     Slog.e(TAG, "Package name " + pkg + " not found");
3348                 } catch (Resources.NotFoundException e) {
3349                     Slog.e(TAG, "Resource not found: " + resId);
3350                 } catch (IOException e) {
3351                     Slog.e(TAG, "IOException while restoring wallpaper ", e);
3352                 } finally {
3353                     IoUtils.closeQuietly(res);
3354                     if (fos != null) {
3355                         FileUtils.sync(fos);
3356                     }
3357                     if (cos != null) {
3358                         FileUtils.sync(cos);
3359                     }
3360                     IoUtils.closeQuietly(fos);
3361                     IoUtils.closeQuietly(cos);
3362                 }
3363             }
3364         }
3365         return false;
3366     }
3367 
3368     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)3369     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3370         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
3371 
3372         synchronized (mLock) {
3373             pw.println("System wallpaper state:");
3374             for (int i = 0; i < mWallpaperMap.size(); i++) {
3375                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
3376                 pw.print(" User "); pw.print(wallpaper.userId);
3377                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
3378                 pw.println(" Display state:");
3379                 forEachDisplayData(wpSize -> {
3380                     pw.print("  displayId=");
3381                     pw.println(wpSize.mDisplayId);
3382                     pw.print("  mWidth=");
3383                     pw.print(wpSize.mWidth);
3384                     pw.print("  mHeight=");
3385                     pw.println(wpSize.mHeight);
3386                     pw.print("  mPadding="); pw.println(wpSize.mPadding);
3387                 });
3388                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
3389                 pw.print("  mName=");  pw.println(wallpaper.name);
3390                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
3391                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
3392                 if (wallpaper.connection != null) {
3393                     WallpaperConnection conn = wallpaper.connection;
3394                     pw.print("  Wallpaper connection ");
3395                     pw.print(conn);
3396                     pw.println(":");
3397                     if (conn.mInfo != null) {
3398                         pw.print("    mInfo.component=");
3399                         pw.println(conn.mInfo.getComponent());
3400                     }
3401                     conn.forEachDisplayConnector(connector -> {
3402                         pw.print("     mDisplayId=");
3403                         pw.println(connector.mDisplayId);
3404                         pw.print("     mToken=");
3405                         pw.println(connector.mToken);
3406                         pw.print("     mEngine=");
3407                         pw.println(connector.mEngine);
3408                     });
3409                     pw.print("    mService=");
3410                     pw.println(conn.mService);
3411                     pw.print("    mLastDiedTime=");
3412                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
3413                 }
3414             }
3415             pw.println("Lock wallpaper state:");
3416             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
3417                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
3418                 pw.print(" User "); pw.print(wallpaper.userId);
3419                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
3420                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
3421                 pw.print("  mName=");  pw.println(wallpaper.name);
3422                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
3423             }
3424             pw.println("Fallback wallpaper state:");
3425             pw.print(" User "); pw.print(mFallbackWallpaper.userId);
3426             pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
3427             pw.print("  mCropHint="); pw.println(mFallbackWallpaper.cropHint);
3428             pw.print("  mName=");  pw.println(mFallbackWallpaper.name);
3429             pw.print("  mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
3430             if (mFallbackWallpaper.connection != null) {
3431                 WallpaperConnection conn = mFallbackWallpaper.connection;
3432                 pw.print("  Fallback Wallpaper connection ");
3433                 pw.print(conn);
3434                 pw.println(":");
3435                 if (conn.mInfo != null) {
3436                     pw.print("    mInfo.component=");
3437                     pw.println(conn.mInfo.getComponent());
3438                 }
3439                 conn.forEachDisplayConnector(connector -> {
3440                     pw.print("     mDisplayId=");
3441                     pw.println(connector.mDisplayId);
3442                     pw.print("     mToken=");
3443                     pw.println(connector.mToken);
3444                     pw.print("     mEngine=");
3445                     pw.println(connector.mEngine);
3446                 });
3447                 pw.print("    mService=");
3448                 pw.println(conn.mService);
3449                 pw.print("    mLastDiedTime=");
3450                 pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
3451             }
3452         }
3453     }
3454 }
3455