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