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