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