summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_hfp_slc.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_hfp_slc.c')
-rw-r--r--cras/src/server/cras_hfp_slc.c101
1 files changed, 90 insertions, 11 deletions
diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c
index e4f0127d..28f73edc 100644
--- a/cras/src/server/cras_hfp_slc.c
+++ b/cras/src/server/cras_hfp_slc.c
@@ -441,12 +441,10 @@ static int available_codecs(struct hfp_slc_handle *handle, const char *cmd)
id_str = strtok(NULL, ",");
}
- for (id = HFP_MAX_CODECS - 1; id > 0; id--) {
- if (handle->hf_codec_supported[id]) {
- handle->preferred_codec = id;
- break;
- }
- }
+ if (hfp_slc_get_wideband_speech_supported(handle))
+ handle->preferred_codec = HFP_CODEC_ID_MSBC;
+ else
+ handle->preferred_codec = HFP_CODEC_ID_CVSD;
free(tokens);
return hfp_send(handle, AT_CMD("OK"));
@@ -609,6 +607,26 @@ static int operator_selection(struct hfp_slc_handle *handle, const char *buf)
return hfp_send(handle, AT_CMD("OK"));
}
+/* The AT+CHLD command is used to control call hold, release, and multiparty
+ * states.
+ */
+static int call_hold(struct hfp_slc_handle *handle, const char *buf)
+{
+ int rc;
+
+ // Chrome OS doesn't yet support CHLD features but we need to reply
+ // the query with an empty feature list rather than "ERROR" to increase
+ // interoperability with certain devices (b/172413440).
+ if (strlen(buf) > 8 && buf[7] == '=' && buf[8] == '?') {
+ rc = hfp_send(handle, AT_CMD("+CHLD:"));
+ if (rc)
+ return rc;
+ return hfp_send(handle, AT_CMD("OK"));
+ }
+
+ return hfp_send(handle, AT_CMD("ERROR"));
+}
+
/* AT+CIND command retrieves the supported indicator and its corresponding
* range and order index or read current status of indicators. Mandatory
* support per spec 4.2.
@@ -938,6 +956,70 @@ static int terminate_call(struct hfp_slc_handle *handle, const char *cmd)
return cras_telephony_event_terminate_call();
}
+/* AT+XEVENT is defined by Android to support vendor specific features.
+ * Currently, the only known supported case for CrOS is the battery event sent
+ * by some Plantronics headsets.
+ */
+static int vendor_specific_features(struct hfp_slc_handle *handle,
+ const char *cmd)
+{
+ char *tokens, *event, *level_str, *num_of_level_str;
+ int level, num_of_level;
+
+ tokens = strdup(cmd);
+ strtok(tokens, "=");
+ event = strtok(NULL, ",");
+ if (!event)
+ goto error_out;
+
+ /* AT+XEVENT=BATTERY,Level,NumberOfLevel,MinutesOfTalkTime,IsCharging
+ * Level: The charge level with a zero-based integer.
+ * NumberOfLevel: How many charging levels there are.
+ * MinuteOfTalkTime: The estimated number of talk minutes remaining.
+ * IsCharging: A 0 or 1 value.
+ *
+ * We only support the battery level and thus only care about the first
+ * 3 arguments.
+ */
+ if (!strncmp(event, "BATTERY", 7)) {
+ level_str = strtok(NULL, ",");
+ num_of_level_str = strtok(NULL, ",");
+ if (!level_str || !num_of_level_str)
+ goto error_out;
+
+ level = atoi(level_str);
+ num_of_level = atoi(num_of_level_str);
+ if (level < 0 || num_of_level <= 1 || level >= num_of_level)
+ goto error_out;
+
+ level = (int64_t)level * 100 / (num_of_level - 1);
+ if (handle->hf_battery != level) {
+ handle->hf_supports_battery_indicator |=
+ CRAS_HFP_BATTERY_INDICATOR_PLANTRONICS;
+ cras_server_metrics_hfp_battery_report(
+ CRAS_HFP_BATTERY_INDICATOR_PLANTRONICS);
+ handle->hf_battery = level;
+ cras_observer_notify_bt_battery_changed(
+ cras_bt_device_address(handle->device),
+ (uint32_t)(level));
+ }
+ }
+
+ free(tokens);
+ /* For Plantronic headsets, it is required to reply "OK" for the first
+ * AT+XEVENT=USER-AGENT... command to tell the headset our support of
+ * the xevent protocol. Otherwise, all following events including
+ * BATTERY won't be sent.
+ */
+ return hfp_send(handle, AT_CMD("OK"));
+
+error_out:
+ syslog(LOG_ERR, "%s: malformed vendor specific command: '%s'", __func__,
+ cmd);
+ free(tokens);
+ return hfp_send(handle, AT_CMD("ERROR"));
+}
+
/* AT commands to support in order to conform HFP specification.
*
* An initialized service level connection is the pre-condition for all
@@ -999,6 +1081,8 @@ static struct at_command at_commands[] = {
{ "AT+VG", signal_gain_setting },
{ "AT+VTS", dtmf_tone },
{ "AT+XAPL", apple_supported_features },
+ { "AT+XEVENT", vendor_specific_features },
+ { "AT+CHLD", call_hold },
{ 0 }
};
@@ -1314,8 +1398,3 @@ int hfp_slc_get_hf_supports_battery_indicator(struct hfp_slc_handle *handle)
{
return handle->hf_supports_battery_indicator;
}
-
-int hfp_slc_get_hf_battery_level(struct hfp_slc_handle *handle)
-{
- return handle->hf_battery;
-}