• 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.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
23 import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
24 
25 import android.appwidget.AppWidgetProviderInfo;
26 import android.content.ComponentName;
27 import android.content.ContentResolver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.LauncherActivityInfo;
32 import android.content.pm.PackageInstaller;
33 import android.content.pm.PackageInstaller.SessionInfo;
34 import android.graphics.Bitmap;
35 import android.os.Handler;
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.AllAppsList;
44 import com.android.launcher3.AppInfo;
45 import com.android.launcher3.FolderInfo;
46 import com.android.launcher3.IconCache;
47 import com.android.launcher3.InstallShortcutReceiver;
48 import com.android.launcher3.ItemInfo;
49 import com.android.launcher3.LauncherAppState;
50 import com.android.launcher3.LauncherAppWidgetInfo;
51 import com.android.launcher3.LauncherModel;
52 import com.android.launcher3.LauncherSettings;
53 import com.android.launcher3.ShortcutInfo;
54 import com.android.launcher3.Utilities;
55 import com.android.launcher3.compat.AppWidgetManagerCompat;
56 import com.android.launcher3.compat.LauncherAppsCompat;
57 import com.android.launcher3.compat.PackageInstallerCompat;
58 import com.android.launcher3.compat.UserManagerCompat;
59 import com.android.launcher3.config.FeatureFlags;
60 import com.android.launcher3.folder.Folder;
61 import com.android.launcher3.folder.FolderIconPreviewVerifier;
62 import com.android.launcher3.graphics.LauncherIcons;
63 import com.android.launcher3.logging.FileLog;
64 import com.android.launcher3.provider.ImportDataTask;
65 import com.android.launcher3.shortcuts.DeepShortcutManager;
66 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
67 import com.android.launcher3.shortcuts.ShortcutKey;
68 import com.android.launcher3.util.ComponentKey;
69 import com.android.launcher3.util.LooperIdleLock;
70 import com.android.launcher3.util.MultiHashMap;
71 import com.android.launcher3.util.PackageManagerHelper;
72 import com.android.launcher3.util.Provider;
73 import com.android.launcher3.util.TraceHelper;
74 
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.HashMap;
78 import java.util.HashSet;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.concurrent.CancellationException;
82 
83 /**
84  * Runnable for the thread that loads the contents of the launcher:
85  *   - workspace icons
86  *   - widgets
87  *   - all apps icons
88  *   - deep shortcuts within apps
89  */
90 public class LoaderTask implements Runnable {
91     private static final String TAG = "LoaderTask";
92 
93     private final LauncherAppState mApp;
94     private final AllAppsList mBgAllAppsList;
95     private final BgDataModel mBgDataModel;
96 
97     private FirstScreenBroadcast mFirstScreenBroadcast;
98 
99     private final LoaderResults mResults;
100 
101     private final LauncherAppsCompat mLauncherApps;
102     private final UserManagerCompat mUserManager;
103     private final DeepShortcutManager mShortcutManager;
104     private final PackageInstallerCompat mPackageInstaller;
105     private final AppWidgetManagerCompat mAppWidgetManager;
106     private final IconCache mIconCache;
107 
108     private boolean mStopped;
109 
LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, LoaderResults results)110     public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
111             LoaderResults results) {
112         mApp = app;
113         mBgAllAppsList = bgAllAppsList;
114         mBgDataModel = dataModel;
115         mResults = results;
116 
117         mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
118         mUserManager = UserManagerCompat.getInstance(mApp.getContext());
119         mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
120         mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
121         mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
122         mIconCache = mApp.getIconCache();
123     }
124 
waitForIdle()125     protected synchronized void waitForIdle() {
126         // Wait until the either we're stopped or the other threads are done.
127         // This way we don't start loading all apps until the workspace has settled
128         // down.
129         LooperIdleLock idleLock = mResults.newIdleLock(this);
130         // Just in case mFlushingWorkerThread changes but we aren't woken up,
131         // wait no longer than 1sec at a time
132         while (!mStopped && idleLock.awaitLocked(1000));
133     }
134 
verifyNotStopped()135     private synchronized void verifyNotStopped() throws CancellationException {
136         if (mStopped) {
137             throw new CancellationException("Loader stopped");
138         }
139     }
140 
sendFirstScreenActiveInstallsBroadcast()141     private void sendFirstScreenActiveInstallsBroadcast() {
142         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
143 
144         ArrayList<ItemInfo> allItems = new ArrayList<>();
145         synchronized (mBgDataModel) {
146             allItems.addAll(mBgDataModel.workspaceItems);
147             allItems.addAll(mBgDataModel.appWidgets);
148         }
149         long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
150                 ? -1 // In this case, we can still look at the items in the hotseat.
151                 : mBgDataModel.workspaceScreens.get(0);
152         filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
153                 new ArrayList<>() /* otherScreenItems are ignored */);
154         mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
155     }
156 
run()157     public void run() {
158         synchronized (this) {
159             // Skip fast if we are already stopped.
160             if (mStopped) {
161                 return;
162             }
163         }
164 
165         TraceHelper.beginSection(TAG);
166         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
167             TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
168             loadWorkspace();
169 
170             verifyNotStopped();
171             TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
172             mResults.bindWorkspace();
173 
174             // Notify the installer packages of packages with active installs on the first screen.
175             TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
176             sendFirstScreenActiveInstallsBroadcast();
177 
178             // Take a break
179             TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
180             waitForIdle();
181             verifyNotStopped();
182 
183             // second step
184             TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
185             loadAllApps();
186 
187             TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
188             verifyNotStopped();
189             mResults.bindAllApps();
190 
191             verifyNotStopped();
192             TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
193             updateIconCache();
194 
195             // Take a break
196             TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
197             waitForIdle();
198             verifyNotStopped();
199 
200             // third step
201             TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
202             loadDeepShortcuts();
203 
204             verifyNotStopped();
205             TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
206             mResults.bindDeepShortcuts();
207 
208             // Take a break
209             TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
210             waitForIdle();
211             verifyNotStopped();
212 
213             // fourth step
214             TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
215             mBgDataModel.widgetsModel.update(mApp, null);
216 
217             verifyNotStopped();
218             TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
219             mResults.bindWidgets();
220 
221             transaction.commit();
222         } catch (CancellationException e) {
223             // Loader stopped, ignore
224             TraceHelper.partitionSection(TAG, "Cancelled");
225         }
226         TraceHelper.endSection(TAG);
227     }
228 
stopLocked()229     public synchronized void stopLocked() {
230         mStopped = true;
231         this.notify();
232     }
233 
loadWorkspace()234     private void loadWorkspace() {
235         final Context context = mApp.getContext();
236         final ContentResolver contentResolver = context.getContentResolver();
237         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
238         final boolean isSafeMode = pmHelper.isSafeMode();
239         final boolean isSdCardReady = Utilities.isBootCompleted();
240         final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
241 
242         boolean clearDb = false;
243         try {
244             ImportDataTask.performImportIfPossible(context);
245         } catch (Exception e) {
246             // Migration failed. Clear workspace.
247             clearDb = true;
248         }
249 
250         if (!clearDb && GridSizeMigrationTask.ENABLED &&
251                 !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
252             // Migration failed. Clear workspace.
253             clearDb = true;
254         }
255 
256         if (clearDb) {
257             Log.d(TAG, "loadWorkspace: resetting launcher database");
258             LauncherSettings.Settings.call(contentResolver,
259                     LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
260         }
261 
262         Log.d(TAG, "loadWorkspace: loading default favorites");
263         LauncherSettings.Settings.call(contentResolver,
264                 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
265 
266         synchronized (mBgDataModel) {
267             mBgDataModel.clear();
268 
269             final HashMap<String, SessionInfo> installingPkgs =
270                     mPackageInstaller.updateAndGetActiveSessionCache();
271             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
272             mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
273 
274             Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
275             final LoaderCursor c = new LoaderCursor(contentResolver.query(
276                     LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
277 
278             HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
279 
280             try {
281                 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
282                         LauncherSettings.Favorites.APPWIDGET_ID);
283                 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
284                         LauncherSettings.Favorites.APPWIDGET_PROVIDER);
285                 final int spanXIndex = c.getColumnIndexOrThrow
286                         (LauncherSettings.Favorites.SPANX);
287                 final int spanYIndex = c.getColumnIndexOrThrow(
288                         LauncherSettings.Favorites.SPANY);
289                 final int rankIndex = c.getColumnIndexOrThrow(
290                         LauncherSettings.Favorites.RANK);
291                 final int optionsIndex = c.getColumnIndexOrThrow(
292                         LauncherSettings.Favorites.OPTIONS);
293 
294                 final LongSparseArray<UserHandle> allUsers = c.allUsers;
295                 final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
296                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
297                 for (UserHandle user : mUserManager.getUserProfiles()) {
298                     long serialNo = mUserManager.getSerialNumberForUser(user);
299                     allUsers.put(serialNo, user);
300                     quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
301 
302                     boolean userUnlocked = mUserManager.isUserUnlocked(user);
303 
304                     // We can only query for shortcuts when the user is unlocked.
305                     if (userUnlocked) {
306                         List<ShortcutInfoCompat> pinnedShortcuts =
307                                 mShortcutManager.queryForPinnedShortcuts(null, user);
308                         if (mShortcutManager.wasLastCallSuccess()) {
309                             for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
310                                 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
311                                         shortcut);
312                             }
313                         } else {
314                             // Shortcut manager can fail due to some race condition when the
315                             // lock state changes too frequently. For the purpose of the loading
316                             // shortcuts, consider the user is still locked.
317                             userUnlocked = false;
318                         }
319                     }
320                     unlockedUsers.put(serialNo, userUnlocked);
321                 }
322 
323                 ShortcutInfo info;
324                 LauncherAppWidgetInfo appWidgetInfo;
325                 Intent intent;
326                 String targetPkg;
327 
328                 FolderIconPreviewVerifier verifier =
329                         new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
330                 while (!mStopped && c.moveToNext()) {
331                     try {
332                         if (c.user == null) {
333                             // User has been deleted, remove the item.
334                             c.markDeleted("User has been deleted");
335                             continue;
336                         }
337 
338                         boolean allowMissingTarget = false;
339                         switch (c.itemType) {
340                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
341                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
342                         case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
343                             intent = c.parseIntent();
344                             if (intent == null) {
345                                 c.markDeleted("Invalid or null intent");
346                                 continue;
347                             }
348 
349                             int disabledState = quietMode.get(c.serialNumber) ?
350                                     ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
351                             ComponentName cn = intent.getComponent();
352                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
353 
354                             if (!Process.myUserHandle().equals(c.user)) {
355                                 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
356                                     c.markDeleted("Legacy shortcuts are only allowed for default user");
357                                     continue;
358                                 } else if (c.restoreFlag != 0) {
359                                     // Don't restore items for other profiles.
360                                     c.markDeleted("Restore from managed profile not supported");
361                                     continue;
362                                 }
363                             }
364                             if (TextUtils.isEmpty(targetPkg) &&
365                                     c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
366                                 c.markDeleted("Only legacy shortcuts can have null package");
367                                 continue;
368                             }
369 
370                             // If there is no target package, its an implicit intent
371                             // (legacy shortcut) which is always valid
372                             boolean validTarget = TextUtils.isEmpty(targetPkg) ||
373                                     mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user);
374 
375                             if (cn != null && validTarget) {
376                                 // If the apk is present and the shortcut points to a specific
377                                 // component.
378 
379                                 // If the component is already present
380                                 if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
381                                     // no special handling necessary for this item
382                                     c.markRestored();
383                                 } else {
384                                     if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
385                                         // We allow auto install apps to have their intent
386                                         // updated after an install.
387                                         intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
388                                         if (intent != null) {
389                                             c.restoreFlag = 0;
390                                             c.updater().put(
391                                                     LauncherSettings.Favorites.INTENT,
392                                                     intent.toUri(0)).commit();
393                                             cn = intent.getComponent();
394                                         } else {
395                                             c.markDeleted("Unable to find a launch target");
396                                             continue;
397                                         }
398                                     } else {
399                                         // The app is installed but the component is no
400                                         // longer available.
401                                         c.markDeleted("Invalid component removed: " + cn);
402                                         continue;
403                                     }
404                                 }
405                             }
406                             // else if cn == null => can't infer much, leave it
407                             // else if !validPkg => could be restored icon or missing sd-card
408 
409                             if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
410                                 // Points to a valid app (superset of cn != null) but the apk
411                                 // is not available.
412 
413                                 if (c.restoreFlag != 0) {
414                                     // Package is not yet available but might be
415                                     // installed later.
416                                     FileLog.d(TAG, "package not yet restored: " + targetPkg);
417 
418                                     if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
419                                         // Restore has started once.
420                                     } else if (installingPkgs.containsKey(targetPkg)) {
421                                         // App restore has started. Update the flag
422                                         c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
423                                         c.updater().commit();
424                                     } else {
425                                         c.markDeleted("Unrestored app removed: " + targetPkg);
426                                         continue;
427                                     }
428                                 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
429                                     // Package is present but not available.
430                                     disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
431                                     // Add the icon on the workspace anyway.
432                                     allowMissingTarget = true;
433                                 } else if (!isSdCardReady) {
434                                     // SdCard is not ready yet. Package might get available,
435                                     // once it is ready.
436                                     Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
437                                     pendingPackages.addToList(c.user, targetPkg);
438                                     // Add the icon on the workspace anyway.
439                                     allowMissingTarget = true;
440                                 } else {
441                                     // Do not wait for external media load anymore.
442                                     c.markDeleted("Invalid package removed: " + targetPkg);
443                                     continue;
444                                 }
445                             }
446 
447                             if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
448                                 validTarget = false;
449                             }
450 
451                             if (validTarget) {
452                                 // The shortcut points to a valid target (either no target
453                                 // or something which is ready to be used)
454                                 c.markRestored();
455                             }
456 
457                             boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
458                                     !verifier.isItemInPreview(c.getInt(rankIndex));
459 
460                             if (c.restoreFlag != 0) {
461                                 // Already verified above that user is same as default user
462                                 info = c.getRestoredItemInfo(intent);
463                             } else if (c.itemType ==
464                                     LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
465                                 info = c.getAppShortcutInfo(
466                                         intent, allowMissingTarget, useLowResIcon);
467                             } else if (c.itemType ==
468                                     LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
469 
470                                 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
471                                 if (unlockedUsers.get(c.serialNumber)) {
472                                     ShortcutInfoCompat pinnedShortcut =
473                                             shortcutKeyToPinnedShortcuts.get(key);
474                                     if (pinnedShortcut == null) {
475                                         // The shortcut is no longer valid.
476                                         c.markDeleted("Pinned shortcut not found");
477                                         continue;
478                                     }
479                                     info = new ShortcutInfo(pinnedShortcut, context);
480                                     final ShortcutInfo finalInfo = info;
481                                     Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() {
482                                         @Override
483                                         public Bitmap get() {
484                                             // If the pinned deep shortcut is no longer published,
485                                             // use the last saved icon instead of the default.
486                                             return c.loadIcon(finalInfo)
487                                                     ? finalInfo.iconBitmap : null;
488                                         }
489                                     };
490                                     LauncherIcons li = LauncherIcons.obtain(context);
491                                     li.createShortcutIcon(pinnedShortcut,
492                                             true /* badged */, fallbackIconProvider).applyTo(info);
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.loadSimpleShortcut();
502                                     info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
503                                 }
504                             } else { // item type == ITEM_TYPE_SHORTCUT
505                                 info = c.loadSimpleShortcut();
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 &= ~ShortcutInfo.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 ShortcutInfo");
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 (!c.isOnWorkspaceOrHotseat()) {
664                                     c.markDeleted("Widget found where container != " +
665                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
666                                     continue;
667                                 }
668 
669                                 if (!customWidget) {
670                                     String providerName =
671                                             appWidgetInfo.providerName.flattenToString();
672                                     if (!providerName.equals(savedProvider) ||
673                                             (appWidgetInfo.restoreStatus != c.restoreFlag)) {
674                                         c.updater()
675                                                 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
676                                                         providerName)
677                                                 .put(LauncherSettings.Favorites.RESTORED,
678                                                         appWidgetInfo.restoreStatus)
679                                                 .commit();
680                                     }
681                                 }
682 
683                                 if (appWidgetInfo.restoreStatus !=
684                                         LauncherAppWidgetInfo.RESTORE_COMPLETED) {
685                                     String pkg = appWidgetInfo.providerName.getPackageName();
686                                     appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
687                                     appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
688                                     mIconCache.getTitleAndIconForApp(
689                                             appWidgetInfo.pendingItemInfo, false);
690                                 }
691 
692                                 c.checkAndAddItem(appWidgetInfo, mBgDataModel);
693                             }
694                             break;
695                         }
696                     } catch (Exception e) {
697                         Log.e(TAG, "Desktop items loading interrupted", e);
698                     }
699                 }
700             } finally {
701                 Utilities.closeSilently(c);
702             }
703 
704             // Break early if we've stopped loading
705             if (mStopped) {
706                 mBgDataModel.clear();
707                 return;
708             }
709 
710             // Remove dead items
711             if (c.commitDeleted()) {
712                 // Remove any empty folder
713                 ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
714                         .call(contentResolver,
715                                 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
716                         .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
717                 for (long folderId : deletedFolderIds) {
718                     mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
719                     mBgDataModel.folders.remove(folderId);
720                     mBgDataModel.itemsIdMap.remove(folderId);
721                 }
722 
723                 // Remove any ghost widgets
724                 LauncherSettings.Settings.call(contentResolver,
725                         LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
726             }
727 
728             // Unpin shortcuts that don't exist on the workspace.
729             HashSet<ShortcutKey> pendingShortcuts =
730                     InstallShortcutReceiver.getPendingShortcuts(context);
731             for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
732                 MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
733                 if ((numTimesPinned == null || numTimesPinned.value == 0)
734                         && !pendingShortcuts.contains(key)) {
735                     // Shortcut is pinned but doesn't exist on the workspace; unpin it.
736                     mShortcutManager.unpinShortcut(key);
737                 }
738             }
739 
740             FolderIconPreviewVerifier verifier =
741                     new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
742             // Sort the folder items and make sure all items in the preview are high resolution.
743             for (FolderInfo folder : mBgDataModel.folders) {
744                 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
745                 verifier.setFolderInfo(folder);
746 
747                 int numItemsInPreview = 0;
748                 for (ShortcutInfo info : folder.contents) {
749                     if (info.usingLowResIcon
750                             && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
751                             && verifier.isItemInPreview(info.rank)) {
752                         mIconCache.getTitleAndIcon(info, false);
753                         numItemsInPreview++;
754                     }
755 
756                     if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) {
757                         break;
758                     }
759                 }
760             }
761 
762             c.commitRestoredItems();
763             if (!isSdCardReady && !pendingPackages.isEmpty()) {
764                 context.registerReceiver(
765                         new SdCardAvailableReceiver(mApp, pendingPackages),
766                         new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
767                         null,
768                         new Handler(LauncherModel.getWorkerLooper()));
769             }
770 
771             // Remove any empty screens
772             ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
773             for (ItemInfo item: mBgDataModel.itemsIdMap) {
774                 long screenId = item.screenId;
775                 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
776                         unusedScreens.contains(screenId)) {
777                     unusedScreens.remove(screenId);
778                 }
779             }
780 
781             // If there are any empty screens remove them, and update.
782             if (unusedScreens.size() != 0) {
783                 mBgDataModel.workspaceScreens.removeAll(unusedScreens);
784                 LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
785             }
786         }
787     }
788 
updateIconCache()789     private void updateIconCache() {
790         // Ignore packages which have a promise icon.
791         HashSet<String> packagesToIgnore = new HashSet<>();
792         synchronized (mBgDataModel) {
793             for (ItemInfo info : mBgDataModel.itemsIdMap) {
794                 if (info instanceof ShortcutInfo) {
795                     ShortcutInfo si = (ShortcutInfo) info;
796                     if (si.isPromise() && si.getTargetComponent() != null) {
797                         packagesToIgnore.add(si.getTargetComponent().getPackageName());
798                     }
799                 } else if (info instanceof LauncherAppWidgetInfo) {
800                     LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
801                     if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
802                         packagesToIgnore.add(lawi.providerName.getPackageName());
803                     }
804                 }
805             }
806         }
807         mIconCache.updateDbIcons(packagesToIgnore);
808     }
809 
loadAllApps()810     private void loadAllApps() {
811         final List<UserHandle> profiles = mUserManager.getUserProfiles();
812 
813         // Clear the list of apps
814         mBgAllAppsList.clear();
815         for (UserHandle user : profiles) {
816             // Query for the set of apps
817             final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
818             // Fail if we don't have any apps
819             // TODO: Fix this. Only fail for the current user.
820             if (apps == null || apps.isEmpty()) {
821                 return;
822             }
823             boolean quietMode = mUserManager.isQuietModeEnabled(user);
824             // Create the ApplicationInfos
825             for (int i = 0; i < apps.size(); i++) {
826                 LauncherActivityInfo app = apps.get(i);
827                 // This builds the icon bitmaps.
828                 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
829             }
830         }
831 
832         if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
833             // get all active sessions and add them to the all apps list
834             for (PackageInstaller.SessionInfo info :
835                     mPackageInstaller.getAllVerifiedSessions()) {
836                 mBgAllAppsList.addPromiseApp(mApp.getContext(),
837                         PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
838             }
839         }
840 
841         mBgAllAppsList.added = new ArrayList<>();
842     }
843 
loadDeepShortcuts()844     private void loadDeepShortcuts() {
845         mBgDataModel.deepShortcutMap.clear();
846         mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
847         if (mBgDataModel.hasShortcutHostPermission) {
848             for (UserHandle user : mUserManager.getUserProfiles()) {
849                 if (mUserManager.isUserUnlocked(user)) {
850                     List<ShortcutInfoCompat> shortcuts =
851                             mShortcutManager.queryForAllShortcuts(user);
852                     mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
853                 }
854             }
855         }
856     }
857 
isValidProvider(AppWidgetProviderInfo provider)858     public static boolean isValidProvider(AppWidgetProviderInfo provider) {
859         return (provider != null) && (provider.provider != null)
860                 && (provider.provider.getPackageName() != null);
861     }
862 }
863