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