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