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