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