Merge "Fixing ConcurrentModificationException in FirstScreenBroadcast" into tm-dev am: 69149d07ff

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/18580078

Change-Id: I23aea31514feac769609ae1b1093421accab94d0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 5fac7cf..9e91b9d 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -20,6 +20,7 @@
 import static android.os.Process.myUserHandle;
 
 import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import static java.util.stream.Collectors.groupingBy;
 import static java.util.stream.Collectors.mapping;
@@ -31,13 +32,18 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -78,6 +84,7 @@
      * Sends a broadcast to all package installers that have items with active sessions on the users
      * first screen.
      */
+    @WorkerThread
     public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
         UserHandle myUser = myUserHandle();
         mSessionInfoForPackage
@@ -95,6 +102,7 @@
      * @param packages List of packages with active sessions for this package installer.
      * @param firstScreenItems List of items on the first screen.
      */
+    @WorkerThread
     private void sendBroadcastToInstaller(Context context, String installerPackageName,
             Set<String> packages, List<ItemInfo> firstScreenItems) {
         Set<String> folderItems = new HashSet<>();
@@ -106,7 +114,7 @@
             if (info instanceof FolderInfo) {
                 FolderInfo folderInfo = (FolderInfo) info;
                 String folderItemInfoPackage;
-                for (ItemInfo folderItemInfo : folderInfo.contents) {
+                for (ItemInfo folderItemInfo : cloneOnMainThread(folderInfo.contents)) {
                     folderItemInfoPackage = getPackageName(folderItemInfo);
                     if (folderItemInfoPackage != null
                             && packages.contains(folderItemInfoPackage)) {
@@ -170,4 +178,17 @@
             Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
         }
     }
+
+    /**
+     * Clone the provided list on UI thread. This is used for {@link FolderInfo#contents} which
+     * is always modified on UI thread.
+     */
+    @AnyThread
+    private static List<WorkspaceItemInfo> cloneOnMainThread(ArrayList<WorkspaceItemInfo> list) {
+        try {
+            return MAIN_EXECUTOR.submit(() -> new ArrayList(list)).get();
+        } catch (Exception e) {
+            return Collections.emptyList();
+        }
+    }
 }