| package com.android.launcher3; |
| |
| import static com.android.launcher3.LauncherPrefs.NO_DB_FILES_RESTORED; |
| |
| import android.app.backup.BackupAgent; |
| import android.app.backup.BackupDataInput; |
| import android.app.backup.BackupDataOutput; |
| import android.os.ParcelFileDescriptor; |
| |
| import com.android.launcher3.logging.FileLog; |
| import com.android.launcher3.provider.RestoreDbTask; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.stream.Collectors; |
| |
| public class LauncherBackupAgent extends BackupAgent { |
| private static final String TAG = "LauncherBackupAgent"; |
| private static final String DB_FILE_PREFIX = "launcher"; |
| private static final String DB_FILE_SUFFIX = ".db"; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| // Set the log dir as LauncherAppState is not initialized during restore. |
| FileLog.setDir(getFilesDir()); |
| } |
| |
| @Override |
| public void onRestore( |
| BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { |
| // Doesn't do incremental backup/restore |
| } |
| |
| @Override |
| public void onRestoreFile(ParcelFileDescriptor data, long size, File destination, int type, |
| long mode, long mtime) throws IOException { |
| // Remove old files which might contain obsolete attributes like idp_grid_name in shared |
| // preference that will obstruct backup's attribute from writing to shared preferences. |
| if (destination.delete()) { |
| FileLog.d(TAG, "onRestoreFile: Removed obsolete file " + destination); |
| } |
| super.onRestoreFile(data, size, destination, type, mode, mtime); |
| } |
| |
| @Override |
| public void onBackup( |
| ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { |
| // Doesn't do incremental backup/restore |
| } |
| |
| @Override |
| public void onRestoreFinished() { |
| RestoreDbTask.setPending(this); |
| FileLog.d(TAG, "onRestoreFinished: set pending for RestoreDbTask"); |
| markIfFilesWereNotActuallyRestored(); |
| } |
| |
| /** |
| * When restore is finished, we check to see if any db files were successfully restored. If not, |
| * our restore will fail later, but will report a different cause. This is important to split |
| * out the metric failures that are launcher's fault, and those that are due to bugs in the |
| * backup/restore code itself. |
| */ |
| private void markIfFilesWereNotActuallyRestored() { |
| File directory = new File(getDatabasePath(InvariantDeviceProfile.INSTANCE.get(this).dbFile) |
| .getParent()); |
| if (!directory.exists()) { |
| FileLog.e(TAG, "restore failed as target database directory doesn't exist"); |
| } else { |
| // Check for any db file that was restored, and collect as list |
| String fileNames = Arrays.stream(directory.listFiles()) |
| .map(File::getName) |
| .filter(n -> n.startsWith(DB_FILE_PREFIX) && n.endsWith(DB_FILE_SUFFIX)) |
| .collect(Collectors.joining(", ")); |
| if (fileNames.isBlank()) { |
| FileLog.e(TAG, "no database files were successfully restored"); |
| LauncherPrefs.get(this).putSync(NO_DB_FILES_RESTORED.to(true)); |
| } else { |
| FileLog.d(TAG, "database files successfully restored: " + fileNames); |
| } |
| } |
| } |
| } |