blob: 098521fbda516fed437a51376fd9a3640a44e10b [file] [log] [blame]
Fabian Kozynski8d06c712018-11-07 10:33:02 -05001/*
2 * Copyright (C) 2018 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
17package com.android.systemui.privacy
18
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050019import android.app.ActivityManager
Fabian Kozynski8d06c712018-11-07 10:33:02 -050020import android.app.AppOpsManager
Fabian Kozynski81765392019-02-11 12:38:26 -050021import android.content.Context
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050022import android.content.Intent
Fabian Kozynski510585d2018-12-19 13:59:17 -050023import android.content.pm.UserInfo
Fabian Kozynski8d06c712018-11-07 10:33:02 -050024import android.os.Handler
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050025import android.os.UserHandle
26import android.os.UserManager
Fabian Kozynski16b26992019-05-06 10:18:41 -040027import android.provider.DeviceConfig
28import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS
Fabian Kozynski8d06c712018-11-07 10:33:02 -050029import android.testing.AndroidTestingRunner
30import android.testing.TestableLooper
31import android.testing.TestableLooper.RunWithLooper
Fabian Kozynski16b26992019-05-06 10:18:41 -040032import androidx.test.filters.SmallTest
33import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
Fabian Kozynski8d06c712018-11-07 10:33:02 -050034import com.android.systemui.Dependency
Fabian Kozynski508422b2018-12-20 10:58:17 -050035import com.android.systemui.R
Fabian Kozynski8d06c712018-11-07 10:33:02 -050036import com.android.systemui.SysuiTestCase
37import com.android.systemui.appops.AppOpItem
38import com.android.systemui.appops.AppOpsController
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000039import com.android.systemui.broadcast.BroadcastDispatcher
Fabian Kozynskia6ff80b2019-02-12 11:32:44 -050040import org.hamcrest.Matchers.hasItem
41import org.hamcrest.Matchers.not
42import org.hamcrest.Matchers.nullValue
Fabian Kozynski16b26992019-05-06 10:18:41 -040043import org.junit.After
Fabian Kozynski510585d2018-12-19 13:59:17 -050044import org.junit.Assert.assertEquals
Fabian Kozynskia6ff80b2019-02-12 11:32:44 -050045import org.junit.Assert.assertThat
46import org.junit.Assert.assertTrue
Fabian Kozynski8d06c712018-11-07 10:33:02 -050047import org.junit.Before
Fabian Kozynskicbd58952019-06-24 15:00:20 -040048import org.junit.Ignore
Fabian Kozynski8d06c712018-11-07 10:33:02 -050049import org.junit.Test
50import org.junit.runner.RunWith
Fabian Kozynski510585d2018-12-19 13:59:17 -050051import org.mockito.ArgumentCaptor
Fabian Kozynski8d06c712018-11-07 10:33:02 -050052import org.mockito.ArgumentMatchers.any
Matt Papecb888792019-05-22 15:32:32 -070053import org.mockito.ArgumentMatchers.anyBoolean
Fabian Kozynski8d06c712018-11-07 10:33:02 -050054import org.mockito.ArgumentMatchers.anyInt
55import org.mockito.ArgumentMatchers.anyList
Matt Papecb888792019-05-22 15:32:32 -070056import org.mockito.ArgumentMatchers.anyString
Fabian Kozynski8d06c712018-11-07 10:33:02 -050057import org.mockito.ArgumentMatchers.eq
Fabian Kozynski510585d2018-12-19 13:59:17 -050058import org.mockito.Captor
Fabian Kozynski8d06c712018-11-07 10:33:02 -050059import org.mockito.Mock
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050060import org.mockito.Mockito.atLeastOnce
Matt Papecb888792019-05-22 15:32:32 -070061import org.mockito.Mockito.doAnswer
Fabian Kozynski8d06c712018-11-07 10:33:02 -050062import org.mockito.Mockito.doReturn
Fabian Kozynski04f83eb2019-01-22 10:38:40 -050063import org.mockito.Mockito.mock
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050064import org.mockito.Mockito.never
Fabian Kozynski04f83eb2019-01-22 10:38:40 -050065import org.mockito.Mockito.reset
Fabian Kozynski8d06c712018-11-07 10:33:02 -050066import org.mockito.Mockito.verify
Fabian Kozynski04f83eb2019-01-22 10:38:40 -050067import org.mockito.Mockito.verifyNoMoreInteractions
Fabian Kozynski8d06c712018-11-07 10:33:02 -050068import org.mockito.MockitoAnnotations
69
70@RunWith(AndroidTestingRunner::class)
71@SmallTest
72@RunWithLooper
Fabian Kozynskicbd58952019-06-24 15:00:20 -040073@Ignore
Fabian Kozynski8d06c712018-11-07 10:33:02 -050074class PrivacyItemControllerTest : SysuiTestCase() {
75
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050076 companion object {
77 val CURRENT_USER_ID = ActivityManager.getCurrentUser()
Fabian Kozynski508422b2018-12-20 10:58:17 -050078 val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
79 const val SYSTEM_UID = 1000
80 const val TEST_PACKAGE_NAME = "test"
81 const val DEVICE_SERVICES_STRING = "Device services"
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050082 const val TAG = "PrivacyItemControllerTest"
Fabian Kozynski510585d2018-12-19 13:59:17 -050083 fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050084 }
85
Fabian Kozynski8d06c712018-11-07 10:33:02 -050086 @Mock
87 private lateinit var appOpsController: AppOpsController
88 @Mock
89 private lateinit var callback: PrivacyItemController.Callback
Fabian Kozynskib5625ac2018-11-21 08:56:55 -050090 @Mock
91 private lateinit var userManager: UserManager
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000092 @Mock
93 private lateinit var broadcastDispatcher: BroadcastDispatcher
Fabian Kozynski510585d2018-12-19 13:59:17 -050094 @Captor
95 private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
Fabian Kozynski04f83eb2019-01-22 10:38:40 -050096 @Captor
97 private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
Fabian Kozynski8d06c712018-11-07 10:33:02 -050098
99 private lateinit var testableLooper: TestableLooper
100 private lateinit var privacyItemController: PrivacyItemController
Fabian Kozynski81765392019-02-11 12:38:26 -0500101 private lateinit var handler: Handler
102
103 fun PrivacyItemController(context: Context) =
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000104 PrivacyItemController(context, appOpsController, handler, handler, broadcastDispatcher)
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500105
106 @Before
107 fun setup() {
108 MockitoAnnotations.initMocks(this)
109 testableLooper = TestableLooper.get(this)
Fabian Kozynski81765392019-02-11 12:38:26 -0500110 handler = Handler(testableLooper.looper)
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500111
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500112 appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
Fabian Kozynski81765392019-02-11 12:38:26 -0500113 mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
114 mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500115 mContext.addMockSystemService(UserManager::class.java, userManager)
Fabian Kozynski508422b2018-12-20 10:58:17 -0500116 mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
117 DEVICE_SERVICES_STRING)
Fabian Kozynski16b26992019-05-06 10:18:41 -0400118 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
119 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
120 "true", false)
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500121
Fabian Kozynski510585d2018-12-19 13:59:17 -0500122 doReturn(listOf(object : UserInfo() {
123 init {
124 id = CURRENT_USER_ID
125 }
126 })).`when`(userManager).getProfiles(anyInt())
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500127
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500128 privacyItemController = PrivacyItemController(mContext)
129 }
130
Fabian Kozynski16b26992019-05-06 10:18:41 -0400131 @After
132 fun tearDown() {
133 DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY)
134 }
135
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500136 @Test
137 fun testSetListeningTrueByAddingCallback() {
138 privacyItemController.addCallback(callback)
Fabian Kozynski16b26992019-05-06 10:18:41 -0400139 testableLooper.processAllMessages()
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500140 verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
141 any(AppOpsController.Callback::class.java))
142 testableLooper.processAllMessages()
143 verify(callback).privacyChanged(anyList())
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500144 }
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500145
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500146 @Test
Fabian Kozynski16b26992019-05-06 10:18:41 -0400147 fun testSetListeningFalseByRemovingLastCallback() {
148 privacyItemController.addCallback(callback)
149 testableLooper.processAllMessages()
150 verify(appOpsController, never()).removeCallback(any(IntArray::class.java),
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500151 any(AppOpsController.Callback::class.java))
Fabian Kozynski16b26992019-05-06 10:18:41 -0400152 privacyItemController.removeCallback(callback)
153 testableLooper.processAllMessages()
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500154 verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500155 any(AppOpsController.Callback::class.java))
Fabian Kozynski16b26992019-05-06 10:18:41 -0400156 verify(callback).privacyChanged(emptyList())
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500157 }
158
159 @Test
Fabian Kozynski510585d2018-12-19 13:59:17 -0500160 fun testDistinctItems() {
Fabian Kozynski508422b2018-12-20 10:58:17 -0500161 doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
162 AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
Fabian Kozynski510585d2018-12-19 13:59:17 -0500163 .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
164
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500165 privacyItemController.addCallback(callback)
Fabian Kozynski510585d2018-12-19 13:59:17 -0500166 testableLooper.processAllMessages()
167 verify(callback).privacyChanged(capture(argCaptor))
168 assertEquals(1, argCaptor.value.size)
169 }
170
171 @Test
Fabian Kozynski508422b2018-12-20 10:58:17 -0500172 fun testSystemApps() {
173 doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
174 0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500175 privacyItemController.addCallback(callback)
Fabian Kozynski508422b2018-12-20 10:58:17 -0500176 testableLooper.processAllMessages()
177 verify(callback).privacyChanged(capture(argCaptor))
178 assertEquals(1, argCaptor.value.size)
179 assertEquals(context.getString(R.string.device_services),
180 argCaptor.value[0].application.applicationName)
181 }
182
183 @Test
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500184 fun testRegisterReceiver_allUsers() {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000185 privacyItemController.addCallback(callback)
Fabian Kozynski16b26992019-05-06 10:18:41 -0400186 testableLooper.processAllMessages()
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000187 verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
188 eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
189 verify(broadcastDispatcher, never())
190 .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
Fabian Kozynskib5625ac2018-11-21 08:56:55 -0500191 }
192
193 @Test
194 fun testReceiver_ACTION_USER_FOREGROUND() {
195 privacyItemController.userSwitcherReceiver.onReceive(context,
196 Intent(Intent.ACTION_USER_FOREGROUND))
197 verify(userManager).getProfiles(anyInt())
198 }
199
200 @Test
201 fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
202 privacyItemController.userSwitcherReceiver.onReceive(context,
203 Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
204 verify(userManager).getProfiles(anyInt())
205 }
206
207 @Test
208 fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
209 privacyItemController.userSwitcherReceiver.onReceive(context,
210 Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
211 verify(userManager).getProfiles(anyInt())
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500212 }
Fabian Kozynski04f83eb2019-01-22 10:38:40 -0500213
214 @Test
215 fun testAddMultipleCallbacks() {
216 val otherCallback = mock(PrivacyItemController.Callback::class.java)
217 privacyItemController.addCallback(callback)
218 testableLooper.processAllMessages()
219 verify(callback).privacyChanged(anyList())
220
221 privacyItemController.addCallback(otherCallback)
222 testableLooper.processAllMessages()
223 verify(otherCallback).privacyChanged(anyList())
224 // Adding a callback should not unnecessarily call previous ones
225 verifyNoMoreInteractions(callback)
226 }
227
228 @Test
229 fun testMultipleCallbacksAreUpdated() {
230 doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
231
232 val otherCallback = mock(PrivacyItemController.Callback::class.java)
233 privacyItemController.addCallback(callback)
234 privacyItemController.addCallback(otherCallback)
235 testableLooper.processAllMessages()
236 reset(callback)
237 reset(otherCallback)
238
239 verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
240 argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
241 testableLooper.processAllMessages()
242 verify(callback).privacyChanged(anyList())
243 verify(otherCallback).privacyChanged(anyList())
244 }
245
246 @Test
247 fun testRemoveCallback() {
248 doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
249 val otherCallback = mock(PrivacyItemController.Callback::class.java)
250 privacyItemController.addCallback(callback)
251 privacyItemController.addCallback(otherCallback)
252 testableLooper.processAllMessages()
253 reset(callback)
254 reset(otherCallback)
255
256 verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
257 privacyItemController.removeCallback(callback)
258 argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
259 testableLooper.processAllMessages()
260 verify(callback, never()).privacyChanged(anyList())
261 verify(otherCallback).privacyChanged(anyList())
262 }
Fabian Kozynskia6ff80b2019-02-12 11:32:44 -0500263
264 @Test
265 fun testListShouldNotHaveNull() {
266 doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
267 AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
268 .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
269 privacyItemController.addCallback(callback)
270 testableLooper.processAllMessages()
271
272 verify(callback).privacyChanged(capture(argCaptor))
273 assertEquals(1, argCaptor.value.size)
274 assertThat(argCaptor.value, not(hasItem(nullValue())))
275 }
276
277 @Test
278 fun testListShouldBeCopy() {
279 val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
280 PrivacyApplication("", TEST_UID, mContext)))
281 privacyItemController.privacyList = list
Fabian Kozynski72090102019-03-04 10:55:55 -0500282 val privacyList = privacyItemController.privacyList
283 assertEquals(list, privacyList)
284 assertTrue(list !== privacyList)
Fabian Kozynskia6ff80b2019-02-12 11:32:44 -0500285 }
Fabian Kozynski16b26992019-05-06 10:18:41 -0400286
287 @Test
288 fun testNotListeningWhenIndicatorsDisabled() {
Matt Papecb888792019-05-22 15:32:32 -0700289 val properties = getProperties(
Fabian Kozynski16b26992019-05-06 10:18:41 -0400290 DeviceConfig.NAMESPACE_PRIVACY,
291 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
Matt Papecb888792019-05-22 15:32:32 -0700292 false)
293 privacyItemController.devicePropertiesChangedListener.onPropertiesChanged(properties)
Fabian Kozynski16b26992019-05-06 10:18:41 -0400294 privacyItemController.addCallback(callback)
295 testableLooper.processAllMessages()
296 verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
297 any(AppOpsController.Callback::class.java))
298 }
Matt Papecb888792019-05-22 15:32:32 -0700299
300 private fun getProperties(namespace: String, name: String, value: Boolean):
301 DeviceConfig.Properties {
302 val properties = mock<DeviceConfig.Properties>(DeviceConfig.Properties::class.java)
303 doReturn(namespace).`when`(properties).getNamespace()
304 doReturn(setOf(name)).`when`(properties).getKeyset()
305 doAnswer {
306 val key: String = it.getArgument(0)
307 val defaultValue: Boolean = it.getArgument(1)
308 if (name.equals(key, ignoreCase = true)) {
309 value
310 } else {
311 defaultValue
312 }
313 }.`when`(properties).getBoolean(anyString(), anyBoolean())
314 return properties
315 }
Fabian Kozynski8d06c712018-11-07 10:33:02 -0500316}