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