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