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