1 /*
2  * Copyright (C) 2022 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 
18 package com.android.systemui.user.data.repository
19 
20 import android.annotation.SuppressLint
21 import android.annotation.UserIdInt
22 import android.content.Context
23 import android.content.pm.UserInfo
24 import android.os.UserHandle
25 import android.os.UserManager
26 import android.provider.Settings
27 import androidx.annotation.VisibleForTesting
28 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
29 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.dagger.qualifiers.Application
32 import com.android.systemui.dagger.qualifiers.Background
33 import com.android.systemui.dagger.qualifiers.Main
34 import com.android.systemui.res.R
35 import com.android.systemui.settings.UserTracker
36 import com.android.systemui.user.data.model.SelectedUserModel
37 import com.android.systemui.user.data.model.SelectionStatus
38 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
39 import com.android.systemui.util.settings.GlobalSettings
40 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
41 import java.util.concurrent.atomic.AtomicBoolean
42 import javax.inject.Inject
43 import kotlinx.coroutines.CoroutineDispatcher
44 import kotlinx.coroutines.CoroutineScope
45 import kotlinx.coroutines.asExecutor
46 import kotlinx.coroutines.channels.awaitClose
47 import kotlinx.coroutines.flow.Flow
48 import kotlinx.coroutines.flow.MutableStateFlow
49 import kotlinx.coroutines.flow.SharingStarted
50 import kotlinx.coroutines.flow.StateFlow
51 import kotlinx.coroutines.flow.filterNotNull
52 import kotlinx.coroutines.flow.map
53 import kotlinx.coroutines.flow.onEach
54 import kotlinx.coroutines.flow.onStart
55 import kotlinx.coroutines.flow.stateIn
56 import kotlinx.coroutines.launch
57 import kotlinx.coroutines.runBlocking
58 import kotlinx.coroutines.withContext
59 
60 /**
61  * Acts as source of truth for user related data.
62  *
63  * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
64  * upstream changes.
65  */
66 interface UserRepository {
67     /** User switcher related settings. */
68     val userSwitcherSettings: Flow<UserSwitcherSettingsModel>
69 
70     /** List of all users on the device. */
71     val userInfos: Flow<List<UserInfo>>
72 
73     /** Information about the currently-selected user, including [UserInfo] and other details. */
74     val selectedUser: StateFlow<SelectedUserModel>
75 
76     /** [UserInfo] of the currently-selected user. */
77     val selectedUserInfo: Flow<UserInfo>
78 
79     /** User ID of the main user. */
80     val mainUserId: Int
81 
82     /** User ID of the last non-guest selected user. */
83     val lastSelectedNonGuestUserId: Int
84 
85     /** Whether the device is configured to always have a guest user available. */
86     val isGuestUserAutoCreated: Boolean
87 
88     /** Whether the guest user is currently being reset. */
89     var isGuestUserResetting: Boolean
90 
91     /** Whether we've scheduled the creation of a guest user. */
92     val isGuestUserCreationScheduled: AtomicBoolean
93 
94     /** Whether to enable the status bar user chip (which launches the user switcher) */
95     val isStatusBarUserChipEnabled: Boolean
96 
97     /** The user of the secondary service. */
98     var secondaryUserId: Int
99 
100     /** Whether refresh users should be paused. */
101     var isRefreshUsersPaused: Boolean
102 
103     /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
refreshUsersnull104     fun refreshUsers()
105 
106     fun getSelectedUserInfo(): UserInfo
107 
108     fun isSimpleUserSwitcher(): Boolean
109 
110     fun isUserSwitcherEnabled(): Boolean
111 
112     /**
113      * Returns the user ID of the "main user" of the device. This user may have access to certain
114      * features which are limited to at most one user. There will never be more than one main user
115      * on a device.
116      *
117      * <p>Currently, on most form factors the first human user on the device will be the main user;
118      * in the future, the concept may be transferable, so a different user (or even no user at all)
119      * may be designated the main user instead. On other form factors there might not be a main
120      * user.
121      *
122      * <p> When the device doesn't have a main user, this will return {@code null}.
123      *
124      * @see [UserManager.getMainUser]
125      */
126     @UserIdInt suspend fun getMainUserId(): Int?
127 }
128 
129 @SysUISingleton
130 class UserRepositoryImpl
131 @Inject
132 constructor(
133     @Application private val appContext: Context,
134     private val manager: UserManager,
135     @Application private val applicationScope: CoroutineScope,
136     @Main private val mainDispatcher: CoroutineDispatcher,
137     @Background private val backgroundDispatcher: CoroutineDispatcher,
138     private val globalSettings: GlobalSettings,
139     private val tracker: UserTracker,
140 ) : UserRepository {
141 
142     private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
143         globalSettings
144             .observerFlow(
145                 names =
146                     arrayOf(
147                         SETTING_SIMPLE_USER_SWITCHER,
148                         Settings.Global.ADD_USERS_WHEN_LOCKED,
149                         Settings.Global.USER_SWITCHER_ENABLED,
150                     ),
151             )
152             .onStart { emit(Unit) } // Forces an initial update.
153             .map { getSettings() }
154             .stateIn(
155                 scope = applicationScope,
156                 started = SharingStarted.Eagerly,
157                 initialValue = runBlocking { getSettings() },
158             )
159     override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings
160 
161     private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
162     override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
163 
164     override var mainUserId: Int = UserHandle.USER_NULL
165         private set
166     override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
167         private set
168 
169     override val isGuestUserAutoCreated: Boolean =
170         appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated)
171 
172     private var _isGuestUserResetting: Boolean = false
173     override var isGuestUserResetting: Boolean = _isGuestUserResetting
174 
175     override val isGuestUserCreationScheduled = AtomicBoolean()
176 
177     override val isStatusBarUserChipEnabled: Boolean =
178         appContext.resources.getBoolean(R.bool.flag_user_switcher_chip)
179 
180     override var secondaryUserId: Int = UserHandle.USER_NULL
181 
182     override var isRefreshUsersPaused: Boolean = false
183 
184     override val selectedUser: StateFlow<SelectedUserModel> = run {
185         // Some callbacks don't modify the selection status, so maintain the current value.
186         var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
187         conflatedCallbackFlow {
188                 fun send(selectionStatus: SelectionStatus) {
189                     currentSelectionStatus = selectionStatus
190                     trySendWithFailureLogging(
191                         SelectedUserModel(tracker.userInfo, selectionStatus),
192                         TAG,
193                     )
194                 }
195 
196                 val callback =
197                     object : UserTracker.Callback {
198                         override fun onUserChanging(newUser: Int, userContext: Context) {
199                             send(SelectionStatus.SELECTION_IN_PROGRESS)
200                         }
201 
202                         override fun onUserChanged(newUser: Int, userContext: Context) {
203                             send(SelectionStatus.SELECTION_COMPLETE)
204                         }
205 
206                         override fun onProfilesChanged(profiles: List<UserInfo>) {
207                             send(currentSelectionStatus)
208                         }
209                     }
210 
211                 tracker.addCallback(callback, mainDispatcher.asExecutor())
212                 send(currentSelectionStatus)
213 
214                 awaitClose { tracker.removeCallback(callback) }
215             }
216             .onEach {
217                 if (!it.userInfo.isGuest) {
218                     lastSelectedNonGuestUserId = it.userInfo.id
219                 }
220             }
221             .stateIn(
222                 applicationScope,
223                 SharingStarted.Eagerly,
224                 initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus)
225             )
226     }
227 
228     override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
229 
230     @SuppressLint("MissingPermission")
231     override fun refreshUsers() {
232         applicationScope.launch {
233             _userInfos.value =
234                 withContext(backgroundDispatcher) { manager.aliveUsers }
235                     // Users should be sorted by ascending creation time.
236                     .sortedBy { it.creationTime }
237                     // The guest user is always last, regardless of creation time.
238                     .sortedBy { it.isGuest }
239 
240             if (mainUserId == UserHandle.USER_NULL) {
241                 val mainUser = withContext(backgroundDispatcher) { manager.mainUser }
242                 mainUser?.let { mainUserId = it.identifier }
243             }
244         }
245     }
246 
247     override fun getSelectedUserInfo(): UserInfo {
248         return selectedUser.value.userInfo
249     }
250 
251     override fun isSimpleUserSwitcher(): Boolean {
252         return _userSwitcherSettings.value.isSimpleUserSwitcher
253     }
254 
255     override fun isUserSwitcherEnabled(): Boolean {
256         return _userSwitcherSettings.value.isUserSwitcherEnabled
257     }
258 
259     override suspend fun getMainUserId(): Int? {
260         return withContext(backgroundDispatcher) { manager.mainUser?.identifier }
261     }
262 
263     private suspend fun getSettings(): UserSwitcherSettingsModel {
264         return withContext(backgroundDispatcher) {
265             val isSimpleUserSwitcher =
266                 globalSettings.getInt(
267                     SETTING_SIMPLE_USER_SWITCHER,
268                     if (
269                         appContext.resources.getBoolean(
270                             com.android.internal.R.bool.config_expandLockScreenUserSwitcher
271                         )
272                     ) {
273                         1
274                     } else {
275                         0
276                     },
277                 ) != 0
278 
279             val isAddUsersFromLockscreen =
280                 globalSettings.getInt(
281                     Settings.Global.ADD_USERS_WHEN_LOCKED,
282                     0,
283                 ) != 0
284 
285             val isUserSwitcherEnabled =
286                 globalSettings.getInt(
287                     Settings.Global.USER_SWITCHER_ENABLED,
288                     if (
289                         appContext.resources.getBoolean(
290                             com.android.internal.R.bool.config_showUserSwitcherByDefault
291                         )
292                     ) {
293                         1
294                     } else {
295                         0
296                     },
297                 ) != 0
298 
299             UserSwitcherSettingsModel(
300                 isSimpleUserSwitcher = isSimpleUserSwitcher,
301                 isAddUsersFromLockscreen = isAddUsersFromLockscreen,
302                 isUserSwitcherEnabled = isUserSwitcherEnabled,
303             )
304         }
305     }
306 
307     companion object {
308         private const val TAG = "UserRepository"
309         @VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
310     }
311 }
312