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