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