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";