From 82f09bcf93cc2e0f9a363f40bf8a64bcaa6d8b9f Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Fri, 12 Jan 2018 22:08:40 -0800 Subject: [PATCH] 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 --- core/java/android/app/AppOpsManager.java | 19 ++++++ .../camera2/impl/CameraDeviceImpl.java | 44 ++++++------- core/res/AndroidManifest.xml | 5 ++ data/etc/platform.xml | 1 + packages/Shell/AndroidManifest.xml | 2 +- .../com/android/server/AppOpsService.java | 65 ++++++++++--------- 6 files changed, 83 insertions(+), 53 deletions(-) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 57f9f67b4f9c3..7ca680239eef0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1325,6 +1325,25 @@ public class AppOpsManager { return sOpDefaultMode[op]; } + /** + * 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 f1ffb890eecd1..4455d45d41602 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.ICameraDeviceUser; 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 @@ public class CameraDeviceImpl extends CameraDevice 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 990c574f5f6c6..ba30981e63778 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3711,6 +3711,11 @@ + + + diff --git a/data/etc/platform.xml b/data/etc/platform.xml index c0633cb44e023..4be6408dca16f 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -162,6 +162,7 @@ + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2a6d55c28ec46..64b2ae6e23d32 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -136,8 +136,8 @@ - + = 0) { isPrivileged = false; } } @@ -1957,9 +1951,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 {