Camera: Enhance camera atoms for system health
Introduce camera system health atoms, including:
- Camera open, close, and session creation time
- Camera session and stream combination information,
- Stream statistics:
- stream width, height, format, dataspace, usage flag,
- max buffer count
- buffer loss
- first frame latency.
Test: ./out/host/linux-x86/bin/statsd_testdrive 227
Test: CTS PerformanceTest
Bug: 154159000
Change-Id: I858d64f3324bb9652a59e5857f2730c92c616a8e
diff --git a/Android.bp b/Android.bp
index 9b8e018..d29bcdf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -527,6 +527,7 @@
"android.hardware.vibrator-V1.3-java",
"android.system.keystore2-java",
"android.system.suspend.control.internal-java",
+ "cameraprotosnano",
"devicepolicyprotosnano",
"com.android.sysprop.apex",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0f60eae..2a1cdab 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -42,6 +42,7 @@
import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
+import "frameworks/base/core/proto/android/stats/camera/camera.proto";
import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto";
import "frameworks/base/core/proto/android/stats/connectivity/tethering.proto";
import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto";
@@ -9946,10 +9947,12 @@
* frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
*/
message CameraActionEvent {
- // Camera session duration
+ // Camera session duration in milliseconds if action is SESSION.
+ // 0 if action is OPEN or CLOSE.
optional int64 duration_millis = 1;
- // Camera API level used
+ // Camera API level used.
+ // 1 for camera1 API, and 2 for camera2 API.
optional int32 api_level = 2;
// Name of client package
@@ -9963,6 +9966,56 @@
EXTERNAL = 3;
}
optional Facing facing = 4;
+
+ // Camera ID
+ optional string camera_id = 5;
+
+ // Camera action type
+ enum Action {
+ UNKNOWN_ACTION = 0;
+ OPEN = 1;
+ CLOSE = 2;
+ SESSION = 3;
+ }
+ optional Action action = 6;
+
+ // Whether the client is accessing camera using ndk
+ optional bool is_ndk = 7;
+
+ // Action OPEN: Open latency
+ // Action CLOSE: Close latency
+ // Action SESSION: Camera session creation duration.
+ // If this entry is reusing an existing session, the value is -1.
+ optional int32 latency_millis = 8;
+
+ // session type: 0 for normal mode, 1 for constrained high speed mode
+ optional int32 operating_mode = 9;
+
+ // If actioh is SESSION: number of internal reconfigurations
+ // Else: 0
+ optional int32 internal_reconfig = 10;
+
+ // Number of requests for this capture session. Only applicable to SESSION
+ // action.
+ optional int64 request_count = 11;
+ // Number of result errors. Only applicable to SESSION action.
+ optional int64 result_error_count = 12;
+ // Whether the device runs into error state.
+ optional bool device_error = 13;
+
+ // If action is SESSION: Stream states
+ // Else: stream_count = 0
+ optional int32 stream_count = 14;
+ optional android.stats.camera.CameraStreamProto stream_1 = 15
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ optional android.stats.camera.CameraStreamProto stream_2 = 16
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ optional android.stats.camera.CameraStreamProto stream_3 = 17
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ optional android.stats.camera.CameraStreamProto stream_4 = 18
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ optional android.stats.camera.CameraStreamProto stream_5 = 19
+ [(android.os.statsd.log_mode) = MODE_BYTES];
}
/**
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 822b40b..df5ca11 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6750,6 +6750,7 @@
android.sysprop.-$$Lambda$TelephonyProperties$klELuV5zVSqFveC5l6c3FSJmLAU
android.sysprop.-$$Lambda$TelephonyProperties$pFU8zg4eHAdooeRLJg1WBG52cKk
android.sysprop.-$$Lambda$TelephonyProperties$sXc3eBCFirzHWb9pvClH7EsiM_Q
+android.stats.camera.nano.CameraStreamProto
android.sysprop.AdbProperties
android.sysprop.ApexProperties
android.sysprop.ContactsProperties
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
new file mode 100644
index 0000000..f34e2bf
--- /dev/null
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+/**
+ * The camera action state used for passing camera usage information from
+ * camera service to camera service proxy .
+ *
+ * Include camera id, facing, state, client apk name, apiLevel, isNdk,
+ * and session/stream statistics.
+ *
+ * @hide
+ */
+public class CameraSessionStats implements Parcelable {
+ public static final int CAMERA_STATE_OPEN = 0;
+ public static final int CAMERA_STATE_ACTIVE = 1;
+ public static final int CAMERA_STATE_IDLE = 2;
+ public static final int CAMERA_STATE_CLOSED = 3;
+
+ /**
+ * Values for notifyCameraState facing
+ */
+ public static final int CAMERA_FACING_BACK = 0;
+ public static final int CAMERA_FACING_FRONT = 1;
+ public static final int CAMERA_FACING_EXTERNAL = 2;
+
+ /**
+ * Values for notifyCameraState api level
+ */
+ public static final int CAMERA_API_LEVEL_1 = 1;
+ public static final int CAMERA_API_LEVEL_2 = 2;
+
+ private String mCameraId;
+ private int mFacing;
+ private int mNewCameraState;
+ private String mClientName;
+ private int mApiLevel;
+ private boolean mIsNdk;
+ private int mLatencyMs;
+ private int mSessionType;
+ private int mInternalReconfigure;
+ private long mRequestCount;
+ private long mResultErrorCount;
+ private boolean mDeviceError;
+ private ArrayList<CameraStreamStats> mStreamStats;
+
+ public CameraSessionStats() {
+ mFacing = -1;
+ mNewCameraState = -1;
+ mApiLevel = -1;
+ mIsNdk = false;
+ mLatencyMs = -1;
+ mSessionType = -1;
+ mInternalReconfigure = -1;
+ mRequestCount = 0;
+ mResultErrorCount = 0;
+ mDeviceError = false;
+ mStreamStats = new ArrayList<CameraStreamStats>();
+ }
+
+ public CameraSessionStats(String cameraId, int facing, int newCameraState,
+ String clientName, int apiLevel, boolean isNdk, int creationDuration,
+ int sessionType, int internalReconfigure) {
+ mCameraId = cameraId;
+ mFacing = facing;
+ mNewCameraState = newCameraState;
+ mClientName = clientName;
+ mApiLevel = apiLevel;
+ mIsNdk = isNdk;
+ mLatencyMs = creationDuration;
+ mSessionType = sessionType;
+ mInternalReconfigure = internalReconfigure;
+ mStreamStats = new ArrayList<CameraStreamStats>();
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<CameraSessionStats> CREATOR =
+ new Parcelable.Creator<CameraSessionStats>() {
+ @Override
+ public CameraSessionStats createFromParcel(Parcel in) {
+ return new CameraSessionStats(in);
+ }
+
+ @Override
+ public CameraSessionStats[] newArray(int size) {
+ return new CameraSessionStats[size];
+ }
+ };
+
+ private CameraSessionStats(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mCameraId);
+ dest.writeInt(mFacing);
+ dest.writeInt(mNewCameraState);
+ dest.writeString(mClientName);
+ dest.writeInt(mApiLevel);
+ dest.writeBoolean(mIsNdk);
+ dest.writeInt(mLatencyMs);
+ dest.writeInt(mSessionType);
+ dest.writeInt(mInternalReconfigure);
+ dest.writeLong(mRequestCount);
+ dest.writeLong(mResultErrorCount);
+ dest.writeBoolean(mDeviceError);
+ dest.writeTypedList(mStreamStats);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mCameraId = in.readString();
+ mFacing = in.readInt();
+ mNewCameraState = in.readInt();
+ mClientName = in.readString();
+ mApiLevel = in.readInt();
+ mIsNdk = in.readBoolean();
+ mLatencyMs = in.readInt();
+ mSessionType = in.readInt();
+ mInternalReconfigure = in.readInt();
+ mRequestCount = in.readLong();
+ mResultErrorCount = in.readLong();
+ mDeviceError = in.readBoolean();
+
+ ArrayList<CameraStreamStats> streamStats = new ArrayList<CameraStreamStats>();
+ in.readTypedList(streamStats, CameraStreamStats.CREATOR);
+ mStreamStats = streamStats;
+ }
+
+ public String getCameraId() {
+ return mCameraId;
+ }
+
+ public int getFacing() {
+ return mFacing;
+ }
+
+ public int getNewCameraState() {
+ return mNewCameraState;
+ }
+
+ public String getClientName() {
+ return mClientName;
+ }
+
+ public int getApiLevel() {
+ return mApiLevel;
+ }
+
+ public boolean isNdk() {
+ return mIsNdk;
+ }
+
+ public int getLatencyMs() {
+ return mLatencyMs;
+ }
+
+ public int getSessionType() {
+ return mSessionType;
+ }
+
+ public int getInternalReconfigureCount() {
+ return mInternalReconfigure;
+ }
+
+ public long getRequestCount() {
+ return mRequestCount;
+ }
+
+ public long getResultErrorCount() {
+ return mResultErrorCount;
+ }
+
+ public boolean getDeviceErrorFlag() {
+ return mDeviceError;
+ }
+
+ public List<CameraStreamStats> getStreamStats() {
+ return mStreamStats;
+ }
+}
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
new file mode 100644
index 0000000..ae801b6
--- /dev/null
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * The camera stream statistics used for passing camera stream information from
+ * camera service to camera service proxy.
+ *
+ * Include camera stream configuration, request/error counts, startup latency,
+ * and latency/jitter histograms.
+ *
+ * @hide
+ */
+public class CameraStreamStats implements Parcelable {
+
+ private int mWidth;
+ private int mHeight;
+ private int mFormat;
+ private int mDataSpace;
+ private long mUsage;
+ private long mRequestCount;
+ private long mErrorCount;
+ private int mStartLatencyMs;
+ private int mMaxHalBuffers;
+ private int mMaxAppBuffers;
+
+ private static final String TAG = "CameraStreamStats";
+
+ public CameraStreamStats() {
+ mWidth = 0;
+ mHeight = 0;
+ mFormat = 0;
+ mDataSpace = 0;
+ mUsage = 0;
+ mRequestCount = 0;
+ mErrorCount = 0;
+ mStartLatencyMs = 0;
+ mMaxHalBuffers = 0;
+ mMaxAppBuffers = 0;
+ }
+
+ public CameraStreamStats(int width, int height, int format,
+ int dataSpace, long usage, long requestCount, long errorCount,
+ int startLatencyMs, int maxHalBuffers, int maxAppBuffers) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mDataSpace = dataSpace;
+ mUsage = usage;
+ mRequestCount = requestCount;
+ mErrorCount = errorCount;
+ mStartLatencyMs = startLatencyMs;
+ mMaxHalBuffers = maxHalBuffers;
+ mMaxAppBuffers = maxAppBuffers;
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
+ new Parcelable.Creator<CameraStreamStats>() {
+ @Override
+ public CameraStreamStats createFromParcel(Parcel in) {
+ try {
+ CameraStreamStats streamStats = new CameraStreamStats(in);
+ return streamStats;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating CameraStreamStats from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public CameraStreamStats[] newArray(int size) {
+ return new CameraStreamStats[size];
+ }
+ };
+
+ private CameraStreamStats(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mFormat);
+ dest.writeInt(mDataSpace);
+ dest.writeLong(mUsage);
+ dest.writeLong(mRequestCount);
+ dest.writeLong(mErrorCount);
+ dest.writeInt(mStartLatencyMs);
+ dest.writeInt(mMaxHalBuffers);
+ dest.writeInt(mMaxAppBuffers);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mFormat = in.readInt();
+ mDataSpace = in.readInt();
+ mUsage = in.readLong();
+ mRequestCount = in.readLong();
+ mErrorCount = in.readLong();
+ mStartLatencyMs = in.readInt();
+ mMaxHalBuffers = in.readInt();
+ mMaxAppBuffers = in.readInt();
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public int getFormat() {
+ return mFormat;
+ }
+
+ public int getDataSpace() {
+ return mDataSpace;
+ }
+
+ public long getUsage() {
+ return mUsage;
+ }
+
+ public long getRequestCount() {
+ return mRequestCount;
+ }
+
+ public long getErrorCount() {
+ return mErrorCount;
+ }
+
+ public int getStartLatencyMs() {
+ return mStartLatencyMs;
+ }
+
+ public int getMaxHalBuffers() {
+ return mMaxHalBuffers;
+ }
+
+ public int getMaxAppBuffers() {
+ return mMaxAppBuffers;
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b6f4bd3..9a9163c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -27,6 +27,7 @@
import android.hardware.camera2.utils.TaskSingleDrainer;
import android.os.Binder;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
@@ -1002,7 +1003,7 @@
// begin transition to unconfigured
mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
/*operatingMode*/ ICameraDeviceUser.NORMAL_MODE,
- /*sessionParams*/ null);
+ /*sessionParams*/ null, SystemClock.uptimeMillis());
} catch (CameraAccessException e) {
// OK: do not throw checked exceptions.
Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 819d966..f564ad7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -45,6 +45,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.SystemClock;
import android.util.Log;
import android.util.Range;
import android.util.Size;
@@ -374,7 +375,8 @@
outputConfigs.add(new OutputConfiguration(s));
}
configureStreamsChecked(/*inputConfig*/null, outputConfigs,
- /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null,
+ SystemClock.uptimeMillis());
}
@@ -395,12 +397,15 @@
* @param operatingMode If the stream configuration is for a normal session,
* a constrained high speed session, or something else.
* @param sessionParams Session parameters.
+ * @param createSessionStartTimeMs The timestamp when session creation starts, measured by
+ * uptimeMillis().
* @return whether or not the configuration was successful
*
* @throws CameraAccessException if there were any unexpected problems during configuration
*/
public boolean configureStreamsChecked(InputConfiguration inputConfig,
- List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
+ List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams,
+ long createSessionStartTime)
throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
@@ -479,9 +484,10 @@
int offlineStreamIds[];
if (sessionParams != null) {
offlineStreamIds = mRemoteDevice.endConfigure(operatingMode,
- sessionParams.getNativeCopy());
+ sessionParams.getNativeCopy(), createSessionStartTime);
} else {
- offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null);
+ offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null,
+ createSessionStartTime);
}
mOfflineSupport.clear();
@@ -650,6 +656,7 @@
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Executor executor,
int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
+ long createSessionStartTime = SystemClock.uptimeMillis();
synchronized(mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSessionInternal");
@@ -677,7 +684,7 @@
try {
// configure streams and then block until IDLE
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
- operatingMode, sessionParams);
+ operatingMode, sessionParams, createSessionStartTime);
if (configureSuccess == true && inputConfig != null) {
input = mRemoteDevice.getInputSurface();
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index fa7301b..ba4395f 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -110,11 +110,11 @@
}
}
- public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams)
- throws CameraAccessException {
+ public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams,
+ long startTimeMs) throws CameraAccessException {
try {
return mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ?
- new CameraMetadataNative() : sessionParams);
+ new CameraMetadataNative() : sessionParams, startTimeMs);
} catch (Throwable t) {
CameraManager.throwAsPublicException(t);
throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/proto/android/stats/camera/Android.bp b/core/proto/android/stats/camera/Android.bp
new file mode 100644
index 0000000..cc75e57
--- /dev/null
+++ b/core/proto/android/stats/camera/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "cameraprotosnano",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "*.proto",
+ ],
+ java_version: "1.8",
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ }
+ },
+ sdk_version: "core_platform",
+}
diff --git a/core/proto/android/stats/camera/camera.proto b/core/proto/android/stats/camera/camera.proto
new file mode 100644
index 0000000..4062855
--- /dev/null
+++ b/core/proto/android/stats/camera/camera.proto
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.stats.camera;
+option java_multiple_files = true;
+
+message CameraStreamProto {
+ // The stream width (in pixels)
+ optional int32 width = 1;
+ // The stream height (in pixels)
+ optional int32 height = 2;
+ // The format of the stream
+ optional int32 format = 3;
+ // The dataspace of the stream
+ optional int32 data_space = 4;
+ // The usage flag of the stream
+ optional int64 usage = 5;
+
+ // The number of requests for this stream
+ optional int64 request_count = 6;
+ // The number of buffer error for this stream
+ optional int64 error_count = 7;
+ // The capture latency of first request for this stream
+ optional int32 first_capture_latency_millis = 8;
+
+ // The maximum number of hal buffers
+ optional int32 max_hal_buffers = 9;
+ // The maximum number of app buffers
+ optional int32 max_app_buffers = 10;
+}
diff --git a/core/proto/android/stats/camera/jarjar-rules.txt b/core/proto/android/stats/camera/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/camera/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index a4ae9c8..2a81426 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.hardware.CameraSessionStats;
+import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
import android.media.AudioManager;
@@ -36,11 +38,13 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserManager;
+import android.stats.camera.nano.CameraStreamProto;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.framework.protobuf.nano.MessageNano;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
@@ -97,7 +101,10 @@
@interface DeviceStateFlags {}
// Maximum entries to keep in usage history before dumping out
- private static final int MAX_USAGE_HISTORY = 100;
+ private static final int MAX_USAGE_HISTORY = 20;
+ // Number of stream statistics being dumped for each camera session
+ // Must be equal to number of CameraStreamProto in CameraActionEvent
+ private static final int MAX_STREAM_STATISTICS = 5;
private final Context mContext;
private final ServiceThread mHandlerThread;
@@ -123,7 +130,6 @@
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();
@@ -137,27 +143,50 @@
* Structure to track camera usage
*/
private static class CameraUsageEvent {
+ public final String mCameraId;
public final int mCameraFacing;
public final String mClientName;
public final int mAPILevel;
+ public final boolean mIsNdk;
+ public final int mAction;
+ public final int mLatencyMs;
+ public final int mOperatingMode;
private boolean mCompleted;
+ public int mInternalReconfigure;
+ public long mRequestCount;
+ public long mResultErrorCount;
+ public boolean mDeviceError;
+ public List<CameraStreamStats> mStreamStats;
private long mDurationOrStartTimeMs; // Either start time, or duration once completed
- public CameraUsageEvent(int facing, String clientName, int apiLevel) {
+ CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel,
+ boolean isNdk, int action, int latencyMs, int operatingMode) {
+ mCameraId = cameraId;
mCameraFacing = facing;
mClientName = clientName;
mAPILevel = apiLevel;
mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
mCompleted = false;
+ mIsNdk = isNdk;
+ mAction = action;
+ mLatencyMs = latencyMs;
+ mOperatingMode = operatingMode;
}
- public void markCompleted() {
+ public void markCompleted(int internalReconfigure, long requestCount,
+ long resultErrorCount, boolean deviceError,
+ List<CameraStreamStats> streamStats) {
if (mCompleted) {
return;
}
mCompleted = true;
mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
+ mInternalReconfigure = internalReconfigure;
+ mRequestCount = requestCount;
+ mResultErrorCount = resultErrorCount;
+ mDeviceError = deviceError;
+ mStreamStats = streamStats;
if (CameraServiceProxy.DEBUG) {
Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
" was in use by " + mClientName + " for " +
@@ -211,19 +240,22 @@
}
@Override
- public void notifyCameraState(String cameraId, int newCameraState, int facing,
- String clientName, int apiLevel) {
+ public void notifyCameraState(CameraSessionStats cameraState) {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
" camera service UID!");
return;
}
- String state = cameraStateToString(newCameraState);
- String facingStr = cameraFacingToString(facing);
- if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
- state + " for client " + clientName + " API Level " + apiLevel);
+ String state = cameraStateToString(cameraState.getNewCameraState());
+ String facingStr = cameraFacingToString(cameraState.getFacing());
+ if (DEBUG) {
+ Slog.v(TAG, "Camera " + cameraState.getCameraId()
+ + " facing " + facingStr + " state now " + state
+ + " for client " + cameraState.getClientName()
+ + " API Level " + cameraState.getApiLevel());
+ }
- updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
+ updateActivityCount(cameraState);
}
};
@@ -385,20 +417,80 @@
private void logCameraUsageEvent(CameraUsageEvent e) {
int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
switch(e.mCameraFacing) {
- case ICameraServiceProxy.CAMERA_FACING_BACK:
+ case CameraSessionStats.CAMERA_FACING_BACK:
facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
break;
- case ICameraServiceProxy.CAMERA_FACING_FRONT:
+ case CameraSessionStats.CAMERA_FACING_FRONT:
facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
break;
- case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
+ case CameraSessionStats.CAMERA_FACING_EXTERNAL:
facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
break;
default:
Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
}
+
+ int streamCount = 0;
+ if (e.mStreamStats != null) {
+ streamCount = e.mStreamStats.size();
+ }
+ if (CameraServiceProxy.DEBUG) {
+ Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
+ + " clientName " + e.mClientName
+ + ", duration " + e.getDuration()
+ + ", APILevel " + e.mAPILevel
+ + ", cameraId " + e.mCameraId
+ + ", facing " + facing
+ + ", isNdk " + e.mIsNdk
+ + ", latencyMs " + e.mLatencyMs
+ + ", operatingMode " + e.mOperatingMode
+ + ", internalReconfigure " + e.mInternalReconfigure
+ + ", requestCount " + e.mRequestCount
+ + ", resultErrorCount " + e.mResultErrorCount
+ + ", deviceError " + e.mDeviceError
+ + ", streamCount is " + streamCount);
+ }
+ // Convert from CameraStreamStats to CameraStreamProto
+ CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
+ for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
+ streamProtos[i] = new CameraStreamProto();
+ if (i < streamCount) {
+ CameraStreamStats streamStats = e.mStreamStats.get(i);
+ streamProtos[i].width = streamStats.getWidth();
+ streamProtos[i].height = streamStats.getHeight();
+ streamProtos[i].format = streamStats.getFormat();
+ streamProtos[i].dataSpace = streamStats.getDataSpace();
+ streamProtos[i].usage = streamStats.getUsage();
+ streamProtos[i].requestCount = streamStats.getRequestCount();
+ streamProtos[i].errorCount = streamStats.getErrorCount();
+ streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
+ streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
+ streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
+
+ if (CameraServiceProxy.DEBUG) {
+ Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+ + ", height " + streamProtos[i].height
+ + ", format " + streamProtos[i].format
+ + ", dataSpace " + streamProtos[i].dataSpace
+ + ", usage " + streamProtos[i].usage
+ + ", requestCount " + streamProtos[i].requestCount
+ + ", errorCount " + streamProtos[i].errorCount
+ + ", firstCaptureLatencyMillis "
+ + streamProtos[i].firstCaptureLatencyMillis
+ + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
+ + ", maxAppBuffers " + streamProtos[i].maxAppBuffers);
+ }
+ }
+ }
FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
- e.mAPILevel, e.mClientName, facing);
+ e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk,
+ e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure,
+ e.mRequestCount, e.mResultErrorCount, e.mDeviceError,
+ streamCount, MessageNano.toByteArray(streamProtos[0]),
+ MessageNano.toByteArray(streamProtos[1]),
+ MessageNano.toByteArray(streamProtos[2]),
+ MessageNano.toByteArray(streamProtos[3]),
+ MessageNano.toByteArray(streamProtos[4]));
}
}
@@ -410,35 +502,6 @@
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");
- }
- 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())
- .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
- .setPackageName(e.mClientName);
- mLogger.write(l);
- }
-
mLogWriterService.execute(new EventWriterTask(
new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
@@ -569,13 +632,25 @@
return true;
}
- private void updateActivityCount(String cameraId, int newCameraState, int facing,
- String clientName, int apiLevel) {
+ private void updateActivityCount(CameraSessionStats cameraState) {
+ String cameraId = cameraState.getCameraId();
+ int newCameraState = cameraState.getNewCameraState();
+ int facing = cameraState.getFacing();
+ String clientName = cameraState.getClientName();
+ int apiLevel = cameraState.getApiLevel();
+ boolean isNdk = cameraState.isNdk();
+ int sessionType = cameraState.getSessionType();
+ int internalReconfigureCount = cameraState.getInternalReconfigureCount();
+ int latencyMs = cameraState.getLatencyMs();
+ long requestCount = cameraState.getRequestCount();
+ long resultErrorCount = cameraState.getResultErrorCount();
+ boolean deviceError = cameraState.getDeviceErrorFlag();
+ List<CameraStreamStats> streamStats = cameraState.getStreamStats();
synchronized(mLock) {
// Update active camera list and notify NFC if necessary
boolean wasEmpty = mActiveCameraUsage.isEmpty();
switch (newCameraState) {
- case ICameraServiceProxy.CAMERA_STATE_OPEN:
+ case CameraSessionStats.CAMERA_STATE_OPEN:
// Notify the audio subsystem about the facing of the most-recently opened
// camera This can be used to select the best audio tuning in case video
// recording with that camera will happen. Since only open events are used, if
@@ -584,13 +659,18 @@
AudioManager audioManager = getContext().getSystemService(AudioManager.class);
if (audioManager != null) {
// Map external to front for audio tuning purposes
- String facingStr = (facing == ICameraServiceProxy.CAMERA_FACING_BACK) ?
+ String facingStr = (facing == CameraSessionStats.CAMERA_FACING_BACK) ?
"back" : "front";
String facingParameter = "cameraFacing=" + facingStr;
audioManager.setParameters(facingParameter);
}
+ CameraUsageEvent openEvent = new CameraUsageEvent(
+ cameraId, facing, clientName, apiLevel, isNdk,
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN,
+ latencyMs, sessionType);
+ mCameraUsageHistory.add(openEvent);
break;
- case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
+ case CameraSessionStats.CAMERA_STATE_ACTIVE:
// Check current active camera IDs to see if this package is already talking to
// some camera
boolean alreadyActivePackage = false;
@@ -609,40 +689,55 @@
}
// Update activity events
- CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
+ CameraUsageEvent newEvent = new CameraUsageEvent(
+ cameraId, facing, clientName, apiLevel, isNdk,
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__SESSION,
+ latencyMs, sessionType);
CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
if (oldEvent != null) {
Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
- oldEvent.markCompleted();
+ oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
+ /*resultErrorCount*/0, /*deviceError*/false, streamStats);
mCameraUsageHistory.add(oldEvent);
}
break;
- case ICameraServiceProxy.CAMERA_STATE_IDLE:
- case ICameraServiceProxy.CAMERA_STATE_CLOSED:
+ case CameraSessionStats.CAMERA_STATE_IDLE:
+ case CameraSessionStats.CAMERA_STATE_CLOSED:
CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
- if (doneEvent == null) break;
+ if (doneEvent != null) {
- doneEvent.markCompleted();
- mCameraUsageHistory.add(doneEvent);
- if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
- dumpUsageEvents();
- }
+ doneEvent.markCompleted(internalReconfigureCount, requestCount,
+ resultErrorCount, deviceError, streamStats);
+ mCameraUsageHistory.add(doneEvent);
- // Check current active camera IDs to see if this package is still talking to
- // some camera
- boolean stillActivePackage = false;
- for (int i = 0; i < mActiveCameraUsage.size(); i++) {
- if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
- stillActivePackage = true;
- break;
+ // Check current active camera IDs to see if this package is still
+ // talking to some camera
+ boolean stillActivePackage = false;
+ for (int i = 0; i < mActiveCameraUsage.size(); i++) {
+ if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
+ stillActivePackage = true;
+ break;
+ }
+ }
+ // If not longer active, notify window manager about this package being done
+ // with camera
+ if (!stillActivePackage) {
+ WindowManagerInternal wmi =
+ LocalServices.getService(WindowManagerInternal.class);
+ wmi.removeNonHighRefreshRatePackage(clientName);
}
}
- // If not longer active, notify window manager about this package being done
- // with camera
- if (!stillActivePackage) {
- WindowManagerInternal wmi =
- LocalServices.getService(WindowManagerInternal.class);
- wmi.removeNonHighRefreshRatePackage(clientName);
+
+ if (newCameraState == CameraSessionStats.CAMERA_STATE_CLOSED) {
+ CameraUsageEvent closeEvent = new CameraUsageEvent(
+ cameraId, facing, clientName, apiLevel, isNdk,
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE,
+ latencyMs, sessionType);
+ mCameraUsageHistory.add(closeEvent);
+ }
+
+ if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
+ dumpUsageEvents();
}
break;
@@ -683,10 +778,10 @@
private static String cameraStateToString(int newCameraState) {
switch (newCameraState) {
- case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
- case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
- case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
- case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+ case CameraSessionStats.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+ case CameraSessionStats.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+ case CameraSessionStats.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+ case CameraSessionStats.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
default: break;
}
return "CAMERA_STATE_UNKNOWN";
@@ -694,9 +789,9 @@
private static String cameraFacingToString(int cameraFacing) {
switch (cameraFacing) {
- case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
- case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
- case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
+ case CameraSessionStats.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
+ case CameraSessionStats.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
+ case CameraSessionStats.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
default: break;
}
return "CAMERA_FACING_UNKNOWN";