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