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.UserSwitcherSettingsModel 37 import com.android.systemui.util.settings.GlobalSettings 38 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow 39 import java.util.concurrent.atomic.AtomicBoolean 40 import javax.inject.Inject 41 import kotlinx.coroutines.CoroutineDispatcher 42 import kotlinx.coroutines.CoroutineScope 43 import kotlinx.coroutines.asExecutor 44 import kotlinx.coroutines.channels.awaitClose 45 import kotlinx.coroutines.flow.Flow 46 import kotlinx.coroutines.flow.MutableStateFlow 47 import kotlinx.coroutines.flow.SharingStarted 48 import kotlinx.coroutines.flow.StateFlow 49 import kotlinx.coroutines.flow.filterNotNull 50 import kotlinx.coroutines.flow.launchIn 51 import kotlinx.coroutines.flow.map 52 import kotlinx.coroutines.flow.onEach 53 import kotlinx.coroutines.flow.onStart 54 import kotlinx.coroutines.flow.stateIn 55 import kotlinx.coroutines.launch 56 import kotlinx.coroutines.runBlocking 57 import kotlinx.coroutines.withContext 58 59 /** 60 * Acts as source of truth for user related data. 61 * 62 * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about 63 * upstream changes. 64 */ 65 interface UserRepository { 66 /** User switcher related settings. */ 67 val userSwitcherSettings: Flow<UserSwitcherSettingsModel> 68 69 /** List of all users on the device. */ 70 val userInfos: Flow<List<UserInfo>> 71 72 /** [UserInfo] of the currently-selected user. */ 73 val selectedUserInfo: Flow<UserInfo> 74 75 /** Whether user switching is currently in progress. */ 76 val userSwitchingInProgress: Flow<Boolean> 77 78 /** User ID of the last non-guest selected user. */ 79 val lastSelectedNonGuestUserId: Int 80 81 /** Whether the device is configured to always have a guest user available. */ 82 val isGuestUserAutoCreated: Boolean 83 84 /** Whether the guest user is currently being reset. */ 85 var isGuestUserResetting: Boolean 86 87 /** Whether we've scheduled the creation of a guest user. */ 88 val isGuestUserCreationScheduled: AtomicBoolean 89 90 /** Whether to enable the status bar user chip (which launches the user switcher) */ 91 val isStatusBarUserChipEnabled: Boolean 92 93 /** The user of the secondary service. */ 94 var secondaryUserId: Int 95 96 /** Whether refresh users should be paused. */ 97 var isRefreshUsersPaused: Boolean 98 99 /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */ refreshUsersnull100 fun refreshUsers() 101 102 fun getSelectedUserInfo(): UserInfo 103 104 fun isSimpleUserSwitcher(): Boolean 105 } 106 107 @SysUISingleton 108 class UserRepositoryImpl 109 @Inject 110 constructor( 111 @Application private val appContext: Context, 112 private val manager: UserManager, 113 @Application private val applicationScope: CoroutineScope, 114 @Main private val mainDispatcher: CoroutineDispatcher, 115 @Background private val backgroundDispatcher: CoroutineDispatcher, 116 private val globalSettings: GlobalSettings, 117 private val tracker: UserTracker, 118 featureFlags: FeatureFlags, 119 ) : UserRepository { 120 121 private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> = 122 globalSettings 123 .observerFlow( 124 names = 125 arrayOf( 126 SETTING_SIMPLE_USER_SWITCHER, 127 Settings.Global.ADD_USERS_WHEN_LOCKED, 128 Settings.Global.USER_SWITCHER_ENABLED, 129 ), 130 userId = UserHandle.USER_SYSTEM, 131 ) 132 .onStart { emit(Unit) } // Forces an initial update. 133 .map { getSettings() } 134 .stateIn( 135 scope = applicationScope, 136 started = SharingStarted.Eagerly, 137 initialValue = runBlocking { getSettings() }, 138 ) 139 override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings 140 141 private val _userInfos = MutableStateFlow<List<UserInfo>?>(null) 142 override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull() 143 144 private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null) 145 override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull() 146 147 override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM 148 private set 149 150 override val isGuestUserAutoCreated: Boolean = 151 appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated) 152 153 private var _isGuestUserResetting: Boolean = false 154 override var isGuestUserResetting: Boolean = _isGuestUserResetting 155 156 private val _isUserSwitchingInProgress = MutableStateFlow(false) 157 override val userSwitchingInProgress: Flow<Boolean> 158 get() = _isUserSwitchingInProgress 159 160 override val isGuestUserCreationScheduled = AtomicBoolean() 161 162 override val isStatusBarUserChipEnabled: Boolean = 163 appContext.resources.getBoolean(R.bool.flag_user_switcher_chip) 164 165 override var secondaryUserId: Int = UserHandle.USER_NULL 166 167 override var isRefreshUsersPaused: Boolean = false 168 169 init { 170 observeSelectedUser() 171 if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) { 172 observeUserSwitching() 173 } 174 } 175 176 override fun refreshUsers() { 177 applicationScope.launch { 178 val result = withContext(backgroundDispatcher) { manager.aliveUsers } 179 180 if (result != null) { 181 _userInfos.value = 182 result 183 // Users should be sorted by ascending creation time. 184 .sortedBy { it.creationTime } 185 // The guest user is always last, regardless of creation time. 186 .sortedBy { it.isGuest } 187 } 188 } 189 } 190 191 override fun getSelectedUserInfo(): UserInfo { 192 return checkNotNull(_selectedUserInfo.value) 193 } 194 195 override fun isSimpleUserSwitcher(): Boolean { 196 return _userSwitcherSettings.value.isSimpleUserSwitcher 197 } 198 199 private fun observeUserSwitching() { 200 conflatedCallbackFlow { 201 val callback = 202 object : UserTracker.Callback { 203 override fun onUserChanging(newUser: Int, userContext: Context) { 204 trySendWithFailureLogging(true, TAG, "userSwitching started") 205 } 206 207 override fun onUserChanged(newUserId: Int, userContext: Context) { 208 trySendWithFailureLogging(false, TAG, "userSwitching completed") 209 } 210 } 211 tracker.addCallback(callback, mainDispatcher.asExecutor()) 212 trySendWithFailureLogging(false, TAG, "initial value defaulting to false") 213 awaitClose { tracker.removeCallback(callback) } 214 } 215 .onEach { _isUserSwitchingInProgress.value = it } 216 // TODO (b/262838215), Make this stateIn and initialize directly in field declaration 217 // once the flag is launched 218 .launchIn(applicationScope) 219 } 220 221 private fun observeSelectedUser() { 222 conflatedCallbackFlow { 223 fun send() { 224 trySendWithFailureLogging(tracker.userInfo, TAG) 225 } 226 227 val callback = 228 object : UserTracker.Callback { 229 override fun onUserChanging(newUser: Int, userContext: Context) { 230 send() 231 } 232 233 override fun onProfilesChanged(profiles: List<UserInfo>) { 234 send() 235 } 236 } 237 238 tracker.addCallback(callback, mainDispatcher.asExecutor()) 239 send() 240 241 awaitClose { tracker.removeCallback(callback) } 242 } 243 .onEach { 244 if (!it.isGuest) { 245 lastSelectedNonGuestUserId = it.id 246 } 247 248 _selectedUserInfo.value = it 249 } 250 .launchIn(applicationScope) 251 } 252 253 private suspend fun getSettings(): UserSwitcherSettingsModel { 254 return withContext(backgroundDispatcher) { 255 val isSimpleUserSwitcher = 256 globalSettings.getIntForUser( 257 SETTING_SIMPLE_USER_SWITCHER, 258 if ( 259 appContext.resources.getBoolean( 260 com.android.internal.R.bool.config_expandLockScreenUserSwitcher 261 ) 262 ) { 263 1 264 } else { 265 0 266 }, 267 UserHandle.USER_SYSTEM, 268 ) != 0 269 270 val isAddUsersFromLockscreen = 271 globalSettings.getIntForUser( 272 Settings.Global.ADD_USERS_WHEN_LOCKED, 273 0, 274 UserHandle.USER_SYSTEM, 275 ) != 0 276 277 val isUserSwitcherEnabled = 278 globalSettings.getIntForUser( 279 Settings.Global.USER_SWITCHER_ENABLED, 280 if ( 281 appContext.resources.getBoolean( 282 com.android.internal.R.bool.config_showUserSwitcherByDefault 283 ) 284 ) { 285 1 286 } else { 287 0 288 }, 289 UserHandle.USER_SYSTEM, 290 ) != 0 291 292 UserSwitcherSettingsModel( 293 isSimpleUserSwitcher = isSimpleUserSwitcher, 294 isAddUsersFromLockscreen = isAddUsersFromLockscreen, 295 isUserSwitcherEnabled = isUserSwitcherEnabled, 296 ) 297 } 298 } 299 300 companion object { 301 private const val TAG = "UserRepository" 302 @VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher" 303 } 304 } 305