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.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; 20 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; 21 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; 22 import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems; 23 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 24 import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; 25 26 import android.appwidget.AppWidgetProviderInfo; 27 import android.content.ComponentName; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.LauncherActivityInfo; 33 import android.content.pm.PackageInstaller; 34 import android.content.pm.PackageInstaller.SessionInfo; 35 import android.content.pm.ShortcutInfo; 36 import android.os.Process; 37 import android.os.UserHandle; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.LongSparseArray; 41 import android.util.MutableInt; 42 43 import com.android.launcher3.AppInfo; 44 import com.android.launcher3.FolderInfo; 45 import com.android.launcher3.InstallShortcutReceiver; 46 import com.android.launcher3.ItemInfo; 47 import com.android.launcher3.ItemInfoWithIcon; 48 import com.android.launcher3.LauncherAppState; 49 import com.android.launcher3.LauncherAppWidgetInfo; 50 import com.android.launcher3.LauncherModel; 51 import com.android.launcher3.LauncherSettings; 52 import com.android.launcher3.Utilities; 53 import com.android.launcher3.WorkspaceItemInfo; 54 import com.android.launcher3.compat.AppWidgetManagerCompat; 55 import com.android.launcher3.compat.LauncherAppsCompat; 56 import com.android.launcher3.compat.PackageInstallerCompat; 57 import com.android.launcher3.compat.UserManagerCompat; 58 import com.android.launcher3.config.FeatureFlags; 59 import com.android.launcher3.folder.Folder; 60 import com.android.launcher3.folder.FolderGridOrganizer; 61 import com.android.launcher3.icons.ComponentWithLabel; 62 import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic; 63 import com.android.launcher3.icons.IconCache; 64 import com.android.launcher3.icons.LauncherActivityCachingLogic; 65 import com.android.launcher3.icons.LauncherIcons; 66 import com.android.launcher3.icons.cache.IconCacheUpdateHandler; 67 import com.android.launcher3.logging.FileLog; 68 import com.android.launcher3.provider.ImportDataTask; 69 import com.android.launcher3.qsb.QsbContainerView; 70 import com.android.launcher3.shortcuts.DeepShortcutManager; 71 import com.android.launcher3.shortcuts.ShortcutKey; 72 import com.android.launcher3.util.ComponentKey; 73 import com.android.launcher3.util.IOUtils; 74 import com.android.launcher3.util.LooperIdleLock; 75 import com.android.launcher3.util.MultiHashMap; 76 import com.android.launcher3.util.PackageManagerHelper; 77 import com.android.launcher3.util.PackageUserKey; 78 import com.android.launcher3.util.TraceHelper; 79 80 import java.util.ArrayList; 81 import java.util.Collections; 82 import java.util.HashMap; 83 import java.util.HashSet; 84 import java.util.List; 85 import java.util.Map; 86 import java.util.concurrent.CancellationException; 87 import java.util.function.Supplier; 88 89 /** 90 * Runnable for the thread that loads the contents of the launcher: 91 * - workspace icons 92 * - widgets 93 * - all apps icons 94 * - deep shortcuts within apps 95 */ 96 public class LoaderTask implements Runnable { 97 private static final String TAG = "LoaderTask"; 98 99 private final LauncherAppState mApp; 100 private final AllAppsList mBgAllAppsList; 101 private final BgDataModel mBgDataModel; 102 103 private FirstScreenBroadcast mFirstScreenBroadcast; 104 105 private final LoaderResults mResults; 106 107 private final LauncherAppsCompat mLauncherApps; 108 private final UserManagerCompat mUserManager; 109 private final DeepShortcutManager mShortcutManager; 110 private final PackageInstallerCompat mPackageInstaller; 111 private final AppWidgetManagerCompat mAppWidgetManager; 112 private final IconCache mIconCache; 113 114 private boolean mStopped; 115 LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, LoaderResults results)116 public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, 117 LoaderResults results) { 118 mApp = app; 119 mBgAllAppsList = bgAllAppsList; 120 mBgDataModel = dataModel; 121 mResults = results; 122 123 mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext()); 124 mUserManager = UserManagerCompat.getInstance(mApp.getContext()); 125 mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext()); 126 mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext()); 127 mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext()); 128 mIconCache = mApp.getIconCache(); 129 } 130 waitForIdle()131 protected synchronized void waitForIdle() { 132 // Wait until the either we're stopped or the other threads are done. 133 // This way we don't start loading all apps until the workspace has settled 134 // down. 135 LooperIdleLock idleLock = mResults.newIdleLock(this); 136 // Just in case mFlushingWorkerThread changes but we aren't woken up, 137 // wait no longer than 1sec at a time 138 while (!mStopped && idleLock.awaitLocked(1000)); 139 } 140 verifyNotStopped()141 private synchronized void verifyNotStopped() throws CancellationException { 142 if (mStopped) { 143 throw new CancellationException("Loader stopped"); 144 } 145 } 146 sendFirstScreenActiveInstallsBroadcast()147 private void sendFirstScreenActiveInstallsBroadcast() { 148 ArrayList<ItemInfo> firstScreenItems = new ArrayList<>(); 149 150 ArrayList<ItemInfo> allItems = new ArrayList<>(); 151 synchronized (mBgDataModel) { 152 allItems.addAll(mBgDataModel.workspaceItems); 153 allItems.addAll(mBgDataModel.appWidgets); 154 } 155 // Screen set is never empty 156 final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0); 157 158 filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems, 159 new ArrayList<>() /* otherScreenItems are ignored */); 160 mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems); 161 } 162 run()163 public void run() { 164 synchronized (this) { 165 // Skip fast if we are already stopped. 166 if (mStopped) { 167 return; 168 } 169 } 170 171 TraceHelper.beginSection(TAG); 172 try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { 173 TraceHelper.partitionSection(TAG, "step 1.1: loading workspace"); 174 loadWorkspace(); 175 176 verifyNotStopped(); 177 TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace"); 178 mResults.bindWorkspace(); 179 180 // Notify the installer packages of packages with active installs on the first screen. 181 TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast"); 182 sendFirstScreenActiveInstallsBroadcast(); 183 184 // Take a break 185 TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle"); 186 waitForIdle(); 187 verifyNotStopped(); 188 189 // second step 190 TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); 191 List<LauncherActivityInfo> allActivityList = loadAllApps(); 192 193 TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); 194 verifyNotStopped(); 195 mResults.bindAllApps(); 196 197 verifyNotStopped(); 198 TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); 199 IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); 200 setIgnorePackages(updateHandler); 201 updateHandler.updateIcons(allActivityList, 202 LauncherActivityCachingLogic.newInstance(mApp.getContext()), 203 mApp.getModel()::onPackageIconsUpdated); 204 205 // Take a break 206 TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); 207 waitForIdle(); 208 verifyNotStopped(); 209 210 // third step 211 TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts"); 212 loadDeepShortcuts(); 213 214 verifyNotStopped(); 215 TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts"); 216 mResults.bindDeepShortcuts(); 217 218 // Take a break 219 TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle"); 220 waitForIdle(); 221 verifyNotStopped(); 222 223 // fourth step 224 TraceHelper.partitionSection(TAG, "step 4.1: loading widgets"); 225 List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null); 226 227 verifyNotStopped(); 228 TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets"); 229 mResults.bindWidgets(); 230 231 verifyNotStopped(); 232 233 TraceHelper.partitionSection(TAG, "step 4.3: save widgets in icon cache"); 234 updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic( 235 mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated); 236 237 verifyNotStopped(); 238 TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update"); 239 updateHandler.finish(); 240 241 transaction.commit(); 242 } catch (CancellationException e) { 243 // Loader stopped, ignore 244 TraceHelper.partitionSection(TAG, "Cancelled"); 245 } 246 TraceHelper.endSection(TAG); 247 } 248 stopLocked()249 public synchronized void stopLocked() { 250 mStopped = true; 251 this.notify(); 252 } 253 loadWorkspace()254 private void loadWorkspace() { 255 final Context context = mApp.getContext(); 256 final ContentResolver contentResolver = context.getContentResolver(); 257 final PackageManagerHelper pmHelper = new PackageManagerHelper(context); 258 final boolean isSafeMode = pmHelper.isSafeMode(); 259 final boolean isSdCardReady = Utilities.isBootCompleted(); 260 final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>(); 261 262 boolean clearDb = false; 263 try { 264 ImportDataTask.performImportIfPossible(context); 265 } catch (Exception e) { 266 // Migration failed. Clear workspace. 267 clearDb = true; 268 } 269 270 if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) { 271 // Migration failed. Clear workspace. 272 clearDb = true; 273 } 274 275 if (clearDb) { 276 Log.d(TAG, "loadWorkspace: resetting launcher database"); 277 LauncherSettings.Settings.call(contentResolver, 278 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 279 } 280 281 Log.d(TAG, "loadWorkspace: loading default favorites"); 282 LauncherSettings.Settings.call(contentResolver, 283 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); 284 285 synchronized (mBgDataModel) { 286 mBgDataModel.clear(); 287 288 final HashMap<PackageUserKey, SessionInfo> installingPkgs = 289 mPackageInstaller.updateAndGetActiveSessionCache(); 290 final PackageUserKey tempPackageKey = new PackageUserKey(null, null); 291 mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); 292 293 Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>(); 294 final LoaderCursor c = new LoaderCursor(contentResolver.query( 295 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); 296 297 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null; 298 299 try { 300 final int appWidgetIdIndex = c.getColumnIndexOrThrow( 301 LauncherSettings.Favorites.APPWIDGET_ID); 302 final int appWidgetProviderIndex = c.getColumnIndexOrThrow( 303 LauncherSettings.Favorites.APPWIDGET_PROVIDER); 304 final int spanXIndex = c.getColumnIndexOrThrow 305 (LauncherSettings.Favorites.SPANX); 306 final int spanYIndex = c.getColumnIndexOrThrow( 307 LauncherSettings.Favorites.SPANY); 308 final int rankIndex = c.getColumnIndexOrThrow( 309 LauncherSettings.Favorites.RANK); 310 final int optionsIndex = c.getColumnIndexOrThrow( 311 LauncherSettings.Favorites.OPTIONS); 312 313 final LongSparseArray<UserHandle> allUsers = c.allUsers; 314 final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); 315 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); 316 for (UserHandle user : mUserManager.getUserProfiles()) { 317 long serialNo = mUserManager.getSerialNumberForUser(user); 318 allUsers.put(serialNo, user); 319 quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); 320 321 boolean userUnlocked = mUserManager.isUserUnlocked(user); 322 323 // We can only query for shortcuts when the user is unlocked. 324 if (userUnlocked) { 325 DeepShortcutManager.QueryResult pinnedShortcuts = 326 mShortcutManager.queryForPinnedShortcuts(null, user); 327 if (pinnedShortcuts.wasSuccess()) { 328 for (ShortcutInfo shortcut : pinnedShortcuts) { 329 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), 330 shortcut); 331 } 332 } else { 333 // Shortcut manager can fail due to some race condition when the 334 // lock state changes too frequently. For the purpose of the loading 335 // shortcuts, consider the user is still locked. 336 userUnlocked = false; 337 } 338 } 339 unlockedUsers.put(serialNo, userUnlocked); 340 } 341 342 WorkspaceItemInfo info; 343 LauncherAppWidgetInfo appWidgetInfo; 344 Intent intent; 345 String targetPkg; 346 347 while (!mStopped && c.moveToNext()) { 348 try { 349 if (c.user == null) { 350 // User has been deleted, remove the item. 351 c.markDeleted("User has been deleted"); 352 continue; 353 } 354 355 boolean allowMissingTarget = false; 356 switch (c.itemType) { 357 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 358 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 359 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: 360 intent = c.parseIntent(); 361 if (intent == null) { 362 c.markDeleted("Invalid or null intent"); 363 continue; 364 } 365 366 int disabledState = quietMode.get(c.serialNumber) ? 367 WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0; 368 ComponentName cn = intent.getComponent(); 369 targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); 370 371 if (allUsers.indexOfValue(c.user) < 0) { 372 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 373 c.markDeleted("Legacy shortcuts are only allowed for current users"); 374 continue; 375 } else if (c.restoreFlag != 0) { 376 // Don't restore items for other profiles. 377 c.markDeleted("Restore from other profiles not supported"); 378 continue; 379 } 380 } 381 if (TextUtils.isEmpty(targetPkg) && 382 c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 383 c.markDeleted("Only legacy shortcuts can have null package"); 384 continue; 385 } 386 387 // If there is no target package, its an implicit intent 388 // (legacy shortcut) which is always valid 389 boolean validTarget = TextUtils.isEmpty(targetPkg) || 390 mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); 391 392 // If it's a deep shortcut, we'll use pinned shortcuts to restore it 393 if (cn != null && validTarget && c.itemType 394 != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { 395 // If the apk is present and the shortcut points to a specific 396 // component. 397 398 // If the component is already present 399 if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) { 400 // no special handling necessary for this item 401 c.markRestored(); 402 } else { 403 // Gracefully try to find a fallback activity. 404 intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); 405 if (intent != null) { 406 c.restoreFlag = 0; 407 c.updater().put( 408 LauncherSettings.Favorites.INTENT, 409 intent.toUri(0)).commit(); 410 cn = intent.getComponent(); 411 } else { 412 c.markDeleted("Unable to find a launch target"); 413 continue; 414 } 415 } 416 } 417 // else if cn == null => can't infer much, leave it 418 // else if !validPkg => could be restored icon or missing sd-card 419 420 if (!TextUtils.isEmpty(targetPkg) && !validTarget) { 421 // Points to a valid app (superset of cn != null) but the apk 422 // is not available. 423 424 if (c.restoreFlag != 0) { 425 // Package is not yet available but might be 426 // installed later. 427 FileLog.d(TAG, "package not yet restored: " + targetPkg); 428 429 tempPackageKey.update(targetPkg, c.user); 430 if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { 431 // Restore has started once. 432 } else if (installingPkgs.containsKey(tempPackageKey)) { 433 // App restore has started. Update the flag 434 c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; 435 c.updater().put(LauncherSettings.Favorites.RESTORED, 436 c.restoreFlag).commit(); 437 } else { 438 c.markDeleted("Unrestored app removed: " + targetPkg); 439 continue; 440 } 441 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { 442 // Package is present but not available. 443 disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE; 444 // Add the icon on the workspace anyway. 445 allowMissingTarget = true; 446 } else if (!isSdCardReady) { 447 // SdCard is not ready yet. Package might get available, 448 // once it is ready. 449 Log.d(TAG, "Missing pkg, will check later: " + targetPkg); 450 pendingPackages.addToList(c.user, targetPkg); 451 // Add the icon on the workspace anyway. 452 allowMissingTarget = true; 453 } else { 454 // Do not wait for external media load anymore. 455 c.markDeleted("Invalid package removed: " + targetPkg); 456 continue; 457 } 458 } 459 460 if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) { 461 validTarget = false; 462 } 463 464 if (validTarget) { 465 // The shortcut points to a valid target (either no target 466 // or something which is ready to be used) 467 c.markRestored(); 468 } 469 470 boolean useLowResIcon = !c.isOnWorkspaceOrHotseat(); 471 472 if (c.restoreFlag != 0) { 473 // Already verified above that user is same as default user 474 info = c.getRestoredItemInfo(intent); 475 } else if (c.itemType == 476 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 477 info = c.getAppShortcutInfo( 478 intent, allowMissingTarget, useLowResIcon); 479 } else if (c.itemType == 480 LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { 481 482 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); 483 if (unlockedUsers.get(c.serialNumber)) { 484 ShortcutInfo pinnedShortcut = 485 shortcutKeyToPinnedShortcuts.get(key); 486 if (pinnedShortcut == null) { 487 // The shortcut is no longer valid. 488 c.markDeleted("Pinned shortcut not found"); 489 continue; 490 } 491 info = new WorkspaceItemInfo(pinnedShortcut, context); 492 final WorkspaceItemInfo finalInfo = info; 493 494 LauncherIcons li = LauncherIcons.obtain(context); 495 // If the pinned deep shortcut is no longer published, 496 // use the last saved icon instead of the default. 497 Supplier<ItemInfoWithIcon> fallbackIconProvider = () -> 498 c.loadIcon(finalInfo, li) ? finalInfo : null; 499 info.applyFrom(li.createShortcutIcon(pinnedShortcut, 500 true /* badged */, fallbackIconProvider)); 501 li.recycle(); 502 if (pmHelper.isAppSuspended( 503 pinnedShortcut.getPackage(), info.user)) { 504 info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; 505 } 506 intent = info.intent; 507 } else { 508 // Create a shortcut info in disabled mode for now. 509 info = c.loadSimpleWorkspaceItem(); 510 info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; 511 } 512 } else { // item type == ITEM_TYPE_SHORTCUT 513 info = c.loadSimpleWorkspaceItem(); 514 515 // Shortcuts are only available on the primary profile 516 if (!TextUtils.isEmpty(targetPkg) 517 && pmHelper.isAppSuspended(targetPkg, c.user)) { 518 disabledState |= FLAG_DISABLED_SUSPENDED; 519 } 520 521 // App shortcuts that used to be automatically added to Launcher 522 // didn't always have the correct intent flags set, so do that 523 // here 524 if (intent.getAction() != null && 525 intent.getCategories() != null && 526 intent.getAction().equals(Intent.ACTION_MAIN) && 527 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { 528 intent.addFlags( 529 Intent.FLAG_ACTIVITY_NEW_TASK | 530 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 531 } 532 } 533 534 if (info != null) { 535 c.applyCommonProperties(info); 536 537 info.intent = intent; 538 info.rank = c.getInt(rankIndex); 539 info.spanX = 1; 540 info.spanY = 1; 541 info.runtimeStatusFlags |= disabledState; 542 if (isSafeMode && !isSystemApp(context, intent)) { 543 info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; 544 } 545 546 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { 547 tempPackageKey.update(targetPkg, c.user); 548 SessionInfo si = installingPkgs.get(tempPackageKey); 549 if (si == null) { 550 info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE; 551 } else { 552 info.setInstallProgress((int) (si.getProgress() * 100)); 553 } 554 } 555 556 c.checkAndAddItem(info, mBgDataModel); 557 } else { 558 throw new RuntimeException("Unexpected null WorkspaceItemInfo"); 559 } 560 break; 561 562 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 563 FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); 564 c.applyCommonProperties(folderInfo); 565 566 // Do not trim the folder label, as is was set by the user. 567 folderInfo.title = c.getString(c.titleIndex); 568 folderInfo.spanX = 1; 569 folderInfo.spanY = 1; 570 folderInfo.options = c.getInt(optionsIndex); 571 572 // no special handling required for restored folders 573 c.markRestored(); 574 575 c.checkAndAddItem(folderInfo, mBgDataModel); 576 break; 577 578 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 579 if (FeatureFlags.GO_DISABLE_WIDGETS) { 580 c.markDeleted("Only legacy shortcuts can have null package"); 581 continue; 582 } 583 // Follow through 584 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: 585 // Read all Launcher-specific widget details 586 boolean customWidget = c.itemType == 587 LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; 588 589 int appWidgetId = c.getInt(appWidgetIdIndex); 590 String savedProvider = c.getString(appWidgetProviderIndex); 591 final ComponentName component; 592 593 boolean isSearchWidget = (c.getInt(optionsIndex) 594 & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0; 595 if (isSearchWidget) { 596 component = QsbContainerView.getSearchComponentName(context); 597 if (component == null) { 598 c.markDeleted("Discarding SearchWidget without packagename "); 599 continue; 600 } 601 } else { 602 component = ComponentName.unflattenFromString(savedProvider); 603 } 604 final boolean isIdValid = !c.hasRestoreFlag( 605 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); 606 final boolean wasProviderReady = !c.hasRestoreFlag( 607 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); 608 609 if (widgetProvidersMap == null) { 610 widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); 611 } 612 final AppWidgetProviderInfo provider = widgetProvidersMap.get( 613 new ComponentKey(component, c.user)); 614 615 final boolean isProviderReady = isValidProvider(provider); 616 if (!isSafeMode && !customWidget && 617 wasProviderReady && !isProviderReady) { 618 c.markDeleted( 619 "Deleting widget that isn't installed anymore: " 620 + provider); 621 } else { 622 if (isProviderReady) { 623 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 624 provider.provider); 625 626 // The provider is available. So the widget is either 627 // available or not available. We do not need to track 628 // any future restore updates. 629 int status = c.restoreFlag & 630 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED & 631 ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; 632 if (!wasProviderReady) { 633 // If provider was not previously ready, update the 634 // status and UI flag. 635 636 // Id would be valid only if the widget restore broadcast was received. 637 if (isIdValid) { 638 status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 639 } 640 } 641 appWidgetInfo.restoreStatus = status; 642 } else { 643 Log.v(TAG, "Widget restore pending id=" + c.id 644 + " appWidgetId=" + appWidgetId 645 + " status =" + c.restoreFlag); 646 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 647 component); 648 appWidgetInfo.restoreStatus = c.restoreFlag; 649 650 tempPackageKey.update(component.getPackageName(), c.user); 651 SessionInfo si = 652 installingPkgs.get(tempPackageKey); 653 Integer installProgress = si == null 654 ? null 655 : (int) (si.getProgress() * 100); 656 657 if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { 658 // Restore has started once. 659 } else if (installProgress != null) { 660 // App restore has started. Update the flag 661 appWidgetInfo.restoreStatus |= 662 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; 663 } else if (!isSafeMode) { 664 c.markDeleted("Unrestored widget removed: " + component); 665 continue; 666 } 667 668 appWidgetInfo.installProgress = 669 installProgress == null ? 0 : installProgress; 670 } 671 if (appWidgetInfo.hasRestoreFlag( 672 LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { 673 appWidgetInfo.bindOptions = c.parseIntent(); 674 } 675 676 c.applyCommonProperties(appWidgetInfo); 677 appWidgetInfo.spanX = c.getInt(spanXIndex); 678 appWidgetInfo.spanY = c.getInt(spanYIndex); 679 appWidgetInfo.options = c.getInt(optionsIndex); 680 appWidgetInfo.user = c.user; 681 682 if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { 683 c.markDeleted("Widget has invalid size: " 684 + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); 685 continue; 686 } 687 if (!c.isOnWorkspaceOrHotseat()) { 688 c.markDeleted("Widget found where container != " + 689 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); 690 continue; 691 } 692 693 if (!customWidget) { 694 String providerName = 695 appWidgetInfo.providerName.flattenToString(); 696 if (!providerName.equals(savedProvider) || 697 (appWidgetInfo.restoreStatus != c.restoreFlag)) { 698 c.updater() 699 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, 700 providerName) 701 .put(LauncherSettings.Favorites.RESTORED, 702 appWidgetInfo.restoreStatus) 703 .commit(); 704 } 705 } 706 707 if (appWidgetInfo.restoreStatus != 708 LauncherAppWidgetInfo.RESTORE_COMPLETED) { 709 String pkg = appWidgetInfo.providerName.getPackageName(); 710 appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg); 711 appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; 712 mIconCache.getTitleAndIconForApp( 713 appWidgetInfo.pendingItemInfo, false); 714 } 715 716 c.checkAndAddItem(appWidgetInfo, mBgDataModel); 717 } 718 break; 719 } 720 } catch (Exception e) { 721 Log.e(TAG, "Desktop items loading interrupted", e); 722 } 723 } 724 } finally { 725 IOUtils.closeSilently(c); 726 } 727 728 // Break early if we've stopped loading 729 if (mStopped) { 730 mBgDataModel.clear(); 731 return; 732 } 733 734 // Remove dead items 735 if (c.commitDeleted()) { 736 // Remove any empty folder 737 int[] deletedFolderIds = LauncherSettings.Settings 738 .call(contentResolver, 739 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) 740 .getIntArray(LauncherSettings.Settings.EXTRA_VALUE); 741 for (int folderId : deletedFolderIds) { 742 mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); 743 mBgDataModel.folders.remove(folderId); 744 mBgDataModel.itemsIdMap.remove(folderId); 745 } 746 747 // Remove any ghost widgets 748 LauncherSettings.Settings.call(contentResolver, 749 LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); 750 } 751 752 // Unpin shortcuts that don't exist on the workspace. 753 HashSet<ShortcutKey> pendingShortcuts = 754 InstallShortcutReceiver.getPendingShortcuts(context); 755 for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) { 756 MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key); 757 if ((numTimesPinned == null || numTimesPinned.value == 0) 758 && !pendingShortcuts.contains(key)) { 759 // Shortcut is pinned but doesn't exist on the workspace; unpin it. 760 mShortcutManager.unpinShortcut(key); 761 } 762 } 763 764 // Sort the folder items, update ranks, and make sure all preview items are high res. 765 FolderGridOrganizer verifier = 766 new FolderGridOrganizer(mApp.getInvariantDeviceProfile()); 767 for (FolderInfo folder : mBgDataModel.folders) { 768 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); 769 verifier.setFolderInfo(folder); 770 int size = folder.contents.size(); 771 772 // Update ranks here to ensure there are no gaps caused by removed folder items. 773 // Ranks are the source of truth for folder items, so cellX and cellY can be ignored 774 // for now. Database will be updated once user manually modifies folder. 775 for (int rank = 0; rank < size; ++rank) { 776 WorkspaceItemInfo info = folder.contents.get(rank); 777 info.rank = rank; 778 779 if (info.usingLowResIcon() 780 && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 781 && verifier.isItemInPreview(info.rank)) { 782 mIconCache.getTitleAndIcon(info, false); 783 } 784 } 785 } 786 787 c.commitRestoredItems(); 788 if (!isSdCardReady && !pendingPackages.isEmpty()) { 789 context.registerReceiver( 790 new SdCardAvailableReceiver(mApp, pendingPackages), 791 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), 792 null, 793 MODEL_EXECUTOR.getHandler()); 794 } 795 } 796 } 797 setIgnorePackages(IconCacheUpdateHandler updateHandler)798 private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { 799 // Ignore packages which have a promise icon. 800 HashSet<String> packagesToIgnore = new HashSet<>(); 801 synchronized (mBgDataModel) { 802 for (ItemInfo info : mBgDataModel.itemsIdMap) { 803 if (info instanceof WorkspaceItemInfo) { 804 WorkspaceItemInfo si = (WorkspaceItemInfo) info; 805 if (si.isPromise() && si.getTargetComponent() != null) { 806 packagesToIgnore.add(si.getTargetComponent().getPackageName()); 807 } 808 } else if (info instanceof LauncherAppWidgetInfo) { 809 LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; 810 if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { 811 packagesToIgnore.add(lawi.providerName.getPackageName()); 812 } 813 } 814 } 815 } 816 updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore); 817 } 818 loadAllApps()819 private List<LauncherActivityInfo> loadAllApps() { 820 final List<UserHandle> profiles = mUserManager.getUserProfiles(); 821 List<LauncherActivityInfo> allActivityList = new ArrayList<>(); 822 // Clear the list of apps 823 mBgAllAppsList.clear(); 824 for (UserHandle user : profiles) { 825 // Query for the set of apps 826 final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); 827 // Fail if we don't have any apps 828 // TODO: Fix this. Only fail for the current user. 829 if (apps == null || apps.isEmpty()) { 830 return allActivityList; 831 } 832 boolean quietMode = mUserManager.isQuietModeEnabled(user); 833 // Create the ApplicationInfos 834 for (int i = 0; i < apps.size(); i++) { 835 LauncherActivityInfo app = apps.get(i); 836 // This builds the icon bitmaps. 837 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); 838 } 839 allActivityList.addAll(apps); 840 } 841 842 if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { 843 // get all active sessions and add them to the all apps list 844 for (PackageInstaller.SessionInfo info : 845 mPackageInstaller.getAllVerifiedSessions()) { 846 mBgAllAppsList.addPromiseApp(mApp.getContext(), 847 PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info)); 848 } 849 } 850 851 mBgAllAppsList.getAndResetChangeFlag(); 852 return allActivityList; 853 } 854 loadDeepShortcuts()855 private void loadDeepShortcuts() { 856 mBgDataModel.deepShortcutMap.clear(); 857 mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission(); 858 if (mBgDataModel.hasShortcutHostPermission) { 859 for (UserHandle user : mUserManager.getUserProfiles()) { 860 if (mUserManager.isUserUnlocked(user)) { 861 List<ShortcutInfo> shortcuts = 862 mShortcutManager.queryForAllShortcuts(user); 863 mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts); 864 } 865 } 866 } 867 } 868 isValidProvider(AppWidgetProviderInfo provider)869 public static boolean isValidProvider(AppWidgetProviderInfo provider) { 870 return (provider != null) && (provider.provider != null) 871 && (provider.provider.getPackageName() != null); 872 } 873 } 874