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