1 /* 2 * Copyright (C) 2016 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.wallpaperbackup; 18 19 import static android.app.WallpaperManager.FLAG_LOCK; 20 import static android.app.WallpaperManager.FLAG_SYSTEM; 21 22 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE; 23 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA; 24 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPER; 25 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED; 26 27 import android.app.AppGlobals; 28 import android.app.WallpaperManager; 29 import android.app.backup.BackupAgent; 30 import android.app.backup.BackupDataInput; 31 import android.app.backup.BackupDataOutput; 32 import android.app.backup.BackupManager; 33 import android.app.backup.BackupRestoreEventLogger.BackupRestoreError; 34 import android.app.backup.FullBackupDataOutput; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.SharedPreferences; 38 import android.content.pm.IPackageManager; 39 import android.content.pm.PackageInfo; 40 import android.graphics.Rect; 41 import android.os.FileUtils; 42 import android.os.ParcelFileDescriptor; 43 import android.os.RemoteException; 44 import android.os.UserHandle; 45 import android.provider.Settings; 46 import android.util.Slog; 47 import android.util.Xml; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.content.PackageMonitor; 51 52 import org.xmlpull.v1.XmlPullParser; 53 54 import java.io.File; 55 import java.io.FileInputStream; 56 import java.io.FileOutputStream; 57 import java.io.IOException; 58 59 /** 60 * Backs up and restores wallpaper and metadata related to it. 61 * 62 * This agent has its own package because it does full backup as opposed to SystemBackupAgent 63 * which does key/value backup. 64 * 65 * This class stages wallpaper files for backup by copying them into its own directory because of 66 * the following reasons: 67 * 68 * <ul> 69 * <li>Non-system users don't have permission to read the directory that the system stores 70 * the wallpaper files in</li> 71 * <li>{@link BackupAgent} enforces that backed up files must live inside the package's 72 * {@link Context#getFilesDir()}</li> 73 * </ul> 74 * 75 * There are 3 files to back up: 76 * <ul> 77 * <li>The "wallpaper info" file which contains metadata like the crop applied to the 78 * wallpaper or the live wallpaper component name.</li> 79 * <li>The "system" wallpaper file.</li> 80 * <li>An optional "lock" wallpaper, which is shown on the lockscreen instead of the system 81 * wallpaper if set.</li> 82 * </ul> 83 * 84 * On restore, the metadata file is parsed and {@link WallpaperManager} APIs are used to set the 85 * wallpaper. Note that if there's a live wallpaper, the live wallpaper package name will be 86 * part of the metadata file and the wallpaper will be applied when the package it's installed. 87 */ 88 public class WallpaperBackupAgent extends BackupAgent { 89 private static final String TAG = "WallpaperBackup"; 90 private static final boolean DEBUG = false; 91 92 // Names of our local-data stage files 93 @VisibleForTesting 94 static final String SYSTEM_WALLPAPER_STAGE = "wallpaper-stage"; 95 @VisibleForTesting 96 static final String LOCK_WALLPAPER_STAGE = "wallpaper-lock-stage"; 97 @VisibleForTesting 98 static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage"; 99 100 static final String EMPTY_SENTINEL = "empty"; 101 static final String QUOTA_SENTINEL = "quota"; 102 103 // Shared preferences constants. 104 static final String PREFS_NAME = "wbprefs.xml"; 105 static final String SYSTEM_GENERATION = "system_gen"; 106 static final String LOCK_GENERATION = "lock_gen"; 107 108 // If this file exists, it means we exceeded our quota last time 109 private File mQuotaFile; 110 private boolean mQuotaExceeded; 111 112 private WallpaperManager mWallpaperManager; 113 private WallpaperEventLogger mEventLogger; 114 private BackupManager mBackupManager; 115 116 private boolean mSystemHasLiveComponent; 117 private boolean mLockHasLiveComponent; 118 119 @Override onCreate()120 public void onCreate() { 121 if (DEBUG) { 122 Slog.v(TAG, "onCreate()"); 123 } 124 125 mWallpaperManager = getSystemService(WallpaperManager.class); 126 127 mQuotaFile = new File(getFilesDir(), QUOTA_SENTINEL); 128 mQuotaExceeded = mQuotaFile.exists(); 129 if (DEBUG) { 130 Slog.v(TAG, "quota file " + mQuotaFile.getPath() + " exists=" + mQuotaExceeded); 131 } 132 133 mBackupManager = new BackupManager(getBaseContext()); 134 mEventLogger = new WallpaperEventLogger(mBackupManager, /* wallpaperAgent */ this); 135 } 136 137 @Override onFullBackup(FullBackupDataOutput data)138 public void onFullBackup(FullBackupDataOutput data) throws IOException { 139 try { 140 // We always back up this 'empty' file to ensure that the absence of 141 // storable wallpaper imagery still produces a non-empty backup data 142 // stream, otherwise it'd simply be ignored in preflight. 143 final File empty = new File(getFilesDir(), EMPTY_SENTINEL); 144 if (!empty.exists()) { 145 FileOutputStream touch = new FileOutputStream(empty); 146 touch.close(); 147 } 148 backupFile(empty, data); 149 150 SharedPreferences sharedPrefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); 151 152 // Check the IDs of the wallpapers that we backed up last time. If they haven't 153 // changed, we won't re-stage them for backup and use the old staged versions to avoid 154 // disk churn. 155 final int lastSysGeneration = sharedPrefs.getInt(SYSTEM_GENERATION, /* defValue= */ -1); 156 final int lastLockGeneration = sharedPrefs.getInt(LOCK_GENERATION, /* defValue= */ -1); 157 final int sysGeneration = mWallpaperManager.getWallpaperId(FLAG_SYSTEM); 158 final int lockGeneration = mWallpaperManager.getWallpaperId(FLAG_LOCK); 159 final boolean sysChanged = (sysGeneration != lastSysGeneration); 160 final boolean lockChanged = (lockGeneration != lastLockGeneration); 161 162 if (DEBUG) { 163 Slog.v(TAG, "sysGen=" + sysGeneration + " : sysChanged=" + sysChanged); 164 Slog.v(TAG, "lockGen=" + lockGeneration + " : lockChanged=" + lockChanged); 165 } 166 167 // Due to the way image vs live wallpaper backup logic is intermingled, for logging 168 // purposes first check if we have live components for each wallpaper to avoid 169 // over-reporting errors. 170 mSystemHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM) != null; 171 mLockHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_LOCK) != null; 172 173 backupWallpaperInfoFile(/* sysOrLockChanged= */ sysChanged || lockChanged, data); 174 backupSystemWallpaperFile(sharedPrefs, sysChanged, sysGeneration, data); 175 backupLockWallpaperFileIfItExists(sharedPrefs, lockChanged, lockGeneration, data); 176 } catch (Exception e) { 177 Slog.e(TAG, "Unable to back up wallpaper", e); 178 mEventLogger.onBackupException(e); 179 } finally { 180 // Even if this time we had to back off on attempting to store the lock image 181 // due to exceeding the data quota, try again next time. This will alternate 182 // between "try both" and "only store the primary image" until either there 183 // is no lock image to store, or the quota is raised, or both fit under the 184 // quota. 185 mQuotaFile.delete(); 186 } 187 } 188 backupWallpaperInfoFile(boolean sysOrLockChanged, FullBackupDataOutput data)189 private void backupWallpaperInfoFile(boolean sysOrLockChanged, FullBackupDataOutput data) 190 throws IOException { 191 final ParcelFileDescriptor wallpaperInfoFd = mWallpaperManager.getWallpaperInfoFile(); 192 193 if (wallpaperInfoFd == null) { 194 Slog.w(TAG, "Wallpaper metadata file doesn't exist"); 195 // If we have live components, getting the file to back up somehow failed, so log it 196 // as an error. 197 if (mSystemHasLiveComponent) { 198 mEventLogger.onSystemLiveWallpaperBackupFailed(ERROR_NO_METADATA); 199 } 200 if (mLockHasLiveComponent) { 201 mEventLogger.onLockLiveWallpaperBackupFailed(ERROR_NO_METADATA); 202 } 203 return; 204 } 205 206 final File infoStage = new File(getFilesDir(), WALLPAPER_INFO_STAGE); 207 208 if (sysOrLockChanged || !infoStage.exists()) { 209 if (DEBUG) Slog.v(TAG, "New wallpaper configuration; copying"); 210 copyFromPfdToFileAndClosePfd(wallpaperInfoFd, infoStage); 211 } 212 213 if (DEBUG) Slog.v(TAG, "Storing wallpaper metadata"); 214 backupFile(infoStage, data); 215 216 // We've backed up the info file which contains the live component, so log it as success 217 if (mSystemHasLiveComponent) { 218 mEventLogger.onSystemLiveWallpaperBackedUp( 219 mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM)); 220 } 221 if (mLockHasLiveComponent) { 222 mEventLogger.onLockLiveWallpaperBackedUp(mWallpaperManager.getWallpaperInfo(FLAG_LOCK)); 223 } 224 } 225 backupSystemWallpaperFile(SharedPreferences sharedPrefs, boolean sysChanged, int sysGeneration, FullBackupDataOutput data)226 private void backupSystemWallpaperFile(SharedPreferences sharedPrefs, 227 boolean sysChanged, int sysGeneration, FullBackupDataOutput data) throws IOException { 228 if (!mWallpaperManager.isWallpaperBackupEligible(FLAG_SYSTEM)) { 229 Slog.d(TAG, "System wallpaper ineligible for backup"); 230 logSystemImageErrorIfNoLiveComponent(ERROR_INELIGIBLE); 231 return; 232 } 233 234 final ParcelFileDescriptor systemWallpaperImageFd = mWallpaperManager.getWallpaperFile( 235 FLAG_SYSTEM, 236 /* getCropped= */ false); 237 238 if (systemWallpaperImageFd == null) { 239 Slog.w(TAG, "System wallpaper doesn't exist"); 240 logSystemImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER); 241 return; 242 } 243 244 final File imageStage = new File(getFilesDir(), SYSTEM_WALLPAPER_STAGE); 245 246 if (sysChanged || !imageStage.exists()) { 247 if (DEBUG) Slog.v(TAG, "New system wallpaper; copying"); 248 copyFromPfdToFileAndClosePfd(systemWallpaperImageFd, imageStage); 249 } 250 251 if (DEBUG) Slog.v(TAG, "Storing system wallpaper image"); 252 backupFile(imageStage, data); 253 sharedPrefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply(); 254 mEventLogger.onSystemImageWallpaperBackedUp(); 255 } 256 logSystemImageErrorIfNoLiveComponent(@ackupRestoreError String error)257 private void logSystemImageErrorIfNoLiveComponent(@BackupRestoreError String error) { 258 if (mSystemHasLiveComponent) { 259 return; 260 } 261 mEventLogger.onSystemImageWallpaperBackupFailed(error); 262 } 263 264 backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs, boolean lockChanged, int lockGeneration, FullBackupDataOutput data)265 private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs, 266 boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException { 267 final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE); 268 269 // This means there's no lock wallpaper set by the user. 270 if (lockGeneration == -1) { 271 if (lockChanged && lockImageStage.exists()) { 272 if (DEBUG) Slog.v(TAG, "Removed lock wallpaper; deleting"); 273 lockImageStage.delete(); 274 } 275 Slog.d(TAG, "No lockscreen wallpaper set, add nothing to backup"); 276 sharedPrefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply(); 277 logLockImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER); 278 return; 279 } 280 281 if (!mWallpaperManager.isWallpaperBackupEligible(FLAG_LOCK)) { 282 Slog.d(TAG, "Lock screen wallpaper ineligible for backup"); 283 logLockImageErrorIfNoLiveComponent(ERROR_INELIGIBLE); 284 return; 285 } 286 287 final ParcelFileDescriptor lockWallpaperFd = mWallpaperManager.getWallpaperFile( 288 FLAG_LOCK, /* getCropped= */ false); 289 290 // If we get to this point, that means lockGeneration != -1 so there's a lock wallpaper 291 // set, but we can't find it. 292 if (lockWallpaperFd == null) { 293 Slog.w(TAG, "Lock wallpaper doesn't exist"); 294 logLockImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER); 295 return; 296 } 297 298 if (mQuotaExceeded) { 299 Slog.w(TAG, "Not backing up lock screen wallpaper. Quota was exceeded last time"); 300 logLockImageErrorIfNoLiveComponent(ERROR_QUOTA_EXCEEDED); 301 return; 302 } 303 304 if (lockChanged || !lockImageStage.exists()) { 305 if (DEBUG) Slog.v(TAG, "New lock wallpaper; copying"); 306 copyFromPfdToFileAndClosePfd(lockWallpaperFd, lockImageStage); 307 } 308 309 if (DEBUG) Slog.v(TAG, "Storing lock wallpaper image"); 310 backupFile(lockImageStage, data); 311 sharedPrefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply(); 312 mEventLogger.onLockImageWallpaperBackedUp(); 313 } 314 logLockImageErrorIfNoLiveComponent(@ackupRestoreError String error)315 private void logLockImageErrorIfNoLiveComponent(@BackupRestoreError String error) { 316 if (mLockHasLiveComponent) { 317 return; 318 } 319 mEventLogger.onLockImageWallpaperBackupFailed(error); 320 } 321 322 /** 323 * Copies the contents of the given {@code pfd} to the given {@code file}. 324 * 325 * All resources used in the process including the {@code pfd} will be closed. 326 */ copyFromPfdToFileAndClosePfd(ParcelFileDescriptor pfd, File file)327 private static void copyFromPfdToFileAndClosePfd(ParcelFileDescriptor pfd, File file) 328 throws IOException { 329 try (ParcelFileDescriptor.AutoCloseInputStream inputStream = 330 new ParcelFileDescriptor.AutoCloseInputStream(pfd); 331 FileOutputStream outputStream = new FileOutputStream(file) 332 ) { 333 FileUtils.copy(inputStream, outputStream); 334 } 335 } 336 337 @VisibleForTesting 338 // fullBackupFile is final, so we intercept backups here in tests. backupFile(File file, FullBackupDataOutput data)339 protected void backupFile(File file, FullBackupDataOutput data) { 340 fullBackupFile(file, data); 341 } 342 343 @Override onQuotaExceeded(long backupDataBytes, long quotaBytes)344 public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { 345 Slog.i(TAG, "Quota exceeded (" + backupDataBytes + " vs " + quotaBytes + ')'); 346 try (FileOutputStream f = new FileOutputStream(mQuotaFile)) { 347 f.write(0); 348 } catch (Exception e) { 349 Slog.w(TAG, "Unable to record quota-exceeded: " + e.getMessage()); 350 } 351 } 352 353 // We use the default onRestoreFile() implementation that will recreate our stage files, 354 // then post-process in onRestoreFinished() to apply the new wallpaper. 355 @Override onRestoreFinished()356 public void onRestoreFinished() { 357 Slog.v(TAG, "onRestoreFinished()"); 358 final File filesDir = getFilesDir(); 359 final File infoStage = new File(filesDir, WALLPAPER_INFO_STAGE); 360 final File imageStage = new File(filesDir, SYSTEM_WALLPAPER_STAGE); 361 final File lockImageStage = new File(filesDir, LOCK_WALLPAPER_STAGE); 362 boolean lockImageStageExists = lockImageStage.exists(); 363 364 try { 365 // First parse the live component name so that we know for logging if we care about 366 // logging errors with the image restore. 367 ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); 368 mSystemHasLiveComponent = wpService != null; 369 370 ComponentName kwpService = null; 371 boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); 372 if (lockscreenLiveWallpaper) { 373 kwpService = parseWallpaperComponent(infoStage, "kwp"); 374 } 375 mLockHasLiveComponent = kwpService != null; 376 boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists(); 377 378 // if there's no separate lock wallpaper, apply the system wallpaper to both screens. 379 final int sysWhich = separateLockWallpaper ? FLAG_SYSTEM : FLAG_SYSTEM | FLAG_LOCK; 380 381 // It is valid for the imagery to be absent; it means that we were not permitted 382 // to back up the original image on the source device, or there was no user-supplied 383 // wallpaper image present. 384 if (!lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); 385 if (lockImageStageExists) { 386 restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); 387 } 388 if (lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); 389 390 // And reset to the wallpaper service we should be using 391 if (lockscreenLiveWallpaper && mLockHasLiveComponent) { 392 updateWallpaperComponent(kwpService, false, FLAG_LOCK); 393 } 394 updateWallpaperComponent(wpService, !lockImageStageExists, sysWhich); 395 } catch (Exception e) { 396 Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage()); 397 mEventLogger.onRestoreException(e); 398 } finally { 399 Slog.v(TAG, "Restore finished; clearing backup bookkeeping"); 400 infoStage.delete(); 401 imageStage.delete(); 402 lockImageStage.delete(); 403 404 SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); 405 prefs.edit() 406 .putInt(SYSTEM_GENERATION, -1) 407 .putInt(LOCK_GENERATION, -1) 408 .commit(); 409 } 410 } 411 412 @VisibleForTesting updateWallpaperComponent(ComponentName wpService, boolean applyToLock, int which)413 void updateWallpaperComponent(ComponentName wpService, boolean applyToLock, int which) 414 throws IOException { 415 boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); 416 if (servicePackageExists(wpService)) { 417 Slog.i(TAG, "Using wallpaper service " + wpService); 418 if (lockscreenLiveWallpaper) { 419 mWallpaperManager.setWallpaperComponentWithFlags(wpService, which); 420 if ((which & FLAG_LOCK) != 0) { 421 mEventLogger.onLockLiveWallpaperRestored(wpService); 422 } 423 if ((which & FLAG_SYSTEM) != 0) { 424 mEventLogger.onSystemLiveWallpaperRestored(wpService); 425 } 426 return; 427 } 428 mWallpaperManager.setWallpaperComponent(wpService); 429 if (applyToLock) { 430 // We have a live wallpaper and no static lock image, 431 // allow live wallpaper to show "through" on lock screen. 432 mWallpaperManager.clear(FLAG_LOCK); 433 mEventLogger.onLockLiveWallpaperRestored(wpService); 434 } 435 mEventLogger.onSystemLiveWallpaperRestored(wpService); 436 } else { 437 // If we've restored a live wallpaper, but the component doesn't exist, 438 // we should log it as an error so we can easily identify the problem 439 // in reports from users 440 if (wpService != null) { 441 // TODO(b/268471749): Handle delayed case 442 applyComponentAtInstall(wpService, applyToLock, which); 443 Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. " 444 + " Will try to apply later"); 445 } 446 } 447 } 448 restoreFromStage(File stage, File info, String hintTag, int which)449 private void restoreFromStage(File stage, File info, String hintTag, int which) 450 throws IOException { 451 if (stage.exists()) { 452 // Parse the restored info file to find the crop hint. Note that this currently 453 // relies on a priori knowledge of the wallpaper info file schema. 454 Rect cropHint = parseCropHint(info, hintTag); 455 if (cropHint != null) { 456 Slog.i(TAG, "Got restored wallpaper; applying which=" + which 457 + "; cropHint = " + cropHint); 458 try (FileInputStream in = new FileInputStream(stage)) { 459 mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, true, 460 which); 461 462 // And log the success 463 if ((which & FLAG_SYSTEM) > 0) { 464 mEventLogger.onSystemImageWallpaperRestored(); 465 } 466 if ((which & FLAG_LOCK) > 0) { 467 mEventLogger.onLockImageWallpaperRestored(); 468 } 469 } 470 } else { 471 logRestoreError(which, ERROR_NO_METADATA); 472 } 473 } else { 474 Slog.d(TAG, "Restore data doesn't exist for file " + stage.getPath()); 475 logRestoreErrorIfNoLiveComponent(which, ERROR_NO_WALLPAPER); 476 } 477 } 478 logRestoreErrorIfNoLiveComponent(int which, String error)479 private void logRestoreErrorIfNoLiveComponent(int which, String error) { 480 if (mSystemHasLiveComponent) { 481 return; 482 } 483 logRestoreError(which, error); 484 } 485 logRestoreError(int which, String error)486 private void logRestoreError(int which, String error) { 487 if ((which & FLAG_SYSTEM) == FLAG_SYSTEM) { 488 mEventLogger.onSystemImageWallpaperRestoreFailed(error); 489 } 490 if ((which & FLAG_LOCK) == FLAG_LOCK) { 491 mEventLogger.onLockImageWallpaperRestoreFailed(error); 492 } 493 } parseCropHint(File wallpaperInfo, String sectionTag)494 private Rect parseCropHint(File wallpaperInfo, String sectionTag) { 495 Rect cropHint = new Rect(); 496 try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { 497 XmlPullParser parser = Xml.resolvePullParser(stream); 498 499 int type; 500 do { 501 type = parser.next(); 502 if (type == XmlPullParser.START_TAG) { 503 String tag = parser.getName(); 504 if (sectionTag.equals(tag)) { 505 cropHint.left = getAttributeInt(parser, "cropLeft", 0); 506 cropHint.top = getAttributeInt(parser, "cropTop", 0); 507 cropHint.right = getAttributeInt(parser, "cropRight", 0); 508 cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); 509 } 510 } 511 } while (type != XmlPullParser.END_DOCUMENT); 512 } catch (Exception e) { 513 // Whoops; can't process the info file at all. Report failure. 514 Slog.w(TAG, "Failed to parse restored crop: " + e.getMessage()); 515 return null; 516 } 517 518 return cropHint; 519 } 520 parseWallpaperComponent(File wallpaperInfo, String sectionTag)521 private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) { 522 ComponentName name = null; 523 try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { 524 final XmlPullParser parser = Xml.resolvePullParser(stream); 525 526 int type; 527 do { 528 type = parser.next(); 529 if (type == XmlPullParser.START_TAG) { 530 String tag = parser.getName(); 531 if (sectionTag.equals(tag)) { 532 final String parsedName = parser.getAttributeValue(null, "component"); 533 name = (parsedName != null) 534 ? ComponentName.unflattenFromString(parsedName) 535 : null; 536 break; 537 } 538 } 539 } while (type != XmlPullParser.END_DOCUMENT); 540 } catch (Exception e) { 541 // Whoops; can't process the info file at all. Report failure. 542 Slog.w(TAG, "Failed to parse restored component: " + e.getMessage()); 543 return null; 544 } 545 return name; 546 } 547 getAttributeInt(XmlPullParser parser, String name, int defValue)548 private int getAttributeInt(XmlPullParser parser, String name, int defValue) { 549 final String value = parser.getAttributeValue(null, name); 550 return (value == null) ? defValue : Integer.parseInt(value); 551 } 552 553 @VisibleForTesting servicePackageExists(ComponentName comp)554 boolean servicePackageExists(ComponentName comp) { 555 try { 556 if (comp != null) { 557 final IPackageManager pm = AppGlobals.getPackageManager(); 558 final PackageInfo info = pm.getPackageInfo(comp.getPackageName(), 559 0, getUserId()); 560 return (info != null); 561 } 562 } catch (RemoteException e) { 563 Slog.e(TAG, "Unable to contact package manager"); 564 } 565 return false; 566 } 567 568 /** Unused Key/Value API. */ 569 @Override onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)570 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 571 ParcelFileDescriptor newState) throws IOException { 572 // Intentionally blank 573 } 574 575 /** Unused Key/Value API. */ 576 @Override onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)577 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) 578 throws IOException { 579 // Intentionally blank 580 } 581 applyComponentAtInstall(ComponentName componentName, boolean applyToLock, int which)582 private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock, 583 int which) { 584 PackageMonitor packageMonitor = getWallpaperPackageMonitor( 585 componentName, applyToLock, which); 586 packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true); 587 } 588 589 @VisibleForTesting getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock, int which)590 PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock, 591 int which) { 592 return new PackageMonitor() { 593 @Override 594 public void onPackageAdded(String packageName, int uid) { 595 boolean lockscreenLiveWallpaper = 596 mWallpaperManager.isLockscreenLiveWallpaperEnabled(); 597 if (!isDeviceInRestore()) { 598 // We don't want to reapply the wallpaper outside a restore. 599 unregister(); 600 601 // We have finished restore and not succeeded, so let's log that as an error. 602 WallpaperEventLogger logger = new WallpaperEventLogger( 603 mBackupManager.getDelayedRestoreLogger()); 604 logger.onSystemLiveWallpaperRestoreFailed( 605 WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED); 606 if (applyToLock) { 607 logger.onLockLiveWallpaperRestoreFailed( 608 WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED); 609 } 610 mBackupManager.reportDelayedRestoreResult(logger.getBackupRestoreLogger()); 611 612 return; 613 } 614 615 if (componentName.getPackageName().equals(packageName)) { 616 Slog.d(TAG, "Applying component " + componentName); 617 boolean success = lockscreenLiveWallpaper 618 ? mWallpaperManager.setWallpaperComponentWithFlags(componentName, which) 619 : mWallpaperManager.setWallpaperComponent(componentName); 620 WallpaperEventLogger logger = new WallpaperEventLogger( 621 mBackupManager.getDelayedRestoreLogger()); 622 if (success) { 623 if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { 624 logger.onSystemLiveWallpaperRestored(componentName); 625 } 626 if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { 627 logger.onLockLiveWallpaperRestored(componentName); 628 } 629 } else { 630 if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { 631 logger.onSystemLiveWallpaperRestoreFailed( 632 WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); 633 } 634 if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { 635 logger.onLockLiveWallpaperRestoreFailed( 636 WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); 637 } 638 } 639 if (applyToLock && !lockscreenLiveWallpaper) { 640 try { 641 mWallpaperManager.clear(FLAG_LOCK); 642 logger.onLockLiveWallpaperRestored(componentName); 643 } catch (IOException e) { 644 Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e); 645 logger.onLockLiveWallpaperRestoreFailed(e.getClass().getName()); 646 } 647 } 648 // We're only expecting to restore the wallpaper component once. 649 unregister(); 650 mBackupManager.reportDelayedRestoreResult(logger.getBackupRestoreLogger()); 651 } 652 } 653 }; 654 } 655 656 @VisibleForTesting 657 boolean isDeviceInRestore() { 658 try { 659 boolean isInSetup = Settings.Secure.getInt(getBaseContext().getContentResolver(), 660 Settings.Secure.USER_SETUP_COMPLETE) == 0; 661 boolean isInDeferredSetup = Settings.Secure.getInt(getBaseContext() 662 .getContentResolver(), 663 Settings.Secure.USER_SETUP_PERSONALIZATION_STATE) == 664 Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; 665 return isInSetup || isInDeferredSetup; 666 } catch (Settings.SettingNotFoundException e) { 667 Slog.w(TAG, "Failed to check if the user is in restore: " + e); 668 return false; 669 } 670 } 671 672 @VisibleForTesting 673 void setBackupManagerForTesting(BackupManager backupManager) { 674 mBackupManager = backupManager; 675 } 676 }