1 /* <lambda>null2 * Copyright (C) 2024 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.security.cts 18 19 import android.Manifest 20 import android.app.Activity 21 import android.app.ActivityManager 22 import android.app.AppOpsManager 23 import android.app.Instrumentation 24 import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT 25 import android.content.AttributionSourceState 26 import android.content.BroadcastReceiver 27 import android.content.ComponentName 28 import android.content.Context 29 import android.content.Intent 30 import android.content.IntentFilter 31 import android.hardware.Camera 32 import android.hardware.ICameraClient 33 import android.hardware.ICameraService 34 import android.hardware.SensorPrivacyManager 35 import android.hardware.camera2.CameraCharacteristics 36 import android.hardware.camera2.CameraDevice 37 import android.hardware.camera2.CameraManager 38 import android.hardware.camera2.CameraMetadata 39 import android.hardware.camera2.CameraMetadataInfo 40 import android.hardware.camera2.ICameraDeviceCallbacks 41 import android.hardware.camera2.impl.CameraMetadataNative 42 import android.hardware.camera2.impl.CaptureResultExtras 43 import android.hardware.camera2.impl.PhysicalCaptureResultInfo 44 import android.os.Binder 45 import android.os.IBinder 46 import android.os.Process 47 import android.os.ServiceManager 48 import android.os.ServiceSpecificException 49 import android.platform.test.annotations.RequiresFlagsDisabled 50 import android.platform.test.annotations.RequiresFlagsEnabled 51 import android.platform.test.flag.junit.DeviceFlagsValueProvider 52 import android.provider.Settings 53 import android.security.cts.camera.open.lib.ICameraOpener 54 import android.security.cts.camera.open.lib.IntentKeys 55 import android.util.Log 56 import androidx.test.core.app.ActivityScenario 57 import androidx.test.filters.LargeTest 58 import androidx.test.platform.app.InstrumentationRegistry 59 import androidx.test.runner.AndroidJUnit4 60 import com.android.bedstead.nene.TestApis 61 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity 62 import com.android.cts.install.lib.Install 63 import com.android.cts.install.lib.TestApp 64 import com.android.cts.install.lib.Uninstall 65 import com.android.internal.camera.flags.Flags 66 import java.util.concurrent.CompletableFuture 67 import java.util.concurrent.TimeUnit 68 import java.util.concurrent.TimeoutException 69 import org.junit.After 70 import org.junit.AfterClass 71 import org.junit.Assert.assertEquals 72 import org.junit.Assert.assertNotNull 73 import org.junit.Assert.assertTrue 74 import org.junit.Assume.assumeFalse 75 import org.junit.Assume.assumeTrue 76 import org.junit.Before 77 import org.junit.BeforeClass 78 import org.junit.Rule 79 import org.junit.Test 80 import org.junit.runner.RunWith 81 82 /** Tests that cameraserver checks for permissions correctly. */ 83 @LargeTest 84 @RunWith(AndroidJUnit4::class) 85 class CameraPermissionTest { 86 @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() 87 88 private val onTearDown = mutableListOf<() -> Unit>() 89 90 class DummyCameraDeviceCallbacks : ICameraDeviceCallbacks.Stub() { 91 override fun onDeviceError(errorCode: Int, resultExtras: CaptureResultExtras) { 92 Log.i(TAG, "onDeviceError($errorCode)") 93 } 94 95 override fun onCaptureStarted(resultExtras: CaptureResultExtras, timestamp: Long) { 96 Log.i(TAG, "onCaptureStated($timestamp)") 97 } 98 99 override fun onResultReceived( 100 result: CameraMetadataInfo, 101 resultExtras: CaptureResultExtras, 102 physicalResults: Array<PhysicalCaptureResultInfo> 103 ) { 104 Log.i(TAG, "onResultReceived()") 105 } 106 107 override fun onDeviceIdle() { 108 Log.i(TAG, "onDeviceIdle()") 109 } 110 111 override fun onPrepared(streamId: Int) { 112 Log.i(TAG, "onPrepared()") 113 } 114 115 override fun onRequestQueueEmpty() { 116 Log.i(TAG, "onRequestQueueEmpty()") 117 } 118 119 override fun onRepeatingRequestError(lastFrameNumber: Long, repeatingRequestId: Int) { 120 Log.i(TAG, "onRepeatingRequestError($lastFrameNumber, $repeatingRequestId)") 121 } 122 123 override fun onClientSharedAccessPriorityChanged(primaryClient: Boolean) { 124 Log.i(TAG, "onClientSharedAccessPriorityChanged($primaryClient)") 125 } 126 } 127 128 abstract class DummyBase : Binder(), android.os.IInterface { 129 override fun asBinder(): IBinder { 130 return this 131 } 132 } 133 134 class DummyCameraClient : DummyBase(), ICameraClient 135 136 private lateinit var broadcastReceiver: BroadcastReceiver 137 private lateinit var cameraOpener: ICameraOpener 138 private lateinit var appOpsManager: AppOpsManager 139 private var oldAppOpsSettings: String? = null 140 private var shouldRestoreAppOpsSettings: Boolean = false 141 private val onResumeFuture = CompletableFuture<Intent>() 142 private val streamOpenedFuture = CompletableFuture<Intent>() 143 private var openCameraResultFuture: CompletableFuture<Instrumentation.ActivityResult>? = null 144 private var restoreSensorPrivacy: (() -> Unit)? = null 145 146 private lateinit var cameraManager: CameraManager 147 148 private val context: Context 149 get() = instrumentation.context 150 151 @Before 152 fun setUp() { 153 TestApis.packages() 154 .find(OPEN_CAMERA_APP.packageName) 155 .grantPermission(Manifest.permission.CAMERA) 156 157 TestApis.packages() 158 .find(CAMERA_PROXY_APP.packageName) 159 .grantPermission(Manifest.permission.CAMERA) 160 161 cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager 162 assumeTrue(cameraManager.getCameraIdList().size > 0) 163 164 appOpsManager = context.getSystemService(AppOpsManager::class.java) 165 166 runWithShellPermissionIdentity { 167 oldAppOpsSettings = 168 Settings.Global.getString(context.contentResolver, Settings.Global.APP_OPS_CONSTANTS) 169 Settings.Global.putString( 170 context.contentResolver, 171 Settings.Global.APP_OPS_CONSTANTS, 172 "top_state_settle_time=0,fg_service_state_settle_time=0,bg_state_settle_time=0") 173 shouldRestoreAppOpsSettings = true 174 appOpsManager.clearHistory() 175 appOpsManager.resetHistoryParameters() 176 } 177 178 broadcastReceiver = 179 object : BroadcastReceiver() { 180 override fun onReceive(context: Context, intent: Intent) { 181 Log.i(TAG, "onReceive") 182 when (intent.action) { 183 OPEN_CAMERA_APP.keys.onResume -> { 184 onResumeFuture.complete(intent) 185 } 186 187 OPEN_CAMERA_APP.keys.streamOpened -> { 188 streamOpenedFuture.complete(intent) 189 } 190 } 191 } 192 } 193 194 val filter = 195 IntentFilter().apply { 196 addAction(OPEN_CAMERA_APP.keys.onResume) 197 addAction(OPEN_CAMERA_APP.keys.streamOpened) 198 } 199 context.registerReceiver(broadcastReceiver, filter, Context.RECEIVER_EXPORTED) 200 } 201 202 @After 203 fun tearDown() { 204 finishActivity() 205 206 if (this::broadcastReceiver.isInitialized) { 207 context.unregisterReceiver(broadcastReceiver) 208 } 209 210 if (shouldRestoreAppOpsSettings) { 211 runWithShellPermissionIdentity { 212 // restore old AppOps settings. 213 Settings.Global.putString( 214 context.contentResolver, Settings.Global.APP_OPS_CONSTANTS, oldAppOpsSettings) 215 appOpsManager.clearHistory() 216 appOpsManager.resetHistoryParameters() 217 } 218 } 219 220 for (callback in onTearDown) { 221 callback() 222 } 223 224 restoreSensorPrivacy?.let { it() } 225 } 226 227 @Test 228 @RequiresFlagsDisabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 229 fun testConnectDevice_dataDeliveryPermissionChecks_off() { 230 testConnectDevice(expectDenial = true) 231 } 232 233 @Test 234 @RequiresFlagsEnabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 235 fun testConnectDevice_dataDeliveryPermissionChecks_on() { 236 testConnectDevice(expectDenial = false) 237 } 238 239 @Test 240 @RequiresFlagsDisabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 241 fun testConnect_dataDeliveryPermissionChecks_off() { 242 testConnect(expectDenial = true) 243 } 244 245 @Test 246 @RequiresFlagsEnabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 247 fun testConnect_dataDeliveryPermissionChecks_on() { 248 testConnect(expectDenial = false) 249 } 250 251 @Test fun testAppConnectDevice() = testAppOpenCamera(Api.API_2) 252 253 @Test fun testAppConnect() = testAppOpenCamera(Api.API_1) 254 255 @Test fun testAppNdkConnect() = testAppOpenCamera(Api.NDK) 256 257 private fun testAppOpenCamera(api: Api) { 258 openCameraResultFuture = startOpenCameraActivityForOpenCamera(api) 259 checkAppOpenedCamera(cameraOpenedKey(api)) 260 } 261 262 @Test fun testAppConnectDevice_noPermission() = testAppOpenCamera_noPermission(Api.API_2) 263 264 @Test fun testAppConnect_noPermission() = testAppOpenCamera_noPermission(Api.API_1) 265 266 private fun testAppOpenCamera_noPermission(api: Api) { 267 denyAppPermission(OPEN_CAMERA_APP) 268 openCameraResultFuture = startOpenCameraActivityForOpenCamera(api) 269 checkAppFailedToOpenCamera(cameraOpenedKey(api)) 270 } 271 272 @Test 273 fun testAppNdkConnect_noPermission() { 274 denyAppPermission(OPEN_CAMERA_APP) 275 openCameraResultFuture = startOpenCameraActivityForOpenCamera(Api.NDK) 276 checkAppFailedToOpenCameraNdk() 277 } 278 279 @Test fun testAppStreaming2() = testAppStreaming(Api.API_2) 280 281 @Test fun testAppStreaming1() = testAppStreaming(Api.API_1) 282 283 @Test fun testAppStreamingNdk() = testAppStreaming(Api.NDK) 284 285 private fun testAppStreaming(api: Api, shouldRestoreSensorPrivacy: Boolean = false) { 286 openCameraResultFuture = startOpenCameraActivityForOpenCamera(api, shouldStream = true) 287 assertStreamOpened() 288 if (shouldRestoreSensorPrivacy) { 289 restoreSensorPrivacy!!() 290 } 291 checkAppOpenedCamera(cameraOpenedKey(api), expectStreamOpened = true) 292 } 293 294 @Test 295 fun testAppStreaming2_softDenial_cameraMute() = testAppStreaming_softDenial_cameraMute(Api.API_2) 296 297 @Test 298 fun testAppStreaming1_softDenial_cameraMute() = testAppStreaming_softDenial_cameraMute(Api.API_1) 299 300 @Test 301 fun testAppStreamingNdk_softDenial_cameraMute() = testAppStreaming_softDenial_cameraMute(Api.NDK) 302 303 private fun testAppStreaming_softDenial_cameraMute(api: Api) { 304 assumeTrue(supportsCameraMute()) 305 setSensorPrivacy(enabled = true) 306 testAppStreaming(api, shouldRestoreSensorPrivacy = true) 307 } 308 309 @Test 310 fun testAppStreaming2_opChanged_softDenial_block() = 311 testAppStreaming_opChanged_softDenial_block(Api.API_2) 312 313 @Test 314 fun testAppStreaming1_opChanged_softDenial_block() = 315 testAppStreaming_opChanged_softDenial_block(Api.API_1) 316 317 @Test 318 fun testAppStreamingNdk_opChanged_softDenial_block() = 319 testAppStreaming_opChanged_softDenial_block(Api.NDK) 320 321 private fun testAppStreaming_opChanged_softDenial_block(api: Api) { 322 setSensorPrivacy(enabled = false) 323 openCameraResultFuture = 324 startOpenCameraActivityForOpenCamera(api, shouldStream = true, shouldRepeat = true) 325 326 assertStreamOpened() 327 328 setOpMode(OPEN_CAMERA_APP, AppOpsManager.MODE_IGNORED) 329 330 checkAppOpenedCamera( 331 cameraOpenedKey(api), expectStreamOpened = true, expectError = cameraOpChangedError(api)) 332 } 333 334 @Test 335 fun testAppStreaming2_opChanged_softDenial_cameraMute() = 336 testAppStreaming_opChanged_softDenial_cameraMute(Api.API_2) 337 338 @Test 339 fun testAppStreaming1_opChanged_softDenial_cameraMute() = 340 testAppStreaming_opChanged_softDenial_cameraMute(Api.API_1) 341 342 @Test 343 fun testAppStreamingNdk_opChanged_softDenial_cameraMute() = 344 testAppStreaming_opChanged_softDenial_cameraMute(Api.NDK) 345 346 private fun testAppStreaming_opChanged_softDenial_cameraMute(api: Api) { 347 assumeTrue(supportsCameraMute()) 348 openCameraResultFuture = 349 startOpenCameraActivityForOpenCamera(api, shouldStream = true, shouldRepeat = true) 350 351 assertStreamOpened() 352 353 val am = context.getSystemService(ActivityManager::class.java) 354 val importance = am.getPackageImportance(OPEN_CAMERA_APP.packageName) 355 Log.v(TAG, "OpenCameraApp importance: ${importance}") 356 357 setSensorPrivacy(enabled = true) 358 359 // Wait for any potential block() 360 Thread.sleep(TIMEOUT_MILLIS) 361 362 sendStopRepeating(OPEN_CAMERA_APP) 363 checkAppOpenedCamera( 364 cameraOpenedKey(api), expectStreamOpened = true, expectStoppedRepeating = true) 365 } 366 367 @Test fun testProxyConnectDevice() = testProxyOpenCamera(Api.API_2) 368 369 @Test fun testProxyConnect() = testProxyOpenCamera(Api.API_1) 370 371 @Test fun testProxyNdkConnect() = testProxyOpenCamera(Api.NDK) 372 373 private fun testProxyOpenCamera(api: Api) { 374 openCameraByProxy(openCameraByProxyKey(api)) 375 checkAppOpenedCameraByProxy(cameraOpenedKey(api)) 376 } 377 378 @Test 379 fun testProxyConnectDevice_noOpenCameraPermission() = 380 testProxyOpenCamera_noPermission(Api.API_2, OPEN_CAMERA_APP) 381 382 @Test 383 @RequiresFlagsEnabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 384 fun testProxyConnectDevice_noCameraProxyPermission() = 385 testProxyOpenCamera_noPermission(Api.API_2, CAMERA_PROXY_APP) 386 387 private fun testProxyOpenCamera_noPermission(api: Api, deniedApp: TestApp) { 388 denyAppPermission(deniedApp) 389 openCameraByProxy(openCameraByProxyKey(api)) 390 checkAppFailedToOpenCameraByProxy(cameraOpenedKey(api)) 391 } 392 393 @Test 394 @RequiresFlagsDisabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 395 fun testProxyConnectDevice_noCameraProxyPermission_dataDeliveryPermissionChecks_off() { 396 denyAppPermission(CAMERA_PROXY_APP) 397 openCameraByProxy(OPEN_CAMERA_APP.keys.openCamera2ByProxy) 398 checkAppOpenedCameraByProxy(OPEN_CAMERA_APP.keys.cameraOpened2) 399 } 400 401 @Test fun testProxyStreaming2() = testProxyStreaming(Api.API_2) 402 403 @Test fun testProxyStreaming1() = testProxyStreaming(Api.API_1) 404 405 @Test fun testProxyStreamingNdk() = testProxyStreaming(Api.NDK) 406 407 private fun testProxyStreaming(api: Api) { 408 openCameraByProxy(openCameraByProxyKey(api), shouldStream = true) 409 assertStreamOpened() 410 checkAppOpenedCameraByProxy(cameraOpenedKey(api), expectStreamOpened = true) 411 } 412 413 @Test 414 fun testProxyStreaming2_opChanged_softDenial_cameraMute() = 415 testProxyStreaming_opChanged_softDenial_cameraMute(Api.API_2) 416 417 @Test 418 fun testProxyStreaming1_opChanged_softDenial_cameraMute() = 419 testProxyStreaming_opChanged_softDenial_cameraMute(Api.API_1) 420 421 @Test 422 fun testProxyStreamingNdk_opChanged_softDenial_cameraMute() = 423 testProxyStreaming_opChanged_softDenial_cameraMute(Api.NDK) 424 425 private fun testProxyStreaming_opChanged_softDenial_cameraMute(api: Api) { 426 assumeTrue(supportsCameraMute()) 427 openCameraByProxy(openCameraByProxyKey(api), shouldStream = true, shouldRepeat = true) 428 429 val pid = 430 onResumeFuture 431 .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 432 .getIntExtra(OPEN_CAMERA_APP.keys.pid, -1) 433 434 assertStreamOpened() 435 436 val am = context.getSystemService(ActivityManager::class.java) 437 438 // Wait until OpenCameraApp is no longer visible according to ActivityManager 439 for (i in 0..7) { // 7s 440 val importance = am.getPackageImportance(OPEN_CAMERA_APP.packageName) 441 Log.v(TAG, "OpenCameraApp importance: ${importance}") 442 if (importance > ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 443 break 444 } 445 Thread.sleep(1000) 446 } 447 448 setSensorPrivacy(enabled = true) 449 450 // Wait for any potential block() 451 Thread.sleep(TIMEOUT_MILLIS) 452 453 sendStopRepeating(CAMERA_PROXY_APP) 454 checkAppOpenedCameraByProxy( 455 cameraOpenedKey(api), 456 expectStreamOpened = true, 457 expectStoppedRepeating = true, 458 expectError = 0) 459 } 460 461 @Test 462 @RequiresFlagsEnabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 463 fun testProxyStreaming2_noOpenCameraPermission_opChanged_softDenial_block() { 464 testProxyStreaming2_opChanged_softDenial_block(OPEN_CAMERA_APP) 465 } 466 467 @Test 468 @RequiresFlagsEnabled(Flags.FLAG_DATA_DELIVERY_PERMISSION_CHECKS) 469 fun testProxyStreaming2_noCameraProxyPermission_opChanged_softDenial_block() { 470 testProxyStreaming2_opChanged_softDenial_block(CAMERA_PROXY_APP) 471 } 472 473 @Test 474 fun testSpoofedAttributionSourceConnectDevice() { 475 val clientAttribution = startActivityForSpoofing() 476 testConnectDeviceWithAttribution(clientAttribution, ICameraService.ERROR_PERMISSION_DENIED) 477 } 478 479 @Test 480 fun testSpoofedAttributionSourceConnect() { 481 val clientAttribution = startActivityForSpoofing() 482 testConnectWithAttribution(clientAttribution, ICameraService.ERROR_PERMISSION_DENIED) 483 } 484 485 private fun openCameraByProxy( 486 openCameraKey: String, 487 shouldStream: Boolean = false, 488 shouldRepeat: Boolean = false 489 ): Int { 490 openCameraResultFuture = startOpenCameraActivity() 491 val pid = 492 onResumeFuture 493 .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 494 .getIntExtra(OPEN_CAMERA_APP.keys.pid, -1) 495 context.sendBroadcast( 496 Intent(openCameraKey).apply { 497 putExtra(OPEN_CAMERA_APP.keys.shouldStream, shouldStream) 498 putExtra(OPEN_CAMERA_APP.keys.shouldRepeat, shouldRepeat) 499 setPackage(OPEN_CAMERA_APP.packageName) 500 }) 501 return pid 502 } 503 504 private fun requireActivityResultData(timeout: Long = TIMEOUT_MILLIS) = 505 openCameraResultFuture!! 506 .get(timeout, TimeUnit.MILLISECONDS) 507 .apply { assertEquals(Activity.RESULT_OK, resultCode) } 508 .resultData!! 509 510 private fun checkAppOpenedCamera( 511 openCameraKey: String, 512 expectStreamOpened: Boolean = false, 513 expectStoppedRepeating: Boolean = false, 514 expectError: Int = 0, 515 ) { 516 requireActivityResultData().let { 517 maybePrintAttributionSource(it) 518 Log.v(TAG, "checkAppOpenedCamera Intent:") 519 Log.v(TAG, "${it.getExtras().toString()}") 520 OPEN_CAMERA_APP.keys.apply { 521 assumeFalse(it.getBooleanExtra(noCamera, false)) 522 assertEquals(true, it.getBooleanExtra(openCameraKey, false)) 523 assertEquals(null, it.getStringExtra(exception)) 524 assertEquals(expectStreamOpened, it.getBooleanExtra(streamOpened, false)) 525 assertEquals(expectError, it.getIntExtra(error, 0)) 526 assertEquals(expectStoppedRepeating, it.getBooleanExtra(stoppedRepeating, false)) 527 } 528 } 529 } 530 531 private fun checkAppOpenedCameraByProxy( 532 openCameraKey: String, 533 expectStreamOpened: Boolean = false, 534 expectStoppedRepeating: Boolean = false, 535 expectError: Int = 0 536 ) { 537 requireActivityResultData().let { 538 maybePrintAttributionSource(it) 539 Log.v(TAG, "checkAppOpenedCameraByProxy Intent:") 540 Log.v(TAG, "${it.getExtras().toString()}") 541 542 assertEquals(null, it.getStringExtra(CAMERA_PROXY_APP.keys.exception)) 543 544 OPEN_CAMERA_APP.keys.apply { 545 assumeFalse(it.getBooleanExtra(noCamera, false)) 546 assertEquals(null, it.getStringExtra(exception)) 547 assertEquals(expectStreamOpened, it.getBooleanExtra(streamOpened, false)) 548 assertEquals(expectError, it.getIntExtra(error, 0)) 549 assertEquals(expectStoppedRepeating, it.getBooleanExtra(stoppedRepeating, false)) 550 } 551 552 assertEquals(true, it.getBooleanExtra(openCameraKey, false)) 553 } 554 } 555 556 private fun checkAppFailedToOpenCamera(openCameraKey: String) { 557 requireActivityResultData().let { 558 maybePrintAttributionSource(it) 559 assumeFalse(it.getBooleanExtra(OPEN_CAMERA_APP.keys.noCamera, false)) 560 assertNotNull(it.getStringExtra(OPEN_CAMERA_APP.keys.exception)) 561 assertEquals(false, it.getBooleanExtra(openCameraKey, false)) 562 } 563 } 564 565 private fun checkAppFailedToOpenCameraNdk() { 566 requireActivityResultData().let { 567 maybePrintAttributionSource(it) 568 assumeFalse(it.getBooleanExtra(OPEN_CAMERA_APP.keys.noCamera, false)) 569 570 // Check for ACAMERA_ERROR_PERMISSION_DENIED 571 assertEquals(-10013, it.getIntExtra(OPEN_CAMERA_APP.keys.error, 0)) 572 assertEquals(false, it.getBooleanExtra(OPEN_CAMERA_APP.keys.cameraOpenedNdk, false)) 573 } 574 } 575 576 private fun checkAppFailedToOpenCameraByProxy(openCameraKey: String) { 577 requireActivityResultData().let { 578 maybePrintAttributionSource(it) 579 Log.v(TAG, "testProxyConnectDevice_noOpenCameraPermission Intent:") 580 Log.v(TAG, "${it.getExtras().toString()}") 581 assumeFalse(it.getBooleanExtra(OPEN_CAMERA_APP.keys.noCamera, false)) 582 assertNotNull(it.getStringExtra(OPEN_CAMERA_APP.keys.exception)) 583 assertEquals(null, it.getStringExtra(CAMERA_PROXY_APP.keys.exception)) 584 assertEquals(false, it.getBooleanExtra(openCameraKey, false)) 585 } 586 } 587 588 private fun testConnectDevice(expectDenial: Boolean) { 589 val clientAttribution = context.getAttributionSource().asState() 590 val expectedError = 591 if (expectDenial) { 592 ICameraService.ERROR_PERMISSION_DENIED 593 } else { 594 0 595 } 596 testConnectDeviceWithAttribution(clientAttribution, expectedError) 597 } 598 599 private fun testConnect(expectDenial: Boolean) { 600 val clientAttribution = context.getAttributionSource().asState() 601 val expectedError = 602 if (expectDenial) { 603 ICameraService.ERROR_PERMISSION_DENIED 604 } else { 605 0 606 } 607 testConnectWithAttribution(clientAttribution, expectedError) 608 } 609 610 private fun testConnectDeviceWithAttribution( 611 clientAttribution: AttributionSourceState, 612 expectedError: Int, 613 ) { 614 var errorCode = 0 615 try { 616 TestApis.permissions().withPermission(Manifest.permission.CAMERA).use { 617 connectDevice(clientAttribution) 618 } 619 } catch (e: ServiceSpecificException) { 620 Log.i(TAG, "Received error ${e.errorCode}") 621 errorCode = e.errorCode 622 } 623 624 assertEquals(expectedError, errorCode) 625 } 626 627 private fun connectDevice(clientAttribution: AttributionSourceState) { 628 getCameraService() 629 .connectDevice( 630 DummyCameraDeviceCallbacks(), 631 cameraManager.getCameraIdList()[0], 632 0 /*oomScoreOffset*/, 633 context.applicationInfo.targetSdkVersion, 634 ICameraService.ROTATION_OVERRIDE_NONE, 635 clientAttribution, 636 DEVICE_POLICY_DEFAULT, 637 false) 638 .disconnect() 639 } 640 641 private fun testConnectWithAttribution( 642 clientAttribution: AttributionSourceState, 643 expectedError: Int, 644 ) { 645 var errorCode = 0 646 try { 647 TestApis.permissions().withPermission(Manifest.permission.CAMERA).use { 648 connect(clientAttribution) 649 } 650 } catch (e: ServiceSpecificException) { 651 Log.i(TAG, "Received error ${e.errorCode}") 652 errorCode = e.errorCode 653 } 654 655 assertEquals(expectedError, errorCode) 656 } 657 658 private fun connect(clientAttribution: AttributionSourceState) { 659 getCameraService() 660 .connect( 661 DummyCameraClient(), 662 /* cameraId= */ 0, 663 context.applicationInfo.targetSdkVersion, 664 ICameraService.ROTATION_OVERRIDE_NONE, 665 /* forceSlowJpegMode= */ false, 666 clientAttribution, 667 DEVICE_POLICY_DEFAULT) 668 .disconnect() 669 } 670 671 private fun startActivityForSpoofing(): AttributionSourceState { 672 openCameraResultFuture = startOpenCameraActivity() 673 674 val pid = 675 onResumeFuture 676 .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 677 .getIntExtra(OPEN_CAMERA_APP.keys.pid, Process.INVALID_PID) 678 val uid = context.packageManager.getApplicationInfo(OPEN_CAMERA_APP.packageName, 0).uid 679 680 val context = context 681 val contextAttribution = context.getAttributionSource().asState() 682 val clientAttribution = AttributionSourceState() 683 clientAttribution.uid = uid 684 clientAttribution.pid = pid 685 clientAttribution.deviceId = contextAttribution.deviceId 686 clientAttribution.packageName = OPEN_CAMERA_APP.packageName 687 clientAttribution.next = arrayOf<AttributionSourceState>() 688 689 Log.i( 690 TAG, 691 "Spoofing client uid = $uid, pid = $pid : myUid = ${Process.myUid()}, myPid = ${Process.myPid()}") 692 693 return clientAttribution 694 } 695 696 private fun getCameraService(): ICameraService { 697 val cameraServiceBinder = ServiceManager.getService("media.camera") 698 assertNotNull("Camera service IBinder should not be null", cameraServiceBinder) 699 700 val cameraService = ICameraService.Stub.asInterface(cameraServiceBinder) 701 assertNotNull("Camera service should not be null", cameraService) 702 703 return cameraService 704 } 705 706 private fun finishActivity() { 707 openCameraResultFuture?.let { 708 val finishIntent = Intent(OPEN_CAMERA_APP.keys.finish) 709 finishIntent.setPackage(OPEN_CAMERA_APP.packageName) 710 context.sendBroadcast(finishIntent) 711 } 712 } 713 714 private fun startOpenCameraActivity( 715 openCamera1: Boolean = false, 716 openCamera2: Boolean = false, 717 openCameraNdk: Boolean = false, 718 shouldStream: Boolean = false, 719 shouldRepeat: Boolean = false, 720 ): CompletableFuture<Instrumentation.ActivityResult> = 721 CompletableFuture<Instrumentation.ActivityResult>().also { 722 ActivityScenario.launch(StartForFutureActivity::class.java).onActivity { 723 startForFutureActivity -> 724 startForFutureActivity.startActivityForFuture( 725 Intent().apply { 726 component = ComponentName(OPEN_CAMERA_APP.packageName, OPEN_CAMERA_ACTIVITY) 727 putExtra(OPEN_CAMERA_APP.keys.shouldOpenCamera1, openCamera1) 728 putExtra(OPEN_CAMERA_APP.keys.shouldOpenCamera2, openCamera2) 729 putExtra(OPEN_CAMERA_APP.keys.shouldOpenCameraNdk, openCameraNdk) 730 putExtra(OPEN_CAMERA_APP.keys.shouldStream, shouldStream) 731 putExtra(OPEN_CAMERA_APP.keys.shouldRepeat, shouldRepeat) 732 }, 733 it) 734 } 735 } 736 737 private fun startOpenCameraActivityForOpenCamera( 738 api: Api, 739 shouldStream: Boolean = false, 740 shouldRepeat: Boolean = false 741 ) = 742 startOpenCameraActivity( 743 openCamera1 = (api == Api.API_1), 744 openCamera2 = (api == Api.API_2), 745 openCameraNdk = (api == Api.NDK), 746 shouldStream = shouldStream, 747 shouldRepeat = shouldRepeat) 748 749 private fun maybePrintAttributionSource(intent: Intent) { 750 intent.getStringExtra(OPEN_CAMERA_APP.keys.attributionSource)?.let { Log.i(TAG, it) } 751 } 752 753 private fun denyAppPermission(app: TestApp) { 754 TestApis.packages().find(app.packageName).denyPermission(Manifest.permission.CAMERA) 755 } 756 757 private fun setSensorPrivacy(enabled: Boolean) { 758 val spm = context.getSystemService(SensorPrivacyManager::class.java)!! 759 val supportsToggle = supportsSoftwarePrivacyToggle(spm) 760 if (enabled) { 761 assumeTrue(supportsToggle) 762 } else if (!supportsToggle) { 763 return // No need to do anything if enabled = false and the software toggle is not supported 764 } 765 766 TestApis.permissions() 767 .withPermission( 768 Manifest.permission.OBSERVE_SENSOR_PRIVACY, Manifest.permission.MANAGE_SENSOR_PRIVACY) 769 .use { 770 val newState = 771 if (enabled) SensorPrivacyManager.StateTypes.ENABLED 772 else SensorPrivacyManager.StateTypes.DISABLED 773 val stateStr = if (enabled) "Enabled" else "Disabled" 774 val oldState = 775 spm.getSensorPrivacyState( 776 SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, TOGGLE_SENSOR_CAMERA) 777 778 spm.setSensorPrivacyState(TOGGLE_SENSOR_CAMERA, newState) 779 Log.v(TAG, "${stateStr} sensor privacy") 780 restoreSensorPrivacy = { 781 TestApis.permissions() 782 .withPermission( 783 Manifest.permission.OBSERVE_SENSOR_PRIVACY, 784 Manifest.permission.MANAGE_SENSOR_PRIVACY) 785 .use { spm.setSensorPrivacyState(TOGGLE_SENSOR_CAMERA, oldState) } 786 restoreSensorPrivacy = null 787 } 788 } 789 } 790 791 private fun supportsSoftwarePrivacyToggle(spm: SensorPrivacyManager): Boolean = 792 spm.supportsSensorToggle(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, TOGGLE_SENSOR_CAMERA) 793 794 private fun supportsCameraMute(): Boolean { 795 val cameraIdList = cameraManager.cameraIdList 796 assumeFalse(cameraIdList.isEmpty()) 797 798 val cameraId = cameraManager.cameraIdList[0] 799 val availableTestPatternModes = 800 cameraManager 801 .getCameraCharacteristics(cameraId) 802 .get(CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES) ?: return false 803 for (mode in availableTestPatternModes) { 804 if ((mode == CameraMetadata.SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) || 805 (mode == CameraMetadata.SENSOR_TEST_PATTERN_MODE_BLACK)) { 806 return true 807 } 808 } 809 return false 810 } 811 812 private fun setOpMode(app: TestApp, @AppOpsManager.Mode mode: Int) { 813 runWithShellPermissionIdentity { 814 val uid = context.packageManager.getApplicationInfo(app.packageName, 0).uid 815 816 val oldMode = 817 appOpsManager.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_CAMERA, uid, app.packageName) 818 819 appOpsManager.setUidMode(AppOpsManager.OP_CAMERA, uid, mode) 820 821 onTearDown.add({ 822 runWithShellPermissionIdentity { 823 appOpsManager.setUidMode(AppOpsManager.OP_CAMERA, uid, oldMode) 824 } 825 }) 826 827 val currentMode = 828 appOpsManager.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_CAMERA, uid, app.packageName) 829 assertEquals(mode, currentMode) 830 } 831 } 832 833 private fun assertStreamOpened() { 834 val streamOpened = 835 try { 836 streamOpenedFuture 837 .get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 838 .getBooleanExtra(OPEN_CAMERA_APP.keys.streamOpened, false) 839 } catch (e: TimeoutException) { 840 Log.e(TAG, "assertStreamOpened: TimeoutException") 841 false 842 } 843 844 if (!streamOpened) { 845 // Assert the error / exception first, to make the cause more clear 846 requireActivityResultData(timeout = 1000L).let { 847 assertEquals(0, it.getIntExtra(OPEN_CAMERA_APP.keys.error, 0)) 848 assertEquals(null, it.getStringExtra(OPEN_CAMERA_APP.keys.exception)) 849 } 850 } 851 assertTrue(streamOpened) 852 } 853 854 private fun testProxyStreaming2_opChanged_softDenial_block(deniedApp: TestApp) { 855 setSensorPrivacy(enabled = false) 856 openCameraByProxy( 857 OPEN_CAMERA_APP.keys.openCamera2ByProxy, shouldStream = true, shouldRepeat = true) 858 859 assertStreamOpened() 860 861 setOpMode(deniedApp, AppOpsManager.MODE_IGNORED) 862 863 checkAppOpenedCameraByProxy( 864 OPEN_CAMERA_APP.keys.cameraOpened2, 865 expectStreamOpened = true, 866 expectError = CameraDevice.StateCallback.ERROR_CAMERA_DISABLED) 867 } 868 869 private fun sendStopRepeating(app: TestApp) { 870 val stopRepeatingIntent = Intent(app.keys.stopRepeating) 871 stopRepeatingIntent.setPackage(app.packageName) 872 context.sendBroadcast(stopRepeatingIntent) 873 } 874 875 private companion object { 876 val TAG = CameraPermissionTest::class.java.simpleName 877 val OPEN_CAMERA_APP = 878 TestApp("OpenCameraApp", "android.security.cts.camera.open", 30, false, "OpenCameraApp.apk") 879 val CAMERA_PROXY_APP = 880 TestApp( 881 "CameraProxyApp", "android.security.cts.camera.proxy", 30, false, "CameraProxyApp.apk") 882 val OPEN_CAMERA_APP_KEYS = IntentKeys(OPEN_CAMERA_APP.packageName) 883 val CAMERA_PROXY_APP_KEYS = IntentKeys(CAMERA_PROXY_APP.packageName) 884 val APP_TO_KEYS = 885 mapOf(OPEN_CAMERA_APP to OPEN_CAMERA_APP_KEYS, CAMERA_PROXY_APP to CAMERA_PROXY_APP_KEYS) 886 val OPEN_CAMERA_ACTIVITY = "${OPEN_CAMERA_APP.packageName}.OpenCameraActivity" 887 val CAMERA_PROXY_ACTIVITY = "${CAMERA_PROXY_APP.packageName}.CameraProxyActivity" 888 const val TIMEOUT_MILLIS: Long = 10000 889 const val TOGGLE_SENSOR_CAMERA = SensorPrivacyManager.Sensors.CAMERA 890 891 enum class Api { 892 API_1, 893 API_2, 894 NDK, 895 } 896 897 val TestApp.keys: IntentKeys 898 get() = APP_TO_KEYS.getValue(this) 899 900 val instrumentation = InstrumentationRegistry.getInstrumentation() 901 902 fun cameraOpenedKey(api: Api) = 903 when (api) { 904 Api.API_1 -> OPEN_CAMERA_APP.keys.cameraOpened1 905 Api.API_2 -> OPEN_CAMERA_APP.keys.cameraOpened2 906 Api.NDK -> OPEN_CAMERA_APP.keys.cameraOpenedNdk 907 } 908 909 fun cameraBlockedError(api: Api) = 910 when (api) { 911 Api.API_1 -> Camera.CAMERA_ERROR_UNKNOWN 912 Api.API_2 -> CameraDevice.StateCallback.ERROR_CAMERA_DISABLED 913 Api.NDK -> -10013 // ACAMERA_ERROR_PERMISSION_DENIED 914 } 915 916 fun cameraOpChangedError(api: Api) = 917 when (api) { 918 Api.NDK -> CameraDevice.StateCallback.ERROR_CAMERA_DEVICE 919 else -> cameraBlockedError(api) 920 } 921 922 fun openCameraByProxyKey(api: Api) = 923 when (api) { 924 Api.API_1 -> OPEN_CAMERA_APP.keys.openCamera1ByProxy 925 Api.API_2 -> OPEN_CAMERA_APP.keys.openCamera2ByProxy 926 Api.NDK -> OPEN_CAMERA_APP.keys.openCameraNdkByProxy 927 } 928 929 @BeforeClass 930 @JvmStatic 931 fun beforeClass() { 932 TestApis.permissions().withPermission(Manifest.permission.DELETE_PACKAGES).use { 933 Uninstall.packages(OPEN_CAMERA_APP.packageName, CAMERA_PROXY_APP.packageName) 934 } 935 936 TestApis.permissions().withPermission(Manifest.permission.INSTALL_PACKAGES).use { 937 Install.multi(OPEN_CAMERA_APP, CAMERA_PROXY_APP).commit() 938 } 939 } 940 941 @AfterClass 942 @JvmStatic 943 fun afterClass() { 944 TestApis.permissions().withPermission(Manifest.permission.DELETE_PACKAGES).use { 945 Uninstall.packages(OPEN_CAMERA_APP.packageName, CAMERA_PROXY_APP.packageName) 946 } 947 } 948 } 949 } 950