• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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