1d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate/* 2d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * Copyright (C) 2016 The Android Open Source Project 3d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * 4d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 5d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * you may not use this file except in compliance with the License. 6d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * You may obtain a copy of the License at 7d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * 8d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * http://www.apache.org/licenses/LICENSE-2.0 9d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * 10d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * Unless required by applicable law or agreed to in writing, software 11d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 12d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * See the License for the specific language governing permissions and 14d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate * limitations under the License. 15d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate */ 16d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 17d7faf53605838487cace9979e577005cc7c8cabcChristopher Tatepackage com.android.wallpaperbackup; 18d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 19ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tateimport static android.app.WallpaperManager.FLAG_LOCK; 20ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tateimport static android.app.WallpaperManager.FLAG_SYSTEM; 21ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate 222471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tateimport android.app.AppGlobals; 23d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.app.WallpaperManager; 24d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.app.backup.BackupAgent; 25d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.app.backup.BackupDataInput; 26d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.app.backup.BackupDataOutput; 27d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.app.backup.FullBackupDataOutput; 282471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tateimport android.content.ComponentName; 29d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.content.Context; 30ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tateimport android.content.SharedPreferences; 312471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tateimport android.content.pm.IPackageManager; 322471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tateimport android.content.pm.PackageInfo; 3361f2d63889696252f10e7a3f2c999df5979a3021Christopher Tateimport android.graphics.Rect; 34d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.os.Environment; 35ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tateimport android.os.FileUtils; 36d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.os.ParcelFileDescriptor; 372471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tateimport android.os.RemoteException; 38d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.os.UserHandle; 39d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport android.util.Slog; 4061f2d63889696252f10e7a3f2c999df5979a3021Christopher Tateimport android.util.Xml; 4161f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate 42f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tateimport libcore.io.IoUtils; 43f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate 4461f2d63889696252f10e7a3f2c999df5979a3021Christopher Tateimport org.xmlpull.v1.XmlPullParser; 45d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 46d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport java.io.File; 4761f2d63889696252f10e7a3f2c999df5979a3021Christopher Tateimport java.io.FileInputStream; 48d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport java.io.FileOutputStream; 49d7faf53605838487cace9979e577005cc7c8cabcChristopher Tateimport java.io.IOException; 5061f2d63889696252f10e7a3f2c999df5979a3021Christopher Tateimport java.nio.charset.StandardCharsets; 51d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 52d7faf53605838487cace9979e577005cc7c8cabcChristopher Tatepublic class WallpaperBackupAgent extends BackupAgent { 53d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate private static final String TAG = "WallpaperBackup"; 54ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate private static final boolean DEBUG = false; 55d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 56d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // NB: must be kept in sync with WallpaperManagerService but has no 5761f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate // compile-time visibility. 58d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 59d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // Target filenames within the system's wallpaper directory 60d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate static final String WALLPAPER = "wallpaper_orig"; 61bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate static final String WALLPAPER_LOCK = "wallpaper_lock_orig"; 62d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate static final String WALLPAPER_INFO = "wallpaper_info.xml"; 63d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 64d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // Names of our local-data stage files/links 65d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate static final String IMAGE_STAGE = "wallpaper-stage"; 66bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate static final String LOCK_IMAGE_STAGE = "wallpaper-lock-stage"; 67d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate static final String INFO_STAGE = "wallpaper-info-stage"; 68d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate static final String EMPTY_SENTINEL = "empty"; 69bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate static final String QUOTA_SENTINEL = "quota"; 70d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 71ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate // Not-for-backup bookkeeping 72ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate static final String PREFS_NAME = "wbprefs.xml"; 73ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate static final String SYSTEM_GENERATION = "system_gen"; 74ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate static final String LOCK_GENERATION = "lock_gen"; 75ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate 76bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private File mWallpaperInfo; // wallpaper metadata file 77bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private File mWallpaperFile; // primary wallpaper image file 78bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private File mLockWallpaperFile; // lock wallpaper image file 79bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate 80bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // If this file exists, it means we exceeded our quota last time 81bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private File mQuotaFile; 82bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private boolean mQuotaExceeded; 83d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 84d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate private WallpaperManager mWm; 85d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 86d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate @Override 87d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate public void onCreate() { 88d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate if (DEBUG) { 89d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate Slog.v(TAG, "onCreate()"); 90d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 91d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 92d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate File wallpaperDir = Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM); 93d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate mWallpaperInfo = new File(wallpaperDir, WALLPAPER_INFO); 94d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate mWallpaperFile = new File(wallpaperDir, WALLPAPER); 95bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate mLockWallpaperFile = new File(wallpaperDir, WALLPAPER_LOCK); 96d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate mWm = (WallpaperManager) getSystemService(Context.WALLPAPER_SERVICE); 97bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate 98bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate mQuotaFile = new File(getFilesDir(), QUOTA_SENTINEL); 99bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate mQuotaExceeded = mQuotaFile.exists(); 100bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (DEBUG) { 101bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate Slog.v(TAG, "quota file " + mQuotaFile.getPath() + " exists=" + mQuotaExceeded); 102bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 103d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 104d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 105d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate @Override 106d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate public void onFullBackup(FullBackupDataOutput data) throws IOException { 107d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // To avoid data duplication and disk churn, use links as the stage. 108d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate final File filesDir = getFilesDir(); 109d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate final File infoStage = new File(filesDir, INFO_STAGE); 110d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate final File imageStage = new File (filesDir, IMAGE_STAGE); 111bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE); 112d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate final File empty = new File (filesDir, EMPTY_SENTINEL); 113d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 114d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate try { 115d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // We always back up this 'empty' file to ensure that the absence of 116d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // storable wallpaper imagery still produces a non-empty backup data 117d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // stream, otherwise it'd simply be ignored in preflight. 118d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate FileOutputStream touch = new FileOutputStream(empty); 119d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate touch.close(); 120d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate fullBackupFile(empty, data); 121d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 1226172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); 1236172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final int lastSysGeneration = prefs.getInt(SYSTEM_GENERATION, -1); 1246172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final int lastLockGeneration = prefs.getInt(LOCK_GENERATION, -1); 1255cb5e89d770f474031e4ab30fb7f24c66333590aChristopher Tate 1266172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final int sysGeneration = 1276172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate mWm.getWallpaperIdForUser(FLAG_SYSTEM, UserHandle.USER_SYSTEM); 1286172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final int lockGeneration = 1296172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate mWm.getWallpaperIdForUser(FLAG_LOCK, UserHandle.USER_SYSTEM); 1306172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final boolean sysChanged = (sysGeneration != lastSysGeneration); 1316172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final boolean lockChanged = (lockGeneration != lastLockGeneration); 1325cb5e89d770f474031e4ab30fb7f24c66333590aChristopher Tate 1336172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final boolean sysEligible = mWm.isWallpaperBackupEligible(FLAG_SYSTEM); 1346172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate final boolean lockEligible = mWm.isWallpaperBackupEligible(FLAG_LOCK); 135ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate 136f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate // There might be a latent lock wallpaper file present but unused: don't 137f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate // include it in the backup if that's the case. 138f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate ParcelFileDescriptor lockFd = mWm.getWallpaperFile(FLAG_LOCK, UserHandle.USER_SYSTEM); 139f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate final boolean hasLockWallpaper = (lockFd != null); 140f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate IoUtils.closeQuietly(lockFd); 141f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate 1426172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (DEBUG) { 1436172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate Slog.v(TAG, "sysGen=" + sysGeneration + " : sysChanged=" + sysChanged); 1446172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate Slog.v(TAG, "lockGen=" + lockGeneration + " : lockChanged=" + lockChanged); 1456172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate Slog.v(TAG, "sysEligble=" + sysEligible); 1466172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate Slog.v(TAG, "lockEligible=" + lockEligible); 1476172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate } 1486172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate 1496172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate // only back up the wallpapers if we've been told they're eligible 150a611fdc66b1c72c429cc2c8bf70afab9b5825c0bChristopher Tate if (mWallpaperInfo.exists()) { 1516172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (sysChanged || lockChanged || !infoStage.exists()) { 1526172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (DEBUG) Slog.v(TAG, "New wallpaper configuration; copying"); 1536172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate FileUtils.copyFileOrThrow(mWallpaperInfo, infoStage); 1545f829d287f5c8cd72fee1522b8308fa459adc122Christopher Tate } 155a611fdc66b1c72c429cc2c8bf70afab9b5825c0bChristopher Tate if (DEBUG) Slog.v(TAG, "Storing wallpaper metadata"); 1566172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate fullBackupFile(infoStage, data); 1576172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate } 1586172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (sysEligible && mWallpaperFile.exists()) { 1596172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (sysChanged || !imageStage.exists()) { 1606172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (DEBUG) Slog.v(TAG, "New system wallpaper; copying"); 1616172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate FileUtils.copyFileOrThrow(mWallpaperFile, imageStage); 1625f829d287f5c8cd72fee1522b8308fa459adc122Christopher Tate } 163a611fdc66b1c72c429cc2c8bf70afab9b5825c0bChristopher Tate if (DEBUG) Slog.v(TAG, "Storing system wallpaper image"); 1646172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate fullBackupFile(imageStage, data); 1656172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate prefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply(); 1666172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate } 167bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate 1686172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate // Don't try to store the lock image if we overran our quota last time 1696172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (lockEligible && hasLockWallpaper && mLockWallpaperFile.exists() && !mQuotaExceeded) { 1706172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (lockChanged || !lockImageStage.exists()) { 1716172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate if (DEBUG) Slog.v(TAG, "New lock wallpaper; copying"); 1726172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate FileUtils.copyFileOrThrow(mLockWallpaperFile, lockImageStage); 173d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 174a611fdc66b1c72c429cc2c8bf70afab9b5825c0bChristopher Tate if (DEBUG) Slog.v(TAG, "Storing lock wallpaper image"); 1756172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate fullBackupFile(lockImageStage, data); 1766172266154e9071abba2c0aab9ffb31e0ec8c239Christopher Tate prefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply(); 177d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 178d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } catch (Exception e) { 1795f829d287f5c8cd72fee1522b8308fa459adc122Christopher Tate Slog.e(TAG, "Unable to back up wallpaper", e); 180d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } finally { 181bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // Even if this time we had to back off on attempting to store the lock image 182bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // due to exceeding the data quota, try again next time. This will alternate 183bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // between "try both" and "only store the primary image" until either there 184bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // is no lock image to store, or the quota is raised, or both fit under the 185bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // quota. 186bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate mQuotaFile.delete(); 187bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 188bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 189bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate 190bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate @Override 191bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { 192bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (DEBUG) { 193bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate Slog.i(TAG, "Quota exceeded (" + backupDataBytes + " vs " + quotaBytes + ')'); 194bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 195bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate try (FileOutputStream f = new FileOutputStream(mQuotaFile)) { 196bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate f.write(0); 197bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } catch (Exception e) { 198bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate Slog.w(TAG, "Unable to record quota-exceeded: " + e.getMessage()); 199d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 200d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 201d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 202d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // We use the default onRestoreFile() implementation that will recreate our stage files, 20361f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate // then post-process in onRestoreFinished() to apply the new wallpaper. 204d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate @Override 205d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate public void onRestoreFinished() { 206d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate if (DEBUG) { 207d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate Slog.v(TAG, "onRestoreFinished()"); 208d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 209bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate final File filesDir = getFilesDir(); 210bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate final File infoStage = new File(filesDir, INFO_STAGE); 211bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate final File imageStage = new File (filesDir, IMAGE_STAGE); 212bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE); 213d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 214f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate // If we restored separate lock imagery, the system wallpaper should be 215f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate // applied as system-only; but if there's no separate lock image, make 216f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate // sure to apply the restored system wallpaper as both. 217f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate final int sysWhich = FLAG_SYSTEM | (lockImageStage.exists() ? 0 : FLAG_LOCK); 218f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate 219d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate try { 220d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // It is valid for the imagery to be absent; it means that we were not permitted 221bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // to back up the original image on the source device, or there was no user-supplied 222bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // wallpaper image present. 223f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate restoreFromStage(imageStage, infoStage, "wp", sysWhich); 224f47eff7343866d9cbcd7e9ba49d50b8f601743bdChristopher Tate restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); 2252471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate 2262471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate // And reset to the wallpaper service we should be using 2272471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); 2282471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (servicePackageExists(wpService)) { 2292471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (DEBUG) { 2302471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate Slog.i(TAG, "Using wallpaper service " + wpService); 2312471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 2322471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM); 23396eb5b724bab31125e15eef2c926f6648567c3a1Bryan Mawhinney if (!lockImageStage.exists()) { 23496eb5b724bab31125e15eef2c926f6648567c3a1Bryan Mawhinney // We have a live wallpaper and no static lock image, 23596eb5b724bab31125e15eef2c926f6648567c3a1Bryan Mawhinney // allow live wallpaper to show "through" on lock screen. 23696eb5b724bab31125e15eef2c926f6648567c3a1Bryan Mawhinney mWm.clear(FLAG_LOCK); 23796eb5b724bab31125e15eef2c926f6648567c3a1Bryan Mawhinney } 2382471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } else { 2392471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (DEBUG) { 2402471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate Slog.v(TAG, "Can't use wallpaper service " + wpService); 2412471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 2422471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 243d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } catch (Exception e) { 244d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage()); 245d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } finally { 246d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate if (DEBUG) { 247ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate Slog.v(TAG, "Restore finished; clearing backup bookkeeping"); 248d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 249d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate infoStage.delete(); 250d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate imageStage.delete(); 251bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate lockImageStage.delete(); 252ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate 253ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); 254ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate prefs.edit() 255ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate .putInt(SYSTEM_GENERATION, -1) 256ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate .putInt(LOCK_GENERATION, -1) 257ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate .commit(); 258bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 259bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 260bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate 261bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private void restoreFromStage(File stage, File info, String hintTag, int which) 262bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate throws IOException { 263bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (stage.exists()) { 264bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // Parse the restored info file to find the crop hint. Note that this currently 265bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate // relies on a priori knowledge of the wallpaper info file schema. 266bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate Rect cropHint = parseCropHint(info, hintTag); 267bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (cropHint != null) { 268ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate Slog.i(TAG, "Got restored wallpaper; applying which=" + which); 269bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (DEBUG) { 270ac482ebb00c8217d86227dde08b33a37b04ece68Christopher Tate Slog.v(TAG, "Restored crop hint " + cropHint); 271bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 272bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate try (FileInputStream in = new FileInputStream(stage)) { 273dd7110db2b54c59ff758e25a26a06178b57c2469Christopher Tate mWm.setStream(in, cropHint.isEmpty() ? null : cropHint, true, which); 274bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } finally {} // auto-closes 'in' 275bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate } 276d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 277d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 278d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 279bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate private Rect parseCropHint(File wallpaperInfo, String sectionTag) { 28061f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate Rect cropHint = new Rect(); 28161f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { 28261f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate XmlPullParser parser = Xml.newPullParser(); 28361f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate parser.setInput(stream, StandardCharsets.UTF_8.name()); 28461f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate 28561f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate int type; 28661f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate do { 28761f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate type = parser.next(); 28861f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate if (type == XmlPullParser.START_TAG) { 28961f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate String tag = parser.getName(); 290bf13ccf4b7d7e50f24f6eefeeaa34b7a09e25ec8Christopher Tate if (sectionTag.equals(tag)) { 29161f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate cropHint.left = getAttributeInt(parser, "cropLeft", 0); 29261f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate cropHint.top = getAttributeInt(parser, "cropTop", 0); 29361f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate cropHint.right = getAttributeInt(parser, "cropRight", 0); 29461f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); 29561f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } 29661f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } 29761f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } while (type != XmlPullParser.END_DOCUMENT); 29861f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } catch (Exception e) { 29961f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate // Whoops; can't process the info file at all. Report failure. 3002471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate Slog.w(TAG, "Failed to parse restored crop: " + e.getMessage()); 30161f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate return null; 30261f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } 30361f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate 30461f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate return cropHint; 30561f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } 30661f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate 3072471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) { 3082471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate ComponentName name = null; 3092471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { 3102471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate final XmlPullParser parser = Xml.newPullParser(); 3112471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate parser.setInput(stream, StandardCharsets.UTF_8.name()); 3122471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate 3132471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate int type; 3142471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate do { 3152471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate type = parser.next(); 3162471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (type == XmlPullParser.START_TAG) { 3172471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate String tag = parser.getName(); 3182471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (sectionTag.equals(tag)) { 3192471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate final String parsedName = parser.getAttributeValue(null, "component"); 3202471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate name = (parsedName != null) 3212471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate ? ComponentName.unflattenFromString(parsedName) 3222471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate : null; 3232471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate break; 3242471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3252471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3262471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } while (type != XmlPullParser.END_DOCUMENT); 3272471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } catch (Exception e) { 3282471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate // Whoops; can't process the info file at all. Report failure. 3292471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate Slog.w(TAG, "Failed to parse restored component: " + e.getMessage()); 3302471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate return null; 3312471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3322471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate return name; 3332471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3342471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate 33561f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate private int getAttributeInt(XmlPullParser parser, String name, int defValue) { 33661f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate final String value = parser.getAttributeValue(null, name); 33761f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate return (value == null) ? defValue : Integer.parseInt(value); 33861f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate } 33961f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate 3402471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate private boolean servicePackageExists(ComponentName comp) { 3412471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate try { 3422471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate if (comp != null) { 3432471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate final IPackageManager pm = AppGlobals.getPackageManager(); 3442471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate final PackageInfo info = pm.getPackageInfo(comp.getPackageName(), 3452471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate 0, UserHandle.USER_SYSTEM); 3462471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate return (info != null); 3472471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3482471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } catch (RemoteException e) { 3492471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate Slog.e(TAG, "Unable to contact package manager"); 3502471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3512471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate return false; 3522471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate } 3532471a3724f5e95752bbd03ab1dac90de338a7f08Christopher Tate 354d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // 35561f2d63889696252f10e7a3f2c999df5979a3021Christopher Tate // Key/value API: abstract, therefore required; but not used 356d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // 357d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 358d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate @Override 359d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 360d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate ParcelFileDescriptor newState) throws IOException { 361d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // Intentionally blank 362d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 363d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate 364d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate @Override 365d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) 366d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate throws IOException { 367d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate // Intentionally blank 368d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate } 369d7faf53605838487cace9979e577005cc7c8cabcChristopher Tate}