2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.wallpaper;
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;
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.graphics.Bitmap;
55 import android.graphics.BitmapFactory;
56 import android.graphics.BitmapRegionDecoder;
57 import android.graphics.Color;
58 import android.graphics.Point;
59 import android.graphics.Rect;
60 import android.os.Binder;
61 import android.os.Bundle;
62 import android.os.Environment;
63 import android.os.FileObserver;
64 import android.os.FileUtils;
65 import android.os.Handler;
66 import android.os.IBinder;
67 import android.os.IInterface;
68 import android.os.IRemoteCallback;
69 import android.os.ParcelFileDescriptor;
70 import android.os.Process;
71 import android.os.RemoteCallbackList;
72 import android.os.RemoteException;
73 import android.os.SELinux;
74 import android.os.ServiceManager;
75 import android.os.SystemClock;
76 import android.os.UserHandle;
77 import android.os.UserManager;
78 import android.service.wallpaper.IWallpaperConnection;
79 import android.service.wallpaper.IWallpaperEngine;
80 import android.service.wallpaper.IWallpaperService;
81 import android.service.wallpaper.WallpaperService;
82 import android.system.ErrnoException;
83 import android.system.Os;
84 import android.util.EventLog;
85 import android.util.Slog;
86 import android.util.SparseArray;
87 import android.util.Xml;
88 import android.view.Display;
89 import android.view.IWindowManager;
90 import android.view.WindowManager;
92 import com.android.internal.R;
93 import com.android.internal.content.PackageMonitor;
94 import com.android.internal.os.BackgroundThread;
95 import com.android.internal.util.DumpUtils;
96 import com.android.internal.util.FastXmlSerializer;
97 import com.android.internal.util.JournaledFile;
98 import com.android.server.EventLogTags;
99 import com.android.server.FgThread;
100 import com.android.server.SystemService;
102 import libcore.io.IoUtils;
104 import org.xmlpull.v1.XmlPullParser;
105 import org.xmlpull.v1.XmlPullParserException;
106 import org.xmlpull.v1.XmlSerializer;
108 import java.io.BufferedOutputStream;
110 import java.io.FileDescriptor;
111 import java.io.FileInputStream;
112 import java.io.FileNotFoundException;
113 import java.io.FileOutputStream;
114 import java.io.IOException;
115 import java.io.InputStream;
116 import java.io.PrintWriter;
117 import java.nio.charset.StandardCharsets;
118 import java.util.ArrayList;
119 import java.util.Arrays;
120 import java.util.List;
121 import java.util.Objects;
123 public class WallpaperManagerService extends IWallpaperManager.Stub {
124 static final String TAG = "WallpaperManagerService";
125 static final boolean DEBUG = false;
126 static final boolean DEBUG_LIVE = DEBUG || true;
128 public static class Lifecycle extends SystemService {
129 private WallpaperManagerService mService;
131 public Lifecycle(Context context) {
136 public void onStart() {
137 mService = new WallpaperManagerService(getContext());
138 publishBinderService(Context.WALLPAPER_SERVICE, mService);
142 public void onBootPhase(int phase) {
143 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
144 mService.systemReady();
145 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
146 mService.switchUser(UserHandle.USER_SYSTEM, null);
151 public void onUnlockUser(int userHandle) {
152 mService.onUnlockUser(userHandle);
156 final Object mLock = new Object();
159 * Minimum time between crashes of a wallpaper service for us to consider
160 * restarting it vs. just reverting to the static wallpaper.
162 static final long MIN_WALLPAPER_CRASH_TIME = 10000;
163 static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
164 static final String WALLPAPER = "wallpaper_orig";
165 static final String WALLPAPER_CROP = "wallpaper";
166 static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
167 static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
168 static final String WALLPAPER_INFO = "wallpaper_info.xml";
170 // All the various per-user state files we need to be aware of
171 static final String[] sPerUserFiles = new String[] {
172 WALLPAPER, WALLPAPER_CROP,
173 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
178 * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
179 * that the wallpaper has changed. The CREATE is triggered when there is no
180 * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
181 * every time the wallpaper is changed.
183 private class WallpaperObserver extends FileObserver {
186 final WallpaperData mWallpaper;
187 final File mWallpaperDir;
188 final File mWallpaperFile;
189 final File mWallpaperLockFile;
191 public WallpaperObserver(WallpaperData wallpaper) {
192 super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
193 CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
194 mUserId = wallpaper.userId;
195 mWallpaperDir = getWallpaperDir(wallpaper.userId);
196 mWallpaper = wallpaper;
197 mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
198 mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
201 private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
202 WallpaperData wallpaper = null;
203 synchronized (mLock) {
205 wallpaper = mLockWallpaperMap.get(mUserId);
207 if (wallpaper == null) {
208 // no lock-specific wallpaper exists, or sys case, handled together
209 wallpaper = mWallpaperMap.get(mUserId);
212 return (wallpaper != null) ? wallpaper : mWallpaper;
216 public void onEvent(int event, String path) {
220 final boolean moved = (event == MOVED_TO);
221 final boolean written = (event == CLOSE_WRITE || moved);
222 final File changedFile = new File(mWallpaperDir, path);
224 // System and system+lock changes happen on the system wallpaper input file;
225 // lock-only changes happen on the dedicated lock wallpaper input file
226 final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
227 final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
228 int notifyColorsWhich = 0;
229 WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
232 Slog.v(TAG, "Wallpaper file change: evt=" + event
234 + " sys=" + sysWallpaperChanged
235 + " lock=" + lockWallpaperChanged
236 + " imagePending=" + wallpaper.imageWallpaperPending
237 + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending)
238 + " written=" + written);
241 if (moved && lockWallpaperChanged) {
242 // We just migrated sys -> lock to preserve imagery for an impending
243 // new system-only wallpaper. Tell keyguard about it and make sure it
244 // has the right SELinux label.
246 Slog.i(TAG, "Sys -> lock MOVED_TO");
248 SELinux.restorecon(changedFile);
249 notifyLockWallpaperChanged();
250 notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);
254 synchronized (mLock) {
255 if (sysWallpaperChanged || lockWallpaperChanged) {
256 notifyCallbacksLocked(wallpaper);
257 if (wallpaper.wallpaperComponent == null
258 || event != CLOSE_WRITE // includes the MOVED_TO case
259 || wallpaper.imageWallpaperPending) {
261 // The image source has finished writing the source image,
262 // so we now produce the crop rect (in the background), and
263 // only publish the new displayable (sub)image as a result
266 Slog.v(TAG, "Wallpaper written; generating crop");
268 SELinux.restorecon(changedFile);
270 // This is a restore, so generate the crop using any just-restored new
271 // crop guidelines, making sure to preserve our local dimension hints.
272 // We also make sure to reapply the correct SELinux label.
274 Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
276 loadSettingsLocked(wallpaper.userId, true);
278 generateCrop(wallpaper);
280 Slog.v(TAG, "Crop done; invoking completion callback");
282 wallpaper.imageWallpaperPending = false;
283 if (wallpaper.setComplete != null) {
285 wallpaper.setComplete.onWallpaperChanged();
286 } catch (RemoteException e) {
287 // if this fails we don't really care; the setting app may just
288 // have crashed and that sort of thing is a fact of life.
291 if (sysWallpaperChanged) {
292 // If this was the system wallpaper, rebind...
293 bindWallpaperComponentLocked(mImageWallpaper, true,
294 false, wallpaper, null);
295 notifyColorsWhich |= FLAG_SYSTEM;
297 if (lockWallpaperChanged
298 || (wallpaper.whichPending & FLAG_LOCK) != 0) {
300 Slog.i(TAG, "Lock-relevant wallpaper changed");
302 // either a lock-only wallpaper commit or a system+lock event.
303 // if it's system-plus-lock we need to wipe the lock bookkeeping;
304 // we're falling back to displaying the system wallpaper there.
305 if (!lockWallpaperChanged) {
306 mLockWallpaperMap.remove(wallpaper.userId);
308 // and in any case, tell keyguard about it
309 notifyLockWallpaperChanged();
310 notifyColorsWhich |= FLAG_LOCK;
313 saveSettingsLocked(wallpaper.userId);
319 // Outside of the lock since it will synchronize itself
320 if (notifyColorsWhich != 0) {
321 notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
326 void notifyLockWallpaperChanged() {
327 final IWallpaperManagerCallback cb = mKeyguardListener;
330 cb.onWallpaperChanged();
331 } catch (RemoteException e) {
332 // Oh well it went away; no big deal
337 private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
338 boolean needsExtraction;
339 synchronized (mLock) {
340 final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
341 mColorsChangedListeners.get(wallpaper.userId);
342 final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
343 mColorsChangedListeners.get(UserHandle.USER_ALL);
344 // No-op until someone is listening to it.
345 if (emptyCallbackList(currentUserColorListeners) &&
346 emptyCallbackList(userAllColorListeners)) {
351 Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
354 needsExtraction = wallpaper.primaryColors == null;
357 // Let's notify the current values, it's fine if it's null, it just means
358 // that we don't know yet.
359 notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
361 if (needsExtraction) {
362 extractColors(wallpaper);
363 synchronized (mLock) {
364 // Don't need to notify if nothing changed.
365 if (wallpaper.primaryColors == null) {
369 notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
373 private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
374 return (list == null || list.getRegisteredCallbackCount() == 0);
377 private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
379 final IWallpaperManagerCallback keyguardListener;
380 final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
381 synchronized (mLock) {
382 final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
383 mColorsChangedListeners.get(userId);
384 final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
385 mColorsChangedListeners.get(UserHandle.USER_ALL);
386 keyguardListener = mKeyguardListener;
388 if (currentUserColorListeners != null) {
389 final int count = currentUserColorListeners.beginBroadcast();
390 for (int i = 0; i < count; i++) {
391 colorListeners.add(currentUserColorListeners.getBroadcastItem(i));
393 currentUserColorListeners.finishBroadcast();
396 if (userAllColorListeners != null) {
397 final int count = userAllColorListeners.beginBroadcast();
398 for (int i = 0; i < count; i++) {
399 colorListeners.add(userAllColorListeners.getBroadcastItem(i));
401 userAllColorListeners.finishBroadcast();
405 final int count = colorListeners.size();
406 for (int i = 0; i < count; i++) {
408 colorListeners.get(i).onWallpaperColorsChanged(wallpaperColors, which, userId);
409 } catch (RemoteException e) {
410 // Callback is gone, it's not necessary to unregister it since
411 // RemoteCallbackList#getBroadcastItem will take care of it.
415 if (keyguardListener != null) {
417 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
418 } catch (RemoteException e) {
419 // Oh well it went away; no big deal
425 * We can easily extract colors from an ImageWallpaper since it's only a bitmap.
426 * In this case, using the crop is more than enough. Live wallpapers are just ignored.
428 * @param wallpaper a wallpaper representation
430 private void extractColors(WallpaperData wallpaper) {
431 String cropFile = null;
434 synchronized (mLock) {
435 // Not having a wallpaperComponent means it's a lock screen wallpaper.
436 final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
437 || wallpaper.wallpaperComponent == null;
438 if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) {
439 cropFile = wallpaper.cropFile.getAbsolutePath();
441 wallpaperId = wallpaper.wallpaperId;
444 WallpaperColors colors = null;
445 if (cropFile != null) {
446 Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
447 if (bitmap != null) {
448 colors = WallpaperColors.fromBitmap(bitmap);
453 if (colors == null) {
454 Slog.w(TAG, "Cannot extract colors because wallpaper could not be read.");
458 synchronized (mLock) {
459 if (wallpaper.wallpaperId == wallpaperId) {
460 wallpaper.primaryColors = colors;
461 // Now that we have the colors, let's save them into the xml
462 // to avoid having to run this again.
463 saveSettingsLocked(wallpaper.userId);
465 Slog.w(TAG, "Not setting primary colors since wallpaper changed");
471 * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
474 private void generateCrop(WallpaperData wallpaper) {
475 boolean success = false;
477 Rect cropHint = new Rect(wallpaper.cropHint);
480 Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
481 + Integer.toHexString(wallpaper.whichPending)
482 + " to " + wallpaper.cropFile.getName()
483 + " crop=(" + cropHint.width() + 'x' + cropHint.height()
484 + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
487 // Analyse the source; needed in multiple cases
488 BitmapFactory.Options options = new BitmapFactory.Options();
489 options.inJustDecodeBounds = true;
490 BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
491 if (options.outWidth <= 0 || options.outHeight <= 0) {
492 Slog.w(TAG, "Invalid wallpaper data");
495 boolean needCrop = false;
496 boolean needScale = false;
498 // Empty crop means use the full image
499 if (cropHint.isEmpty()) {
500 cropHint.left = cropHint.top = 0;
501 cropHint.right = options.outWidth;
502 cropHint.bottom = options.outHeight;
504 // force the crop rect to lie within the measured bounds
506 (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
507 (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
509 // If the crop hint was larger than the image we just overshot. Patch things up.
510 if (cropHint.left < 0) {
513 if (cropHint.top < 0) {
517 // Don't bother cropping if what we're left with is identity
518 needCrop = (options.outHeight > cropHint.height()
519 || options.outWidth > cropHint.width());
522 // scale if the crop height winds up not matching the recommended metrics
523 needScale = (wallpaper.height != cropHint.height());
526 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
527 Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
528 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
529 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
532 if (!needCrop && !needScale) {
533 // Simple case: the nominal crop fits what we want, so we take
534 // the whole thing and just copy the image file directly.
536 Slog.v(TAG, "Null crop of new wallpaper; copying");
538 success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
540 wallpaper.cropFile.delete();
541 // TODO: fall back to default wallpaper in this case
544 // Fancy case: crop and scale. First, we decode and scale down if appropriate.
545 FileOutputStream f = null;
546 BufferedOutputStream bos = null;
548 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
549 wallpaper.wallpaperFile.getAbsolutePath(), false);
551 // This actually downsamples only by powers of two, but that's okay; we do
552 // a proper scaling blit later. This is to minimize transient RAM use.
553 // We calculate the largest power-of-two under the actual ratio rather than
554 // just let the decode take care of it because we also want to remap where the
555 // cropHint rectangle lies in the decoded [super]rect.
556 final BitmapFactory.Options scaler;
557 final int actualScale = cropHint.height() / wallpaper.height;
559 while (2*scale < actualScale) {
563 scaler = new BitmapFactory.Options();
564 scaler.inSampleSize = scale;
566 Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
571 Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
574 if (cropped == null) {
575 Slog.e(TAG, "Could not decode new wallpaper");
577 // We've got the extracted crop; now we want to scale it properly to
578 // the desired rectangle. That's a height-biased operation: make it
579 // fit the hinted height, and accept whatever width we end up with.
580 cropHint.offsetTo(0, 0);
581 cropHint.right /= scale; // adjust by downsampling factor
582 cropHint.bottom /= scale;
583 final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
585 Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
587 final int destWidth = (int)(cropHint.width() * heightR);
588 final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
589 destWidth, wallpaper.height, true);
591 Slog.v(TAG, "Final extract:");
592 Slog.v(TAG, " dims: w=" + wallpaper.width
593 + " h=" + wallpaper.height);
594 Slog.v(TAG, " out: w=" + finalCrop.getWidth()
595 + " h=" + finalCrop.getHeight());
598 f = new FileOutputStream(wallpaper.cropFile);
599 bos = new BufferedOutputStream(f, 32*1024);
600 finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
601 bos.flush(); // don't rely on the implicit flush-at-close when noting success
604 } catch (Exception e) {
606 Slog.e(TAG, "Error decoding crop", e);
609 IoUtils.closeQuietly(bos);
610 IoUtils.closeQuietly(f);
616 Slog.e(TAG, "Unable to apply new wallpaper");
617 wallpaper.cropFile.delete();
620 if (wallpaper.cropFile.exists()) {
621 boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
623 Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
628 final Context mContext;
629 final IWindowManager mIWindowManager;
630 final IPackageManager mIPackageManager;
631 final MyPackageMonitor mMonitor;
632 final AppOpsManager mAppOpsManager;
634 * Map of color listeners per user id.
635 * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
637 final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
638 WallpaperData mLastWallpaper;
639 IWallpaperManagerCallback mKeyguardListener;
640 boolean mWaitingForUnlock;
641 boolean mShuttingDown;
644 * ID of the current wallpaper, changed every time anything sets a wallpaper.
645 * This is used for external detection of wallpaper update activity.
650 * Name of the component used to display bitmap wallpapers from either the gallery or
651 * built-in wallpapers.
653 final ComponentName mImageWallpaper;
656 * Name of the default wallpaper component; might be different from mImageWallpaper
658 final ComponentName mDefaultWallpaperComponent;
660 final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
661 final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
663 final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
666 static class WallpaperData {
670 final File wallpaperFile; // source image
671 final File cropFile; // eventual destination
674 * True while the client is writing a new wallpaper
676 boolean imageWallpaperPending;
679 * Which new wallpapers are being written; mirrors the 'which'
680 * selector bit field to setWallpaper().
685 * Callback once the set + crop is finished
687 IWallpaperManagerCallback setComplete;
690 * Is the OS allowed to back up this wallpaper imagery?
695 * Resource name if using a picture from the wallpaper gallery
700 * The component name of the currently set live wallpaper.
702 ComponentName wallpaperComponent;
705 * The component name of the wallpaper that should be set next.
707 ComponentName nextWallpaperComponent;
710 * The ID of this wallpaper
715 * Primary colors histogram
717 WallpaperColors primaryColors;
719 WallpaperConnection connection;
721 boolean wallpaperUpdating;
722 WallpaperObserver wallpaperObserver;
725 * List of callbacks registered they should each be notified when the wallpaper is changed.
727 private RemoteCallbackList<IWallpaperManagerCallback> callbacks
728 = new RemoteCallbackList<IWallpaperManagerCallback>();
734 * The crop hint supplied for displaying a subset of the source image
736 final Rect cropHint = new Rect(0, 0, 0, 0);
738 final Rect padding = new Rect(0, 0, 0, 0);
740 WallpaperData(int userId, String inputFileName, String cropFileName) {
741 this.userId = userId;
742 final File wallpaperDir = getWallpaperDir(userId);
743 wallpaperFile = new File(wallpaperDir, inputFileName);
744 cropFile = new File(wallpaperDir, cropFileName);
747 // Called during initialization of a given user's wallpaper bookkeeping
748 boolean cropExists() {
749 return cropFile.exists();
752 boolean sourceExists() {
753 return wallpaperFile.exists();
757 int makeWallpaperIdLocked() {
760 } while (mWallpaperId == 0);
764 class WallpaperConnection extends IWallpaperConnection.Stub
765 implements ServiceConnection {
767 /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
768 * middle of an update). If exceeded, the wallpaper gets reset to the system default. */
769 private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
771 final WallpaperInfo mInfo;
772 final Binder mToken = new Binder();
773 IWallpaperService mService;
774 IWallpaperEngine mEngine;
775 WallpaperData mWallpaper;
776 IRemoteCallback mReply;
778 boolean mDimensionsChanged = false;
779 boolean mPaddingChanged = false;
781 private Runnable mResetRunnable = () -> {
782 synchronized (mLock) {
784 // Don't expect wallpaper services to relaunch during shutdown
786 Slog.i(TAG, "Ignoring relaunch timeout during shutdown");
791 if (!mWallpaper.wallpaperUpdating
792 && mWallpaper.userId == mCurrentUserId) {
793 Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
794 + ", reverting to built-in wallpaper!");
795 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
801 public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
803 mWallpaper = wallpaper;
807 public void onServiceConnected(ComponentName name, IBinder service) {
808 synchronized (mLock) {
809 if (mWallpaper.connection == this) {
810 mService = IWallpaperService.Stub.asInterface(service);
811 attachServiceLocked(this, mWallpaper);
812 // XXX should probably do saveSettingsLocked() later
813 // when we have an engine, but I'm not sure about
814 // locking there and anyway we always need to be able to
815 // recover if there is something wrong.
816 saveSettingsLocked(mWallpaper.userId);
817 FgThread.getHandler().removeCallbacks(mResetRunnable);
823 public void onServiceDisconnected(ComponentName name) {
824 synchronized (mLock) {
825 Slog.w(TAG, "Wallpaper service gone: " + name);
826 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
827 Slog.e(TAG, "Does not match expected wallpaper component "
828 + mWallpaper.wallpaperComponent);
832 if (mWallpaper.connection == this) {
833 // There is an inherent ordering race between this callback and the
834 // package monitor that receives notice that a package is being updated,
835 // so we cannot quite trust at this moment that we know for sure that
836 // this is not an update. If we think this is a genuine non-update
837 // wallpaper outage, we do our "wait for reset" work as a continuation,
838 // a short time in the future, specifically to allow any pending package
839 // update message on this same looper thread to be processed.
840 if (!mWallpaper.wallpaperUpdating) {
841 mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
848 private void processDisconnect(final ServiceConnection connection) {
849 synchronized (mLock) {
850 // The wallpaper disappeared. If this isn't a system-default one, track
851 // crashes and fall back to default if it continues to misbehave.
852 if (connection == mWallpaper.connection) {
853 final ComponentName wpService = mWallpaper.wallpaperComponent;
854 if (!mWallpaper.wallpaperUpdating
855 && mWallpaper.userId == mCurrentUserId
856 && !Objects.equals(mDefaultWallpaperComponent, wpService)
857 && !Objects.equals(mImageWallpaper, wpService)) {
858 // There is a race condition which causes
859 // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
860 // currently updating since the broadcast notifying us is async.
861 // This race is overcome by the general rule that we only reset the
862 // wallpaper if its service was shut down twice
863 // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
864 if (mWallpaper.lastDiedTime != 0
865 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
866 > SystemClock.uptimeMillis()) {
867 Slog.w(TAG, "Reverting to built-in wallpaper!");
868 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
870 mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
872 // If we didn't reset it right away, do so after we couldn't connect to
873 // it for an extended amount of time to avoid having a black wallpaper.
874 final Handler fgHandler = FgThread.getHandler();
875 fgHandler.removeCallbacks(mResetRunnable);
876 fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
878 Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
881 final String flattened = wpService.flattenToString();
882 EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
883 flattened.substring(0, Math.min(flattened.length(),
884 MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
888 Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
895 * Called by a live wallpaper if its colors have changed.
896 * @param primaryColors representation of wallpaper primary colors
899 public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
901 synchronized (mLock) {
902 // Do not broadcast changes on ImageWallpaper since it's handled
903 // internally by this class.
904 if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) {
908 mWallpaper.primaryColors = primaryColors;
910 // Live wallpapers always are system wallpapers.
912 // It's also the lock screen wallpaper when we don't have a bitmap in there
913 WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
914 if (lockedWallpaper == null) {
919 notifyWallpaperColorsChanged(mWallpaper, which);
924 public void attachEngine(IWallpaperEngine engine) {
925 synchronized (mLock) {
927 if (mDimensionsChanged) {
929 mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
930 } catch (RemoteException e) {
931 Slog.w(TAG, "Failed to set wallpaper dimensions", e);
933 mDimensionsChanged = false;
935 if (mPaddingChanged) {
937 mEngine.setDisplayPadding(mWallpaper.padding);
938 } catch (RemoteException e) {
939 Slog.w(TAG, "Failed to set wallpaper padding", e);
941 mPaddingChanged = false;
944 // This will trigger onComputeColors in the wallpaper engine.
945 // It's fine to be locked in here since the binder is oneway.
946 mEngine.requestWallpaperColors();
947 } catch (RemoteException e) {
948 Slog.w(TAG, "Failed to request wallpaper colors", e);
954 public void engineShown(IWallpaperEngine engine) {
955 synchronized (mLock) {
956 if (mReply != null) {
957 long ident = Binder.clearCallingIdentity();
959 mReply.sendResult(null);
960 } catch (RemoteException e) {
961 Binder.restoreCallingIdentity(ident);
969 public ParcelFileDescriptor setWallpaper(String name) {
970 synchronized (mLock) {
971 if (mWallpaper.connection == this) {
972 return updateWallpaperBitmapLocked(name, mWallpaper, null);
979 class MyPackageMonitor extends PackageMonitor {
981 public void onPackageUpdateFinished(String packageName, int uid) {
982 synchronized (mLock) {
983 if (mCurrentUserId != getChangingUserId()) {
986 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
987 if (wallpaper != null) {
988 final ComponentName wpService = wallpaper.wallpaperComponent;
989 if (wpService != null && wpService.getPackageName().equals(packageName)) {
991 Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
993 wallpaper.wallpaperUpdating = false;
994 clearWallpaperComponentLocked(wallpaper);
995 if (!bindWallpaperComponentLocked(wpService, false, false,
997 Slog.w(TAG, "Wallpaper " + wpService
998 + " no longer available; reverting to default");
999 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1007 public void onPackageModified(String packageName) {
1008 synchronized (mLock) {
1009 if (mCurrentUserId != getChangingUserId()) {
1012 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1013 if (wallpaper != null) {
1014 if (wallpaper.wallpaperComponent == null
1015 || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1018 doPackagesChangedLocked(true, wallpaper);
1024 public void onPackageUpdateStarted(String packageName, int uid) {
1025 synchronized (mLock) {
1026 if (mCurrentUserId != getChangingUserId()) {
1029 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1030 if (wallpaper != null) {
1031 if (wallpaper.wallpaperComponent != null
1032 && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) {
1034 Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent
1037 wallpaper.wallpaperUpdating = true;
1038 if (wallpaper.connection != null) {
1039 FgThread.getHandler().removeCallbacks(
1040 wallpaper.connection.mResetRunnable);
1048 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
1049 synchronized (mLock) {
1050 boolean changed = false;
1051 if (mCurrentUserId != getChangingUserId()) {
1054 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1055 if (wallpaper != null) {
1056 boolean res = doPackagesChangedLocked(doit, wallpaper);
1064 public void onSomePackagesChanged() {
1065 synchronized (mLock) {
1066 if (mCurrentUserId != getChangingUserId()) {
1069 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
1070 if (wallpaper != null) {
1071 doPackagesChangedLocked(true, wallpaper);
1076 boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) {
1077 boolean changed = false;
1078 if (wallpaper.wallpaperComponent != null) {
1079 int change = isPackageDisappearing(wallpaper.wallpaperComponent
1081 if (change == PACKAGE_PERMANENT_CHANGE
1082 || change == PACKAGE_TEMPORARY_CHANGE) {
1085 Slog.w(TAG, "Wallpaper uninstalled, removing: "
1086 + wallpaper.wallpaperComponent);
1087 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1091 if (wallpaper.nextWallpaperComponent != null) {
1092 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent
1094 if (change == PACKAGE_PERMANENT_CHANGE
1095 || change == PACKAGE_TEMPORARY_CHANGE) {
1096 wallpaper.nextWallpaperComponent = null;
1099 if (wallpaper.wallpaperComponent != null
1100 && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
1102 mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
1103 PackageManager.MATCH_DIRECT_BOOT_AWARE
1104 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1105 } catch (NameNotFoundException e) {
1106 Slog.w(TAG, "Wallpaper component gone, removing: "
1107 + wallpaper.wallpaperComponent);
1108 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
1111 if (wallpaper.nextWallpaperComponent != null
1112 && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
1114 mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
1115 PackageManager.MATCH_DIRECT_BOOT_AWARE
1116 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
1117 } catch (NameNotFoundException e) {
1118 wallpaper.nextWallpaperComponent = null;
1125 public WallpaperManagerService(Context context) {
1126 if (DEBUG) Slog.v(TAG, "WallpaperService startup");
1128 mShuttingDown = false;
1129 mImageWallpaper = ComponentName.unflattenFromString(
1130 context.getResources().getString(R.string.image_wallpaper_component));
1131 mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
1132 mIWindowManager = IWindowManager.Stub.asInterface(
1133 ServiceManager.getService(Context.WINDOW_SERVICE));
1134 mIPackageManager = AppGlobals.getPackageManager();
1135 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
1136 mMonitor = new MyPackageMonitor();
1137 mMonitor.register(context, null, UserHandle.ALL, true);
1138 getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
1139 loadSettingsLocked(UserHandle.USER_SYSTEM, false);
1140 mColorsChangedListeners = new SparseArray<>();
1143 private static File getWallpaperDir(int userId) {
1144 return Environment.getUserSystemDirectory(userId);
1148 protected void finalize() throws Throwable {
1150 for (int i = 0; i < mWallpaperMap.size(); i++) {
1151 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
1152 wallpaper.wallpaperObserver.stopWatching();
1156 void systemReady() {
1157 if (DEBUG) Slog.v(TAG, "systemReady");
1158 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
1159 // If we think we're going to be using the system image wallpaper imagery, make
1160 // sure we have something to render
1161 if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
1162 // No crop file? Make sure we've finished the processing sequence if necessary
1163 if (!wallpaper.cropExists()) {
1165 Slog.i(TAG, "No crop; regenerating from source");
1167 generateCrop(wallpaper);
1169 // Still nothing? Fall back to default.
1170 if (!wallpaper.cropExists()) {
1172 Slog.i(TAG, "Unable to regenerate crop; resetting");
1174 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
1178 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
1182 IntentFilter userFilter = new IntentFilter();
1183 userFilter.addAction(Intent.ACTION_USER_REMOVED);
1184 mContext.registerReceiver(new BroadcastReceiver() {
1186 public void onReceive(Context context, Intent intent) {
1187 final String action = intent.getAction();
1188 if (Intent.ACTION_USER_REMOVED.equals(action)) {
1189 onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
1190 UserHandle.USER_NULL));
1195 final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
1196 mContext.registerReceiver(new BroadcastReceiver() {
1198 public void onReceive(Context context, Intent intent) {
1199 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
1201 Slog.i(TAG, "Shutting down");
1203 synchronized (mLock) {
1204 mShuttingDown = true;
1211 ActivityManager.getService().registerUserSwitchObserver(
1212 new UserSwitchObserver() {
1214 public void onUserSwitching(int newUserId, IRemoteCallback reply) {
1215 switchUser(newUserId, reply);
1218 } catch (RemoteException e) {
1219 e.rethrowAsRuntimeException();
1223 /** Called by SystemBackupAgent */
1224 public String getName() {
1225 // Verify caller is the system
1226 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
1227 throw new RuntimeException("getName() can only be called from the system process");
1229 synchronized (mLock) {
1230 return mWallpaperMap.get(0).name;
1234 void stopObserver(WallpaperData wallpaper) {
1235 if (wallpaper != null) {
1236 if (wallpaper.wallpaperObserver != null) {
1237 wallpaper.wallpaperObserver.stopWatching();
1238 wallpaper.wallpaperObserver = null;
1243 void stopObserversLocked(int userId) {
1244 stopObserver(mWallpaperMap.get(userId));
1245 stopObserver(mLockWallpaperMap.get(userId));
1246 mWallpaperMap.remove(userId);
1247 mLockWallpaperMap.remove(userId);
1250 void onUnlockUser(final int userId) {
1251 synchronized (mLock) {
1252 if (mCurrentUserId == userId) {
1253 if (mWaitingForUnlock) {
1254 // If we're switching users, now is when we transition the wallpaper
1255 switchUser(userId, null);
1258 // Make sure that the SELinux labeling of all the relevant files is correct.
1259 // This corrects for mislabeling bugs that might have arisen from move-to
1260 // operations involving the wallpaper files. This isn't timing-critical,
1261 // so we do it in the background to avoid holding up the user unlock operation.
1262 if (mUserRestorecon.get(userId) != Boolean.TRUE) {
1263 mUserRestorecon.put(userId, Boolean.TRUE);
1264 Runnable relabeler = new Runnable() {
1267 final File wallpaperDir = getWallpaperDir(userId);
1268 for (String filename : sPerUserFiles) {
1269 File f = new File(wallpaperDir, filename);
1271 SELinux.restorecon(f);
1276 BackgroundThread.getHandler().post(relabeler);
1282 void onRemoveUser(int userId) {
1283 if (userId < 1) return;
1285 final File wallpaperDir = getWallpaperDir(userId);
1286 synchronized (mLock) {
1287 stopObserversLocked(userId);
1288 for (String filename : sPerUserFiles) {
1289 new File(wallpaperDir, filename).delete();
1291 mUserRestorecon.remove(userId);
1295 void switchUser(int userId, IRemoteCallback reply) {
1296 WallpaperData systemWallpaper;
1297 WallpaperData lockWallpaper;
1298 synchronized (mLock) {
1299 mCurrentUserId = userId;
1300 systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1301 lockWallpaper = mLockWallpaperMap.get(userId);
1302 if (lockWallpaper == null) {
1303 lockWallpaper = systemWallpaper;
1305 // Not started watching yet, in case wallpaper data was loaded for other reasons.
1306 if (systemWallpaper.wallpaperObserver == null) {
1307 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
1308 systemWallpaper.wallpaperObserver.startWatching();
1310 switchWallpaper(systemWallpaper, reply);
1312 notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
1313 notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
1316 void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
1317 synchronized (mLock) {
1318 mWaitingForUnlock = false;
1319 final ComponentName cname = wallpaper.wallpaperComponent != null ?
1320 wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
1321 if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
1322 // We failed to bind the desired wallpaper, but that might
1323 // happen if the wallpaper isn't direct-boot aware
1324 ServiceInfo si = null;
1326 si = mIPackageManager.getServiceInfo(cname,
1327 PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
1328 } catch (RemoteException ignored) {
1332 Slog.w(TAG, "Failure starting previous wallpaper; clearing");
1333 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
1335 Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
1336 // We might end up persisting the current wallpaper data
1337 // while locked, so pretend like the component was actually
1339 wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
1340 final WallpaperData fallback = new WallpaperData(wallpaper.userId,
1341 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1342 ensureSaneWallpaperData(fallback);
1343 bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
1344 mWaitingForUnlock = true;
1351 public void clearWallpaper(String callingPackage, int which, int userId) {
1352 if (DEBUG) Slog.v(TAG, "clearWallpaper");
1353 checkPermission(android.Manifest.permission.SET_WALLPAPER);
1354 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1357 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1358 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null);
1360 WallpaperData data = null;
1361 synchronized (mLock) {
1362 clearWallpaperLocked(false, which, userId, null);
1364 if (which == FLAG_LOCK) {
1365 data = mLockWallpaperMap.get(userId);
1367 if (which == FLAG_SYSTEM || data == null) {
1368 data = mWallpaperMap.get(userId);
1372 // When clearing a wallpaper, broadcast new valid colors
1374 notifyWallpaperColorsChanged(data, which);
1378 void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
1379 if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1380 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
1383 WallpaperData wallpaper = null;
1384 if (which == FLAG_LOCK) {
1385 wallpaper = mLockWallpaperMap.get(userId);
1386 if (wallpaper == null) {
1387 // It's already gone; we're done.
1389 Slog.i(TAG, "Lock wallpaper already cleared");
1394 wallpaper = mWallpaperMap.get(userId);
1395 if (wallpaper == null) {
1396 // Might need to bring it in the first time to establish our rewrite
1397 loadSettingsLocked(userId, false);
1398 wallpaper = mWallpaperMap.get(userId);
1401 if (wallpaper == null) {
1405 final long ident = Binder.clearCallingIdentity();
1407 if (wallpaper.wallpaperFile.exists()) {
1408 wallpaper.wallpaperFile.delete();
1409 wallpaper.cropFile.delete();
1410 if (which == FLAG_LOCK) {
1411 mLockWallpaperMap.remove(userId);
1412 final IWallpaperManagerCallback cb = mKeyguardListener;
1415 Slog.i(TAG, "Notifying keyguard of lock wallpaper clear");
1418 cb.onWallpaperChanged();
1419 } catch (RemoteException e) {
1420 // Oh well it went away; no big deal
1423 saveSettingsLocked(userId);
1428 RuntimeException e = null;
1430 wallpaper.primaryColors = null;
1431 wallpaper.imageWallpaperPending = false;
1432 if (userId != mCurrentUserId) return;
1433 if (bindWallpaperComponentLocked(defaultFailed
1435 : null, true, false, wallpaper, reply)) {
1438 } catch (IllegalArgumentException e1) {
1442 // This can happen if the default wallpaper component doesn't
1443 // exist. This should be a system configuration problem, but
1444 // let's not let it crash the system and just live with no
1446 Slog.e(TAG, "Default wallpaper component not found!", e);
1447 clearWallpaperComponentLocked(wallpaper);
1448 if (reply != null) {
1450 reply.sendResult(null);
1451 } catch (RemoteException e1) {
1455 Binder.restoreCallingIdentity(ident);
1459 public boolean hasNamedWallpaper(String name) {
1460 synchronized (mLock) {
1461 List<UserInfo> users;
1462 long ident = Binder.clearCallingIdentity();
1464 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers();
1466 Binder.restoreCallingIdentity(ident);
1468 for (UserInfo user: users) {
1469 // ignore managed profiles
1470 if (user.isManagedProfile()) {
1473 WallpaperData wd = mWallpaperMap.get(user.id);
1475 // User hasn't started yet, so load her settings to peek at the wallpaper
1476 loadSettingsLocked(user.id, false);
1477 wd = mWallpaperMap.get(user.id);
1479 if (wd != null && name.equals(wd.name)) {
1487 private Point getDefaultDisplaySize() {
1488 Point p = new Point();
1489 WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
1490 Display d = wm.getDefaultDisplay();
1495 public void setDimensionHints(int width, int height, String callingPackage)
1496 throws RemoteException {
1497 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1498 if (!isWallpaperSupported(callingPackage)) {
1501 synchronized (mLock) {
1502 int userId = UserHandle.getCallingUserId();
1503 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1504 if (width <= 0 || height <= 0) {
1505 throw new IllegalArgumentException("width and height must be > 0");
1507 // Make sure it is at least as large as the display.
1508 Point displaySize = getDefaultDisplaySize();
1509 width = Math.max(width, displaySize.x);
1510 height = Math.max(height, displaySize.y);
1512 if (width != wallpaper.width || height != wallpaper.height) {
1513 wallpaper.width = width;
1514 wallpaper.height = height;
1515 saveSettingsLocked(userId);
1516 if (mCurrentUserId != userId) return; // Don't change the properties now
1517 if (wallpaper.connection != null) {
1518 if (wallpaper.connection.mEngine != null) {
1520 wallpaper.connection.mEngine.setDesiredSize(
1522 } catch (RemoteException e) {
1524 notifyCallbacksLocked(wallpaper);
1525 } else if (wallpaper.connection.mService != null) {
1526 // We've attached to the service but the engine hasn't attached back to us
1527 // yet. This means it will be created with the previous dimensions, so we
1528 // need to update it to the new dimensions once it attaches.
1529 wallpaper.connection.mDimensionsChanged = true;
1536 public int getWidthHint() throws RemoteException {
1537 synchronized (mLock) {
1538 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1539 if (wallpaper != null) {
1540 return wallpaper.width;
1547 public int getHeightHint() throws RemoteException {
1548 synchronized (mLock) {
1549 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
1550 if (wallpaper != null) {
1551 return wallpaper.height;
1558 public void setDisplayPadding(Rect padding, String callingPackage) {
1559 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
1560 if (!isWallpaperSupported(callingPackage)) {
1563 synchronized (mLock) {
1564 int userId = UserHandle.getCallingUserId();
1565 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
1566 if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
1567 throw new IllegalArgumentException("padding must be positive: " + padding);
1570 if (!padding.equals(wallpaper.padding)) {
1571 wallpaper.padding.set(padding);
1572 saveSettingsLocked(userId);
1573 if (mCurrentUserId != userId) return; // Don't change the properties now
1574 if (wallpaper.connection != null) {
1575 if (wallpaper.connection.mEngine != null) {
1577 wallpaper.connection.mEngine.setDisplayPadding(padding);
1578 } catch (RemoteException e) {
1580 notifyCallbacksLocked(wallpaper);
1581 } else if (wallpaper.connection.mService != null) {
1582 // We've attached to the service but the engine hasn't attached back to us
1583 // yet. This means it will be created with the previous dimensions, so we
1584 // need to update it to the new dimensions once it attaches.
1585 wallpaper.connection.mPaddingChanged = true;
1592 private void enforceCallingOrSelfPermissionAndAppOp(String permission, final String callingPkg,
1593 final int callingUid, String message) {
1594 mContext.enforceCallingOrSelfPermission(permission, message);
1596 final String opName = AppOpsManager.permissionToOp(permission);
1597 if (opName != null) {
1598 final int appOpMode = mAppOpsManager.noteOp(opName, callingUid, callingPkg);
1599 if (appOpMode != AppOpsManager.MODE_ALLOWED) {
1600 throw new SecurityException(
1601 message + ": " + callingPkg + " is not allowed to " + permission);
1607 public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
1608 final int which, Bundle outParams, int wallpaperUserId) {
1609 final int hasPrivilege = mContext.checkCallingOrSelfPermission(
1610 android.Manifest.permission.READ_WALLPAPER_INTERNAL);
1611 if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
1612 enforceCallingOrSelfPermissionAndAppOp(android.Manifest.permission.READ_EXTERNAL_STORAGE,
1613 callingPkg, Binder.getCallingUid(), "read wallpaper");
1616 wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1617 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
1619 if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1620 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
1623 synchronized (mLock) {
1624 final SparseArray<WallpaperData> whichSet =
1625 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1626 WallpaperData wallpaper = whichSet.get(wallpaperUserId);
1627 if (wallpaper == null) {
1628 // common case, this is the first lookup post-boot of the system or
1629 // unified lock, so we bring up the saved state lazily now and recheck.
1630 loadSettingsLocked(wallpaperUserId, false);
1631 wallpaper = whichSet.get(wallpaperUserId);
1632 if (wallpaper == null) {
1637 if (outParams != null) {
1638 outParams.putInt("width", wallpaper.width);
1639 outParams.putInt("height", wallpaper.height);
1642 wallpaper.callbacks.register(cb);
1644 if (!wallpaper.cropFile.exists()) {
1647 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
1648 } catch (FileNotFoundException e) {
1649 /* Shouldn't happen as we check to see if the file exists */
1650 Slog.w(TAG, "Error getting wallpaper", e);
1657 public WallpaperInfo getWallpaperInfo(int userId) {
1658 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1659 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
1660 synchronized (mLock) {
1661 WallpaperData wallpaper = mWallpaperMap.get(userId);
1662 if (wallpaper != null && wallpaper.connection != null) {
1663 return wallpaper.connection.mInfo;
1670 public int getWallpaperIdForUser(int which, int userId) {
1671 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1672 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
1674 if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
1675 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
1678 final SparseArray<WallpaperData> map =
1679 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
1680 synchronized (mLock) {
1681 WallpaperData wallpaper = map.get(userId);
1682 if (wallpaper != null) {
1683 return wallpaper.wallpaperId;
1690 public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
1691 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1692 userId, true, true, "registerWallpaperColorsCallback", null);
1693 synchronized (mLock) {
1694 RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
1695 mColorsChangedListeners.get(userId);
1696 if (userColorsChangedListeners == null) {
1697 userColorsChangedListeners = new RemoteCallbackList<>();
1698 mColorsChangedListeners.put(userId, userColorsChangedListeners);
1700 userColorsChangedListeners.register(cb);
1705 public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
1706 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1707 userId, true, true, "unregisterWallpaperColorsCallback", null);
1708 synchronized (mLock) {
1709 final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
1710 mColorsChangedListeners.get(userId);
1711 if (userColorsChangedListeners != null) {
1712 userColorsChangedListeners.unregister(cb);
1718 public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
1719 checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
1720 synchronized (mLock) {
1721 mKeyguardListener = cb;
1727 public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
1728 if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
1729 throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
1731 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
1732 userId, false, true, "getWallpaperColors", null);
1734 WallpaperData wallpaperData = null;
1735 boolean shouldExtract;
1737 synchronized (mLock) {
1738 if (which == FLAG_LOCK) {
1739 wallpaperData = mLockWallpaperMap.get(userId);
1742 // Try to get the system wallpaper anyway since it might
1743 // also be the lock screen wallpaper
1744 if (wallpaperData == null) {
1745 wallpaperData = mWallpaperMap.get(userId);
1748 if (wallpaperData == null) {
1751 shouldExtract = wallpaperData.primaryColors == null;
1754 if (shouldExtract) {
1755 extractColors(wallpaperData);
1758 synchronized (mLock) {
1759 return wallpaperData.primaryColors;
1764 public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
1765 Rect cropHint, boolean allowBackup, Bundle extras, int which,
1766 IWallpaperManagerCallback completion, int userId) {
1767 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
1768 false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
1769 checkPermission(android.Manifest.permission.SET_WALLPAPER);
1771 if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
1772 final String msg = "Must specify a valid wallpaper category to set";
1774 throw new IllegalArgumentException(msg);
1777 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
1781 // "null" means the no-op crop, preserving the full input image
1782 if (cropHint == null) {
1783 cropHint = new Rect(0, 0, 0, 0);
1785 if (cropHint.isEmpty()
1786 || cropHint.left < 0
1787 || cropHint.top < 0) {
1788 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
1792 synchronized (mLock) {
1793 if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
1794 WallpaperData wallpaper;
1796 /* If we're setting system but not lock, and lock is currently sharing the system
1797 * wallpaper, we need to migrate that image over to being lock-only before
1798 * the caller here writes new bitmap data.
1800 if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
1802 Slog.i(TAG, "Migrating system->lock to preserve");
1804 migrateSystemToLockWallpaperLocked(userId);
1807 wallpaper = getWallpaperSafeLocked(userId, which);
1808 final long ident = Binder.clearCallingIdentity();
1810 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
1812 wallpaper.imageWallpaperPending = true;
1813 wallpaper.whichPending = which;
1814 wallpaper.setComplete = completion;
1815 wallpaper.cropHint.set(cropHint);
1816 wallpaper.allowBackup = allowBackup;
1820 Binder.restoreCallingIdentity(ident);
1825 private void migrateSystemToLockWallpaperLocked(int userId) {
1826 WallpaperData sysWP = mWallpaperMap.get(userId);
1827 if (sysWP == null) {
1829 Slog.i(TAG, "No system wallpaper? Not tracking for lock-only");
1834 // We know a-priori that there is no lock-only wallpaper currently
1835 WallpaperData lockWP = new WallpaperData(userId,
1836 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
1837 lockWP.wallpaperId = sysWP.wallpaperId;
1838 lockWP.cropHint.set(sysWP.cropHint);
1839 lockWP.width = sysWP.width;
1840 lockWP.height = sysWP.height;
1841 lockWP.allowBackup = sysWP.allowBackup;
1842 lockWP.primaryColors = sysWP.primaryColors;
1844 // Migrate the bitmap files outright; no need to copy
1846 Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
1847 Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
1848 } catch (ErrnoException e) {
1849 Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
1850 lockWP.wallpaperFile.delete();
1851 lockWP.cropFile.delete();
1855 mLockWallpaperMap.put(userId, lockWP);
1858 ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
1860 if (name == null) name = "";
1862 File dir = getWallpaperDir(wallpaper.userId);
1863 if (!dir.exists()) {
1865 FileUtils.setPermissions(
1867 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
1870 ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
1871 MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
1872 if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
1875 wallpaper.name = name;
1876 wallpaper.wallpaperId = makeWallpaperIdLocked();
1877 if (extras != null) {
1878 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
1880 // Nullify field to require new computation
1881 wallpaper.primaryColors = null;
1883 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
1884 + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
1887 } catch (FileNotFoundException e) {
1888 Slog.w(TAG, "Error setting wallpaper", e);
1894 public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
1897 if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
1898 setWallpaperComponent(name, userId);
1902 // ToDo: Remove this version of the function
1904 public void setWallpaperComponent(ComponentName name) {
1905 setWallpaperComponent(name, UserHandle.getCallingUserId());
1908 private void setWallpaperComponent(ComponentName name, int userId) {
1909 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
1910 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
1911 checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
1913 int which = FLAG_SYSTEM;
1914 boolean shouldNotifyColors = false;
1915 WallpaperData wallpaper;
1917 synchronized (mLock) {
1918 if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
1919 wallpaper = mWallpaperMap.get(userId);
1920 if (wallpaper == null) {
1921 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
1923 final long ident = Binder.clearCallingIdentity();
1925 // Live wallpapers can't be specified for keyguard. If we're using a static
1926 // system+lock image currently, migrate the system wallpaper to be a lock-only
1927 // image as part of making a different live component active as the system
1929 if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
1930 if (mLockWallpaperMap.get(userId) == null) {
1931 // We're using the static imagery and there is no lock-specific image in place,
1932 // therefore it's a shared system+lock image that we need to migrate.
1933 migrateSystemToLockWallpaperLocked(userId);
1937 // New live wallpaper is also a lock wallpaper if nothing is set
1938 if (mLockWallpaperMap.get(userId) == null) {
1943 wallpaper.imageWallpaperPending = false;
1944 boolean same = changingToSame(name, wallpaper);
1945 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
1947 wallpaper.primaryColors = null;
1949 wallpaper.wallpaperId = makeWallpaperIdLocked();
1950 notifyCallbacksLocked(wallpaper);
1951 shouldNotifyColors = true;
1954 Binder.restoreCallingIdentity(ident);
1958 if (shouldNotifyColors) {
1959 notifyWallpaperColorsChanged(wallpaper, which);
1963 private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
1964 if (wallpaper.connection != null) {
1965 if (wallpaper.wallpaperComponent == null) {
1966 if (componentName == null) {
1967 if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
1968 // Still using default wallpaper.
1971 } else if (wallpaper.wallpaperComponent.equals(componentName)) {
1972 // Changing to same wallpaper.
1973 if (DEBUG) Slog.v(TAG, "same wallpaper");
1980 boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
1981 boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
1983 Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
1985 // Has the component changed?
1986 if (!force && changingToSame(componentName, wallpaper)) {
1991 if (componentName == null) {
1992 componentName = mDefaultWallpaperComponent;
1993 if (componentName == null) {
1994 // Fall back to static image wallpaper
1995 componentName = mImageWallpaper;
1996 //clearWallpaperComponentLocked();
1998 if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
2001 int serviceUserId = wallpaper.userId;
2002 ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
2003 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
2005 // The wallpaper component we're trying to use doesn't exist
2006 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
2009 if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
2010 String msg = "Selected service does not require "
2011 + android.Manifest.permission.BIND_WALLPAPER
2012 + ": " + componentName;
2014 throw new SecurityException(msg);
2020 WallpaperInfo wi = null;
2022 Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
2023 if (componentName != null && !componentName.equals(mImageWallpaper)) {
2024 // Make sure the selected service is actually a wallpaper service.
2025 List<ResolveInfo> ris =
2026 mIPackageManager.queryIntentServices(intent,
2027 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2028 PackageManager.GET_META_DATA, serviceUserId).getList();
2029 for (int i=0; i<ris.size(); i++) {
2030 ServiceInfo rsi = ris.get(i).serviceInfo;
2031 if (rsi.name.equals(si.name) &&
2032 rsi.packageName.equals(si.packageName)) {
2034 wi = new WallpaperInfo(mContext, ris.get(i));
2035 } catch (XmlPullParserException e) {
2037 throw new IllegalArgumentException(e);
2041 } catch (IOException e) {
2043 throw new IllegalArgumentException(e);
2052 String msg = "Selected service is not a wallpaper: "
2055 throw new SecurityException(msg);
2062 // Bind the service!
2063 if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
2064 WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
2065 intent.setComponent(componentName);
2066 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
2067 com.android.internal.R.string.wallpaper_binding_label);
2068 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
2070 Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
2071 mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
2072 0, null, new UserHandle(serviceUserId)));
2073 if (!mContext.bindServiceAsUser(intent, newConn,
2074 Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
2075 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
2076 new UserHandle(serviceUserId))) {
2077 String msg = "Unable to bind service: "
2080 throw new IllegalArgumentException(msg);
2085 if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
2086 detachWallpaperLocked(mLastWallpaper);
2088 wallpaper.wallpaperComponent = componentName;
2089 wallpaper.connection = newConn;
2090 newConn.mReply = reply;
2092 if (wallpaper.userId == mCurrentUserId) {
2094 Slog.v(TAG, "Adding window token: " + newConn.mToken);
2095 mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
2096 mLastWallpaper = wallpaper;
2098 } catch (RemoteException e) {
2100 } catch (RemoteException e) {
2101 String msg = "Remote exception for " + componentName + "\n" + e;
2103 throw new IllegalArgumentException(msg);
2111 void detachWallpaperLocked(WallpaperData wallpaper) {
2112 if (wallpaper.connection != null) {
2113 if (wallpaper.connection.mReply != null) {
2115 wallpaper.connection.mReply.sendResult(null);
2116 } catch (RemoteException e) {
2118 wallpaper.connection.mReply = null;
2120 if (wallpaper.connection.mEngine != null) {
2122 wallpaper.connection.mEngine.destroy();
2123 } catch (RemoteException e) {
2126 mContext.unbindService(wallpaper.connection);
2129 Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
2130 mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
2131 } catch (RemoteException e) {
2133 wallpaper.connection.mService = null;
2134 wallpaper.connection.mEngine = null;
2135 wallpaper.connection = null;
2139 void clearWallpaperComponentLocked(WallpaperData wallpaper) {
2140 wallpaper.wallpaperComponent = null;
2141 detachWallpaperLocked(wallpaper);
2144 void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
2146 conn.mService.attach(conn, conn.mToken,
2147 TYPE_WALLPAPER, false,
2148 wallpaper.width, wallpaper.height, wallpaper.padding);
2149 } catch (RemoteException e) {
2150 Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
2151 if (!wallpaper.wallpaperUpdating) {
2152 bindWallpaperComponentLocked(null, false, false, wallpaper, null);
2157 private void notifyCallbacksLocked(WallpaperData wallpaper) {
2158 final int n = wallpaper.callbacks.beginBroadcast();
2159 for (int i = 0; i < n; i++) {
2161 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged();
2162 } catch (RemoteException e) {
2164 // The RemoteCallbackList will take care of removing
2165 // the dead object for us.
2168 wallpaper.callbacks.finishBroadcast();
2170 final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
2171 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
2174 private void checkPermission(String permission) {
2175 if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
2176 throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
2177 + ", must have permission " + permission);
2182 * Certain user types do not support wallpapers (e.g. managed profiles). The check is
2183 * implemented through through the OP_WRITE_WALLPAPER AppOp.
2185 public boolean isWallpaperSupported(String callingPackage) {
2186 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
2187 callingPackage) == AppOpsManager.MODE_ALLOWED;
2191 public boolean isSetWallpaperAllowed(String callingPackage) {
2192 final PackageManager pm = mContext.getPackageManager();
2193 String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid());
2194 boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage);
2195 if (!uidMatchPackage) {
2196 return false; // callingPackage was faked.
2199 final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
2200 if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
2203 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
2204 return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER);
2208 public boolean isWallpaperBackupEligible(int which, int userId) {
2209 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2210 throw new SecurityException("Only the system may call isWallpaperBackupEligible");
2213 WallpaperData wallpaper = (which == FLAG_LOCK)
2214 ? mLockWallpaperMap.get(userId)
2215 : mWallpaperMap.get(userId);
2216 return (wallpaper != null) ? wallpaper.allowBackup : false;
2219 private static JournaledFile makeJournaledFile(int userId) {
2220 final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
2221 return new JournaledFile(new File(base), new File(base + ".tmp"));
2224 private void saveSettingsLocked(int userId) {
2225 JournaledFile journal = makeJournaledFile(userId);
2226 FileOutputStream fstream = null;
2227 BufferedOutputStream stream = null;
2229 XmlSerializer out = new FastXmlSerializer();
2230 fstream = new FileOutputStream(journal.chooseForWrite(), false);
2231 stream = new BufferedOutputStream(fstream);
2232 out.setOutput(stream, StandardCharsets.UTF_8.name());
2233 out.startDocument(null, true);
2235 WallpaperData wallpaper;
2237 wallpaper = mWallpaperMap.get(userId);
2238 if (wallpaper != null) {
2239 writeWallpaperAttributes(out, "wp", wallpaper);
2241 wallpaper = mLockWallpaperMap.get(userId);
2242 if (wallpaper != null) {
2243 writeWallpaperAttributes(out, "kwp", wallpaper);
2248 stream.flush(); // also flushes fstream
2249 FileUtils.sync(fstream);
2250 stream.close(); // also closes fstream
2252 } catch (IOException e) {
2253 IoUtils.closeQuietly(stream);
2258 private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
2259 throws IllegalArgumentException, IllegalStateException, IOException {
2261 Slog.v(TAG, "writeWallpaperAttributes");
2263 out.startTag(null, tag);
2264 out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
2265 out.attribute(null, "width", Integer.toString(wallpaper.width));
2266 out.attribute(null, "height", Integer.toString(wallpaper.height));
2268 out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
2269 out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
2270 out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
2271 out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
2273 if (wallpaper.padding.left != 0) {
2274 out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
2276 if (wallpaper.padding.top != 0) {
2277 out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
2279 if (wallpaper.padding.right != 0) {
2280 out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
2282 if (wallpaper.padding.bottom != 0) {
2283 out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
2286 if (wallpaper.primaryColors != null) {
2287 int colorsCount = wallpaper.primaryColors.getMainColors().size();
2288 out.attribute(null, "colorsCount", Integer.toString(colorsCount));
2289 if (colorsCount > 0) {
2290 for (int i = 0; i < colorsCount; i++) {
2291 final Color wc = wallpaper.primaryColors.getMainColors().get(i);
2292 out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
2295 out.attribute(null, "colorHints",
2296 Integer.toString(wallpaper.primaryColors.getColorHints()));
2299 out.attribute(null, "name", wallpaper.name);
2300 if (wallpaper.wallpaperComponent != null
2301 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
2302 out.attribute(null, "component",
2303 wallpaper.wallpaperComponent.flattenToShortString());
2306 if (wallpaper.allowBackup) {
2307 out.attribute(null, "backup", "true");
2310 out.endTag(null, tag);
2313 private void migrateFromOld() {
2314 // Pre-N, what existed is the one we're now using as the display crop
2315 File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
2316 // In the very-long-ago, imagery lived with the settings app
2317 File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
2318 File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
2320 // Migrations from earlier wallpaper image storage schemas
2321 if (preNWallpaper.exists()) {
2322 if (!newWallpaper.exists()) {
2323 // we've got the 'wallpaper' crop file but not the nominal source image,
2324 // so do the simple "just take everything" straight copy of legacy data
2326 Slog.i(TAG, "Migrating wallpaper schema");
2328 FileUtils.copyFile(preNWallpaper, newWallpaper);
2329 } // else we're in the usual modern case: both source & crop exist
2330 } else if (originalWallpaper.exists()) {
2331 // VERY old schema; make sure things exist and are in the right place
2333 Slog.i(TAG, "Migrating antique wallpaper schema");
2335 File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
2336 if (oldInfo.exists()) {
2337 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
2338 oldInfo.renameTo(newInfo);
2341 FileUtils.copyFile(originalWallpaper, preNWallpaper);
2342 originalWallpaper.renameTo(newWallpaper);
2346 private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
2347 String value = parser.getAttributeValue(null, name);
2348 if (value == null) {
2351 return Integer.parseInt(value);
2355 * Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could
2356 * happen during user switch. The async user switch observer may not have received
2357 * the event yet. We use this safe method when we don't care about this ordering and just
2358 * want to update the data. The data is going to be applied when the user switch observer
2359 * is eventually executed.
2361 private WallpaperData getWallpaperSafeLocked(int userId, int which) {
2362 // We're setting either just system (work with the system wallpaper),
2363 // both (also work with the system wallpaper), or just the lock
2364 // wallpaper (update against the existing lock wallpaper if any).
2365 // Combined or just-system operations use the 'system' WallpaperData
2366 // for this use; lock-only operations use the dedicated one.
2367 final SparseArray<WallpaperData> whichSet =
2368 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
2369 WallpaperData wallpaper = whichSet.get(userId);
2370 if (wallpaper == null) {
2371 // common case, this is the first lookup post-boot of the system or
2372 // unified lock, so we bring up the saved state lazily now and recheck.
2373 loadSettingsLocked(userId, false);
2374 wallpaper = whichSet.get(userId);
2375 // if it's still null here, this is a lock-only operation and there is not
2376 // yet a lock-only wallpaper set for this user, so we need to establish
2378 if (wallpaper == null) {
2379 if (which == FLAG_LOCK) {
2380 wallpaper = new WallpaperData(userId,
2381 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2382 mLockWallpaperMap.put(userId, wallpaper);
2383 ensureSaneWallpaperData(wallpaper);
2385 // sanity fallback: we're in bad shape, but establishing a known
2386 // valid system+lock WallpaperData will keep us from dying.
2387 Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
2388 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2389 mWallpaperMap.put(userId, wallpaper);
2390 ensureSaneWallpaperData(wallpaper);
2397 private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
2398 if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
2400 JournaledFile journal = makeJournaledFile(userId);
2401 FileInputStream stream = null;
2402 File file = journal.chooseForRead();
2404 WallpaperData wallpaper = mWallpaperMap.get(userId);
2405 if (wallpaper == null) {
2406 // Do this once per boot
2409 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
2410 wallpaper.allowBackup = true;
2411 mWallpaperMap.put(userId, wallpaper);
2412 if (!wallpaper.cropExists()) {
2413 if (wallpaper.sourceExists()) {
2414 generateCrop(wallpaper);
2416 Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
2420 boolean success = false;
2422 stream = new FileInputStream(file);
2423 XmlPullParser parser = Xml.newPullParser();
2424 parser.setInput(stream, StandardCharsets.UTF_8.name());
2428 type = parser.next();
2429 if (type == XmlPullParser.START_TAG) {
2430 String tag = parser.getName();
2431 if ("wp".equals(tag)) {
2432 // Common to system + lock wallpapers
2433 parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
2435 // A system wallpaper might also be a live wallpaper
2436 String comp = parser.getAttributeValue(null, "component");
2437 wallpaper.nextWallpaperComponent = comp != null
2438 ? ComponentName.unflattenFromString(comp)
2440 if (wallpaper.nextWallpaperComponent == null
2441 || "android".equals(wallpaper.nextWallpaperComponent
2442 .getPackageName())) {
2443 wallpaper.nextWallpaperComponent = mImageWallpaper;
2447 Slog.v(TAG, "mWidth:" + wallpaper.width);
2448 Slog.v(TAG, "mHeight:" + wallpaper.height);
2449 Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
2450 Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
2451 Slog.v(TAG, "mName:" + wallpaper.name);
2452 Slog.v(TAG, "mNextWallpaperComponent:"
2453 + wallpaper.nextWallpaperComponent);
2455 } else if ("kwp".equals(tag)) {
2456 // keyguard-specific wallpaper for this user
2457 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
2458 if (lockWallpaper == null) {
2459 lockWallpaper = new WallpaperData(userId,
2460 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
2461 mLockWallpaperMap.put(userId, lockWallpaper);
2463 parseWallpaperAttributes(parser, lockWallpaper, false);
2466 } while (type != XmlPullParser.END_DOCUMENT);
2468 } catch (FileNotFoundException e) {
2469 Slog.w(TAG, "no current wallpaper -- first boot?");
2470 } catch (NullPointerException e) {
2471 Slog.w(TAG, "failed parsing " + file + " " + e);
2472 } catch (NumberFormatException e) {
2473 Slog.w(TAG, "failed parsing " + file + " " + e);
2474 } catch (XmlPullParserException e) {
2475 Slog.w(TAG, "failed parsing " + file + " " + e);
2476 } catch (IOException e) {
2477 Slog.w(TAG, "failed parsing " + file + " " + e);
2478 } catch (IndexOutOfBoundsException e) {
2479 Slog.w(TAG, "failed parsing " + file + " " + e);
2481 IoUtils.closeQuietly(stream);
2484 wallpaper.width = -1;
2485 wallpaper.height = -1;
2486 wallpaper.cropHint.set(0, 0, 0, 0);
2487 wallpaper.padding.set(0, 0, 0, 0);
2488 wallpaper.name = "";
2490 mLockWallpaperMap.remove(userId);
2492 if (wallpaper.wallpaperId <= 0) {
2493 wallpaper.wallpaperId = makeWallpaperIdLocked();
2495 Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
2496 + "); now " + wallpaper.wallpaperId);
2501 ensureSaneWallpaperData(wallpaper);
2502 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
2503 if (lockWallpaper != null) {
2504 ensureSaneWallpaperData(lockWallpaper);
2508 private void ensureSaneWallpaperData(WallpaperData wallpaper) {
2509 // We always want to have some reasonable width hint.
2510 int baseSize = getMaximumSizeDimension();
2511 if (wallpaper.width < baseSize) {
2512 wallpaper.width = baseSize;
2514 if (wallpaper.height < baseSize) {
2515 wallpaper.height = baseSize;
2517 // and crop, if not previously specified
2518 if (wallpaper.cropHint.width() <= 0
2519 || wallpaper.cropHint.height() <= 0) {
2520 wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
2524 private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
2525 boolean keepDimensionHints) {
2526 final String idString = parser.getAttributeValue(null, "id");
2527 if (idString != null) {
2528 final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
2529 if (id > mWallpaperId) {
2533 wallpaper.wallpaperId = makeWallpaperIdLocked();
2536 if (!keepDimensionHints) {
2537 wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
2538 wallpaper.height = Integer.parseInt(parser
2539 .getAttributeValue(null, "height"));
2541 wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
2542 wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
2543 wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
2544 wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
2545 wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
2546 wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
2547 wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
2548 wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
2549 int colorsCount = getAttributeInt(parser, "colorsCount", 0);
2550 if (colorsCount > 0) {
2551 Color primary = null, secondary = null, tertiary = null;
2552 for (int i = 0; i < colorsCount; i++) {
2553 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
2556 } else if (i == 1) {
2558 } else if (i == 2) {
2564 int colorHints = getAttributeInt(parser, "colorHints", 0);
2565 wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
2567 wallpaper.name = parser.getAttributeValue(null, "name");
2568 wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
2571 private int getMaximumSizeDimension() {
2572 WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
2573 Display d = wm.getDefaultDisplay();
2574 return d.getMaximumSizeDimension();
2577 // Called by SystemBackupAgent after files are restored to disk.
2578 public void settingsRestored() {
2579 // Verify caller is the system
2580 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
2581 throw new RuntimeException("settingsRestored() can only be called from the system process");
2583 // TODO: If necessary, make it work for secondary users as well. This currently assumes
2584 // restores only to the primary user
2585 if (DEBUG) Slog.v(TAG, "settingsRestored");
2586 WallpaperData wallpaper = null;
2587 boolean success = false;
2588 synchronized (mLock) {
2589 loadSettingsLocked(UserHandle.USER_SYSTEM, false);
2590 wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
2591 wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore
2592 wallpaper.allowBackup = true; // by definition if it was restored
2593 if (wallpaper.nextWallpaperComponent != null
2594 && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
2595 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
2597 // No such live wallpaper or other failure; fall back to the default
2598 // live wallpaper (since the profile being restored indicated that the
2599 // user had selected a live rather than static one).
2600 bindWallpaperComponentLocked(null, false, false, wallpaper, null);
2604 // If there's a wallpaper name, we use that. If that can't be loaded, then we
2606 if ("".equals(wallpaper.name)) {
2607 if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
2610 if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
2611 success = restoreNamedResourceLocked(wallpaper);
2613 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
2614 + " id=" + wallpaper.wallpaperId);
2616 generateCrop(wallpaper); // based on the new image + metadata
2617 bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
2624 Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
2625 wallpaper.name = "";
2626 getWallpaperDir(UserHandle.USER_SYSTEM).delete();
2629 synchronized (mLock) {
2630 saveSettingsLocked(UserHandle.USER_SYSTEM);
2634 // Restore the named resource bitmap to both source + crop files
2635 boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
2636 if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
2637 String resName = wallpaper.name.substring(4);
2640 int colon = resName.indexOf(':');
2642 pkg = resName.substring(0, colon);
2645 String ident = null;
2646 int slash = resName.lastIndexOf('/');
2648 ident = resName.substring(slash+1);
2652 if (colon > 0 && slash > 0 && (slash-colon) > 1) {
2653 type = resName.substring(colon+1, slash);
2656 if (pkg != null && ident != null && type != null) {
2658 InputStream res = null;
2659 FileOutputStream fos = null;
2660 FileOutputStream cos = null;
2662 Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
2663 Resources r = c.getResources();
2664 resId = r.getIdentifier(resName, null, null);
2666 Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
2667 + " ident=" + ident);
2671 res = r.openRawResource(resId);
2672 if (wallpaper.wallpaperFile.exists()) {
2673 wallpaper.wallpaperFile.delete();
2674 wallpaper.cropFile.delete();
2676 fos = new FileOutputStream(wallpaper.wallpaperFile);
2677 cos = new FileOutputStream(wallpaper.cropFile);
2679 byte[] buffer = new byte[32768];
2681 while ((amt=res.read(buffer)) > 0) {
2682 fos.write(buffer, 0, amt);
2683 cos.write(buffer, 0, amt);
2685 // mWallpaperObserver will notice the close and send the change broadcast
2687 Slog.v(TAG, "Restored wallpaper: " + resName);
2689 } catch (NameNotFoundException e) {
2690 Slog.e(TAG, "Package name " + pkg + " not found");
2691 } catch (Resources.NotFoundException e) {
2692 Slog.e(TAG, "Resource not found: " + resId);
2693 } catch (IOException e) {
2694 Slog.e(TAG, "IOException while restoring wallpaper ", e);
2696 IoUtils.closeQuietly(res);
2698 FileUtils.sync(fos);
2701 FileUtils.sync(cos);
2703 IoUtils.closeQuietly(fos);
2704 IoUtils.closeQuietly(cos);
2712 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2713 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
2715 synchronized (mLock) {
2716 pw.println("System wallpaper state:");
2717 for (int i = 0; i < mWallpaperMap.size(); i++) {
2718 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
2719 pw.print(" User "); pw.print(wallpaper.userId);
2720 pw.print(": id="); pw.println(wallpaper.wallpaperId);
2721 pw.print(" mWidth=");
2722 pw.print(wallpaper.width);
2723 pw.print(" mHeight=");
2724 pw.println(wallpaper.height);
2725 pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
2726 pw.print(" mPadding="); pw.println(wallpaper.padding);
2727 pw.print(" mName="); pw.println(wallpaper.name);
2728 pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
2729 pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
2730 if (wallpaper.connection != null) {
2731 WallpaperConnection conn = wallpaper.connection;
2732 pw.print(" Wallpaper connection ");
2735 if (conn.mInfo != null) {
2736 pw.print(" mInfo.component=");
2737 pw.println(conn.mInfo.getComponent());
2739 pw.print(" mToken=");
2740 pw.println(conn.mToken);
2741 pw.print(" mService=");
2742 pw.println(conn.mService);
2743 pw.print(" mEngine=");
2744 pw.println(conn.mEngine);
2745 pw.print(" mLastDiedTime=");
2746 pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
2749 pw.println("Lock wallpaper state:");
2750 for (int i = 0; i < mLockWallpaperMap.size(); i++) {
2751 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
2752 pw.print(" User "); pw.print(wallpaper.userId);
2753 pw.print(": id="); pw.println(wallpaper.wallpaperId);
2754 pw.print(" mWidth="); pw.print(wallpaper.width);
2755 pw.print(" mHeight="); pw.println(wallpaper.height);
2756 pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
2757 pw.print(" mPadding="); pw.println(wallpaper.padding);
2758 pw.print(" mName="); pw.println(wallpaper.name);
2759 pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);