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