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;