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