From c80e60dc7ab88f0bf23b42546811411a6727969b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 7 Jun 2023 12:52:30 -0700 Subject: [PATCH] Making UserCache the source of truth for all user events Bug: 243688989 Test: Presubmit Flag: N/A Change-Id: I0e6b853d965eff1abaeb3b26dd6b94424e5212df --- src/com/android/launcher3/Launcher.java | 13 +- .../android/launcher3/LauncherAppState.java | 6 +- src/com/android/launcher3/LauncherModel.java | 58 +++--- .../allapps/ActivityAllAppsContainerView.java | 5 +- src/com/android/launcher3/pm/UserCache.java | 170 +++++++++--------- 5 files changed, 118 insertions(+), 134 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8071ae41ef..caf5755058 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -183,7 +183,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.pm.PinRequestHelper; -import com.android.launcher3.pm.UserCache; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; @@ -208,7 +207,6 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; -import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; import com.android.launcher3.util.SystemUiController; @@ -411,8 +409,6 @@ public class Launcher extends StatefulActivity protected long mLastTouchUpTime = -1; private boolean mTouchInProgress; - private SafeCloseable mUserChangedCallbackCloseable; - // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. // User actions within AllApps state are logged with this InstanceId, to recreate AllApps @@ -581,9 +577,6 @@ public class Launcher extends StatefulActivity mRotationHelper.initialize(); TraceHelper.INSTANCE.endSection(); - mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( - () -> getStateManager().goToState(NORMAL)); - if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); } @@ -1804,7 +1797,6 @@ public class Launcher extends StatefulActivity LauncherAppState.getIDP(this).removeOnChangeListener(this); mOverlayManager.onActivityDestroyed(this); - mUserChangedCallbackCloseable.close(); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { @@ -2974,9 +2966,14 @@ public class Launcher extends StatefulActivity public void bindAllApplications(AppInfo[] apps, int flags, Map packageUserKeytoUidMap) { Preconditions.assertUIThread(); + boolean hadWorkApps = mAppsView.shouldShowTabs(); AllAppsStore appsStore = mAppsView.getAppsStore(); appsStore.setApps(apps, flags, packageUserKeytoUidMap); PopupContainerWithArrow.dismissInvalidPopup(this); + if (hadWorkApps != mAppsView.shouldShowTabs()) { + getStateManager().goToState(NORMAL); + } + if (Utilities.ATLEAST_S) { Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, DISPLAY_ALL_APPS_TRACE_COOKIE); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 4b7aeeb898..eb1c4d440c 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -111,10 +111,6 @@ public class LauncherAppState implements SafeCloseable { SimpleBroadcastReceiver modelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, - Intent.ACTION_MANAGED_PROFILE_AVAILABLE, - Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, - Intent.ACTION_MANAGED_PROFILE_UNLOCKED, - Intent.ACTION_PROFILE_INACCESSIBLE, ACTION_DEVICE_POLICY_RESOURCE_UPDATED); if (FeatureFlags.IS_STUDIO_BUILD) { modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD); @@ -122,7 +118,7 @@ public class LauncherAppState implements SafeCloseable { mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) - .addUserChangeListener(mModel::forceReload); + .addUserEventListener(mModel::onUserEvent); mOnTerminateCallback.add(userChangeListener::close); LockedUserState.get(context).runOnUserUnlocked(() -> { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f225f855f1..ddafd539af 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -95,15 +95,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi static final String TAG = "Launcher.Model"; - // Broadcast intent to track when the profile gets locked: - // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets - // locked when paused. - // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only - // appeared in Android S. - private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U - ? Intent.ACTION_PROFILE_INACCESSIBLE - : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; - @NonNull private final LauncherAppState mApp; @NonNull @@ -303,28 +294,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action) - || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) { - UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + - " user: " + user); - } - if (user != null) { - if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || - Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { - enqueueModelUpdateTask(new PackageUpdatedTask( - PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); - } - - if (ACTION_PROFILE_LOCKED.equals(action) - || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { - enqueueModelUpdateTask(new UserLockStateChangedTask( - user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action))); - } - } } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) { enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate)); } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { @@ -336,6 +305,33 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } + /** + * Called then there use a user event + * @see UserCache#addUserEventListener + */ + public void onUserEvent(UserHandle user, String action) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + + action + " user: " + user); + } + + if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) + || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { + enqueueModelUpdateTask(new PackageUpdatedTask( + PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); + } + + if (UserCache.ACTION_PROFILE_LOCKED.equals(action) + || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) { + enqueueModelUpdateTask(new UserLockStateChangedTask( + user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action))); + } + if (UserCache.ACTION_PROFILE_ADDED.equals(action) + || UserCache.ACTION_PROFILE_REMOVED.equals(action)) { + forceReload(); + } + } + /** * Reloads the workspace items from the DB and re-binds the workspace. This should generally * not be called as DB updates are automatically followed by UI update diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 898009dead..2b8b2c9717 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -1100,7 +1100,10 @@ public class ActivityAllAppsContainerView } } - protected boolean shouldShowTabs() { + /** + * Returns true if the container has work apps. + */ + public boolean shouldShowTabs() { return mHasWorkApps; } diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java index 24a9609a83..c3138865fa 100644 --- a/src/com/android/launcher3/pm/UserCache.java +++ b/src/com/android/launcher3/pm/UserCache.java @@ -16,16 +16,21 @@ package com.android.launcher3.pm; +import static com.android.launcher3.Utilities.ATLEAST_U; import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; -import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; import static com.android.launcher3.testing.shared.TestProtocol.testLogD; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import android.content.Intent; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; -import android.util.LongSparseArray; + +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SafeCloseable; @@ -34,136 +39,123 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; /** * Class which manages a local cache of user handles to avoid system rpc */ -public class UserCache { +public class UserCache implements SafeCloseable { + + public static final String ACTION_PROFILE_ADDED = ATLEAST_U + ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; + public static final String ACTION_PROFILE_REMOVED = ATLEAST_U + ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; + + public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U + ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; + public static final String ACTION_PROFILE_LOCKED = ATLEAST_U + ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(UserCache::new); - private final Context mContext; - private final UserManager mUserManager; - private final ArrayList mUserChangeListeners = new ArrayList<>(); + private final List> mUserEventListeners = new ArrayList<>(); private final SimpleBroadcastReceiver mUserChangeReceiver = new SimpleBroadcastReceiver(this::onUsersChanged); - private LongSparseArray mUsers; - // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same - // and not {@link Object#equals} - private ArrayMap mUserToSerialMap; + private final Context mContext; + + @NonNull + private Map mUserToSerialMap; private UserCache(Context context) { mContext = context; - mUserManager = context.getSystemService(UserManager.class); + mUserToSerialMap = Collections.emptyMap(); + MODEL_EXECUTOR.execute(this::initAsync); } + @Override + public void close() { + MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); + } + + @WorkerThread + private void initAsync() { + mUserChangeReceiver.register(mContext, + Intent.ACTION_MANAGED_PROFILE_AVAILABLE, + Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, + ACTION_PROFILE_ADDED, + ACTION_PROFILE_REMOVED, + ACTION_PROFILE_UNLOCKED, + ACTION_PROFILE_LOCKED); + updateCache(); + } + + @AnyThread private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); - enableAndResetCache(); - mUserChangeListeners.forEach(Runnable::run); + + MODEL_EXECUTOR.execute(this::updateCache); + UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); + if (user == null) { + return; + } + String action = intent.getAction(); + mUserEventListeners.forEach(l -> l.accept(user, action)); + } + + @WorkerThread + private void updateCache() { + mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class)); } /** * Adds a listener for user additions and removals */ - public SafeCloseable addUserChangeListener(Runnable command) { - synchronized (this) { - if (mUserChangeListeners.isEmpty()) { - // Enable caching and start listening for user broadcast - mUserChangeReceiver.register(mContext, - Intent.ACTION_MANAGED_PROFILE_ADDED, - Intent.ACTION_MANAGED_PROFILE_REMOVED); - enableAndResetCache(); - } - mUserChangeListeners.add(command); - return () -> removeUserChangeListener(command); - } - } - - private void enableAndResetCache() { - synchronized (this) { - mUsers = new LongSparseArray<>(); - mUserToSerialMap = new ArrayMap<>(); - List users = mUserManager.getUserProfiles(); - if (users != null) { - for (UserHandle user : users) { - testLogD(WORK_TAB_MISSING, "caching user: " + user); - long serial = mUserManager.getSerialNumberForUser(user); - mUsers.put(serial, user); - mUserToSerialMap.put(user, serial); - } - } - } - } - - private void removeUserChangeListener(Runnable command) { - synchronized (this) { - mUserChangeListeners.remove(command); - if (mUserChangeListeners.isEmpty()) { - // Disable cache and stop listening - mContext.unregisterReceiver(mUserChangeReceiver); - - mUsers = null; - mUserToSerialMap = null; - } - } + public SafeCloseable addUserEventListener(BiConsumer listener) { + mUserEventListeners.add(listener); + return () -> mUserEventListeners.remove(listener); } /** * @see UserManager#getSerialNumberForUser(UserHandle) */ public long getSerialNumberForUser(UserHandle user) { - synchronized (this) { - if (mUserToSerialMap != null) { - Long serial = mUserToSerialMap.get(user); - return serial == null ? 0 : serial; - } - } - return mUserManager.getSerialNumberForUser(user); + Long serial = mUserToSerialMap.get(user); + return serial == null ? 0 : serial; } /** * @see UserManager#getUserForSerialNumber(long) */ public UserHandle getUserForSerialNumber(long serialNumber) { - synchronized (this) { - if (mUsers != null) { - return mUsers.get(serialNumber); - } - } - return mUserManager.getUserForSerialNumber(serialNumber); + Long value = serialNumber; + return mUserToSerialMap + .entrySet() + .stream() + .filter(entry -> value.equals(entry.getValue())) + .findFirst() + .map(Map.Entry::getKey) + .orElse(Process.myUserHandle()); } /** * @see UserManager#getUserProfiles() */ public List getUserProfiles() { - StringBuilder usersToReturn = new StringBuilder(); - List users; - if (sDebugTracing) { - users = mUserManager.getUserProfiles(); - for (UserHandle u : users) { - usersToReturn.append(u).append(" && "); - } - testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn); - } + return List.copyOf(mUserToSerialMap.keySet()); + } - synchronized (this) { - if (mUsers != null) { - usersToReturn = new StringBuilder(); - for (UserHandle u : mUserToSerialMap.keySet()) { - usersToReturn.append(u).append(" && "); - } - testLogD(WORK_TAB_MISSING, "users from cache: " + usersToReturn); - return new ArrayList<>(mUserToSerialMap.keySet()); - } else { - testLogD(WORK_TAB_MISSING, "users from cache null"); + private static Map queryAllUsers(UserManager userManager) { + Map users = new ArrayMap<>(); + List usersActual = userManager.getUserProfiles(); + if (usersActual != null) { + for (UserHandle user : usersActual) { + long serial = userManager.getSerialNumberForUser(user); + users.put(user, serial); } } - - users = mUserManager.getUserProfiles(); - return users == null ? Collections.emptyList() : users; + return users; } }