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 static com.android.launcher3.Utilities.ATLEAST_U; 20 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.Process; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.util.ArrayMap; 28 29 import androidx.annotation.AnyThread; 30 import androidx.annotation.NonNull; 31 import androidx.annotation.WorkerThread; 32 33 import com.android.launcher3.util.MainThreadInitializedObject; 34 import com.android.launcher3.util.SafeCloseable; 35 import com.android.launcher3.util.SimpleBroadcastReceiver; 36 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.function.BiConsumer; 42 43 /** 44 * Class which manages a local cache of user handles to avoid system rpc 45 */ 46 public class UserCache implements SafeCloseable { 47 48 public static final String ACTION_PROFILE_ADDED = ATLEAST_U 49 ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; 50 public static final String ACTION_PROFILE_REMOVED = ATLEAST_U 51 ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; 52 53 public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U 54 ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; 55 public static final String ACTION_PROFILE_LOCKED = ATLEAST_U 56 ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; 57 58 public static final MainThreadInitializedObject<UserCache> INSTANCE = 59 new MainThreadInitializedObject<>(UserCache::new); 60 61 private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>(); 62 private final SimpleBroadcastReceiver mUserChangeReceiver = 63 new SimpleBroadcastReceiver(this::onUsersChanged); 64 65 private final Context mContext; 66 67 @NonNull 68 private Map<UserHandle, Long> mUserToSerialMap; 69 UserCache(Context context)70 private UserCache(Context context) { 71 mContext = context; 72 mUserToSerialMap = Collections.emptyMap(); 73 MODEL_EXECUTOR.execute(this::initAsync); 74 } 75 76 @Override close()77 public void close() { 78 MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); 79 } 80 81 @WorkerThread initAsync()82 private void initAsync() { 83 mUserChangeReceiver.register(mContext, 84 Intent.ACTION_MANAGED_PROFILE_AVAILABLE, 85 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, 86 ACTION_PROFILE_ADDED, 87 ACTION_PROFILE_REMOVED, 88 ACTION_PROFILE_UNLOCKED, 89 ACTION_PROFILE_LOCKED); 90 updateCache(); 91 } 92 93 @AnyThread onUsersChanged(Intent intent)94 private void onUsersChanged(Intent intent) { 95 MODEL_EXECUTOR.execute(this::updateCache); 96 UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); 97 if (user == null) { 98 return; 99 } 100 String action = intent.getAction(); 101 mUserEventListeners.forEach(l -> l.accept(user, action)); 102 } 103 104 @WorkerThread updateCache()105 private void updateCache() { 106 mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class)); 107 } 108 109 /** 110 * Adds a listener for user additions and removals 111 */ addUserEventListener(BiConsumer<UserHandle, String> listener)112 public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) { 113 mUserEventListeners.add(listener); 114 return () -> mUserEventListeners.remove(listener); 115 } 116 117 /** 118 * @see UserManager#getSerialNumberForUser(UserHandle) 119 */ getSerialNumberForUser(UserHandle user)120 public long getSerialNumberForUser(UserHandle user) { 121 Long serial = mUserToSerialMap.get(user); 122 return serial == null ? 0 : serial; 123 } 124 125 /** 126 * @see UserManager#getUserForSerialNumber(long) 127 */ getUserForSerialNumber(long serialNumber)128 public UserHandle getUserForSerialNumber(long serialNumber) { 129 Long value = serialNumber; 130 return mUserToSerialMap 131 .entrySet() 132 .stream() 133 .filter(entry -> value.equals(entry.getValue())) 134 .findFirst() 135 .map(Map.Entry::getKey) 136 .orElse(Process.myUserHandle()); 137 } 138 139 /** 140 * @see UserManager#getUserProfiles() 141 */ getUserProfiles()142 public List<UserHandle> getUserProfiles() { 143 return List.copyOf(mUserToSerialMap.keySet()); 144 } 145 queryAllUsers(UserManager userManager)146 private static Map<UserHandle, Long> queryAllUsers(UserManager userManager) { 147 Map<UserHandle, Long> users = new ArrayMap<>(); 148 List<UserHandle> usersActual = userManager.getUserProfiles(); 149 if (usersActual != null) { 150 for (UserHandle user : usersActual) { 151 long serial = userManager.getSerialNumberForUser(user); 152 users.put(user, serial); 153 } 154 } 155 return users; 156 } 157 } 158