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