No camera for idle uids - framework

If a UID is idle (being in the background for more than
cartain amount of time) it should not be able to use the
camera. If the UID becomes idle we generate an eror and
close the cameras for this UID. If an app in an idle UID
tries to use the camera we immediately generate an error.
Since apps already should handle these errors it is safe
to apply this policy to all apps to protect user privacy.

Test: Pass - cts-tradefed run cts -m CtsCameraTestCases
      Added - CameraTest#testCameraAccessForIdleUid

Change-Id: If6ad1662f2af6592b6aca1aeee4bd481389b5e00
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 57f9f67..7ca6802 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1326,6 +1326,25 @@
     }
 
     /**
+     * Retrieve the human readable mode.
+     * @hide
+     */
+    public static String modeToString(int mode) {
+        switch (mode) {
+            case MODE_ALLOWED:
+                return "allow";
+            case MODE_IGNORED:
+                return "ignore";
+            case MODE_ERRORED:
+                return "deny";
+            case MODE_DEFAULT:
+                return "default";
+            default:
+                return "mode=" + mode;
+        }
+    }
+
+    /**
      * Retrieve whether the op allows itself to be reset.
      * @hide
      */
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index f1ffb89..4455d45 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -16,9 +16,8 @@
 
 package android.hardware.camera2.impl;
 
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
-import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
@@ -31,7 +30,6 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.ReprocessFormatsMap;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
@@ -1798,34 +1796,36 @@
                     case ERROR_CAMERA_DISCONNECTED:
                         CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
                         break;
-                    default:
-                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
-                        // no break
-                    case ERROR_CAMERA_DEVICE:
-                    case ERROR_CAMERA_SERVICE:
-                        mInError = true;
-                        final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
-                                StateCallback.ERROR_CAMERA_DEVICE :
-                                StateCallback.ERROR_CAMERA_SERVICE;
-                        Runnable r = new Runnable() {
-                            @Override
-                            public void run() {
-                                if (!CameraDeviceImpl.this.isClosed()) {
-                                    mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
-                                }
-                            }
-                        };
-                        CameraDeviceImpl.this.mDeviceHandler.post(r);
-                        break;
                     case ERROR_CAMERA_REQUEST:
                     case ERROR_CAMERA_RESULT:
                     case ERROR_CAMERA_BUFFER:
                         onCaptureErrorLocked(errorCode, resultExtras);
                         break;
+                    case ERROR_CAMERA_DEVICE:
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
+                        break;
+                    case ERROR_CAMERA_DISABLED:
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
+                        break;
+                    default:
+                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
                 }
             }
         }
 
+        private void scheduleNotifyError(int code) {
+            mInError = true;
+            CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
+                    CameraDeviceCallbacks::notifyError, this, code));
+        }
+
+        private void notifyError(int code) {
+            if (!CameraDeviceImpl.this.isClosed()) {
+                mDeviceCallback.onError(CameraDeviceImpl.this, code);
+            }
+        }
+
         @Override
         public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
             if (DEBUG) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 990c574..ba30981 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3711,6 +3711,11 @@
     <permission android:name="android.permission.MODIFY_QUIET_MODE"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows internal management of the camera framework
+         @hide -->
+    <permission android:name="android.permission.MANAGE_CAMERA"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to control remote animations. See
          {@link ActivityOptions#makeRemoteAnimation}
          @hide <p>Not for use by third-party applications. -->