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