1 /* 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 17 package com.android.systemui.appops; 18 19 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; 20 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static junit.framework.TestCase.assertFalse; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertNotNull; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assume.assumeFalse; 30 import static org.mockito.ArgumentMatchers.any; 31 import static org.mockito.ArgumentMatchers.anyBoolean; 32 import static org.mockito.ArgumentMatchers.anyInt; 33 import static org.mockito.ArgumentMatchers.anyLong; 34 import static org.mockito.ArgumentMatchers.anyString; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.Mockito.doAnswer; 37 import static org.mockito.Mockito.inOrder; 38 import static org.mockito.Mockito.mock; 39 import static org.mockito.Mockito.never; 40 import static org.mockito.Mockito.times; 41 import static org.mockito.Mockito.verify; 42 import static org.mockito.Mockito.when; 43 44 import android.app.AppOpsManager; 45 import android.content.pm.PackageManager; 46 import android.media.AudioManager; 47 import android.media.AudioRecordingConfiguration; 48 import android.os.Looper; 49 import android.os.UserHandle; 50 import android.testing.AndroidTestingRunner; 51 import android.testing.TestableLooper; 52 53 import androidx.test.filters.SmallTest; 54 55 import com.android.internal.R; 56 import com.android.systemui.SysuiTestCase; 57 import com.android.systemui.broadcast.BroadcastDispatcher; 58 import com.android.systemui.dump.DumpManager; 59 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; 60 import com.android.systemui.util.time.FakeSystemClock; 61 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 import org.mockito.InOrder; 66 import org.mockito.Mock; 67 import org.mockito.MockitoAnnotations; 68 69 import java.util.Collections; 70 import java.util.List; 71 import java.util.Map; 72 73 @SmallTest 74 @RunWith(AndroidTestingRunner.class) 75 @TestableLooper.RunWithLooper 76 public class AppOpsControllerTest extends SysuiTestCase { 77 private static final String TEST_PACKAGE_NAME = "test"; 78 private static final String TEST_ATTRIBUTION_NAME = "attribution"; 79 private static final String SYSTEM_PKG = "android"; 80 private static final int TEST_UID = UserHandle.getUid(0, 0); 81 private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0); 82 private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0); 83 private static final int TEST_CHAIN_ID = 1; 84 85 @Mock 86 private AppOpsManager mAppOpsManager; 87 @Mock 88 private AppOpsController.Callback mCallback; 89 @Mock 90 private AppOpsControllerImpl.H mMockHandler; 91 @Mock 92 private DumpManager mDumpManager; 93 @Mock 94 private PackageManager mPackageManager; 95 @Mock 96 private IndividualSensorPrivacyController mSensorPrivacyController; 97 @Mock(stubOnly = true) 98 private AudioManager mAudioManager; 99 @Mock() 100 private BroadcastDispatcher mDispatcher; 101 @Mock(stubOnly = true) 102 private AudioManager.AudioRecordingCallback mRecordingCallback; 103 @Mock(stubOnly = true) 104 private AudioRecordingConfiguration mPausedMockRecording; 105 106 private AppOpsControllerImpl mController; 107 private TestableLooper mTestableLooper; 108 109 private String mExemptedRolePkgName; 110 111 @Before setUp()112 public void setUp() { 113 MockitoAnnotations.initMocks(this); 114 mTestableLooper = TestableLooper.get(this); 115 mExemptedRolePkgName = getContext().getString(R.string.config_systemUiIntelligence); 116 117 getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager); 118 119 // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of 120 // TEST_UID_NON_USER_SENSITIVE are user sensitive. 121 getContext().setMockPackageManager(mPackageManager); 122 123 doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0)) 124 .when(mAudioManager).registerAudioRecordingCallback(any(), any()); 125 when(mPausedMockRecording.getClientUid()).thenReturn(TEST_UID); 126 when(mPausedMockRecording.isClientSilenced()).thenReturn(true); 127 128 when(mAudioManager.getActiveRecordingConfigurations()) 129 .thenReturn(List.of(mPausedMockRecording)); 130 131 when(mSensorPrivacyController.isSensorBlocked(CAMERA)) 132 .thenReturn(false); 133 when(mSensorPrivacyController.isSensorBlocked(CAMERA)) 134 .thenReturn(false); 135 136 mController = new AppOpsControllerImpl( 137 mContext, 138 mTestableLooper.getLooper(), 139 mDumpManager, 140 mAudioManager, 141 mSensorPrivacyController, 142 mDispatcher, 143 new FakeSystemClock() 144 ); 145 } 146 147 @Test testOnlyListenForFewOps()148 public void testOnlyListenForFewOps() { 149 mController.setListening(true); 150 verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); 151 verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any()); 152 verify(mSensorPrivacyController, times(1)).addCallback(mController); 153 } 154 155 @Test testStopListening()156 public void testStopListening() { 157 mController.setListening(false); 158 verify(mAppOpsManager, times(1)).stopWatchingActive(mController); 159 verify(mDispatcher, times(1)).unregisterReceiver(mController); 160 verify(mSensorPrivacyController, times(1)).removeCallback(mController); 161 } 162 163 @Test startListening_fetchesCurrentActive_none()164 public void startListening_fetchesCurrentActive_none() { 165 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 166 .thenReturn(List.of()); 167 168 mController.setListening(true); 169 170 assertThat(mController.getActiveAppOps()).isEmpty(); 171 } 172 173 /** Regression test for b/294104969. */ 174 @Test startListening_fetchesCurrentActive_oneActive()175 public void startListening_fetchesCurrentActive_oneActive() { 176 AppOpsManager.PackageOps packageOps = createPackageOp( 177 "package.test", 178 /* packageUid= */ 2, 179 AppOpsManager.OPSTR_FINE_LOCATION, 180 /* isRunning= */ true); 181 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 182 .thenReturn(List.of(packageOps)); 183 184 // WHEN we start listening 185 mController.setListening(true); 186 187 // THEN the active list has the op 188 List<AppOpItem> list = mController.getActiveAppOps(); 189 assertEquals(1, list.size()); 190 AppOpItem first = list.get(0); 191 assertThat(first.getPackageName()).isEqualTo("package.test"); 192 assertThat(first.getUid()).isEqualTo(2); 193 assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); 194 } 195 196 @Test startListening_fetchesCurrentActive_multiplePackages()197 public void startListening_fetchesCurrentActive_multiplePackages() { 198 AppOpsManager.PackageOps packageOps1 = createPackageOp( 199 "package.one", 200 /* packageUid= */ 1, 201 AppOpsManager.OPSTR_FINE_LOCATION, 202 /* isRunning= */ true); 203 AppOpsManager.PackageOps packageOps2 = createPackageOp( 204 "package.two", 205 /* packageUid= */ 2, 206 AppOpsManager.OPSTR_FINE_LOCATION, 207 /* isRunning= */ false); 208 AppOpsManager.PackageOps packageOps3 = createPackageOp( 209 "package.three", 210 /* packageUid= */ 3, 211 AppOpsManager.OPSTR_FINE_LOCATION, 212 /* isRunning= */ true); 213 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 214 .thenReturn(List.of(packageOps1, packageOps2, packageOps3)); 215 216 // WHEN we start listening 217 mController.setListening(true); 218 219 // THEN the active list has the ops 220 List<AppOpItem> list = mController.getActiveAppOps(); 221 assertEquals(2, list.size()); 222 223 AppOpItem item0 = list.get(0); 224 assertThat(item0.getPackageName()).isEqualTo("package.one"); 225 assertThat(item0.getUid()).isEqualTo(1); 226 assertThat(item0.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); 227 228 AppOpItem item1 = list.get(1); 229 assertThat(item1.getPackageName()).isEqualTo("package.three"); 230 assertThat(item1.getUid()).isEqualTo(3); 231 assertThat(item1.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION); 232 } 233 234 @Test startListening_fetchesCurrentActive_multipleEntries()235 public void startListening_fetchesCurrentActive_multipleEntries() { 236 AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); 237 when(packageOps.getUid()).thenReturn(1); 238 when(packageOps.getPackageName()).thenReturn("package.one"); 239 240 // Entry 1 241 AppOpsManager.OpEntry entry1 = mock(AppOpsManager.OpEntry.class); 242 when(entry1.getOpStr()).thenReturn(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE); 243 AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class); 244 when(attributed1.isRunning()).thenReturn(true); 245 when(entry1.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed1)); 246 // Entry 2 247 AppOpsManager.OpEntry entry2 = mock(AppOpsManager.OpEntry.class); 248 when(entry2.getOpStr()).thenReturn(AppOpsManager.OPSTR_CAMERA); 249 AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class); 250 when(attributed2.isRunning()).thenReturn(true); 251 when(entry2.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed2)); 252 // Entry 3 253 AppOpsManager.OpEntry entry3 = mock(AppOpsManager.OpEntry.class); 254 when(entry3.getOpStr()).thenReturn(AppOpsManager.OPSTR_FINE_LOCATION); 255 AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class); 256 when(attributed3.isRunning()).thenReturn(false); 257 when(entry3.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed3)); 258 259 when(packageOps.getOps()).thenReturn(List.of(entry1, entry2, entry3)); 260 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 261 .thenReturn(List.of(packageOps)); 262 263 // WHEN we start listening 264 mController.setListening(true); 265 266 // THEN the active list has the ops 267 List<AppOpItem> list = mController.getActiveAppOps(); 268 assertEquals(2, list.size()); 269 270 AppOpItem first = list.get(0); 271 assertThat(first.getPackageName()).isEqualTo("package.one"); 272 assertThat(first.getUid()).isEqualTo(1); 273 assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_PHONE_CALL_MICROPHONE); 274 275 AppOpItem second = list.get(1); 276 assertThat(second.getPackageName()).isEqualTo("package.one"); 277 assertThat(second.getUid()).isEqualTo(1); 278 assertThat(second.getCode()).isEqualTo(AppOpsManager.OP_CAMERA); 279 } 280 281 @Test startListening_fetchesCurrentActive_multipleAttributes()282 public void startListening_fetchesCurrentActive_multipleAttributes() { 283 AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); 284 when(packageOps.getUid()).thenReturn(1); 285 when(packageOps.getPackageName()).thenReturn("package.one"); 286 AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class); 287 when(entry.getOpStr()).thenReturn(AppOpsManager.OPSTR_RECORD_AUDIO); 288 289 AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class); 290 when(attributed1.isRunning()).thenReturn(false); 291 AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class); 292 when(attributed2.isRunning()).thenReturn(true); 293 AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class); 294 when(attributed3.isRunning()).thenReturn(true); 295 when(entry.getAttributedOpEntries()).thenReturn( 296 Map.of("attr1", attributed1, "attr2", attributed2, "attr3", attributed3)); 297 298 when(packageOps.getOps()).thenReturn(List.of(entry)); 299 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 300 .thenReturn(List.of(packageOps)); 301 302 // WHEN we start listening 303 mController.setListening(true); 304 305 // THEN the active list has the ops 306 List<AppOpItem> list = mController.getActiveAppOps(); 307 // Multiple attributes get merged into one entry in the active ops 308 assertEquals(1, list.size()); 309 310 AppOpItem first = list.get(0); 311 assertThat(first.getPackageName()).isEqualTo("package.one"); 312 assertThat(first.getUid()).isEqualTo(1); 313 assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_RECORD_AUDIO); 314 } 315 316 /** Regression test for b/294104969. */ 317 @Test addCallback_existingCallbacksNotifiedOfCurrentActive()318 public void addCallback_existingCallbacksNotifiedOfCurrentActive() { 319 AppOpsManager.PackageOps packageOps1 = createPackageOp( 320 "package.one", 321 /* packageUid= */ 1, 322 AppOpsManager.OPSTR_FINE_LOCATION, 323 /* isRunning= */ true); 324 AppOpsManager.PackageOps packageOps2 = createPackageOp( 325 "package.two", 326 /* packageUid= */ 2, 327 AppOpsManager.OPSTR_RECORD_AUDIO, 328 /* isRunning= */ true); 329 AppOpsManager.PackageOps packageOps3 = createPackageOp( 330 "package.three", 331 /* packageUid= */ 3, 332 AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 333 /* isRunning= */ true); 334 when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)) 335 .thenReturn(List.of(packageOps1, packageOps2, packageOps3)); 336 337 // WHEN we start listening 338 mController.addCallback( 339 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION}, 340 mCallback); 341 mTestableLooper.processAllMessages(); 342 343 // THEN the callback is notified of the current active ops it cares about 344 verify(mCallback).onActiveStateChanged( 345 AppOpsManager.OP_FINE_LOCATION, 346 /* uid= */ 1, 347 "package.one", 348 true); 349 verify(mCallback).onActiveStateChanged( 350 AppOpsManager.OP_RECORD_AUDIO, 351 /* uid= */ 2, 352 "package.two", 353 true); 354 verify(mCallback, never()).onActiveStateChanged( 355 AppOpsManager.OP_PHONE_CALL_MICROPHONE, 356 /* uid= */ 3, 357 "package.three", 358 true); 359 } 360 361 @Test addCallback_includedCode()362 public void addCallback_includedCode() { 363 mController.addCallback( 364 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION}, 365 mCallback); 366 mController.onOpActiveChanged( 367 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 368 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 369 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 370 mTestableLooper.processAllMessages(); 371 verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, 372 TEST_UID, TEST_PACKAGE_NAME, true); 373 } 374 375 @Test addCallback_notIncludedCode()376 public void addCallback_notIncludedCode() { 377 mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); 378 mController.onOpActiveChanged( 379 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 380 mTestableLooper.processAllMessages(); 381 verify(mCallback, never()).onActiveStateChanged( 382 anyInt(), anyInt(), anyString(), anyBoolean()); 383 } 384 385 @Test removeCallback_sameCode()386 public void removeCallback_sameCode() { 387 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 388 mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 389 mController.onOpActiveChanged( 390 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 391 mTestableLooper.processAllMessages(); 392 verify(mCallback, never()).onActiveStateChanged( 393 anyInt(), anyInt(), anyString(), anyBoolean()); 394 } 395 396 @Test addCallback_notSameCode()397 public void addCallback_notSameCode() { 398 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 399 mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback); 400 mController.onOpActiveChanged( 401 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 402 mTestableLooper.processAllMessages(); 403 verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, 404 TEST_UID, TEST_PACKAGE_NAME, true); 405 } 406 407 @Test getActiveItems_sameDetails()408 public void getActiveItems_sameDetails() { 409 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 410 TEST_UID, TEST_PACKAGE_NAME, true); 411 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 412 TEST_UID, TEST_PACKAGE_NAME, true); 413 assertEquals(1, mController.getActiveAppOps().size()); 414 } 415 416 @Test getActiveItems_differentDetails()417 public void getActiveItems_differentDetails() { 418 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 419 TEST_UID, TEST_PACKAGE_NAME, true); 420 mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, 421 TEST_UID, TEST_PACKAGE_NAME, true); 422 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, 423 TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, 424 AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 425 assertEquals(3, mController.getActiveAppOps().size()); 426 } 427 428 @Test getActiveItemsForUser()429 public void getActiveItemsForUser() { 430 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 431 TEST_UID, TEST_PACKAGE_NAME, true); 432 mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, 433 TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 434 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, 435 TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, 436 AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 437 assertEquals(2, 438 mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID), false).size()); 439 assertEquals(1, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER), 440 false).size()); 441 } 442 443 @Test systemAndExemptedRolesAreIgnored()444 public void systemAndExemptedRolesAreIgnored() { 445 assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals("")); 446 447 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 448 TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true); 449 assertEquals(0, mController.getActiveAppOpsForUser( 450 UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size()); 451 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 452 TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true); 453 assertEquals(0, mController.getActiveAppOpsForUser( 454 UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size()); 455 } 456 457 @Test exemptedRoleNotNotified()458 public void exemptedRoleNotNotified() { 459 assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals("")); 460 461 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 462 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, 463 TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true); 464 465 mTestableLooper.processAllMessages(); 466 467 verify(mCallback, never()) 468 .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean()); 469 } 470 471 @Test opNotedScheduledForRemoval()472 public void opNotedScheduledForRemoval() { 473 mController.setBGHandler(mMockHandler); 474 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 475 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 476 verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong()); 477 } 478 479 @Test noItemsAfterStopListening()480 public void noItemsAfterStopListening() { 481 mController.setBGHandler(mMockHandler); 482 483 mController.setListening(true); 484 mController.onOpActiveChanged(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, 485 TEST_PACKAGE_NAME, true); 486 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 487 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 488 assertFalse(mController.getActiveAppOps().isEmpty()); 489 490 mController.setListening(false); 491 492 verify(mMockHandler).removeCallbacksAndMessages(null); 493 assertTrue(mController.getActiveAppOps().isEmpty()); 494 } 495 496 @Test noDoubleUpdateOnOpNoted()497 public void noDoubleUpdateOnOpNoted() { 498 mController.setBGHandler(mMockHandler); 499 500 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 501 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 502 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 503 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 504 505 // Only one post to notify subscribers 506 verify(mMockHandler, times(1)).post(any()); 507 508 List<AppOpItem> list = mController.getActiveAppOps(); 509 assertEquals(1, list.size()); 510 } 511 512 @Test onDoubleOPNoted_scheduleTwiceForRemoval()513 public void onDoubleOPNoted_scheduleTwiceForRemoval() { 514 mController.setBGHandler(mMockHandler); 515 516 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 517 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 518 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 519 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 520 521 // Only one post to notify subscribers 522 verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong()); 523 } 524 525 @Test testUntrustedChainUsagesDiscarded()526 public void testUntrustedChainUsagesDiscarded() { 527 assertTrue(mController.getActiveAppOps().isEmpty()); 528 mController.setBGHandler(mMockHandler); 529 530 //untrusted receiver access 531 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, 532 TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true, 533 AppOpsManager.ATTRIBUTION_FLAG_RECEIVER, TEST_CHAIN_ID); 534 //untrusted intermediary access 535 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, 536 TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true, 537 AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY, TEST_CHAIN_ID); 538 assertTrue(mController.getActiveAppOps().isEmpty()); 539 } 540 541 @Test testTrustedChainUsagesKept()542 public void testTrustedChainUsagesKept() { 543 //untrusted accessor access 544 mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, 545 TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true, 546 AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, TEST_CHAIN_ID); 547 //trusted access 548 mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, TEST_UID, 549 TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true, 550 AppOpsManager.ATTRIBUTION_FLAG_RECEIVER | AppOpsManager.ATTRIBUTION_FLAG_TRUSTED, 551 TEST_CHAIN_ID); 552 assertEquals(2, mController.getActiveAppOps().size()); 553 } 554 555 @Test testActiveOpNotRemovedAfterNoted()556 public void testActiveOpNotRemovedAfterNoted() throws InterruptedException { 557 // Replaces the timeout delay with 5 ms 558 TestHandler testHandler = new TestHandler(mTestableLooper.getLooper()); 559 560 mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); 561 mController.setBGHandler(testHandler); 562 563 mController.onOpActiveChanged( 564 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 565 566 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 567 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 568 569 // Check that we "scheduled" the removal. Don't actually schedule until we are ready to 570 // process messages at a later time. 571 assertNotNull(testHandler.mDelayScheduled); 572 573 mTestableLooper.processAllMessages(); 574 List<AppOpItem> list = mController.getActiveAppOps(); 575 verify(mCallback).onActiveStateChanged( 576 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 577 578 // Duplicates are not removed between active and noted 579 assertEquals(2, list.size()); 580 581 // Now is later, so we can schedule delayed messages. 582 testHandler.scheduleDelayed(); 583 mTestableLooper.processAllMessages(); 584 585 verify(mCallback, never()).onActiveStateChanged( 586 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); 587 list = mController.getActiveAppOps(); 588 assertEquals(1, list.size()); 589 } 590 591 @Test testNotedNotRemovedAfterActive()592 public void testNotedNotRemovedAfterActive() { 593 mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); 594 595 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 596 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 597 598 mController.onOpActiveChanged( 599 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 600 601 mTestableLooper.processAllMessages(); 602 List<AppOpItem> list = mController.getActiveAppOps(); 603 verify(mCallback).onActiveStateChanged( 604 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 605 606 // Duplicates are not removed between active and noted 607 assertEquals(2, list.size()); 608 609 mController.onOpActiveChanged( 610 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); 611 612 mTestableLooper.processAllMessages(); 613 614 verify(mCallback, never()).onActiveStateChanged( 615 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false); 616 list = mController.getActiveAppOps(); 617 assertEquals(1, list.size()); 618 } 619 620 @Test testNotedAndActiveOnlyOneCall()621 public void testNotedAndActiveOnlyOneCall() { 622 mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); 623 624 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 625 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 626 627 mController.onOpActiveChanged( 628 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 629 630 mTestableLooper.processAllMessages(); 631 verify(mCallback).onActiveStateChanged( 632 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 633 } 634 635 @Test testActiveAndNotedOnlyOneCall()636 public void testActiveAndNotedOnlyOneCall() { 637 mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); 638 639 mController.onOpActiveChanged( 640 AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 641 642 mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 643 TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); 644 645 mTestableLooper.processAllMessages(); 646 verify(mCallback).onActiveStateChanged( 647 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); 648 } 649 650 @Test testPausedRecordingIsRetrievedOnCreation()651 public void testPausedRecordingIsRetrievedOnCreation() { 652 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 653 mTestableLooper.processAllMessages(); 654 655 mController.onOpActiveChanged( 656 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 657 mTestableLooper.processAllMessages(); 658 659 verify(mCallback, never()) 660 .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean()); 661 } 662 663 @Test testPausedRecordingFilteredOut()664 public void testPausedRecordingFilteredOut() { 665 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 666 mTestableLooper.processAllMessages(); 667 668 mController.onOpActiveChanged( 669 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 670 mTestableLooper.processAllMessages(); 671 672 assertTrue(mController.getActiveAppOps().isEmpty()); 673 } 674 675 @Test testPausedPhoneCallMicrophoneFilteredOut()676 public void testPausedPhoneCallMicrophoneFilteredOut() { 677 mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback); 678 mTestableLooper.processAllMessages(); 679 680 mController.onOpActiveChanged( 681 AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, true); 682 mTestableLooper.processAllMessages(); 683 684 assertTrue(mController.getActiveAppOps().isEmpty()); 685 } 686 687 @Test testOnlyRecordAudioPhoneCallMicrophonePaused()688 public void testOnlyRecordAudioPhoneCallMicrophonePaused() { 689 mController.addCallback(new int[]{ 690 AppOpsManager.OP_RECORD_AUDIO, 691 AppOpsManager.OP_CAMERA 692 }, mCallback); 693 mTestableLooper.processAllMessages(); 694 695 mController.onOpActiveChanged( 696 AppOpsManager.OPSTR_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); 697 mTestableLooper.processAllMessages(); 698 699 verify(mCallback).onActiveStateChanged( 700 AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); 701 List<AppOpItem> list = mController.getActiveAppOps(); 702 703 assertEquals(1, list.size()); 704 assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); 705 } 706 707 @Test testUnpausedRecordingSentActive()708 public void testUnpausedRecordingSentActive() { 709 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 710 mTestableLooper.processAllMessages(); 711 mController.onOpActiveChanged( 712 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 713 714 mTestableLooper.processAllMessages(); 715 mRecordingCallback.onRecordingConfigChanged(Collections.emptyList()); 716 717 mTestableLooper.processAllMessages(); 718 719 verify(mCallback).onActiveStateChanged( 720 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); 721 } 722 723 @Test testAudioPausedSentInactive()724 public void testAudioPausedSentInactive() { 725 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); 726 mTestableLooper.processAllMessages(); 727 mController.onOpActiveChanged( 728 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 729 mTestableLooper.processAllMessages(); 730 731 AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class); 732 when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER); 733 when(mockARC.isClientSilenced()).thenReturn(true); 734 735 mRecordingCallback.onRecordingConfigChanged(List.of(mockARC)); 736 mTestableLooper.processAllMessages(); 737 738 InOrder inOrder = inOrder(mCallback); 739 inOrder.verify(mCallback).onActiveStateChanged( 740 AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 741 inOrder.verify(mCallback).onActiveStateChanged( 742 AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); 743 } 744 745 @Test testAudioFilteredWhenMicDisabled()746 public void testAudioFilteredWhenMicDisabled() { 747 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, 748 mCallback); 749 mTestableLooper.processAllMessages(); 750 mController.onOpActiveChanged( 751 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 752 mTestableLooper.processAllMessages(); 753 List<AppOpItem> list = mController.getActiveAppOps(); 754 assertEquals(1, list.size()); 755 assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); 756 assertFalse(list.get(0).isDisabled()); 757 758 // Add a camera op, and disable the microphone. The camera op should be the only op returned 759 mController.onSensorBlockedChanged(MICROPHONE, true); 760 mController.onOpActiveChanged( 761 AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 762 mTestableLooper.processAllMessages(); 763 list = mController.getActiveAppOps(); 764 assertEquals(1, list.size()); 765 assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); 766 767 768 // Re enable the microphone, and verify the op returns 769 mController.onSensorBlockedChanged(MICROPHONE, false); 770 mTestableLooper.processAllMessages(); 771 772 list = mController.getActiveAppOps(); 773 assertEquals(2, list.size()); 774 int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; 775 assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode()); 776 } 777 778 @Test testPhoneCallMicrophoneFilteredWhenMicDisabled()779 public void testPhoneCallMicrophoneFilteredWhenMicDisabled() { 780 mController.addCallback( 781 new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_CAMERA}, 782 mCallback); 783 mTestableLooper.processAllMessages(); 784 mController.onOpActiveChanged( 785 AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 786 mTestableLooper.processAllMessages(); 787 List<AppOpItem> list = mController.getActiveAppOps(); 788 assertEquals(1, list.size()); 789 assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(0).getCode()); 790 assertFalse(list.get(0).isDisabled()); 791 792 // Add a camera op, and disable the microphone. The camera op should be the only op returned 793 mController.onSensorBlockedChanged(MICROPHONE, true); 794 mController.onOpActiveChanged( 795 AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 796 mTestableLooper.processAllMessages(); 797 list = mController.getActiveAppOps(); 798 assertEquals(1, list.size()); 799 assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); 800 801 802 // Re enable the microphone, and verify the op returns 803 mController.onSensorBlockedChanged(MICROPHONE, false); 804 mTestableLooper.processAllMessages(); 805 806 list = mController.getActiveAppOps(); 807 assertEquals(2, list.size()); 808 int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; 809 assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(micIdx).getCode()); 810 } 811 812 @Test testCameraFilteredWhenCameraDisabled()813 public void testCameraFilteredWhenCameraDisabled() { 814 mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, 815 mCallback); 816 mTestableLooper.processAllMessages(); 817 mController.onOpActiveChanged( 818 AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 819 mTestableLooper.processAllMessages(); 820 List<AppOpItem> list = mController.getActiveAppOps(); 821 assertEquals(1, list.size()); 822 assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); 823 assertFalse(list.get(0).isDisabled()); 824 825 // Add an audio op, and disable the camera. The audio op should be the only op returned 826 mController.onSensorBlockedChanged(CAMERA, true); 827 mController.onOpActiveChanged( 828 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 829 mTestableLooper.processAllMessages(); 830 list = mController.getActiveAppOps(); 831 assertEquals(1, list.size()); 832 assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); 833 834 // Re enable the camera, and verify the op returns 835 mController.onSensorBlockedChanged(CAMERA, false); 836 mTestableLooper.processAllMessages(); 837 838 list = mController.getActiveAppOps(); 839 assertEquals(2, list.size()); 840 int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 0 : 1; 841 assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode()); 842 } 843 844 @Test testPhoneCallCameraFilteredWhenCameraDisabled()845 public void testPhoneCallCameraFilteredWhenCameraDisabled() { 846 mController.addCallback( 847 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA}, 848 mCallback); 849 mTestableLooper.processAllMessages(); 850 mController.onOpActiveChanged( 851 AppOpsManager.OPSTR_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 852 mTestableLooper.processAllMessages(); 853 List<AppOpItem> list = mController.getActiveAppOps(); 854 assertEquals(1, list.size()); 855 assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(0).getCode()); 856 assertFalse(list.get(0).isDisabled()); 857 858 // Add an audio op, and disable the camera. The audio op should be the only op returned 859 mController.onSensorBlockedChanged(CAMERA, true); 860 mController.onOpActiveChanged( 861 AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); 862 mTestableLooper.processAllMessages(); 863 list = mController.getActiveAppOps(); 864 assertEquals(1, list.size()); 865 assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); 866 867 // Re enable the camera, and verify the op returns 868 mController.onSensorBlockedChanged(CAMERA, false); 869 mTestableLooper.processAllMessages(); 870 871 list = mController.getActiveAppOps(); 872 assertEquals(2, list.size()); 873 int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_PHONE_CALL_CAMERA ? 0 : 1; 874 assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode()); 875 } 876 createPackageOp( String packageName, int packageUid, String opStr, boolean isRunning)877 private AppOpsManager.PackageOps createPackageOp( 878 String packageName, int packageUid, String opStr, boolean isRunning) { 879 AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class); 880 when(packageOps.getPackageName()).thenReturn(packageName); 881 when(packageOps.getUid()).thenReturn(packageUid); 882 AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class); 883 when(entry.getOpStr()).thenReturn(opStr); 884 AppOpsManager.AttributedOpEntry attributed = mock(AppOpsManager.AttributedOpEntry.class); 885 when(attributed.isRunning()).thenReturn(isRunning); 886 887 when(packageOps.getOps()).thenReturn(Collections.singletonList(entry)); 888 when(entry.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed)); 889 890 return packageOps; 891 } 892 893 private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper)894 TestHandler(Looper looper) { 895 mController.super(looper); 896 } 897 898 Runnable mDelayScheduled; 899 scheduleDelayed()900 void scheduleDelayed() { 901 if (mDelayScheduled != null) { 902 mDelayScheduled.run(); 903 mDelayScheduled = null; 904 } 905 } 906 907 @Override scheduleRemoval(AppOpItem item, long timeToRemoval)908 public void scheduleRemoval(AppOpItem item, long timeToRemoval) { 909 mDelayScheduled = () -> super.scheduleRemoval(item, 0L); 910 } 911 } 912 } 913