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