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 WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid)1183 WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) { 1184 mInfo = info; 1185 mWallpaper = wallpaper; 1186 mClientUid = clientUid; 1187 initDisplayState(); 1188 } 1189 initDisplayState()1190 private void initDisplayState() { 1191 // Do not initialize fallback wallpaper 1192 if (!mWallpaper.equals(mFallbackWallpaper)) { 1193 if (supportsMultiDisplay(this)) { 1194 // The system wallpaper is image wallpaper or it can supports multiple displays. 1195 appendConnectorWithCondition(this::isUsableDisplay); 1196 } else { 1197 // The system wallpaper does not support multiple displays, so just attach it on 1198 // default display. 1199 mDisplayConnector.append(DEFAULT_DISPLAY, 1200 new DisplayConnector(DEFAULT_DISPLAY)); 1201 } 1202 } 1203 } 1204 appendConnectorWithCondition(Predicate<Display> tester)1205 private void appendConnectorWithCondition(Predicate<Display> tester) { 1206 final Display[] displays = mDisplayManager.getDisplays(); 1207 for (Display display : displays) { 1208 if (tester.test(display)) { 1209 final int displayId = display.getDisplayId(); 1210 final DisplayConnector connector = mDisplayConnector.get(displayId); 1211 if (connector == null) { 1212 mDisplayConnector.append(displayId, 1213 new DisplayConnector(displayId)); 1214 } 1215 } 1216 } 1217 } 1218 isUsableDisplay(Display display)1219 private boolean isUsableDisplay(Display display) { 1220 if (display == null || !display.hasAccess(mClientUid)) { 1221 return false; 1222 } 1223 final int displayId = display.getDisplayId(); 1224 if (displayId == DEFAULT_DISPLAY) { 1225 return true; 1226 } 1227 1228 final long ident = Binder.clearCallingIdentity(); 1229 try { 1230 return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); 1231 } finally { 1232 Binder.restoreCallingIdentity(ident); 1233 } 1234 } 1235 forEachDisplayConnector(Consumer<DisplayConnector> action)1236 void forEachDisplayConnector(Consumer<DisplayConnector> action) { 1237 for (int i = mDisplayConnector.size() - 1; i >= 0; i--) { 1238 final DisplayConnector connector = mDisplayConnector.valueAt(i); 1239 action.accept(connector); 1240 } 1241 } 1242 getConnectedEngineSize()1243 int getConnectedEngineSize() { 1244 int engineSize = 0; 1245 for (int i = mDisplayConnector.size() - 1; i >= 0; i--) { 1246 final DisplayConnector connector = mDisplayConnector.valueAt(i); 1247 if (connector.mEngine != null) engineSize++; 1248 } 1249 return engineSize; 1250 } 1251 getDisplayConnectorOrCreate(int displayId)1252 DisplayConnector getDisplayConnectorOrCreate(int displayId) { 1253 DisplayConnector connector = mDisplayConnector.get(displayId); 1254 if (connector == null) { 1255 final Display display = mDisplayManager.getDisplay(displayId); 1256 if (isUsableDisplay(display)) { 1257 connector = new DisplayConnector(displayId); 1258 mDisplayConnector.append(displayId, connector); 1259 } 1260 } 1261 return connector; 1262 } 1263 containsDisplay(int displayId)1264 boolean containsDisplay(int displayId) { 1265 return mDisplayConnector.get(displayId) != null; 1266 } 1267 removeDisplayConnector(int displayId)1268 void removeDisplayConnector(int displayId) { 1269 final DisplayConnector connector = mDisplayConnector.get(displayId); 1270 if (connector != null) { 1271 mDisplayConnector.remove(displayId); 1272 } 1273 } 1274 1275 @Override onServiceConnected(ComponentName name, IBinder service)1276 public void onServiceConnected(ComponentName name, IBinder service) { 1277 synchronized (mLock) { 1278 if (mWallpaper.connection == this) { 1279 mService = IWallpaperService.Stub.asInterface(service); 1280 attachServiceLocked(this, mWallpaper); 1281 // XXX should probably do saveSettingsLocked() later 1282 // when we have an engine, but I'm not sure about 1283 // locking there and anyway we always need to be able to 1284 // recover if there is something wrong. 1285 if (!mWallpaper.equals(mFallbackWallpaper)) { 1286 saveSettingsLocked(mWallpaper.userId); 1287 } 1288 FgThread.getHandler().removeCallbacks(mResetRunnable); 1289 } 1290 } 1291 } 1292 1293 @Override onServiceDisconnected(ComponentName name)1294 public void onServiceDisconnected(ComponentName name) { 1295 synchronized (mLock) { 1296 Slog.w(TAG, "Wallpaper service gone: " + name); 1297 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) { 1298 Slog.e(TAG, "Does not match expected wallpaper component " 1299 + mWallpaper.wallpaperComponent); 1300 } 1301 mService = null; 1302 forEachDisplayConnector(connector -> connector.mEngine = null); 1303 if (mWallpaper.connection == this) { 1304 // There is an inherent ordering race between this callback and the 1305 // package monitor that receives notice that a package is being updated, 1306 // so we cannot quite trust at this moment that we know for sure that 1307 // this is not an update. If we think this is a genuine non-update 1308 // wallpaper outage, we do our "wait for reset" work as a continuation, 1309 // a short time in the future, specifically to allow any pending package 1310 // update message on this same looper thread to be processed. 1311 if (!mWallpaper.wallpaperUpdating) { 1312 mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this), 1313 1000); 1314 } 1315 } 1316 } 1317 } 1318 scheduleTimeoutLocked()1319 public void scheduleTimeoutLocked() { 1320 // If we didn't reset it right away, do so after we couldn't connect to 1321 // it for an extended amount of time to avoid having a black wallpaper. 1322 final Handler fgHandler = FgThread.getHandler(); 1323 fgHandler.removeCallbacks(mResetRunnable); 1324 fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS); 1325 if (DEBUG_LIVE) { 1326 Slog.i(TAG, 1327 "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent); 1328 } 1329 } 1330 processDisconnect(final ServiceConnection connection)1331 private void processDisconnect(final ServiceConnection connection) { 1332 synchronized (mLock) { 1333 // The wallpaper disappeared. If this isn't a system-default one, track 1334 // crashes and fall back to default if it continues to misbehave. 1335 if (connection == mWallpaper.connection) { 1336 final ComponentName wpService = mWallpaper.wallpaperComponent; 1337 if (!mWallpaper.wallpaperUpdating 1338 && mWallpaper.userId == mCurrentUserId 1339 && !Objects.equals(mDefaultWallpaperComponent, wpService) 1340 && !Objects.equals(mImageWallpaper, wpService)) { 1341 // There is a race condition which causes 1342 // {@link #mWallpaper.wallpaperUpdating} to be false even if it is 1343 // currently updating since the broadcast notifying us is async. 1344 // This race is overcome by the general rule that we only reset the 1345 // wallpaper if its service was shut down twice 1346 // during {@link #MIN_WALLPAPER_CRASH_TIME} millis. 1347 if (mWallpaper.lastDiedTime != 0 1348 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME 1349 > SystemClock.uptimeMillis()) { 1350 Slog.w(TAG, "Reverting to built-in wallpaper!"); 1351 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); 1352 } else { 1353 mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); 1354 1355 clearWallpaperComponentLocked(mWallpaper); 1356 if (bindWallpaperComponentLocked( 1357 wpService, false, false, mWallpaper, null)) { 1358 mWallpaper.connection.scheduleTimeoutLocked(); 1359 } else { 1360 Slog.w(TAG, "Reverting to built-in wallpaper!"); 1361 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); 1362 } 1363 } 1364 final String flattened = wpService.flattenToString(); 1365 EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, 1366 flattened.substring(0, Math.min(flattened.length(), 1367 MAX_WALLPAPER_COMPONENT_LOG_LENGTH))); 1368 } 1369 } else { 1370 if (DEBUG_LIVE) { 1371 Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring"); 1372 } 1373 } 1374 } 1375 } 1376 1377 /** 1378 * Called by a live wallpaper if its colors have changed. 1379 * @param primaryColors representation of wallpaper primary colors 1380 * @param displayId for which display 1381 */ 1382 @Override onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId)1383 public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) { 1384 int which; 1385 synchronized (mLock) { 1386 // Do not broadcast changes on ImageWallpaper since it's handled 1387 // internally by this class. 1388 if (mImageWallpaper.equals(mWallpaper.wallpaperComponent)) { 1389 return; 1390 } 1391 1392 mWallpaper.primaryColors = primaryColors; 1393 1394 // Live wallpapers always are system wallpapers. 1395 which = FLAG_SYSTEM; 1396 // It's also the lock screen wallpaper when we don't have a bitmap in there. 1397 if (displayId == DEFAULT_DISPLAY) { 1398 final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId); 1399 if (lockedWallpaper == null) { 1400 which |= FLAG_LOCK; 1401 } 1402 } 1403 } 1404 if (which != 0) { 1405 notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId); 1406 } 1407 } 1408 1409 @Override attachEngine(IWallpaperEngine engine, int displayId)1410 public void attachEngine(IWallpaperEngine engine, int displayId) { 1411 synchronized (mLock) { 1412 final DisplayConnector connector = getDisplayConnectorOrCreate(displayId); 1413 if (connector == null) { 1414 try { 1415 engine.destroy(); 1416 } catch (RemoteException e) { 1417 Slog.w(TAG, "Failed to destroy engine", e); 1418 } 1419 return; 1420 } 1421 connector.mEngine = engine; 1422 connector.ensureStatusHandled(); 1423 1424 // TODO(multi-display) TBD. 1425 if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) { 1426 try { 1427 connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */); 1428 } catch (RemoteException e) { 1429 Slog.w(TAG, "Failed to set ambient mode state", e); 1430 } 1431 } 1432 try { 1433 // This will trigger onComputeColors in the wallpaper engine. 1434 // It's fine to be locked in here since the binder is oneway. 1435 connector.mEngine.requestWallpaperColors(); 1436 } catch (RemoteException e) { 1437 Slog.w(TAG, "Failed to request wallpaper colors", e); 1438 } 1439 } 1440 } 1441 1442 @Override engineShown(IWallpaperEngine engine)1443 public void engineShown(IWallpaperEngine engine) { 1444 synchronized (mLock) { 1445 if (mReply != null) { 1446 long ident = Binder.clearCallingIdentity(); 1447 try { 1448 mReply.sendResult(null); 1449 } catch (RemoteException e) { 1450 Binder.restoreCallingIdentity(ident); 1451 } 1452 mReply = null; 1453 } 1454 } 1455 } 1456 1457 @Override setWallpaper(String name)1458 public ParcelFileDescriptor setWallpaper(String name) { 1459 synchronized (mLock) { 1460 if (mWallpaper.connection == this) { 1461 return updateWallpaperBitmapLocked(name, mWallpaper, null); 1462 } 1463 return null; 1464 } 1465 } 1466 } 1467 1468 class MyPackageMonitor extends PackageMonitor { 1469 @Override onPackageUpdateFinished(String packageName, int uid)1470 public void onPackageUpdateFinished(String packageName, int uid) { 1471 synchronized (mLock) { 1472 if (mCurrentUserId != getChangingUserId()) { 1473 return; 1474 } 1475 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 1476 if (wallpaper != null) { 1477 final ComponentName wpService = wallpaper.wallpaperComponent; 1478 if (wpService != null && wpService.getPackageName().equals(packageName)) { 1479 if (DEBUG_LIVE) { 1480 Slog.i(TAG, "Wallpaper " + wpService + " update has finished"); 1481 } 1482 wallpaper.wallpaperUpdating = false; 1483 clearWallpaperComponentLocked(wallpaper); 1484 if (!bindWallpaperComponentLocked(wpService, false, false, 1485 wallpaper, null)) { 1486 Slog.w(TAG, "Wallpaper " + wpService 1487 + " no longer available; reverting to default"); 1488 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 1489 } 1490 } 1491 } 1492 } 1493 } 1494 1495 @Override onPackageModified(String packageName)1496 public void onPackageModified(String packageName) { 1497 synchronized (mLock) { 1498 if (mCurrentUserId != getChangingUserId()) { 1499 return; 1500 } 1501 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 1502 if (wallpaper != null) { 1503 if (wallpaper.wallpaperComponent == null 1504 || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 1505 return; 1506 } 1507 doPackagesChangedLocked(true, wallpaper); 1508 } 1509 } 1510 } 1511 1512 @Override onPackageUpdateStarted(String packageName, int uid)1513 public void onPackageUpdateStarted(String packageName, int uid) { 1514 synchronized (mLock) { 1515 if (mCurrentUserId != getChangingUserId()) { 1516 return; 1517 } 1518 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 1519 if (wallpaper != null) { 1520 if (wallpaper.wallpaperComponent != null 1521 && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 1522 if (DEBUG_LIVE) { 1523 Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent 1524 + " is updating"); 1525 } 1526 wallpaper.wallpaperUpdating = true; 1527 if (wallpaper.connection != null) { 1528 FgThread.getHandler().removeCallbacks( 1529 wallpaper.connection.mResetRunnable); 1530 } 1531 } 1532 } 1533 } 1534 } 1535 1536 @Override onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)1537 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { 1538 synchronized (mLock) { 1539 boolean changed = false; 1540 if (mCurrentUserId != getChangingUserId()) { 1541 return false; 1542 } 1543 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 1544 if (wallpaper != null) { 1545 boolean res = doPackagesChangedLocked(doit, wallpaper); 1546 changed |= res; 1547 } 1548 return changed; 1549 } 1550 } 1551 1552 @Override onSomePackagesChanged()1553 public void onSomePackagesChanged() { 1554 synchronized (mLock) { 1555 if (mCurrentUserId != getChangingUserId()) { 1556 return; 1557 } 1558 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 1559 if (wallpaper != null) { 1560 doPackagesChangedLocked(true, wallpaper); 1561 } 1562 } 1563 } 1564 doPackagesChangedLocked(boolean doit, WallpaperData wallpaper)1565 boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) { 1566 boolean changed = false; 1567 if (wallpaper.wallpaperComponent != null) { 1568 int change = isPackageDisappearing(wallpaper.wallpaperComponent 1569 .getPackageName()); 1570 if (change == PACKAGE_PERMANENT_CHANGE 1571 || change == PACKAGE_TEMPORARY_CHANGE) { 1572 changed = true; 1573 if (doit) { 1574 Slog.w(TAG, "Wallpaper uninstalled, removing: " 1575 + wallpaper.wallpaperComponent); 1576 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 1577 } 1578 } 1579 } 1580 if (wallpaper.nextWallpaperComponent != null) { 1581 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent 1582 .getPackageName()); 1583 if (change == PACKAGE_PERMANENT_CHANGE 1584 || change == PACKAGE_TEMPORARY_CHANGE) { 1585 wallpaper.nextWallpaperComponent = null; 1586 } 1587 } 1588 if (wallpaper.wallpaperComponent != null 1589 && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) { 1590 try { 1591 mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent, 1592 PackageManager.MATCH_DIRECT_BOOT_AWARE 1593 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 1594 } catch (NameNotFoundException e) { 1595 Slog.w(TAG, "Wallpaper component gone, removing: " 1596 + wallpaper.wallpaperComponent); 1597 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 1598 } 1599 } 1600 if (wallpaper.nextWallpaperComponent != null 1601 && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) { 1602 try { 1603 mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent, 1604 PackageManager.MATCH_DIRECT_BOOT_AWARE 1605 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 1606 } catch (NameNotFoundException e) { 1607 wallpaper.nextWallpaperComponent = null; 1608 } 1609 } 1610 return changed; 1611 } 1612 } 1613 WallpaperManagerService(Context context)1614 public WallpaperManagerService(Context context) { 1615 if (DEBUG) Slog.v(TAG, "WallpaperService startup"); 1616 mContext = context; 1617 mShuttingDown = false; 1618 mImageWallpaper = ComponentName.unflattenFromString( 1619 context.getResources().getString(R.string.image_wallpaper_component)); 1620 mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); 1621 mIWindowManager = IWindowManager.Stub.asInterface( 1622 ServiceManager.getService(Context.WINDOW_SERVICE)); 1623 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); 1624 mIPackageManager = AppGlobals.getPackageManager(); 1625 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 1626 mDisplayManager = mContext.getSystemService(DisplayManager.class); 1627 mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */); 1628 mMonitor = new MyPackageMonitor(); 1629 mColorsChangedListeners = new SparseArray<>(); 1630 1631 LocalServices.addService(WallpaperManagerInternal.class, new LocalService()); 1632 } 1633 1634 private final class LocalService extends WallpaperManagerInternal { 1635 @Override onDisplayReady(int displayId)1636 public void onDisplayReady(int displayId) { 1637 onDisplayReadyInternal(displayId); 1638 } 1639 } 1640 initialize()1641 void initialize() { 1642 mMonitor.register(mContext, null, UserHandle.ALL, true); 1643 getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs(); 1644 1645 // Initialize state from the persistent store, then guarantee that the 1646 // WallpaperData for the system imagery is instantiated & active, creating 1647 // it from defaults if necessary. 1648 loadSettingsLocked(UserHandle.USER_SYSTEM, false); 1649 getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM); 1650 } 1651 getWallpaperDir(int userId)1652 private static File getWallpaperDir(int userId) { 1653 return Environment.getUserSystemDirectory(userId); 1654 } 1655 1656 @Override finalize()1657 protected void finalize() throws Throwable { 1658 super.finalize(); 1659 for (int i = 0; i < mWallpaperMap.size(); i++) { 1660 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 1661 wallpaper.wallpaperObserver.stopWatching(); 1662 } 1663 } 1664 systemReady()1665 void systemReady() { 1666 if (DEBUG) Slog.v(TAG, "systemReady"); 1667 initialize(); 1668 1669 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); 1670 // If we think we're going to be using the system image wallpaper imagery, make 1671 // sure we have something to render 1672 if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) { 1673 // No crop file? Make sure we've finished the processing sequence if necessary 1674 if (!wallpaper.cropExists()) { 1675 if (DEBUG) { 1676 Slog.i(TAG, "No crop; regenerating from source"); 1677 } 1678 generateCrop(wallpaper); 1679 } 1680 // Still nothing? Fall back to default. 1681 if (!wallpaper.cropExists()) { 1682 if (DEBUG) { 1683 Slog.i(TAG, "Unable to regenerate crop; resetting"); 1684 } 1685 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null); 1686 } 1687 } else { 1688 if (DEBUG) { 1689 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring"); 1690 } 1691 } 1692 1693 IntentFilter userFilter = new IntentFilter(); 1694 userFilter.addAction(Intent.ACTION_USER_REMOVED); 1695 mContext.registerReceiver(new BroadcastReceiver() { 1696 @Override 1697 public void onReceive(Context context, Intent intent) { 1698 final String action = intent.getAction(); 1699 if (Intent.ACTION_USER_REMOVED.equals(action)) { 1700 onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 1701 UserHandle.USER_NULL)); 1702 } 1703 } 1704 }, userFilter); 1705 1706 final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 1707 mContext.registerReceiver(new BroadcastReceiver() { 1708 @Override 1709 public void onReceive(Context context, Intent intent) { 1710 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { 1711 if (DEBUG) { 1712 Slog.i(TAG, "Shutting down"); 1713 } 1714 synchronized (mLock) { 1715 mShuttingDown = true; 1716 } 1717 } 1718 } 1719 }, shutdownFilter); 1720 1721 try { 1722 ActivityManager.getService().registerUserSwitchObserver( 1723 new UserSwitchObserver() { 1724 @Override 1725 public void onUserSwitching(int newUserId, IRemoteCallback reply) { 1726 switchUser(newUserId, reply); 1727 } 1728 }, TAG); 1729 } catch (RemoteException e) { 1730 e.rethrowAsRuntimeException(); 1731 } 1732 } 1733 1734 /** Called by SystemBackupAgent */ getName()1735 public String getName() { 1736 // Verify caller is the system 1737 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 1738 throw new RuntimeException("getName() can only be called from the system process"); 1739 } 1740 synchronized (mLock) { 1741 return mWallpaperMap.get(0).name; 1742 } 1743 } 1744 stopObserver(WallpaperData wallpaper)1745 void stopObserver(WallpaperData wallpaper) { 1746 if (wallpaper != null) { 1747 if (wallpaper.wallpaperObserver != null) { 1748 wallpaper.wallpaperObserver.stopWatching(); 1749 wallpaper.wallpaperObserver = null; 1750 } 1751 } 1752 } 1753 stopObserversLocked(int userId)1754 void stopObserversLocked(int userId) { 1755 stopObserver(mWallpaperMap.get(userId)); 1756 stopObserver(mLockWallpaperMap.get(userId)); 1757 mWallpaperMap.remove(userId); 1758 mLockWallpaperMap.remove(userId); 1759 } 1760 1761 @Override onBootPhase(int phase)1762 public void onBootPhase(int phase) { 1763 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 1764 systemReady(); 1765 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 1766 switchUser(UserHandle.USER_SYSTEM, null); 1767 } 1768 } 1769 1770 @Override onUnlockUser(final int userId)1771 public void onUnlockUser(final int userId) { 1772 synchronized (mLock) { 1773 if (mCurrentUserId == userId) { 1774 if (mWaitingForUnlock) { 1775 // the desired wallpaper is not direct-boot aware, load it now 1776 final WallpaperData systemWallpaper = 1777 getWallpaperSafeLocked(userId, FLAG_SYSTEM); 1778 switchWallpaper(systemWallpaper, null); 1779 notifyCallbacksLocked(systemWallpaper); 1780 } 1781 1782 // Make sure that the SELinux labeling of all the relevant files is correct. 1783 // This corrects for mislabeling bugs that might have arisen from move-to 1784 // operations involving the wallpaper files. This isn't timing-critical, 1785 // so we do it in the background to avoid holding up the user unlock operation. 1786 if (!mUserRestorecon.get(userId)) { 1787 mUserRestorecon.put(userId, true); 1788 Runnable relabeler = new Runnable() { 1789 @Override 1790 public void run() { 1791 final File wallpaperDir = getWallpaperDir(userId); 1792 for (String filename : sPerUserFiles) { 1793 File f = new File(wallpaperDir, filename); 1794 if (f.exists()) { 1795 SELinux.restorecon(f); 1796 } 1797 } 1798 } 1799 }; 1800 BackgroundThread.getHandler().post(relabeler); 1801 } 1802 } 1803 } 1804 } 1805 onRemoveUser(int userId)1806 void onRemoveUser(int userId) { 1807 if (userId < 1) return; 1808 1809 final File wallpaperDir = getWallpaperDir(userId); 1810 synchronized (mLock) { 1811 stopObserversLocked(userId); 1812 for (String filename : sPerUserFiles) { 1813 new File(wallpaperDir, filename).delete(); 1814 } 1815 mUserRestorecon.delete(userId); 1816 } 1817 } 1818 switchUser(int userId, IRemoteCallback reply)1819 void switchUser(int userId, IRemoteCallback reply) { 1820 final WallpaperData systemWallpaper; 1821 final WallpaperData lockWallpaper; 1822 synchronized (mLock) { 1823 if (mCurrentUserId == userId) { 1824 return; 1825 } 1826 mCurrentUserId = userId; 1827 systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 1828 final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId); 1829 lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper; 1830 // Not started watching yet, in case wallpaper data was loaded for other reasons. 1831 if (systemWallpaper.wallpaperObserver == null) { 1832 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper); 1833 systemWallpaper.wallpaperObserver.startWatching(); 1834 } 1835 switchWallpaper(systemWallpaper, reply); 1836 } 1837 1838 // Offload color extraction to another thread since switchUser will be called 1839 // from the main thread. 1840 FgThread.getHandler().post(() -> { 1841 notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM); 1842 notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK); 1843 notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); 1844 }); 1845 } 1846 switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply)1847 void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) { 1848 synchronized (mLock) { 1849 mWaitingForUnlock = false; 1850 final ComponentName cname = wallpaper.wallpaperComponent != null ? 1851 wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent; 1852 if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) { 1853 // We failed to bind the desired wallpaper, but that might 1854 // happen if the wallpaper isn't direct-boot aware 1855 ServiceInfo si = null; 1856 try { 1857 si = mIPackageManager.getServiceInfo(cname, 1858 PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId); 1859 } catch (RemoteException ignored) { 1860 } 1861 1862 if (si == null) { 1863 Slog.w(TAG, "Failure starting previous wallpaper; clearing"); 1864 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply); 1865 } else { 1866 Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked"); 1867 // We might end up persisting the current wallpaper data 1868 // while locked, so pretend like the component was actually 1869 // bound into place 1870 wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent; 1871 final WallpaperData fallback = new WallpaperData(wallpaper.userId, 1872 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 1873 ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY); 1874 bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply); 1875 mWaitingForUnlock = true; 1876 } 1877 } 1878 } 1879 } 1880 1881 @Override clearWallpaper(String callingPackage, int which, int userId)1882 public void clearWallpaper(String callingPackage, int which, int userId) { 1883 if (DEBUG) Slog.v(TAG, "clearWallpaper"); 1884 checkPermission(android.Manifest.permission.SET_WALLPAPER); 1885 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) { 1886 return; 1887 } 1888 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1889 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null); 1890 1891 WallpaperData data = null; 1892 synchronized (mLock) { 1893 clearWallpaperLocked(false, which, userId, null); 1894 1895 if (which == FLAG_LOCK) { 1896 data = mLockWallpaperMap.get(userId); 1897 } 1898 if (which == FLAG_SYSTEM || data == null) { 1899 data = mWallpaperMap.get(userId); 1900 } 1901 } 1902 1903 // When clearing a wallpaper, broadcast new valid colors 1904 if (data != null) { 1905 notifyWallpaperColorsChanged(data, which); 1906 notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); 1907 } 1908 } 1909 clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply)1910 void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) { 1911 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 1912 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear"); 1913 } 1914 1915 WallpaperData wallpaper = null; 1916 if (which == FLAG_LOCK) { 1917 wallpaper = mLockWallpaperMap.get(userId); 1918 if (wallpaper == null) { 1919 // It's already gone; we're done. 1920 if (DEBUG) { 1921 Slog.i(TAG, "Lock wallpaper already cleared"); 1922 } 1923 return; 1924 } 1925 } else { 1926 wallpaper = mWallpaperMap.get(userId); 1927 if (wallpaper == null) { 1928 // Might need to bring it in the first time to establish our rewrite 1929 loadSettingsLocked(userId, false); 1930 wallpaper = mWallpaperMap.get(userId); 1931 } 1932 } 1933 if (wallpaper == null) { 1934 return; 1935 } 1936 1937 final long ident = Binder.clearCallingIdentity(); 1938 try { 1939 if (wallpaper.wallpaperFile.exists()) { 1940 wallpaper.wallpaperFile.delete(); 1941 wallpaper.cropFile.delete(); 1942 if (which == FLAG_LOCK) { 1943 mLockWallpaperMap.remove(userId); 1944 final IWallpaperManagerCallback cb = mKeyguardListener; 1945 if (cb != null) { 1946 if (DEBUG) { 1947 Slog.i(TAG, "Notifying keyguard of lock wallpaper clear"); 1948 } 1949 try { 1950 cb.onWallpaperChanged(); 1951 } catch (RemoteException e) { 1952 // Oh well it went away; no big deal 1953 } 1954 } 1955 saveSettingsLocked(userId); 1956 return; 1957 } 1958 } 1959 1960 RuntimeException e = null; 1961 try { 1962 wallpaper.primaryColors = null; 1963 wallpaper.imageWallpaperPending = false; 1964 if (userId != mCurrentUserId) return; 1965 if (bindWallpaperComponentLocked(defaultFailed 1966 ? mImageWallpaper 1967 : null, true, false, wallpaper, reply)) { 1968 return; 1969 } 1970 } catch (IllegalArgumentException e1) { 1971 e = e1; 1972 } 1973 1974 // This can happen if the default wallpaper component doesn't 1975 // exist. This should be a system configuration problem, but 1976 // let's not let it crash the system and just live with no 1977 // wallpaper. 1978 Slog.e(TAG, "Default wallpaper component not found!", e); 1979 clearWallpaperComponentLocked(wallpaper); 1980 if (reply != null) { 1981 try { 1982 reply.sendResult(null); 1983 } catch (RemoteException e1) { 1984 } 1985 } 1986 } finally { 1987 Binder.restoreCallingIdentity(ident); 1988 } 1989 } 1990 hasNamedWallpaper(String name)1991 public boolean hasNamedWallpaper(String name) { 1992 synchronized (mLock) { 1993 List<UserInfo> users; 1994 long ident = Binder.clearCallingIdentity(); 1995 try { 1996 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers(); 1997 } finally { 1998 Binder.restoreCallingIdentity(ident); 1999 } 2000 for (UserInfo user: users) { 2001 // ignore managed profiles 2002 if (user.isManagedProfile()) { 2003 continue; 2004 } 2005 WallpaperData wd = mWallpaperMap.get(user.id); 2006 if (wd == null) { 2007 // User hasn't started yet, so load her settings to peek at the wallpaper 2008 loadSettingsLocked(user.id, false); 2009 wd = mWallpaperMap.get(user.id); 2010 } 2011 if (wd != null && name.equals(wd.name)) { 2012 return true; 2013 } 2014 } 2015 } 2016 return false; 2017 } 2018 isValidDisplay(int displayId)2019 private boolean isValidDisplay(int displayId) { 2020 return mDisplayManager.getDisplay(displayId) != null; 2021 } 2022 2023 /** 2024 * Sets the dimension hint for the wallpaper. These hints indicate the desired 2025 * minimum width and height for the wallpaper in a particular display. 2026 */ setDimensionHints(int width, int height, String callingPackage, int displayId)2027 public void setDimensionHints(int width, int height, String callingPackage, int displayId) 2028 throws RemoteException { 2029 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 2030 if (!isWallpaperSupported(callingPackage)) { 2031 return; 2032 } 2033 2034 // Make sure both width and height are not larger than max texture size. 2035 width = Math.min(width, GLHelper.getMaxTextureSize()); 2036 height = Math.min(height, GLHelper.getMaxTextureSize()); 2037 2038 synchronized (mLock) { 2039 int userId = UserHandle.getCallingUserId(); 2040 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 2041 if (width <= 0 || height <= 0) { 2042 throw new IllegalArgumentException("width and height must be > 0"); 2043 } 2044 2045 if (!isValidDisplay(displayId)) { 2046 throw new IllegalArgumentException("Cannot find display with id=" + displayId); 2047 } 2048 2049 final DisplayData wpdData = getDisplayDataOrCreate(displayId); 2050 if (width != wpdData.mWidth || height != wpdData.mHeight) { 2051 wpdData.mWidth = width; 2052 wpdData.mHeight = height; 2053 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId); 2054 if (mCurrentUserId != userId) return; // Don't change the properties now 2055 if (wallpaper.connection != null) { 2056 final WallpaperConnection.DisplayConnector connector = wallpaper.connection 2057 .getDisplayConnectorOrCreate(displayId); 2058 final IWallpaperEngine engine = connector != null ? connector.mEngine : null; 2059 if (engine != null) { 2060 try { 2061 engine.setDesiredSize(width, height); 2062 } catch (RemoteException e) { 2063 } 2064 notifyCallbacksLocked(wallpaper); 2065 } else if (wallpaper.connection.mService != null && connector != null) { 2066 // We've attached to the service but the engine hasn't attached back to us 2067 // yet. This means it will be created with the previous dimensions, so we 2068 // need to update it to the new dimensions once it attaches. 2069 connector.mDimensionsChanged = true; 2070 } 2071 } 2072 } 2073 } 2074 } 2075 2076 /** 2077 * Returns the desired minimum width for the wallpaper in a particular display. 2078 */ getWidthHint(int displayId)2079 public int getWidthHint(int displayId) throws RemoteException { 2080 synchronized (mLock) { 2081 if (!isValidDisplay(displayId)) { 2082 throw new IllegalArgumentException("Cannot find display with id=" + displayId); 2083 } 2084 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); 2085 if (wallpaper != null) { 2086 final DisplayData wpdData = getDisplayDataOrCreate(displayId); 2087 return wpdData.mWidth; 2088 } else { 2089 return 0; 2090 } 2091 } 2092 } 2093 2094 /** 2095 * Returns the desired minimum height for the wallpaper in a particular display. 2096 */ getHeightHint(int displayId)2097 public int getHeightHint(int displayId) throws RemoteException { 2098 synchronized (mLock) { 2099 if (!isValidDisplay(displayId)) { 2100 throw new IllegalArgumentException("Cannot find display with id=" + displayId); 2101 } 2102 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); 2103 if (wallpaper != null) { 2104 final DisplayData wpdData = getDisplayDataOrCreate(displayId); 2105 return wpdData.mHeight; 2106 } else { 2107 return 0; 2108 } 2109 } 2110 } 2111 2112 /** 2113 * Sets extra padding that we would like the wallpaper to have outside of the display. 2114 */ setDisplayPadding(Rect padding, String callingPackage, int displayId)2115 public void setDisplayPadding(Rect padding, String callingPackage, int displayId) { 2116 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 2117 if (!isWallpaperSupported(callingPackage)) { 2118 return; 2119 } 2120 synchronized (mLock) { 2121 if (!isValidDisplay(displayId)) { 2122 throw new IllegalArgumentException("Cannot find display with id=" + displayId); 2123 } 2124 int userId = UserHandle.getCallingUserId(); 2125 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 2126 if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) { 2127 throw new IllegalArgumentException("padding must be positive: " + padding); 2128 } 2129 2130 final DisplayData wpdData = getDisplayDataOrCreate(displayId); 2131 if (!padding.equals(wpdData.mPadding)) { 2132 wpdData.mPadding.set(padding); 2133 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId); 2134 if (mCurrentUserId != userId) return; // Don't change the properties now 2135 if (wallpaper.connection != null) { 2136 final WallpaperConnection.DisplayConnector connector = wallpaper.connection 2137 .getDisplayConnectorOrCreate(displayId); 2138 final IWallpaperEngine engine = connector != null ? connector.mEngine : null; 2139 if (engine != null) { 2140 try { 2141 engine.setDisplayPadding(padding); 2142 } catch (RemoteException e) { 2143 } 2144 notifyCallbacksLocked(wallpaper); 2145 } else if (wallpaper.connection.mService != null && connector != null) { 2146 // We've attached to the service but the engine hasn't attached back to us 2147 // yet. This means it will be created with the previous dimensions, so we 2148 // need to update it to the new dimensions once it attaches. 2149 connector.mPaddingChanged = true; 2150 } 2151 } 2152 } 2153 } 2154 } 2155 2156 @Override getWallpaper(String callingPkg, IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId)2157 public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb, 2158 final int which, Bundle outParams, int wallpaperUserId) { 2159 final int hasPrivilege = mContext.checkCallingOrSelfPermission( 2160 android.Manifest.permission.READ_WALLPAPER_INTERNAL); 2161 if (hasPrivilege != PackageManager.PERMISSION_GRANTED) { 2162 mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true, 2163 Binder.getCallingPid(), Binder.getCallingUid(), callingPkg); 2164 } 2165 2166 wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 2167 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null); 2168 2169 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 2170 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read"); 2171 } 2172 2173 synchronized (mLock) { 2174 final SparseArray<WallpaperData> whichSet = 2175 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 2176 WallpaperData wallpaper = whichSet.get(wallpaperUserId); 2177 if (wallpaper == null) { 2178 // There is no established wallpaper imagery of this type (expected 2179 // only for lock wallpapers; a system WallpaperData is established at 2180 // user switch) 2181 return null; 2182 } 2183 // Only for default display. 2184 final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); 2185 try { 2186 if (outParams != null) { 2187 outParams.putInt("width", wpdData.mWidth); 2188 outParams.putInt("height", wpdData.mHeight); 2189 } 2190 if (cb != null) { 2191 wallpaper.callbacks.register(cb); 2192 } 2193 if (!wallpaper.cropFile.exists()) { 2194 return null; 2195 } 2196 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY); 2197 } catch (FileNotFoundException e) { 2198 /* Shouldn't happen as we check to see if the file exists */ 2199 Slog.w(TAG, "Error getting wallpaper", e); 2200 } 2201 return null; 2202 } 2203 } 2204 2205 @Override getWallpaperInfo(int userId)2206 public WallpaperInfo getWallpaperInfo(int userId) { 2207 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 2208 Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null); 2209 synchronized (mLock) { 2210 WallpaperData wallpaper = mWallpaperMap.get(userId); 2211 if (wallpaper != null && wallpaper.connection != null) { 2212 return wallpaper.connection.mInfo; 2213 } 2214 return null; 2215 } 2216 } 2217 2218 @Override getWallpaperIdForUser(int which, int userId)2219 public int getWallpaperIdForUser(int which, int userId) { 2220 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 2221 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null); 2222 2223 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 2224 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper"); 2225 } 2226 2227 final SparseArray<WallpaperData> map = 2228 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 2229 synchronized (mLock) { 2230 WallpaperData wallpaper = map.get(userId); 2231 if (wallpaper != null) { 2232 return wallpaper.wallpaperId; 2233 } 2234 } 2235 return -1; 2236 } 2237 2238 @Override registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2239 public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, 2240 int displayId) { 2241 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), 2242 userId, true, true, "registerWallpaperColorsCallback", null); 2243 synchronized (mLock) { 2244 SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> 2245 userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId); 2246 if (userDisplayColorsChangedListeners == null) { 2247 userDisplayColorsChangedListeners = new SparseArray<>(); 2248 mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners); 2249 } 2250 RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners = 2251 userDisplayColorsChangedListeners.get(displayId); 2252 if (displayChangedListeners == null) { 2253 displayChangedListeners = new RemoteCallbackList<>(); 2254 userDisplayColorsChangedListeners.put(displayId, displayChangedListeners); 2255 } 2256 displayChangedListeners.register(cb); 2257 } 2258 } 2259 2260 @Override unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId)2261 public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, 2262 int displayId) { 2263 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), 2264 userId, true, true, "unregisterWallpaperColorsCallback", null); 2265 synchronized (mLock) { 2266 SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> 2267 userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId); 2268 if (userDisplayColorsChangedListeners != null) { 2269 RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners = 2270 userDisplayColorsChangedListeners.get(displayId); 2271 if (displayChangedListeners != null) { 2272 displayChangedListeners.unregister(cb); 2273 } 2274 } 2275 } 2276 } 2277 2278 /** 2279 * TODO(multi-display) Extends this method with specific display. 2280 * Propagate ambient state to wallpaper engine. 2281 * 2282 * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise. 2283 * @param animationDuration Duration of the animation, or 0 when immediate. 2284 */ setInAmbientMode(boolean inAmbientMode, long animationDuration)2285 public void setInAmbientMode(boolean inAmbientMode, long animationDuration) { 2286 final IWallpaperEngine engine; 2287 synchronized (mLock) { 2288 mInAmbientMode = inAmbientMode; 2289 final WallpaperData data = mWallpaperMap.get(mCurrentUserId); 2290 // The wallpaper info is null for image wallpaper, also use the engine in this case. 2291 if (data != null && data.connection != null && (data.connection.mInfo == null 2292 || data.connection.mInfo.supportsAmbientMode())) { 2293 // TODO(multi-display) Extends this method with specific display. 2294 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; 2295 } else { 2296 engine = null; 2297 } 2298 } 2299 2300 if (engine != null) { 2301 try { 2302 engine.setInAmbientMode(inAmbientMode, animationDuration); 2303 } catch (RemoteException e) { 2304 // Cannot talk to wallpaper engine. 2305 } 2306 } 2307 } 2308 2309 @Override setLockWallpaperCallback(IWallpaperManagerCallback cb)2310 public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) { 2311 checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW); 2312 synchronized (mLock) { 2313 mKeyguardListener = cb; 2314 } 2315 return true; 2316 } 2317 2318 @Override getWallpaperColors(int which, int userId, int displayId)2319 public WallpaperColors getWallpaperColors(int which, int userId, int displayId) 2320 throws RemoteException { 2321 if (which != FLAG_LOCK && which != FLAG_SYSTEM) { 2322 throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); 2323 } 2324 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), 2325 userId, false, true, "getWallpaperColors", null); 2326 2327 WallpaperData wallpaperData = null; 2328 boolean shouldExtract; 2329 2330 synchronized (mLock) { 2331 if (which == FLAG_LOCK) { 2332 wallpaperData = mLockWallpaperMap.get(userId); 2333 } 2334 2335 // Try to get the system wallpaper anyway since it might 2336 // also be the lock screen wallpaper 2337 if (wallpaperData == null) { 2338 wallpaperData = findWallpaperAtDisplay(userId, displayId); 2339 } 2340 2341 if (wallpaperData == null) { 2342 return null; 2343 } 2344 shouldExtract = wallpaperData.primaryColors == null; 2345 } 2346 2347 if (shouldExtract) { 2348 extractColors(wallpaperData); 2349 } 2350 2351 synchronized (mLock) { 2352 return wallpaperData.primaryColors; 2353 } 2354 } 2355 findWallpaperAtDisplay(int userId, int displayId)2356 private WallpaperData findWallpaperAtDisplay(int userId, int displayId) { 2357 if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null 2358 && mFallbackWallpaper.connection.containsDisplay(displayId)) { 2359 return mFallbackWallpaper; 2360 } else { 2361 return mWallpaperMap.get(userId); 2362 } 2363 } 2364 2365 @Override setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, IWallpaperManagerCallback completion, int userId)2366 public ParcelFileDescriptor setWallpaper(String name, String callingPackage, 2367 Rect cropHint, boolean allowBackup, Bundle extras, int which, 2368 IWallpaperManagerCallback completion, int userId) { 2369 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, 2370 false /* all */, true /* full */, "changing wallpaper", null /* pkg */); 2371 checkPermission(android.Manifest.permission.SET_WALLPAPER); 2372 2373 if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) { 2374 final String msg = "Must specify a valid wallpaper category to set"; 2375 Slog.e(TAG, msg); 2376 throw new IllegalArgumentException(msg); 2377 } 2378 2379 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) { 2380 return null; 2381 } 2382 2383 // "null" means the no-op crop, preserving the full input image 2384 if (cropHint == null) { 2385 cropHint = new Rect(0, 0, 0, 0); 2386 } else { 2387 if (cropHint.isEmpty() 2388 || cropHint.left < 0 2389 || cropHint.top < 0) { 2390 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint); 2391 } 2392 } 2393 2394 synchronized (mLock) { 2395 if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which)); 2396 WallpaperData wallpaper; 2397 2398 /* If we're setting system but not lock, and lock is currently sharing the system 2399 * wallpaper, we need to migrate that image over to being lock-only before 2400 * the caller here writes new bitmap data. 2401 */ 2402 if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) { 2403 if (DEBUG) { 2404 Slog.i(TAG, "Migrating system->lock to preserve"); 2405 } 2406 migrateSystemToLockWallpaperLocked(userId); 2407 } 2408 2409 wallpaper = getWallpaperSafeLocked(userId, which); 2410 final long ident = Binder.clearCallingIdentity(); 2411 try { 2412 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras); 2413 if (pfd != null) { 2414 wallpaper.imageWallpaperPending = true; 2415 wallpaper.whichPending = which; 2416 wallpaper.setComplete = completion; 2417 wallpaper.cropHint.set(cropHint); 2418 wallpaper.allowBackup = allowBackup; 2419 } 2420 return pfd; 2421 } finally { 2422 Binder.restoreCallingIdentity(ident); 2423 } 2424 } 2425 } 2426 migrateSystemToLockWallpaperLocked(int userId)2427 private void migrateSystemToLockWallpaperLocked(int userId) { 2428 WallpaperData sysWP = mWallpaperMap.get(userId); 2429 if (sysWP == null) { 2430 if (DEBUG) { 2431 Slog.i(TAG, "No system wallpaper? Not tracking for lock-only"); 2432 } 2433 return; 2434 } 2435 2436 // We know a-priori that there is no lock-only wallpaper currently 2437 WallpaperData lockWP = new WallpaperData(userId, 2438 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 2439 lockWP.wallpaperId = sysWP.wallpaperId; 2440 lockWP.cropHint.set(sysWP.cropHint); 2441 lockWP.allowBackup = sysWP.allowBackup; 2442 lockWP.primaryColors = sysWP.primaryColors; 2443 2444 // Migrate the bitmap files outright; no need to copy 2445 try { 2446 Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath()); 2447 Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath()); 2448 } catch (ErrnoException e) { 2449 Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage()); 2450 lockWP.wallpaperFile.delete(); 2451 lockWP.cropFile.delete(); 2452 return; 2453 } 2454 2455 mLockWallpaperMap.put(userId, lockWP); 2456 } 2457 updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, Bundle extras)2458 ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, 2459 Bundle extras) { 2460 if (name == null) name = ""; 2461 try { 2462 File dir = getWallpaperDir(wallpaper.userId); 2463 if (!dir.exists()) { 2464 dir.mkdir(); 2465 FileUtils.setPermissions( 2466 dir.getPath(), 2467 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 2468 -1, -1); 2469 } 2470 ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile, 2471 MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE); 2472 if (!SELinux.restorecon(wallpaper.wallpaperFile)) { 2473 return null; 2474 } 2475 wallpaper.name = name; 2476 wallpaper.wallpaperId = makeWallpaperIdLocked(); 2477 if (extras != null) { 2478 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId); 2479 } 2480 // Nullify field to require new computation 2481 wallpaper.primaryColors = null; 2482 if (DEBUG) { 2483 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId 2484 + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); 2485 } 2486 return fd; 2487 } catch (FileNotFoundException e) { 2488 Slog.w(TAG, "Error setting wallpaper", e); 2489 } 2490 return null; 2491 } 2492 2493 @Override setWallpaperComponentChecked(ComponentName name, String callingPackage, int userId)2494 public void setWallpaperComponentChecked(ComponentName name, String callingPackage, 2495 int userId) { 2496 2497 if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) { 2498 setWallpaperComponent(name, userId); 2499 } 2500 } 2501 2502 // ToDo: Remove this version of the function 2503 @Override setWallpaperComponent(ComponentName name)2504 public void setWallpaperComponent(ComponentName name) { 2505 setWallpaperComponent(name, UserHandle.getCallingUserId()); 2506 } 2507 setWallpaperComponent(ComponentName name, int userId)2508 private void setWallpaperComponent(ComponentName name, int userId) { 2509 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, 2510 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */); 2511 checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); 2512 2513 int which = FLAG_SYSTEM; 2514 boolean shouldNotifyColors = false; 2515 WallpaperData wallpaper; 2516 2517 synchronized (mLock) { 2518 if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); 2519 wallpaper = mWallpaperMap.get(userId); 2520 if (wallpaper == null) { 2521 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); 2522 } 2523 final long ident = Binder.clearCallingIdentity(); 2524 2525 // Live wallpapers can't be specified for keyguard. If we're using a static 2526 // system+lock image currently, migrate the system wallpaper to be a lock-only 2527 // image as part of making a different live component active as the system 2528 // wallpaper. 2529 if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) { 2530 if (mLockWallpaperMap.get(userId) == null) { 2531 // We're using the static imagery and there is no lock-specific image in place, 2532 // therefore it's a shared system+lock image that we need to migrate. 2533 migrateSystemToLockWallpaperLocked(userId); 2534 } 2535 } 2536 2537 // New live wallpaper is also a lock wallpaper if nothing is set 2538 if (mLockWallpaperMap.get(userId) == null) { 2539 which |= FLAG_LOCK; 2540 } 2541 2542 try { 2543 wallpaper.imageWallpaperPending = false; 2544 boolean same = changingToSame(name, wallpaper); 2545 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { 2546 if (!same) { 2547 wallpaper.primaryColors = null; 2548 } 2549 wallpaper.wallpaperId = makeWallpaperIdLocked(); 2550 notifyCallbacksLocked(wallpaper); 2551 shouldNotifyColors = true; 2552 } 2553 } finally { 2554 Binder.restoreCallingIdentity(ident); 2555 } 2556 } 2557 2558 if (shouldNotifyColors) { 2559 notifyWallpaperColorsChanged(wallpaper, which); 2560 notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); 2561 } 2562 } 2563 changingToSame(ComponentName componentName, WallpaperData wallpaper)2564 private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) { 2565 if (wallpaper.connection != null) { 2566 if (wallpaper.wallpaperComponent == null) { 2567 if (componentName == null) { 2568 if (DEBUG) Slog.v(TAG, "changingToSame: still using default"); 2569 // Still using default wallpaper. 2570 return true; 2571 } 2572 } else if (wallpaper.wallpaperComponent.equals(componentName)) { 2573 // Changing to same wallpaper. 2574 if (DEBUG) Slog.v(TAG, "same wallpaper"); 2575 return true; 2576 } 2577 } 2578 return false; 2579 } 2580 bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply)2581 private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, 2582 boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { 2583 if (DEBUG_LIVE) { 2584 Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); 2585 } 2586 // Has the component changed? 2587 if (!force && changingToSame(componentName, wallpaper)) { 2588 return true; 2589 } 2590 2591 try { 2592 if (componentName == null) { 2593 componentName = mDefaultWallpaperComponent; 2594 if (componentName == null) { 2595 // Fall back to static image wallpaper 2596 componentName = mImageWallpaper; 2597 //clearWallpaperComponentLocked(); 2598 //return; 2599 if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper"); 2600 } 2601 } 2602 int serviceUserId = wallpaper.userId; 2603 ServiceInfo si = mIPackageManager.getServiceInfo(componentName, 2604 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId); 2605 if (si == null) { 2606 // The wallpaper component we're trying to use doesn't exist 2607 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable"); 2608 return false; 2609 } 2610 if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { 2611 String msg = "Selected service does not have " 2612 + android.Manifest.permission.BIND_WALLPAPER 2613 + ": " + componentName; 2614 if (fromUser) { 2615 throw new SecurityException(msg); 2616 } 2617 Slog.w(TAG, msg); 2618 return false; 2619 } 2620 2621 WallpaperInfo wi = null; 2622 2623 Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); 2624 if (componentName != null && !componentName.equals(mImageWallpaper)) { 2625 // Make sure the selected service is actually a wallpaper service. 2626 List<ResolveInfo> ris = 2627 mIPackageManager.queryIntentServices(intent, 2628 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 2629 PackageManager.GET_META_DATA, serviceUserId).getList(); 2630 for (int i=0; i<ris.size(); i++) { 2631 ServiceInfo rsi = ris.get(i).serviceInfo; 2632 if (rsi.name.equals(si.name) && 2633 rsi.packageName.equals(si.packageName)) { 2634 try { 2635 wi = new WallpaperInfo(mContext, ris.get(i)); 2636 } catch (XmlPullParserException e) { 2637 if (fromUser) { 2638 throw new IllegalArgumentException(e); 2639 } 2640 Slog.w(TAG, e); 2641 return false; 2642 } catch (IOException e) { 2643 if (fromUser) { 2644 throw new IllegalArgumentException(e); 2645 } 2646 Slog.w(TAG, e); 2647 return false; 2648 } 2649 break; 2650 } 2651 } 2652 if (wi == null) { 2653 String msg = "Selected service is not a wallpaper: " 2654 + componentName; 2655 if (fromUser) { 2656 throw new SecurityException(msg); 2657 } 2658 Slog.w(TAG, msg); 2659 return false; 2660 } 2661 } 2662 2663 if (wi != null && wi.supportsAmbientMode()) { 2664 final int hasPrivilege = mIPackageManager.checkPermission( 2665 android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(), 2666 serviceUserId); 2667 if (hasPrivilege != PackageManager.PERMISSION_GRANTED) { 2668 String msg = "Selected service does not have " 2669 + android.Manifest.permission.AMBIENT_WALLPAPER 2670 + ": " + componentName; 2671 if (fromUser) { 2672 throw new SecurityException(msg); 2673 } 2674 Slog.w(TAG, msg); 2675 return false; 2676 } 2677 } 2678 2679 // Bind the service! 2680 if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); 2681 final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(), 2682 MATCH_DIRECT_BOOT_AUTO, wallpaper.userId); 2683 WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid); 2684 intent.setComponent(componentName); 2685 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 2686 com.android.internal.R.string.wallpaper_binding_label); 2687 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( 2688 mContext, 0, 2689 Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER), 2690 mContext.getText(com.android.internal.R.string.chooser_wallpaper)), 2691 0, null, new UserHandle(serviceUserId))); 2692 if (!mContext.bindServiceAsUser(intent, newConn, 2693 Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI 2694 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE 2695 | Context.BIND_INCLUDE_CAPABILITIES, 2696 new UserHandle(serviceUserId))) { 2697 String msg = "Unable to bind service: " 2698 + componentName; 2699 if (fromUser) { 2700 throw new IllegalArgumentException(msg); 2701 } 2702 Slog.w(TAG, msg); 2703 return false; 2704 } 2705 if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null 2706 && !wallpaper.equals(mFallbackWallpaper)) { 2707 detachWallpaperLocked(mLastWallpaper); 2708 } 2709 wallpaper.wallpaperComponent = componentName; 2710 wallpaper.connection = newConn; 2711 newConn.mReply = reply; 2712 if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) { 2713 mLastWallpaper = wallpaper; 2714 } 2715 updateFallbackConnection(); 2716 } catch (RemoteException e) { 2717 String msg = "Remote exception for " + componentName + "\n" + e; 2718 if (fromUser) { 2719 throw new IllegalArgumentException(msg); 2720 } 2721 Slog.w(TAG, msg); 2722 return false; 2723 } 2724 return true; 2725 } 2726 detachWallpaperLocked(WallpaperData wallpaper)2727 private void detachWallpaperLocked(WallpaperData wallpaper) { 2728 if (wallpaper.connection != null) { 2729 if (wallpaper.connection.mReply != null) { 2730 try { 2731 wallpaper.connection.mReply.sendResult(null); 2732 } catch (RemoteException e) { 2733 } 2734 wallpaper.connection.mReply = null; 2735 } 2736 try { 2737 // It can be null if user switching happens before service connection. 2738 if (wallpaper.connection.mService != null) { 2739 wallpaper.connection.mService.detach(); 2740 } 2741 } catch (RemoteException e) { 2742 Slog.w(TAG, "Failed detaching wallpaper service ", e); 2743 } 2744 mContext.unbindService(wallpaper.connection); 2745 wallpaper.connection.forEachDisplayConnector( 2746 WallpaperConnection.DisplayConnector::disconnectLocked); 2747 wallpaper.connection.mService = null; 2748 wallpaper.connection.mDisplayConnector.clear(); 2749 wallpaper.connection = null; 2750 if (wallpaper == mLastWallpaper) mLastWallpaper = null; 2751 } 2752 } 2753 clearWallpaperComponentLocked(WallpaperData wallpaper)2754 private void clearWallpaperComponentLocked(WallpaperData wallpaper) { 2755 wallpaper.wallpaperComponent = null; 2756 detachWallpaperLocked(wallpaper); 2757 } 2758 attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper)2759 private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) { 2760 conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper)); 2761 } 2762 notifyCallbacksLocked(WallpaperData wallpaper)2763 private void notifyCallbacksLocked(WallpaperData wallpaper) { 2764 final int n = wallpaper.callbacks.beginBroadcast(); 2765 for (int i = 0; i < n; i++) { 2766 try { 2767 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged(); 2768 } catch (RemoteException e) { 2769 2770 // The RemoteCallbackList will take care of removing 2771 // the dead object for us. 2772 } 2773 } 2774 wallpaper.callbacks.finishBroadcast(); 2775 2776 final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); 2777 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); 2778 } 2779 checkPermission(String permission)2780 private void checkPermission(String permission) { 2781 if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) { 2782 throw new SecurityException("Access denied to process: " + Binder.getCallingPid() 2783 + ", must have permission " + permission); 2784 } 2785 } 2786 2787 /** 2788 * Certain user types do not support wallpapers (e.g. managed profiles). The check is 2789 * implemented through through the OP_WRITE_WALLPAPER AppOp. 2790 */ isWallpaperSupported(String callingPackage)2791 public boolean isWallpaperSupported(String callingPackage) { 2792 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(), 2793 callingPackage) == AppOpsManager.MODE_ALLOWED; 2794 } 2795 2796 @Override isSetWallpaperAllowed(String callingPackage)2797 public boolean isSetWallpaperAllowed(String callingPackage) { 2798 final PackageManager pm = mContext.getPackageManager(); 2799 String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid()); 2800 boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage); 2801 if (!uidMatchPackage) { 2802 return false; // callingPackage was faked. 2803 } 2804 2805 final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 2806 if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) { 2807 return true; 2808 } 2809 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 2810 return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER); 2811 } 2812 2813 @Override isWallpaperBackupEligible(int which, int userId)2814 public boolean isWallpaperBackupEligible(int which, int userId) { 2815 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2816 throw new SecurityException("Only the system may call isWallpaperBackupEligible"); 2817 } 2818 2819 WallpaperData wallpaper = (which == FLAG_LOCK) 2820 ? mLockWallpaperMap.get(userId) 2821 : mWallpaperMap.get(userId); 2822 return (wallpaper != null) ? wallpaper.allowBackup : false; 2823 } 2824 onDisplayReadyInternal(int displayId)2825 private void onDisplayReadyInternal(int displayId) { 2826 synchronized (mLock) { 2827 if (mLastWallpaper == null) { 2828 return; 2829 } 2830 if (supportsMultiDisplay(mLastWallpaper.connection)) { 2831 final WallpaperConnection.DisplayConnector connector = 2832 mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId); 2833 if (connector == null) return; 2834 connector.connectLocked(mLastWallpaper.connection, mLastWallpaper); 2835 return; 2836 } 2837 // System wallpaper does not support multiple displays, attach this display to 2838 // the fallback wallpaper. 2839 if (mFallbackWallpaper != null) { 2840 final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper 2841 .connection.getDisplayConnectorOrCreate(displayId); 2842 if (connector == null) return; 2843 connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper); 2844 } else { 2845 Slog.w(TAG, "No wallpaper can be added to the new display"); 2846 } 2847 } 2848 } 2849 makeJournaledFile(int userId)2850 private static JournaledFile makeJournaledFile(int userId) { 2851 final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath(); 2852 return new JournaledFile(new File(base), new File(base + ".tmp")); 2853 } 2854 saveSettingsLocked(int userId)2855 private void saveSettingsLocked(int userId) { 2856 JournaledFile journal = makeJournaledFile(userId); 2857 FileOutputStream fstream = null; 2858 BufferedOutputStream stream = null; 2859 try { 2860 XmlSerializer out = new FastXmlSerializer(); 2861 fstream = new FileOutputStream(journal.chooseForWrite(), false); 2862 stream = new BufferedOutputStream(fstream); 2863 out.setOutput(stream, StandardCharsets.UTF_8.name()); 2864 out.startDocument(null, true); 2865 2866 WallpaperData wallpaper; 2867 2868 wallpaper = mWallpaperMap.get(userId); 2869 if (wallpaper != null) { 2870 writeWallpaperAttributes(out, "wp", wallpaper); 2871 } 2872 wallpaper = mLockWallpaperMap.get(userId); 2873 if (wallpaper != null) { 2874 writeWallpaperAttributes(out, "kwp", wallpaper); 2875 } 2876 2877 out.endDocument(); 2878 2879 stream.flush(); // also flushes fstream 2880 FileUtils.sync(fstream); 2881 stream.close(); // also closes fstream 2882 journal.commit(); 2883 } catch (IOException e) { 2884 IoUtils.closeQuietly(stream); 2885 journal.rollback(); 2886 } 2887 } 2888 writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)2889 private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper) 2890 throws IllegalArgumentException, IllegalStateException, IOException { 2891 if (DEBUG) { 2892 Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); 2893 } 2894 final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); 2895 out.startTag(null, tag); 2896 out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); 2897 out.attribute(null, "width", Integer.toString(wpdData.mWidth)); 2898 out.attribute(null, "height", Integer.toString(wpdData.mHeight)); 2899 2900 out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); 2901 out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); 2902 out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); 2903 out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); 2904 2905 if (wpdData.mPadding.left != 0) { 2906 out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left)); 2907 } 2908 if (wpdData.mPadding.top != 0) { 2909 out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top)); 2910 } 2911 if (wpdData.mPadding.right != 0) { 2912 out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right)); 2913 } 2914 if (wpdData.mPadding.bottom != 0) { 2915 out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom)); 2916 } 2917 2918 if (wallpaper.primaryColors != null) { 2919 int colorsCount = wallpaper.primaryColors.getMainColors().size(); 2920 out.attribute(null, "colorsCount", Integer.toString(colorsCount)); 2921 if (colorsCount > 0) { 2922 for (int i = 0; i < colorsCount; i++) { 2923 final Color wc = wallpaper.primaryColors.getMainColors().get(i); 2924 out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb())); 2925 } 2926 } 2927 out.attribute(null, "colorHints", 2928 Integer.toString(wallpaper.primaryColors.getColorHints())); 2929 } 2930 2931 out.attribute(null, "name", wallpaper.name); 2932 if (wallpaper.wallpaperComponent != null 2933 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { 2934 out.attribute(null, "component", 2935 wallpaper.wallpaperComponent.flattenToShortString()); 2936 } 2937 2938 if (wallpaper.allowBackup) { 2939 out.attribute(null, "backup", "true"); 2940 } 2941 2942 out.endTag(null, tag); 2943 } 2944 migrateFromOld()2945 private void migrateFromOld() { 2946 // Pre-N, what existed is the one we're now using as the display crop 2947 File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP); 2948 // In the very-long-ago, imagery lived with the settings app 2949 File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY); 2950 File newWallpaper = new File(getWallpaperDir(0), WALLPAPER); 2951 2952 // Migrations from earlier wallpaper image storage schemas 2953 if (preNWallpaper.exists()) { 2954 if (!newWallpaper.exists()) { 2955 // we've got the 'wallpaper' crop file but not the nominal source image, 2956 // so do the simple "just take everything" straight copy of legacy data 2957 if (DEBUG) { 2958 Slog.i(TAG, "Migrating wallpaper schema"); 2959 } 2960 FileUtils.copyFile(preNWallpaper, newWallpaper); 2961 } // else we're in the usual modern case: both source & crop exist 2962 } else if (originalWallpaper.exists()) { 2963 // VERY old schema; make sure things exist and are in the right place 2964 if (DEBUG) { 2965 Slog.i(TAG, "Migrating antique wallpaper schema"); 2966 } 2967 File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY); 2968 if (oldInfo.exists()) { 2969 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO); 2970 oldInfo.renameTo(newInfo); 2971 } 2972 2973 FileUtils.copyFile(originalWallpaper, preNWallpaper); 2974 originalWallpaper.renameTo(newWallpaper); 2975 } 2976 } 2977 getAttributeInt(XmlPullParser parser, String name, int defValue)2978 private int getAttributeInt(XmlPullParser parser, String name, int defValue) { 2979 String value = parser.getAttributeValue(null, name); 2980 if (value == null) { 2981 return defValue; 2982 } 2983 return Integer.parseInt(value); 2984 } 2985 2986 /** 2987 * Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could 2988 * happen during user switch. The async user switch observer may not have received 2989 * the event yet. We use this safe method when we don't care about this ordering and just 2990 * want to update the data. The data is going to be applied when the user switch observer 2991 * is eventually executed. 2992 * 2993 * Important: this method loads settings to initialize the given user's wallpaper data if 2994 * there is no current in-memory state. 2995 */ getWallpaperSafeLocked(int userId, int which)2996 private WallpaperData getWallpaperSafeLocked(int userId, int which) { 2997 // We're setting either just system (work with the system wallpaper), 2998 // both (also work with the system wallpaper), or just the lock 2999 // wallpaper (update against the existing lock wallpaper if any). 3000 // Combined or just-system operations use the 'system' WallpaperData 3001 // for this use; lock-only operations use the dedicated one. 3002 final SparseArray<WallpaperData> whichSet = 3003 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 3004 WallpaperData wallpaper = whichSet.get(userId); 3005 if (wallpaper == null) { 3006 // common case, this is the first lookup post-boot of the system or 3007 // unified lock, so we bring up the saved state lazily now and recheck. 3008 loadSettingsLocked(userId, false); 3009 wallpaper = whichSet.get(userId); 3010 // if it's still null here, this is a lock-only operation and there is not 3011 // yet a lock-only wallpaper set for this user, so we need to establish 3012 // it now. 3013 if (wallpaper == null) { 3014 if (which == FLAG_LOCK) { 3015 wallpaper = new WallpaperData(userId, 3016 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 3017 mLockWallpaperMap.put(userId, wallpaper); 3018 ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); 3019 } else { 3020 // sanity fallback: we're in bad shape, but establishing a known 3021 // valid system+lock WallpaperData will keep us from dying. 3022 Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!"); 3023 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); 3024 mWallpaperMap.put(userId, wallpaper); 3025 ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); 3026 } 3027 } 3028 } 3029 return wallpaper; 3030 } 3031 loadSettingsLocked(int userId, boolean keepDimensionHints)3032 private void loadSettingsLocked(int userId, boolean keepDimensionHints) { 3033 JournaledFile journal = makeJournaledFile(userId); 3034 FileInputStream stream = null; 3035 File file = journal.chooseForRead(); 3036 3037 WallpaperData wallpaper = mWallpaperMap.get(userId); 3038 if (wallpaper == null) { 3039 // Do this once per boot 3040 migrateFromOld(); 3041 3042 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); 3043 wallpaper.allowBackup = true; 3044 mWallpaperMap.put(userId, wallpaper); 3045 if (!wallpaper.cropExists()) { 3046 if (wallpaper.sourceExists()) { 3047 generateCrop(wallpaper); 3048 } else { 3049 Slog.i(TAG, "No static wallpaper imagery; defaults will be shown"); 3050 } 3051 } 3052 initializeFallbackWallpaper(); 3053 } 3054 boolean success = false; 3055 final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); 3056 try { 3057 stream = new FileInputStream(file); 3058 XmlPullParser parser = Xml.newPullParser(); 3059 parser.setInput(stream, StandardCharsets.UTF_8.name()); 3060 3061 int type; 3062 do { 3063 type = parser.next(); 3064 if (type == XmlPullParser.START_TAG) { 3065 String tag = parser.getName(); 3066 if ("wp".equals(tag)) { 3067 // Common to system + lock wallpapers 3068 parseWallpaperAttributes(parser, wallpaper, keepDimensionHints); 3069 3070 // A system wallpaper might also be a live wallpaper 3071 String comp = parser.getAttributeValue(null, "component"); 3072 wallpaper.nextWallpaperComponent = comp != null 3073 ? ComponentName.unflattenFromString(comp) 3074 : null; 3075 if (wallpaper.nextWallpaperComponent == null 3076 || "android".equals(wallpaper.nextWallpaperComponent 3077 .getPackageName())) { 3078 wallpaper.nextWallpaperComponent = mImageWallpaper; 3079 } 3080 3081 if (DEBUG) { 3082 Slog.v(TAG, "mWidth:" + wpdData.mWidth); 3083 Slog.v(TAG, "mHeight:" + wpdData.mHeight); 3084 Slog.v(TAG, "cropRect:" + wallpaper.cropHint); 3085 Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors); 3086 Slog.v(TAG, "mName:" + wallpaper.name); 3087 Slog.v(TAG, "mNextWallpaperComponent:" 3088 + wallpaper.nextWallpaperComponent); 3089 } 3090 } else if ("kwp".equals(tag)) { 3091 // keyguard-specific wallpaper for this user 3092 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); 3093 if (lockWallpaper == null) { 3094 lockWallpaper = new WallpaperData(userId, 3095 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 3096 mLockWallpaperMap.put(userId, lockWallpaper); 3097 } 3098 parseWallpaperAttributes(parser, lockWallpaper, false); 3099 } 3100 } 3101 } while (type != XmlPullParser.END_DOCUMENT); 3102 success = true; 3103 } catch (FileNotFoundException e) { 3104 Slog.w(TAG, "no current wallpaper -- first boot?"); 3105 } catch (NullPointerException e) { 3106 Slog.w(TAG, "failed parsing " + file + " " + e); 3107 } catch (NumberFormatException e) { 3108 Slog.w(TAG, "failed parsing " + file + " " + e); 3109 } catch (XmlPullParserException e) { 3110 Slog.w(TAG, "failed parsing " + file + " " + e); 3111 } catch (IOException e) { 3112 Slog.w(TAG, "failed parsing " + file + " " + e); 3113 } catch (IndexOutOfBoundsException e) { 3114 Slog.w(TAG, "failed parsing " + file + " " + e); 3115 } 3116 IoUtils.closeQuietly(stream); 3117 3118 if (!success) { 3119 wallpaper.cropHint.set(0, 0, 0, 0); 3120 wpdData.mPadding.set(0, 0, 0, 0); 3121 wallpaper.name = ""; 3122 3123 mLockWallpaperMap.remove(userId); 3124 } else { 3125 if (wallpaper.wallpaperId <= 0) { 3126 wallpaper.wallpaperId = makeWallpaperIdLocked(); 3127 if (DEBUG) { 3128 Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId 3129 + "); now " + wallpaper.wallpaperId); 3130 } 3131 } 3132 } 3133 3134 ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY); 3135 ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY); 3136 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); 3137 if (lockWallpaper != null) { 3138 ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY); 3139 } 3140 } 3141 initializeFallbackWallpaper()3142 private void initializeFallbackWallpaper() { 3143 if (mFallbackWallpaper == null) { 3144 if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper"); 3145 mFallbackWallpaper = new WallpaperData( 3146 UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP); 3147 mFallbackWallpaper.allowBackup = false; 3148 mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked(); 3149 bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null); 3150 } 3151 } 3152 ensureSaneWallpaperData(WallpaperData wallpaper, int displayId)3153 private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) { 3154 final DisplayData size = getDisplayDataOrCreate(displayId); 3155 3156 if (displayId == DEFAULT_DISPLAY) { 3157 // crop, if not previously specified 3158 if (wallpaper.cropHint.width() <= 0 3159 || wallpaper.cropHint.height() <= 0) { 3160 wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight); 3161 } 3162 } 3163 } 3164 parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)3165 private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, 3166 boolean keepDimensionHints) { 3167 final String idString = parser.getAttributeValue(null, "id"); 3168 if (idString != null) { 3169 final int id = wallpaper.wallpaperId = Integer.parseInt(idString); 3170 if (id > mWallpaperId) { 3171 mWallpaperId = id; 3172 } 3173 } else { 3174 wallpaper.wallpaperId = makeWallpaperIdLocked(); 3175 } 3176 3177 final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); 3178 3179 if (!keepDimensionHints) { 3180 wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); 3181 wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height")); 3182 } 3183 wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); 3184 wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); 3185 wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0); 3186 wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); 3187 wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0); 3188 wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0); 3189 wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0); 3190 wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0); 3191 int colorsCount = getAttributeInt(parser, "colorsCount", 0); 3192 if (colorsCount > 0) { 3193 Color primary = null, secondary = null, tertiary = null; 3194 for (int i = 0; i < colorsCount; i++) { 3195 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0)); 3196 if (i == 0) { 3197 primary = color; 3198 } else if (i == 1) { 3199 secondary = color; 3200 } else if (i == 2) { 3201 tertiary = color; 3202 } else { 3203 break; 3204 } 3205 } 3206 int colorHints = getAttributeInt(parser, "colorHints", 0); 3207 wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints); 3208 } 3209 wallpaper.name = parser.getAttributeValue(null, "name"); 3210 wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); 3211 } 3212 3213 // Called by SystemBackupAgent after files are restored to disk. settingsRestored()3214 public void settingsRestored() { 3215 // Verify caller is the system 3216 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 3217 throw new RuntimeException("settingsRestored() can only be called from the system process"); 3218 } 3219 // TODO: If necessary, make it work for secondary users as well. This currently assumes 3220 // restores only to the primary user 3221 if (DEBUG) Slog.v(TAG, "settingsRestored"); 3222 WallpaperData wallpaper = null; 3223 boolean success = false; 3224 synchronized (mLock) { 3225 loadSettingsLocked(UserHandle.USER_SYSTEM, false); 3226 wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); 3227 wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore 3228 wallpaper.allowBackup = true; // by definition if it was restored 3229 if (wallpaper.nextWallpaperComponent != null 3230 && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) { 3231 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, 3232 wallpaper, null)) { 3233 // No such live wallpaper or other failure; fall back to the default 3234 // live wallpaper (since the profile being restored indicated that the 3235 // user had selected a live rather than static one). 3236 bindWallpaperComponentLocked(null, false, false, wallpaper, null); 3237 } 3238 success = true; 3239 } else { 3240 // If there's a wallpaper name, we use that. If that can't be loaded, then we 3241 // use the default. 3242 if ("".equals(wallpaper.name)) { 3243 if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty"); 3244 success = true; 3245 } else { 3246 if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource"); 3247 success = restoreNamedResourceLocked(wallpaper); 3248 } 3249 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success 3250 + " id=" + wallpaper.wallpaperId); 3251 if (success) { 3252 generateCrop(wallpaper); // based on the new image + metadata 3253 bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false, 3254 wallpaper, null); 3255 } 3256 } 3257 } 3258 3259 if (!success) { 3260 Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'"); 3261 wallpaper.name = ""; 3262 getWallpaperDir(UserHandle.USER_SYSTEM).delete(); 3263 } 3264 3265 synchronized (mLock) { 3266 saveSettingsLocked(UserHandle.USER_SYSTEM); 3267 } 3268 } 3269 3270 // Restore the named resource bitmap to both source + crop files restoreNamedResourceLocked(WallpaperData wallpaper)3271 private boolean restoreNamedResourceLocked(WallpaperData wallpaper) { 3272 if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { 3273 String resName = wallpaper.name.substring(4); 3274 3275 String pkg = null; 3276 int colon = resName.indexOf(':'); 3277 if (colon > 0) { 3278 pkg = resName.substring(0, colon); 3279 } 3280 3281 String ident = null; 3282 int slash = resName.lastIndexOf('/'); 3283 if (slash > 0) { 3284 ident = resName.substring(slash+1); 3285 } 3286 3287 String type = null; 3288 if (colon > 0 && slash > 0 && (slash-colon) > 1) { 3289 type = resName.substring(colon+1, slash); 3290 } 3291 3292 if (pkg != null && ident != null && type != null) { 3293 int resId = -1; 3294 InputStream res = null; 3295 FileOutputStream fos = null; 3296 FileOutputStream cos = null; 3297 try { 3298 Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); 3299 Resources r = c.getResources(); 3300 resId = r.getIdentifier(resName, null, null); 3301 if (resId == 0) { 3302 Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type 3303 + " ident=" + ident); 3304 return false; 3305 } 3306 3307 res = r.openRawResource(resId); 3308 if (wallpaper.wallpaperFile.exists()) { 3309 wallpaper.wallpaperFile.delete(); 3310 wallpaper.cropFile.delete(); 3311 } 3312 fos = new FileOutputStream(wallpaper.wallpaperFile); 3313 cos = new FileOutputStream(wallpaper.cropFile); 3314 3315 byte[] buffer = new byte[32768]; 3316 int amt; 3317 while ((amt=res.read(buffer)) > 0) { 3318 fos.write(buffer, 0, amt); 3319 cos.write(buffer, 0, amt); 3320 } 3321 // mWallpaperObserver will notice the close and send the change broadcast 3322 3323 Slog.v(TAG, "Restored wallpaper: " + resName); 3324 return true; 3325 } catch (NameNotFoundException e) { 3326 Slog.e(TAG, "Package name " + pkg + " not found"); 3327 } catch (Resources.NotFoundException e) { 3328 Slog.e(TAG, "Resource not found: " + resId); 3329 } catch (IOException e) { 3330 Slog.e(TAG, "IOException while restoring wallpaper ", e); 3331 } finally { 3332 IoUtils.closeQuietly(res); 3333 if (fos != null) { 3334 FileUtils.sync(fos); 3335 } 3336 if (cos != null) { 3337 FileUtils.sync(cos); 3338 } 3339 IoUtils.closeQuietly(fos); 3340 IoUtils.closeQuietly(cos); 3341 } 3342 } 3343 } 3344 return false; 3345 } 3346 3347 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)3348 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3349 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 3350 3351 synchronized (mLock) { 3352 pw.println("System wallpaper state:"); 3353 for (int i = 0; i < mWallpaperMap.size(); i++) { 3354 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 3355 pw.print(" User "); pw.print(wallpaper.userId); 3356 pw.print(": id="); pw.println(wallpaper.wallpaperId); 3357 pw.println(" Display state:"); 3358 forEachDisplayData(wpSize -> { 3359 pw.print(" displayId="); 3360 pw.println(wpSize.mDisplayId); 3361 pw.print(" mWidth="); 3362 pw.print(wpSize.mWidth); 3363 pw.print(" mHeight="); 3364 pw.println(wpSize.mHeight); 3365 pw.print(" mPadding="); pw.println(wpSize.mPadding); 3366 }); 3367 pw.print(" mCropHint="); pw.println(wallpaper.cropHint); 3368 pw.print(" mName="); pw.println(wallpaper.name); 3369 pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup); 3370 pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent); 3371 if (wallpaper.connection != null) { 3372 WallpaperConnection conn = wallpaper.connection; 3373 pw.print(" Wallpaper connection "); 3374 pw.print(conn); 3375 pw.println(":"); 3376 if (conn.mInfo != null) { 3377 pw.print(" mInfo.component="); 3378 pw.println(conn.mInfo.getComponent()); 3379 } 3380 conn.forEachDisplayConnector(connector -> { 3381 pw.print(" mDisplayId="); 3382 pw.println(connector.mDisplayId); 3383 pw.print(" mToken="); 3384 pw.println(connector.mToken); 3385 pw.print(" mEngine="); 3386 pw.println(connector.mEngine); 3387 }); 3388 pw.print(" mService="); 3389 pw.println(conn.mService); 3390 pw.print(" mLastDiedTime="); 3391 pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis()); 3392 } 3393 } 3394 pw.println("Lock wallpaper state:"); 3395 for (int i = 0; i < mLockWallpaperMap.size(); i++) { 3396 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i); 3397 pw.print(" User "); pw.print(wallpaper.userId); 3398 pw.print(": id="); pw.println(wallpaper.wallpaperId); 3399 pw.print(" mCropHint="); pw.println(wallpaper.cropHint); 3400 pw.print(" mName="); pw.println(wallpaper.name); 3401 pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup); 3402 } 3403 pw.println("Fallback wallpaper state:"); 3404 pw.print(" User "); pw.print(mFallbackWallpaper.userId); 3405 pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId); 3406 pw.print(" mCropHint="); pw.println(mFallbackWallpaper.cropHint); 3407 pw.print(" mName="); pw.println(mFallbackWallpaper.name); 3408 pw.print(" mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup); 3409 if (mFallbackWallpaper.connection != null) { 3410 WallpaperConnection conn = mFallbackWallpaper.connection; 3411 pw.print(" Fallback Wallpaper connection "); 3412 pw.print(conn); 3413 pw.println(":"); 3414 if (conn.mInfo != null) { 3415 pw.print(" mInfo.component="); 3416 pw.println(conn.mInfo.getComponent()); 3417 } 3418 conn.forEachDisplayConnector(connector -> { 3419 pw.print(" mDisplayId="); 3420 pw.println(connector.mDisplayId); 3421 pw.print(" mToken="); 3422 pw.println(connector.mToken); 3423 pw.print(" mEngine="); 3424 pw.println(connector.mEngine); 3425 }); 3426 pw.print(" mService="); 3427 pw.println(conn.mService); 3428 pw.print(" mLastDiedTime="); 3429 pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis()); 3430 } 3431 } 3432 } 3433 } 3434