1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.model;
18 
19 import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
20 import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
21 import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
22 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
23 import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
24 import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
25 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
26 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
27 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
28 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
29 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
30 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
31 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
32 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
33 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
34 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
35 
36 import android.appwidget.AppWidgetProviderInfo;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.pm.LauncherActivityInfo;
41 import android.content.pm.LauncherApps;
42 import android.content.pm.PackageInstaller;
43 import android.content.pm.PackageInstaller.SessionInfo;
44 import android.content.pm.PackageManager;
45 import android.content.pm.ShortcutInfo;
46 import android.os.Bundle;
47 import android.os.Trace;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.provider.Settings;
51 import android.util.ArrayMap;
52 import android.util.Log;
53 import android.util.LongSparseArray;
54 
55 import androidx.annotation.NonNull;
56 import androidx.annotation.Nullable;
57 import androidx.annotation.VisibleForTesting;
58 import androidx.annotation.WorkerThread;
59 
60 import com.android.launcher3.Flags;
61 import com.android.launcher3.LauncherAppState;
62 import com.android.launcher3.LauncherModel;
63 import com.android.launcher3.LauncherPrefs;
64 import com.android.launcher3.LauncherSettings.Favorites;
65 import com.android.launcher3.Utilities;
66 import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
67 import com.android.launcher3.config.FeatureFlags;
68 import com.android.launcher3.folder.Folder;
69 import com.android.launcher3.folder.FolderGridOrganizer;
70 import com.android.launcher3.folder.FolderNameInfos;
71 import com.android.launcher3.folder.FolderNameProvider;
72 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
73 import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
74 import com.android.launcher3.icons.IconCache;
75 import com.android.launcher3.icons.LauncherActivityCachingLogic;
76 import com.android.launcher3.icons.ShortcutCachingLogic;
77 import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
78 import com.android.launcher3.logging.FileLog;
79 import com.android.launcher3.model.data.AppInfo;
80 import com.android.launcher3.model.data.AppPairInfo;
81 import com.android.launcher3.model.data.CollectionInfo;
82 import com.android.launcher3.model.data.FolderInfo;
83 import com.android.launcher3.model.data.IconRequestInfo;
84 import com.android.launcher3.model.data.ItemInfo;
85 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
86 import com.android.launcher3.model.data.WorkspaceItemInfo;
87 import com.android.launcher3.pm.InstallSessionHelper;
88 import com.android.launcher3.pm.PackageInstallInfo;
89 import com.android.launcher3.pm.UserCache;
90 import com.android.launcher3.shortcuts.ShortcutKey;
91 import com.android.launcher3.shortcuts.ShortcutRequest;
92 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
93 import com.android.launcher3.util.ApiWrapper;
94 import com.android.launcher3.util.ComponentKey;
95 import com.android.launcher3.util.IOUtils;
96 import com.android.launcher3.util.IntArray;
97 import com.android.launcher3.util.IntSet;
98 import com.android.launcher3.util.LooperIdleLock;
99 import com.android.launcher3.util.PackageManagerHelper;
100 import com.android.launcher3.util.PackageUserKey;
101 import com.android.launcher3.util.TraceHelper;
102 import com.android.launcher3.widget.WidgetInflater;
103 
104 import java.util.ArrayList;
105 import java.util.HashMap;
106 import java.util.HashSet;
107 import java.util.List;
108 import java.util.Map;
109 import java.util.Objects;
110 import java.util.Set;
111 import java.util.concurrent.CancellationException;
112 
113 /**
114  * Runnable for the thread that loads the contents of the launcher:
115  *   - workspace icons
116  *   - widgets
117  *   - all apps icons
118  *   - deep shortcuts within apps
119  */
120 @SuppressWarnings("NewApi")
121 public class LoaderTask implements Runnable {
122     private static final String TAG = "LoaderTask";
123     public static final String SMARTSPACE_ON_HOME_SCREEN = "pref_smartspace_home_screen";
124 
125     private static final boolean DEBUG = true;
126 
127     @NonNull
128     protected final LauncherAppState mApp;
129     private final AllAppsList mBgAllAppsList;
130     protected final BgDataModel mBgDataModel;
131     private final ModelDelegate mModelDelegate;
132     private boolean mIsRestoreFromBackup;
133 
134     private FirstScreenBroadcast mFirstScreenBroadcast;
135 
136     @NonNull
137     private final BaseLauncherBinder mLauncherBinder;
138 
139     private final LauncherApps mLauncherApps;
140     private final UserManager mUserManager;
141     private final UserCache mUserCache;
142     private final PackageManagerHelper mPmHelper;
143 
144     private final InstallSessionHelper mSessionHelper;
145     private final IconCache mIconCache;
146 
147     private final UserManagerState mUserManagerState;
148     protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
149     private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
150     private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
151 
152     private boolean mStopped;
153 
154     private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
155     private boolean mItemsDeleted = false;
156     private String mDbName;
157 
LoaderTask(@onNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder)158     public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
159             ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
160         this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
161     }
162 
163     @VisibleForTesting
LoaderTask(@onNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder, UserManagerState userManagerState)164     LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
165             ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
166             UserManagerState userManagerState) {
167         mApp = app;
168         mBgAllAppsList = bgAllAppsList;
169         mBgDataModel = bgModel;
170         mModelDelegate = modelDelegate;
171         mLauncherBinder = launcherBinder;
172         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
173         mUserManager = mApp.getContext().getSystemService(UserManager.class);
174         mUserCache = UserCache.INSTANCE.get(mApp.getContext());
175         mPmHelper = PackageManagerHelper.INSTANCE.get(mApp.getContext());
176         mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
177         mIconCache = mApp.getIconCache();
178         mUserManagerState = userManagerState;
179         mInstallingPkgsCached = null;
180     }
181 
waitForIdle()182     protected synchronized void waitForIdle() {
183         // Wait until the either we're stopped or the other threads are done.
184         // This way we don't start loading all apps until the workspace has settled
185         // down.
186         LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this);
187         // Just in case mFlushingWorkerThread changes but we aren't woken up,
188         // wait no longer than 1sec at a time
189         while (!mStopped && idleLock.awaitLocked(1000));
190     }
191 
verifyNotStopped()192     private synchronized void verifyNotStopped() throws CancellationException {
193         if (mStopped) {
194             throw new CancellationException("Loader stopped");
195         }
196     }
197 
sendFirstScreenActiveInstallsBroadcast()198     private void sendFirstScreenActiveInstallsBroadcast() {
199         // Screen set is never empty
200         IntArray allScreens = mBgDataModel.collectWorkspaceScreens();
201         final int firstScreen = allScreens.get(0);
202         IntSet firstScreens = IntSet.wrap(firstScreen);
203 
204         ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
205         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
206         filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems,
207                 new ArrayList<>() /* otherScreenItems are ignored */);
208         final int launcherBroadcastInstalledApps = Settings.Secure.getInt(
209                 mApp.getContext().getContentResolver(),
210                 "launcher_broadcast_installed_apps",
211                 /* def= */ 0);
212         if (launcherBroadcastInstalledApps == 1 && mIsRestoreFromBackup) {
213             List<FirstScreenBroadcastModel> broadcastModels =
214                     FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
215                             mPmHelper,
216                             firstScreenItems,
217                             mInstallingPkgsCached,
218                             mBgDataModel.appWidgets
219                     );
220             logASplit("Sending first screen broadcast with additional archiving Extras");
221             FirstScreenBroadcastHelper.sendBroadcastsForModels(mApp.getContext(), broadcastModels);
222         } else {
223             mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
224         }
225     }
226 
run()227     public void run() {
228         synchronized (this) {
229             // Skip fast if we are already stopped.
230             if (mStopped) {
231                 return;
232             }
233         }
234 
235         TraceHelper.INSTANCE.beginSection(TAG);
236         LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
237         mIsRestoreFromBackup =
238                 (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
239         LauncherRestoreEventLogger restoreEventLogger = null;
240         if (enableLauncherBrMetricsFixed()) {
241             restoreEventLogger = LauncherRestoreEventLogger.Companion
242                     .newInstance(mApp.getContext());
243         }
244         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
245 
246             List<ShortcutInfo> allShortcuts = new ArrayList<>();
247             loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);
248 
249             // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
250             // sanitizeData should not be invoked if the workspace is loaded from a db different
251             // from the main db as defined in the invariant device profile.
252             // (e.g. both grid preview and minimal device mode uses a different db)
253             if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
254                 verifyNotStopped();
255                 sanitizeFolders(mItemsDeleted);
256                 sanitizeAppPairs();
257                 sanitizeWidgetsShortcutsAndPackages();
258                 logASplit("sanitizeData");
259             }
260 
261             verifyNotStopped();
262             mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
263             logASplit("bindWorkspace");
264 
265             mModelDelegate.workspaceLoadComplete();
266             // Notify the installer packages of packages with active installs on the first screen.
267             sendFirstScreenActiveInstallsBroadcast();
268             logASplit("sendFirstScreenBroadcast");
269 
270             // Take a break
271             waitForIdle();
272             logASplit("step 1 complete");
273             verifyNotStopped();
274 
275             // second step
276             Trace.beginSection("LoadAllApps");
277             List<LauncherActivityInfo> allActivityList;
278             try {
279                 allActivityList = loadAllApps();
280             } finally {
281                 Trace.endSection();
282             }
283             logASplit("loadAllApps");
284 
285             if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
286                 mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
287                         mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
288                 logASplit("allAppsDelegateItems");
289             }
290             verifyNotStopped();
291             mLauncherBinder.bindAllApps();
292             logASplit("bindAllApps");
293 
294             verifyNotStopped();
295             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
296             setIgnorePackages(updateHandler);
297             updateHandler.updateIcons(allActivityList,
298                     LauncherActivityCachingLogic.newInstance(mApp.getContext()),
299                     mApp.getModel()::onPackageIconsUpdated);
300             logASplit("update icon cache");
301 
302             verifyNotStopped();
303             logASplit("save shortcuts in icon cache");
304             updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
305                     mApp.getModel()::onPackageIconsUpdated);
306 
307             // Take a break
308             waitForIdle();
309             logASplit("step 2 complete");
310             verifyNotStopped();
311 
312             // third step
313             List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
314             logASplit("loadDeepShortcuts");
315 
316             verifyNotStopped();
317             mLauncherBinder.bindDeepShortcuts();
318             logASplit("bindDeepShortcuts");
319 
320             verifyNotStopped();
321             logASplit("save deep shortcuts in icon cache");
322             updateHandler.updateIcons(allDeepShortcuts,
323                     new ShortcutCachingLogic(), (pkgs, user) -> { });
324 
325             // Take a break
326             waitForIdle();
327             logASplit("step 3 complete");
328             verifyNotStopped();
329 
330             // fourth step
331             List<ComponentWithLabelAndIcon> allWidgetsList =
332                     mBgDataModel.widgetsModel.update(mApp, null);
333             logASplit("load widgets");
334 
335             verifyNotStopped();
336             mLauncherBinder.bindWidgets();
337             logASplit("bindWidgets");
338             verifyNotStopped();
339             LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
340 
341             if (enableSmartspaceAsAWidget() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
342                 mLauncherBinder.bindSmartspaceWidget();
343                 // Turn off pref.
344                 prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(false));
345                 logASplit("bindSmartspaceWidget");
346                 verifyNotStopped();
347             } else if (!enableSmartspaceAsAWidget() && WIDGET_ON_FIRST_SCREEN
348                     && !prefs.get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) {
349                 // Turn on pref.
350                 prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true));
351             }
352 
353             if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
354                 mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
355                 logASplit("otherDelegateItems");
356                 verifyNotStopped();
357             }
358 
359             updateHandler.updateIcons(allWidgetsList,
360                     new ComponentWithIconCachingLogic(mApp.getContext(), true),
361                     mApp.getModel()::onWidgetLabelsUpdated);
362             logASplit("save widgets in icon cache");
363 
364             // fifth step
365             loadFolderNames();
366 
367             verifyNotStopped();
368             updateHandler.finish();
369             logASplit("finish icon update");
370 
371             mModelDelegate.modelLoadComplete();
372             transaction.commit();
373             memoryLogger.clearLogs();
374             if (mIsRestoreFromBackup) {
375                 mIsRestoreFromBackup = false;
376                 LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
377                 if (restoreEventLogger != null) {
378                     restoreEventLogger.reportLauncherRestoreResults();
379                 }
380             }
381         } catch (CancellationException e) {
382             // Loader stopped, ignore
383             logASplit("Cancelled");
384         } catch (Exception e) {
385             memoryLogger.printLogs();
386             throw e;
387         }
388         TraceHelper.INSTANCE.endSection();
389     }
390 
stopLocked()391     public synchronized void stopLocked() {
392         mStopped = true;
393         this.notify();
394     }
395 
loadWorkspace( List<ShortcutInfo> allDeepShortcuts, String selection, LoaderMemoryLogger memoryLogger, @Nullable LauncherRestoreEventLogger restoreEventLogger )396     protected void loadWorkspace(
397             List<ShortcutInfo> allDeepShortcuts,
398             String selection,
399             LoaderMemoryLogger memoryLogger,
400             @Nullable LauncherRestoreEventLogger restoreEventLogger
401     ) {
402         Trace.beginSection("LoadWorkspace");
403         try {
404             loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger);
405         } finally {
406             Trace.endSection();
407         }
408         logASplit("loadWorkspace");
409 
410         if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
411             verifyNotStopped();
412             mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
413                     mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
414             mModelDelegate.markActive();
415             logASplit("workspaceDelegateItems");
416         }
417         mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
418                 && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
419                 mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
420     }
421 
loadWorkspaceImpl( List<ShortcutInfo> allDeepShortcuts, String selection, @Nullable LoaderMemoryLogger memoryLogger, @Nullable LauncherRestoreEventLogger restoreEventLogger)422     private void loadWorkspaceImpl(
423             List<ShortcutInfo> allDeepShortcuts,
424             String selection,
425             @Nullable LoaderMemoryLogger memoryLogger,
426             @Nullable LauncherRestoreEventLogger restoreEventLogger) {
427         final Context context = mApp.getContext();
428         final boolean isSdCardReady = Utilities.isBootCompleted();
429         final WidgetInflater widgetInflater = new WidgetInflater(context);
430 
431         ModelDbController dbController = mApp.getModel().getModelDbController();
432         dbController.tryMigrateDB(restoreEventLogger);
433         Log.d(TAG, "loadWorkspace: loading default favorites");
434         dbController.loadDefaultFavoritesIfNecessary();
435 
436         synchronized (mBgDataModel) {
437             mBgDataModel.clear();
438             mPendingPackages.clear();
439 
440             final HashMap<PackageUserKey, SessionInfo> installingPkgs =
441                     mSessionHelper.getActiveSessions();
442             if (Flags.enableSupportForArchiving()) {
443                 mInstallingPkgsCached = installingPkgs;
444             }
445             installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
446             FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: "
447                     + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList());
448 
449             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
450 
451             mShortcutKeyToPinnedShortcuts = new HashMap<>();
452             final LoaderCursor c = new LoaderCursor(
453                     dbController.query(TABLE_NAME, null, selection, null, null),
454                     mApp, mUserManagerState, mPmHelper,
455                     mIsRestoreFromBackup ? restoreEventLogger : null);
456             final Bundle extras = c.getExtras();
457             mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
458             try {
459                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
460                 queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
461 
462                 List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
463 
464                 WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
465                         mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
466                         mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
467                         mWidgetProvidersMap, installingPkgs, isSdCardReady,
468                         widgetInflater, mPmHelper, iconRequestInfos, unlockedUsers,
469                         allDeepShortcuts);
470 
471                 while (!mStopped && c.moveToNext()) {
472                     itemProcessor.processItem();
473                 }
474                 tryLoadWorkspaceIconsInBulk(iconRequestInfos);
475             } finally {
476                 IOUtils.closeSilently(c);
477             }
478 
479             if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
480                 mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
481                         mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
482                 mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
483                         mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
484                 mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
485                 mModelDelegate.markActive();
486             }
487 
488             // Break early if we've stopped loading
489             if (mStopped) {
490                 mBgDataModel.clear();
491                 return;
492             }
493 
494             // Remove dead items
495             mItemsDeleted = c.commitDeleted();
496 
497             processFolderItems();
498             processAppPairItems();
499 
500             c.commitRestoredItems();
501         }
502     }
503 
504     /**
505      * After all items have been processed and added to the BgDataModel, this method sorts and
506      * requests high-res icons for the items that are part of an app pair.
507      */
processAppPairItems()508     private void processAppPairItems() {
509         for (CollectionInfo collection : mBgDataModel.collections) {
510             if (!(collection instanceof AppPairInfo appPair)) {
511                 continue;
512             }
513 
514             appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
515             appPair.fetchHiResIconsIfNeeded(mIconCache);
516         }
517     }
518 
519     /**
520      * Initialized the UserManagerState, and determines which users are unlocked. Additionally, if
521      * the user is unlocked, it queries LauncherAppsService for pinned shortcuts and stores the
522      * result in a class variable to be used in other methods while processing workspace items.
523      *
524      * @param context used to query LauncherAppsService
525      * @param unlockedUsers this param is changed, and the updated value is used outside this method
526      */
527     @WorkerThread
queryPinnedShortcutsForUnlockedUsers(Context context, LongSparseArray<Boolean> unlockedUsers)528     private void queryPinnedShortcutsForUnlockedUsers(Context context,
529             LongSparseArray<Boolean> unlockedUsers) {
530         mUserManagerState.init(mUserCache, mUserManager);
531 
532         for (UserHandle user : mUserCache.getUserProfiles()) {
533             long serialNo = mUserCache.getSerialNumberForUser(user);
534             boolean userUnlocked = mUserManager.isUserUnlocked(user);
535 
536             // We can only query for shortcuts when the user is unlocked.
537             if (userUnlocked) {
538                 QueryResult pinnedShortcuts = new ShortcutRequest(context, user)
539                         .query(ShortcutRequest.PINNED);
540                 if (pinnedShortcuts.wasSuccess()) {
541                     for (ShortcutInfo shortcut : pinnedShortcuts) {
542                         mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
543                                 shortcut);
544                     }
545                     if (pinnedShortcuts.isEmpty()) {
546                         FileLog.d(TAG, "No pinned shortcuts found for user " + user);
547                     }
548                 } else {
549                     // Shortcut manager can fail due to some race condition when the
550                     // lock state changes too frequently. For the purpose of the loading
551                     // shortcuts, consider the user is still locked.
552                     FileLog.d(TAG, "Shortcut request failed for user "
553                             + user + ", user may still be locked.");
554                     userUnlocked = false;
555                 }
556             }
557             unlockedUsers.put(serialNo, userUnlocked);
558         }
559 
560     }
561 
562     /**
563      * After all items have been processed and added to the BgDataModel, this method can correctly
564      * rank items inside folders and load the correct miniature preview icons to be shown when the
565      * folder is collapsed.
566      */
567     @WorkerThread
processFolderItems()568     private void processFolderItems() {
569         // Sort the folder items, update ranks, and make sure all preview items are high res.
570         List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
571                 .stream().map(FolderGridOrganizer::new).toList();
572         for (CollectionInfo collection : mBgDataModel.collections) {
573             if (!(collection instanceof FolderInfo folder)) {
574                 continue;
575             }
576 
577             folder.getContents().sort(Folder.ITEM_POS_COMPARATOR);
578             verifiers.forEach(verifier -> verifier.setFolderInfo(folder));
579             int size = folder.getContents().size();
580 
581             // Update ranks here to ensure there are no gaps caused by removed folder items.
582             // Ranks are the source of truth for folder items, so cellX and cellY can be
583             // ignored for now. Database will be updated once user manually modifies folder.
584             for (int rank = 0; rank < size; ++rank) {
585                 ItemInfo info = folder.getContents().get(rank);
586                 info.rank = rank;
587 
588                 if (info instanceof WorkspaceItemInfo wii
589                         && wii.usingLowResIcon()
590                         && wii.itemType == Favorites.ITEM_TYPE_APPLICATION
591                         && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) {
592                     mIconCache.getTitleAndIcon(wii, false);
593                 } else if (info instanceof AppPairInfo api) {
594                     api.fetchHiResIconsIfNeeded(mIconCache);
595                 }
596             }
597         }
598     }
599 
tryLoadWorkspaceIconsInBulk( List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos)600     private void tryLoadWorkspaceIconsInBulk(
601             List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos) {
602         Trace.beginSection("LoadWorkspaceIconsInBulk");
603         try {
604             mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
605             for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
606                 WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
607                 if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
608                     iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
609                 }
610             }
611         } finally {
612             Trace.endSection();
613         }
614     }
615 
setIgnorePackages(IconCacheUpdateHandler updateHandler)616     private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
617         // Ignore packages which have a promise icon.
618         synchronized (mBgDataModel) {
619             for (ItemInfo info : mBgDataModel.itemsIdMap) {
620                 if (info instanceof WorkspaceItemInfo) {
621                     WorkspaceItemInfo si = (WorkspaceItemInfo) info;
622                     if (si.isPromise() && si.getTargetComponent() != null) {
623                         updateHandler.addPackagesToIgnore(
624                                 si.user, si.getTargetComponent().getPackageName());
625                     }
626                 } else if (info instanceof LauncherAppWidgetInfo) {
627                     LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
628                     if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
629                         updateHandler.addPackagesToIgnore(
630                                 lawi.user, lawi.providerName.getPackageName());
631                     }
632                 }
633             }
634         }
635     }
636 
sanitizeFolders(boolean itemsDeleted)637     private void sanitizeFolders(boolean itemsDeleted) {
638         if (itemsDeleted) {
639             // Remove any empty folder
640             IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
641             synchronized (mBgDataModel) {
642                 for (int folderId : deletedFolderIds) {
643                     mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(folderId));
644                     mBgDataModel.collections.remove(folderId);
645                     mBgDataModel.itemsIdMap.remove(folderId);
646                 }
647             }
648         }
649     }
650 
651     /** Cleans up app pairs if they don't have the right number of member apps (2). */
sanitizeAppPairs()652     private void sanitizeAppPairs() {
653         IntArray deletedAppPairIds = mApp.getModel().getModelDbController().deleteBadAppPairs();
654         IntArray deletedAppIds = mApp.getModel().getModelDbController().deleteUnparentedApps();
655 
656         IntArray deleted = new IntArray();
657         deleted.addAll(deletedAppPairIds);
658         deleted.addAll(deletedAppIds);
659 
660         synchronized (mBgDataModel) {
661             for (int id : deleted) {
662                 mBgDataModel.workspaceItems.remove(mBgDataModel.collections.get(id));
663                 mBgDataModel.collections.remove(id);
664                 mBgDataModel.itemsIdMap.remove(id);
665             }
666         }
667     }
668 
sanitizeWidgetsShortcutsAndPackages()669     private void sanitizeWidgetsShortcutsAndPackages() {
670         Context context = mApp.getContext();
671 
672         // Remove any ghost widgets
673         mApp.getModel().getModelDbController().removeGhostWidgets();
674 
675         // Update pinned state of model shortcuts
676         mBgDataModel.updateShortcutPinnedState(context);
677 
678         if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
679             context.registerReceiver(
680                     new SdCardAvailableReceiver(mApp, mPendingPackages),
681                     new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
682                     null,
683                     MODEL_EXECUTOR.getHandler());
684         }
685     }
686 
loadAllApps()687     private List<LauncherActivityInfo> loadAllApps() {
688         final List<UserHandle> profiles = mUserCache.getUserProfiles();
689         List<LauncherActivityInfo> allActivityList = new ArrayList<>();
690         // Clear the list of apps
691         mBgAllAppsList.clear();
692 
693         List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
694         boolean isWorkProfileQuiet = false;
695         boolean isPrivateProfileQuiet = false;
696         for (UserHandle user : profiles) {
697             // Query for the set of apps
698             final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
699             // Fail if we don't have any apps
700             // TODO: Fix this. Only fail for the current user.
701             if (apps == null || apps.isEmpty()) {
702                 return allActivityList;
703             }
704             boolean quietMode = mUserManagerState.isUserQuiet(user);
705 
706             if (Flags.enablePrivateSpace()) {
707                 if (mUserCache.getUserInfo(user).isWork()) {
708                     isWorkProfileQuiet = quietMode;
709                 } else if (mUserCache.getUserInfo(user).isPrivate()) {
710                     isPrivateProfileQuiet = quietMode;
711                 }
712             }
713             // Create the ApplicationInfos
714             for (int i = 0; i < apps.size(); i++) {
715                 LauncherActivityInfo app = apps.get(i);
716                 AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
717                         ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
718                 if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
719                     // For archived apps, include progress info in case there is a pending
720                     // install session post restart of device.
721                     String appPackageName = app.getApplicationInfo().packageName;
722                     SessionInfo si = mInstallingPkgsCached != null ? mInstallingPkgsCached.get(
723                             new PackageUserKey(appPackageName, user))
724                             : mSessionHelper.getActiveSessionInfo(user,
725                                     appPackageName);
726                     if (si != null) {
727                         appInfo.runtimeStatusFlags |= FLAG_INSTALL_SESSION_ACTIVE;
728                         appInfo.setProgressLevel((int) (si.getProgress() * 100),
729                                 PackageInstallInfo.STATUS_INSTALLING);
730                     }
731                 }
732 
733                 iconRequestInfos.add(new IconRequestInfo<>(
734                         appInfo, app, /* useLowResIcon= */ false));
735                 mBgAllAppsList.add(
736                         appInfo, app, false);
737             }
738             allActivityList.addAll(apps);
739         }
740 
741 
742         if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
743             // get all active sessions and add them to the all apps list
744             for (PackageInstaller.SessionInfo info :
745                     mSessionHelper.getAllVerifiedSessions()) {
746                 AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
747                         mApp.getContext(),
748                         PackageInstallInfo.fromInstallingState(info),
749                         false);
750 
751                 if (promiseAppInfo != null) {
752                     iconRequestInfos.add(new IconRequestInfo<>(
753                             promiseAppInfo,
754                             /* launcherActivityInfo= */ null,
755                             promiseAppInfo.usingLowResIcon()));
756                 }
757             }
758         }
759 
760         Trace.beginSection("LoadAllAppsIconsInBulk");
761         try {
762             mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
763             iconRequestInfos.forEach(iconRequestInfo ->
764                     mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
765         } finally {
766             Trace.endSection();
767         }
768 
769         if (Flags.enablePrivateSpace()) {
770             mBgAllAppsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isWorkProfileQuiet);
771             mBgAllAppsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isPrivateProfileQuiet);
772         } else {
773             mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
774                     mUserManagerState.isAnyProfileQuietModeEnabled());
775         }
776         mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
777                 hasShortcutsPermission(mApp.getContext()));
778         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
779                 mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
780                         == PackageManager.PERMISSION_GRANTED);
781 
782         mBgAllAppsList.getAndResetChangeFlag();
783         return allActivityList;
784     }
785 
loadDeepShortcuts()786     private List<ShortcutInfo> loadDeepShortcuts() {
787         List<ShortcutInfo> allShortcuts = new ArrayList<>();
788         mBgDataModel.deepShortcutMap.clear();
789 
790         if (mBgAllAppsList.hasShortcutHostPermission()) {
791             for (UserHandle user : mUserCache.getUserProfiles()) {
792                 if (mUserManager.isUserUnlocked(user)) {
793                     List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
794                             .query(ShortcutRequest.ALL);
795                     allShortcuts.addAll(shortcuts);
796                     mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
797                 }
798             }
799         }
800         return allShortcuts;
801     }
802 
loadFolderNames()803     private void loadFolderNames() {
804         FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
805                 mBgAllAppsList.data, mBgDataModel.collections);
806 
807         synchronized (mBgDataModel) {
808             for (int i = 0; i < mBgDataModel.collections.size(); i++) {
809                 FolderNameInfos suggestionInfos = new FolderNameInfos();
810                 CollectionInfo info = mBgDataModel.collections.valueAt(i);
811                 if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) {
812                     provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
813                             suggestionInfos);
814                     fi.suggestedFolderNames = suggestionInfos;
815                 }
816             }
817         }
818     }
819 
isValidProvider(AppWidgetProviderInfo provider)820     public static boolean isValidProvider(AppWidgetProviderInfo provider) {
821         return (provider != null) && (provider.provider != null)
822                 && (provider.provider.getPackageName() != null);
823     }
824 
logASplit(String label)825     private static void logASplit(String label) {
826         if (DEBUG) {
827             Log.d(TAG, label);
828         }
829     }
830 }
831