CRAS: Log the volume of output devices

It is used for tracing the user behavior on the volume setting.
When the device is closed, submit the volume to the UMA server.

BUG=b:160939695
TEST=Play audio and check the chrome://histograms

Change-Id: Ib6b60f546fbdd74eea59525c2ed18a54cd422b4c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/2291484
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
Commit-Queue: Yu-Hsuan Hsu <yuhsuan@chromium.org>
Tested-by: Yu-Hsuan Hsu <yuhsuan@chromium.org>
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index a741def..60dc34c 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -1003,7 +1003,10 @@
 	if (!cras_iodev_is_open(iodev))
 		return 0;
 
-	cras_server_metrics_device_runtime(iodev);
+	if (iodev->active_node) {
+		cras_server_metrics_device_runtime(iodev);
+		cras_server_metrics_device_volume(iodev);
+	}
 
 	if (iodev->input_data) {
 		if (iodev->ext_dsp_module == &iodev->input_data->ext)
diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c
index 5997cbd..1038c92 100644
--- a/cras/src/server/cras_server_metrics.c
+++ b/cras/src/server/cras_server_metrics.c
@@ -25,6 +25,7 @@
 const char kBusyloopLength[] = "Cras.BusyloopLength";
 const char kDeviceTypeInput[] = "Cras.DeviceTypeInput";
 const char kDeviceTypeOutput[] = "Cras.DeviceTypeOutput";
+const char kDeviceVolume[] = "Cras.DeviceVolume";
 const char kHighestDeviceDelayInput[] = "Cras.HighestDeviceDelayInput";
 const char kHighestDeviceDelayOutput[] = "Cras.HighestDeviceDelayOutput";
 const char kHighestInputHardwareLevel[] = "Cras.HighestInputHardwareLevel";
@@ -93,6 +94,7 @@
 	BUSYLOOP,
 	BUSYLOOP_LENGTH,
 	DEVICE_RUNTIME,
+	DEVICE_VOLUME,
 	HIGHEST_DEVICE_DELAY_INPUT,
 	HIGHEST_DEVICE_DELAY_OUTPUT,
 	HIGHEST_INPUT_HW_LEVEL,
@@ -157,6 +159,7 @@
 	enum CRAS_METRICS_DEVICE_TYPE type;
 	enum CRAS_STREAM_DIRECTION direction;
 	struct timespec runtime;
+	unsigned value;
 };
 
 struct cras_server_metrics_stream_data {
@@ -531,6 +534,31 @@
 	return 0;
 }
 
+int cras_server_metrics_device_volume(struct cras_iodev *iodev)
+{
+	struct cras_server_metrics_message msg;
+	union cras_server_metrics_data data;
+	int err;
+
+	if (iodev->direction == CRAS_STREAM_INPUT)
+		return 0;
+
+	data.device_data.type = get_metrics_device_type(iodev);
+	data.device_data.value = iodev->active_node->volume;
+
+	init_server_metrics_msg(&msg, DEVICE_VOLUME, data);
+
+	err = cras_server_metrics_message_send(
+		(struct cras_main_message *)&msg);
+	if (err < 0) {
+		syslog(LOG_ERR,
+		       "Failed to send metrics message: DEVICE_VOLUME");
+		return err;
+	}
+
+	return 0;
+}
+
 int cras_server_metrics_highest_device_delay(
 	unsigned int hw_level, unsigned int largest_cb_level,
 	enum CRAS_STREAM_DIRECTION direction)
@@ -930,6 +958,15 @@
 		cras_metrics_log_sparse_histogram(kDeviceTypeOutput, data.type);
 }
 
+static void metrics_device_volume(struct cras_server_metrics_device_data data)
+{
+	char metrics_name[METRICS_NAME_BUFFER_SIZE];
+
+	snprintf(metrics_name, METRICS_NAME_BUFFER_SIZE, "%s.%s", kDeviceVolume,
+		 metrics_device_type_str(data.type));
+	cras_metrics_log_histogram(metrics_name, data.value, 0, 100, 20);
+}
+
 static void metrics_stream_runtime(struct cras_server_metrics_stream_data data)
 {
 	char metrics_name[METRICS_NAME_BUFFER_SIZE];
@@ -1059,6 +1096,9 @@
 	case DEVICE_RUNTIME:
 		metrics_device_runtime(metrics_msg->data.device_data);
 		break;
+	case DEVICE_VOLUME:
+		metrics_device_volume(metrics_msg->data.device_data);
+		break;
 	case HIGHEST_DEVICE_DELAY_INPUT:
 		cras_metrics_log_histogram(kHighestDeviceDelayInput,
 					   metrics_msg->data.value, 1, 10000,
diff --git a/cras/src/server/cras_server_metrics.h b/cras/src/server/cras_server_metrics.h
index 26adbb2..91f13c3 100644
--- a/cras/src/server/cras_server_metrics.h
+++ b/cras/src/server/cras_server_metrics.h
@@ -49,6 +49,9 @@
 /* Logs runtime of a device. */
 int cras_server_metrics_device_runtime(struct cras_iodev *iodev);
 
+/* Logs the volume of a device. */
+int cras_server_metrics_device_volume(struct cras_iodev *iodev);
+
 /* Logs the highest delay time of a device. */
 int cras_server_metrics_highest_device_delay(
 	unsigned int hw_level, unsigned int largest_cb_level,
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index fd9ae97..f16094d 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -2749,6 +2749,10 @@
   return 0;
 }
 
+int cras_server_metrics_device_volume(struct cras_iodev* iodev) {
+  return 0;
+}
+
 }  // extern "C"
 }  //  namespace