• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3;
2 
3 import static com.android.launcher3.LauncherPrefs.NO_DB_FILES_RESTORED;
4 
5 import android.app.backup.BackupAgent;
6 import android.app.backup.BackupDataInput;
7 import android.app.backup.BackupDataOutput;
8 import android.os.ParcelFileDescriptor;
9 
10 import com.android.launcher3.logging.FileLog;
11 import com.android.launcher3.provider.RestoreDbTask;
12 
13 import java.io.File;
14 import java.io.IOException;
15 import java.util.Arrays;
16 import java.util.stream.Collectors;
17 
18 public class LauncherBackupAgent extends BackupAgent {
19     private static final String TAG = "LauncherBackupAgent";
20     private static final String DB_FILE_PREFIX = "launcher";
21     private static final String DB_FILE_SUFFIX = ".db";
22 
23     @Override
onCreate()24     public void onCreate() {
25         super.onCreate();
26         // Set the log dir as LauncherAppState is not initialized during restore.
27         FileLog.setDir(getFilesDir());
28     }
29 
30     @Override
onRestore( BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)31     public void onRestore(
32             BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
33         // Doesn't do incremental backup/restore
34     }
35 
36     @Override
onRestoreFile(ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)37     public void onRestoreFile(ParcelFileDescriptor data, long size, File destination, int type,
38             long mode, long mtime) throws IOException {
39         // Remove old files which might contain obsolete attributes like idp_grid_name in shared
40         // preference that will obstruct backup's attribute from writing to shared preferences.
41         if (destination.delete()) {
42             FileLog.d(TAG, "onRestoreFile: Removed obsolete file " + destination);
43         }
44         super.onRestoreFile(data, size, destination, type, mode, mtime);
45     }
46 
47     @Override
onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)48     public void onBackup(
49             ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
50         // Doesn't do incremental backup/restore
51     }
52 
53     @Override
onRestoreFinished()54     public void onRestoreFinished() {
55         RestoreDbTask.setPending(this);
56         FileLog.d(TAG, "onRestoreFinished: set pending for RestoreDbTask");
57         markIfFilesWereNotActuallyRestored();
58     }
59 
60     /**
61      * When restore is finished, we check to see if any db files were successfully restored. If not,
62      * our restore will fail later, but will report a different cause. This is important to split
63      * out the metric failures that are launcher's fault, and those that are due to bugs in the
64      * backup/restore code itself.
65      */
markIfFilesWereNotActuallyRestored()66     private void markIfFilesWereNotActuallyRestored() {
67         File directory = new File(getDatabasePath(InvariantDeviceProfile.INSTANCE.get(this).dbFile)
68                 .getParent());
69         if (!directory.exists()) {
70             FileLog.e(TAG, "restore failed as target database directory doesn't exist");
71         } else {
72             // Check for any db file that was restored, and collect as list
73             String fileNames = Arrays.stream(directory.listFiles())
74                     .map(File::getName)
75                     .filter(n -> n.startsWith(DB_FILE_PREFIX) && n.endsWith(DB_FILE_SUFFIX))
76                     .collect(Collectors.joining(", "));
77             if (fileNames.isBlank()) {
78                 FileLog.e(TAG, "no database files were successfully restored");
79                 LauncherPrefs.get(this).putSync(NO_DB_FILES_RESTORED.to(true));
80             } else {
81                 FileLog.d(TAG, "database files successfully restored: " + fileNames);
82             }
83         }
84     }
85 }
86