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