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