Merge "Removing flake when 2 long-press events happened on single long press" into ub-launcher3-master
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ab6393a..71d77fc 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -43,6 +43,7 @@
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -51,10 +52,18 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsQuickstep extends AbstractQuickStepTest {
+    private int mLauncherPid;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
         TaplTestsLauncher3.initialize(this);
+        mLauncherPid = mLauncher.getPid();
+    }
+
+    @After
+    public void teardown() {
+        assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
     }
 
     private void startTestApps() throws Exception {
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 83b8599..6223760 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -23,12 +23,14 @@
 
 import android.database.sqlite.SQLiteDatabase;
 
+import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.LauncherModelHelper;
 import com.android.launcher3.util.LauncherRoboTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.LooperMode;
 
 /**
@@ -44,7 +46,8 @@
     @Before
     public void setUp() {
         mModelHelper = new LauncherModelHelper();
-        mDb = mModelHelper.provider.getDbWithRestoreDbTask();
+        RestoreDbTask.setPending(RuntimeEnvironment.application, true);
+        mDb = mModelHelper.provider.getDb();
     }
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 655055c..e8b7157 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.provider.Settings;
 
@@ -276,6 +277,8 @@
         Context context = RuntimeEnvironment.application;
         LauncherSettings.Settings.call(context.getContentResolver(),
                 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+        LauncherSettings.Settings.call(context.getContentResolver(),
+                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
         int[][][] ids = new int[typeArray.length][][];
 
         for (int i = 0; i < typeArray.length; i++) {
@@ -312,7 +315,6 @@
         idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
         idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
 
-        provider.setAllowLoadDefaultFavorites(true);
         Settings.Secure.putString(context.getContentResolver(),
                 "launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
 
@@ -340,4 +342,20 @@
         filter.addCategory(Intent.CATEGORY_DEFAULT);
         spm.addIntentFilterForActivity(cn, filter);
     }
+
+    /**
+     * An extension of LauncherProvider backed up by in-memory database.
+     */
+    public static class TestLauncherProvider extends LauncherProvider {
+
+        @Override
+        public boolean onCreate() {
+            return true;
+        }
+
+        public SQLiteDatabase getDb() {
+            createDbIfNotExists();
+            return mOpenHelper.getWritableDatabase();
+        }
+    }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
deleted file mode 100644
index 8ae6528..0000000
--- a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.provider.RestoreDbTask;
-
-/**
- * An extension of LauncherProvider backed up by in-memory database.
- */
-public class TestLauncherProvider extends LauncherProvider {
-
-    private boolean mAllowLoadDefaultFavorites;
-
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    protected synchronized void createDbIfNotExists() {
-        if (mOpenHelper == null) {
-            mOpenHelper = new MyDatabaseHelper(getContext(), mAllowLoadDefaultFavorites);
-        }
-    }
-
-    public void setAllowLoadDefaultFavorites(boolean allowLoadDefaultFavorites) {
-        mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
-    }
-
-    public SQLiteDatabase getDb() {
-        createDbIfNotExists();
-        return mOpenHelper.getWritableDatabase();
-    }
-
-    public SQLiteDatabase getDbWithRestoreDbTask() {
-        RestoreDbTask.setPending(getContext(), true);
-        super.createDbIfNotExists();
-        return mOpenHelper.getWritableDatabase();
-    }
-
-    private static class MyDatabaseHelper extends DatabaseHelper {
-
-        private final boolean mAllowLoadDefaultFavorites;
-
-        MyDatabaseHelper(Context context, boolean allowLoadDefaultFavorites) {
-            super(context, null);
-            mAllowLoadDefaultFavorites = allowLoadDefaultFavorites;
-            initIds();
-        }
-
-        @Override
-        public long getDefaultUserSerial() {
-            return 0;
-        }
-
-        @Override
-        protected void onEmptyDbCreated() {
-            if (mAllowLoadDefaultFavorites) {
-                super.onEmptyDbCreated();
-            }
-        }
-
-        @Override
-        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-    }
-}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b589560..b0ab35c 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -18,6 +18,7 @@
 
 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.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 @@
     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 @@
                 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 f0bae02..89f0a3d 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -78,6 +78,7 @@
         }
 
         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 81dcba3..75609fe 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -136,6 +136,10 @@
     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 186293f..976d7ba 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -29,6 +29,10 @@
 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 @@
     // Set<String> 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 @@
         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 7ee30cc..407ff31 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 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 8b2ee36..6c18747 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.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 @@
         }
         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;
+        }
+    }
 }