From b5625ac6f677e7ddbc3be735e3a17a0cae80daad Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Wed, 21 Nov 2018 08:56:55 -0500 Subject: [PATCH] Fixes multi-user support The controller is probably not recreated on user changed, so it listens to intent broadcasts to update its current profiles list. Test: atest Fixes: 120028217 Change-Id: I8139827065fc61bc215870631f0215dd1bebe697 --- .../systemui/privacy/PrivacyItemController.kt | 54 ++++++++++++++++--- .../privacy/PrivacyItemControllerTest.kt | 54 +++++++++++++++++-- 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 3fa3e8eec0abe..268462ea526e1 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -18,10 +18,14 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.Handler import android.os.UserHandle import android.os.UserManager +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dependency import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController @@ -33,25 +37,29 @@ class PrivacyItemController(val context: Context, val callback: Callback) { AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) + val intents = listOf(Intent.ACTION_USER_FOREGROUND, + Intent.ACTION_MANAGED_PROFILE_ADDED, + Intent.ACTION_MANAGED_PROFILE_REMOVED) + const val TAG = "PrivacyItemController" } private var privacyList = emptyList() private val appOpsController = Dependency.get(AppOpsController::class.java) private val userManager = context.getSystemService(UserManager::class.java) - private val currentUser = ActivityManager.getCurrentUser() - private val currentUserIds = userManager.getProfiles(currentUser).map { it.id } + private var currentUserIds = emptyList() private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER)) private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER) + private var listening = false + private val notifyChanges = Runnable { callback.privacyChanged(privacyList) } + private val updateListAndNotifyChanges = Runnable { updatePrivacyList() uiHandler.post(notifyChanges) } - private var listening = false - private val cb = object : AppOpsController.Callback { override fun onActiveStateChanged( code: Int, @@ -61,12 +69,36 @@ class PrivacyItemController(val context: Context, val callback: Callback) { ) { val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { - update() + update(false) } } } - private fun update() { + @VisibleForTesting + internal var userSwitcherReceiver = Receiver() + set(value) { + context.unregisterReceiver(field) + field = value + registerReceiver() + } + + init { + registerReceiver() + } + + private fun registerReceiver() { + context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply { + intents.forEach { + addAction(it) + } + }, null, null) + } + + private fun update(updateUsers: Boolean) { + if (updateUsers) { + val currentUser = ActivityManager.getCurrentUser() + currentUserIds = userManager.getProfiles(currentUser).map { it.id } + } bgHandler.post(updateListAndNotifyChanges) } @@ -75,7 +107,7 @@ class PrivacyItemController(val context: Context, val callback: Callback) { listening = listen if (listening) { appOpsController.addCallback(OPS, cb) - update() + update(true) } else { appOpsController.removeCallback(OPS, cb) } @@ -102,4 +134,12 @@ class PrivacyItemController(val context: Context, val callback: Callback) { interface Callback { fun privacyChanged(privacyItems: List) } + + internal inner class Receiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action in intents) { + update(true) + } + } + } } \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 48491d7ac88c8..24bcca50d34a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -16,8 +16,12 @@ package com.android.systemui.privacy +import android.app.ActivityManager import android.app.AppOpsManager +import android.content.Intent import android.os.Handler +import android.os.UserHandle +import android.os.UserManager import android.support.test.filters.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -34,9 +38,11 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyList import org.mockito.ArgumentMatchers.eq import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy import org.mockito.Mockito.verify - import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -44,10 +50,18 @@ import org.mockito.MockitoAnnotations @RunWithLooper class PrivacyItemControllerTest : SysuiTestCase() { + companion object { + val CURRENT_USER_ID = ActivityManager.getCurrentUser() + val OTHER_USER = UserHandle(CURRENT_USER_ID + 1) + const val TAG = "PrivacyItemControllerTest" + } + @Mock private lateinit var appOpsController: AppOpsController @Mock private lateinit var callback: PrivacyItemController.Callback + @Mock + private lateinit var userManager: UserManager private lateinit var testableLooper: TestableLooper private lateinit var privacyItemController: PrivacyItemController @@ -57,15 +71,17 @@ class PrivacyItemControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) - appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java) + appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper) mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper)) + mContext.addMockSystemService(UserManager::class.java, userManager) doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0))) .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) privacyItemController = PrivacyItemController(mContext, callback) } + @Test fun testSetListeningTrue() { privacyItemController.setListening(true) @@ -80,6 +96,38 @@ class PrivacyItemControllerTest : SysuiTestCase() { privacyItemController.setListening(true) privacyItemController.setListening(false) verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS), - any(AppOpsController.Callback:: class.java)) + any(AppOpsController.Callback::class.java)) + } + + @Test + fun testRegisterReceiver_allUsers() { + val spiedContext = spy(mContext) + val itemController = PrivacyItemController(spiedContext, callback) + + verify(spiedContext, atLeastOnce()).registerReceiverAsUser( + eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null), + eq(null)) + verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver)) + } + + @Test + fun testReceiver_ACTION_USER_FOREGROUND() { + privacyItemController.userSwitcherReceiver.onReceive(context, + Intent(Intent.ACTION_USER_FOREGROUND)) + verify(userManager).getProfiles(anyInt()) + } + + @Test + fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() { + privacyItemController.userSwitcherReceiver.onReceive(context, + Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)) + verify(userManager).getProfiles(anyInt()) + } + + @Test + fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() { + privacyItemController.userSwitcherReceiver.onReceive(context, + Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)) + verify(userManager).getProfiles(anyInt()) } } \ No newline at end of file