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