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.content.Context
21 import android.content.pm.UserInfo
22 import android.os.UserHandle
23 import android.os.UserManager
24 import android.provider.Settings
25 import androidx.annotation.VisibleForTesting
26 import com.android.systemui.R
27 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
28 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
29 import com.android.systemui.dagger.SysUISingleton
30 import com.android.systemui.dagger.qualifiers.Application
31 import com.android.systemui.dagger.qualifiers.Background
32 import com.android.systemui.dagger.qualifiers.Main
33 import com.android.systemui.flags.FeatureFlags
34 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
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.launchIn
53 import kotlinx.coroutines.flow.map
54 import kotlinx.coroutines.flow.onEach
55 import kotlinx.coroutines.flow.onStart
56 import kotlinx.coroutines.flow.stateIn
57 import kotlinx.coroutines.launch
58 import kotlinx.coroutines.runBlocking
59 import kotlinx.coroutines.withContext
60 
61 /**
62  * Acts as source of truth for user related data.
63  *
64  * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
65  * upstream changes.
66  */
67 interface UserRepository {
68     /** User switcher related settings. */
69     val userSwitcherSettings: Flow<UserSwitcherSettingsModel>
70 
71     /** List of all users on the device. */
72     val userInfos: Flow<List<UserInfo>>
73 
74     /** Information about the currently-selected user, including [UserInfo] and other details. */
75     val selectedUser: StateFlow<SelectedUserModel>
76 
77     /** [UserInfo] of the currently-selected user. */
78     val selectedUserInfo: Flow<UserInfo>
79 
80     /** Whether user switching is currently in progress. */
81     val userSwitchingInProgress: Flow<Boolean>
82 
83     /** User ID of the main user. */
84     val mainUserId: Int
85 
86     /** User ID of the last non-guest selected user. */
87     val lastSelectedNonGuestUserId: Int
88 
89     /** Whether the device is configured to always have a guest user available. */
90     val isGuestUserAutoCreated: Boolean
91 
92     /** Whether the guest user is currently being reset. */
93     var isGuestUserResetting: Boolean
94 
95     /** Whether we've scheduled the creation of a guest user. */
96     val isGuestUserCreationScheduled: AtomicBoolean
97 
98     /** Whether to enable the status bar user chip (which launches the user switcher) */
99     val isStatusBarUserChipEnabled: Boolean
100 
101     /** The user of the secondary service. */
102     var secondaryUserId: Int
103 
104     /** Whether refresh users should be paused. */
105     var isRefreshUsersPaused: Boolean
106 
107     /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
108     fun refreshUsers()
109 
110     fun getSelectedUserInfo(): UserInfo
111 
112     fun isSimpleUserSwitcher(): Boolean
113 
114     fun isUserSwitcherEnabled(): Boolean
115 }
116 
117 @SysUISingleton
118 class UserRepositoryImpl
119 @Inject
120 constructor(
121     @Application private val appContext: Context,
122     private val manager: UserManager,
123     @Application private val applicationScope: CoroutineScope,
124     @Main private val mainDispatcher: CoroutineDispatcher,
125     @Background private val backgroundDispatcher: CoroutineDispatcher,
126     private val globalSettings: GlobalSettings,
127     private val tracker: UserTracker,
128     featureFlags: FeatureFlags,
129 ) : UserRepository {
130 
131     private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
132         globalSettings
133             .observerFlow(
134                 names =
135                     arrayOf(
136                         SETTING_SIMPLE_USER_SWITCHER,
137                         Settings.Global.ADD_USERS_WHEN_LOCKED,
138                         Settings.Global.USER_SWITCHER_ENABLED,
139                     ),
140                 userId = UserHandle.USER_SYSTEM,
141             )
142             .onStart { emit(Unit) } // Forces an initial update.
143             .map { getSettings() }
144             .stateIn(
145                 scope = applicationScope,
146                 started = SharingStarted.Eagerly,
147                 initialValue = runBlocking { getSettings() },
148             )
149     override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings
150 
151     private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
152     override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
153 
154     override var mainUserId: Int = UserHandle.USER_NULL
155         private set
156     override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
157         private set
158 
159     override val isGuestUserAutoCreated: Boolean =
160         appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated)
161 
162     private var _isGuestUserResetting: Boolean = false
163     override var isGuestUserResetting: Boolean = _isGuestUserResetting
164 
165     private val _isUserSwitchingInProgress = MutableStateFlow(false)
166     override val userSwitchingInProgress: Flow<Boolean>
167         get() = _isUserSwitchingInProgress
168 
169     override val isGuestUserCreationScheduled = AtomicBoolean()
170 
171     override val isStatusBarUserChipEnabled: Boolean =
172         appContext.resources.getBoolean(R.bool.flag_user_switcher_chip)
173 
174     override var secondaryUserId: Int = UserHandle.USER_NULL
175 
176     override var isRefreshUsersPaused: Boolean = false
177 
178     init {
179         if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
180             observeUserSwitching()
181         }
182     }
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     override fun refreshUsers() {
231         applicationScope.launch {
232             val result = withContext(backgroundDispatcher) { manager.aliveUsers }
233 
234             if (result != null) {
235                 _userInfos.value =
236                     result
237                         // Users should be sorted by ascending creation time.
238                         .sortedBy { it.creationTime }
239                         // The guest user is always last, regardless of creation time.
240                         .sortedBy { it.isGuest }
241             }
242 
243             if (mainUserId == UserHandle.USER_NULL) {
244                 val mainUser = withContext(backgroundDispatcher) { manager.mainUser }
245                 mainUser?.let { mainUserId = it.identifier }
246             }
247         }
248     }
249 
250     override fun getSelectedUserInfo(): UserInfo {
251         return selectedUser.value.userInfo
252     }
253 
254     override fun isSimpleUserSwitcher(): Boolean {
255         return _userSwitcherSettings.value.isSimpleUserSwitcher
256     }
257 
258     override fun isUserSwitcherEnabled(): Boolean {
259         return _userSwitcherSettings.value.isUserSwitcherEnabled
260     }
261 
262     private fun observeUserSwitching() {
263         conflatedCallbackFlow {
264                 val callback =
265                     object : UserTracker.Callback {
266                         override fun onUserChanging(newUser: Int, userContext: Context) {
267                             trySendWithFailureLogging(true, TAG, "userSwitching started")
268                         }
269 
270                         override fun onUserChanged(newUserId: Int, userContext: Context) {
271                             trySendWithFailureLogging(false, TAG, "userSwitching completed")
272                         }
273                     }
274                 tracker.addCallback(callback, mainDispatcher.asExecutor())
275                 trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
276                 awaitClose { tracker.removeCallback(callback) }
277             }
278             .onEach { _isUserSwitchingInProgress.value = it }
279             // TODO (b/262838215), Make this stateIn and initialize directly in field declaration
280             //  once the flag is launched
281             .launchIn(applicationScope)
282     }
283 
284     private suspend fun getSettings(): UserSwitcherSettingsModel {
285         return withContext(backgroundDispatcher) {
286             val isSimpleUserSwitcher =
287                 globalSettings.getIntForUser(
288                     SETTING_SIMPLE_USER_SWITCHER,
289                     if (
290                         appContext.resources.getBoolean(
291                             com.android.internal.R.bool.config_expandLockScreenUserSwitcher
292                         )
293                     ) {
294                         1
295                     } else {
296                         0
297                     },
298                     UserHandle.USER_SYSTEM,
299                 ) != 0
300 
301             val isAddUsersFromLockscreen =
302                 globalSettings.getIntForUser(
303                     Settings.Global.ADD_USERS_WHEN_LOCKED,
304                     0,
305                     UserHandle.USER_SYSTEM,
306                 ) != 0
307 
308             val isUserSwitcherEnabled =
309                 globalSettings.getIntForUser(
310                     Settings.Global.USER_SWITCHER_ENABLED,
311                     if (
312                         appContext.resources.getBoolean(
313                             com.android.internal.R.bool.config_showUserSwitcherByDefault
314                         )
315                     ) {
316                         1
317                     } else {
318                         0
319                     },
320                     UserHandle.USER_SYSTEM,
321                 ) != 0
322 
323             UserSwitcherSettingsModel(
324                 isSimpleUserSwitcher = isSimpleUserSwitcher,
325                 isAddUsersFromLockscreen = isAddUsersFromLockscreen,
326                 isUserSwitcherEnabled = isUserSwitcherEnabled,
327             )
328         }
329     }
330 
331     companion object {
332         private const val TAG = "UserRepository"
333         @VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
334     }
335 }
336