Disconnect external cameras if sensor privacy enabled
If the camera device does not support mute test patterns, then it
will be disconnected when the camera sensor privacy is enabled.
Bug: 182204067
Test: Connect an USB camera.
Enable camera privacy toggle.
Apps using the camera should get a blank preview (camera disconnected)
Change-Id: I7298e16190d6416abbe981adcbb1f87637fcaf77
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3deea6b..c1d7b55 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1834,10 +1834,31 @@
}
// Set camera muting behavior
+ bool isCameraPrivacyEnabled =
+ mSensorPrivacyPolicy->isCameraPrivacyEnabled(multiuser_get_user_id(clientUid));
if (client->supportsCameraMute()) {
- bool isCameraPrivacyEnabled =
- mSensorPrivacyPolicy->isCameraPrivacyEnabled(multiuser_get_user_id(clientUid));
- client->setCameraMute(mOverrideCameraMuteMode || isCameraPrivacyEnabled);
+ client->setCameraMute(
+ mOverrideCameraMuteMode || isCameraPrivacyEnabled);
+ } else if (isCameraPrivacyEnabled) {
+ // no camera mute supported, but privacy is on! => disconnect
+ ALOGI("Camera mute not supported for package: %s, camera id: %s",
+ String8(client->getPackageName()).string(), cameraId.string());
+ // Do not hold mServiceLock while disconnecting clients, but
+ // retain the condition blocking other clients from connecting
+ // in mServiceLockWrapper if held.
+ mServiceLock.unlock();
+ // Clear caller identity temporarily so client disconnect PID
+ // checks work correctly
+ int64_t token = CameraThreadState::clearCallingIdentity();
+ // Note AppOp to trigger the "Unblock" dialog
+ client->noteAppOp();
+ client->disconnect();
+ CameraThreadState::restoreCallingIdentity(token);
+ // Reacquire mServiceLock
+ mServiceLock.lock();
+
+ return STATUS_ERROR_FMT(ERROR_DISABLED,
+ "Camera \"%s\" disabled due to camera mute", cameraId.string());
}
if (shimUpdateOnly) {
@@ -3195,6 +3216,27 @@
return OK;
}
+status_t CameraService::BasicClient::noteAppOp() {
+ ATRACE_CALL();
+
+ ALOGV("%s: Start camera noteAppOp, package name = %s, client UID = %d",
+ __FUNCTION__, String8(mClientPackageName).string(), mClientUid);
+
+ // noteAppOp is only used for when camera mute is not supported, in order
+ // to trigger the sensor privacy "Unblock" dialog
+ if (mAppOpsManager != nullptr) {
+ int32_t mode = mAppOpsManager->noteOp(AppOpsManager::OP_CAMERA, mClientUid,
+ mClientPackageName, mClientFeatureId,
+ String16("start camera ") + String16(mCameraIdStr));
+ status_t res = handleAppOpMode(mode);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ return OK;
+}
+
status_t CameraService::BasicClient::finishCameraStreamingOps() {
ATRACE_CALL();
@@ -3287,10 +3329,13 @@
// If the calling Uid is trusted (a native service), or the client Uid is active (WAR for
// b/175320666), the AppOpsManager could return MODE_IGNORED. Do not treat such cases as
// error.
- if (!mUidIsTrusted && isUidActive && isCameraPrivacyEnabled) {
- setCameraMute(true);
- } else if (!mUidIsTrusted && !isUidActive) {
- block();
+ if (!mUidIsTrusted) {
+ if (isUidActive && isCameraPrivacyEnabled && supportsCameraMute()) {
+ setCameraMute(true);
+ } else if (!isUidActive
+ || (isCameraPrivacyEnabled && !supportsCameraMute())) {
+ block();
+ }
}
} else if (res == AppOpsManager::MODE_ALLOWED) {
setCameraMute(sCameraService->mOverrideCameraMuteMode);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1fb7104..aeba7f2 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -371,6 +371,9 @@
virtual status_t finishCameraOps();
// Handle errors for start/checkOps
virtual status_t handleAppOpMode(int32_t mode);
+ // Just notify camera appops to trigger unblocking dialog if sensor
+ // privacy is enabled and camera mute is not supported
+ virtual status_t noteAppOp();
std::unique_ptr<AppOpsManager> mAppOpsManager = nullptr;