Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74a27fb3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Making UserCache the source of truth for all user events" into udc-qpr-dev

parents 0a13b2de c80e60dc
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -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<LauncherState>
    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<LauncherState>
        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<LauncherState>
        LauncherAppState.getIDP(this).removeOnChangeListener(this);

        mOverlayManager.onActivityDestroyed(this);
        mUserChangedCallbackCloseable.close();
    }

    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -2974,9 +2966,14 @@ public class Launcher extends StatefulActivity<LauncherState>
    public void bindAllApplications(AppInfo[] apps, int flags,
            Map<PackageUserKey, Integer> 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);
+1 −5
Original line number Diff line number Diff line
@@ -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(() -> {
+27 −31
Original line number Diff line number Diff line
@@ -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
+4 −1
Original line number Diff line number Diff line
@@ -1150,7 +1150,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
        }
    }

    protected boolean shouldShowTabs() {
    /**
     * Returns true if the container has work apps.
     */
    public boolean shouldShowTabs() {
        return mHasWorkApps;
    }

+80 −88
Original line number Diff line number Diff line
@@ -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<UserCache> INSTANCE =
            new MainThreadInitializedObject<>(UserCache::new);

    private final Context mContext;
    private final UserManager mUserManager;
    private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>();
    private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>();
    private final SimpleBroadcastReceiver mUserChangeReceiver =
            new SimpleBroadcastReceiver(this::onUsersChanged);

    private LongSparseArray<UserHandle> mUsers;
    // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same
    // and not {@link Object#equals}
    private ArrayMap<UserHandle, Long> mUserToSerialMap;
    private final Context mContext;

    @NonNull
    private Map<UserHandle, Long> mUserToSerialMap;

    private UserCache(Context context) {
        mContext = context;
        mUserManager = context.getSystemService(UserManager.class);
        mUserToSerialMap = Collections.emptyMap();
        MODEL_EXECUTOR.execute(this::initAsync);
    }

    private void onUsersChanged(Intent intent) {
        testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent);
        enableAndResetCache();
        mUserChangeListeners.forEach(Runnable::run);
    @Override
    public void close() {
        MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext));
    }

    /**
     * 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
    @WorkerThread
    private void initAsync() {
        mUserChangeReceiver.register(mContext,
                        Intent.ACTION_MANAGED_PROFILE_ADDED,
                        Intent.ACTION_MANAGED_PROFILE_REMOVED);
                enableAndResetCache();
            }
            mUserChangeListeners.add(command);
            return () -> removeUserChangeListener(command);
        }
                Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                ACTION_PROFILE_ADDED,
                ACTION_PROFILE_REMOVED,
                ACTION_PROFILE_UNLOCKED,
                ACTION_PROFILE_LOCKED);
        updateCache();
    }

    private void enableAndResetCache() {
        synchronized (this) {
            mUsers = new LongSparseArray<>();
            mUserToSerialMap = new ArrayMap<>();
            List<UserHandle> 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);
                }
            }
    @AnyThread
    private void onUsersChanged(Intent intent) {
        testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent);

        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));
    }

    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;
            }
    @WorkerThread
    private void updateCache() {
        mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class));
    }

    /**
     * Adds a listener for user additions and removals
     */
    public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> 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);
    }

    /**
     * @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<UserHandle> getUserProfiles() {
        StringBuilder usersToReturn = new StringBuilder();
        List<UserHandle> 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<UserHandle, Long> queryAllUsers(UserManager userManager) {
        Map<UserHandle, Long> users = new ArrayMap<>();
        List<UserHandle> 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;
    }
}