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.privacy 18 19 import android.app.ActivityManager 20 import android.os.UserHandle 21 import android.testing.TestableLooper.RunWithLooper 22 import androidx.test.ext.junit.runners.AndroidJUnit4 23 import androidx.test.filters.SmallTest 24 import com.android.systemui.SysuiTestCase 25 import com.android.systemui.dump.DumpManager 26 import com.android.systemui.privacy.logging.PrivacyLogger 27 import com.android.systemui.util.DeviceConfigProxy 28 import com.android.systemui.util.DeviceConfigProxyFake 29 import com.android.systemui.util.concurrency.FakeExecutor 30 import com.android.systemui.util.mockito.argumentCaptor 31 import com.android.systemui.util.time.FakeSystemClock 32 import org.junit.Assert.assertEquals 33 import org.junit.Assert.assertTrue 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.ArgumentCaptor 38 import org.mockito.ArgumentMatchers.anyList 39 import org.mockito.Captor 40 import org.mockito.Mock 41 import org.mockito.Mockito 42 import org.mockito.Mockito.`when` 43 import org.mockito.Mockito.atLeastOnce 44 import org.mockito.Mockito.doReturn 45 import org.mockito.Mockito.mock 46 import org.mockito.Mockito.never 47 import org.mockito.Mockito.reset 48 import org.mockito.Mockito.verify 49 import org.mockito.Mockito.verifyNoMoreInteractions 50 import org.mockito.MockitoAnnotations 51 52 @RunWith(AndroidJUnit4::class) 53 @SmallTest 54 @RunWithLooper 55 class PrivacyItemControllerTest : SysuiTestCase() { 56 57 companion object { 58 val CURRENT_USER_ID = ActivityManager.getCurrentUser() 59 val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE 60 const val TEST_PACKAGE_NAME = "test" 61 capturenull62 fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() 63 fun <T> any(): T = Mockito.any<T>() 64 } 65 66 @Mock 67 private lateinit var callback: PrivacyItemController.Callback 68 @Mock 69 private lateinit var privacyConfig: PrivacyConfig 70 @Mock 71 private lateinit var privacyItemMonitor: PrivacyItemMonitor 72 @Mock 73 private lateinit var privacyItemMonitor2: PrivacyItemMonitor 74 @Mock 75 private lateinit var dumpManager: DumpManager 76 @Mock 77 private lateinit var logger: PrivacyLogger 78 @Captor 79 private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>> 80 @Captor 81 private lateinit var argCaptorCallback: ArgumentCaptor<PrivacyItemMonitor.Callback> 82 @Captor 83 private lateinit var argCaptorConfigCallback: ArgumentCaptor<PrivacyConfig.Callback> 84 85 private lateinit var privacyItemController: PrivacyItemController 86 private lateinit var executor: FakeExecutor 87 private lateinit var fakeClock: FakeSystemClock 88 private lateinit var deviceConfigProxy: DeviceConfigProxy 89 90 fun createPrivacyItemController(): PrivacyItemController { 91 return PrivacyItemController( 92 executor, 93 executor, 94 privacyConfig, 95 setOf(privacyItemMonitor, privacyItemMonitor2), 96 logger, 97 fakeClock, 98 dumpManager) 99 } 100 101 @Before setupnull102 fun setup() { 103 MockitoAnnotations.initMocks(this) 104 fakeClock = FakeSystemClock() 105 executor = FakeExecutor(fakeClock) 106 deviceConfigProxy = DeviceConfigProxyFake() 107 privacyItemController = createPrivacyItemController() 108 } 109 110 @Test testStartListeningByAddingCallbacknull111 fun testStartListeningByAddingCallback() { 112 privacyItemController.addCallback(callback) 113 executor.runAllReady() 114 verify(privacyItemMonitor).startListening(any()) 115 verify(privacyItemMonitor2).startListening(any()) 116 verify(callback).onPrivacyItemsChanged(anyList()) 117 } 118 119 @Test testStopListeningByRemovingLastCallbacknull120 fun testStopListeningByRemovingLastCallback() { 121 privacyItemController.addCallback(callback) 122 executor.runAllReady() 123 verify(privacyItemMonitor, never()).stopListening() 124 privacyItemController.removeCallback(callback) 125 executor.runAllReady() 126 verify(privacyItemMonitor).stopListening() 127 verify(privacyItemMonitor2).stopListening() 128 verify(callback).onPrivacyItemsChanged(emptyList()) 129 } 130 131 @Test testPrivacyItemsAggregatednull132 fun testPrivacyItemsAggregated() { 133 val item1 = PrivacyItem(PrivacyType.TYPE_CAMERA, 134 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0) 135 val item2 = PrivacyItem(PrivacyType.TYPE_MICROPHONE, 136 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 1) 137 doReturn(listOf(item1)) 138 .`when`(privacyItemMonitor).getActivePrivacyItems() 139 doReturn(listOf(item2)) 140 .`when`(privacyItemMonitor2).getActivePrivacyItems() 141 142 privacyItemController.addCallback(callback) 143 executor.runAllReady() 144 verify(callback).onPrivacyItemsChanged(capture(argCaptor)) 145 assertEquals(2, argCaptor.value.size) 146 assertTrue(argCaptor.value.contains(item1)) 147 assertTrue(argCaptor.value.contains(item2)) 148 } 149 150 @Test testDistinctItemsnull151 fun testDistinctItems() { 152 doReturn(listOf( 153 PrivacyItem(PrivacyType.TYPE_CAMERA, 154 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0), 155 PrivacyItem(PrivacyType.TYPE_CAMERA, 156 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0))) 157 .`when`(privacyItemMonitor).getActivePrivacyItems() 158 doReturn(listOf( 159 PrivacyItem(PrivacyType.TYPE_CAMERA, 160 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0))) 161 .`when`(privacyItemMonitor2).getActivePrivacyItems() 162 163 privacyItemController.addCallback(callback) 164 executor.runAllReady() 165 verify(callback).onPrivacyItemsChanged(capture(argCaptor)) 166 assertEquals(1, argCaptor.value.size) 167 } 168 169 @Test testSimilarItemsDifferentTimeStampnull170 fun testSimilarItemsDifferentTimeStamp() { 171 doReturn(listOf( 172 PrivacyItem(PrivacyType.TYPE_CAMERA, 173 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0), 174 PrivacyItem(PrivacyType.TYPE_CAMERA, 175 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 1))) 176 .`when`(privacyItemMonitor).getActivePrivacyItems() 177 178 privacyItemController.addCallback(callback) 179 executor.runAllReady() 180 verify(callback).onPrivacyItemsChanged(capture(argCaptor)) 181 assertEquals(2, argCaptor.value.size) 182 } 183 184 @Test testAddMultipleCallbacksnull185 fun testAddMultipleCallbacks() { 186 val otherCallback = mock(PrivacyItemController.Callback::class.java) 187 privacyItemController.addCallback(callback) 188 executor.runAllReady() 189 verify(callback).onPrivacyItemsChanged(anyList()) 190 191 privacyItemController.addCallback(otherCallback) 192 executor.runAllReady() 193 verify(otherCallback).onPrivacyItemsChanged(anyList()) 194 // Adding a callback should not unnecessarily call previous ones 195 verifyNoMoreInteractions(callback) 196 } 197 198 @Test testMultipleCallbacksAreUpdatednull199 fun testMultipleCallbacksAreUpdated() { 200 doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems() 201 202 val otherCallback = mock(PrivacyItemController.Callback::class.java) 203 privacyItemController.addCallback(callback) 204 privacyItemController.addCallback(otherCallback) 205 executor.runAllReady() 206 reset(callback) 207 reset(otherCallback) 208 209 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 210 argCaptorCallback.value.onPrivacyItemsChanged() 211 executor.runAllReady() 212 verify(callback).onPrivacyItemsChanged(anyList()) 213 verify(otherCallback).onPrivacyItemsChanged(anyList()) 214 } 215 216 @Test testRemoveCallbacknull217 fun testRemoveCallback() { 218 doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems() 219 val otherCallback = mock(PrivacyItemController.Callback::class.java) 220 privacyItemController.addCallback(callback) 221 privacyItemController.addCallback(otherCallback) 222 executor.runAllReady() 223 executor.runAllReady() 224 reset(callback) 225 reset(otherCallback) 226 227 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 228 privacyItemController.removeCallback(callback) 229 argCaptorCallback.value.onPrivacyItemsChanged() 230 executor.runAllReady() 231 verify(callback, never()).onPrivacyItemsChanged(anyList()) 232 verify(otherCallback).onPrivacyItemsChanged(anyList()) 233 } 234 235 @Test testListShouldBeCopynull236 fun testListShouldBeCopy() { 237 val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA, 238 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0)) 239 privacyItemController.privacyList = list 240 val privacyList = privacyItemController.privacyList 241 assertEquals(list, privacyList) 242 assertTrue(list !== privacyList) 243 } 244 245 @Test testLogListUpdatednull246 fun testLogListUpdated() { 247 val privacyItem = PrivacyItem( 248 PrivacyType.TYPE_LOCATION, 249 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 250 0 251 ) 252 253 doReturn(listOf(privacyItem)).`when`(privacyItemMonitor).getActivePrivacyItems() 254 255 privacyItemController.addCallback(callback) 256 executor.runAllReady() 257 258 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 259 argCaptorCallback.value.onPrivacyItemsChanged() 260 executor.runAllReady() 261 262 val captor = argumentCaptor<List<PrivacyItem>>() 263 verify(logger, atLeastOnce()).logRetrievedPrivacyItemsList(capture(captor)) 264 // Let's look at the last log 265 val values = captor.allValues 266 assertTrue(values[values.size - 1].contains(privacyItem)) 267 } 268 269 @Test testPassageOfTimeDoesNotRemoveIndicatorsnull270 fun testPassageOfTimeDoesNotRemoveIndicators() { 271 doReturn(listOf( 272 PrivacyItem(PrivacyType.TYPE_CAMERA, 273 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0) 274 )).`when`(privacyItemMonitor).getActivePrivacyItems() 275 276 privacyItemController.addCallback(callback) 277 278 fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS * 10) 279 executor.runAllReady() 280 281 verify(callback, never()).onPrivacyItemsChanged(emptyList()) 282 assertTrue(privacyItemController.privacyList.isNotEmpty()) 283 } 284 285 @Test testNotHeldAfterTimeIsOffnull286 fun testNotHeldAfterTimeIsOff() { 287 // Start with some element at time 0 288 doReturn(listOf( 289 PrivacyItem(PrivacyType.TYPE_CAMERA, 290 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0) 291 )).`when`(privacyItemMonitor).getActivePrivacyItems() 292 privacyItemController.addCallback(callback) 293 executor.runAllReady() 294 295 // Then remove it at time HOLD + 1 296 doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems() 297 fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS + 1) 298 299 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 300 argCaptorCallback.value.onPrivacyItemsChanged() 301 executor.runAllReady() 302 303 // See it's not there 304 verify(callback).onPrivacyItemsChanged(emptyList()) 305 assertTrue(privacyItemController.privacyList.isEmpty()) 306 } 307 308 @Test testElementNotRemovedBeforeHoldTimenull309 fun testElementNotRemovedBeforeHoldTime() { 310 // Start with some element at current time 311 doReturn(listOf( 312 PrivacyItem(PrivacyType.TYPE_CAMERA, 313 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 314 fakeClock.elapsedRealtime()) 315 )).`when`(privacyItemMonitor).getActivePrivacyItems() 316 privacyItemController.addCallback(callback) 317 executor.runAllReady() 318 319 // Then remove it at time HOLD - 1 320 doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems() 321 fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1) 322 323 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 324 argCaptorCallback.value.onPrivacyItemsChanged() 325 executor.runAllReady() 326 327 // See it's still there 328 verify(callback, never()).onPrivacyItemsChanged(emptyList()) 329 assertTrue(privacyItemController.privacyList.isNotEmpty()) 330 } 331 332 @Test testElementAutoRemovedAfterHoldTimenull333 fun testElementAutoRemovedAfterHoldTime() { 334 // Start with some element at time 0 335 doReturn(listOf( 336 PrivacyItem(PrivacyType.TYPE_CAMERA, 337 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0) 338 )).`when`(privacyItemMonitor).getActivePrivacyItems() 339 privacyItemController.addCallback(callback) 340 executor.runAllReady() 341 342 // Then remove it at time HOLD - 1 343 doReturn(emptyList<PrivacyItem>()).`when`(privacyItemMonitor).getActivePrivacyItems() 344 fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1) 345 346 verify(privacyItemMonitor).startListening(capture(argCaptorCallback)) 347 argCaptorCallback.value.onPrivacyItemsChanged() 348 executor.runAllReady() 349 350 fakeClock.advanceTime(2L) 351 executor.runAllReady() 352 353 // See it was auto-removed 354 verify(callback).onPrivacyItemsChanged(emptyList()) 355 assertTrue(privacyItemController.privacyList.isEmpty()) 356 } 357 358 @Test testFlagsAll_listeningToAllnull359 fun testFlagsAll_listeningToAll() { 360 verify(privacyConfig).addCallback(capture(argCaptorConfigCallback)) 361 privacyItemController.addCallback(callback) 362 `when`(privacyConfig.micCameraAvailable).thenReturn(true) 363 `when`(privacyConfig.locationAvailable).thenReturn(true) 364 `when`(privacyConfig.mediaProjectionAvailable).thenReturn(true) 365 argCaptorConfigCallback.value.onFlagMicCameraChanged(true) 366 argCaptorConfigCallback.value.onFlagLocationChanged(true) 367 argCaptorConfigCallback.value.onFlagMediaProjectionChanged(true) 368 executor.runAllReady() 369 370 assertTrue(privacyItemController.allIndicatorsAvailable) 371 } 372 373 @Test testFlags_onFlagMicCameraChangednull374 fun testFlags_onFlagMicCameraChanged() { 375 verify(privacyConfig).addCallback(capture(argCaptorConfigCallback)) 376 privacyItemController.addCallback(callback) 377 `when`(privacyConfig.micCameraAvailable).thenReturn(true) 378 argCaptorConfigCallback.value.onFlagMicCameraChanged(true) 379 executor.runAllReady() 380 381 assertTrue(privacyItemController.micCameraAvailable) 382 verify(callback).onFlagMicCameraChanged(true) 383 } 384 385 @Test testFlags_onFlagLocationChangednull386 fun testFlags_onFlagLocationChanged() { 387 verify(privacyConfig).addCallback(capture(argCaptorConfigCallback)) 388 privacyItemController.addCallback(callback) 389 `when`(privacyConfig.locationAvailable).thenReturn(true) 390 argCaptorConfigCallback.value.onFlagLocationChanged(true) 391 executor.runAllReady() 392 393 assertTrue(privacyItemController.locationAvailable) 394 verify(callback).onFlagLocationChanged(true) 395 } 396 397 @Test testFlags_onFlagMediaProjectionChangednull398 fun testFlags_onFlagMediaProjectionChanged() { 399 verify(privacyConfig).addCallback(capture(argCaptorConfigCallback)) 400 privacyItemController.addCallback(callback) 401 `when`(privacyConfig.mediaProjectionAvailable).thenReturn(true) 402 argCaptorConfigCallback.value.onFlagMediaProjectionChanged(true) 403 executor.runAllReady() 404 405 verify(callback).onFlagMediaProjectionChanged(true) 406 } 407 408 @Test testPausedElementsAreRemovednull409 fun testPausedElementsAreRemoved() { 410 doReturn(listOf( 411 PrivacyItem(PrivacyType.TYPE_MICROPHONE, 412 PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID), 0, true))) 413 .`when`(privacyItemMonitor).getActivePrivacyItems() 414 415 privacyItemController.addCallback(callback) 416 executor.runAllReady() 417 418 assertTrue(privacyItemController.privacyList.isEmpty()) 419 } 420 } 421