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