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