1 /* 2 * Copyright (C) 2014 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.pm; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.os.UserHandle; 22 import android.os.UserManager; 23 import android.util.ArrayMap; 24 import android.util.LongSparseArray; 25 26 import com.android.launcher3.util.MainThreadInitializedObject; 27 import com.android.launcher3.util.SafeCloseable; 28 import com.android.launcher3.util.SimpleBroadcastReceiver; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 34 /** 35 * Class which manages a local cache of user handles to avoid system rpc 36 */ 37 public class UserCache { 38 39 public static final MainThreadInitializedObject<UserCache> INSTANCE = 40 new MainThreadInitializedObject<>(UserCache::new); 41 42 private final Context mContext; 43 private final UserManager mUserManager; 44 private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>(); 45 private final SimpleBroadcastReceiver mUserChangeReceiver = 46 new SimpleBroadcastReceiver(this::onUsersChanged); 47 48 private LongSparseArray<UserHandle> mUsers; 49 // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same 50 // and not {@link Object#equals} 51 private ArrayMap<UserHandle, Long> mUserToSerialMap; 52 UserCache(Context context)53 private UserCache(Context context) { 54 mContext = context; 55 mUserManager = context.getSystemService(UserManager.class); 56 } 57 onUsersChanged(Intent intent)58 private void onUsersChanged(Intent intent) { 59 enableAndResetCache(); 60 mUserChangeListeners.forEach(Runnable::run); 61 } 62 63 /** 64 * Adds a listener for user additions and removals 65 */ addUserChangeListener(Runnable command)66 public SafeCloseable addUserChangeListener(Runnable command) { 67 synchronized (this) { 68 if (mUserChangeListeners.isEmpty()) { 69 // Enable caching and start listening for user broadcast 70 mUserChangeReceiver.register(mContext, 71 Intent.ACTION_MANAGED_PROFILE_ADDED, 72 Intent.ACTION_MANAGED_PROFILE_REMOVED); 73 enableAndResetCache(); 74 } 75 mUserChangeListeners.add(command); 76 return () -> removeUserChangeListener(command); 77 } 78 } 79 enableAndResetCache()80 private void enableAndResetCache() { 81 synchronized (this) { 82 mUsers = new LongSparseArray<>(); 83 mUserToSerialMap = new ArrayMap<>(); 84 List<UserHandle> users = mUserManager.getUserProfiles(); 85 if (users != null) { 86 for (UserHandle user : users) { 87 long serial = mUserManager.getSerialNumberForUser(user); 88 mUsers.put(serial, user); 89 mUserToSerialMap.put(user, serial); 90 } 91 } 92 } 93 } 94 removeUserChangeListener(Runnable command)95 private void removeUserChangeListener(Runnable command) { 96 synchronized (this) { 97 mUserChangeListeners.remove(command); 98 if (mUserChangeListeners.isEmpty()) { 99 // Disable cache and stop listening 100 mContext.unregisterReceiver(mUserChangeReceiver); 101 102 mUsers = null; 103 mUserToSerialMap = null; 104 } 105 } 106 } 107 108 /** 109 * @see UserManager#getSerialNumberForUser(UserHandle) 110 */ getSerialNumberForUser(UserHandle user)111 public long getSerialNumberForUser(UserHandle user) { 112 synchronized (this) { 113 if (mUserToSerialMap != null) { 114 Long serial = mUserToSerialMap.get(user); 115 return serial == null ? 0 : serial; 116 } 117 } 118 return mUserManager.getSerialNumberForUser(user); 119 } 120 121 /** 122 * @see UserManager#getUserForSerialNumber(long) 123 */ getUserForSerialNumber(long serialNumber)124 public UserHandle getUserForSerialNumber(long serialNumber) { 125 synchronized (this) { 126 if (mUsers != null) { 127 return mUsers.get(serialNumber); 128 } 129 } 130 return mUserManager.getUserForSerialNumber(serialNumber); 131 } 132 133 /** 134 * @see UserManager#getUserProfiles() 135 */ getUserProfiles()136 public List<UserHandle> getUserProfiles() { 137 synchronized (this) { 138 if (mUsers != null) { 139 return new ArrayList<>(mUserToSerialMap.keySet()); 140 } 141 } 142 143 List<UserHandle> users = mUserManager.getUserProfiles(); 144 return users == null ? Collections.emptyList() : users; 145 } 146 } 147