1 /* <lambda>null2 * Copyright (C) 2021 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 android.sensorprivacy.cts 18 19 import android.app.AppOpsManager 20 import android.app.KeyguardManager 21 import android.content.Context 22 import android.content.Intent 23 import android.content.pm.PackageManager 24 import android.content.res.Resources.NotFoundException 25 import android.hardware.SensorPrivacyManager 26 import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener 27 import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams 28 import android.hardware.SensorPrivacyManager.Sensors.CAMERA 29 import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE 30 import android.hardware.SensorPrivacyManager.Sources.OTHER 31 import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE 32 import android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE 33 import android.hardware.camera2.CameraCharacteristics 34 import android.hardware.camera2.CameraManager 35 import android.hardware.camera2.CameraMetadata 36 import android.os.PowerManager 37 import android.platform.test.annotations.AppModeFull 38 import android.platform.test.annotations.AsbSecurityTest 39 import android.support.test.uiautomator.By 40 import android.util.Log 41 import android.view.KeyEvent 42 import androidx.test.platform.app.InstrumentationRegistry 43 import androidx.test.uiautomator.UiDevice 44 import androidx.test.uiautomator.Until 45 import com.android.bedstead.multiuser.annotations.RequireNotVisibleBackgroundUsers 46 import com.android.compatibility.common.util.SystemUtil 47 import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity 48 import com.android.compatibility.common.util.SystemUtil.eventually 49 import com.android.compatibility.common.util.SystemUtil.getEventually 50 import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow 51 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity 52 import com.android.compatibility.common.util.UiAutomatorUtils 53 import java.nio.charset.StandardCharsets 54 import java.util.concurrent.CountDownLatch 55 import java.util.concurrent.Executors 56 import java.util.concurrent.TimeUnit 57 import java.util.regex.Pattern 58 import org.junit.After 59 import org.junit.Assert.assertEquals 60 import org.junit.Assert.assertFalse 61 import org.junit.Assert.assertNotNull 62 import org.junit.Assert.assertNull 63 import org.junit.Assert.assertTrue 64 import org.junit.Assume.assumeFalse 65 import org.junit.Assume.assumeTrue 66 import org.junit.Before 67 import org.junit.Test 68 69 abstract class SensorPrivacyBaseTest( 70 val sensor: Int, 71 vararg val extras: String 72 ) { 73 74 companion object { 75 val TAG = this::class.simpleName 76 const val MIC_CAM_ACTIVITY_ACTION = 77 "android.sensorprivacy.cts.usemiccamera.action.USE_MIC_CAM" 78 const val MIC_CAM_OVERLAY_ACTIVITY_ACTION = 79 "android.sensorprivacy.cts.usemiccamera.overlay.action.USE_MIC_CAM" 80 const val SHOW_OVERLAY_ACTION = 81 "android.sensorprivacy.cts.usemiccamera.action.SHOW_OVERLAY_ACTION" 82 const val FINISH_MIC_CAM_ACTIVITY_ACTION = 83 "android.sensorprivacy.cts.usemiccamera.action.FINISH_USE_MIC_CAM" 84 const val USE_MIC_EXTRA = 85 "android.sensorprivacy.cts.usemiccamera.extra.USE_MICROPHONE" 86 const val USE_CAM_EXTRA = 87 "android.sensorprivacy.cts.usemiccamera.extra.USE_CAMERA" 88 const val DELAYED_ACTIVITY_EXTRA = 89 "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY" 90 const val DELAYED_ACTIVITY_NEW_TASK_EXTRA = 91 "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY_NEW_TASK" 92 const val RETRY_CAM_EXTRA = 93 "android.sensorprivacy.cts.usemiccamera.extra.RETRY_CAM_EXTRA" 94 const val PKG_NAME = "android.sensorprivacy.cts.usemiccamera" 95 const val RECORDING_FILE_NAME = "${PKG_NAME}_record.mp4" 96 const val ACTIVITY_TITLE_SNIP = "CtsUseMic" 97 const val SENSOR_USE_TIME_MS = 5L 98 const val NEW_WINDOW_TIMEOUT_MILLIS = 5_000L 99 } 100 101 protected val instrumentation = InstrumentationRegistry.getInstrumentation()!! 102 protected val uiAutomation = instrumentation.uiAutomation!! 103 protected val uiDevice = UiDevice.getInstance(instrumentation)!! 104 protected val context = instrumentation.targetContext!! 105 protected val spm = context.getSystemService(SensorPrivacyManager::class.java)!! 106 protected val packageManager = context.packageManager!! 107 protected val op = getOpForSensor(sensor) 108 109 var oldState: Boolean = false 110 111 @Before 112 open fun init() { 113 oldState = isSensorPrivacyEnabled() 114 setSensor(false) 115 uiDevice.wakeUp() 116 runShellCommandOrThrow("wm dismiss-keyguard") 117 uiDevice.waitForIdle() 118 SystemUtil.waitForBroadcastDispatch(FINISH_MIC_CAM_ACTIVITY_ACTION) 119 } 120 121 @After 122 open fun tearDown() { 123 finishTestApp() 124 Thread.sleep(3000) 125 setSensor(oldState) 126 } 127 128 @Test 129 fun testSetSensor() { 130 assumeSensorToggleSupport() 131 setSensor(true) 132 assertTrue(isSensorPrivacyEnabled()) 133 134 setSensor(false) 135 assertFalse(isSensorPrivacyEnabled()) 136 } 137 138 @Test 139 fun testSensorPrivacy_softwareToggle() { 140 assumeSensorToggleSupport() 141 setSensor(true) 142 assertTrue(isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE)) 143 144 setSensor(false) 145 assertFalse(isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE)) 146 } 147 148 @Test 149 fun testSensorPrivacy_hardwareToggle() { 150 assumeSensorToggleSupport() 151 // Default value should be false weather HW toggles 152 // are supported or not 153 assertFalse(isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE)) 154 } 155 156 @Test 157 fun testSensorPrivacy_comboToggle() { 158 assumeSensorToggleSupport() 159 setSensor(sensor, true) 160 assertTrue(isCombinedSensorPrivacyEnabled()) 161 162 setSensor(sensor, false) 163 assertFalse(isCombinedSensorPrivacyEnabled()) 164 } 165 166 @Test 167 fun testDialog() { 168 assumeSensorToggleSupport() 169 testDialog(delayedActivity = false, delayedActivityNewTask = false) 170 } 171 172 @Test 173 fun testDialog_remainsOnTop() { 174 assumeSensorToggleSupport() 175 testDialog(delayedActivity = true, delayedActivityNewTask = false) 176 } 177 178 @Test 179 fun testDialog_remainsOnTop_newTask() { 180 assumeSensorToggleSupport() 181 testDialog(delayedActivity = true, delayedActivityNewTask = true) 182 } 183 184 fun testDialog(delayedActivity: Boolean = false, delayedActivityNewTask: Boolean = false) { 185 try { 186 setSensor(true) 187 val intent = Intent(MIC_CAM_ACTIVITY_ACTION) 188 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 189 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 190 .addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) 191 for (extra in extras) { 192 intent.putExtra(extra, true) 193 } 194 intent.putExtra(DELAYED_ACTIVITY_EXTRA, delayedActivity) 195 intent.putExtra(DELAYED_ACTIVITY_NEW_TASK_EXTRA, delayedActivityNewTask) 196 doAndWaitForWindowTransition { 197 context.startActivity(intent) 198 } 199 Thread.sleep(3000) 200 unblockSensorWithDialogAndAssert() 201 } finally { 202 broadcastAndWait(FINISH_MIC_CAM_ACTIVITY_ACTION) 203 } 204 } 205 206 @Test 207 fun testListener() { 208 assumeSensorToggleSupport() 209 val executor = Executors.newSingleThreadExecutor() 210 setSensor(false) 211 val latchEnabled = CountDownLatch(1) 212 var listener = 213 OnSensorPrivacyChangedListener { _, enabled: Boolean -> 214 if (enabled) { 215 latchEnabled.countDown() 216 } 217 } 218 runWithShellPermissionIdentity { 219 spm.addSensorPrivacyListener(sensor, executor, listener) 220 } 221 setSensor(true) 222 latchEnabled.await(100, TimeUnit.MILLISECONDS) 223 runWithShellPermissionIdentity { 224 spm.removeSensorPrivacyListener(sensor, listener) 225 } 226 227 val latchDisabled = CountDownLatch(1) 228 listener = OnSensorPrivacyChangedListener { _, enabled: Boolean -> 229 if (!enabled) { 230 latchDisabled.countDown() 231 } 232 } 233 runWithShellPermissionIdentity { 234 spm.addSensorPrivacyListener(sensor, executor, listener) 235 } 236 setSensor(false) 237 latchEnabled.await(100, TimeUnit.MILLISECONDS) 238 runWithShellPermissionIdentity { 239 spm.removeSensorPrivacyListener(sensor, listener) 240 } 241 } 242 243 @Test 244 fun testToggleListener() { 245 assumeSensorToggleSupport() 246 val executor = Executors.newSingleThreadExecutor() 247 setSensor(false) 248 val latchEnabled = CountDownLatch(1) 249 val listenerSensorEnabled = object : OnSensorPrivacyChangedListener { 250 override fun onSensorPrivacyChanged(params: SensorPrivacyChangedParams) { 251 if (params.isEnabled && 252 params.sensor == sensor && 253 params.toggleType == TOGGLE_TYPE_SOFTWARE) { 254 latchEnabled.countDown() 255 } 256 } 257 258 override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) { 259 } 260 } 261 runWithShellPermissionIdentity { 262 spm.addSensorPrivacyListener(executor, listenerSensorEnabled) 263 } 264 setSensor(true) 265 latchEnabled.await(100, TimeUnit.MILLISECONDS) 266 runWithShellPermissionIdentity { 267 spm.removeSensorPrivacyListener(listenerSensorEnabled) 268 } 269 270 val latchDisabled = CountDownLatch(1) 271 val listenerSensorDisabled = object : OnSensorPrivacyChangedListener { 272 override fun onSensorPrivacyChanged(params: SensorPrivacyChangedParams) { 273 if (!params.isEnabled && 274 params.sensor == sensor && 275 params.toggleType == TOGGLE_TYPE_SOFTWARE) { 276 latchDisabled.countDown() 277 } 278 } 279 280 override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) { 281 } 282 } 283 runWithShellPermissionIdentity { 284 spm.addSensorPrivacyListener(executor, listenerSensorDisabled) 285 } 286 setSensor(false) 287 latchEnabled.await(100, TimeUnit.MILLISECONDS) 288 runWithShellPermissionIdentity { 289 spm.removeSensorPrivacyListener(listenerSensorDisabled) 290 } 291 } 292 293 @Test 294 fun testToggleListener_defaultExecutor() { 295 assumeSensorToggleSupport() 296 setSensor(false) 297 val latchEnabled = CountDownLatch(1) 298 var listenerSensorEnabled = object : OnSensorPrivacyChangedListener { 299 override fun onSensorPrivacyChanged(params: SensorPrivacyChangedParams) { 300 if (params.isEnabled && params.sensor == sensor) { 301 latchEnabled.countDown() 302 } 303 } 304 305 override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) { 306 } 307 } 308 runWithShellPermissionIdentity { 309 spm.addSensorPrivacyListener(listenerSensorEnabled) 310 } 311 setSensor(true) 312 latchEnabled.await(100, TimeUnit.MILLISECONDS) 313 runWithShellPermissionIdentity { 314 spm.removeSensorPrivacyListener(listenerSensorEnabled) 315 } 316 317 val latchDisabled = CountDownLatch(1) 318 val listenerSensorDisabled = object : OnSensorPrivacyChangedListener { 319 override fun onSensorPrivacyChanged(params: SensorPrivacyChangedParams) { 320 if (!params.isEnabled && params.sensor == sensor) { 321 latchDisabled.countDown() 322 } 323 } 324 325 override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) { 326 } 327 } 328 runWithShellPermissionIdentity { 329 spm.addSensorPrivacyListener(listenerSensorDisabled) 330 } 331 setSensor(false) 332 latchEnabled.await(100, TimeUnit.MILLISECONDS) 333 runWithShellPermissionIdentity { 334 spm.removeSensorPrivacyListener(listenerSensorDisabled) 335 } 336 } 337 338 // TODO(b/371636626): Re-enable once per-display interactiveness is supported. 339 @RequireNotVisibleBackgroundUsers( 340 reason = "This test relies on turning screen off and on " + 341 "to bring up keyguard. This test has to be skipped on devices with visible background " + 342 "users enabled (primarily Automotive Multi Display) because currently on such devices " + 343 "there is no support for per display interactiveness. PowerManager#IsInteractive will " + 344 "still return true when the driver screen is turned off because passenger screens are " + 345 "on, causing the test to get stuck." 346 ) 347 @Test 348 @AppModeFull(reason = "Instant apps can't manage keyguard") 349 fun testCantChangeWhenLocked() { 350 assumeSensorToggleSupport() 351 assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)) 352 353 // TODO use actual test api when it can be added 354 // assumeTrue(callWithShellPermissionIdentity { spm.requiresAuthentication() }) 355 val packageContext: Context = context.createPackageContext("android", 0) 356 try { 357 assumeTrue( 358 packageContext.resources.getBoolean( 359 packageContext.resources 360 .getIdentifier("config_sensorPrivacyRequiresAuthentication", "bool", "android") 361 ) 362 ) 363 } catch (e: NotFoundException) { 364 // Since by default we want authentication to be required we 365 // continue the test if the OEM has removed this resource. 366 } 367 368 setSensor(false) 369 assertFalse(isSensorPrivacyEnabled()) 370 runWhileLocked { 371 setSensor(true) 372 assertFalse( 373 "State was changed while device is locked", 374 isSensorPrivacyEnabled() 375 ) 376 } 377 378 setSensor(true) 379 assertTrue(isSensorPrivacyEnabled()) 380 runWhileLocked { 381 setSensor(false) 382 assertTrue( 383 "State was changed while device is locked", 384 isSensorPrivacyEnabled() 385 ) 386 } 387 } 388 389 fun unblockSensorWithDialogAndAssert() { 390 val buttonResId = getDialogPositiveButtonId() 391 UiAutomatorUtils.waitFindObject(By.res(buttonResId)).click() 392 eventually { 393 assertFalse(isSensorPrivacyEnabled()) 394 } 395 } 396 397 private fun getDialogPositiveButtonId() = 398 if (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 399 "com.android.systemui:id/bottom_sheet_positive_button" 400 } else { 401 "android:id/button1" 402 } 403 404 @Test 405 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 406 fun testOpNotRunningWhileSensorPrivacyEnabled() { 407 assumeSensorToggleSupport() 408 setSensor(false) 409 val before = System.currentTimeMillis() 410 startTestApp() 411 eventually { 412 assertOpRunning(true) 413 } 414 Thread.sleep(SENSOR_USE_TIME_MS) 415 setSensor(true) 416 eventually { 417 val after = System.currentTimeMillis() 418 assertOpRunning(false) 419 assertLastAccessTimeAndDuration(before, after) 420 } 421 } 422 423 @Test 424 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 425 fun testOpStartsRunningAfterStartedWithSensoryPrivacyEnabled() { 426 assumeSensorToggleSupport() 427 if (sensor == CAMERA) { 428 assumeTrue(supportsCameraMute()) 429 } 430 setSensor(true) 431 // Retry camera connection because external cameras are disconnected 432 // if sensor privacy is enabled (b/182204067) 433 startTestApp(true) 434 UiAutomatorUtils.waitFindObject(By.text( 435 Pattern.compile("Cancel", Pattern.CASE_INSENSITIVE) 436 )).click() 437 assertOpRunning(false) 438 setSensor(false) 439 eventually { 440 assertOpRunning(true) 441 } 442 } 443 444 @Test 445 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 446 fun testOpGetsRecordedAfterStartedWithSensorPrivacyEnabled() { 447 assumeSensorToggleSupport() 448 if (sensor == CAMERA) { 449 assumeTrue(supportsCameraMute()) 450 } 451 setSensor(true) 452 // Retry camera connection because external cameras are disconnected 453 // if sensor privacy is enabled (b/182204067) 454 startTestApp(true) 455 UiAutomatorUtils.waitFindObject(By.text( 456 Pattern.compile("Cancel", Pattern.CASE_INSENSITIVE) 457 )).click() 458 val before = System.currentTimeMillis() 459 setSensor(false) 460 eventually { 461 assertOpRunning(true) 462 } 463 setSensor(true) 464 eventually { 465 val after = System.currentTimeMillis() 466 assertLastAccessTimeAndDuration(before, after) 467 } 468 } 469 470 @Test 471 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 472 fun testOpLastAccessUpdatesAfterToggleSensorPrivacy() { 473 assumeSensorToggleSupport() 474 setSensor(false) 475 val before = System.currentTimeMillis() 476 startTestApp() 477 eventually { 478 assertOpRunning(true) 479 } 480 Thread.sleep(SENSOR_USE_TIME_MS) 481 setSensor(true) 482 eventually { 483 val after = System.currentTimeMillis() 484 assertOpRunning(false) 485 assertLastAccessTimeAndDuration(before, after) 486 } 487 488 val before2 = System.currentTimeMillis() 489 setSensor(false) 490 eventually { 491 assertOpRunning(true) 492 } 493 Thread.sleep(SENSOR_USE_TIME_MS) 494 setSensor(true) 495 eventually { 496 val after = System.currentTimeMillis() 497 assertOpRunning(false) 498 assertLastAccessTimeAndDuration(before2, after) 499 } 500 } 501 502 @Test 503 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 504 fun testOpFinishedWhileToggleOn() { 505 assumeSensorToggleSupport() 506 setSensor(false) 507 startTestApp() 508 eventually { 509 assertOpRunning(true) 510 } 511 setSensor(true) 512 Thread.sleep(5000) 513 eventually { 514 assertOpRunning(false) 515 } 516 finishTestApp() 517 Thread.sleep(1000) 518 setSensor(false) 519 Thread.sleep(1000) 520 assertOpRunning(false) 521 } 522 523 @Test 524 @AsbSecurityTest(cveBugId = [199550934]) 525 fun testTapjacking() { 526 assumeSensorToggleSupport() 527 setSensor(true) 528 startTestOverlayApp(false) 529 assertNotNull( 530 "Dialog never showed", 531 UiAutomatorUtils.waitFindObject(By.res(getDialogPositiveButtonId())) 532 ) 533 val view = UiAutomatorUtils.waitFindObjectOrNull(By.text("This Should Be Hidden"), 10_000) 534 assertNull("Overlay should not have shown.", view) 535 } 536 537 @Test 538 @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") 539 fun testCantEnablePrivacyIfNotSupported() { 540 assumeFalse(spm.supportsSensorToggle(sensor)) 541 assumeFalse(spm.supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)) 542 setSensor(true) 543 assertFalse(isSensorPrivacyEnabled()) 544 } 545 546 protected fun assumeSensorToggleSupport() { 547 assumeTrue(spm.supportsSensorToggle(sensor)) 548 assumeTrue(spm.supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)) 549 } 550 551 private fun startTestApp() { 552 startTestApp(false) 553 } 554 555 private fun startTestApp(retryCameraOnError: Boolean) { 556 val intent = Intent(MIC_CAM_ACTIVITY_ACTION) 557 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 558 .addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) 559 for (extra in extras) { 560 intent.putExtra(extra, true) 561 } 562 intent.putExtra(RETRY_CAM_EXTRA, retryCameraOnError) 563 context.startActivity(intent) 564 // Wait for app to open 565 if (!isWear()) { 566 UiAutomatorUtils.waitFindObject(By.textContains(ACTIVITY_TITLE_SNIP)) 567 } 568 } 569 570 private fun startTestOverlayApp(retryCameraOnError: Boolean) { 571 val intent = Intent(MIC_CAM_OVERLAY_ACTIVITY_ACTION) 572 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 573 .addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) 574 for (extra in extras) { 575 intent.putExtra(extra, true) 576 } 577 intent.putExtra(RETRY_CAM_EXTRA, retryCameraOnError) 578 context.startActivity(intent) 579 // Wait for app to open 580 if (!isWear()) { 581 eventually { 582 UiAutomatorUtils.waitFindObject(By.textContains(ACTIVITY_TITLE_SNIP)) 583 } 584 } 585 586 context.sendBroadcast(Intent(SHOW_OVERLAY_ACTION)) 587 } 588 589 private fun finishTestApp() { 590 // instant apps can't broadcast to other instant apps; use the shell 591 broadcastAndWait(FINISH_MIC_CAM_ACTIVITY_ACTION) 592 } 593 594 private fun broadcastAndWait(action: String) { 595 Log.i(TAG, "Broadcasting action '$action'") 596 runShellCommandOrThrow( 597 "am broadcast" + 598 " --user ${context.userId}" + 599 " -a $action" + 600 " -f ${Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS}" 601 ) 602 SystemUtil.waitForBroadcastDispatch(FINISH_MIC_CAM_ACTIVITY_ACTION) 603 Log.i(TAG, "Finished broadcasting action '$action'") 604 } 605 606 protected fun setSensor(enable: Boolean) { 607 runWithShellPermissionIdentity { 608 spm.setSensorPrivacy(OTHER, sensor, enable) 609 } 610 } 611 612 protected fun setSensor(sensor: Int, enable: Boolean) { 613 runWithShellPermissionIdentity { 614 spm.setSensorPrivacy(sensor, enable) 615 } 616 } 617 618 private fun isSensorPrivacyEnabled(): Boolean { 619 return callWithShellPermissionIdentity { 620 spm.isSensorPrivacyEnabled(sensor) 621 } 622 } 623 624 private fun isToggleSensorPrivacyEnabled(toggleType: Int): Boolean { 625 return callWithShellPermissionIdentity { 626 spm.isSensorPrivacyEnabled(toggleType, sensor) 627 } 628 } 629 630 private fun isCombinedSensorPrivacyEnabled(): Boolean { 631 return callWithShellPermissionIdentity { 632 spm.areAnySensorPrivacyTogglesEnabled(sensor) 633 } 634 } 635 636 private fun supportsCameraMute(): Boolean { 637 val cameraManager = context.getSystemService(CameraManager::class.java)!! 638 val cameraIdList = cameraManager.cameraIdList 639 assumeFalse(cameraIdList.isEmpty()) 640 641 val cameraId = cameraManager.cameraIdList[0] 642 val availableTestPatternModes = cameraManager.getCameraCharacteristics(cameraId) 643 .get(CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES) ?: return false 644 for (mode in availableTestPatternModes) { 645 if ((mode == CameraMetadata.SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) || 646 (mode == CameraMetadata.SENSOR_TEST_PATTERN_MODE_BLACK)) { 647 return true 648 } 649 } 650 return false 651 } 652 653 private fun getOpForSensor(sensor: Int): String? { 654 return when (sensor) { 655 CAMERA -> AppOpsManager.OPSTR_CAMERA 656 MICROPHONE -> AppOpsManager.OPSTR_RECORD_AUDIO 657 else -> null 658 } 659 } 660 661 private fun getOpForPackage(): AppOpsManager.PackageOps { 662 return callWithShellPermissionIdentity { 663 val uid = try { 664 packageManager.getPackageUid(PKG_NAME, 0) 665 } catch (e: PackageManager.NameNotFoundException) { 666 // fail test 667 assertNull(e) 668 -1 669 } 670 val appOpsManager: AppOpsManager = 671 context.getSystemService(AppOpsManager::class.java)!! 672 val pkgOps = appOpsManager.getOpsForPackage(uid, PKG_NAME, op) 673 assertFalse("expected non empty app op list", pkgOps.isEmpty()) 674 pkgOps[0] 675 } 676 } 677 678 private fun assertOpRunning(isRunning: Boolean) { 679 val pkgOp = getOpForPackage() 680 for (op in pkgOp.ops) { 681 for ((_, attrOp) in op.attributedOpEntries) { 682 assertEquals("Unexpected op running state", isRunning, attrOp.isRunning) 683 } 684 } 685 } 686 687 private fun isWear(): Boolean = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) 688 689 private fun assertLastAccessTimeAndDuration(before: Long, after: Long) { 690 val pkgOp = getOpForPackage() 691 for (op in pkgOp.ops) { 692 for ((_, attrOp) in op.attributedOpEntries) { 693 val lastAccess = attrOp.getLastAccessTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED) 694 val lastDuration = attrOp.getLastDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED) 695 assertTrue( 696 "lastAccess was $lastAccess, not between $before and $after", 697 lastAccess in before..after 698 ) 699 assertTrue( 700 "lastAccess had duration $lastDuration, greater than ${after - before}", 701 lastDuration <= (after - before) 702 ) 703 } 704 } 705 } 706 707 fun runWhileLocked(r: () -> Unit) { 708 val km = context.getSystemService(KeyguardManager::class.java)!! 709 val pm = context.getSystemService(PowerManager::class.java)!! 710 val pin = "1234".toByteArray(StandardCharsets.UTF_8) 711 try { 712 runWithShellPermissionIdentity { 713 assumeTrue( 714 "Could not set lock.", 715 km.setLock(KeyguardManager.PIN, pin, KeyguardManager.PIN, null) 716 ) 717 } 718 getEventually { 719 uiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP) 720 assumeFalse("Device never slept.", pm.isInteractive) 721 } 722 getEventually { 723 uiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP) 724 assumeTrue("Device never woke up.", pm.isInteractive) 725 } 726 getEventually { 727 assumeTrue("Device isn't locked", km.isDeviceLocked) 728 } 729 730 r.invoke() 731 } finally { 732 runWithShellPermissionIdentity { 733 assumeTrue( 734 "Could not remove lock.", 735 km.setLock(KeyguardManager.PIN, null, KeyguardManager.PIN, pin) 736 ) 737 } 738 739 // Recycle the screen power in case the keyguard is stuck open 740 getEventually { 741 uiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP) 742 assumeFalse("Device never slept.", pm.isInteractive) 743 } 744 getEventually { 745 uiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP) 746 assumeTrue("Device never woke up.", pm.isInteractive) 747 } 748 749 getEventually { 750 assumeFalse("Device isn't unlocked", km.isDeviceLocked) 751 } 752 } 753 } 754 755 /** 756 * Perform the requested action, then wait both for the action to complete, and for at least 757 * one window transition to occur since the moment the action begins executing. 758 */ 759 private inline fun doAndWaitForWindowTransition( 760 crossinline block: () -> Unit 761 ) { 762 val timeoutOccurred = !uiDevice.performActionAndWait({ 763 block() 764 }, Until.newWindow(), NEW_WINDOW_TIMEOUT_MILLIS) 765 766 if (timeoutOccurred) { 767 throw RuntimeException("Timed out waiting for window transition.") 768 } 769 } 770 } 771