From ad5f24072ce72b120a88da18c6aa83d2eadece41 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Tue, 10 Dec 2019 14:56:34 -0800 Subject: [PATCH] hook database restore with restore session 1. Create feature flag for new backup & restore flow. 2. For each restore session (install reason is restore), if its creation time is newer than the one we have in SharedPreference, we update the entry and restores favorite table from backup. 3. The restore operation is debounced so that when multiple restore session is created within a small amount of time, only the last invocation will get executed. Bug: 141472083 Change-Id: I7b5b63ec28741ba2b02ccfd13f591c961362ba36 Test: 1. apply on master, build & flash on physical device. 2. factory reset the device. 3. go through SuW, perform restore, exit without adding work profile. 4. settings -> account -> add work profile account. 5. finish work profile setup, verify work profiles is restored as well. --- .../android/launcher3/LauncherProvider.java | 11 ++++-- .../launcher3/SessionCommitReceiver.java | 1 + .../launcher3/config/FeatureFlags.java | 4 +++ .../launcher3/pm/InstallSessionHelper.java | 34 +++++++++++++++++++ .../launcher3/provider/RestoreDbTask.java | 3 ++ .../launcher3/util/PackageManagerHelper.java | 14 ++++++++ 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index b58956078d..b0ab35c5b0 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -18,6 +18,7 @@ package com.android.launcher3; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import static com.android.launcher3.provider.LauncherDbUtils.tableExists; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.annotation.TargetApi; import android.app.backup.BackupManager; @@ -45,6 +46,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; @@ -87,6 +89,8 @@ public class LauncherProvider extends ContentProvider { private static final boolean LOGD = false; private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json"; + private static final String TOKEN_RESTORE_BACKUP_TABLE = "restore_backup_table"; + private static final long RESTORE_BACKUP_TABLE_DELAY = 60000; /** * Represents the schema of the database. Changes in scheme need not be backwards compatible. @@ -388,8 +392,11 @@ public class LauncherProvider extends ContentProvider { return null; } case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: { - RestoreDbTask.restoreIfPossible( - getContext(), mOpenHelper, new BackupManager(getContext())); + final Handler handler = MODEL_EXECUTOR.getHandler(); + handler.removeCallbacksAndMessages(TOKEN_RESTORE_BACKUP_TABLE); + handler.postDelayed(() -> RestoreDbTask.restoreIfPossible( + getContext(), mOpenHelper, new BackupManager(getContext())), + TOKEN_RESTORE_BACKUP_TABLE, RESTORE_BACKUP_TABLE_DELAY); return null; } } diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index f0bae021d5..89f0a3d286 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -78,6 +78,7 @@ public class SessionCommitReceiver extends BroadcastReceiver { } InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context); + packageInstallerCompat.restoreDbIfApplicable(info); if (TextUtils.isEmpty(info.getAppPackageName()) || info.getInstallReason() != PackageManager.INSTALL_REASON_USER || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 81dcba3a17..75609fe68e 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -136,6 +136,10 @@ public final class FeatureFlags { public static final TogglableFlag ENABLE_OVERVIEW_ACTIONS = new TogglableFlag( "ENABLE_OVERVIEW_ACTIONS", false, "Show app actions in Overview"); + public static final TogglableFlag ENABLE_DATABASE_RESTORE = new TogglableFlag( + "ENABLE_DATABASE_RESTORE", true, + "Enable database restore when new restore session is created"); + public static void initialize(Context context) { // Avoid the disk read for user builds if (Utilities.IS_DEBUG_DEVICE) { diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index 186293f700..976d7ba5a0 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -29,6 +29,10 @@ import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.android.launcher3.LauncherSettings; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -52,6 +56,8 @@ public class InstallSessionHelper { // Set of session ids of promise icons that have been added to the home screen // as FLAG_PROMISE_NEW_INSTALLS. protected static final String PROMISE_ICON_IDS = "promise_icon_ids"; + public static final String KEY_INSTALL_SESSION_CREATED_TIMESTAMP = + "key_install_session_created_timestamp"; private static final boolean DEBUG = false; @@ -159,6 +165,34 @@ public class InstallSessionHelper { return list; } + /** + * Attempt to restore workspace layout if the session is triggered due to device restore and it + * has a newer timestamp. + */ + public boolean restoreDbIfApplicable(@NonNull final SessionInfo info) { + if (!Utilities.ATLEAST_OREO || !FeatureFlags.ENABLE_DATABASE_RESTORE.get()) { + return false; + } + if (isRestore(info) && hasNewerTimestamp(mAppContext, info)) { + LauncherSettings.Settings.call(mAppContext.getContentResolver(), + LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE); + return true; + } + return false; + } + + @RequiresApi(26) + private static boolean isRestore(@NonNull final SessionInfo info) { + return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE; + } + + private static boolean hasNewerTimestamp( + @NonNull final Context context, @NonNull final SessionInfo info) { + return PackageManagerHelper.getSessionCreatedTimeInMillis(info) + > Utilities.getDevicePrefs(context).getLong( + KEY_INSTALL_SESSION_CREATED_TIMESTAMP, 0); + } + public boolean promiseIconAddedForId(int sessionId) { return mPromiseIconIds.contains(sessionId); } diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 7ee30cca77..407ff3153d 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -16,6 +16,7 @@ package com.android.launcher3.provider; +import static com.android.launcher3.pm.InstallSessionHelper.KEY_INSTALL_SESSION_CREATED_TIMESTAMP; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import android.app.backup.BackupManager; @@ -86,6 +87,8 @@ public class RestoreDbTask { */ public static boolean restoreIfPossible(@NonNull Context context, @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager) { + Utilities.getDevicePrefs(context).edit().putLong( + KEY_INSTALL_SESSION_CREATED_TIMESTAMP, System.currentTimeMillis()).apply(); final SQLiteDatabase db = helper.getWritableDatabase(); try (SQLiteTransaction t = new SQLiteTransaction(db)) { RestoreDbTask task = new RestoreDbTask(); diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index 8b2ee36cb9..6c187475d3 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -16,6 +16,7 @@ package com.android.launcher3.util; +import static android.content.pm.PackageInstaller.SessionInfo; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import android.app.AppOpsManager; @@ -44,6 +45,8 @@ import android.util.Log; import android.util.Pair; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; @@ -345,4 +348,15 @@ public class PackageManagerHelper { } return false; } + + /** + * Returns the created time in millis of given session info. Returns 0 if not available. + */ + public static long getSessionCreatedTimeInMillis(@NonNull final SessionInfo info) { + try { + return (long) SessionInfo.class.getDeclaredMethod("getCreatedMillis").invoke(info); + } catch (Exception e) { + return 0; + } + } }