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