1 /* 2 * Copyright (C) 2020 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 package com.android.systemui.settings 18 19 import android.app.IActivityManager 20 import android.app.UserSwitchObserver 21 import android.content.BroadcastReceiver 22 import android.content.ContentResolver 23 import android.content.Context 24 import android.content.Intent 25 import android.content.IntentFilter 26 import android.content.pm.UserInfo 27 import android.os.Handler 28 import android.os.IRemoteCallback 29 import android.os.UserHandle 30 import android.os.UserManager 31 import android.util.Log 32 import androidx.annotation.GuardedBy 33 import androidx.annotation.WorkerThread 34 import com.android.systemui.Dumpable 35 import com.android.systemui.dump.DumpManager 36 import com.android.systemui.util.Assert 37 import java.io.PrintWriter 38 import java.lang.IllegalStateException 39 import java.lang.ref.WeakReference 40 import java.util.concurrent.CountDownLatch 41 import java.util.concurrent.Executor 42 import kotlin.properties.ReadWriteProperty 43 import kotlin.reflect.KProperty 44 45 /** 46 * SystemUI cache for keeping track of the current user and associated values. 47 * 48 * The values provided asynchronously are NOT copies, but shared among all requesters. Do not 49 * modify them. 50 * 51 * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as 52 * soon as possible (and reduce its dependency graph). 53 * Other classes that want to listen to the broadcasts listened here SHOULD 54 * subscribe to this class instead. 55 * 56 * @see UserTracker 57 * 58 * Class constructed and initialized in [SettingsModule]. 59 */ 60 class UserTrackerImpl internal constructor( 61 private val context: Context, 62 private val userManager: UserManager, 63 private val iActivityManager: IActivityManager, 64 private val dumpManager: DumpManager, 65 private val backgroundHandler: Handler 66 ) : UserTracker, Dumpable, BroadcastReceiver() { 67 68 companion object { 69 private const val TAG = "UserTrackerImpl" 70 } 71 72 var initialized = false 73 private set 74 75 private val mutex = Any() 76 77 override var userId: Int by SynchronizedDelegate(context.userId) 78 private set 79 80 override var userHandle: UserHandle by SynchronizedDelegate(context.user) 81 private set 82 83 override var userContext: Context by SynchronizedDelegate(context) 84 private set 85 86 override val userContentResolver: ContentResolver 87 get() = userContext.contentResolver 88 89 override val userInfo: UserInfo 90 get() { 91 val user = userId <lambda>null92 return userProfiles.first { it.id == user } 93 } 94 95 /** 96 * Returns a [List<UserInfo>] of all profiles associated with the current user. 97 * 98 * The list returned is not a copy, so a copy should be made if its elements need to be 99 * modified. 100 */ 101 override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList()) 102 private set 103 104 @GuardedBy("callbacks") 105 private val callbacks: MutableList<DataItem> = ArrayList() 106 initializenull107 fun initialize(startingUser: Int) { 108 if (initialized) { 109 return 110 } 111 initialized = true 112 setUserIdInternal(startingUser) 113 114 val filter = IntentFilter().apply { 115 addAction(Intent.ACTION_USER_INFO_CHANGED) 116 // These get called when a managed profile goes in or out of quiet mode. 117 addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) 118 addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) 119 addAction(Intent.ACTION_MANAGED_PROFILE_ADDED) 120 addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED) 121 addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED) 122 } 123 context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler) 124 125 registerUserSwitchObserver() 126 127 dumpManager.registerDumpable(TAG, this) 128 } 129 onReceivenull130 override fun onReceive(context: Context, intent: Intent) { 131 when (intent.action) { 132 Intent.ACTION_USER_INFO_CHANGED, 133 Intent.ACTION_MANAGED_PROFILE_AVAILABLE, 134 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, 135 Intent.ACTION_MANAGED_PROFILE_ADDED, 136 Intent.ACTION_MANAGED_PROFILE_REMOVED, 137 Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> { 138 handleProfilesChanged() 139 } 140 } 141 } 142 createCurrentUserContextnull143 override fun createCurrentUserContext(context: Context): Context { 144 synchronized(mutex) { 145 return context.createContextAsUser(userHandle, 0) 146 } 147 } 148 setUserIdInternalnull149 private fun setUserIdInternal(user: Int): Pair<Context, List<UserInfo>> { 150 val profiles = userManager.getProfiles(user) 151 val handle = UserHandle(user) 152 val ctx = context.createContextAsUser(handle, 0) 153 154 synchronized(mutex) { 155 userId = user 156 userHandle = handle 157 userContext = ctx 158 userProfiles = profiles.map { UserInfo(it) } 159 } 160 return ctx to profiles 161 } 162 registerUserSwitchObservernull163 private fun registerUserSwitchObserver() { 164 iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() { 165 override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) { 166 backgroundHandler.run { 167 handleUserSwitching(newUserId) 168 reply?.sendResult(null) 169 } 170 } 171 172 override fun onUserSwitchComplete(newUserId: Int) { 173 backgroundHandler.run { 174 handleUserSwitchComplete(newUserId) 175 } 176 } 177 }, TAG) 178 } 179 180 @WorkerThread handleUserSwitchingnull181 private fun handleUserSwitching(newUserId: Int) { 182 Assert.isNotMainThread() 183 Log.i(TAG, "Switching to user $newUserId") 184 185 setUserIdInternal(newUserId) 186 187 val list = synchronized(callbacks) { 188 callbacks.toList() 189 } 190 val latch = CountDownLatch(list.size) 191 list.forEach { 192 val callback = it.callback.get() 193 if (callback != null) { 194 it.executor.execute { 195 callback.onUserChanging(userId, userContext, latch) 196 } 197 } else { 198 latch.countDown() 199 } 200 } 201 latch.await() 202 } 203 204 @WorkerThread handleUserSwitchCompletenull205 private fun handleUserSwitchComplete(newUserId: Int) { 206 Assert.isNotMainThread() 207 Log.i(TAG, "Switched to user $newUserId") 208 209 setUserIdInternal(newUserId) 210 notifySubscribers { 211 onUserChanged(newUserId, userContext) 212 onProfilesChanged(userProfiles) 213 } 214 } 215 216 @WorkerThread handleProfilesChangednull217 private fun handleProfilesChanged() { 218 Assert.isNotMainThread() 219 220 val profiles = userManager.getProfiles(userId) 221 synchronized(mutex) { 222 userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy 223 } 224 notifySubscribers { 225 onProfilesChanged(profiles) 226 } 227 } 228 addCallbacknull229 override fun addCallback(callback: UserTracker.Callback, executor: Executor) { 230 synchronized(callbacks) { 231 callbacks.add(DataItem(WeakReference(callback), executor)) 232 } 233 } 234 removeCallbacknull235 override fun removeCallback(callback: UserTracker.Callback) { 236 synchronized(callbacks) { 237 callbacks.removeIf { it.sameOrEmpty(callback) } 238 } 239 } 240 notifySubscribersnull241 private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) { 242 val list = synchronized(callbacks) { 243 callbacks.toList() 244 } 245 246 list.forEach { 247 if (it.callback.get() != null) { 248 it.executor.execute { 249 it.callback.get()?.action() 250 } 251 } 252 } 253 } 254 dumpnull255 override fun dump(pw: PrintWriter, args: Array<out String>) { 256 pw.println("Initialized: $initialized") 257 if (initialized) { 258 pw.println("userId: $userId") 259 val ids = userProfiles.map { it.toFullString() } 260 pw.println("userProfiles: $ids") 261 } 262 val list = synchronized(callbacks) { 263 callbacks.toList() 264 } 265 pw.println("Callbacks:") 266 list.forEach { 267 it.callback.get()?.let { 268 pw.println(" $it") 269 } 270 } 271 } 272 273 private class SynchronizedDelegate<T : Any>( 274 private var value: T 275 ) : ReadWriteProperty<UserTrackerImpl, T> { 276 277 @GuardedBy("mutex") getValuenull278 override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T { 279 if (!thisRef.initialized) { 280 throw IllegalStateException("Must initialize before getting ${property.name}") 281 } 282 return synchronized(thisRef.mutex) { value } 283 } 284 285 @GuardedBy("mutex") setValuenull286 override fun setValue(thisRef: UserTrackerImpl, property: KProperty<*>, value: T) { 287 synchronized(thisRef.mutex) { this.value = value } 288 } 289 } 290 } 291 292 private data class DataItem( 293 val callback: WeakReference<UserTracker.Callback>, 294 val executor: Executor 295 ) { sameOrEmptynull296 fun sameOrEmpty(other: UserTracker.Callback): Boolean { 297 return callback.get()?.equals(other) ?: true 298 } 299 } 300