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