• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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