Bluetooth: TWS+ : Process Battery status Information

Process battery status information for TWS+ devices

Change-Id: I1c7105fb63f6e4c70a9061ae1cef645f3f629a14
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
index f11d4cd..d9bee6a 100644
--- a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
@@ -38,6 +38,7 @@
 static btvendor_hf_interface_t *sBluetoothVendorHfInterface = NULL;
 static jobject mCallbacksObj = NULL;
 static jmethodID method_onSWB;
+static jmethodID method_onTwsBatteryStateCallback;
 
 static jbyteArray marshall_bda(RawAddress* bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
@@ -65,14 +66,41 @@
                                  addr.get());
 }
 
+void bt_twsp_battery_status_callback(char* at_string, RawAddress* bd_addr) {
+    ALOGD("TwsBatteryStateCallback :%s, BD_ADDR: %s",
+          at_string, bd_addr->ToString().c_str());
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid() || !mCallbacksObj) return;
+
+    ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
+    if (addr.get() == nullptr) return;
+
+    char null_str[] = "";
+    if (!sCallbackEnv.isValidUtf(at_string)) {
+      android_errorWriteLog(0x534e4554, "109838537");
+      ALOGE("%s: at_string is not a valid UTF string.", __func__);
+      at_string = null_str;
+    }
+
+    ScopedLocalRef<jstring> js_at_string(sCallbackEnv.get(),
+                                         sCallbackEnv->NewStringUTF(at_string));
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onTwsBatteryStateCallback,
+                                 js_at_string.get(), addr.get());
+}
+
 static btvendor_hf_callbacks_t sBluetoothVendorHfCallbacks = {
     sizeof(sBluetoothVendorHfCallbacks),
     SwbCallback,
+    bt_twsp_battery_status_callback
 };
 
 static void classInitNative(JNIEnv* env, jclass clazz) {
 
     method_onSWB = env->GetMethodID(clazz, "onSWB", "(I[B)V");
+    method_onTwsBatteryStateCallback =
+         env->GetMethodID(clazz, "onTwsBatteryStateCallback",
+                                 "(Ljava/lang/String;[B)V");
     ALOGI("%s: succeeds", __FUNCTION__);
 }
 
diff --git a/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
index f0c38b1..aab40ef 100644
--- a/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
+++ b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
@@ -73,6 +73,13 @@
         sendMessageToService(event);
     }
 
+    private void onTwsBatteryStateCallback(String atString, byte[] address) {
+        /*HeadsetStackEvent event = new HeadsetStackEvent(
+                HeadsetStackEvent.EVENT_TYPE_TWSP_BATTERY_STATE, atString,
+                getDevice(address));
+        sendMessageToService(event);*/
+    }
+
     private void sendMessageToService(HeadsetStackEvent event) {
         HeadsetService service = HeadsetService.getHeadsetService();
         if (service != null) {
diff --git a/system_bt_ext/bta/include/bta_ag_twsp.h b/system_bt_ext/bta/include/bta_ag_twsp.h
index bd4d960..c9cfd95 100644
--- a/system_bt_ext/bta/include/bta_ag_twsp.h
+++ b/system_bt_ext/bta/include/bta_ag_twsp.h
@@ -34,6 +34,7 @@
 #ifndef _BTA_AG_TWS_H_
 #define _BTA_AG_TWS_H_
 
+#include "btif_twsp_hf.h"
 #include "bta_ag_int.h"
 
 #define VS_QHCI_TWS_ESCO_SETUP_OPCODE 0x0D
diff --git a/system_bt_ext/bta/include/bta_ag_twsp_dev.h b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
index c1e2b23..fb6f5b5 100644
--- a/system_bt_ext/bta/include/bta_ag_twsp_dev.h
+++ b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
@@ -47,6 +47,9 @@
 #define TWSPLUS_MIN_BATTERY_CHARGE_STATE_POWERFAULT 2
 #define TWSPLUS_MAX_BATTERY_CHARGE 255
 
+#define TWSPLUS_MIN_BATTERY_LEVEL 0
+#define TWSPLUS_MAX_BATTERY_LEVEL 100
+
 #define TWSPLUS_INVALID_MICPATH_DELAY  (-1)
 #define TWSPLUS_MAX_MICPATH_DELAY   (65535)
 #define TWSPLUS_MIN_MICPATH_DELAY  (0)
@@ -82,7 +85,8 @@
 typedef struct {
    tBTA_AG_SCB *p_scb;
    uint8_t mic_quality;
-   uint8_t battery_charge;
+   uint16_t battery_state;
+   uint16_t battery_level;
    uint8_t state;
    int16_t mic_path_delay;
    uint8_t role;
@@ -91,8 +95,9 @@
    uint8_t ring_sent;
 } tTWSPLUS_DEVICE;
 
-void twsp_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg);
 void update_twsp_device(tBTA_AG_SCB* p_scb);
+void twsp_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd,
+                                    tBTA_AG_VAL* val , int16_t int_arg);
 void reset_twsp_device(int eb_idx);
 bool twsp_set_ring_sent(tBTA_AG_SCB *p_scb, bool ring_sent);
 void twsp_clr_all_ring_sent();
