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