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