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