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