/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.pm; import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import android.content.Context; import android.content.Intent; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.LongSparseArray; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Class which manages a local cache of user handles to avoid system rpc */ public class UserCache { 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 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 UserCache(Context context) { mContext = context; mUserManager = context.getSystemService(UserManager.class); } private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); enableAndResetCache(); mUserChangeListeners.forEach(Runnable::run); } /** * 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; } } } /** * @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); } /** * @see UserManager#getUserProfiles() */ public List getUserProfiles() { StringBuilder usersToReturn = new StringBuilder(); synchronized (this) { if (mUsers != null) { 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"); } } List users = mUserManager.getUserProfiles(); for (UserHandle u : users) { usersToReturn.append(u).append(" && "); } testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn); return users == null ? Collections.emptyList() : users; } }