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