CameraServiceProxy: Log camera usage metrics
- Add new events for camera usage by camera facing
- Dump stored camera events into metrics logs
- Shuffle event ordering for privacy
- Limit history to 100 entries at most
Test: Verify event log collection includes camera events
Bug: 32449509
Change-Id: I2e80b84f9bba3691893dca653ac085fef0b6c98c
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 78d593e..d6ca23f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -138,6 +138,18 @@
REASON_TIMEOUT = 19;
}
+ // Subtypes of camera events for ACTION_CAMERA_EVENT
+ enum CameraEvent {
+ // A back-facing camera was used
+ CAMERA_BACK_USED = 0;
+
+ // A front-facing camera was used
+ CAMERA_FRONT_USED = 1;
+
+ // An external camera was used
+ CAMERA_EXTERNAL_USED = 2;
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -4196,6 +4208,12 @@
// OS: O DR
DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031;
+ // An event from the camera service
+ // CATEGORY: OTHER
+ // SUBTYPE: CameraEvent
+ // OS: O DR
+ ACTION_CAMERA_EVENT = 1032;
+
// ---- End O-DR1 Constants, all O-DR1 constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 4cb7729..3133a51 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
+import android.metrics.LogMaker;
import android.nfc.INfcAdapter;
import android.os.Binder;
import android.os.Handler;
@@ -35,12 +36,15 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -70,6 +74,9 @@
private static final int RETRY_DELAY_TIME = 20; //ms
+ // Maximum entries to keep in usage history before dumping out
+ private static final int MAX_USAGE_HISTORY = 100;
+
private final Context mContext;
private final ServiceThread mHandlerThread;
private final Handler mHandler;
@@ -83,7 +90,7 @@
private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
-
+ private final MetricsLogger mLogger = new MetricsLogger();
private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
private static final String NFC_SERVICE_BINDER_NAME = "nfc";
private static final IBinder nfcInterfaceToken = new Binder();
@@ -260,13 +267,34 @@
*/
void dumpUsageEvents() {
synchronized(mLock) {
+ // Randomize order of events so that it's not meaningful
+ Collections.shuffle(mCameraUsageHistory);
for (CameraUsageEvent e : mCameraUsageHistory) {
if (DEBUG) {
Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
cameraFacingToString(e.mCameraFacing) + " for " +
e.getDuration() + " ms");
}
- // TODO: Add event logging here
+ int subtype = 0;
+ switch(e.mCameraFacing) {
+ case ICameraServiceProxy.CAMERA_FACING_BACK:
+ subtype = MetricsEvent.CAMERA_BACK_USED;
+ break;
+ case ICameraServiceProxy.CAMERA_FACING_FRONT:
+ subtype = MetricsEvent.CAMERA_FRONT_USED;
+ break;
+ case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
+ subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
+ break;
+ default:
+ continue;
+ }
+ LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(subtype)
+ .setLatency(e.getDuration())
+ .setPackageName(e.mClientName);
+ mLogger.write(l);
}
mCameraUsageHistory.clear();
}
@@ -362,6 +390,9 @@
if (doneEvent != null) {
doneEvent.markCompleted();
mCameraUsageHistory.add(doneEvent);
+ if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
+ dumpUsageEvents();
+ }
}
break;
}