diff --git a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
index 11642e3..d2c25c4 100644
--- a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
+++ b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
@@ -37,6 +37,7 @@
 #include "bta_ag_twsp.h"
 #include "internal_include/bt_trace.h"
 #include "bta_ag_int.h"
+#include "utl.h"
 
 
 #if (TWS_AG_ENABLED == TRUE)
@@ -94,15 +95,16 @@
         }
      }
 
-    twsp_devices[eb_idx].p_scb = NULL;
-    twsp_devices[eb_idx].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
-    twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_UNKNOWN;
-    twsp_devices[eb_idx].role =  TWSPLUS_EB_ROLE_INVALID;
-    twsp_devices[eb_idx].mic_path_delay = TWSPLUS_INVALID_MICPATH_DELAY;
-    twsp_devices[eb_idx].mic_quality = TWSPLUS_MIN_MIC_QUALITY;
-    twsp_devices[eb_idx].qdsp_nr = TWSPLUS_INVALID_QDSP_VALUE;
-    twsp_devices[eb_idx].qdsp_ec = TWSPLUS_INVALID_QDSP_VALUE;
-    twsp_devices[eb_idx].ring_sent = false;
+     twsp_devices[eb_idx].p_scb = NULL;
+     twsp_devices[eb_idx].battery_state = TWSPLUS_MIN_BATTERY_CHARGE_STATE_DISCHARGING;
+     twsp_devices[eb_idx].battery_level = TWSPLUS_MIN_BATTERY_LEVEL;
+     twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_OFF;
+     twsp_devices[eb_idx].role =  TWSPLUS_EB_ROLE_INVALID;
+     twsp_devices[eb_idx].mic_path_delay = TWSPLUS_INVALID_MICPATH_DELAY;
+     twsp_devices[eb_idx].mic_quality = TWSPLUS_MIN_MIC_QUALITY;
+     twsp_devices[eb_idx].qdsp_nr = TWSPLUS_INVALID_QDSP_VALUE;
+     twsp_devices[eb_idx].qdsp_ec = TWSPLUS_INVALID_QDSP_VALUE;
+     twsp_devices[eb_idx].ring_sent = false;
 }
 
 void update_twsp_device(tBTA_AG_SCB* p_scb) {
@@ -110,8 +112,10 @@
         if (twsp_devices[i].p_scb == NULL) {
             APPL_TRACE_WARNING("%s: idx: %d, p_scb: %x", __func__, i, p_scb);
             twsp_devices[i].p_scb = p_scb;
-            twsp_devices[i].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
-            twsp_devices[i].state = TWSPLUS_EB_STATE_UNKNOWN;
+            twsp_devices[i].battery_state =
+                              TWSPLUS_MIN_BATTERY_CHARGE_STATE_DISCHARGING;
+            twsp_devices[i].battery_level = TWSPLUS_MIN_BATTERY_LEVEL;
+            twsp_devices[i].state = TWSPLUS_EB_STATE_OFF;
 
             int other_idx = (i == PRIMARY_EB_IDX) ? SECONDARY_EB_IDX : PRIMARY_EB_IDX;
             if (twsp_devices[other_idx].p_scb != NULL) {
@@ -156,8 +160,10 @@
     APPL_TRACE_DEBUG("%s: p_scb : %x", __func__, twsp_devices[eb_idx].p_scb);
     APPL_TRACE_DEBUG("%s: mic_quality : %d", __func__,
                                twsp_devices[eb_idx].mic_quality);
-    APPL_TRACE_DEBUG("%s: battery_charge : %d", __func__,
-                               twsp_devices[eb_idx].battery_charge);
+    APPL_TRACE_DEBUG("%s: battery_state : %d", __func__,
+                               twsp_devices[eb_idx].battery_state);
+    APPL_TRACE_DEBUG("%s: battery_level : %d", __func__,
+                               twsp_devices[eb_idx].battery_level);
     APPL_TRACE_DEBUG("%s: mic_path_delay : %d", __func__,
                                twsp_devices[eb_idx].mic_path_delay);
     APPL_TRACE_DEBUG("%s: state : %d", __func__, twsp_devices[eb_idx].state);
@@ -350,20 +356,26 @@
     return twsp_devices[eb_idx].qdsp_ec;
 }
 
-bool set_twsp_battery_charge(int eb_idx, uint8_t battery_charge) {
-    APPL_TRACE_DEBUG("%s: battery_charge : %d\n", __func__, battery_charge);
+bool set_twsp_battery_charge(int eb_idx, int16_t state, int16_t level) {
+    APPL_TRACE_DEBUG("%s: state : %d, level : %d\n", __func__, state, level);
 
     if (eb_idx < PRIMARY_EB_IDX || eb_idx > SECONDARY_EB_IDX) {
         APPL_TRACE_WARNING("%s: Invalid eb_idx: %d\n", __func__, eb_idx);
         return false;
     }
 
-    if (battery_charge < TWSPLUS_MIN_BATTERY_CHARGE || battery_charge > TWSPLUS_MAX_BATTERY_CHARGE) {
-        APPL_TRACE_WARNING("%s: Invalid battery_charge: %d\n", __func__, battery_charge);
+    if (state < TWSPLUS_MIN_BATTERY_CHARGE_STATE_CHARGING ||
+       state > TWSPLUS_MIN_BATTERY_CHARGE_STATE_POWERFAULT) {
+        APPL_TRACE_WARNING("%s: Invalid state: %d\n", __func__, state);
         return false;
     }
 
-    twsp_devices[eb_idx].battery_charge = battery_charge;
+    if (level < TWSPLUS_MIN_BATTERY_LEVEL ||
+       state > TWSPLUS_MAX_BATTERY_LEVEL) {
+        APPL_TRACE_WARNING("%s: Invalid level: %d\n", __func__, level);
+        return false;
+    }
+
     return true;
 }
 
@@ -578,10 +590,50 @@
     }
 }
 
-void twsp_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg)
+bool bta_ag_twsp_parse_qbc(tBTA_AG_SCB* p_scb, char* p_s,
+                           int16_t *state, int16_t *level) {
+    int16_t n[2] = {-1, -1};
+    int i;
+    char* p;
+
+    for (i = 0; i < 2; i++) {
+        /* skip to comma delimiter */
+        for (p = p_s; *p != ',' && *p != 0; p++)
+        ;
+
+        /* get integer value */
+        *p = 0;
+        n[i] = utl_str2int(p_s);
+        p_s = p + 1;
+        if (p_s == 0) {
+            break;
+        }
+    }
+
+    /* process values */
+    if (n[0] < TWSPLUS_MIN_BATTERY_CHARGE_STATE_CHARGING ||
+      n[0] > TWSPLUS_MAX_BATTERY_CHARGE) {
+        APPL_TRACE_ERROR("%s: Invalid QBC state: %d", __func__, n[0]);
+        return false;
+    }
+
+    if (n[1] < TWSPLUS_MIN_BATTERY_CHARGE_STATE_CHARGING ||
+      n[1] > TWSPLUS_MAX_BATTERY_CHARGE) {
+        APPL_TRACE_ERROR("%s: Invalid QBC level: %d", __func__, n[1]);
+        return false;
+    }
+
+    *state = n[0];
+    *level = n[1];
+    return true;
+}
+
+void twsp_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd,
+                                    tBTA_AG_VAL* val, int16_t int_arg)
 {
     APPL_TRACE_DEBUG("%s: p_scb : %x cmd : %d", __func__, p_scb, cmd);
-
+    int16_t state, level;
+    char str_to_parse[BTA_AG_AT_MAX_LEN];
     int idx = twsp_get_idx_by_scb(p_scb);
     if (idx < 0) {
         APPL_TRACE_ERROR("%s: Invalid SCB handle: %x", __func__, p_scb);
@@ -591,24 +643,38 @@
 
     //bta_ag_send_ok(p_scb);
     switch(cmd) {
-       case BTA_AG_TWSP_AT_QMQ_EVT: {
-            set_twsp_mic_quality(idx, int_arg);
-       } break;
-       case BTA_AG_TWSP_AT_QES_EVT: {
-            set_twsp_state(idx, int_arg);
-       } break;
-       case BTA_AG_TWSP_AT_QER_EVT: {
+        case BTA_AG_TWSP_AT_QMQ_EVT: {
+           set_twsp_mic_quality(idx, int_arg);
+        } break;
+        case BTA_AG_TWSP_AT_QES_EVT: {
+           set_twsp_state(idx, int_arg);
+        } break;
+        case BTA_AG_TWSP_AT_QER_EVT: {
             set_twsp_role(idx, int_arg);
-       } break;
-       case BTA_AG_TWSP_AT_QBC_EVT: {
-            set_twsp_battery_charge(idx, int_arg);
-       } break;
-       case BTA_AG_TWSP_AT_QMD_EVT: {
+        } break;
+        case BTA_AG_TWSP_AT_QBC_EVT: {
+            strlcpy(str_to_parse, val->str, sizeof(str_to_parse));
+            int ret = bta_ag_twsp_parse_qbc(p_scb,
+                                   str_to_parse, &state, &level);
+            if (ret) {
+                APPL_TRACE_DEBUG("%s: QBC=%d, %d", __func__, state, level);
+                if(set_twsp_battery_charge(idx, state, level)) {
+                    //Generate cb to BTIF/JNI
+                    APPL_TRACE_DEBUG("%s: at_str : %s", __func__, val->str);
+                    (*bta_ag_cb.p_cback)
+                           ((tBTA_AG_EVT)BTA_AG_TWSP_BATTERY_UPDATE,
+                           (tBTA_AG *)val);
+                }
+            } else {
+                APPL_TRACE_ERROR("%s: Fail to parse QBC", __func__)
+            }
+        } break;
+        case BTA_AG_TWSP_AT_QMD_EVT: {
             set_twsp_mic_path_delay(idx, int_arg);
-       } break;
-       case BTA_AG_TWSP_AT_QDSP_EVT: {
+        } break;
+        case BTA_AG_TWSP_AT_QDSP_EVT: {
             set_twsp_qdsp_features(idx, int_arg);
-       } break;
+        } break;
     }
 }
 
diff --git a/system_bt_ext/btif/include/btif_twsp_hf.h b/system_bt_ext/btif/include/btif_twsp_hf.h
index 658312f..0b11ca9 100644
--- a/system_bt_ext/btif/include/btif_twsp_hf.h
+++ b/system_bt_ext/btif/include/btif_twsp_hf.h
@@ -32,6 +32,12 @@
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#define BTA_AG_TWSP_BATTERY_UPDATE 255
+
+#include "bta_ag_api.h"
+
+void btif_handle_vendor_hf_events(uint16_t event, tBTA_AG* data,
+                                  RawAddress *bd_addr);
 namespace bluetooth {
 namespace headset {
 
diff --git a/system_bt_ext/btif/src/btif_vendor_hf.cc b/system_bt_ext/btif/src/btif_vendor_hf.cc
index dc4895e..8468db9 100644
--- a/system_bt_ext/btif/src/btif_vendor_hf.cc
+++ b/system_bt_ext/btif/src/btif_vendor_hf.cc
@@ -39,6 +39,8 @@
 #define LOG_TAG "bt_btif_vendor_hf"
 
 #include <hardware/vendor_hf.h>
+#include "bta_ag_swb.h"
+#include "btif_twsp_hf.h"
 #include "btif_api.h"
 
 btvendor_hf_callbacks_t *bt_vendor_hf_callbacks = NULL;
@@ -85,8 +87,25 @@
     enable_swb,
 };
 
-void btif_handle_vendor_hf_events(uint16_t event, uint16_t swb_config, RawAddress *bd_addr) {
-    HAL_CBACK(bt_vendor_hf_callbacks, swb_codec_cb, swb_config, bd_addr);
+void btif_handle_vendor_hf_events(uint16_t event, tBTA_AG* data,
+                                  RawAddress *bd_addr) {
+    BTIF_TRACE_EVENT("%s: event: %d", __func__, event)
+    switch (event) {
+        case BTA_AG_SWB_EVT:
+            HAL_CBACK(bt_vendor_hf_callbacks, swb_codec_cb, data->val.num,
+                        bd_addr);
+            break;
+        case BTA_AG_AT_QCS_EVT:
+            HAL_CBACK(bt_vendor_hf_callbacks, swb_codec_cb, data->val.num,
+                        bd_addr);
+            break;
+        case BTA_AG_TWSP_BATTERY_UPDATE:
+            HAL_CBACK(bt_vendor_hf_callbacks, twsp_batt_status_cb,
+                        data->val.str, bd_addr);
+            break;
+        default:
+            BTIF_TRACE_EVENT("%s: unknown vendor hf event: %d", __func__,event);
+    }
 }
 
 bool get_swb_codec_status() {
diff --git a/vhal/include/hardware/vendor_hf.h b/vhal/include/hardware/vendor_hf.h
index 6b07c5c..242561c 100644
--- a/vhal/include/hardware/vendor_hf.h
+++ b/vhal/include/hardware/vendor_hf.h
@@ -43,11 +43,14 @@
 /* SWB callback events */
 typedef void (* bt_swb_update_callback)(uint16_t swb_codec_config, RawAddress *bd_addr);
 
+typedef void (* bt_twsp_battery_status_callback)(char* at_string, RawAddress *bd_addr);
+
 /** BT-Vendor hf callback structure. */
 typedef struct {
     /** set to sizeof(BtVendorhfCallbacks) */
     size_t      size;
     bt_swb_update_callback  swb_codec_cb;
+    bt_twsp_battery_status_callback twsp_batt_status_cb;
 } btvendor_hf_callbacks_t;