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