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.Nullable; 32 import androidx.annotation.VisibleForTesting; 33 import androidx.annotation.WorkerThread; 34 35 import com.android.launcher3.icons.BitmapInfo; 36 import com.android.launcher3.icons.UserBadgeDrawable; 37 import com.android.launcher3.util.ApiWrapper; 38 import com.android.launcher3.util.FlagOp; 39 import com.android.launcher3.util.MainThreadInitializedObject; 40 import com.android.launcher3.util.SafeCloseable; 41 import com.android.launcher3.util.SimpleBroadcastReceiver; 42 import com.android.launcher3.util.UserIconInfo; 43 44 import java.util.ArrayList; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.function.BiConsumer; 49 50 /** 51 * Class which manages a local cache of user handles to avoid system rpc 52 */ 53 public class UserCache implements SafeCloseable { 54 55 public static final String ACTION_PROFILE_ADDED = ATLEAST_U 56 ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; 57 public static final String ACTION_PROFILE_REMOVED = ATLEAST_U 58 ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; 59 60 public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U 61 ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; 62 public static final String ACTION_PROFILE_LOCKED = ATLEAST_U 63 ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; 64 public static final String ACTION_PROFILE_AVAILABLE = "android.intent.action.PROFILE_AVAILABLE"; 65 public static final String ACTION_PROFILE_UNAVAILABLE = 66 "android.intent.action.PROFILE_UNAVAILABLE"; 67 68 public static final MainThreadInitializedObject<UserCache> INSTANCE = 69 new MainThreadInitializedObject<>(UserCache::new); 70 71 /** Returns an instance of UserCache bound to the context provided. */ getInstance(Context context)72 public static UserCache getInstance(Context context) { 73 return INSTANCE.get(context); 74 } 75 76 private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>(); 77 private final SimpleBroadcastReceiver mUserChangeReceiver = 78 new SimpleBroadcastReceiver(this::onUsersChanged); 79 80 private final Context mContext; 81 82 @NonNull 83 private Map<UserHandle, UserIconInfo> mUserToSerialMap; 84 85 @NonNull 86 private Map<UserHandle, List<String>> mUserToPreInstallAppMap; 87 UserCache(Context context)88 private UserCache(Context context) { 89 mContext = context; 90 mUserToSerialMap = Collections.emptyMap(); 91 MODEL_EXECUTOR.execute(this::initAsync); 92 } 93 94 @Override close()95 public void close() { 96 MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); 97 } 98 99 @WorkerThread initAsync()100 private void initAsync() { 101 mUserChangeReceiver.register(mContext, 102 Intent.ACTION_MANAGED_PROFILE_AVAILABLE, 103 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, 104 Intent.ACTION_MANAGED_PROFILE_REMOVED, 105 ACTION_PROFILE_ADDED, 106 ACTION_PROFILE_REMOVED, 107 ACTION_PROFILE_UNLOCKED, 108 ACTION_PROFILE_LOCKED, 109 ACTION_PROFILE_AVAILABLE, 110 ACTION_PROFILE_UNAVAILABLE); 111 updateCache(); 112 } 113 114 @AnyThread onUsersChanged(Intent intent)115 private void onUsersChanged(Intent intent) { 116 MODEL_EXECUTOR.execute(this::updateCache); 117 UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); 118 if (user == null) { 119 return; 120 } 121 String action = intent.getAction(); 122 mUserEventListeners.forEach(l -> l.accept(user, action)); 123 } 124 125 @WorkerThread updateCache()126 private void updateCache() { 127 mUserToSerialMap = ApiWrapper.INSTANCE.get(mContext).queryAllUsers(); 128 mUserToPreInstallAppMap = fetchPreInstallApps(); 129 } 130 131 @WorkerThread fetchPreInstallApps()132 private Map<UserHandle, List<String>> fetchPreInstallApps() { 133 Map<UserHandle, List<String>> userToPreInstallApp = new ArrayMap<>(); 134 mUserToSerialMap.forEach((userHandle, userIconInfo) -> { 135 // Fetch only for private profile, as other profiles have no usages yet. 136 List<String> preInstallApp = userIconInfo.isPrivate() 137 ? ApiWrapper.INSTANCE.get(mContext).getPreInstalledSystemPackages(userHandle) 138 : new ArrayList<>(); 139 userToPreInstallApp.put(userHandle, preInstallApp); 140 }); 141 return userToPreInstallApp; 142 } 143 144 /** 145 * Adds a listener for user additions and removals 146 */ addUserEventListener(BiConsumer<UserHandle, String> listener)147 public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) { 148 mUserEventListeners.add(listener); 149 return () -> mUserEventListeners.remove(listener); 150 } 151 152 /** 153 * @see UserManager#getSerialNumberForUser(UserHandle) 154 */ getSerialNumberForUser(UserHandle user)155 public long getSerialNumberForUser(UserHandle user) { 156 return getUserInfo(user).userSerial; 157 } 158 159 /** 160 * Returns the user properties for the provided user or default values 161 */ 162 @NonNull getUserInfo(UserHandle user)163 public UserIconInfo getUserInfo(UserHandle user) { 164 UserIconInfo info = mUserToSerialMap.get(user); 165 return info == null ? new UserIconInfo(user, UserIconInfo.TYPE_MAIN) : info; 166 } 167 168 /** 169 * @see UserManager#getUserForSerialNumber(long) 170 */ getUserForSerialNumber(long serialNumber)171 public UserHandle getUserForSerialNumber(long serialNumber) { 172 return mUserToSerialMap 173 .entrySet() 174 .stream() 175 .filter(entry -> serialNumber == entry.getValue().userSerial) 176 .findFirst() 177 .map(Map.Entry::getKey) 178 .orElse(Process.myUserHandle()); 179 } 180 181 @VisibleForTesting putToCache(UserHandle userHandle, UserIconInfo info)182 public void putToCache(UserHandle userHandle, UserIconInfo info) { 183 mUserToSerialMap.put(userHandle, info); 184 } 185 186 /** 187 * @see UserManager#getUserProfiles() 188 */ getUserProfiles()189 public List<UserHandle> getUserProfiles() { 190 return List.copyOf(mUserToSerialMap.keySet()); 191 } 192 193 /** 194 * Returns the pre-installed apps for a user. 195 */ 196 @NonNull getPreInstallApps(UserHandle user)197 public List<String> getPreInstallApps(UserHandle user) { 198 List<String> preInstallApp = mUserToPreInstallAppMap.get(user); 199 return preInstallApp == null ? new ArrayList<>() : preInstallApp; 200 } 201 202 /** 203 * Get a non-themed {@link UserBadgeDrawable} based on the provided {@link UserHandle}. 204 */ 205 @Nullable getBadgeDrawable(Context context, UserHandle userHandle)206 public static UserBadgeDrawable getBadgeDrawable(Context context, UserHandle userHandle) { 207 return (UserBadgeDrawable) BitmapInfo.LOW_RES_INFO.withFlags(UserCache.getInstance(context) 208 .getUserInfo(userHandle).applyBitmapInfoFlags(FlagOp.NO_OP)) 209 .getBadgeDrawable(context, false /* isThemed */); 210 } 211 } 212