aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYimin Li <ymli@google.com>2022-04-26 11:30:54 -0700
committerYimin Li <ymli@google.com>2022-04-26 11:30:54 -0700
commit17ae718d78b0baa63d6f7bfe769b417f15568b73 (patch)
tree09313c256b3a38c8aa253e6322d6ec463a4df364
parent13435b1c9678462d2ff9f3bb2189f67eb23c2e92 (diff)
parent6cab953d1b6df0a93630d3c8aa1c351932a6df44 (diff)
downloadbt-17ae718d78b0baa63d6f7bfe769b417f15568b73.tar.gz
Merge commit '6cab953d1b6df0a93630d3c8aa1c351932a6df44' of sso://googleplex-android/platform/system/bt into HEAD
Change-Id: I73a6088889d7656b3529a8d4f29abc4b3a077d62
-rw-r--r--bta/hearing_aid/hearing_aid.cc118
-rw-r--r--bta/include/bta_hearing_aid_api.h12
-rw-r--r--btif/avrcp/avrcp_service.cc10
-rw-r--r--btif/avrcp/avrcp_service.h1
-rw-r--r--gd/cert/ble_lib.py261
-rw-r--r--gd/cert/bt_constants.py740
-rw-r--r--gd/cert/gd_base_test.py2
-rw-r--r--gd/cert/gd_base_test_lib.py3
-rw-r--r--gd/cert/gd_device.py32
-rw-r--r--gd/cert/gd_sl4a_base_test.py129
-rw-r--r--gd/cert/gd_sl4a_device_config.json46
-rwxr-xr-xgd/cert/run55
-rw-r--r--gd/hci/cert/le_advanced_scanning_test.py250
-rw-r--r--gd/hci/facade/le_advertising_manager_facade.cc60
-rw-r--r--osi/src/config.cc6
-rw-r--r--profile/avrcp/avrcp_config.h7
-rw-r--r--stack/acl/btm_acl.cc9
-rw-r--r--stack/gatt/gatt_sr.cc3
-rw-r--r--stack/include/l2c_api.h12
-rw-r--r--stack/l2cap/l2c_api.cc28
20 files changed, 1716 insertions, 68 deletions
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index bf3f39fa6..f008a88a2 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -1152,10 +1152,54 @@ class HearingAidImpl : public HearingAid {
hearingDevice->playback_started = true;
}
+ /* Compare the two sides LE CoC credit and return true to drop two sides
+ * packet on these situations.
+ * 1) The credit is close
+ * 2) Other side is disconnected
+ * 3) Get one side current credit value failure.
+ *
+ * Otherwise, just flush audio packet one side.
+ */
+ bool NeedToDropPacket(HearingDevice* target_side, HearingDevice* other_side) {
+ // Just drop packet if the other side does not exist.
+ if (!other_side) {
+ VLOG(2) << __func__ << ": other side not connected to profile";
+ return true;
+ }
+
+ uint16_t diff_credit = 0;
+
+ uint16_t target_current_credit = L2CA_GetPeerLECocCredit(
+ target_side->address, GAP_ConnGetL2CAPCid(target_side->gap_handle));
+ if (target_current_credit == L2CAP_LE_CREDIT_MAX) {
+ LOG(ERROR) << __func__ << ": Get target side credit value fail.";
+ return true;
+ }
+
+ uint16_t other_current_credit = L2CA_GetPeerLECocCredit(
+ other_side->address, GAP_ConnGetL2CAPCid(other_side->gap_handle));
+ if (other_current_credit == L2CAP_LE_CREDIT_MAX) {
+ LOG(ERROR) << __func__ << ": Get other side credit value fail.";
+ return true;
+ }
+
+ if (target_current_credit > other_current_credit) {
+ diff_credit = target_current_credit - other_current_credit;
+ } else {
+ diff_credit = other_current_credit - target_current_credit;
+ }
+ VLOG(2) << __func__ << ": Target(" << target_side->address
+ << ") Credit: " << target_current_credit << ", Other("
+ << other_side->address << ") Credit: " << other_current_credit
+ << ", Init Credit: " << init_credit;
+ return diff_credit < (init_credit / 2 - 1);
+ }
+
void OnAudioDataReady(const std::vector<uint8_t>& data) {
/* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */
DVLOG(2) << __func__;
+ bool need_drop = false;
int num_samples =
data.size() / (2 /*bytes_per_sample*/ * 2 /*number of channels*/);
@@ -1229,16 +1273,24 @@ class HearingAidImpl : public HearingAid {
encoded_data_left.resize(encoded_size);
uint16_t cid = GAP_ConnGetL2CAPCid(left->gap_handle);
- uint16_t packets_to_flush = L2CA_FlushChannel(cid, L2CAP_FLUSH_CHANS_GET);
- if (packets_to_flush) {
- VLOG(2) << left->address << " skipping " << packets_to_flush
- << " packets";
- left->audio_stats.packet_flush_count += packets_to_flush;
- left->audio_stats.frame_flush_count++;
+ uint16_t packets_in_chans = L2CA_FlushChannel(cid, L2CAP_FLUSH_CHANS_GET);
+ if (packets_in_chans) {
+ // Compare the two sides LE CoC credit value to confirm need to drop or
+ // skip audio packet.
+ if (NeedToDropPacket(left, right)) {
+ LOG(INFO) << left->address << " triggers dropping, "
+ << packets_in_chans << " packets in channel";
+ need_drop = true;
+ left->audio_stats.trigger_drop_count++;
+ } else {
+ LOG(INFO) << left->address << " skipping " << packets_in_chans
+ << " packets";
+ left->audio_stats.packet_flush_count += packets_in_chans;
+ left->audio_stats.frame_flush_count++;
+ L2CA_FlushChannel(cid, 0xffff);
+ }
hearingDevices.StartRssiLog();
}
- // flush all packets stuck in queue
- L2CA_FlushChannel(cid, 0xffff);
check_and_do_rssi_read(left);
}
@@ -1253,16 +1305,24 @@ class HearingAidImpl : public HearingAid {
encoded_data_right.resize(encoded_size);
uint16_t cid = GAP_ConnGetL2CAPCid(right->gap_handle);
- uint16_t packets_to_flush = L2CA_FlushChannel(cid, L2CAP_FLUSH_CHANS_GET);
- if (packets_to_flush) {
- VLOG(2) << right->address << " skipping " << packets_to_flush
- << " packets";
- right->audio_stats.packet_flush_count += packets_to_flush;
- right->audio_stats.frame_flush_count++;
+ uint16_t packets_in_chans = L2CA_FlushChannel(cid, L2CAP_FLUSH_CHANS_GET);
+ if (packets_in_chans) {
+ // Compare the two sides LE CoC credit value to confirm need to drop or
+ // skip audio packet.
+ if (NeedToDropPacket(right, left)) {
+ LOG(INFO) << right->address << " triggers dropping, "
+ << packets_in_chans << " packets in channel";
+ need_drop = true;
+ right->audio_stats.trigger_drop_count++;
+ } else {
+ LOG(INFO) << right->address << " skipping " << packets_in_chans
+ << " packets";
+ right->audio_stats.packet_flush_count += packets_in_chans;
+ right->audio_stats.frame_flush_count++;
+ L2CA_FlushChannel(cid, 0xffff);
+ }
hearingDevices.StartRssiLog();
}
- // flush all packets stuck in queue
- L2CA_FlushChannel(cid, 0xffff);
check_and_do_rssi_read(right);
}
@@ -1272,6 +1332,16 @@ class HearingAidImpl : public HearingAid {
uint16_t packet_size =
CalcCompressedAudioPacketSize(codec_in_use, default_data_interval_ms);
+ if (need_drop) {
+ if (left) {
+ left->audio_stats.packet_drop_count++;
+ }
+ if (right) {
+ right->audio_stats.packet_drop_count++;
+ }
+ return;
+ }
+
for (size_t i = 0; i < encoded_data_size; i += packet_size) {
if (left) {
left->audio_stats.packet_send_count++;
@@ -1324,7 +1394,11 @@ class HearingAidImpl : public HearingAid {
RawAddress address = *GAP_ConnGetRemoteAddr(gap_handle);
uint16_t tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
- LOG(INFO) << "GAP_EVT_CONN_OPENED " << address << ", tx_mtu=" << tx_mtu;
+ init_credit =
+ L2CA_GetPeerLECocCredit(address, GAP_ConnGetL2CAPCid(gap_handle));
+
+ LOG(INFO) << "GAP_EVT_CONN_OPENED " << address << ", tx_mtu=" << tx_mtu
+ << ", init_credit=" << init_credit;
OnGapConnection(address);
break;
}
@@ -1439,10 +1513,14 @@ class HearingAidImpl : public HearingAid {
<< (side ? "right" : "left") << " " << loghex(device.hi_sync_id)
<< std::endl;
stream
- << " Packet counts (enqueued/flushed) : "
+ << " Trigger dropped counts : "
+ << device.audio_stats.trigger_drop_count
+ << "\n Packet dropped counts : "
+ << device.audio_stats.packet_drop_count
+ << "\n Packet counts (send/flush) : "
<< device.audio_stats.packet_send_count << " / "
<< device.audio_stats.packet_flush_count
- << "\n Frame counts (enqueued/flushed) : "
+ << "\n Frame counts (sent/flush) : "
<< device.audio_stats.frame_send_count << " / "
<< device.audio_stats.frame_flush_count << std::endl;
@@ -1602,6 +1680,8 @@ class HearingAidImpl : public HearingAid {
uint16_t default_data_interval_ms;
+ uint16_t init_credit;
+
HearingDevices hearingDevices;
void find_server_changed_ccc_handle(uint16_t conn_id,
diff --git a/bta/include/bta_hearing_aid_api.h b/bta/include/bta_hearing_aid_api.h
index 613802a21..6fdd101a0 100644
--- a/bta/include/bta_hearing_aid_api.h
+++ b/bta/include/bta_hearing_aid_api.h
@@ -70,19 +70,23 @@ struct rssi_log {
};
struct AudioStats {
- size_t packet_flush_count;
+ size_t trigger_drop_count;
+ size_t packet_drop_count;
size_t packet_send_count;
- size_t frame_flush_count;
+ size_t packet_flush_count;
size_t frame_send_count;
+ size_t frame_flush_count;
std::deque<rssi_log> rssi_history;
AudioStats() { Reset(); }
void Reset() {
- packet_flush_count = 0;
+ trigger_drop_count = 0;
+ packet_drop_count = 0;
packet_send_count = 0;
- frame_flush_count = 0;
+ packet_flush_count = 0;
frame_send_count = 0;
+ frame_flush_count = 0;
}
};
diff --git a/btif/avrcp/avrcp_service.cc b/btif/avrcp/avrcp_service.cc
index 51d66feac..9a4af3651 100644
--- a/btif/avrcp/avrcp_service.cc
+++ b/btif/avrcp/avrcp_service.cc
@@ -297,6 +297,13 @@ void AvrcpService::Init(MediaInterface* media_interface,
profile_version, 0);
btif_dm_add_uuid_to_eir(UUID_SERVCLASS_AV_REM_CTRL_TARGET);
+ ct_sdp_record_handle = SDP_CreateRecord();
+
+ avrcp_interface_.AddRecord(UUID_SERVCLASS_AV_REMOTE_CONTROL,
+ "AV Remote Control", NULL, AVRCP_SUPF_TG_CT,
+ ct_sdp_record_handle, false, AVRC_REV_1_3, 0);
+ btif_dm_add_uuid_to_eir(UUID_SERVCLASS_AV_REMOTE_CONTROL);
+
media_interface_ = new MediaInterfaceWrapper(media_interface);
media_interface->RegisterUpdateCallback(instance_);
@@ -333,6 +340,9 @@ void AvrcpService::Cleanup() {
avrcp_interface_.RemoveRecord(sdp_record_handle);
btif_dm_remove_uuid_from_eir(UUID_SERVCLASS_AV_REM_CTRL_TARGET);
sdp_record_handle = -1;
+ avrcp_interface_.RemoveRecord(ct_sdp_record_handle);
+ btif_dm_remove_uuid_from_eir(UUID_SERVCLASS_AV_REMOTE_CONTROL);
+ ct_sdp_record_handle = -1;
connection_handler_->CleanUp();
connection_handler_ = nullptr;
diff --git a/btif/avrcp/avrcp_service.h b/btif/avrcp/avrcp_service.h
index 067631ecd..4898bbf54 100644
--- a/btif/avrcp/avrcp_service.h
+++ b/btif/avrcp/avrcp_service.h
@@ -98,6 +98,7 @@ class AvrcpService : public MediaCallbacks {
static ServiceInterfaceImpl* service_interface_;
uint32_t sdp_record_handle = -1;
+ uint32_t ct_sdp_record_handle = -1;
uint16_t profile_version = -1;
MediaInterface* media_interface_ = nullptr;
diff --git a/gd/cert/ble_lib.py b/gd/cert/ble_lib.py
new file mode 100644
index 000000000..20eeb81cd
--- /dev/null
+++ b/gd/cert/ble_lib.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 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.
+"""
+Ble libraries
+"""
+
+import time
+import queue
+import logging
+
+from cert.bt_constants import ble_advertise_settings_modes
+from cert.bt_constants import small_timeout
+from cert.bt_constants import adv_fail
+from cert.bt_constants import adv_succ
+from cert.bt_constants import advertising_set_on_own_address_read
+from cert.bt_constants import advertising_set_started
+from cert.bt_constants import bluetooth_on
+from cert.bt_constants import bluetooth_off
+from cert.bt_constants import bt_default_timeout
+
+
+def enable_bluetooth(droid, ed):
+ if droid.bluetoothCheckState():
+ return True
+
+ droid.bluetoothToggleState(True)
+ expected_bluetooth_on_event_name = bluetooth_on
+ try:
+ ed.pop_event(expected_bluetooth_on_event_name, bt_default_timeout)
+ except Exception:
+ logging.info("Failed to toggle Bluetooth on (no broadcast received)")
+ if droid.bluetoothCheckState():
+ logging.info(".. actual state is ON")
+ return True
+ logging.info(".. actual state is OFF")
+ return False
+
+ return True
+
+
+def disable_bluetooth(droid, ed):
+ if not droid.bluetoothCheckState():
+ return True
+ droid.bluetoothToggleState(False)
+ expected_bluetooth_off_event_name = bluetooth_off
+ try:
+ ed.pop_event(expected_bluetooth_off_event_name, bt_default_timeout)
+ except Exception:
+ logging.info("Failed to toggle Bluetooth off (no broadcast received)")
+ if droid.bluetoothCheckState():
+ logging.info(".. actual state is ON")
+ return False
+ logging.info(".. actual state is OFF")
+ return True
+ return True
+
+
+def generate_ble_scan_objects(droid):
+ """Generate generic LE scan objects.
+
+ Args:
+ droid: The droid object to generate LE scan objects from.
+
+ Returns:
+ filter_list: The generated scan filter list id.
+ scan_settings: The generated scan settings id.
+ scan_callback: The generated scan callback id.
+ """
+ filter_list = droid.bleGenFilterList()
+ scan_settings = droid.bleBuildScanSetting()
+ scan_callback = droid.bleGenScanCallback()
+ return filter_list, scan_settings, scan_callback
+
+
+def generate_ble_advertise_objects(droid):
+ """Generate generic LE advertise objects.
+
+ Args:
+ droid: The droid object to generate advertise LE objects from.
+
+ Returns:
+ advertise_callback: The generated advertise callback id.
+ advertise_data: The generated advertise data id.
+ advertise_settings: The generated advertise settings id.
+ """
+ advertise_callback = droid.bleGenBleAdvertiseCallback()
+ advertise_data = droid.bleBuildAdvertiseData()
+ advertise_settings = droid.bleBuildAdvertiseSettings()
+ return advertise_callback, advertise_data, advertise_settings
+
+
+class BleLib():
+
+ def __init__(self, log, dut):
+ self.advertisement_list = []
+ self.dut = dut
+ self.log = log
+ self.default_timeout = 5
+ self.set_advertisement_list = []
+ self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb"
+
+ def _verify_ble_adv_started(self, advertise_callback):
+ """Helper for verifying if an advertisment started or not"""
+ regex = "({}|{})".format(adv_succ.format(advertise_callback), adv_fail.format(advertise_callback))
+ try:
+ event = self.dut.ed.pop_events(regex, 5, small_timeout)
+ except queue.Empty:
+ self.dut.log.error("Failed to get success or failed event.")
+ return
+ if event[0]["name"] == adv_succ.format(advertise_callback):
+ self.dut.log.info("Advertisement started successfully.")
+ return True
+ else:
+ self.dut.log.info("Advertisement failed to start.")
+ return False
+
+ def start_generic_connectable_advertisement(self, line):
+ """Start a connectable LE advertisement"""
+ scan_response = None
+ if line:
+ scan_response = bool(line)
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ advertise_callback, advertise_data, advertise_settings = (generate_ble_advertise_objects(self.dut.droid))
+ if scan_response:
+ self.dut.droid.bleStartBleAdvertisingWithScanResponse(advertise_callback, advertise_data,
+ advertise_settings, advertise_data)
+ else:
+ self.dut.droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info("Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+
+ def start_connectable_advertisement_set(self, line):
+ """Start Connectable Advertisement Set"""
+ adv_callback = self.dut.droid.bleAdvSetGenCallback()
+ adv_data = {
+ "includeDeviceName": True,
+ }
+ self.dut.droid.bleAdvSetStartAdvertisingSet({
+ "connectable": True,
+ "legacyMode": False,
+ "primaryPhy": "PHY_LE_1M",
+ "secondaryPhy": "PHY_LE_1M",
+ "interval": 320
+ }, adv_data, None, None, None, 0, 0, adv_callback)
+ evt = self.dut.ed.pop_event(advertising_set_started.format(adv_callback), self.default_timeout)
+ set_id = evt['data']['setId']
+ self.log.error("did not receive the set started event!")
+ evt = self.dut.ed.pop_event(advertising_set_on_own_address_read.format(set_id), self.default_timeout)
+ address = evt['data']['address']
+ self.log.info("Advertiser address is: {}".format(str(address)))
+ self.set_advertisement_list.append(adv_callback)
+
+ def stop_all_advertisement_set(self, line):
+ """Stop all Advertisement Sets"""
+ for adv in self.set_advertisement_list:
+ try:
+ self.dut.droid.bleAdvSetStopAdvertisingSet(adv)
+ except Exception as err:
+ self.log.error("Failed to stop advertisement: {}".format(err))
+
+ def adv_add_service_uuid_list(self, line):
+ """Add service UUID to the LE advertisement inputs:
+ [uuid1 uuid2 ... uuidN]"""
+ uuids = line.split()
+ uuid_list = []
+ for uuid in uuids:
+ if len(uuid) == 4:
+ uuid = self.generic_uuid.format(line)
+ uuid_list.append(uuid)
+ self.dut.droid.bleSetAdvertiseDataSetServiceUuids(uuid_list)
+
+ def adv_data_include_local_name(self, is_included):
+ """Include local name in the advertisement. inputs: [true|false]"""
+ self.dut.droid.bleSetAdvertiseDataIncludeDeviceName(bool(is_included))
+
+ def adv_data_include_tx_power_level(self, is_included):
+ """Include tx power level in the advertisement. inputs: [true|false]"""
+ self.dut.droid.bleSetAdvertiseDataIncludeTxPowerLevel(bool(is_included))
+
+ def adv_data_add_manufacturer_data(self, line):
+ """Include manufacturer id and data to the advertisment:
+ [id data1 data2 ... dataN]"""
+ info = line.split()
+ manu_id = int(info[0])
+ manu_data = []
+ for data in info[1:]:
+ manu_data.append(int(data))
+ self.dut.droid.bleAddAdvertiseDataManufacturerId(manu_id, manu_data)
+
+ def start_generic_nonconnectable_advertisement(self, line):
+ """Start a nonconnectable LE advertisement"""
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(False)
+ advertise_callback, advertise_data, advertise_settings = (generate_ble_advertise_objects(self.dut.droid))
+ self.dut.droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info("Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+
+ def stop_all_advertisements(self, line):
+ """Stop all LE advertisements"""
+ for callback_id in self.advertisement_list:
+ self.log.info("Stopping Advertisement {}".format(callback_id))
+ self.dut.droid.bleStopBleAdvertising(callback_id)
+ time.sleep(1)
+ self.advertisement_list = []
+
+ def ble_stop_advertisement(self, callback_id):
+ """Stop an LE advertisement"""
+ if not callback_id:
+ self.log.info("Need a callback ID")
+ return
+ callback_id = int(callback_id)
+ if callback_id not in self.advertisement_list:
+ self.log.info("Callback not in list of advertisements.")
+ return
+ self.dut.droid.bleStopBleAdvertising(callback_id)
+ self.advertisement_list.remove(callback_id)
+
+ def start_max_advertisements(self, line):
+ scan_response = None
+ if line:
+ scan_response = bool(line)
+ while (True):
+ try:
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ advertise_callback, advertise_data, advertise_settings = (generate_ble_advertise_objects(
+ self.dut.droid))
+ if scan_response:
+ self.dut.droid.bleStartBleAdvertisingWithScanResponse(advertise_callback, advertise_data,
+ advertise_settings, advertise_data)
+ else:
+ self.dut.droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info("Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+ else:
+ self.log.info("Advertisements active: {}".format(len(self.advertisement_list)))
+ return False
+ except Exception as err:
+ self.log.info("Advertisements active: {}".format(len(self.advertisement_list)))
+ return True
diff --git a/gd/cert/bt_constants.py b/gd/cert/bt_constants.py
new file mode 100644
index 000000000..f144ef57f
--- /dev/null
+++ b/gd/cert/bt_constants.py
@@ -0,0 +1,740 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 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.
+
+### Generic Constants Begin ###
+
+bt_default_timeout = 15
+default_rfcomm_timeout_ms = 10000
+default_bluetooth_socket_timeout_ms = 10000
+pan_connect_timeout = 5
+bt_discovery_timeout = 3
+small_timeout = 0.0001
+
+# Time delay (in seconds) at the end of each LE CoC Test to give sufficient time
+# for the ACL LE link to be disconnected. The ACL link stays connected after
+# L2CAP disconnects. An example of the timeout is L2CAP_LINK_INACTIVITY_TOUT.
+# This delay must be greater than the maximum of these timeouts.
+# TODO: Investigate the use of broadcast intent
+# BluetoothDevice.ACTION_ACL_DISCONNECTED to replace this delay method.
+l2cap_max_inactivity_delay_after_disconnect = 5
+
+# LE specifications related constants
+le_connection_interval_time_step_ms = 1.25
+le_default_supervision_timeout = 2000
+default_le_data_length = 23
+default_le_connection_interval_ms = 30
+le_connection_event_time_step_ms = 0.625
+
+# Headers of LE L2CAP Connection-oriented Channels. See section 3.4, Vol
+# 3, Part A, Version 5.0.
+l2cap_header_size = 4
+l2cap_coc_sdu_length_field_size = 2
+l2cap_coc_header_size = l2cap_header_size + l2cap_coc_sdu_length_field_size
+
+java_integer = {"min": -2147483648, "max": 2147483647}
+
+btsnoop_log_path_on_device = "/data/misc/bluetooth/logs/btsnoop_hci.log"
+btsnoop_last_log_path_on_device = \
+ "/data/misc/bluetooth/logs/btsnoop_hci.log.last"
+pairing_variant_passkey_confirmation = 2
+
+# Callback strings
+scan_result = "BleScan{}onScanResults"
+scan_failed = "BleScan{}onScanFailed"
+batch_scan_result = "BleScan{}onBatchScanResult"
+adv_fail = "BleAdvertise{}onFailure"
+adv_succ = "BleAdvertise{}onSuccess"
+bluetooth_off = "BluetoothStateChangedOff"
+bluetooth_on = "BluetoothStateChangedOn"
+mtu_changed = "GattConnect{}onMtuChanged"
+advertising_set_started = "AdvertisingSet{}onAdvertisingSetStarted"
+advertising_set_stopped = "AdvertisingSet{}onAdvertisingSetStopped"
+advertising_set_on_own_address_read = "AdvertisingSet{}onOwnAddressRead"
+advertising_set_enabled = "AdvertisingSet{}onAdvertisingEnabled"
+advertising_set_data_set = "AdvertisingSet{}onAdvertisingDataSet"
+advertising_set_scan_response_set = "AdvertisingSet{}onScanResponseDataSet"
+advertising_set_parameters_update = \
+ "AdvertisingSet{}onAdvertisingParametersUpdated"
+advertising_set_periodic_parameters_updated = \
+ "AdvertisingSet{}onPeriodicAdvertisingParametersUpdated"
+advertising_set_periodic_data_set = \
+ "AdvertisingSet{}onPeriodicAdvertisingDataSet"
+advertising_set_periodic_enable = "AdvertisingSet{}onPeriodicAdvertisingEnable"
+bluetooth_profile_connection_state_changed = \
+ "BluetoothProfileConnectionStateChanged"
+bluetooth_le_on = "BleStateChangedOn"
+bluetooth_le_off = "BleStateChangedOff"
+bluetooth_a2dp_codec_config_changed = "BluetoothA2dpCodecConfigChanged"
+# End Callback Strings
+
+batch_scan_not_supported_list = [
+ "Nexus 4",
+ "Nexus 5",
+ "Nexus 7",
+]
+
+### Generic Constants End ###
+
+### Bluetooth Constants Begin ###
+
+# rfcomm test uuids
+rfcomm_secure_uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66"
+rfcomm_insecure_uuid = "8ce255c0-200a-11e0-ac64-0800200c9a66"
+
+# bluetooth socket connection test uuid
+bluetooth_socket_conn_test_uuid = "12345678-1234-5678-9abc-123456789abc"
+
+# Bluetooth Adapter Scan Mode Types
+bt_scan_mode_types = {"state_off": -1, "none": 0, "connectable": 1, "connectable_discoverable": 3}
+
+# Bluetooth Adapter State Constants
+bt_adapter_states = {
+ "off": 10,
+ "turning_on": 11,
+ "on": 12,
+ "turning_off": 13,
+ "ble_turning_on": 14,
+ "ble_on": 15,
+ "ble_turning_off": 16
+}
+
+# Should be kept in sync with BluetoothProfile.java
+bt_profile_constants = {
+ "headset": 1,
+ "a2dp": 2,
+ "health": 3,
+ "input_device": 4,
+ "pan": 5,
+ "pbap_server": 6,
+ "gatt": 7,
+ "gatt_server": 8,
+ "map": 9,
+ "sap": 10,
+ "a2dp_sink": 11,
+ "avrcp_controller": 12,
+ "headset_client": 16,
+ "pbap_client": 17,
+ "map_mce": 18
+}
+
+# Bluetooth RFCOMM UUIDs as defined by the SIG
+bt_rfcomm_uuids = {
+ "default_uuid": "457807c0-4897-11df-9879-0800200c9a66",
+ "base_uuid": "00000000-0000-1000-8000-00805F9B34FB",
+ "sdp": "00000001-0000-1000-8000-00805F9B34FB",
+ "udp": "00000002-0000-1000-8000-00805F9B34FB",
+ "rfcomm": "00000003-0000-1000-8000-00805F9B34FB",
+ "tcp": "00000004-0000-1000-8000-00805F9B34FB",
+ "tcs_bin": "00000005-0000-1000-8000-00805F9B34FB",
+ "tcs_at": "00000006-0000-1000-8000-00805F9B34FB",
+ "att": "00000007-0000-1000-8000-00805F9B34FB",
+ "obex": "00000008-0000-1000-8000-00805F9B34FB",
+ "ip": "00000009-0000-1000-8000-00805F9B34FB",
+ "ftp": "0000000A-0000-1000-8000-00805F9B34FB",
+ "http": "0000000C-0000-1000-8000-00805F9B34FB",
+ "wsp": "0000000E-0000-1000-8000-00805F9B34FB",
+ "bnep": "0000000F-0000-1000-8000-00805F9B34FB",
+ "upnp": "00000010-0000-1000-8000-00805F9B34FB",
+ "hidp": "00000011-0000-1000-8000-00805F9B34FB",
+ "hardcopy_control_channel": "00000012-0000-1000-8000-00805F9B34FB",
+ "hardcopy_data_channel": "00000014-0000-1000-8000-00805F9B34FB",
+ "hardcopy_notification": "00000016-0000-1000-8000-00805F9B34FB",
+ "avctp": "00000017-0000-1000-8000-00805F9B34FB",
+ "avdtp": "00000019-0000-1000-8000-00805F9B34FB",
+ "cmtp": "0000001B-0000-1000-8000-00805F9B34FB",
+ "mcap_control_channel": "0000001E-0000-1000-8000-00805F9B34FB",
+ "mcap_data_channel": "0000001F-0000-1000-8000-00805F9B34FB",
+ "l2cap": "00000100-0000-1000-8000-00805F9B34FB"
+}
+
+# Should be kept in sync with BluetoothProfile#STATE_* constants.
+bt_profile_states = {"disconnected": 0, "connecting": 1, "connected": 2, "disconnecting": 3}
+
+# Access Levels from BluetoothDevice.
+bt_access_levels = {"access_allowed": 1, "access_denied": 2}
+
+# Priority levels as defined in BluetoothProfile.java.
+bt_priority_levels = {"auto_connect": 1000, "on": 100, "off": 0, "undefined": -1}
+
+# A2DP codec configuration constants as defined in
+# frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java
+codec_types = {'SBC': 0, 'AAC': 1, 'APTX': 2, 'APTX-HD': 3, 'LDAC': 4, 'MAX': 5, 'INVALID': 1000000}
+
+codec_priorities = {'DISABLED': -1, 'DEFAULT': 0, 'HIGHEST': 1000000}
+
+sample_rates = {
+ 'NONE': 0,
+ '44100': 0x1 << 0,
+ '48000': 0x1 << 1,
+ '88200': 0x1 << 2,
+ '96000': 0x1 << 3,
+ '176400': 0x1 << 4,
+ '192000': 0x1 << 5
+}
+
+bits_per_samples = {'NONE': 0, '16': 0x1 << 0, '24': 0x1 << 1, '32': 0x1 << 2}
+
+channel_modes = {'NONE': 0, 'MONO': 0x1 << 0, 'STEREO': 0x1 << 1}
+
+# Bluetooth HID constants.
+hid_connection_timeout = 5
+
+# Bluetooth HID EventFacade constants.
+hid_on_set_report_event = "onSetReport"
+hid_on_get_report_event = "onGetReport"
+hid_on_set_protocol_event = "onSetProtocol"
+hid_on_intr_data_event = "onInterruptData"
+hid_on_virtual_cable_unplug_event = "onVirtualCableUnplug"
+hid_id_keyboard = 1
+hid_id_mouse = 2
+hid_default_event_timeout = 15
+hid_default_set_report_payload = "Haha"
+
+### Bluetooth Constants End ###
+
+### Bluetooth Low Energy Constants Begin ###
+
+# Bluetooth Low Energy address types
+ble_address_types = {"public": 0, "random": 1}
+
+# Bluetooth Low Energy scan callback types
+ble_scan_settings_callback_types = {"all_matches": 1, "first_match": 2, "match_lost": 4, "found_and_lost": 6}
+
+# Bluetooth Low Energy scan settings match mode
+ble_scan_settings_match_modes = {"aggresive": 1, "sticky": 2}
+
+# Bluetooth Low Energy scan settings match nums
+ble_scan_settings_match_nums = {"one": 1, "few": 2, "max": 3}
+
+# Bluetooth Low Energy scan settings result types
+ble_scan_settings_result_types = {"full": 0, "abbreviated": 1}
+
+# Bluetooth Low Energy scan settings mode
+ble_scan_settings_modes = {
+ "opportunistic": -1,
+ "low_power": 0,
+ "balanced": 1,
+ "low_latency": 2,
+ "ambient_discovery": 3,
+}
+
+# Bluetooth Low Energy scan settings report delay millis
+ble_scan_settings_report_delay_milli_seconds = {"min": 0, "max": 9223372036854775807}
+
+# Bluetooth Low Energy scan settings phy
+ble_scan_settings_phys = {"1m": 1, "2m": 2, "coded": 3, "all_supported": 255}
+
+# Bluetooth Low Energy advertise settings types
+ble_advertise_settings_types = {"non_connectable": 0, "connectable": 1}
+
+# Bluetooth Low Energy advertise settings modes
+ble_advertise_settings_modes = {"low_power": 0, "balanced": 1, "low_latency": 2}
+
+# Bluetooth Low Energy advertise settings tx power
+ble_advertise_settings_tx_powers = {"ultra_low": 0, "low": 1, "medium": 2, "high": 3}
+
+# Bluetooth Low Energy service uuids for specific devices
+ble_uuids = {"p_service": "0000feef-0000-1000-8000-00805f9b34fb", "hr_service": "0000180d-0000-1000-8000-00805f9b34fb"}
+
+# Bluetooth Low Energy advertising error codes
+ble_advertise_error_code = {
+ "data_too_large": 1,
+ "too_many_advertisers": 2,
+ "advertisement_already_started": 3,
+ "bluetooth_internal_failure": 4,
+ "feature_not_supported": 5
+}
+
+### Bluetooth Low Energy Constants End ###
+
+### Bluetooth GATT Constants Begin ###
+
+# Gatt Callback error messages
+gatt_cb_err = {
+ "char_write_req_err": "Characteristic Write Request event not found. Expected {}",
+ "char_write_err": "Characteristic Write event not found. Expected {}",
+ "desc_write_req_err": "Descriptor Write Request event not found. Expected {}",
+ "desc_write_err": "Descriptor Write event not found. Expected {}",
+ "char_read_err": "Characteristic Read event not found. Expected {}",
+ "char_read_req_err": "Characteristic Read Request not found. Expected {}",
+ "desc_read_err": "Descriptor Read event not found. Expected {}",
+ "desc_read_req_err": "Descriptor Read Request event not found. Expected {}",
+ "rd_remote_rssi_err": "Read Remote RSSI event not found. Expected {}",
+ "gatt_serv_disc_err": "GATT Services Discovered event not found. Expected {}",
+ "serv_added_err": "Service Added event not found. Expected {}",
+ "mtu_changed_err": "MTU Changed event not found. Expected {}",
+ "mtu_serv_changed_err": "MTU Server Changed event not found. Expected {}",
+ "gatt_conn_changed_err": "GATT Connection Changed event not found. Expected {}",
+ "char_change_err": "GATT Characteristic Changed event not fond. Expected {}",
+ "phy_read_err": "Phy Read event not fond. Expected {}",
+ "phy_update_err": "Phy Update event not fond. Expected {}",
+ "exec_write_err": "GATT Execute Write event not found. Expected {}"
+}
+
+# GATT callback strings as defined in GattClientFacade.java and
+# GattServerFacade.java implemented callbacks.
+gatt_cb_strings = {
+ "char_write_req": "GattServer{}onCharacteristicWriteRequest",
+ "exec_write": "GattServer{}onExecuteWrite",
+ "char_write": "GattConnect{}onCharacteristicWrite",
+ "desc_write_req": "GattServer{}onDescriptorWriteRequest",
+ "desc_write": "GattConnect{}onDescriptorWrite",
+ "char_read": "GattConnect{}onCharacteristicRead",
+ "char_read_req": "GattServer{}onCharacteristicReadRequest",
+ "desc_read": "GattConnect{}onDescriptorRead",
+ "desc_read_req": "GattServer{}onDescriptorReadRequest",
+ "rd_remote_rssi": "GattConnect{}onReadRemoteRssi",
+ "gatt_serv_disc": "GattConnect{}onServicesDiscovered",
+ "serv_added": "GattServer{}onServiceAdded",
+ "mtu_changed": "GattConnect{}onMtuChanged",
+ "mtu_serv_changed": "GattServer{}onMtuChanged",
+ "gatt_conn_change": "GattConnect{}onConnectionStateChange",
+ "char_change": "GattConnect{}onCharacteristicChanged",
+ "phy_read": "GattConnect{}onPhyRead",
+ "phy_update": "GattConnect{}onPhyUpdate",
+ "serv_phy_read": "GattServer{}onPhyRead",
+ "serv_phy_update": "GattServer{}onPhyUpdate",
+}
+
+# GATT event dictionary of expected callbacks and errors.
+gatt_event = {
+ "char_write_req": {
+ "evt": gatt_cb_strings["char_write_req"],
+ "err": gatt_cb_err["char_write_req_err"]
+ },
+ "exec_write": {
+ "evt": gatt_cb_strings["exec_write"],
+ "err": gatt_cb_err["exec_write_err"]
+ },
+ "char_write": {
+ "evt": gatt_cb_strings["char_write"],
+ "err": gatt_cb_err["char_write_err"]
+ },
+ "desc_write_req": {
+ "evt": gatt_cb_strings["desc_write_req"],
+ "err": gatt_cb_err["desc_write_req_err"]
+ },
+ "desc_write": {
+ "evt": gatt_cb_strings["desc_write"],
+ "err": gatt_cb_err["desc_write_err"]
+ },
+ "char_read": {
+ "evt": gatt_cb_strings["char_read"],
+ "err": gatt_cb_err["char_read_err"]
+ },
+ "char_read_req": {
+ "evt": gatt_cb_strings["char_read_req"],
+ "err": gatt_cb_err["char_read_req_err"]
+ },
+ "desc_read": {
+ "evt": gatt_cb_strings["desc_read"],
+ "err": gatt_cb_err["desc_read_err"]
+ },
+ "desc_read_req": {
+ "evt": gatt_cb_strings["desc_read_req"],
+ "err": gatt_cb_err["desc_read_req_err"]
+ },
+ "rd_remote_rssi": {
+ "evt": gatt_cb_strings["rd_remote_rssi"],
+ "err": gatt_cb_err["rd_remote_rssi_err"]
+ },
+ "gatt_serv_disc": {
+ "evt": gatt_cb_strings["gatt_serv_disc"],
+ "err": gatt_cb_err["gatt_serv_disc_err"]
+ },
+ "serv_added": {
+ "evt": gatt_cb_strings["serv_added"],
+ "err": gatt_cb_err["serv_added_err"]
+ },
+ "mtu_changed": {
+ "evt": gatt_cb_strings["mtu_changed"],
+ "err": gatt_cb_err["mtu_changed_err"]
+ },
+ "gatt_conn_change": {
+ "evt": gatt_cb_strings["gatt_conn_change"],
+ "err": gatt_cb_err["gatt_conn_changed_err"]
+ },
+ "char_change": {
+ "evt": gatt_cb_strings["char_change"],
+ "err": gatt_cb_err["char_change_err"]
+ },
+ "phy_read": {
+ "evt": gatt_cb_strings["phy_read"],
+ "err": gatt_cb_err["phy_read_err"]
+ },
+ "phy_update": {
+ "evt": gatt_cb_strings["phy_update"],
+ "err": gatt_cb_err["phy_update_err"]
+ },
+ "serv_phy_read": {
+ "evt": gatt_cb_strings["serv_phy_read"],
+ "err": gatt_cb_err["phy_read_err"]
+ },
+ "serv_phy_update": {
+ "evt": gatt_cb_strings["serv_phy_update"],
+ "err": gatt_cb_err["phy_update_err"]
+ }
+}
+
+# Matches constants of connection states defined in BluetoothGatt.java
+gatt_connection_state = {"disconnected": 0, "connecting": 1, "connected": 2, "disconnecting": 3, "closed": 4}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattCharacteristic.java
+gatt_characteristic = {
+ "property_broadcast": 0x01,
+ "property_read": 0x02,
+ "property_write_no_response": 0x04,
+ "property_write": 0x08,
+ "property_notify": 0x10,
+ "property_indicate": 0x20,
+ "property_signed_write": 0x40,
+ "property_extended_props": 0x80,
+ "permission_read": 0x01,
+ "permission_read_encrypted": 0x02,
+ "permission_read_encrypted_mitm": 0x04,
+ "permission_write": 0x10,
+ "permission_write_encrypted": 0x20,
+ "permission_write_encrypted_mitm": 0x40,
+ "permission_write_signed": 0x80,
+ "permission_write_signed_mitm": 0x100,
+ "write_type_default": 0x02,
+ "write_type_no_response": 0x01,
+ "write_type_signed": 0x04,
+}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattDescriptor.java
+gatt_descriptor = {
+ "enable_notification_value": [0x01, 0x00],
+ "enable_indication_value": [0x02, 0x00],
+ "disable_notification_value": [0x00, 0x00],
+ "permission_read": 0x01,
+ "permission_read_encrypted": 0x02,
+ "permission_read_encrypted_mitm": 0x04,
+ "permission_write": 0x10,
+ "permission_write_encrypted": 0x20,
+ "permission_write_encrypted_mitm": 0x40,
+ "permission_write_signed": 0x80,
+ "permission_write_signed_mitm": 0x100
+}
+
+# https://www.bluetooth.com/specifications/gatt/descriptors
+gatt_char_desc_uuids = {
+ "char_ext_props": '00002900-0000-1000-8000-00805f9b34fb',
+ "char_user_desc": '00002901-0000-1000-8000-00805f9b34fb',
+ "client_char_cfg": '00002902-0000-1000-8000-00805f9b34fb',
+ "server_char_cfg": '00002903-0000-1000-8000-00805f9b34fb',
+ "char_fmt_uuid": '00002904-0000-1000-8000-00805f9b34fb',
+ "char_agreg_fmt": '00002905-0000-1000-8000-00805f9b34fb',
+ "char_valid_range": '00002906-0000-1000-8000-00805f9b34fb',
+ "external_report_reference": '00002907-0000-1000-8000-00805f9b34fb',
+ "report_reference": '00002908-0000-1000-8000-00805f9b34fb'
+}
+
+# https://www.bluetooth.com/specifications/gatt/characteristics
+gatt_char_types = {
+ "device_name": '00002a00-0000-1000-8000-00805f9b34fb',
+ "appearance": '00002a01-0000-1000-8000-00805f9b34fb',
+ "peripheral_priv_flag": '00002a02-0000-1000-8000-00805f9b34fb',
+ "reconnection_address": '00002a03-0000-1000-8000-00805f9b34fb',
+ "peripheral_pref_conn": '00002a04-0000-1000-8000-00805f9b34fb',
+ "service_changed": '00002a05-0000-1000-8000-00805f9b34fb',
+ "system_id": '00002a23-0000-1000-8000-00805f9b34fb',
+ "model_number_string": '00002a24-0000-1000-8000-00805f9b34fb',
+ "serial_number_string": '00002a25-0000-1000-8000-00805f9b34fb',
+ "firmware_revision_string": '00002a26-0000-1000-8000-00805f9b34fb',
+ "hardware_revision_string": '00002a27-0000-1000-8000-00805f9b34fb',
+ "software_revision_string": '00002a28-0000-1000-8000-00805f9b34fb',
+ "manufacturer_name_string": '00002a29-0000-1000-8000-00805f9b34fb',
+ "pnp_id": '00002a50-0000-1000-8000-00805f9b34fb',
+}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattCharacteristic.java
+gatt_characteristic_value_format = {
+ "string": 0x1,
+ "byte": 0x2,
+ "sint8": 0x21,
+ "uint8": 0x11,
+ "sint16": 0x22,
+ "unit16": 0x12,
+ "sint32": 0x24,
+ "uint32": 0x14
+}
+
+# Matches constants of Bluetooth Gatt Service types as defined in
+# BluetoothGattService.java
+gatt_service_types = {"primary": 0, "secondary": 1}
+
+# Matches constants of Bluetooth Gatt Connection Priority values as defined in
+# BluetoothGatt.java
+gatt_connection_priority = {"balanced": 0, "high": 1, "low_power": 2}
+
+# Min and max MTU values
+gatt_mtu_size = {"min": 23, "max": 217}
+
+# Gatt Characteristic attribute lengths
+gatt_characteristic_attr_length = {"attr_1": 1, "attr_2": 3, "attr_3": 15}
+
+# Matches constants of Bluetooth Gatt operations status as defined in
+# BluetoothGatt.java
+gatt_status = {"success": 0, "failure": 0x101}
+
+# Matches constants of Bluetooth transport values as defined in
+# BluetoothDevice.java
+gatt_transport = {"auto": 0x00, "bredr": 0x01, "le": 0x02}
+
+# Matches constants of Bluetooth physical channeling values as defined in
+# BluetoothDevice.java
+gatt_phy = {"1m": 1, "2m": 2, "le_coded": 3}
+
+# Matches constants of Bluetooth physical channeling bitmask values as defined
+# in BluetoothDevice.java
+gatt_phy_mask = {"1m_mask": 1, "2m_mask": 2, "coded_mask": 4}
+
+# Values as defiend in the Bluetooth GATT specification
+gatt_server_responses = {
+ "GATT_SUCCESS": 0x0,
+ "GATT_FAILURE": 0x1,
+ "GATT_READ_NOT_PERMITTED": 0x2,
+ "GATT_WRITE_NOT_PERMITTED": 0x3,
+ "GATT_INVALID_PDU": 0x4,
+ "GATT_INSUFFICIENT_AUTHENTICATION": 0x5,
+ "GATT_REQUEST_NOT_SUPPORTED": 0x6,
+ "GATT_INVALID_OFFSET": 0x7,
+ "GATT_INSUFFICIENT_AUTHORIZATION": 0x8,
+ "GATT_INVALID_ATTRIBUTE_LENGTH": 0xd,
+ "GATT_INSUFFICIENT_ENCRYPTION": 0xf,
+ "GATT_CONNECTION_CONGESTED": 0x8f,
+ "GATT_13_ERR": 0x13,
+ "GATT_12_ERR": 0x12,
+ "GATT_0C_ERR": 0x0C,
+ "GATT_16": 0x16
+}
+
+### Bluetooth GATT Constants End ###
+
+### Chameleon Constants Begin ###
+
+# Chameleon audio bits per sample.
+audio_bits_per_sample_16 = 16
+audio_bits_per_sample_24 = 24
+audio_bits_per_sample_32 = 32
+
+# Chameleon audio sample rates.
+audio_sample_rate_44100 = 44100
+audio_sample_rate_48000 = 48000
+audio_sample_rate_88200 = 88200
+audio_sample_rate_96000 = 96000
+
+# Chameleon audio channel modes.
+audio_channel_mode_mono = 1
+audio_channel_mode_stereo = 2
+audio_channel_mode_8 = 8
+
+# Chameleon time delays.
+delay_after_binding_seconds = 0.5
+delay_before_record_seconds = 0.5
+silence_wait_seconds = 5
+
+# Chameleon bus endpoints.
+fpga_linein_bus_endpoint = 'Chameleon FPGA line-in'
+headphone_bus_endpoint = 'Cros device headphone'
+
+### Chameleon Constants End ###
+
+# Begin logcat strings dict"""
+logcat_strings = {
+ "media_playback_vol_changed": "onRouteVolumeChanged",
+}
+
+# End logcat strings dict"""
+
+### Begin Service Discovery UUIDS ###
+# Values match the Bluetooth SIG defined values: """
+""" https://www.bluetooth.com/specifications/assigned-numbers/service-discovery """
+sig_uuid_constants = {
+ "BASE_UUID": "0000{}-0000-1000-8000-00805F9B34FB",
+ "SDP": "0001",
+ "UDP": "0002",
+ "RFCOMM": "0003",
+ "TCP": "0004",
+ "TCS-BIN": "0005",
+ "TCS-AT": "0006",
+ "ATT": "0007",
+ "OBEX": "0008",
+ "IP": "0009",
+ "FTP": "000A",
+ "HTTP": "000C",
+ "WSP": "000E",
+ "BNEP": "000F",
+ "UPNP": "0010",
+ "HIDP": "0011",
+ "HardcopyControlChannel": "0012",
+ "HardcopyDataChannel": "0014",
+ "HardcopyNotification": "0016",
+ "AVCTP": "0017",
+ "AVDTP": "0019",
+ "CMTP": "001B",
+ "MCAPControlChannel": "001E",
+ "MCAPDataChannel": "001F",
+ "L2CAP": "0100",
+ "ServiceDiscoveryServerServiceClassID": "1000",
+ "BrowseGroupDescriptorServiceClassID": "1001",
+ "SerialPort": "1101",
+ "LANAccessUsingPPP": "1102",
+ "DialupNetworking": "1103",
+ "IrMCSync": "1104",
+ "OBEXObjectPush": "1105",
+ "OBEXFileTransfer": "1106",
+ "IrMCSyncCommand": "1107",
+ "Headset": "1108",
+ "CordlessTelephony": "1109",
+ "AudioSource": "110A",
+ "AudioSink": "110B",
+ "A/V_RemoteControlTarget": "110C",
+ "AdvancedAudioDistribution": "110D",
+ "A/V_RemoteControl": "110E",
+ "A/V_RemoteControlController": "110F",
+ "Intercom": "1110",
+ "Fax": "1111",
+ "Headset - Audio Gateway (AG)": "1112",
+ "WAP": "1113",
+ "WAP_CLIENT": "1114",
+ "PANU": "1115",
+ "NAP": "1116",
+ "GN": "1117",
+ "DirectPrinting": "1118",
+ "ReferencePrinting": "1119",
+ "ImagingResponder": "111B",
+ "ImagingAutomaticArchive": "111C",
+ "ImagingReferencedObjects": "111D",
+ "Handsfree": "111E",
+ "HandsfreeAudioGateway": "111F",
+ "DirectPrintingReferenceObjectsService": "1120",
+ "ReflectedUI": "1121",
+ "BasicPrinting": "1122",
+ "PrintingStatus": "1123",
+ "HumanInterfaceDeviceService": "1124",
+ "HardcopyCableReplacement": "1125",
+ "HCR_Print": "1126",
+ "HCR_Scan": "1127",
+ "Common_ISDN_Access": "1128",
+ "SIM_Access": "112D",
+ "Phonebook Access - PCE": "112E",
+ "Phonebook Access - PSE": "112F",
+ "Phonebook Access": "1130",
+ "Headset - HS": "1131",
+ "Message Access Server": "1132",
+ "Message Notification Server": "1133",
+ "Message Access Profile": "1134",
+ "GNSS": "1135",
+ "GNSS_Server": "1136",
+ "PnPInformation": "1200",
+ "GenericNetworking": "1201",
+ "GenericFileTransfer": "1202",
+ "GenericAudio": "1203",
+ "GenericTelephony": "1204",
+ "UPNP_Service": "1205",
+ "UPNP_IP_Service": "1206",
+ "ESDP_UPNP_IP_PAN": "1300",
+ "ESDP_UPNP_IP_LAP": "1301",
+ "ESDP_UPNP_L2CAP": "1302",
+ "VideoSource": "1303",
+ "VideoSink": "1304",
+ "VideoDistribution": "1305",
+ "HDP": "1400"
+}
+
+### End Service Discovery UUIDS ###
+
+### Begin Appearance Constants ###
+# https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.gap.appearance.xml
+sig_appearance_constants = {
+ "UNKNOWN": 0,
+ "PHONE": 64,
+ "COMPUTER": 128,
+ "WATCH": 192,
+ "WATCH_SPORTS": 193,
+ "CLOCK": 256,
+ "DISPLAY": 320,
+ "REMOTE_CONTROL": 384,
+ "EYE_GLASSES": 448,
+ "TAG": 512,
+ "KEYRING": 576,
+ "MEDIA_PLAYER": 640,
+ "BARCODE_SCANNER": 704,
+ "THERMOMETER": 768,
+ "THERMOMETER_EAR": 769,
+ "HEART_RATE_SENSOR": 832,
+ "HEART_RATE_SENSOR_BELT": 833,
+ "BLOOD_PRESSURE": 896,
+ "BLOOD_PRESSURE_ARM": 897,
+ "BLOOD_PRESSURE_WRIST": 898,
+ "HID": 960,
+ "HID_KEYBOARD": 961,
+ "HID_MOUSE": 962,
+ "HID_JOYSTICK": 963,
+ "HID_GAMEPAD": 964,
+ "HID_DIGITIZER_TABLET": 965,
+ "HID_CARD_READER": 966,
+ "HID_DIGITAL_PEN": 967,
+ "HID_BARCODE_SCANNER": 968,
+ "GLUCOSE_METER": 1024,
+ "RUNNING_WALKING_SENSOR": 1088,
+ "RUNNING_WALKING_SENSOR_IN_SHOE": 1089,
+ "RUNNING_WALKING_SENSOR_ON_SHOE": 1090,
+ "RUNNING_WALKING_SENSOR_ON_HIP": 1091,
+ "CYCLING": 1152,
+ "CYCLING_COMPUTER": 1153,
+ "CYCLING_SPEED_SENSOR": 1154,
+ "CYCLING_CADENCE_SENSOR": 1155,
+ "CYCLING_POWER_SENSOR": 1156,
+ "CYCLING_SPEED_AND_CADENCE_SENSOR": 1157,
+ "PULSE_OXIMETER": 3136,
+ "PULSE_OXIMETER_FINGERTIP": 3137,
+ "PULSE_OXIMETER_WRIST": 3138,
+ "WEIGHT_SCALE": 3200,
+ "PERSONAL_MOBILITY": 3264,
+ "PERSONAL_MOBILITY_WHEELCHAIR": 3265,
+ "PERSONAL_MOBILITY_SCOOTER": 3266,
+ "GLUCOSE_MONITOR": 3328,
+ "SPORTS_ACTIVITY": 5184,
+ "SPORTS_ACTIVITY_LOCATION_DISPLAY": 5185,
+ "SPORTS_ACTIVITY_LOCATION_AND_NAV_DISPLAY": 5186,
+ "SPORTS_ACTIVITY_LOCATION_POD": 5187,
+ "SPORTS_ACTIVITY_LOCATION_AND_NAV_POD": 5188,
+}
+
+### End Appearance Constants ###
+
+# Attribute Record values from the Bluetooth Specification
+# Version 5, Vol 3, Part B
+bt_attribute_values = {
+ 'ATTR_SERVICE_RECORD_HANDLE': 0x0000,
+ 'ATTR_SERVICE_CLASS_ID_LIST': 0x0001,
+ 'ATTR_SERVICE_RECORD_STATE': 0x0002,
+ 'ATTR_SERVICE_ID': 0x0003,
+ 'ATTR_PROTOCOL_DESCRIPTOR_LIST': 0x0004,
+ 'ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST': 0x000D,
+ 'ATTR_BROWSE_GROUP_LIST': 0x0005,
+ 'ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST': 0x0006,
+ 'ATTR_SERVICE_INFO_TIME_TO_LIVE': 0x0007,
+ 'ATTR_SERVICE_AVAILABILITY': 0x0008,
+ 'ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST': 0x0009,
+ 'ATTR_A2DP_SUPPORTED_FEATURES': 0x0311,
+}
diff --git a/gd/cert/gd_base_test.py b/gd/cert/gd_base_test.py
index b382e6a22..4c02754f7 100644
--- a/gd/cert/gd_base_test.py
+++ b/gd/cert/gd_base_test.py
@@ -63,6 +63,7 @@ class GdBaseTestClass(BaseTestClass):
self.cert_module = self.info['cert_module']
self.rootcanal_running = self.info['rootcanal_running']
self.rootcanal_logpath = self.info['rootcanal_logpath']
+ self.rootcanal_logger = self.info['rootcanal_logger']
self.rootcanal_process = self.info['rootcanal_process']
if 'rootcanal' in self.controller_configs:
@@ -77,7 +78,6 @@ class GdBaseTestClass(BaseTestClass):
msg="Cannot start root-canal at " + str(self.info['rootcanal']))
asserts.assert_true(self.info['is_subprocess_alive'], msg="root-canal stopped immediately after running")
- self.rootcanal_logger = self.info['rootcanal_logger']
self.controller_configs = self.info['controller_configs']
# Parse and construct GD device objects
diff --git a/gd/cert/gd_base_test_lib.py b/gd/cert/gd_base_test_lib.py
index 19b83f1c9..b480f1d2a 100644
--- a/gd/cert/gd_base_test_lib.py
+++ b/gd/cert/gd_base_test_lib.py
@@ -42,6 +42,9 @@ def setup_class_core(dut_module, cert_module, verbose_mode, log_path_base, contr
# Start root-canal if needed
info['rootcanal_running'] = False
+ info['rootcanal_logpath'] = ""
+ info['rootcanal_process'] = None
+ info['rootcanal_logger'] = None
if 'rootcanal' in info['controller_configs']:
info['rootcanal_running'] = True
# Get root canal binary
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index 65d85d216..228e3ad7c 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -426,22 +426,22 @@ class GdAndroidDevice(GdDeviceBase):
try:
self.adb.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log")
except AdbCommandError as error:
- logging.error("Error during setup: " + str(error))
+ logging.warning("Failed to remove old btsnoop log: " + str(error))
try:
self.adb.shell("rm /data/misc/bluetooth/logs/btsnooz_hci.log")
except AdbCommandError as error:
- logging.error("Error during setup: " + str(error))
+ logging.warning("Failed to remove old btsnooz log: " + str(error))
try:
self.adb.shell("rm /data/misc/bluedroid/bt_config.conf")
except AdbCommandError as error:
- logging.error("Error during setup: " + str(error))
+ logging.warning("Failed to remove old bt config: " + str(error))
try:
self.adb.shell("rm /data/misc/bluedroid/bt_config.bak")
except AdbCommandError as error:
- logging.error("Error during setup: " + str(error))
+ logging.warning("Failed to remove back up config: " + str(error))
self.ensure_no_output(self.adb.shell("svc bluetooth disable"))
@@ -486,28 +486,34 @@ class GdAndroidDevice(GdDeviceBase):
logging.error("logcat_process %s_%s stopped with code: %d" % (self.label, self.serial_number, return_code))
self.logcat_logger.stop()
self.cleanup_port_forwarding()
- self.adb.pull("/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(self.log_path_base,
- "%s_btsnoop_hci.log" % self.label))
- self.adb.pull("/data/misc/bluedroid/bt_config.conf %s" % os.path.join(self.log_path_base,
- "%s_bt_config.conf" % self.label))
- self.adb.pull(
- "/data/misc/bluedroid/bt_config.bak %s" % os.path.join(self.log_path_base, "%s_bt_config.bak" % self.label))
+ self.pull_logs(self.log_path_base)
+
+ def pull_logs(self, base_dir):
+ try:
+ self.adb.pull("/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(
+ base_dir, "%s_btsnoop_hci.log" % self.label))
+ self.adb.pull(
+ "/data/misc/bluedroid/bt_config.conf %s" % os.path.join(base_dir, "%s_bt_config.conf" % self.label))
+ self.adb.pull(
+ "/data/misc/bluedroid/bt_config.bak %s" % os.path.join(base_dir, "%s_bt_config.bak" % self.label))
+ except AdbCommandError as error:
+ logging.warning("Failed to pull logs from device: " + str(error))
def cleanup_port_forwarding(self):
try:
self.adb.remove_tcp_forward(self.grpc_port)
except AdbError as error:
- logging.error("Error during port forwarding cleanup: " + str(error))
+ logging.warning("Failed to cleanup gRPC port: " + str(error))
try:
self.adb.remove_tcp_forward(self.grpc_root_server_port)
except AdbError as error:
- logging.error("Error during port forwarding cleanup: " + str(error))
+ logging.warning("Failed to cleanup gRPC server port: " + str(error))
try:
self.adb.reverse("--remove tcp:%d" % self.signal_port)
except AdbError as error:
- logging.error("Error during port forwarding cleanup: " + str(error))
+ logging.warning("Failed to cleanup signal port: " + str(error))
@staticmethod
def ensure_no_output(result):
diff --git a/gd/cert/gd_sl4a_base_test.py b/gd/cert/gd_sl4a_base_test.py
new file mode 100644
index 000000000..be7669896
--- /dev/null
+++ b/gd/cert/gd_sl4a_base_test.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+#
+# Copyright 2021 - 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.
+
+import importlib
+import traceback
+import os
+import logging
+
+from functools import wraps
+from grpc import RpcError
+
+from acts import signals
+from acts.base_test import BaseTestClass
+from acts.context import get_current_context
+from acts.controllers.adb_lib.error import AdbCommandError
+
+from cert.ble_lib import enable_bluetooth, disable_bluetooth
+from cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME
+from cert.ble_lib import BleLib
+from facade import rootservice_pb2 as facade_rootservice
+
+
+class GdSl4aBaseTestClass(BaseTestClass):
+
+ SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
+
+ def setup_class(self, cert_module):
+ self.log_path_base = get_current_context().get_full_output_path()
+ self.verbose_mode = bool(self.user_params.get('verbose_mode', False))
+ for config in self.controller_configs[CONTROLLER_CONFIG_NAME]:
+ config['verbose_mode'] = self.verbose_mode
+ self.cert_module = cert_module
+
+ # Parse and construct GD device objects
+ self.register_controller(importlib.import_module('cert.gd_device'), builtin=True)
+ self.dut = self.android_devices[0]
+ self.cert = self.gd_devices[0]
+
+ # Enable full btsnoop log
+ self.dut.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
+ getprop_result = self.dut.adb.shell("getprop persist.bluetooth.btsnooplogmode") == "full"
+ if not getprop_result:
+ self.dut.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
+
+ self.ble = BleLib(log=self.log, dut=self.dut)
+
+ def teardown_class(self):
+ pass
+
+ def setup_test(self):
+ self.cert.rootservice.StartStack(
+ facade_rootservice.StartStackRequest(
+ module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module),))
+ self.cert.wait_channel_ready()
+
+ self.timer_list = []
+ self.dut.ed.clear_all_events()
+ self.dut.droid.setScreenTimeout(500)
+ self.dut.droid.wakeUpNow()
+
+ # Always start tests with Bluetooth enabled and BLE disabled.
+ self.dut.droid.bluetoothDisableBLE()
+ disable_bluetooth(self.dut.droid, self.dut.ed)
+ # Enable full verbose logging for Bluetooth
+ self.dut.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
+ # Then enable Bluetooth
+ enable_bluetooth(self.dut.droid, self.dut.ed)
+ self.dut.droid.bluetoothDisableBLE()
+ return True
+
+ def teardown_test(self):
+ # Make sure BLE is disabled and Bluetooth is disabled after test
+ self.dut.droid.bluetoothDisableBLE()
+ disable_bluetooth(self.dut.droid, self.dut.ed)
+ self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
+
+ # TODO: split cert logcat logs into individual tests
+ current_test_dir = get_current_context().get_full_output_path()
+
+ # Pull DUT logs
+ self.pull_dut_logs(current_test_dir)
+
+ # Pull CERT logs
+ self.cert.pull_logs(current_test_dir)
+
+ def pull_dut_logs(self, base_dir):
+ try:
+ self.dut.pull_files("/data/misc/bluetooth/logs/btsnoop_hci.log",
+ os.path.join(base_dir, "DUT_%s_btsnoop_hci.log" % self.dut.serial))
+ self.dut.pull_files("/data/misc/bluedroid/bt_config.conf",
+ os.path.join(base_dir, "DUT_%s_bt_config.conf" % self.dut.serial))
+ self.dut.pull_files("/data/misc/bluedroid/bt_config.bak",
+ os.path.join(base_dir, "DUT_%s_bt_config.bak" % self.dut.serial))
+ except AdbCommandError as error:
+ logging.warning("Failed to pull logs from DUT: " + str(error))
+
+ def __getattribute__(self, name):
+ attr = super().__getattribute__(name)
+ if not callable(attr) or not GdSl4aBaseTestClass.__is_entry_function(name):
+ return attr
+
+ @wraps(attr)
+ def __wrapped(*args, **kwargs):
+ try:
+ return attr(*args, **kwargs)
+ except RpcError as e:
+ exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__))
+ raise signals.TestFailure("RpcError during test\n\nRpcError:\n\n%s" % (exception_info))
+
+ return __wrapped
+
+ __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"}
+
+ @staticmethod
+ def __is_entry_function(name):
+ return name.startswith("test_") or name in GdSl4aBaseTestClass.__ENTRY_METHODS
diff --git a/gd/cert/gd_sl4a_device_config.json b/gd/cert/gd_sl4a_device_config.json
new file mode 100644
index 000000000..f3451d2e1
--- /dev/null
+++ b/gd/cert/gd_sl4a_device_config.json
@@ -0,0 +1,46 @@
+{ "_description": "Bluetooth cert testing",
+ "testbed":
+ [
+ {
+ "_description": "Two Android devices with GD cert and SL4A dut",
+ "name": "AndroidDeviceCert",
+ "GdDevice":
+ [
+ {
+ "grpc_port": "8898",
+ "grpc_root_server_port": "8896",
+ "signal_port": "8894",
+ "label": "cert",
+ "serial_number": "CERT",
+ "name": "Cert Device",
+ "cmd":
+ [
+ "adb",
+ "-s",
+ "$(serial_number)",
+ "shell",
+ "ASAN_OPTIONS=detect_container_overflow=0",
+ "/system/bin/bluetooth_stack_with_facade",
+ "--grpc-port=$(grpc_port)",
+ "--root-server-port=$(grpc_root_server_port)",
+ "--btsnoop=/data/misc/bluetooth/logs/btsnoop_hci.log",
+ "--btsnooz=/data/misc/bluetooth/logs/btsnooz_hci.log",
+ "--btconfig=/data/misc/bluedroid/bt_config.conf",
+ "--signal-port=$(signal_port)"
+ ]
+ }
+ ],
+ "AndroidDevice":
+ [
+ {
+ "serial": "DUT",
+ "label": "dut",
+ "client_port": "8895",
+ "forwarded_port": "8899",
+ "server_port": "8899"
+ }
+ ]
+ }
+ ],
+ "logpath": "/tmp/logs"
+}
diff --git a/gd/cert/run b/gd/cert/run
index e666c5fbb..f6f4a36d8 100755
--- a/gd/cert/run
+++ b/gd/cert/run
@@ -89,6 +89,7 @@ NUM_REPETITIONS="1"
SKIP_SOONG_BUILD=false
USE_ASHMEM_VENV=true
VERBOSE_MODE=false
+DEVICE_TEST=false
# Directory for test configs to modify
CONFIG_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
@@ -156,29 +157,7 @@ function parse_options {
;;
--device)
TEST_CONFIG="${ANDROID_BUILD_TOP}/system/bt/gd/cert/devices_config.json"
- RR="$(cat ${TEST_CONFIG}|grep \"CERT\\\|DUT\")"
- if [ "$RR" != "" ]; then
- DUT_SERIAL="$(menu-adb DUT)"
- DUT_ADB="adb -s ${DUT_SERIAL}"
- DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
- CERT_SERIAL="$(menu-adb CERT)"
- CERT_ADB="adb -s ${CERT_SERIAL}"
- CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
-
- if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then
- echo
- echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!"
- echo
- exit 1
- fi
-
- ## Set android devices in config
- pushd .
- cd "${CONFIG_DIR}"
- popd
- sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${CONFIG_DIR}/devices_config.json
- sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${CONFIG_DIR}/devices_config.json
- fi
+ DEVICE_TEST=true
shift # past argument
;;
# Repeat running the specified test cases by N times in one single setup
@@ -198,6 +177,7 @@ function parse_options {
BUILD_TARGET=$RUST_BUILD_TARGET
export RUST_BACKTRACE=1
TEST_CONFIG=$ANDROID_BUILD_TOP/system/bt/gd/cert/rust_android_devices_config.json
+ DEVICE_TEST=true
shift # past argument
;;
--rhost)
@@ -237,6 +217,34 @@ function parse_options {
}
+function select_devices {
+ if [ "$DEVICE_TEST" == true ] ; then
+ RR="$(cat ${TEST_CONFIG}|grep \"CERT\\\|DUT\")"
+ if [ "$RR" != "" ]; then
+ DUT_SERIAL="$(menu-adb DUT)"
+ DUT_ADB="adb -s ${DUT_SERIAL}"
+ DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+ CERT_SERIAL="$(menu-adb CERT)"
+ CERT_ADB="adb -s ${CERT_SERIAL}"
+ CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
+
+ if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then
+ echo
+ echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!"
+ echo
+ exit 1
+ fi
+
+ ## Set android devices in config
+ pushd .
+ cd "${CONFIG_DIR}"
+ popd
+ sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${TEST_CONFIG}
+ sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${TEST_CONFIG}
+ fi
+ fi
+}
+
function soong_build {
if [ "$CLEAN_VENV" == true ] ; then
$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/system/bt/gd" dist $BUILD_TARGET -j20
@@ -475,6 +483,7 @@ function menu-adb() {
function main {
check_environment
parse_options $@
+ select_devices
if [[ "${SKIP_SOONG_BUILD}" != true ]] ; then
soong_build
fi
diff --git a/gd/hci/cert/le_advanced_scanning_test.py b/gd/hci/cert/le_advanced_scanning_test.py
new file mode 100644
index 000000000..a47f79c6e
--- /dev/null
+++ b/gd/hci/cert/le_advanced_scanning_test.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python3
+#
+# Copyright 2021 - 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.
+
+import queue
+import logging
+
+from google.protobuf import empty_pb2 as empty_proto
+
+from bluetooth_packets_python3 import hci_packets
+from cert.bt_constants import ble_scan_settings_modes, ble_address_types, scan_result, ble_scan_settings_phys
+from cert.ble_lib import generate_ble_scan_objects
+from cert.gd_sl4a_base_test import GdSl4aBaseTestClass
+from hci.facade import \
+ le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from facade import common_pb2 as common
+
+
+class LeAdvancedScanningTest(GdSl4aBaseTestClass):
+
+ def setup_class(self):
+ super().setup_class(cert_module='HCI_INTERFACES')
+ self.default_timeout = 10 # seconds
+
+ def setup_test(self):
+ super().setup_test()
+
+ def teardown_test(self):
+ super().teardown_test()
+
+ def set_cert_privacy_policy_with_random_address(self, random_address):
+ private_policy = le_initiator_address_facade.PrivacyPolicy(
+ address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+ address_with_type=common.BluetoothAddressWithType(
+ address=common.BluetoothAddress(address=bytes(random_address, encoding='utf8')),
+ type=common.RANDOM_DEVICE_ADDRESS))
+ self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy)
+
+ def set_cert_privacy_policy_with_public_address(self):
+ public_address_bytes = self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address
+ private_policy = le_initiator_address_facade.PrivacyPolicy(
+ address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
+ address_with_type=common.BluetoothAddressWithType(
+ address=common.BluetoothAddress(address=public_address_bytes), type=common.PUBLIC_DEVICE_ADDRESS))
+ self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy)
+ # Bluetooth MAC address must be upper case
+ return public_address_bytes.decode('utf-8').upper()
+
+ def test_scan_filter_device_name_legacy_pdu(self):
+ # Use public address on cert side
+ logging.info("Setting public address")
+ DEVICE_NAME = 'Im_The_CERT!'
+ public_address = self.set_cert_privacy_policy_with_public_address()
+ logging.info("Set public address")
+
+ # Setup cert side to advertise
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(DEVICE_NAME, encoding='utf8'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+ config = le_advertising_facade.AdvertisingConfig(
+ advertisement=[gap_data],
+ interval_min=512,
+ interval_max=768,
+ advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+ own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
+ channel_map=7,
+ filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES,
+ tx_power=20)
+ request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+ logging.info("Creating advertiser")
+ create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+ logging.info("Created advertiser")
+
+ # Setup SL4A DUT side to scan
+ logging.info("Start scanning with public address %s" % public_address)
+ self.dut.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ filter_list, scan_settings, scan_callback = generate_ble_scan_objects(self.dut.droid)
+ expected_event_name = scan_result.format(scan_callback)
+
+ # Setup SL4A DUT filter
+ self.dut.droid.bleSetScanFilterDeviceName(DEVICE_NAME)
+ self.dut.droid.bleBuildScanFilter(filter_list)
+
+ # Start scanning on SL4A DUT side
+ self.dut.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+ logging.info("Started scanning")
+ try:
+ # Verify if there is scan result
+ event_info = self.dut.ed.pop_event(expected_event_name, self.default_timeout)
+ except queue.Empty as error:
+ self.log.error("Could not find initial advertisement.")
+ return False
+ # Print out scan result
+ mac_address = event_info['data']['Result']['deviceInfo']['address']
+ self.log.info("Filter advertisement with address {}".format(mac_address))
+
+ # Stop scanning
+ logging.info("Stop scanning")
+ self.dut.droid.bleStopBleScan(scan_callback)
+ logging.info("Stopped scanning")
+
+ # Stop advertising
+ logging.info("Stop advertising")
+ remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+ self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+ logging.info("Stopped advertising")
+
+ return True
+
+ def test_scan_filter_device_random_address_legacy_pdu(self):
+ # Use random address on cert side
+ logging.info("Setting random address")
+ RANDOM_ADDRESS = 'D0:05:04:03:02:01'
+ DEVICE_NAME = 'Im_The_CERT!'
+ self.set_cert_privacy_policy_with_random_address(RANDOM_ADDRESS)
+ logging.info("Set random address")
+
+ # Setup cert side to advertise
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(DEVICE_NAME, encoding='utf8'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+ config = le_advertising_facade.AdvertisingConfig(
+ advertisement=[gap_data],
+ interval_min=512,
+ interval_max=768,
+ advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+ own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+ channel_map=7,
+ filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+ request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+ logging.info("Creating advertiser")
+ create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+ logging.info("Created advertiser")
+
+ # Setup SL4A DUT side to scan
+ addr_type = ble_address_types["random"]
+ logging.info("Start scanning for RANDOM_ADDRESS %s with address type %d" % (RANDOM_ADDRESS, addr_type))
+ self.dut.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ filter_list, scan_settings, scan_callback = generate_ble_scan_objects(self.dut.droid)
+ expected_event_name = scan_result.format(scan_callback)
+
+ # Setup SL4A DUT filter
+ self.dut.droid.bleSetScanFilterDeviceAddressAndType(RANDOM_ADDRESS, int(addr_type))
+ self.dut.droid.bleBuildScanFilter(filter_list)
+
+ # Start scanning on SL4A DUT side
+ self.dut.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+ logging.info("Started scanning")
+ try:
+ # Verify if there is scan result
+ event_info = self.dut.ed.pop_event(expected_event_name, self.default_timeout)
+ except queue.Empty as error:
+ self.log.error("Could not find initial advertisement.")
+ return False
+ # Print out scan result
+ mac_address = event_info['data']['Result']['deviceInfo']['address']
+ self.log.info("Filter advertisement with address {}".format(mac_address))
+
+ # Stop scanning
+ logging.info("Stop scanning")
+ self.dut.droid.bleStopBleScan(scan_callback)
+ logging.info("Stopped scanning")
+
+ # Stop advertising
+ logging.info("Stop advertising")
+ remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+ self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+ logging.info("Stopped advertising")
+
+ return True
+
+ def test_scan_filter_device_public_address_extended_pdu(self):
+ # Use public address on cert side
+ logging.info("Setting public address")
+ DEVICE_NAME = 'Im_The_CERT!'
+ public_address = self.set_cert_privacy_policy_with_public_address()
+ logging.info("Set public address")
+
+ # Setup cert side to advertise
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(DEVICE_NAME, encoding='utf8'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+ config = le_advertising_facade.AdvertisingConfig(
+ advertisement=[gap_data],
+ interval_min=512,
+ interval_max=768,
+ advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+ own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
+ channel_map=7,
+ filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+ extended_config = le_advertising_facade.ExtendedAdvertisingConfig(
+ advertising_config=config, secondary_advertising_phy=ble_scan_settings_phys["1m"])
+ request = le_advertising_facade.ExtendedCreateAdvertiserRequest(config=extended_config)
+ logging.info("Creating advertiser")
+ create_response = self.cert.hci_le_advertising_manager.ExtendedCreateAdvertiser(request)
+ logging.info("Created advertiser")
+
+ # Setup SL4A DUT side to scan
+ addr_type = ble_address_types["public"]
+ logging.info("Start scanning for PUBLIC_ADDRESS %s with address type %d" % (public_address, addr_type))
+ self.dut.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.dut.droid.bleSetScanSettingsLegacy(False)
+ filter_list, scan_settings, scan_callback = generate_ble_scan_objects(self.dut.droid)
+ expected_event_name = scan_result.format(scan_callback)
+
+ # Setup SL4A DUT filter
+ self.dut.droid.bleSetScanFilterDeviceAddressAndType(public_address, int(addr_type))
+ self.dut.droid.bleBuildScanFilter(filter_list)
+
+ # Start scanning on SL4A DUT side
+ self.dut.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+ logging.info("Started scanning")
+ try:
+ # Verify if there is scan result
+ event_info = self.dut.ed.pop_event(expected_event_name, self.default_timeout)
+ except queue.Empty as error:
+ self.log.error("Could not find initial advertisement.")
+ return False
+ # Print out scan result
+ mac_address = event_info['data']['Result']['deviceInfo']['address']
+ self.log.info("Filter advertisement with address {}".format(mac_address))
+
+ # Stop scanning
+ logging.info("Stop scanning")
+ self.dut.droid.bleStopBleScan(scan_callback)
+ logging.info("Stopped scanning")
+
+ # Stop advertising
+ logging.info("Stop advertising")
+ remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+ self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+ logging.info("Stopped advertising")
+
+ return True
diff --git a/gd/hci/facade/le_advertising_manager_facade.cc b/gd/hci/facade/le_advertising_manager_facade.cc
index 8e017bb1b..598d7ba99 100644
--- a/gd/hci/facade/le_advertising_manager_facade.cc
+++ b/gd/hci/facade/le_advertising_manager_facade.cc
@@ -93,6 +93,38 @@ bool AdvertisingConfigFromProto(const AdvertisingConfig& config_proto, hci::Adve
config->filter_policy = static_cast<hci::AdvertisingFilterPolicy>(config_proto.filter_policy());
config->tx_power = static_cast<uint8_t>(config_proto.tx_power());
+
+ return true;
+}
+
+bool ExtendedAdvertisingConfigFromProto(
+ const ExtendedAdvertisingConfig& config_proto, hci::ExtendedAdvertisingConfig* config) {
+ AdvertisingConfigFromProto(config_proto.advertising_config(), config);
+ config->connectable = config_proto.connectable();
+ config->scannable = config_proto.scannable();
+ config->directed = config_proto.directed();
+ config->high_duty_directed_connectable = config_proto.high_duty_directed_connectable();
+ config->legacy_pdus = config_proto.legacy_pdus();
+ config->anonymous = config_proto.anonymous();
+ config->include_tx_power = config_proto.include_tx_power();
+ config->use_le_coded_phy = config_proto.use_le_coded_phy();
+ if (config_proto.secondary_map_skip() < 0 || config_proto.secondary_map_skip() > 0xFF) {
+ LOG_WARN("Base secondary_map_skip: 0x%x", config_proto.secondary_map_skip());
+ return false;
+ }
+ config->secondary_max_skip = config_proto.secondary_map_skip();
+ if (config_proto.secondary_advertising_phy() <= 0 || config_proto.secondary_advertising_phy() > 3) {
+ LOG_WARN("Base secondary_advertising_phy: 0x%x", config_proto.secondary_advertising_phy());
+ return false;
+ }
+ config->secondary_advertising_phy = static_cast<SecondaryPhyType>(config_proto.secondary_advertising_phy());
+ if (config_proto.sid() < 0 || config_proto.sid() > 0xF) {
+ LOG_WARN("Base sid: 0x%x", config_proto.sid());
+ return false;
+ }
+ config->sid = config_proto.sid();
+ config->enable_scan_request_notifications =
+ config_proto.enable_scan_request_notification() ? Enable::ENABLED : Enable::DISABLED;
return true;
}
@@ -128,6 +160,8 @@ class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Ser
::grpc::Status CreateAdvertiser(::grpc::ServerContext* context, const CreateAdvertiserRequest* request,
CreateAdvertiserResponse* response) override {
hci::ExtendedAdvertisingConfig config = {};
+ config.legacy_pdus = true;
+ config.secondary_advertising_phy = SecondaryPhyType::LE_1M;
if (!AdvertisingConfigFromProto(request->config(), &config)) {
LOG_WARN("Error parsing advertising config %s", request->SerializeAsString().c_str());
response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
@@ -155,9 +189,29 @@ class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Ser
::grpc::Status ExtendedCreateAdvertiser(::grpc::ServerContext* context,
const ExtendedCreateAdvertiserRequest* request,
ExtendedCreateAdvertiserResponse* response) override {
- LOG_WARN("ExtendedCreateAdvertiser is not implemented");
- response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
- return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "ExtendedCreateAdvertiser is not implemented");
+ hci::ExtendedAdvertisingConfig config = {};
+ if (!ExtendedAdvertisingConfigFromProto(request->config(), &config)) {
+ LOG_WARN("Error parsing extended advertising config %s", request->SerializeAsString().c_str());
+ response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
+ return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Error while parsing extended advertising config");
+ }
+ LeAdvertiser le_advertiser(config);
+ auto advertiser_id = le_advertising_manager_->ExtendedCreateAdvertiser(
+ -1,
+ config,
+ common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)),
+ common::Bind(&LeAdvertiser::TerminatedCallback, common::Unretained(&le_advertiser)),
+ 0,
+ 0,
+ facade_handler_);
+ if (advertiser_id != LeAdvertisingManager::kInvalidId) {
+ le_advertiser.SetAdvertiserId(advertiser_id);
+ le_advertisers_.push_back(le_advertiser);
+ } else {
+ LOG_WARN("Failed to create advertiser");
+ }
+ response->set_advertiser_id(advertiser_id);
+ return ::grpc::Status::OK;
}
::grpc::Status GetNumberOfAdvertisingInstances(::grpc::ServerContext* context,
diff --git a/osi/src/config.cc b/osi/src/config.cc
index 29d620640..18176d0bd 100644
--- a/osi/src/config.cc
+++ b/osi/src/config.cc
@@ -493,8 +493,8 @@ static bool config_parse(FILE* fp, config_t* config) {
CHECK(config != nullptr);
int line_num = 0;
- char line[1024];
- char section[1024];
+ char line[4096];
+ char section[4096];
strcpy(section, CONFIG_DEFAULT_SECTION);
while (fgets(line, sizeof(line), fp)) {
@@ -511,7 +511,7 @@ static bool config_parse(FILE* fp, config_t* config) {
<< line_num;
return false;
}
- strncpy(section, line_ptr + 1, len - 2); // NOLINT (len < 1024)
+ strncpy(section, line_ptr + 1, len - 2); // NOLINT (len < 4096)
section[len - 2] = '\0';
} else {
char* split = strchr(line_ptr, '=');
diff --git a/profile/avrcp/avrcp_config.h b/profile/avrcp/avrcp_config.h
index f6a872443..3f9e74d0e 100644
--- a/profile/avrcp/avrcp_config.h
+++ b/profile/avrcp/avrcp_config.h
@@ -50,3 +50,10 @@
#ifndef AVRCP_SUPF_TG
#define AVRCP_SUPF_TG_DEFAULT AVRCP_SUPF_TG_1_4
#endif
+
+/**
+ * Supported Feature for AVRCP tartget control
+ */
+#ifndef AVRCP_SUPF_TG_CT
+#define AVRCP_SUPF_TG_CT AVRC_SUPF_CT_CAT2
+#endif
diff --git a/stack/acl/btm_acl.cc b/stack/acl/btm_acl.cc
index 4c7e06696..1e8b0a85b 100644
--- a/stack/acl/btm_acl.cc
+++ b/stack/acl/btm_acl.cc
@@ -369,7 +369,9 @@ void btm_acl_created(const RawAddress& bda, uint16_t hci_handle,
p_acl->hci_handle = hci_handle;
p_acl->link_role = link_role;
p_acl->transport = transport;
- btm_set_link_policy(p_acl, btm_cb.acl_cb_.DefaultLinkPolicy());
+ if (transport == BT_TRANSPORT_BR_EDR) {
+ btm_set_link_policy(p_acl, btm_cb.acl_cb_.DefaultLinkPolicy());
+ }
LOG_WARN(
"Unable to create duplicate acl when one already exists handle:%hu"
" role:%s transport:%s",
@@ -399,7 +401,10 @@ void btm_acl_created(const RawAddress& bda, uint16_t hci_handle,
"Created new ACL connection peer:%s role:%s handle:0x%04x transport:%s",
PRIVATE_ADDRESS(bda), RoleText(p_acl->link_role).c_str(), hci_handle,
bt_transport_text(transport).c_str());
- btm_set_link_policy(p_acl, btm_cb.acl_cb_.DefaultLinkPolicy());
+
+ if (transport == BT_TRANSPORT_BR_EDR) {
+ btm_set_link_policy(p_acl, btm_cb.acl_cb_.DefaultLinkPolicy());
+ }
if (transport == BT_TRANSPORT_LE) {
btm_ble_refresh_local_resolvable_private_addr(
diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc
index 9fe642cba..0396bb16f 100644
--- a/stack/gatt/gatt_sr.cc
+++ b/stack/gatt/gatt_sr.cc
@@ -173,6 +173,9 @@ static void build_read_multi_rsp(tGATT_SR_CMD* p_cmd, uint16_t mtu) {
if (p_rsp != NULL) {
total_len = (p_buf->len + p_rsp->attr_value.len);
+ if (p_cmd->multi_req.variable_len) {
+ total_len += 2;
+ }
if (total_len > mtu) {
/* just send the partial response for the overflow case */
diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h
index 9de3b9bb9..52f41614e 100644
--- a/stack/include/l2c_api.h
+++ b/stack/include/l2c_api.h
@@ -469,6 +469,18 @@ extern bool L2CA_GetPeerLECocConfig(uint16_t lcid,
/*******************************************************************************
*
+ * Function L2CA_GetPeerLECocCredit
+ *
+ * Description Get peers current credit for LE Connection Oriented
+ * Channel.
+ *
+ * Return value: Number of the peer current credit
+ *
+ ******************************************************************************/
+uint16_t L2CA_GetPeerLECocCredit(const RawAddress& bd_addr, uint16_t lcid);
+
+/*******************************************************************************
+ *
* Function L2CA_ReconfigCreditBasedConnsReq
*
* Description Start reconfigure procedure on Connection Oriented Channel.
diff --git a/stack/l2cap/l2c_api.cc b/stack/l2cap/l2c_api.cc
index c8f2659ef..9e7e15159 100644
--- a/stack/l2cap/l2c_api.cc
+++ b/stack/l2cap/l2c_api.cc
@@ -616,6 +616,34 @@ bool L2CA_GetPeerLECocConfig(uint16_t lcid, tL2CAP_LE_CFG_INFO* peer_cfg) {
/*******************************************************************************
*
+ * Function L2CA_GetPeerLECocCredit
+ *
+ * Description Get peers current credit for LE Connection Oriented
+ * Channel.
+ *
+ * Return value: Number of the peer current credit
+ *
+ ******************************************************************************/
+uint16_t L2CA_GetPeerLECocCredit(const RawAddress& bd_addr, uint16_t lcid) {
+ /* First, find the link control block */
+ tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE);
+ if (p_lcb == NULL) {
+ /* No link. Get an LCB and start link establishment */
+ L2CAP_TRACE_WARNING("%s no LCB", __func__);
+ return L2CAP_LE_CREDIT_MAX;
+ }
+
+ tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
+ if (p_ccb == NULL) {
+ L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid);
+ return L2CAP_LE_CREDIT_MAX;
+ }
+
+ return p_ccb->peer_conn_cfg.credits;
+}
+
+/*******************************************************************************
+ *
* Function L2CA_ConnectCreditBasedRsp
*
* Description Response for the pL2CA_CreditBasedConnectInd_Cb which is the