Fix A2DP metrics session duration

* Fixed A2DP duration counting. It is now counting from music play start
  to music play end.
* Start logging a2dp connection as Bluetooth sessions. Currently, only
  A2DP connections are logged. Thus the bluetooth session length will be
  the total connection length and the length within A2DP session message
  will be the audio connection length.
* Add a audio_duration_millis field in A2DPSession to record audio duration
* Add bonded memory constraint for metrics entries
* Use a builder mechanism to only build metrics upon dumping
* Refactor metrics module into BluetoothMetricsLogger class
* Created unit test for BluetoothMetricsLogger

Bug: 33694310
Test: Code compilation, Unit test, BtFunhausMetricsTest
Change-Id: Iea2a997c4ea074687a5d50860e9229f0e1b82659
diff --git a/system/osi/include/metrics.h b/system/osi/include/metrics.h
index abe2225..e2bc6cb 100644
--- a/system/osi/include/metrics.h
+++ b/system/osi/include/metrics.h
@@ -19,6 +19,12 @@
 #pragma once
 
 #include <stdint.h>
+#include <memory>
+#include <string>
+
+namespace system_bt_osi {
+
+// Typedefs to hide protobuf definition to the rest of stack
 
 typedef enum {
   DEVICE_TYPE_UNKNOWN,
@@ -27,26 +33,12 @@
   DEVICE_TYPE_DUMO,
 } device_type_t;
 
-// Record a pairing event at Unix epoch time |timestamp_ms|
-// |device_class| and |device_type| denote the type of device paired.
-// |disconnect_reason| is the HCI reason for pairing disconnection,
-// see stack/include/hcidefs.h
-void metrics_pair_event(uint32_t disconnect_reason, uint64_t timestamp_ms,
-                        uint32_t device_class, device_type_t device_type);
-
 typedef enum {
   WAKE_EVENT_UNKNOWN,
   WAKE_EVENT_ACQUIRED,
   WAKE_EVENT_RELEASED,
 } wake_event_type_t;
 
-// Record a wake event at Unix epoch time |timestamp_ms|.
-// |type| specifies whether it was acquired or relased,
-// |requestor| if provided is the service requesting the wake lock.
-// |name| is the name of the wake lock held.
-void metrics_wake_event(wake_event_type_t type, const char* requestor,
-                        const char* name, uint64_t timestamp_ms);
-
 typedef enum {
   SCAN_TYPE_UNKNOWN,
   SCAN_TECH_TYPE_LE,
@@ -54,36 +46,207 @@
   SCAN_TECH_TYPE_BOTH,
 } scan_tech_t;
 
-// Record a scan event at Unix epoch time |timestamp_ms|.
-// |start| is true if this is the beginning of the scan.
-// |initiator| is a unique ID identifying the app starting the scan.
-// |type| is whether the scan reports BR/EDR, LE, or both.
-// |results| is the number of results to be reported.
-void metrics_scan_event(bool start, const char* initator, scan_tech_t type,
-                        uint32_t results, uint64_t timestamp_ms);
+typedef enum {
+  CONNECTION_TECHNOLOGY_TYPE_UNKNOWN,
+  CONNECTION_TECHNOLOGY_TYPE_LE,
+  CONNECTION_TECHNOLOGY_TYPE_BREDR,
+} connection_tech_t;
 
-// Record A2DP session information.
-// |session_duration_sec| is the session duration (in seconds).
-// |device_class| is the device class of the paired device.
-// |media_timer_min_ms| is the minimum scheduled time (in milliseconds)
-// of the media timer.
-// |media_timer_max_ms| is the maximum scheduled time (in milliseconds)
-// of the media timer.
-// |media_timer_avg_ms| is the average scheduled time (in milliseconds)
-// of the media timer.
-// |buffer_overruns_max_count| - TODO - not clear what this is.
-// |buffer_overruns_total| is the number of times the media buffer with
-// audio data has overrun.
-// |buffer_underruns_average| - TODO - not clear what this is.
-// |buffer_underruns_count| is the number of times there was no enough
-// audio data to add to the media buffer.
-void metrics_a2dp_session(
-    int64_t session_duration_sec, const char* disconnect_reason,
-    uint32_t device_class, int32_t media_timer_min_ms,
-    int32_t media_timer_max_ms, int32_t media_timer_avg_ms,
-    int32_t buffer_overruns_max_count, int32_t buffer_overruns_total,
-    float buffer_underruns_average, int32_t buffer_underruns_count);
+/* Values of A2DP metrics that we care about
+ *
+ *    audio_duration_ms : sum of audio duration (in milliseconds).
+ *    device_class: device class of the paired device.
+ *    media_timer_min_ms : minimum scheduled time (in milliseconds)
+ *                         of the media timer.
+ *    media_timer_max_ms: maximum scheduled time (in milliseconds)
+ *                        of the media timer.
+ *    media_timer_avg_ms: average scheduled time (in milliseconds)
+ *                        of the media timer.
+ *    buffer_overruns_max_count: TODO - not clear what this is.
+ *    buffer_overruns_total : number of times the media buffer with
+ *                            audio data has overrun
+ *    buffer_underruns_average: TODO - not clear what this is.
+ *    buffer_underruns_count: number of times there was no enough
+ *                            audio data to add to the media buffer.
+ * NOTE: Negative values are invalid
+*/
+class A2dpSessionMetrics {
+ public:
+  A2dpSessionMetrics() {}
 
-// Writes the metrics, in packed protobuf format, into the descriptor |fd|.
-// If |clear| is true, metrics events are cleared afterwards.
-void metrics_write(int fd, bool clear);
+  /*
+   * Update the metrics value in the current metrics object using the metrics
+   * objects supplied
+   */
+  void Update(const A2dpSessionMetrics& metrics);
+
+  /*
+   * Compare whether two metrics objects are equal
+   */
+  bool operator==(const A2dpSessionMetrics& rhs) const;
+
+  /*
+   * Initialize all values to -1 which is invalid in order to make a distinction
+   * between 0 and invalid values
+   */
+  int64_t audio_duration_ms = -1;
+  int32_t media_timer_min_ms = -1;
+  int32_t media_timer_max_ms = -1;
+  int32_t media_timer_avg_ms = -1;
+  int64_t total_scheduling_count = -1;
+  int32_t buffer_overruns_max_count = -1;
+  int32_t buffer_overruns_total = -1;
+  float buffer_underruns_average = -1;
+  int32_t buffer_underruns_count = -1;
+};
+
+class BluetoothMetricsLogger {
+ public:
+  static BluetoothMetricsLogger* GetInstance() {
+    static BluetoothMetricsLogger* instance = new BluetoothMetricsLogger();
+    return instance;
+  }
+
+  /*
+   * Record a pairing event
+   *
+   * Parameters:
+   *    timestamp_ms: Unix epoch time in milliseconds
+   *    device_class: class of remote device
+   *    device_type: type of remote device
+   *    disconnect_reason: HCI reason for pairing disconnection.
+   *                       See: stack/include/hcidefs.h
+   */
+  void LogPairEvent(uint32_t disconnect_reason, uint64_t timestamp_ms,
+                    uint32_t device_class, device_type_t device_type);
+
+  /*
+   * Record a wake event
+   *
+   * Parameters:
+   *    timestamp_ms: Unix epoch time in milliseconds
+   *    type: whether it was acquired or released
+   *    requestor: if provided is the service requesting the wake lock
+   *    name: the name of the wake lock held
+   */
+  void LogWakeEvent(wake_event_type_t type, const std::string& requestor,
+                    const std::string& name, uint64_t timestamp_ms);
+
+  /*
+   * Record a scan event
+   *
+   * Parameters
+   *    timestamp_ms : Unix epoch time in milliseconds
+   *    start : true if this is the beginning of the scan
+   *    initiator: a unique ID identifying the app starting the scan
+   *    type: whether the scan reports BR/EDR, LE, or both.
+   *    results: number of results to be reported.
+   */
+  void LogScanEvent(bool start, const std::string& initator, scan_tech_t type,
+                    uint32_t results, uint64_t timestamp_ms);
+
+  /*
+   * Start logging a Bluetooth session
+   *
+   * A Bluetooth session is defined a a connection between this device and
+   * another remote device which may include multiple profiles and protocols
+   *
+   * Only one Bluetooth session can exist at one time. Calling this method twice
+   * without LogBluetoothSessionEnd will result in logging a premature end of
+   * current Bluetooth session
+   *
+   * Parameters:
+   *    connection_tech_type : type of connection technology
+   *    timestamp_ms : the timestamp for session start, 0 means now
+   *
+   */
+  void LogBluetoothSessionStart(connection_tech_t connection_tech_type,
+                                uint64_t timestamp_ms);
+
+  /*
+   * Stop logging a Bluetooth session and pushes it to the log queue
+   *
+   * If no Bluetooth session exist, this method exits immediately
+   *
+   * Parameters:
+   *    disconnect_reason : A string representation of disconnect reason
+   *    timestamp_ms : the timestamp of session end, 0 means now
+   *
+   */
+  void LogBluetoothSessionEnd(const std::string& disconnect_reason,
+                              uint64_t timestamp_ms);
+
+  /*
+   * Log information about remote device in a current Bluetooth session
+   *
+   * If a Bluetooth session does not exist, create one with default parameter
+   * and timestamp now
+   *
+   * Parameters:
+   *    device_class : device_class defined in btm_api_types.h
+   *    device_type : type of remote device
+   */
+  void LogBluetoothSessionDeviceInfo(uint32_t device_class,
+                                     device_type_t device_type);
+
+  /*
+   * Log A2DP Audio Session Information
+   *
+   * - Repeated calls to this method will override previous metrics if in the
+   *   same Bluetooth connection
+   * - If a Bluetooth session does not exist, create one with default parameter
+   *   and timestamp now
+   *
+   * Parameters:
+   *    a2dp_session_metrics - pointer to struct holding a2dp stats
+   *
+   */
+  void LogA2dpSession(const A2dpSessionMetrics& a2dp_session_metrics);
+
+  /*
+   * Writes the metrics, in base64 protobuf format, into the descriptor FD
+   * If CLEAR is true, metrics events are cleared afterwards.
+   */
+  void WriteBase64(int fd, bool clear);
+  void WriteBase64String(std::string* serialized, bool clear);
+  void WriteString(std::string* serialized, bool clear);
+
+  /*
+   * Reset the metrics logger by cleaning up its staging queues and existing
+   * protobuf objects.
+   */
+  void Reset();
+
+ private:
+  BluetoothMetricsLogger();
+
+  /*
+   * When a Bluetooth session is on and the user initiates a metrics dump, we
+   * need to be able to upload whatever we have first. This method breaks the
+   * ongoing Bluetooth session into two sessions with the previous one labeled
+   * as "METRICS_DUMP" for the disconnect reason.
+   */
+  void CutoffSession();
+
+  /*
+   * Build the internal metrics object using information gathered
+   */
+  void Build();
+
+  /*
+   * Reset objects related to current Bluetooth session
+   */
+  void ResetSession();
+
+  /*
+   * Reset the underlining BluetoothLog object
+   */
+  void ResetLog();
+
+  /*
+   * PIMPL style implementation to hide internal dependencies
+   */
+  struct impl;
+  std::unique_ptr<impl> const pimpl_;
+};
+}