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