blob: be985894677c4554ef010e5d3efa95465b8cdc5b [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
kholoud mohamedd5a4d602022-04-28 15:55:40 +010019import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
20
Sunny Goyala7a5bf32020-01-05 15:35:29 +053021import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
Zak Cohen3eeb41d2020-02-14 14:15:13 -080022import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
Rohit Goyal86a1b1a2024-03-11 12:27:19 +000023import static com.android.launcher3.icons.cache.BaseIconCache.EMPTY_CLASS_NAME;
Sunny Goyal6449a212024-01-12 09:40:43 -080024import static com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE;
Himanshu Gupta9aab4d42023-10-12 16:53:00 +010025import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_AVAILABLE;
26import static com.android.launcher3.pm.UserCache.ACTION_PROFILE_UNAVAILABLE;
Vinit Nayak134e4292023-06-01 17:09:43 -070027import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing;
Sunny Goyala7a5bf32020-01-05 15:35:29 +053028import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
29import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
Sunny Goyala7a5bf32020-01-05 15:35:29 +053030
Rohit Goyal86a1b1a2024-03-11 12:27:19 +000031import android.content.ComponentName;
Sunny Goyal14168432019-10-24 15:59:49 -070032import android.content.Context;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070033import android.content.Intent;
Sunny Goyal045b4fa2019-09-20 12:51:37 -070034import android.content.pm.PackageInstaller;
Sunny Goyal6bbf6002019-04-17 18:38:52 -070035import android.content.pm.ShortcutInfo;
Sunny Goyal7c74e4a2016-12-15 15:53:17 -080036import android.os.UserHandle;
Winson Chunga90303b2013-11-15 13:05:06 -080037import android.text.TextUtils;
Winson Chungaafa03c2010-06-11 17:34:16 -070038import android.util.Log;
Sunny Goyala474a9b2017-05-04 16:47:11 -070039import android.util.Pair;
Michael Jurka34c2e6c2013-12-13 16:07:45 +010040
Pinyao Ting777c13e2022-09-16 09:44:26 -070041import androidx.annotation.NonNull;
Sunny Goyal6fe3eec2019-08-15 14:53:41 -070042import androidx.annotation.Nullable;
Sunny Goyal14168432019-10-24 15:59:49 -070043import androidx.annotation.WorkerThread;
Sunny Goyal6fe3eec2019-08-15 14:53:41 -070044
Sunny Goyal669b71f2023-01-27 14:37:07 -080045import com.android.launcher3.celllayout.CellPosMapper;
Sunny Goyal045b4fa2019-09-20 12:51:37 -070046import com.android.launcher3.config.FeatureFlags;
Sunny Goyalf840f102018-09-21 14:41:05 -070047import com.android.launcher3.icons.IconCache;
Sunny Goyalf0ba8b72016-09-09 15:47:55 -070048import com.android.launcher3.model.AddWorkspaceItemsTask;
Sunny Goyal87dcde62019-07-17 20:35:56 -070049import com.android.launcher3.model.AllAppsList;
Sunny Goyal77954ba2024-03-25 11:53:17 -070050import com.android.launcher3.model.BaseLauncherBinder;
Sunny Goyale9956a72016-09-01 17:24:47 -070051import com.android.launcher3.model.BgDataModel;
Sunny Goyal87dcde62019-07-17 20:35:56 -070052import com.android.launcher3.model.BgDataModel.Callbacks;
Sunny Goyalf0ba8b72016-09-09 15:47:55 -070053import com.android.launcher3.model.CacheDataUpdatedTask;
Sunny Goyal60e68c92020-08-12 13:59:27 -070054import com.android.launcher3.model.ItemInstallQueue;
Sunny Goyalb434fde2017-06-06 10:46:59 -070055import com.android.launcher3.model.LoaderTask;
Sunny Goyalce953a32023-04-14 14:08:37 -070056import com.android.launcher3.model.ModelDbController;
Sunny Goyal8b74cc72020-07-27 17:50:33 -070057import com.android.launcher3.model.ModelDelegate;
Sunny Goyal6449a212024-01-12 09:40:43 -080058import com.android.launcher3.model.ModelLauncherCallbacks;
Sunny Goyal99389382024-05-01 14:47:43 -070059import com.android.launcher3.model.ModelTaskController;
Sunny Goyal43bf11d2017-02-02 13:52:53 -080060import com.android.launcher3.model.ModelWriter;
Sunny Goyalf0ba8b72016-09-09 15:47:55 -070061import com.android.launcher3.model.PackageInstallStateChangedTask;
62import com.android.launcher3.model.PackageUpdatedTask;
kholoud mohamedd5a4d602022-04-28 15:55:40 +010063import com.android.launcher3.model.ReloadStringCacheTask;
Sunny Goyalf0ba8b72016-09-09 15:47:55 -070064import com.android.launcher3.model.ShortcutsChangedTask;
65import com.android.launcher3.model.UserLockStateChangedTask;
Sunny Goyale396abf2020-04-06 15:11:17 -070066import com.android.launcher3.model.data.AppInfo;
67import com.android.launcher3.model.data.ItemInfo;
68import com.android.launcher3.model.data.WorkspaceItemInfo;
Sunny Goyal045b4fa2019-09-20 12:51:37 -070069import com.android.launcher3.pm.InstallSessionTracker;
70import com.android.launcher3.pm.PackageInstallInfo;
Sunny Goyal337c81f2019-12-10 12:19:13 -080071import com.android.launcher3.pm.UserCache;
Sunny Goyalfa395362019-12-11 10:00:47 -080072import com.android.launcher3.shortcuts.ShortcutRequest;
Sunny Goyalea600c72020-07-09 19:31:40 -070073import com.android.launcher3.util.IntSet;
Jon Miranda088b9c22019-09-17 11:51:49 -070074import com.android.launcher3.util.ItemInfoMatcher;
Rohit Goyal86a1b1a2024-03-11 12:27:19 +000075import com.android.launcher3.util.PackageManagerHelper;
Tony Wickham86222d22017-03-29 15:30:43 -070076import com.android.launcher3.util.PackageUserKey;
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -070077import com.android.launcher3.util.Preconditions;
Romain Guyedcce092010-03-04 13:03:17 -080078
Hyunyoung Song3c7d9cb2017-01-30 15:11:27 -080079import java.io.FileDescriptor;
80import java.io.PrintWriter;
Michael Jurkac2f801e2011-07-12 14:19:46 -070081import java.util.ArrayList;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070082import java.util.HashSet;
Michael Jurkac2f801e2011-07-12 14:19:46 -070083import java.util.List;
Sunny Goyaldd96a5e2017-02-17 11:22:34 -080084import java.util.concurrent.CancellationException;
Sunny Goyal57e08662021-08-06 11:52:10 -070085import java.util.function.Consumer;
Sunny Goyal8c48d8b2019-01-25 15:10:18 -080086import java.util.function.Supplier;
Michael Jurkac2f801e2011-07-12 14:19:46 -070087
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088/**
89 * Maintains in-memory state of the Launcher. It is expected that there should be only one
90 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070091 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080092 */
Sunny Goyal6449a212024-01-12 09:40:43 -080093public class LauncherModel implements InstallSessionTracker.Callback {
Chris Wrenee523362014-09-09 10:09:02 -040094 private static final boolean DEBUG_RECEIVER = false;
Chris Wrenb358f812014-04-16 13:37:00 -040095
Joe Onorato9c1289c2009-08-17 11:03:03 -040096 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070097
Pinyao Ting777c13e2022-09-16 09:44:26 -070098 @NonNull
Sunny Goyala7a5bf32020-01-05 15:35:29 +053099 private final LauncherAppState mApp;
Pinyao Ting777c13e2022-09-16 09:44:26 -0700100 @NonNull
Sunny Goyalce953a32023-04-14 14:08:37 -0700101 private final ModelDbController mModelDbController;
102 @NonNull
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530103 private final Object mLock = new Object();
Pinyao Ting777c13e2022-09-16 09:44:26 -0700104 @Nullable
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530105 private LoaderTask mLoaderTask;
106 private boolean mIsLoaderTaskRunning;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800107
Charlie Anderson673a81b2023-07-21 15:05:33 -0400108 // only allow this once per reboot to reload work apps
109 private boolean mShouldReloadWorkProfile = true;
110
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800111 // Indicates whether the current model data is valid or not.
112 // We start off with everything not loaded. After that, we assume that
Joe Onoratocc67f472010-06-08 10:54:30 -0700113 // our monitoring of the package manager provides all updates and we never
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800114 // need to do a requery. This is only ever touched from the loader thread.
115 private boolean mModelLoaded;
Sunny Goyal777d4902021-08-27 21:22:17 +0000116 private boolean mModelDestroyed = false;
Hyunyoung Song6aa37292017-02-06 10:46:24 -0800117 public boolean isModelLoaded() {
118 synchronized (mLock) {
Sunny Goyal777d4902021-08-27 21:22:17 +0000119 return mModelLoaded && mLoaderTask == null && !mModelDestroyed;
Hyunyoung Song6aa37292017-02-06 10:46:24 -0800120 }
121 }
Joe Onoratocc67f472010-06-08 10:54:30 -0700122
Pinyao Ting777c13e2022-09-16 09:44:26 -0700123 @NonNull
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530124 private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800125
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700126 // < only access in worker thread >
Pinyao Ting777c13e2022-09-16 09:44:26 -0700127 @NonNull
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800128 private final AllAppsList mBgAllAppsList;
Sunny Goyal95f3d6b2016-08-10 16:09:29 -0700129
Sunny Goyale9956a72016-09-01 17:24:47 -0700130 /**
131 * All the static data should be accessed on the background thread, A lock should be acquired
132 * on this object when accessing any data from this model.
133 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700134 @NonNull
Sunny Goyal9ae9b602019-12-18 19:29:00 +0530135 private final BgDataModel mBgDataModel = new BgDataModel();
Tony Wickhambfbf7f92016-05-19 11:19:39 -0700136
Pinyao Ting777c13e2022-09-16 09:44:26 -0700137 @NonNull
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700138 private final ModelDelegate mModelDelegate;
139
Sunny Goyal7b9e28f2023-05-17 12:44:03 -0700140 private int mLastLoadId = -1;
141
Sunny Goyalc6e97692017-06-02 13:46:55 -0700142 // Runnable to check if the shortcuts permission has changed.
Pinyao Ting777c13e2022-09-16 09:44:26 -0700143 @NonNull
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700144 private final Runnable mDataValidationCheck = new Runnable() {
Sunny Goyalc6e97692017-06-02 13:46:55 -0700145 @Override
146 public void run() {
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700147 if (mModelLoaded) {
148 mModelDelegate.validateData();
Sunny Goyalc6e97692017-06-02 13:46:55 -0700149 }
150 }
151 };
Kenny Guyed131872014-04-30 03:02:21 +0100152
Pinyao Ting777c13e2022-09-16 09:44:26 -0700153 LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
154 @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
155 final boolean isPrimaryInstance) {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400156 mApp = app;
Sunny Goyalce953a32023-04-14 14:08:37 -0700157 mModelDbController = new ModelDbController(context);
Bjorn Bringert1307f632013-10-03 22:31:03 +0100158 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
Thiru Ramasamyd495e8c2021-08-26 10:37:17 -0700159 mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
160 isPrimaryInstance);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800161 }
162
Pinyao Ting777c13e2022-09-16 09:44:26 -0700163 @NonNull
Schneider Victor-tulias3ec8f552021-04-13 14:17:41 -0700164 public ModelDelegate getModelDelegate() {
165 return mModelDelegate;
166 }
167
Sunny Goyalce953a32023-04-14 14:08:37 -0700168 public ModelDbController getModelDbController() {
169 return mModelDbController;
170 }
171
Sunny Goyal6449a212024-01-12 09:40:43 -0800172 public ModelLauncherCallbacks newModelCallbacks() {
173 return new ModelLauncherCallbacks(this::enqueueModelUpdateTask);
174 }
175
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800176 /**
177 * Adds the provided items to the workspace.
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800178 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700179 public void addAndBindAddedWorkspaceItems(
180 @NonNull final List<Pair<ItemInfo, Object>> itemList) {
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530181 for (Callbacks cb : getCallbacks()) {
182 cb.preAddApps();
Tony Wickham6a71a5b2018-08-21 11:40:23 -0700183 }
Sunny Goyal91498ab2017-10-05 15:57:40 -0700184 enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
Winson Chung64359a52013-07-08 17:17:08 -0700185 }
186
Pinyao Ting777c13e2022-09-16 09:44:26 -0700187 @NonNull
Sebastian Franco2d023e22023-11-15 16:48:34 -0600188 public ModelWriter getWriter(final boolean verifyChanges, CellPosMapper cellPosMapper,
189 @Nullable final Callbacks owner) {
190 return new ModelWriter(mApp.getContext(), this, mBgDataModel, verifyChanges, cellPosMapper,
191 owner);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800192 }
193
Sunny Goyal14168432019-10-24 15:59:49 -0700194 /**
195 * Called when the icon for an app changes, outside of package event
196 */
197 @WorkerThread
Pinyao Ting777c13e2022-09-16 09:44:26 -0700198 public void onAppIconChanged(@NonNull final String packageName,
199 @NonNull final UserHandle user) {
Sunny Goyal14168432019-10-24 15:59:49 -0700200 // Update the icon for the calendar package
201 Context context = mApp.getContext();
Sunny Goyal6449a212024-01-12 09:40:43 -0800202 enqueueModelUpdateTask(new PackageUpdatedTask(OP_UPDATE, user, packageName));
Sunny Goyal14168432019-10-24 15:59:49 -0700203
Sunny Goyalfa395362019-12-11 10:00:47 -0800204 List<ShortcutInfo> pinnedShortcuts = new ShortcutRequest(context, user)
205 .forPackage(packageName).query(ShortcutRequest.PINNED);
Sunny Goyal14168432019-10-24 15:59:49 -0700206 if (!pinnedShortcuts.isEmpty()) {
207 enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, pinnedShortcuts, user,
208 false));
209 }
Tony Wickhambfbf7f92016-05-19 11:19:39 -0700210 }
211
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700212 /**
Sunny Goyal0fc3d122020-08-11 18:49:28 -0700213 * Called when the workspace items have drastically changed
214 */
215 public void onWorkspaceUiChanged() {
216 MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete);
217 }
218
219 /**
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700220 * Called when the model is destroyed
221 */
222 public void destroy() {
Sunny Goyal777d4902021-08-27 21:22:17 +0000223 mModelDestroyed = true;
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700224 MODEL_EXECUTOR.execute(mModelDelegate::destroy);
225 }
226
Pinyao Ting777c13e2022-09-16 09:44:26 -0700227 public void onBroadcastIntent(@NonNull final Intent intent) {
Vinit Nayak134e4292023-06-01 17:09:43 -0700228 if (DEBUG_RECEIVER || sDebugTracing) Log.d(TAG, "onReceive intent=" + intent);
Joe Onorato36115782010-06-17 13:28:48 -0400229 final String action = intent.getAction();
Kenny Guyed131872014-04-30 03:02:21 +0100230 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Reena Lee93f824a2011-09-23 17:20:28 -0700231 // If we have changed locale we need to clear out the labels in all apps/workspace.
232 forceReload();
kholoud mohamedd5a4d602022-04-28 15:55:40 +0100233 } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) {
234 enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate));
Zak Cohen3eeb41d2020-02-14 14:15:13 -0800235 } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530236 for (Callbacks cb : getCallbacks()) {
237 if (cb instanceof Launcher) {
238 ((Launcher) cb).recreate();
239 }
240 }
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700241 }
242 }
243
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800244 /**
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700245 * Called then there use a user event
246 * @see UserCache#addUserEventListener
247 */
248 public void onUserEvent(UserHandle user, String action) {
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700249 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
Charlie Anderson673a81b2023-07-21 15:05:33 -0400250 && mShouldReloadWorkProfile) {
251 mShouldReloadWorkProfile = false;
252 forceReload();
253 } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700254 || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
Charlie Anderson673a81b2023-07-21 15:05:33 -0400255 mShouldReloadWorkProfile = false;
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700256 enqueueModelUpdateTask(new PackageUpdatedTask(
257 PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
Charlie Anderson673a81b2023-07-21 15:05:33 -0400258 } else if (UserCache.ACTION_PROFILE_LOCKED.equals(action)
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700259 || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) {
260 enqueueModelUpdateTask(new UserLockStateChangedTask(
261 user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action)));
Charlie Anderson673a81b2023-07-21 15:05:33 -0400262 } else if (UserCache.ACTION_PROFILE_ADDED.equals(action)
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700263 || UserCache.ACTION_PROFILE_REMOVED.equals(action)) {
264 forceReload();
Himanshu Gupta9aab4d42023-10-12 16:53:00 +0100265 } else if (ACTION_PROFILE_AVAILABLE.equals(action)
266 || ACTION_PROFILE_UNAVAILABLE.equals(action)) {
267 /*
268 * This broadcast is only available when android.os.Flags.allowPrivateProfile() is set.
269 * For Work-profile this broadcast will be sent in addition to
270 * ACTION_MANAGED_PROFILE_AVAILABLE/UNAVAILABLE.
271 * So effectively, this if block only handles the non-work profile case.
272 */
273 enqueueModelUpdateTask(new PackageUpdatedTask(
274 PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
Sunny Goyalc80e60d2023-06-07 12:52:30 -0700275 }
276 }
277
278 /**
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800279 * Reloads the workspace items from the DB and re-binds the workspace. This should generally
280 * not be called as DB updates are automatically followed by UI update
281 */
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530282 public void forceReload() {
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800283 synchronized (mLock) {
284 // Stop any existing loaders first, so they don't set mModelLoaded to true later
Sunny Goyale86f11f2017-06-06 14:33:18 -0700285 stopLoader();
Sunny Goyaldd96a5e2017-02-17 11:22:34 -0800286 mModelLoaded = false;
287 }
Winson Chungf0c6ae02012-03-21 16:10:31 -0700288
Sunny Goyal29947f02017-12-18 13:49:44 -0800289 // Start the loader if launcher is already running, otherwise the loader will run,
290 // the next time launcher starts
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530291 if (hasCallbacks()) {
292 startLoader();
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700293 }
Joe Onorato36115782010-06-17 13:28:48 -0400294 }
Joe Onoratof99f8c12009-10-31 17:27:36 -0400295
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530296 /**
297 * Rebinds all existing callbacks with already loaded model
298 */
299 public void rebindCallbacks() {
300 if (hasCallbacks()) {
301 startLoader();
302 }
303 }
304
305 /**
306 * Removes an existing callback
307 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700308 public void removeCallbacks(@NonNull final Callbacks callbacks) {
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530309 synchronized (mCallbacksList) {
310 Preconditions.assertUIThread();
311 if (mCallbacksList.remove(callbacks)) {
312 if (stopLoader()) {
313 // Rebind existing callbacks
314 startLoader();
315 }
316 }
317 }
318 }
319
320 /**
321 * Adds a callbacks to receive model updates
322 * @return true if workspace load was performed synchronously
323 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700324 public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530325 synchronized (mLock) {
326 addCallbacks(callbacks);
Sunny Goyal711c5962021-06-23 12:36:18 -0700327 return startLoader(new Callbacks[] { callbacks });
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530328
329 }
330 }
331
332 /**
333 * Adds a callbacks to receive model updates
334 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700335 public void addCallbacks(@NonNull final Callbacks callbacks) {
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530336 Preconditions.assertUIThread();
337 synchronized (mCallbacksList) {
338 mCallbacksList.add(callbacks);
339 }
Adam Cohen1a85c582014-09-30 09:48:49 -0700340 }
341
Sunny Goyalb5b9ad62016-04-02 11:23:39 -0700342 /**
343 * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
344 * @return true if the page could be bound synchronously.
345 */
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530346 public boolean startLoader() {
Sunny Goyal711c5962021-06-23 12:36:18 -0700347 return startLoader(new Callbacks[0]);
348 }
349
Pinyao Ting777c13e2022-09-16 09:44:26 -0700350 private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700351 // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
Sunny Goyal60e68c92020-08-12 13:59:27 -0700352 ItemInstallQueue.INSTANCE.get(mApp.getContext())
353 .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
Joe Onorato36115782010-06-17 13:28:48 -0400354 synchronized (mLock) {
Sunny Goyal711c5962021-06-23 12:36:18 -0700355 // If there is already one running, tell it to stop.
356 boolean wasRunning = stopLoader();
357 boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
358 boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
359 final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
360
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530361 if (callbacksList.length > 0) {
Sunny Goyal527c7d32015-08-28 15:19:36 -0700362 // Clear any pending bind-runnables from the synchronized load process.
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530363 for (Callbacks cb : callbacksList) {
Sunny Goyal5fb83a42020-08-10 10:50:36 -0700364 MAIN_EXECUTOR.execute(cb::clearPendingBinds);
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530365 }
Sunny Goyal527c7d32015-08-28 15:19:36 -0700366
Sunny Goyal77954ba2024-03-25 11:53:17 -0700367 BaseLauncherBinder launcherBinder = new BaseLauncherBinder(
Sunny Goyal5fb83a42020-08-10 10:50:36 -0700368 mApp, mBgDataModel, mBgAllAppsList, callbacksList);
Sunny Goyal711c5962021-06-23 12:36:18 -0700369 if (bindDirectly) {
Sunny Goyalf8d6ed22017-06-01 14:26:38 -0700370 // Divide the set of loaded items into those that we are binding synchronously,
371 // and everything else that is to be bound normally (asynchronously).
Fengjiang Li5a36c172023-04-13 18:18:27 +0000372 launcherBinder.bindWorkspace(bindAllCallbacks, /* isBindSync= */ true);
Sunny Goyalf8d6ed22017-06-01 14:26:38 -0700373 // For now, continue posting the binding of AllApps as there are other
374 // issues that arise from that.
Stefan Andoniane82476a2023-01-10 22:46:31 +0000375 launcherBinder.bindAllApps();
376 launcherBinder.bindDeepShortcuts();
377 launcherBinder.bindWidgets();
Stefan Andoniane4609a22023-04-07 19:28:05 +0000378 if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
379 mModelDelegate.bindAllModelExtras(callbacksList);
380 }
Sunny Goyalb5b9ad62016-04-02 11:23:39 -0700381 return true;
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700382 } else {
Sunny Goyal57e08662021-08-06 11:52:10 -0700383 stopLoader();
384 mLoaderTask = new LoaderTask(
Stefan Andoniane82476a2023-01-10 22:46:31 +0000385 mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
Sunny Goyal57e08662021-08-06 11:52:10 -0700386
387 // Always post the loader task, instead of running directly
388 // (even on same thread) so that we exit any nested synchronized blocks
389 MODEL_EXECUTOR.post(mLoaderTask);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700390 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400391 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400392 }
Sunny Goyalb5b9ad62016-04-02 11:23:39 -0700393 return false;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400394 }
395
Sunny Goyale86f11f2017-06-06 14:33:18 -0700396 /**
397 * If there is already a loader task running, tell it to stop.
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530398 * @return true if an existing loader was stopped.
Sunny Goyale86f11f2017-06-06 14:33:18 -0700399 */
Sunny Goyal711c5962021-06-23 12:36:18 -0700400 private boolean stopLoader() {
Joe Onorato36115782010-06-17 13:28:48 -0400401 synchronized (mLock) {
Sunny Goyale86f11f2017-06-06 14:33:18 -0700402 LoaderTask oldTask = mLoaderTask;
403 mLoaderTask = null;
404 if (oldTask != null) {
405 oldTask.stopLocked();
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530406 return true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400407 }
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530408 return false;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400409 }
Joe Onorato36115782010-06-17 13:28:48 -0400410 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400411
Sunny Goyal57e08662021-08-06 11:52:10 -0700412 /**
413 * Loads the model if not loaded
414 * @param callback called with the data model upon successful load or null on model thread.
415 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700416 public void loadAsync(@NonNull final Consumer<BgDataModel> callback) {
Sunny Goyale86f11f2017-06-06 14:33:18 -0700417 synchronized (mLock) {
Sunny Goyal57e08662021-08-06 11:52:10 -0700418 if (!mModelLoaded && !mIsLoaderTaskRunning) {
419 startLoader();
Winson Chungd6519662018-02-16 03:23:51 +0000420 }
421 }
Sunny Goyal57e08662021-08-06 11:52:10 -0700422 MODEL_EXECUTOR.post(() -> callback.accept(isModelLoaded() ? mBgDataModel : null));
Winson Chungd6519662018-02-16 03:23:51 +0000423 }
424
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700425 @Override
Pinyao Ting777c13e2022-09-16 09:44:26 -0700426 public void onInstallSessionCreated(@NonNull final PackageInstallInfo sessionInfo) {
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700427 if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
Sunny Goyal99389382024-05-01 14:47:43 -0700428 enqueueModelUpdateTask((taskController, dataModel, apps) -> {
429 apps.addPromiseApp(mApp.getContext(), sessionInfo);
430 taskController.bindApplicationsIfNeeded();
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700431 });
432 }
433 }
434
435 @Override
Pinyao Ting777c13e2022-09-16 09:44:26 -0700436 public void onSessionFailure(@NonNull final String packageName,
437 @NonNull final UserHandle user) {
Sunny Goyal99389382024-05-01 14:47:43 -0700438 enqueueModelUpdateTask((taskController, dataModel, apps) -> {
439 IconCache iconCache = mApp.getIconCache();
440 final IntSet removedIds = new IntSet();
441 HashSet<WorkspaceItemInfo> archivedWorkspaceItemsToCacheRefresh = new HashSet<>();
442 boolean isAppArchived = PackageManagerHelper.INSTANCE.get(mApp.getContext())
443 .isAppArchivedForUser(packageName, user);
444 synchronized (dataModel) {
445 if (isAppArchived) {
446 // Remove package icon cache entry for archived app in case of a session
447 // failure.
448 mApp.getIconCache().remove(
449 new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
450 user);
451 }
Rohit Goyal86a1b1a2024-03-11 12:27:19 +0000452
Sunny Goyal99389382024-05-01 14:47:43 -0700453 for (ItemInfo info : dataModel.itemsIdMap) {
454 if (info instanceof WorkspaceItemInfo
455 && ((WorkspaceItemInfo) info).hasPromiseIconUi()
456 && user.equals(info.user)
457 && info.getIntent() != null) {
458 if (TextUtils.equals(packageName, info.getIntent().getPackage())) {
459 removedIds.add(info.id);
460 }
461 if (((WorkspaceItemInfo) info).isArchived()) {
462 WorkspaceItemInfo workspaceItem = (WorkspaceItemInfo) info;
463 // Refresh icons on the workspace for archived apps.
464 iconCache.getTitleAndIcon(workspaceItem,
465 workspaceItem.usingLowResIcon());
466 archivedWorkspaceItemsToCacheRefresh.add(workspaceItem);
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700467 }
468 }
469 }
470
Rohit Goyal86a1b1a2024-03-11 12:27:19 +0000471 if (isAppArchived) {
Sunny Goyal99389382024-05-01 14:47:43 -0700472 apps.updateIconsAndLabels(new HashSet<>(List.of(packageName)), user);
Jakob Schneider4f5b6012024-02-07 17:29:25 +0000473 }
Mario Bertschler817afa32017-03-15 11:56:47 -0700474 }
Sunny Goyal99389382024-05-01 14:47:43 -0700475
476 if (!removedIds.isEmpty()) {
477 taskController.deleteAndBindComponentsRemoved(
478 ItemInfoMatcher.ofItemIds(removedIds),
479 "removed because install session failed");
480 }
481 if (!archivedWorkspaceItemsToCacheRefresh.isEmpty()) {
482 taskController.bindUpdatedWorkspaceItems(
483 archivedWorkspaceItemsToCacheRefresh.stream().toList());
484 }
485 if (isAppArchived) {
486 taskController.bindApplicationsIfNeeded();
487 }
Mario Bertschler817afa32017-03-15 11:56:47 -0700488 });
489 }
490
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700491 @Override
Pinyao Ting777c13e2022-09-16 09:44:26 -0700492 public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) {
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700493 enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
494 }
495
496 /**
497 * Updates the icons and label of all pending icons for the provided package name.
498 */
499 @Override
Pinyao Ting777c13e2022-09-16 09:44:26 -0700500 public void onUpdateSessionDisplay(@NonNull final PackageUserKey key,
501 @NonNull final PackageInstaller.SessionInfo info) {
Sunny Goyal045b4fa2019-09-20 12:51:37 -0700502 mApp.getIconCache().updateSessionCache(key, info);
503
504 HashSet<String> packages = new HashSet<>();
505 packages.add(key.mPackageName);
506 enqueueModelUpdateTask(new CacheDataUpdatedTask(
507 CacheDataUpdatedTask.OP_SESSION_UPDATE, key.mUser, packages));
508 }
509
Sunny Goyalc6e97692017-06-02 13:46:55 -0700510 public class LoaderTransaction implements AutoCloseable {
511
Pinyao Ting777c13e2022-09-16 09:44:26 -0700512 @NonNull
Sunny Goyalc6e97692017-06-02 13:46:55 -0700513 private final LoaderTask mTask;
514
Pinyao Ting777c13e2022-09-16 09:44:26 -0700515 private LoaderTransaction(@NonNull final LoaderTask task) throws CancellationException {
Sunny Goyalc6e97692017-06-02 13:46:55 -0700516 synchronized (mLock) {
517 if (mLoaderTask != task) {
518 throw new CancellationException("Loader already stopped");
519 }
Sunny Goyal7b9e28f2023-05-17 12:44:03 -0700520 mLastLoadId++;
Sunny Goyalc6e97692017-06-02 13:46:55 -0700521 mTask = task;
522 mIsLoaderTaskRunning = true;
523 mModelLoaded = false;
524 }
525 }
526
527 public void commit() {
528 synchronized (mLock) {
529 // Everything loaded bind the data.
530 mModelLoaded = true;
531 }
532 }
533
534 @Override
535 public void close() {
536 synchronized (mLock) {
537 // If we are still the last one to be scheduled, remove ourselves.
538 if (mLoaderTask == mTask) {
539 mLoaderTask = null;
540 }
541 mIsLoaderTaskRunning = false;
542 }
543 }
544 }
545
Pinyao Ting777c13e2022-09-16 09:44:26 -0700546 public LoaderTransaction beginLoader(@NonNull final LoaderTask task)
547 throws CancellationException {
Sunny Goyalc6e97692017-06-02 13:46:55 -0700548 return new LoaderTransaction(task);
549 }
550
Joe Onorato36115782010-06-17 13:28:48 -0400551 /**
Sunny Goyal95f3d6b2016-08-10 16:09:29 -0700552 * Refreshes the cached shortcuts if the shortcut permission has changed.
553 * Current implementation simply reloads the workspace, but it can be optimized to
Sunny Goyal337c81f2019-12-10 12:19:13 -0800554 * use partial updates similar to {@link UserCache}
Sunny Goyal95f3d6b2016-08-10 16:09:29 -0700555 */
Sunny Goyal8b74cc72020-07-27 17:50:33 -0700556 public void validateModelDataOnResume() {
557 MODEL_EXECUTOR.getHandler().removeCallbacks(mDataValidationCheck);
558 MODEL_EXECUTOR.post(mDataValidationCheck);
Sunny Goyal95f3d6b2016-08-10 16:09:29 -0700559 }
560
561 /**
Sunny Goyal75b0f552015-05-20 21:57:06 -0700562 * Called when the icons for packages have been updated in the icon cache.
563 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700564 public void onPackageIconsUpdated(@NonNull final HashSet<String> updatedPackages,
565 @NonNull final UserHandle user) {
Sunny Goyal75b0f552015-05-20 21:57:06 -0700566 // If any package icon has changed (app was updated while launcher was dead),
567 // update the corresponding shortcuts.
Sunny Goyalf0ba8b72016-09-09 15:47:55 -0700568 enqueueModelUpdateTask(new CacheDataUpdatedTask(
569 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
Sunny Goyal75b0f552015-05-20 21:57:06 -0700570 }
571
Sunny Goyalac8154a2018-09-26 12:00:30 -0700572 /**
573 * Called when the labels for the widgets has updated in the icon cache.
574 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700575 public void onWidgetLabelsUpdated(@NonNull final HashSet<String> updatedPackages,
576 @NonNull final UserHandle user) {
Sunny Goyal99389382024-05-01 14:47:43 -0700577 enqueueModelUpdateTask((taskController, dataModel, apps) -> {
578 dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, mApp);
579 taskController.bindUpdatedWidgets(dataModel);
Sunny Goyalac8154a2018-09-26 12:00:30 -0700580 });
581 }
582
Pinyao Ting777c13e2022-09-16 09:44:26 -0700583 public void enqueueModelUpdateTask(@NonNull final ModelUpdateTask task) {
Sunny Goyal777d4902021-08-27 21:22:17 +0000584 if (mModelDestroyed) {
585 return;
586 }
Sunny Goyal99389382024-05-01 14:47:43 -0700587 MODEL_EXECUTOR.execute(() -> {
588 if (!isModelLoaded()) {
589 // Loader has not yet run.
590 return;
591 }
592 ModelTaskController controller = new ModelTaskController(
593 mApp, mBgDataModel, mBgAllAppsList, this, MAIN_EXECUTOR);
594 task.execute(controller, mBgDataModel, mBgAllAppsList);
595 });
Sunny Goyald3b87ef2016-07-28 12:11:54 -0700596 }
597
Sunny Goyalf0ba8b72016-09-09 15:47:55 -0700598 /**
599 * A task to be executed on the current callbacks on the UI thread.
600 * If there is no current callbacks, the task is ignored.
601 */
602 public interface CallbackTask {
Tony Wickhambfbf7f92016-05-19 11:19:39 -0700603
Pinyao Ting777c13e2022-09-16 09:44:26 -0700604 void execute(@NonNull Callbacks callbacks);
Tony Wickhambfbf7f92016-05-19 11:19:39 -0700605 }
606
Sunny Goyal99389382024-05-01 14:47:43 -0700607 public interface ModelUpdateTask {
Joe Onorato36115782010-06-17 13:28:48 -0400608
Sunny Goyal99389382024-05-01 14:47:43 -0700609 void execute(@NonNull ModelTaskController taskController,
610 @NonNull BgDataModel dataModel, @NonNull AllAppsList apps);
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800611 }
612
Pinyao Ting777c13e2022-09-16 09:44:26 -0700613 public void updateAndBindWorkspaceItem(@NonNull final WorkspaceItemInfo si,
614 @NonNull final ShortcutInfo info) {
Sunny Goyal95899162019-03-27 16:03:06 -0700615 updateAndBindWorkspaceItem(() -> {
Sunny Goyal83fd25e2018-07-09 16:47:01 -0700616 si.updateFromDeepShortcutInfo(info, mApp.getContext());
Sunny Goyal18204e42020-02-06 11:28:01 -0800617 mApp.getIconCache().getShortcutIcon(si, info);
Sunny Goyal83fd25e2018-07-09 16:47:01 -0700618 return si;
Sunny Goyal1b072632017-01-18 11:30:23 -0800619 });
620 }
621
Sunny Goyal10923b32016-07-20 15:42:44 -0700622 /**
Sunny Goyal1cc1c9a2017-01-06 16:32:57 -0800623 * Utility method to update a shortcut on the background thread.
Sunny Goyal10923b32016-07-20 15:42:44 -0700624 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700625 public void updateAndBindWorkspaceItem(
626 @NonNull final Supplier<WorkspaceItemInfo> itemProvider) {
Sunny Goyal99389382024-05-01 14:47:43 -0700627 enqueueModelUpdateTask((taskController, dataModel, apps) -> {
628 WorkspaceItemInfo info = itemProvider.get();
629 taskController.getModelWriter().updateItemInDatabase(info);
630 ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
631 update.add(info);
632 taskController.bindUpdatedWorkspaceItems(update);
Sunny Goyal10923b32016-07-20 15:42:44 -0700633 });
634 }
635
Sunny Goyalc6e97692017-06-02 13:46:55 -0700636 public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
Sunny Goyal99389382024-05-01 14:47:43 -0700637 enqueueModelUpdateTask((taskController, dataModel, apps) -> {
638 dataModel.widgetsModel.update(taskController.getApp(), packageUser);
639 taskController.bindUpdatedWidgets(dataModel);
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800640 });
Michael Jurkac402cd92013-05-20 15:49:32 +0200641 }
642
Pinyao Ting777c13e2022-09-16 09:44:26 -0700643 public void dumpState(@Nullable final String prefix, @Nullable final FileDescriptor fd,
644 @NonNull final PrintWriter writer, @NonNull final String[] args) {
Hyunyoung Song3c7d9cb2017-01-30 15:11:27 -0800645 if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
646 writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
647 for (AppInfo info : mBgAllAppsList.data) {
Sunny Goyal3808a692019-10-25 13:41:28 -0700648 writer.println(prefix + " title=\"" + info.title
649 + "\" bitmapIcon=" + info.bitmap.icon
Hyunyoung Song3c7d9cb2017-01-30 15:11:27 -0800650 + " componentName=" + info.componentName.getPackageName());
651 }
Andy Wickhambb993c42021-02-26 12:56:25 -0800652 writer.println();
Joe Onorato36115782010-06-17 13:28:48 -0400653 }
Andy Wickhambb993c42021-02-26 12:56:25 -0800654 mModelDelegate.dump(prefix, fd, writer, args);
Sunny Goyal9ae9b602019-12-18 19:29:00 +0530655 mBgDataModel.dump(prefix, fd, writer, args);
Joe Onoratobe386092009-11-17 17:32:16 -0800656 }
Sunny Goyale0f58d72014-11-10 18:05:31 -0800657
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530658 /**
659 * Returns true if there are any callbacks attached to the model
660 */
661 public boolean hasCallbacks() {
662 synchronized (mCallbacksList) {
663 return !mCallbacksList.isEmpty();
664 }
665 }
666
667 /**
668 * Returns an array of currently attached callbacks
669 */
Pinyao Ting777c13e2022-09-16 09:44:26 -0700670 @NonNull
Sunny Goyala7a5bf32020-01-05 15:35:29 +0530671 public Callbacks[] getCallbacks() {
672 synchronized (mCallbacksList) {
673 return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
674 }
Sunny Goyale0f58d72014-11-10 18:05:31 -0800675 }
Sunny Goyal7b9e28f2023-05-17 12:44:03 -0700676
677 /**
678 * Returns the ID for the last model load. If the load ID doesn't match for a transaction, the
679 * transaction should be ignored.
680 */
681 public int getLastLoadId() {
682 return mLastLoadId;
683 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800684}