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