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. -->
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c0633cb..4be6408 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -162,6 +162,7 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" />
+ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2a6d55c..64b2ae6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -136,8 +136,8 @@
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
<uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
<uses-permission android:name="android.permission.MANAGE_SENSORS" />
-
<uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+ <uses-permission android:name="android.permission.MANAGE_CAMERA" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 4ffa5f1..f4675fd 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,14 +1316,8 @@
isPrivileged = (appInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
} else {
- if ("media".equals(packageName)) {
- pkgUid = Process.MEDIA_UID;
- isPrivileged = false;
- } else if ("audioserver".equals(packageName)) {
- pkgUid = Process.AUDIOSERVER_UID;
- isPrivileged = false;
- } else if ("cameraserver".equals(packageName)) {
- pkgUid = Process.CAMERASERVER_UID;
+ pkgUid = resolveUid(packageName);
+ if (pkgUid >= 0) {
isPrivileged = false;
}
}
@@ -1957,9 +1951,8 @@
if (nonpackageUid != -1) {
packageName = null;
} else {
- if ("root".equals(packageName)) {
- packageUid = 0;
- } else {
+ packageUid = resolveUid(packageName);
+ if (packageUid < 0) {
packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
}
@@ -2052,6 +2045,10 @@
}
if (ops == null || ops.size() <= 0) {
pw.println("No operations.");
+ if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) {
+ pw.println("Default mode: " + AppOpsManager.modeToString(
+ AppOpsManager.opToDefaultMode(shell.op)));
+ }
return 0;
}
final long now = System.currentTimeMillis();
@@ -2061,24 +2058,7 @@
AppOpsManager.OpEntry ent = entries.get(j);
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
- switch (ent.getMode()) {
- case AppOpsManager.MODE_ALLOWED:
- pw.print("allow");
- break;
- case AppOpsManager.MODE_IGNORED:
- pw.print("ignore");
- break;
- case AppOpsManager.MODE_ERRORED:
- pw.print("deny");
- break;
- case AppOpsManager.MODE_DEFAULT:
- pw.print("default");
- break;
- default:
- pw.print("mode=");
- pw.print(ent.getMode());
- break;
- }
+ pw.print(AppOpsManager.modeToString(ent.getMode()));
if (ent.getTime() != 0) {
pw.print("; time=");
TimeUtils.formatDuration(now - ent.getTime(), pw);
@@ -2563,16 +2543,41 @@
}
private static String resolvePackageName(int uid, String packageName) {
- if (uid == 0) {
+ if (uid == Process.ROOT_UID) {
return "root";
} else if (uid == Process.SHELL_UID) {
return "com.android.shell";
+ } else if (uid == Process.MEDIA_UID) {
+ return "media";
+ } else if (uid == Process.AUDIOSERVER_UID) {
+ return "audioserver";
+ } else if (uid == Process.CAMERASERVER_UID) {
+ return "cameraserver";
} else if (uid == Process.SYSTEM_UID && packageName == null) {
return "android";
}
return packageName;
}
+ private static int resolveUid(String packageName) {
+ if (packageName == null) {
+ return -1;
+ }
+ switch (packageName) {
+ case "root":
+ return Process.ROOT_UID;
+ case "shell":
+ return Process.SHELL_UID;
+ case "media":
+ return Process.MEDIA_UID;
+ case "audioserver":
+ return Process.AUDIOSERVER_UID;
+ case "cameraserver":
+ return Process.CAMERASERVER_UID;
+ }
+ return -1;
+ }
+
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {