Snap for 7550740 from 9cdf080f39ba9e5781f21c0a90ed6a8e0252c728 to sdk-release
Change-Id: I88b01dfc5ba25c1002812d8bf7cb583fed63985e
diff --git a/binder/android/bluetooth/IBluetoothGatt.aidl b/binder/android/bluetooth/IBluetoothGatt.aidl
index 49bb513..201be2c 100644
--- a/binder/android/bluetooth/IBluetoothGatt.aidl
+++ b/binder/android/bluetooth/IBluetoothGatt.aidl
@@ -46,7 +46,7 @@
void registerScanner(in IScannerCallback callback, in WorkSource workSource);
void unregisterScanner(in int scannerId);
void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
- in List scanStorages, in String callingPackage, String callingFeatureId);
+ in String callingPackage, String callingFeatureId);
void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
in String callingPackage, String callingFeatureId);
void stopScanForIntent(in PendingIntent intent, in String callingPackage);
@@ -86,7 +86,7 @@
void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid,
in int startHandle, in int endHandle, in int authReq);
- void writeCharacteristic(in int clientIf, in String address, in int handle,
+ int writeCharacteristic(in int clientIf, in String address, in int handle,
in int writeType, in int authReq, in byte[] value);
void readDescriptor(in int clientIf, in String address, in int handle, in int authReq);
void writeDescriptor(in int clientIf, in String address, in int handle,
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index 4636120..b108316 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -2115,7 +2115,7 @@
}
APPL_TRACE_ERROR(
"%s: peer %s : Invalid peer UUID: 0x%x for bta_av_handle 0x%x",
- peer_address.ToString().c_str(), peer_uuid, bta_av_handle);
+ __func__, peer_address.ToString().c_str(), peer_uuid, bta_av_handle);
return A2DP_FAIL;
}
diff --git a/build/Android.bp b/build/Android.bp
index 9d42342..eaa8e9a 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -76,6 +76,9 @@
target: {
android: {
test_config_template: ":BluetoothTestConfigTemplate",
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
},
},
defaults: ["fluoride_types_defaults_fuzzable"],
@@ -94,9 +97,6 @@
"libcutils",
],
cpp_std: "c++17",
- sanitize: {
- misc_undefined: ["bounds"],
- },
}
fluoride_defaults {
@@ -111,9 +111,6 @@
"libutils",
],
- sanitize: {
- misc_undefined: ["bounds"],
- },
static_libs: [
"libbluetooth_gd",
"libbluetooth_rust_interop",
@@ -132,6 +129,9 @@
cflags: [
"-DOS_ANDROID",
],
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
},
},
}
diff --git a/gd/Android.bp b/gd/Android.bp
index 17518c4..ea792b7 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -20,6 +20,9 @@
shared_libs: [
"liblog",
],
+ sanitize: {
+ misc_undefined: ["bounds"],
+ },
},
host: {
cflags: [
@@ -43,9 +46,7 @@
"-std=c99",
],
header_libs: ["jni_headers"],
- sanitize: {
- misc_undefined: ["bounds"],
- },
+
}
// Enables code coverage for a set of source files. Must be combined with
diff --git a/gd/cert/matchers.py b/gd/cert/matchers.py
index 5d8fa1f..6979442 100644
--- a/gd/cert/matchers.py
+++ b/gd/cert/matchers.py
@@ -208,6 +208,19 @@
return lambda event: data == event.payload
+class AdvertisingMatchers(object):
+
+ @staticmethod
+ def CallbackMsg(type, advertiser_id=None, status=None, data=None):
+ return lambda event: True if event.message_type == type and (advertiser_id == None or advertiser_id == event.advertiser_id) \
+ and (status == None or status == event.status) and (data == None or data == event.data) else False
+
+ @staticmethod
+ def AddressMsg(type, advertiser_id=None, address=None):
+ return lambda event: True if event.message_type == type and (advertiser_id == None or advertiser_id == event.advertiser_id) \
+ and (address == None or address == event.address) else False
+
+
class NeighborMatchers(object):
@staticmethod
diff --git a/gd/common/init_flags.cc b/gd/common/init_flags.cc
index a7569b8..c119aa6 100644
--- a/gd/common/init_flags.cc
+++ b/gd/common/init_flags.cc
@@ -18,6 +18,7 @@
#include "init_flags.h"
+#include <cstdlib>
#include <string>
#include "common/strings.h"
@@ -27,6 +28,7 @@
namespace common {
bool InitFlags::logging_debug_enabled_for_all = false;
+int InitFlags::hci_adapter = 0;
std::unordered_map<std::string, bool> InitFlags::logging_debug_explicit_tag_settings = {};
bool ParseBoolFlag(const std::vector<std::string>& flag_pair, const std::string& flag, bool* variable) {
@@ -41,6 +43,19 @@
return true;
}
+bool ParseIntFlag(const std::vector<std::string>& flag_pair, const std::string& flag, int* variable) {
+ if (flag != flag_pair[0]) {
+ return false;
+ }
+ auto value = Int64FromString(flag_pair[1]);
+ if (!value || *value > INT32_MAX) {
+ return false;
+ }
+
+ *variable = *value;
+ return true;
+}
+
void InitFlags::Load(const char** flags) {
const char** flags_copy = flags;
SetAll(false);
@@ -52,6 +67,9 @@
continue;
}
+ // Parse adapter index (defaults to 0)
+ ParseIntFlag(flag_pair, "--hci", &hci_adapter);
+
ParseBoolFlag(flag_pair, "INIT_logging_debug_enabled_for_all", &logging_debug_enabled_for_all);
if ("INIT_logging_debug_enabled_for_tags" == flag_pair[0]) {
auto tags = StringSplit(flag_pair[1], ",");
diff --git a/gd/common/init_flags.h b/gd/common/init_flags.h
index 11af2a1..250e4f5 100644
--- a/gd/common/init_flags.h
+++ b/gd/common/init_flags.h
@@ -41,11 +41,16 @@
return logging_debug_enabled_for_all;
}
+ inline static int GetAdapterIndex() {
+ return hci_adapter;
+ }
+
static void SetAllForTesting();
private:
static void SetAll(bool value);
static bool logging_debug_enabled_for_all;
+ static int hci_adapter;
// save both log allow list and block list in the map to save hashing time
static std::unordered_map<std::string, bool> logging_debug_explicit_tag_settings;
};
diff --git a/gd/grpc/grpc_event_queue.h b/gd/grpc/grpc_event_queue.h
index 9575b2d..089c3d0 100644
--- a/gd/grpc/grpc_event_queue.h
+++ b/gd/grpc/grpc_event_queue.h
@@ -50,12 +50,10 @@
::grpc::Status RunLoop(::grpc::ServerContext* context, ::grpc::ServerWriter<T>* writer) {
using namespace std::chrono_literals;
LOG_INFO("%s: Entering Loop", log_name_.c_str());
- pending_events_.clear();
- running_ = true;
while (!context->IsCancelled()) {
// Wait for 500 ms so that cancellation can be caught in amortized 250 ms latency
if (pending_events_.wait_to_take(500ms)) {
- LOG_INFO("%s: Got event after queue", log_name_.c_str());
+ LOG_INFO("%s: Got event from queue", log_name_.c_str());
writer->Write(pending_events_.take());
}
}
@@ -73,13 +71,13 @@
LOG_INFO("%s: Discarding an event while not running the loop", log_name_.c_str());
return;
}
- LOG_INFO("%s: Got event before queue", log_name_.c_str());
+ LOG_INFO("%s: Got event, enqueuing", log_name_.c_str());
pending_events_.push(std::move(event));
}
private:
std::string log_name_;
- std::atomic<bool> running_ = false;
+ std::atomic<bool> running_{true};
common::BlockingQueue<T> pending_events_;
};
diff --git a/gd/hci/cert/le_advertising_manager_test_lib.py b/gd/hci/cert/le_advertising_manager_test_lib.py
index 10e0ce6..f044316 100644
--- a/gd/hci/cert/le_advertising_manager_test_lib.py
+++ b/gd/hci/cert/le_advertising_manager_test_lib.py
@@ -18,38 +18,74 @@
import sys
import logging
+from bluetooth_packets_python3 import hci_packets
from cert.event_stream import EventStream
-from google.protobuf import empty_pb2 as empty_proto
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.matchers import AdvertisingMatchers
+from cert.py_hci import PyHci
+from cert.truth import assertThat
+from facade import common_pb2 as common
from facade import rootservice_pb2 as facade_rootservice
+from google.protobuf import empty_pb2 as empty_proto
from hci.facade import hci_facade_pb2 as hci_facade
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 bluetooth_packets_python3 import hci_packets
-from facade import common_pb2 as common
-from cert.py_hci import PyHci
-from cert.truth import assertThat
+from hci.facade.le_advertising_manager_facade_pb2 import AdvertisingStatus
+from hci.facade.le_advertising_manager_facade_pb2 import CallbackMsgType
class LeAdvertisingManagerTestBase():
def setup_test(self, cert):
self.cert_hci = PyHci(cert, acl_streaming=True)
+ self.dut.callback_event_stream = EventStream(
+ self.dut.hci_le_advertising_manager.FetchCallbackEvents(empty_proto.Empty()))
+ self.dut.address_event_stream = EventStream(
+ self.dut.hci_le_advertising_manager.FetchAddressEvents(empty_proto.Empty()))
def teardown_test(self):
self.cert_hci.close()
+ if self.dut.callback_event_stream is not None:
+ safeClose(self.dut.callback_event_stream)
+ else:
+ logging.info("DUT: Callback Event Stream is None!")
+ if self.dut.address_event_stream is not None:
+ safeClose(self.dut.address_event_stream)
+ else:
+ logging.info("DUT: address Event Stream is None!")
- def test_le_ad_scan_dut_advertises(self):
+ def set_address_policy_with_static_address(self):
privacy_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(b'D0:05:04:03:02:01')),
+ address=common.BluetoothAddress(address=bytes(b'd0:05:04:03:02:01')),
type=common.RANDOM_DEVICE_ADDRESS),
rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
minimum_rotation_time=0,
maximum_rotation_time=0)
self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+ def create_advertiser(self):
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(b'Im_The_DUT'))
+ 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)
+ create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+ return create_response
+
+ def test_le_ad_scan_dut_advertises(self):
+ self.set_address_policy_with_static_address()
self.cert_hci.register_for_le_events(hci_packets.SubeventCode.ADVERTISING_REPORT,
hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
@@ -67,7 +103,34 @@
hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
hci_packets.FilterDuplicates.DISABLED, 0, 0))
- # DUT Advertises
+ create_response = self.create_advertiser()
+
+ assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload)
+
+ remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+ self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+ self.cert_hci.send_command(
+ hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED, hci_packets.Enable.DISABLED))
+
+ def test_extended_create_advertises(self):
+ self.set_address_policy_with_static_address()
+ self.cert_hci.register_for_le_events(hci_packets.SubeventCode.ADVERTISING_REPORT,
+ hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+
+ # CERT Scans
+ self.cert_hci.send_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+ scan_parameters = hci_packets.PhyScanParameters()
+ scan_parameters.le_scan_type = hci_packets.LeScanType.ACTIVE
+ scan_parameters.le_scan_interval = 40
+ scan_parameters.le_scan_window = 20
+ self.cert_hci.send_command(
+ hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+ hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
+ [scan_parameters]))
+ self.cert_hci.send_command(
+ hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+ hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
gap_name = hci_packets.GapData()
gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
gap_name.data = list(bytes(b'Im_The_DUT'))
@@ -80,9 +143,22 @@
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)
-
- create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+ extended_config = le_advertising_facade.ExtendedAdvertisingConfig(
+ advertising_config=config,
+ connectable=True,
+ scannable=False,
+ directed=False,
+ high_duty_directed_connectable=False,
+ legacy_pdus=True,
+ anonymous=False,
+ include_tx_power=True,
+ use_le_coded_phy=False,
+ secondary_max_skip=0x00,
+ secondary_advertising_phy=0x01,
+ sid=0x00,
+ enable_scan_request_notifications=0x00)
+ request = le_advertising_facade.ExtendedCreateAdvertiserRequest(config=extended_config)
+ create_response = self.dut.hci_le_advertising_manager.ExtendedCreateAdvertiser(request)
assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload)
@@ -90,3 +166,145 @@
self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
self.cert_hci.send_command(
hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED, hci_packets.Enable.DISABLED))
+
+ def test_advertising_set_started_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.ADVERTISING_SET_STARTED, create_response.advertiser_id,
+ AdvertisingStatus.SUCCESS, 0x00))
+
+ def test_enable_advertiser_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ enable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest(
+ advertiser_id=create_response.advertiser_id, enable=True)
+ self.dut.hci_le_advertising_manager.EnableAdvertiser(enable_advertiser_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.ADVERTISING_ENABLED, create_response.advertiser_id,
+ AdvertisingStatus.SUCCESS, 0x01))
+
+ def test_disable_advertiser_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ disable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest(
+ advertiser_id=create_response.advertiser_id, enable=False)
+ self.dut.hci_le_advertising_manager.EnableAdvertiser(disable_advertiser_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.ADVERTISING_ENABLED, create_response.advertiser_id,
+ AdvertisingStatus.SUCCESS, 0x00))
+
+ def test_set_advertising_data_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(b'Im_The_DUT2'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+
+ set_data_request = le_advertising_facade.SetDataRequest(
+ advertiser_id=create_response.advertiser_id, set_scan_rsp=False, data=[gap_data])
+ self.dut.hci_le_advertising_manager.SetData(set_data_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.ADVERTISING_DATA_SET, create_response.advertiser_id,
+ AdvertisingStatus.SUCCESS))
+
+ def test_set_scan_response_data_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(b'Im_The_DUT2'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+
+ set_data_request = le_advertising_facade.SetDataRequest(
+ advertiser_id=create_response.advertiser_id, set_scan_rsp=True, data=[gap_data])
+ self.dut.hci_le_advertising_manager.SetData(set_data_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.SCAN_RESPONSE_DATA_SET, create_response.advertiser_id,
+ AdvertisingStatus.SUCCESS))
+
+ def test_set_parameters_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+
+ # The Host shall not issue set parameters command when advertising is enabled
+ disable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest(
+ advertiser_id=create_response.advertiser_id, enable=False)
+ self.dut.hci_le_advertising_manager.EnableAdvertiser(disable_advertiser_request)
+
+ config = le_advertising_facade.AdvertisingConfig(
+ 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)
+
+ set_parameters_request = le_advertising_facade.SetParametersRequest(
+ advertiser_id=create_response.advertiser_id, config=config)
+ self.dut.hci_le_advertising_manager.SetParameters(set_parameters_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.ADVERTISING_PARAMETERS_UPDATED,
+ create_response.advertiser_id, AdvertisingStatus.SUCCESS))
+
+ def test_set_periodic_parameters_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+
+ config = le_advertising_facade.PeriodicAdvertisingParameters(
+ min_interval=512,
+ max_interval=768,
+ advertising_property=le_advertising_facade.AdvertisingProperty.INCLUDE_TX_POWER)
+
+ set_periodic_parameters_request = le_advertising_facade.SetPeriodicParametersRequest(
+ advertiser_id=create_response.advertiser_id, config=config)
+ self.dut.hci_le_advertising_manager.SetPeriodicParameters(set_periodic_parameters_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.PERIODIC_ADVERTISING_PARAMETERS_UPDATED,
+ create_response.advertiser_id))
+
+ def test_set_periodic_data_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ gap_name = hci_packets.GapData()
+ gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+ gap_name.data = list(bytes(b'Im_The_DUT2'))
+ gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+
+ set_periodic_data_request = le_advertising_facade.SetPeriodicDataRequest(
+ advertiser_id=create_response.advertiser_id, data=[gap_data])
+ self.dut.hci_le_advertising_manager.SetPeriodicData(set_periodic_data_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.PERIODIC_ADVERTISING_DATA_SET,
+ create_response.advertiser_id))
+
+ def test_enable_periodic_advertising_callback(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ enable_periodic_advertising_request = le_advertising_facade.EnablePeriodicAdvertisingRequest(
+ advertiser_id=create_response.advertiser_id, enable=True)
+ self.dut.hci_le_advertising_manager.EnablePeriodicAdvertising(enable_periodic_advertising_request)
+
+ assertThat(self.dut.callback_event_stream).emits(
+ AdvertisingMatchers.CallbackMsg(CallbackMsgType.PERIODIC_ADVERTISING_ENABLED,
+ create_response.advertiser_id))
+
+ def test_get_own_address(self):
+ self.set_address_policy_with_static_address()
+ create_response = self.create_advertiser()
+ get_own_address_request = le_advertising_facade.GetOwnAddressRequest(
+ advertiser_id=create_response.advertiser_id)
+ self.dut.hci_le_advertising_manager.GetOwnAddress(get_own_address_request)
+ address_with_type = common.BluetoothAddressWithType(
+ address=common.BluetoothAddress(address=bytes(b'd0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+ assertThat(self.dut.address_event_stream).emits(
+ AdvertisingMatchers.AddressMsg(CallbackMsgType.OWN_ADDRESS_READ, create_response.advertiser_id,
+ address_with_type))
diff --git a/gd/hci/facade/le_advertising_manager_facade.cc b/gd/hci/facade/le_advertising_manager_facade.cc
index 8e017bb..7d370f8 100644
--- a/gd/hci/facade/le_advertising_manager_facade.cc
+++ b/gd/hci/facade/le_advertising_manager_facade.cc
@@ -20,6 +20,7 @@
#include "common/bidi_queue.h"
#include "common/bind.h"
+#include "grpc/grpc_event_queue.h"
#include "hci/address.h"
#include "hci/address_with_type.h"
#include "hci/facade/le_advertising_manager_facade.grpc.pb.h"
@@ -50,7 +51,7 @@
return gap_data;
}
-bool AdvertisingConfigFromProto(const AdvertisingConfig& config_proto, hci::AdvertisingConfig* config) {
+bool AdvertisingConfigFromProto(const AdvertisingConfig& config_proto, hci::ExtendedAdvertisingConfig* config) {
for (const auto& elem : config_proto.advertisement()) {
config->advertisement.push_back(GapDataFromProto(elem));
}
@@ -93,6 +94,47 @@
config->filter_policy = static_cast<hci::AdvertisingFilterPolicy>(config_proto.filter_policy());
config->tx_power = static_cast<uint8_t>(config_proto.tx_power());
+
+ config->legacy_pdus = true;
+ return true;
+}
+
+bool ExtendedAdvertisingConfigFromProto(
+ const ExtendedAdvertisingConfig& config_proto, hci::ExtendedAdvertisingConfig* config) {
+ if (!AdvertisingConfigFromProto(config_proto.advertising_config(), config)) {
+ LOG_WARN("Error parsing advertising config");
+ return false;
+ }
+ 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();
+ config->secondary_max_skip = static_cast<uint8_t>(config_proto.secondary_max_skip());
+ config->secondary_advertising_phy = static_cast<hci::SecondaryPhyType>(config_proto.secondary_advertising_phy());
+ config->sid = static_cast<uint8_t>(config_proto.sid());
+ config->enable_scan_request_notifications =
+ static_cast<hci::Enable>(config_proto.enable_scan_request_notifications());
+ return true;
+}
+
+bool PeriodicAdvertisingParametersFromProto(
+ const PeriodicAdvertisingParameters& config_proto, hci::PeriodicAdvertisingParameters* config) {
+ if (config_proto.min_interval() > UINT16_MAX || config_proto.min_interval() < 0) {
+ LOG_WARN("Bad interval_min: %d", config_proto.min_interval());
+ return false;
+ }
+ config->min_interval = static_cast<uint16_t>(config_proto.min_interval());
+ if (config_proto.max_interval() > UINT16_MAX || config_proto.max_interval() < 0) {
+ LOG_WARN("Bad interval_max: %d", config_proto.max_interval());
+ return false;
+ }
+ config->max_interval = static_cast<uint16_t>(config_proto.max_interval());
+ config->properties =
+ static_cast<hci::PeriodicAdvertisingParameters::AdvertisingProperty>(config_proto.advertising_property());
return true;
}
@@ -117,7 +159,7 @@
hci::AdvertisingConfig config_;
};
-class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Service {
+class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Service, AdvertisingCallback {
public:
LeAdvertisingManagerFacadeService(LeAdvertisingManager* le_advertising_manager, os::Handler* facade_handler)
: le_advertising_manager_(le_advertising_manager), facade_handler_(facade_handler) {
@@ -135,7 +177,7 @@
}
LeAdvertiser le_advertiser(config);
auto advertiser_id = le_advertising_manager_->ExtendedCreateAdvertiser(
- -1,
+ 0,
config,
common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)),
common::Bind(&LeAdvertiser::TerminatedCallback, common::Unretained(&le_advertiser)),
@@ -155,9 +197,102 @@
::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 advertising config %s", request->SerializeAsString().c_str());
+ response->set_advertiser_id(LeAdvertisingManager::kInvalidId);
+ return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Error while parsing advertising config");
+ }
+ LeAdvertiser le_advertiser(config);
+ auto advertiser_id = le_advertising_manager_->ExtendedCreateAdvertiser(
+ 0,
+ 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 EnableAdvertiser(
+ ::grpc::ServerContext* context,
+ const EnableAdvertiserRequest* request,
+ ::google::protobuf::Empty* response) override {
+ le_advertising_manager_->EnableAdvertiser(request->advertiser_id(), request->enable(), 0, 0);
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetData(
+ ::grpc::ServerContext* context, const SetDataRequest* request, ::google::protobuf::Empty* response) override {
+ std::vector<GapData> advertising_data = {};
+ for (const auto& elem : request->data()) {
+ advertising_data.push_back(GapDataFromProto(elem));
+ }
+ le_advertising_manager_->SetData(request->advertiser_id(), request->set_scan_rsp(), advertising_data);
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetParameters(
+ ::grpc::ServerContext* context,
+ const SetParametersRequest* request,
+ ::google::protobuf::Empty* response) override {
+ hci::ExtendedAdvertisingConfig config = {};
+ if (!AdvertisingConfigFromProto(request->config(), &config)) {
+ LOG_WARN("Error parsing advertising config %s", request->SerializeAsString().c_str());
+ return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Error while parsing advertising config");
+ }
+ le_advertising_manager_->SetParameters(request->advertiser_id(), config);
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetPeriodicParameters(
+ ::grpc::ServerContext* context,
+ const SetPeriodicParametersRequest* request,
+ ::google::protobuf::Empty* response) override {
+ hci::PeriodicAdvertisingParameters config = {};
+ if (!PeriodicAdvertisingParametersFromProto(request->config(), &config)) {
+ LOG_WARN("Error parsing periodic advertising parameters %s", request->SerializeAsString().c_str());
+ return ::grpc::Status(
+ ::grpc::StatusCode::INVALID_ARGUMENT, "Error while parsing periodic advertising parameters");
+ }
+ le_advertising_manager_->SetPeriodicParameters(request->advertiser_id(), config);
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SetPeriodicData(
+ ::grpc::ServerContext* context,
+ const SetPeriodicDataRequest* request,
+ ::google::protobuf::Empty* response) override {
+ std::vector<GapData> advertising_data = {};
+ for (const auto& elem : request->data()) {
+ advertising_data.push_back(GapDataFromProto(elem));
+ }
+ le_advertising_manager_->SetPeriodicData(request->advertiser_id(), advertising_data);
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status EnablePeriodicAdvertising(
+ ::grpc::ServerContext* context,
+ const EnablePeriodicAdvertisingRequest* request,
+ ::google::protobuf::Empty* response) override {
+ le_advertising_manager_->EnablePeriodicAdvertising(request->advertiser_id(), request->enable());
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status GetOwnAddress(
+ ::grpc::ServerContext* context,
+ const GetOwnAddressRequest* request,
+ ::google::protobuf::Empty* response) override {
+ le_advertising_manager_->GetOwnAddress(request->advertiser_id());
+ return ::grpc::Status::OK;
}
::grpc::Status GetNumberOfAdvertisingInstances(::grpc::ServerContext* context,
@@ -184,9 +319,104 @@
return ::grpc::Status::OK;
}
+ ::grpc::Status FetchCallbackEvents(
+ ::grpc::ServerContext* context,
+ const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<CallbackMsg>* writer) override {
+ le_advertising_manager_->RegisterAdvertisingCallback(this);
+ return callback_events_.RunLoop(context, writer);
+ }
+
+ ::grpc::Status FetchAddressEvents(
+ ::grpc::ServerContext* context,
+ const ::google::protobuf::Empty* request,
+ ::grpc::ServerWriter<AddressMsg>* writer) override {
+ return address_events_.RunLoop(context, writer);
+ }
+
+ void OnAdvertisingSetStarted(int reg_id, uint8_t advertiser_id, int8_t tx_power, AdvertisingStatus status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::ADVERTISING_SET_STARTED);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ msg.set_data(reg_id);
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnAdvertisingEnabled(uint8_t advertiser_id, bool enable, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::ADVERTISING_ENABLED);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ msg.set_data(enable ? 1 : 0);
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::ADVERTISING_DATA_SET);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnScanResponseDataSet(uint8_t advertiser_id, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::SCAN_RESPONSE_DATA_SET);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnAdvertisingParametersUpdated(uint8_t advertiser_id, int8_t tx_power, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::ADVERTISING_PARAMETERS_UPDATED);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnPeriodicAdvertisingParametersUpdated(uint8_t advertiser_id, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::PERIODIC_ADVERTISING_PARAMETERS_UPDATED);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnPeriodicAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::PERIODIC_ADVERTISING_DATA_SET);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnPeriodicAdvertisingEnabled(uint8_t advertiser_id, bool enable, uint8_t status) {
+ CallbackMsg msg;
+ msg.set_message_type(CallbackMsgType::PERIODIC_ADVERTISING_ENABLED);
+ msg.set_advertiser_id(advertiser_id);
+ msg.set_status(static_cast<facade::AdvertisingStatus>(status));
+ callback_events_.OnIncomingEvent(msg);
+ };
+
+ void OnOwnAddressRead(uint8_t advertiser_id, uint8_t address_type, Address address) {
+ LOG_INFO("OnOwnAddressRead Address:%s, address_type:%d", address.ToString().c_str(), address_type);
+ AddressMsg msg;
+ msg.set_message_type(CallbackMsgType::OWN_ADDRESS_READ);
+ msg.set_advertiser_id(advertiser_id);
+ bluetooth::facade::BluetoothAddressWithType facade_address;
+ facade_address.mutable_address()->set_address(address.ToString());
+ facade_address.set_type(static_cast<facade::BluetoothAddressTypeEnum>(address_type));
+ *msg.mutable_address() = facade_address;
+ address_events_.OnIncomingEvent(msg);
+ };
+
std::vector<LeAdvertiser> le_advertisers_;
LeAdvertisingManager* le_advertising_manager_;
os::Handler* facade_handler_;
+ ::bluetooth::grpc::GrpcEventQueue<CallbackMsg> callback_events_{"callback events"};
+ ::bluetooth::grpc::GrpcEventQueue<AddressMsg> address_events_{"address events"};
};
void LeAdvertisingManagerFacadeModule::ListDependencies(ModuleList* list) {
diff --git a/gd/hci/facade/le_advertising_manager_facade.proto b/gd/hci/facade/le_advertising_manager_facade.proto
index edb70cf..8f258e9 100644
--- a/gd/hci/facade/le_advertising_manager_facade.proto
+++ b/gd/hci/facade/le_advertising_manager_facade.proto
@@ -8,8 +8,17 @@
service LeAdvertisingManagerFacade {
rpc CreateAdvertiser(CreateAdvertiserRequest) returns (CreateAdvertiserResponse) {}
rpc ExtendedCreateAdvertiser(ExtendedCreateAdvertiserRequest) returns (ExtendedCreateAdvertiserResponse) {}
+ rpc EnableAdvertiser(EnableAdvertiserRequest) returns (google.protobuf.Empty) {}
+ rpc SetData(SetDataRequest) returns (google.protobuf.Empty) {}
+ rpc SetParameters(SetParametersRequest) returns (google.protobuf.Empty) {}
+ rpc SetPeriodicParameters(SetPeriodicParametersRequest) returns (google.protobuf.Empty) {}
+ rpc SetPeriodicData(SetPeriodicDataRequest) returns (google.protobuf.Empty) {}
+ rpc EnablePeriodicAdvertising(EnablePeriodicAdvertisingRequest) returns (google.protobuf.Empty) {}
+ rpc GetOwnAddress(GetOwnAddressRequest) returns (google.protobuf.Empty) {}
rpc GetNumberOfAdvertisingInstances(google.protobuf.Empty) returns (GetNumberOfAdvertisingInstancesResponse) {}
rpc RemoveAdvertiser(RemoveAdvertiserRequest) returns (google.protobuf.Empty) {}
+ rpc FetchCallbackEvents(google.protobuf.Empty) returns (stream CallbackMsg) {}
+ rpc FetchAddressEvents(google.protobuf.Empty) returns (stream AddressMsg) {}
}
message GapDataMsg {
@@ -31,6 +40,20 @@
LISTED_SCAN_AND_CONNECT = 0x3;
};
+enum AdvertisingProperty {
+ NONE = 0x00;
+ INCLUDE_TX_POWER = 0x06;
+};
+
+enum AdvertisingStatus {
+ SUCCESS = 0x00;
+ DATA_TOO_LARGE = 0x01;
+ TOO_MANY_ADVERTISERS = 0x02;
+ ALREADY_STARTED = 0x03;
+ INTERNAL_ERROR = 0x04;
+ FEATURE_UNSUPPORTED = 0x05;
+};
+
message AdvertisingConfig {
repeated GapDataMsg advertisement = 1;
repeated GapDataMsg scan_response = 2;
@@ -57,10 +80,16 @@
bool anonymous = 7;
bool include_tx_power = 8;
bool use_le_coded_phy = 9;
- int32 secondary_map_skip = 10;
+ int32 secondary_max_skip = 10;
int32 secondary_advertising_phy = 11;
int32 sid = 12;
- bool enable_scan_request_notification = 13;
+ bool enable_scan_request_notifications = 13;
+}
+
+message PeriodicAdvertisingParameters {
+ int32 min_interval = 1;
+ int32 max_interval = 2;
+ AdvertisingProperty advertising_property = 3;
}
message CreateAdvertiserRequest {
@@ -77,6 +106,42 @@
}
message ExtendedCreateAdvertiserResponse {
+ // -1 on error
+ int32 advertiser_id = 1;
+}
+
+message EnableAdvertiserRequest {
+ int32 advertiser_id = 1;
+ bool enable = 2;
+}
+
+message SetDataRequest {
+ int32 advertiser_id = 1;
+ bool set_scan_rsp = 2;
+ repeated GapDataMsg data = 3;
+}
+
+message SetParametersRequest {
+ int32 advertiser_id = 1;
+ AdvertisingConfig config = 2;
+}
+
+message SetPeriodicParametersRequest {
+ int32 advertiser_id = 1;
+ PeriodicAdvertisingParameters config = 2;
+}
+
+message SetPeriodicDataRequest {
+ int32 advertiser_id = 1;
+ repeated GapDataMsg data = 2;
+}
+
+message EnablePeriodicAdvertisingRequest {
+ int32 advertiser_id = 1;
+ bool enable = 2;
+}
+
+message GetOwnAddressRequest {
int32 advertiser_id = 1;
}
@@ -87,3 +152,28 @@
message RemoveAdvertiserRequest {
int32 advertiser_id = 1;
}
+
+enum CallbackMsgType {
+ ADVERTISING_SET_STARTED = 0;
+ ADVERTISING_ENABLED = 1;
+ ADVERTISING_DATA_SET = 2;
+ SCAN_RESPONSE_DATA_SET = 3;
+ ADVERTISING_PARAMETERS_UPDATED = 4;
+ PERIODIC_ADVERTISING_PARAMETERS_UPDATED = 5;
+ PERIODIC_ADVERTISING_DATA_SET = 6;
+ PERIODIC_ADVERTISING_ENABLED = 7;
+ OWN_ADDRESS_READ = 8;
+}
+
+message CallbackMsg {
+ CallbackMsgType message_type = 1;
+ uint32 advertiser_id = 2;
+ AdvertisingStatus status = 3;
+ uint32 data = 4;
+}
+
+message AddressMsg {
+ CallbackMsgType message_type = 1;
+ uint32 advertiser_id = 2;
+ bluetooth.facade.BluetoothAddressWithType address = 3;
+}
diff --git a/gd/rust/common/src/time.rs b/gd/rust/common/src/time.rs
index 5e63bf8..ec6d410 100644
--- a/gd/rust/common/src/time.rs
+++ b/gd/rust/common/src/time.rs
@@ -18,7 +18,7 @@
}
/// Reset the alarm to duration, starting from now
- pub fn reset(&mut self, duration: Duration) {
+ pub fn reset(&self, duration: Duration) {
self.fd
.get_ref()
.set(Expiration::OneShot(TimeSpec::from(duration)), TimerSetTimeFlags::empty())
@@ -26,12 +26,12 @@
}
/// Stop the alarm if it is currently started
- pub fn cancel(&mut self) {
+ pub fn cancel(&self) {
self.reset(Duration::from_millis(0));
}
/// Completes when the alarm has expired
- pub async fn expired(&mut self) {
+ pub async fn expired(&self) {
let mut read_ready = self.fd.readable().await.unwrap();
read_ready.clear_ready();
drop(read_ready);
@@ -94,7 +94,7 @@
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
let timer = Instant::now();
- let mut alarm = Alarm::new();
+ let alarm = Alarm::new();
alarm.reset(Duration::from_millis(10));
alarm.expired().await;
@@ -106,7 +106,7 @@
fn alarm_cancel_after_expired() {
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
- let mut alarm = Alarm::new();
+ let alarm = Alarm::new();
alarm.reset(Duration::from_millis(10));
tokio::time::sleep(Duration::from_millis(30)).await;
alarm.cancel();
@@ -131,7 +131,7 @@
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async {
let timer = Instant::now();
- let mut alarm = Alarm::new();
+ let alarm = Alarm::new();
alarm.reset(Duration::from_millis(10));
alarm.expired().await;
let ready_in_10_ms = async {
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/bluetooth_manager.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/bluetooth_manager.rs
index e6fa764..ed39771 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/bluetooth_manager.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/bluetooth_manager.rs
@@ -1,4 +1,4 @@
-use log::error;
+use log::{error, info};
use manager_service::iface_bluetooth_manager::{IBluetoothManager, IBluetoothManagerCallback};
@@ -12,29 +12,35 @@
/// Implementation of IBluetoothManager.
pub struct BluetoothManager {
manager_context: ManagerContext,
- callbacks: Vec<Box<dyn IBluetoothManagerCallback + Send>>,
+ callbacks: Vec<(u32, Box<dyn IBluetoothManagerCallback + Send>)>,
+ callbacks_last_id: u32,
}
impl BluetoothManager {
pub(crate) fn new(manager_context: ManagerContext) -> BluetoothManager {
- BluetoothManager { manager_context, callbacks: vec![] }
+ BluetoothManager { manager_context, callbacks: vec![], callbacks_last_id: 0 }
}
pub(crate) fn callback_hci_device_change(&self, hci_device: i32, present: bool) {
- for callback in &self.callbacks {
+ for (_, callback) in &self.callbacks {
callback.on_hci_device_changed(hci_device, present);
}
}
pub(crate) fn callback_hci_enabled_change(&self, hci_device: i32, enabled: bool) {
- for callback in &self.callbacks {
+ for (_, callback) in &self.callbacks {
callback.on_hci_enabled_changed(hci_device, enabled);
}
}
+
+ pub(crate) fn callback_disconnected(&mut self, id: u32) {
+ self.callbacks.retain(|x| x.0 != id);
+ }
}
impl IBluetoothManager for BluetoothManager {
fn start(&mut self, hci_interface: i32) {
+ info!("Starting {}", hci_interface);
if !config_util::modify_hci_n_enabled(hci_interface, true) {
error!("Config is not successfully modified");
}
@@ -42,6 +48,7 @@
}
fn stop(&mut self, hci_interface: i32) {
+ info!("Stopping {}", hci_interface);
if !config_util::modify_hci_n_enabled(hci_interface, false) {
error!("Config is not successfully modified");
}
@@ -55,9 +62,20 @@
result
}
- fn register_callback(&mut self, callback: Box<dyn IBluetoothManagerCallback + Send>) {
- // TODO: Handle callback disconnects.
- self.callbacks.push(callback);
+ fn register_callback(&mut self, mut callback: Box<dyn IBluetoothManagerCallback + Send>) {
+ let tx = self.manager_context.proxy.get_tx();
+
+ self.callbacks_last_id += 1;
+ let id = self.callbacks_last_id;
+
+ callback.register_disconnect(Box::new(move || {
+ let tx = tx.clone();
+ tokio::spawn(async move {
+ let _result = tx.send(state_machine::Message::CallbackDisconnected(id)).await;
+ });
+ }));
+
+ self.callbacks.push((id, callback));
}
fn get_floss_enabled(&mut self) -> bool {
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
index 9d0f736..61189cd 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
@@ -5,10 +5,10 @@
use nix::unistd::Pid;
use regex::Regex;
use std::process::{Child, Command, Stdio};
-use std::sync::Arc;
+use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::io::unix::AsyncFd;
-use tokio::sync::{mpsc, Mutex};
+use tokio::sync::mpsc;
// Directory for Bluetooth pid file
pub const PID_DIR: &str = "/var/run/bluetooth";
@@ -32,17 +32,28 @@
i32::from(state)
}
+/// Adapter state actions
#[derive(Debug)]
-pub enum StateMachineActions {
+pub enum AdapterStateActions {
StartBluetooth(i32),
StopBluetooth(i32),
BluetoothStarted(i32, i32), // PID and HCI
BluetoothStopped(i32),
}
+/// Enum of all the messages that state machine handles.
+#[derive(Debug)]
+pub enum Message {
+ AdapterStateChange(AdapterStateActions),
+ PidChange(inotify::EventMask, Option<String>),
+ HciDeviceChange(inotify::EventMask, Option<String>),
+ CallbackDisconnected(u32),
+ CommandTimeout(),
+}
+
pub struct StateMachineContext<PM> {
- tx: mpsc::Sender<StateMachineActions>,
- rx: mpsc::Receiver<StateMachineActions>,
+ tx: mpsc::Sender<Message>,
+ rx: mpsc::Receiver<Message>,
state_machine: ManagerStateMachine<PM>,
}
@@ -51,7 +62,7 @@
where
PM: ProcessManager + Send,
{
- let (tx, rx) = mpsc::channel::<StateMachineActions>(10);
+ let (tx, rx) = mpsc::channel::<Message>(10);
StateMachineContext { tx: tx, rx: rx, state_machine: state_machine }
}
@@ -66,8 +77,8 @@
#[derive(Clone)]
pub struct StateMachineProxy {
- tx: mpsc::Sender<StateMachineActions>,
- state: Arc<Mutex<State>>,
+ tx: mpsc::Sender<Message>,
+ state: Arc<std::sync::Mutex<State>>,
}
const TX_SEND_TIMEOUT_DURATION: Duration = Duration::from_secs(3);
@@ -77,21 +88,33 @@
pub fn start_bluetooth(&self, hci_interface: i32) {
let tx = self.tx.clone();
tokio::spawn(async move {
- let _ = tx.send(StateMachineActions::StartBluetooth(hci_interface)).await;
+ let _ = tx
+ .send(Message::AdapterStateChange(AdapterStateActions::StartBluetooth(
+ hci_interface,
+ )))
+ .await;
});
}
pub fn stop_bluetooth(&self, hci_interface: i32) {
let tx = self.tx.clone();
tokio::spawn(async move {
- let _ = tx.send(StateMachineActions::StopBluetooth(hci_interface)).await;
+ let _ = tx
+ .send(Message::AdapterStateChange(AdapterStateActions::StopBluetooth(
+ hci_interface,
+ )))
+ .await;
});
}
pub fn get_state(&self) -> State {
// This assumes that self.state is never locked for a long period, i.e. never lock() and
// await for something else without unlocking. Otherwise this function will block.
- return *futures::executor::block_on(self.state.lock());
+ return *self.state.lock().unwrap();
+ }
+
+ pub fn get_tx(&self) -> mpsc::Sender<Message> {
+ self.tx.clone()
}
}
@@ -103,7 +126,8 @@
AsyncFd::new(pid_detector).expect("failed to add async fd")
}
-fn get_hci_interface_from_pid_file_name(path: &str) -> Option<i32> {
+/// Given an pid path, returns the adapter index for that pid path.
+fn get_hci_index_from_pid_path(path: &str) -> Option<i32> {
let re = Regex::new(r"bluetooth([0-9]+).pid").unwrap();
re.captures(path)?.get(1)?.as_str().parse().ok()
}
@@ -119,161 +143,275 @@
AsyncFd::new(detector).expect("failed to add async fd")
}
-fn get_hci_interface_from_device(path: &str) -> Option<i32> {
+/// Given an hci sysfs path, returns the index of the hci device at the path.
+fn get_hci_index_from_device(path: &str) -> Option<i32> {
let re = Regex::new(r"hci([0-9]+)").unwrap();
re.captures(path)?.get(1)?.as_str().parse().ok()
}
+fn event_name_to_string(name: Option<&std::ffi::OsStr>) -> Option<String> {
+ if let Some(val) = &name {
+ if let Some(strval) = val.to_str() {
+ return Some(strval.to_string());
+ }
+ }
+
+ return None;
+}
+
pub async fn mainloop<PM>(
mut context: StateMachineContext<PM>,
bluetooth_manager: Arc<std::sync::Mutex<Box<BluetoothManager>>>,
) where
PM: ProcessManager + Send,
{
- let mut command_timeout = Alarm::new();
+ // Set up a command timeout listener to emit timeout messages
+ let command_timeout = Arc::new(Alarm::new());
+ let timeout_clone = command_timeout.clone();
+ let timeout_tx = context.tx.clone();
+
+ tokio::spawn(async move {
+ loop {
+ let _expired = timeout_clone.expired().await;
+ let _ = timeout_tx
+ .send_timeout(Message::CommandTimeout(), TX_SEND_TIMEOUT_DURATION)
+ .await
+ .unwrap();
+ }
+ });
+
+ // Set up a PID file listener to emit PID inotify messages
let mut pid_async_fd = pid_inotify_async_fd();
- let mut hci_devices_async_fd = hci_devices_inotify_async_fd();
- loop {
- tokio::select! {
- Some(action) = context.rx.recv() => {
- // Grab previous state from lock and release
- let mut next_state;
- let mut prev_state;
- {
- prev_state = *context.state_machine.state.lock().await;
- }
- let mut hci = 0;
+ let pid_tx = context.tx.clone();
- match action {
- StateMachineActions::StartBluetooth(i) => {
- next_state = State::TurningOn;
- hci = i;
+ tokio::spawn(async move {
+ debug!("Spawned pid notify task");
- match context.state_machine.action_start_bluetooth(i).await {
- true => {
- command_timeout.reset(COMMAND_TIMEOUT_DURATION);
- },
- false => command_timeout.cancel(),
+ loop {
+ let r = pid_async_fd.readable_mut();
+ let mut fd_ready = r.await.unwrap();
+ let mut buffer: [u8; 1024] = [0; 1024];
+ debug!("Found new pid inotify entries. Reading them");
+ match fd_ready.try_io(|inner| inner.get_mut().read_events(&mut buffer)) {
+ Ok(Ok(events)) => {
+ for event in events {
+ debug!("got some events from pid {:?}", event.mask);
+ let _ = pid_tx
+ .send_timeout(
+ Message::PidChange(event.mask, event_name_to_string(event.name)),
+ TX_SEND_TIMEOUT_DURATION,
+ )
+ .await
+ .unwrap();
}
- },
- StateMachineActions::StopBluetooth(i) => {
- next_state = State::TurningOff;
- hci = i;
+ }
+ Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
+ }
+ fd_ready.clear_ready();
+ drop(fd_ready);
+ }
+ });
- match context.state_machine.action_stop_bluetooth(i).await {
- true => {
- command_timeout.reset(COMMAND_TIMEOUT_DURATION);
- },
- false => command_timeout.cancel(),
- }
- },
- StateMachineActions::BluetoothStarted(pid, i) => {
- next_state = State::On;
- hci = i;
+ // Set up an HCI device listener to emit HCI device inotify messages
+ let mut hci_devices_async_fd = hci_devices_inotify_async_fd();
+ let hci_tx = context.tx.clone();
- match context.state_machine.action_on_bluetooth_started(pid, hci).await {
- true => {
- command_timeout.cancel();
- }
- false => error!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
- }
- },
- StateMachineActions::BluetoothStopped(i) => {
- next_state = State::Off;
- hci = i;
+ tokio::spawn(async move {
+ debug!("Spawned hci notify task");
+ loop {
+ let r = hci_devices_async_fd.readable_mut();
+ let mut fd_ready = r.await.unwrap();
+ let mut buffer: [u8; 1024] = [0; 1024];
+ debug!("Found new hci device entries. Reading them.");
+ match fd_ready.try_io(|inner| inner.get_mut().read_events(&mut buffer)) {
+ Ok(Ok(events)) => {
+ for event in events {
+ let _ = hci_tx
+ .send_timeout(
+ Message::HciDeviceChange(
+ event.mask,
+ event_name_to_string(event.name),
+ ),
+ TX_SEND_TIMEOUT_DURATION,
+ )
+ .await
+ .unwrap();
+ }
+ }
+ Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
+ }
+ fd_ready.clear_ready();
+ drop(fd_ready);
+ }
+ });
- match context.state_machine.action_on_bluetooth_stopped().await {
- true => {
- command_timeout.cancel();
- }
- false => {
- command_timeout.reset(COMMAND_TIMEOUT_DURATION);
- }
- }
- },
- };
+ // Listen for all messages and act on them
+ loop {
+ let m = context.rx.recv().await;
- // Only emit enabled event for certain transitions
- if next_state != prev_state &&
- (next_state == State::On || prev_state == State::On) {
- bluetooth_manager.lock().unwrap().callback_hci_enabled_change(hci, next_state == State::On);
- }
+ if m.is_none() {
+ info!("Exiting manager mainloop");
+ break;
+ }
+ println!("Message handler: {:?}", m);
+
+ match m.unwrap() {
+ // Adapter action has changed
+ Message::AdapterStateChange(action) => {
+ // Grab previous state from lock and release
+ let next_state;
+ let prev_state;
+ {
+ prev_state = *context.state_machine.state.lock().unwrap();
+ }
+ let mut hci = 0;
+
+ match action {
+ AdapterStateActions::StartBluetooth(i) => {
+ next_state = State::TurningOn;
+ hci = i;
+
+ match context.state_machine.action_start_bluetooth(i) {
+ true => {
+ command_timeout.reset(COMMAND_TIMEOUT_DURATION);
+ }
+ false => command_timeout.cancel(),
+ }
+ }
+ AdapterStateActions::StopBluetooth(i) => {
+ next_state = State::TurningOff;
+ hci = i;
+
+ match context.state_machine.action_stop_bluetooth(i) {
+ true => {
+ command_timeout.reset(COMMAND_TIMEOUT_DURATION);
+ }
+ false => command_timeout.cancel(),
+ }
+ }
+ AdapterStateActions::BluetoothStarted(pid, i) => {
+ next_state = State::On;
+ hci = i;
+
+ match context.state_machine.action_on_bluetooth_started(pid, hci) {
+ true => {
+ command_timeout.cancel();
+ }
+ false => error!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
+ }
+ }
+ AdapterStateActions::BluetoothStopped(i) => {
+ next_state = State::Off;
+ hci = i;
+
+ match context.state_machine.action_on_bluetooth_stopped() {
+ true => {
+ command_timeout.cancel();
+ }
+ false => {
+ command_timeout.reset(COMMAND_TIMEOUT_DURATION);
+ }
+ }
+ }
+ };
+
+ // Only emit enabled event for certain transitions
+ if next_state != prev_state && (next_state == State::On || prev_state == State::On)
+ {
+ bluetooth_manager
+ .lock()
+ .unwrap()
+ .callback_hci_enabled_change(hci, next_state == State::On);
+ }
+ }
+
+ // Monitored pid directory has a change
+ Message::PidChange(mask, filename) => match (mask, &filename) {
+ (inotify::EventMask::CREATE, Some(fname)) => {
+ let path = std::path::Path::new(PID_DIR).join(&fname);
+ match (get_hci_index_from_pid_path(&fname), tokio::fs::read(path).await.ok()) {
+ (Some(hci), Some(s)) => {
+ let pid = String::from_utf8(s)
+ .expect("invalid pid file")
+ .parse::<i32>()
+ .unwrap_or(0);
+ debug!("Sending bluetooth started action for pid={}, hci={}", pid, hci);
+ let _ = context
+ .tx
+ .send_timeout(
+ Message::AdapterStateChange(
+ AdapterStateActions::BluetoothStarted(pid, hci),
+ ),
+ TX_SEND_TIMEOUT_DURATION,
+ )
+ .await
+ .unwrap();
+ }
+ (hci, s) => {
+ warn!("invalid file hci={:?} pid_file={:?}", hci, s)
+ }
+ }
+ }
+ (inotify::EventMask::DELETE, Some(fname)) => {
+ if let Some(hci) = get_hci_index_from_pid_path(&fname) {
+ debug!("Sending bluetooth stopped action for hci={}", hci);
+ context
+ .tx
+ .send_timeout(
+ Message::AdapterStateChange(AdapterStateActions::BluetoothStopped(
+ hci,
+ )),
+ TX_SEND_TIMEOUT_DURATION,
+ )
+ .await
+ .unwrap();
+ }
+ }
+ _ => debug!("Ignored event {:?} - {:?}", mask, &filename),
},
- _expired = command_timeout.expired() => {
- info!("expired {:?}", *context.state_machine.state.lock().await);
- let timeout_action = context.state_machine.action_on_command_timeout().await;
+
+ // Monitored hci directory has a change
+ Message::HciDeviceChange(mask, filename) => match (mask, &filename) {
+ (inotify::EventMask::CREATE, Some(fname)) => {
+ match get_hci_index_from_device(&fname) {
+ Some(hci) => {
+ bluetooth_manager.lock().unwrap().callback_hci_device_change(hci, true);
+ }
+ _ => (),
+ }
+ }
+ (inotify::EventMask::DELETE, Some(fname)) => {
+ match get_hci_index_from_device(&fname) {
+ Some(hci) => {
+ bluetooth_manager
+ .lock()
+ .unwrap()
+ .callback_hci_device_change(hci, false);
+ }
+ _ => (),
+ }
+ }
+ _ => debug!("Ignored event {:?} - {:?}", mask, &filename),
+ },
+
+ // Callback client has disconnected
+ Message::CallbackDisconnected(id) => {
+ bluetooth_manager.lock().unwrap().callback_disconnected(id);
+ }
+
+ // Handle command timeouts
+ Message::CommandTimeout() => {
+ // Hold state lock for short duration
+ {
+ debug!("expired {:?}", *context.state_machine.state.lock().unwrap());
+ }
+ let timeout_action = context.state_machine.action_on_command_timeout();
match timeout_action {
StateMachineTimeoutActions::Noop => (),
_ => command_timeout.reset(COMMAND_TIMEOUT_DURATION),
}
- },
- r = pid_async_fd.readable_mut() => {
- let mut fd_ready = r.unwrap();
- let mut buffer: [u8; 1024] = [0; 1024];
- match fd_ready.try_io(|inner| inner.get_mut().read_events(&mut buffer)) {
- Ok(Ok(events)) => {
- for event in events {
- debug!("got some events from pid {:?}", event.mask);
- match (event.mask, event.name) {
- (inotify::EventMask::CREATE, Some(oss)) => {
- let path = std::path::Path::new(PID_DIR).join(oss);
- let file_name = oss.to_str().unwrap_or("invalid file");
- match (get_hci_interface_from_pid_file_name(file_name), tokio::fs::read(path).await.ok()) {
- (Some(hci), Some(s)) => {
- let pid = String::from_utf8(s).expect("invalid pid file").parse::<i32>().unwrap_or(0);
- let _ = context.tx.send_timeout(StateMachineActions::BluetoothStarted(pid, hci), TX_SEND_TIMEOUT_DURATION).await.unwrap();
- },
- (hci, s) => warn!("invalid file hci={:?} pid_file={:?}", hci, s),
- }
- },
- (inotify::EventMask::DELETE, Some(oss)) => {
- let file_name = oss.to_str().unwrap_or("invalid file");
- if let Some(hci) = get_hci_interface_from_pid_file_name(file_name) {
- context.tx.send_timeout(StateMachineActions::BluetoothStopped(hci), TX_SEND_TIMEOUT_DURATION).await.unwrap();
- }
- },
- _ => debug!("Ignored event {:?}", event.mask)
- }
- }
- },
- Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
- }
- fd_ready.clear_ready();
- drop(fd_ready);
- },
- r = hci_devices_async_fd.readable_mut() => {
- let mut fd_ready = r.unwrap();
- let mut buffer: [u8; 1024] = [0; 1024];
- match fd_ready.try_io(|inner| inner.get_mut().read_events(&mut buffer)) {
- Ok(Ok(events)) => {
- for event in events {
- match (event.mask, event.name) {
- (inotify::EventMask::CREATE, Some(oss)) => {
- match get_hci_interface_from_device(oss.to_str().unwrap_or("invalid hci device")) {
- Some(hci) => {
- bluetooth_manager.lock().unwrap().callback_hci_device_change(hci, true);
- },
- _ => (),
- }
- },
- (inotify::EventMask::DELETE, Some(oss)) => {
- match get_hci_interface_from_device(oss.to_str().unwrap_or("invalid hci device")) {
- Some(hci) => {
- bluetooth_manager.lock().unwrap().callback_hci_device_change(hci, false);
- },
- _ => (),
- }
- },
- _ => debug!("Ignored event {:?}", event.mask)
- }
- }
- },
- Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
- }
- fd_ready.clear_ready();
- drop(fd_ready);
- },
+ }
}
}
}
@@ -342,7 +480,7 @@
}
struct ManagerStateMachine<PM> {
- state: Arc<Mutex<State>>,
+ state: Arc<std::sync::Mutex<State>>,
process_manager: PM,
hci_interface: i32,
bluetooth_pid: i32,
@@ -373,7 +511,7 @@
{
pub fn new(process_manager: PM) -> ManagerStateMachine<PM> {
ManagerStateMachine {
- state: Arc::new(Mutex::new(State::Off)),
+ state: Arc::new(std::sync::Mutex::new(State::Off)),
process_manager: process_manager,
hci_interface: 0,
bluetooth_pid: 0,
@@ -381,8 +519,8 @@
}
/// Returns true if we are starting bluetooth process.
- pub async fn action_start_bluetooth(&mut self, hci_interface: i32) -> bool {
- let mut state = self.state.lock().await;
+ pub fn action_start_bluetooth(&mut self, hci_interface: i32) -> bool {
+ let mut state = self.state.lock().unwrap();
match *state {
State::Off => {
*state = State::TurningOn;
@@ -396,7 +534,7 @@
}
/// Returns true if we are stopping bluetooth process.
- pub async fn action_stop_bluetooth(&mut self, hci_interface: i32) -> bool {
+ pub fn action_stop_bluetooth(&mut self, hci_interface: i32) -> bool {
if self.hci_interface != hci_interface {
warn!(
"We are running hci{} but attempting to stop hci{}",
@@ -405,7 +543,7 @@
return false;
}
- let mut state = self.state.lock().await;
+ let mut state = self.state.lock().unwrap();
match *state {
State::On => {
*state = State::TurningOff;
@@ -423,8 +561,8 @@
}
/// Returns true if the event is expected.
- pub async fn action_on_bluetooth_started(&mut self, pid: i32, hci_interface: i32) -> bool {
- let mut state = self.state.lock().await;
+ pub fn action_on_bluetooth_started(&mut self, pid: i32, hci_interface: i32) -> bool {
+ let mut state = self.state.lock().unwrap();
if self.hci_interface != hci_interface {
warn!(
"We should start hci{} but hci{} is started; capturing that process",
@@ -443,8 +581,8 @@
/// Returns true if the event is expected.
/// If unexpected, Bluetooth probably crashed;
/// start the timer for restart timeout
- pub async fn action_on_bluetooth_stopped(&mut self) -> bool {
- let mut state = self.state.lock().await;
+ pub fn action_on_bluetooth_stopped(&mut self) -> bool {
+ let mut state = self.state.lock().unwrap();
match *state {
State::TurningOff => {
@@ -466,18 +604,18 @@
/// Triggered on Bluetooth start/stop timeout. Return the actions that the
/// state machine has taken, for the external context to reset the timer.
- pub async fn action_on_command_timeout(&mut self) -> StateMachineTimeoutActions {
- let mut state = self.state.lock().await;
+ pub fn action_on_command_timeout(&mut self) -> StateMachineTimeoutActions {
+ let mut state = self.state.lock().unwrap();
match *state {
State::TurningOn => {
- info!("Restarting bluetooth");
+ info!("Restarting bluetooth {}", self.hci_interface);
*state = State::TurningOn;
self.process_manager.stop(format! {"{}", self.hci_interface});
self.process_manager.start(format! {"{}", self.hci_interface});
StateMachineTimeoutActions::RetryStart
}
State::TurningOff => {
- info!("Killing bluetooth");
+ info!("Killing bluetooth {}", self.hci_interface);
self.process_manager.stop(format! {"{}", self.hci_interface});
StateMachineTimeoutActions::RetryStop
}
@@ -538,7 +676,7 @@
tokio::runtime::Runtime::new().unwrap().block_on(async {
let process_manager = MockProcessManager::new();
let state_machine = ManagerStateMachine::new(process_manager);
- assert_eq!(*state_machine.state.lock().await, State::Off);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
})
}
@@ -547,8 +685,8 @@
tokio::runtime::Runtime::new().unwrap().block_on(async {
let process_manager = MockProcessManager::new();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_stop_bluetooth(0).await;
- assert_eq!(*state_machine.state.lock().await, State::Off);
+ state_machine.action_stop_bluetooth(0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
})
}
@@ -559,8 +697,8 @@
// Expect to send start command
process_manager.expect_start();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+ state_machine.action_start_bluetooth(0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::TurningOn);
})
}
@@ -571,8 +709,8 @@
// Expect to send start command just once
process_manager.expect_start();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- assert_eq!(state_machine.action_start_bluetooth(0).await, false);
+ state_machine.action_start_bluetooth(0);
+ assert_eq!(state_machine.action_start_bluetooth(0), false);
})
}
@@ -582,9 +720,9 @@
let mut process_manager = MockProcessManager::new();
process_manager.expect_start();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- assert_eq!(*state_machine.state.lock().await, State::On);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::On);
})
}
@@ -596,12 +734,12 @@
process_manager.expect_stop();
process_manager.expect_start(); // start bluetooth again
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
+ state_machine.action_start_bluetooth(0);
assert_eq!(
- state_machine.action_on_command_timeout().await,
+ state_machine.action_on_command_timeout(),
StateMachineTimeoutActions::RetryStart
);
- assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::TurningOn);
})
}
@@ -613,9 +751,9 @@
// Expect to send stop command
process_manager.expect_stop();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_stop_bluetooth(0).await;
- assert_eq!(*state_machine.state.lock().await, State::Off);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_stop_bluetooth(0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
})
}
@@ -627,10 +765,10 @@
// Expect to send stop command
process_manager.expect_stop();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- state_machine.action_stop_bluetooth(0).await;
- assert_eq!(*state_machine.state.lock().await, State::TurningOff);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ state_machine.action_stop_bluetooth(0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::TurningOff);
})
}
@@ -642,10 +780,10 @@
// Expect to start again
process_manager.expect_start();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- assert_eq!(state_machine.action_on_bluetooth_stopped().await, false);
- assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ assert_eq!(state_machine.action_on_bluetooth_stopped(), false);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::TurningOn);
})
}
@@ -656,11 +794,11 @@
process_manager.expect_start();
process_manager.expect_stop();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- state_machine.action_stop_bluetooth(0).await;
- state_machine.action_on_bluetooth_stopped().await;
- assert_eq!(*state_machine.state.lock().await, State::Off);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ state_machine.action_stop_bluetooth(0);
+ state_machine.action_on_bluetooth_stopped();
+ assert_eq!(*state_machine.state.lock().unwrap(), State::Off);
})
}
@@ -672,35 +810,26 @@
process_manager.expect_stop();
process_manager.expect_start();
let mut state_machine = ManagerStateMachine::new(process_manager);
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- state_machine.action_stop_bluetooth(0).await;
- state_machine.action_on_bluetooth_stopped().await;
- state_machine.action_start_bluetooth(0).await;
- state_machine.action_on_bluetooth_started(0, 0).await;
- assert_eq!(*state_machine.state.lock().await, State::On);
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ state_machine.action_stop_bluetooth(0);
+ state_machine.action_on_bluetooth_stopped();
+ state_machine.action_start_bluetooth(0);
+ state_machine.action_on_bluetooth_started(0, 0);
+ assert_eq!(*state_machine.state.lock().unwrap(), State::On);
})
}
#[test]
fn path_to_hci_interface() {
- assert_eq!(
- get_hci_interface_from_pid_file_name("/var/run/bluetooth/bluetooth0.pid"),
- Some(0)
- );
- assert_eq!(
- get_hci_interface_from_pid_file_name("/var/run/bluetooth/bluetooth1.pid"),
- Some(1)
- );
- assert_eq!(
- get_hci_interface_from_pid_file_name("/var/run/bluetooth/bluetooth10.pid"),
- Some(10)
- );
- assert_eq!(get_hci_interface_from_pid_file_name("/var/run/bluetooth/garbage"), None);
+ assert_eq!(get_hci_index_from_pid_path("/var/run/bluetooth/bluetooth0.pid"), Some(0));
+ assert_eq!(get_hci_index_from_pid_path("/var/run/bluetooth/bluetooth1.pid"), Some(1));
+ assert_eq!(get_hci_index_from_pid_path("/var/run/bluetooth/bluetooth10.pid"), Some(10));
+ assert_eq!(get_hci_index_from_pid_path("/var/run/bluetooth/garbage"), None);
- assert_eq!(get_hci_interface_from_device("/sys/class/bluetooth/hci0"), Some(0));
- assert_eq!(get_hci_interface_from_device("/sys/class/bluetooth/hci1"), Some(1));
- assert_eq!(get_hci_interface_from_device("/sys/class/bluetooth/hci10"), Some(10));
- assert_eq!(get_hci_interface_from_device("/sys/class/bluetooth/eth0"), None);
+ assert_eq!(get_hci_index_from_device("/sys/class/bluetooth/hci0"), Some(0));
+ assert_eq!(get_hci_index_from_device("/sys/class/bluetooth/hci1"), Some(1));
+ assert_eq!(get_hci_index_from_device("/sys/class/bluetooth/hci10"), Some(10));
+ assert_eq!(get_hci_index_from_device("/sys/class/bluetooth/eth0"), None);
}
}
diff --git a/gd/rust/linux/service/src/iface_bluetooth_media.rs b/gd/rust/linux/service/src/iface_bluetooth_media.rs
index e375db8..233f3e5 100644
--- a/gd/rust/linux/service/src/iface_bluetooth_media.rs
+++ b/gd/rust/linux/service/src/iface_bluetooth_media.rs
@@ -16,7 +16,14 @@
#[dbus_proxy_obj(BluetoothMediaCallback, "org.chromium.bluetooth.BluetoothMediaCallback")]
impl IBluetoothMediaCallback for BluetoothMediaCallbackDBus {
#[dbus_method("OnBluetoothAudioDeviceAdded")]
- fn on_bluetooth_audio_device_added(&self, addr: String) {}
+ fn on_bluetooth_audio_device_added(
+ &self,
+ addr: String,
+ sample_rate: i32,
+ bits_per_sample: i32,
+ channel_mode: i32,
+ ) {
+ }
#[dbus_method("OnBluetoothAudioDeviceRemoved")]
fn on_bluetooth_audio_device_removed(&self, addr: String) {}
@@ -61,6 +68,9 @@
true
}
+ #[dbus_method("SetVolume")]
+ fn set_volume(&mut self, volume: i32) {}
+
#[dbus_method("StartAudioRequest")]
fn start_audio_request(&mut self) {}
diff --git a/gd/rust/linux/stack/src/bluetooth_media.rs b/gd/rust/linux/stack/src/bluetooth_media.rs
index b444307..2566dc4 100644
--- a/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -1,13 +1,14 @@
//! Anything related to audio and media API.
-use bt_topshim::btif::BluetoothInterface;
+use bt_topshim::btif::{BluetoothInterface, RawAddress};
use bt_topshim::profiles::a2dp::{
A2dp, A2dpCallbacks, A2dpCallbacksDispatcher, A2dpCodecBitsPerSample, A2dpCodecChannelMode,
- A2dpCodecSampleRate, BtavConnectionState,
+ A2dpCodecConfig, A2dpCodecIndex, A2dpCodecSampleRate, BtavConnectionState,
};
use bt_topshim::profiles::avrcp::Avrcp;
use bt_topshim::topstack;
+use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::Arc;
use std::sync::Mutex;
@@ -35,13 +36,20 @@
bits_per_sample: i32,
channel_mode: i32,
) -> bool;
+ fn set_volume(&mut self, volume: i32);
fn start_audio_request(&mut self);
fn stop_audio_request(&mut self);
}
pub trait IBluetoothMediaCallback {
///
- fn on_bluetooth_audio_device_added(&self, addr: String);
+ fn on_bluetooth_audio_device_added(
+ &self,
+ addr: String,
+ sample_rate: i32,
+ bits_per_sample: i32,
+ channel_mode: i32,
+ );
///
fn on_bluetooth_audio_device_removed(&self, addr: String);
@@ -55,6 +63,8 @@
tx: Sender<Message>,
a2dp: Option<A2dp>,
avrcp: Option<Avrcp>,
+ a2dp_states: HashMap<RawAddress, BtavConnectionState>,
+ selectable_caps: HashMap<RawAddress, Vec<A2dpCodecConfig>>,
}
impl BluetoothMedia {
@@ -67,24 +77,55 @@
tx,
a2dp: None,
avrcp: None,
+ a2dp_states: HashMap::new(),
+ selectable_caps: HashMap::new(),
}
}
pub fn dispatch_a2dp_callbacks(&mut self, cb: A2dpCallbacks) {
match cb {
- A2dpCallbacks::ConnectionState(addr, state) => match BtavConnectionState::from(state) {
- BtavConnectionState::Connected => {
- self.for_all_callbacks(|callback| {
- callback.on_bluetooth_audio_device_added(addr.to_string());
- });
+ A2dpCallbacks::ConnectionState(addr, state) => {
+ if !self.a2dp_states.get(&addr).is_none()
+ && state == *self.a2dp_states.get(&addr).unwrap()
+ {
+ return;
}
- BtavConnectionState::Connecting => {}
- BtavConnectionState::Disconnected => {}
- BtavConnectionState::Disconnecting => {}
- },
- A2dpCallbacks::AudioState(addr, state) => {}
- A2dpCallbacks::AudioConfig(addr, config, local_caps, selectable_caps) => {}
- A2dpCallbacks::MandatoryCodecPreferred(addr) => {}
+ match state {
+ BtavConnectionState::Connected => {
+ if let Some(caps) = self.selectable_caps.get(&addr) {
+ for cap in caps {
+ // TODO: support codecs other than SBC.
+ if A2dpCodecIndex::SrcSbc != A2dpCodecIndex::from(cap.codec_type) {
+ continue;
+ }
+
+ self.for_all_callbacks(|callback| {
+ callback.on_bluetooth_audio_device_added(
+ addr.to_string(),
+ cap.sample_rate,
+ cap.bits_per_sample,
+ cap.channel_mode,
+ );
+ });
+ return;
+ }
+ }
+ }
+ BtavConnectionState::Connecting => {}
+ BtavConnectionState::Disconnected => {
+ self.for_all_callbacks(|callback| {
+ callback.on_bluetooth_audio_device_removed(addr.to_string());
+ });
+ }
+ BtavConnectionState::Disconnecting => {}
+ };
+ self.a2dp_states.insert(addr, state);
+ }
+ A2dpCallbacks::AudioState(_addr, _state) => {}
+ A2dpCallbacks::AudioConfig(addr, _config, _local_caps, selectable_caps) => {
+ self.selectable_caps.insert(addr, selectable_caps);
+ }
+ A2dpCallbacks::MandatoryCodecPreferred(_addr) => {}
}
}
@@ -159,6 +200,13 @@
true
}
+ fn set_volume(&mut self, volume: i32) {
+ match i8::try_from(volume) {
+ Ok(val) => self.avrcp.as_mut().unwrap().set_volume(val),
+ _ => (),
+ };
+ }
+
fn start_audio_request(&mut self) {
self.a2dp.as_mut().unwrap().start_audio_request();
}
diff --git a/gd/rust/stack/src/hci/mod.rs b/gd/rust/stack/src/hci/mod.rs
index 2fb5f6a..8f24f2a 100644
--- a/gd/rust/stack/src/hci/mod.rs
+++ b/gd/rust/stack/src/hci/mod.rs
@@ -173,7 +173,7 @@
mut cmd_rx: Receiver<QueuedCommand>,
) {
let mut pending: Option<QueuedCommand> = None;
- let mut hci_timeout = Alarm::new();
+ let hci_timeout = Alarm::new();
loop {
select! {
Some(evt) = consume(&evt_rx) => {
diff --git a/gd/rust/topshim/btav/btav_shim.cc b/gd/rust/topshim/btav/btav_shim.cc
index e867b58..c1f7a6c 100644
--- a/gd/rust/topshim/btav/btav_shim.cc
+++ b/gd/rust/topshim/btav/btav_shim.cc
@@ -255,6 +255,9 @@
return intf_->DisconnectDevice(addr);
}
+void AvrcpIntf::set_volume(int8_t volume) {
+ return mVolumeInterface.SetVolume(volume);
+}
} // namespace rust
} // namespace topshim
} // namespace bluetooth
diff --git a/gd/rust/topshim/btav/btav_shim.h b/gd/rust/topshim/btav/btav_shim.h
index 158d775..d2e9d0b 100644
--- a/gd/rust/topshim/btav/btav_shim.h
+++ b/gd/rust/topshim/btav/btav_shim.h
@@ -66,6 +66,9 @@
int connect(RustRawAddress bt_addr);
int disconnect(RustRawAddress bt_addr);
+ // interface for Audio server
+ void set_volume(int8_t volume);
+
private:
bluetooth::avrcp::ServiceInterface* intf_;
};
diff --git a/gd/rust/topshim/src/btif.rs b/gd/rust/topshim/src/btif.rs
index 5b54147..17ce6e3 100644
--- a/gd/rust/topshim/src/btif.rs
+++ b/gd/rust/topshim/src/btif.rs
@@ -268,7 +268,7 @@
/// A shared address structure that has the same representation as
/// bindings::RawAddress. Macros `deref_ffi_address` and `cast_to_ffi_address`
/// are used for transforming between bindings::RawAddress at ffi boundaries.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Hash, Eq, PartialEq)]
#[repr(C)]
pub struct RawAddress {
pub val: [u8; 6],
diff --git a/gd/rust/topshim/src/profiles/a2dp.rs b/gd/rust/topshim/src/profiles/a2dp.rs
index 7a868f4..ad13ff1 100644
--- a/gd/rust/topshim/src/profiles/a2dp.rs
+++ b/gd/rust/topshim/src/profiles/a2dp.rs
@@ -57,9 +57,9 @@
pub const MIN: A2dpCodecIndex = A2dpCodecIndex::SrcSbc;
}
-impl From<u32> for A2dpCodecIndex {
- fn from(item: u32) -> Self {
- A2dpCodecIndex::from_u32(item).unwrap_or_else(|| A2dpCodecIndex::MIN)
+impl From<i32> for A2dpCodecIndex {
+ fn from(item: i32) -> Self {
+ A2dpCodecIndex::from_i32(item).unwrap_or_else(|| A2dpCodecIndex::MIN)
}
}
diff --git a/gd/rust/topshim/src/profiles/avrcp.rs b/gd/rust/topshim/src/profiles/avrcp.rs
index 66a474d..649000b 100644
--- a/gd/rust/topshim/src/profiles/avrcp.rs
+++ b/gd/rust/topshim/src/profiles/avrcp.rs
@@ -11,6 +11,7 @@
fn init(self: Pin<&mut AvrcpIntf>);
fn cleanup(self: Pin<&mut AvrcpIntf>);
+ fn set_volume(self: Pin<&mut AvrcpIntf>, volume: i8);
}
extern "Rust" {}
@@ -43,4 +44,8 @@
self.internal.pin_mut().cleanup();
true
}
+
+ pub fn set_volume(&mut self, volume: i8) {
+ self.internal.pin_mut().set_volume(volume);
+ }
}
diff --git a/gd/stack_manager.cc b/gd/stack_manager.cc
index 3366c7b..17b6582 100644
--- a/gd/stack_manager.cc
+++ b/gd/stack_manager.cc
@@ -16,9 +16,7 @@
#include "stack_manager.h"
-#include <fcntl.h>
#include <stdio.h>
-#include <unistd.h>
#include <chrono>
#include <future>
#include <queue>
@@ -36,9 +34,6 @@
namespace bluetooth {
-// Assume we are hci0 for now
-constexpr char bluetooth_pid_file[] = "/var/run/bluetooth/bluetooth0.pid";
-
void StackManager::StartUp(ModuleList* modules, Thread* stack_thread) {
management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
handler_ = new Handler(management_thread_);
@@ -59,10 +54,6 @@
"Can't start stack, last instance: %s",
registry_.last_instance_.c_str());
- pid_fd_ = open(bluetooth_pid_file, O_WRONLY | O_CREAT, 0644);
- pid_t my_pid = getpid();
- dprintf(pid_fd_, "%d\n", my_pid);
-
LOG_INFO("init complete");
}
@@ -92,9 +83,6 @@
handler_->WaitUntilStopped(std::chrono::milliseconds(2000));
delete handler_;
delete management_thread_;
-
- unlink(bluetooth_pid_file);
- close(pid_fd_);
}
void StackManager::handle_shut_down(std::promise<void> promise) {
diff --git a/gd/stack_manager.h b/gd/stack_manager.h
index 985bd0b..c09a7f6 100644
--- a/gd/stack_manager.h
+++ b/gd/stack_manager.h
@@ -41,7 +41,6 @@
os::Thread* management_thread_ = nullptr;
os::Handler* handler_ = nullptr;
ModuleRegistry registry_;
- int pid_fd_ = 0;
void handle_start_up(ModuleList* modules, os::Thread* stack_thread, std::promise<void> promise);
void handle_shut_down(std::promise<void> promise);
diff --git a/hci/src/hci_layer_linux.cc b/hci/src/hci_layer_linux.cc
index e5e4fde..788abc0 100644
--- a/hci/src/hci_layer_linux.cc
+++ b/hci/src/hci_layer_linux.cc
@@ -35,6 +35,7 @@
#include "buffer_allocator.h"
#include "check.h"
+#include "gd/common/init_flags.h"
#include "hci_internals.h"
#include "hci_layer.h"
#include "osi/include/compat.h"
@@ -171,23 +172,31 @@
LOG(INFO) << __func__;
char prop_value[PROPERTY_VALUE_MAX];
- osi_property_get("bluetooth.interface", prop_value, "0");
+ int read = osi_property_get("bluetooth.interface", prop_value, nullptr);
- errno = 0;
- if (memcmp(prop_value, "hci", 3))
- hci_interface = strtol(prop_value, NULL, 10);
- else
- hci_interface = strtol(prop_value + 3, NULL, 10);
- if (errno) hci_interface = 0;
+ // If bluetooth.interface was read from properties, use it. Otherwise, default
+ // to the value read from the init flags.
+ if (read) {
+ errno = 0;
+ if (memcmp(prop_value, "hci", 3))
+ hci_interface = strtol(prop_value, NULL, 10);
+ else
+ hci_interface = strtol(prop_value + 3, NULL, 10);
+ if (errno) hci_interface = 0;
+ } else {
+ hci_interface = bluetooth::common::InitFlags::GetAdapterIndex();
+ }
LOG(INFO) << "Using interface hci" << +hci_interface;
+#if !defined(TARGET_FLOSS)
osi_property_get("bluetooth.rfkill", prop_value, "1");
rfkill_en = atoi(prop_value);
if (rfkill_en) {
rfkill(0);
}
+#endif
int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
CHECK(fd >= 0) << "socket create error" << strerror(errno);
@@ -242,7 +251,9 @@
reader_thread = NULL;
}
+#if !defined(TARGET_FLOSS)
rfkill(1);
+#endif
}
void hci_transmit(BT_HDR* packet) {
diff --git a/main/shim/stack.cc b/main/shim/stack.cc
index e4cd6fd..9b826fc 100644
--- a/main/shim/stack.cc
+++ b/main/shim/stack.cc
@@ -18,9 +18,15 @@
#include "device/include/controller.h"
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+
#include "gd/att/att_module.h"
#include "gd/btaa/activity_attribution.h"
#include "gd/common/init_flags.h"
+#include "gd/common/strings.h"
#include "gd/hal/hci_hal.h"
#include "gd/hci/acl_manager.h"
#include "gd/hci/controller.h"
@@ -55,6 +61,34 @@
namespace bluetooth {
namespace shim {
+using ::bluetooth::common::InitFlags;
+using ::bluetooth::common::StringFormat;
+
+namespace {
+// PID file format
+constexpr char pid_file_format[] = "/var/run/bluetooth/bluetooth%d.pid";
+
+void CreatePidFile() {
+ std::string pid_file =
+ StringFormat(pid_file_format, InitFlags::GetAdapterIndex());
+ int pid_fd_ = open(pid_file.c_str(), O_WRONLY | O_CREAT, 0644);
+ if (!pid_fd_) return;
+
+ pid_t my_pid = getpid();
+ dprintf(pid_fd_, "%d\n", my_pid);
+ close(pid_fd_);
+
+ LOG_INFO("%s - Created pid file %s", __func__, pid_file.c_str());
+}
+
+void RemovePidFile() {
+ std::string pid_file =
+ StringFormat(pid_file_format, InitFlags::GetAdapterIndex());
+ unlink(pid_file.c_str());
+ LOG_INFO("%s - Deleted pid file %s", __func__, pid_file.c_str());
+}
+} // namespace
+
Stack* Stack::GetInstance() {
static Stack instance;
return &instance;
@@ -87,6 +121,9 @@
rust::get_controller(**rust_stack_));
}
bluetooth::shim::hci_on_reset_complete();
+
+ // Create pid since we're up and running
+ CreatePidFile();
return;
}
@@ -168,6 +205,9 @@
if (common::init_flags::btaa_hci_is_enabled()) {
bluetooth::shim::init_activity_attribution();
}
+
+ // Create pid since we're up and running
+ CreatePidFile();
}
void Stack::Start(ModuleList* modules) {
@@ -184,6 +224,9 @@
}
void Stack::Stop() {
+ // First remove pid file so clients no stack is going down
+ RemovePidFile();
+
if (common::init_flags::gd_rust_is_enabled()) {
if (rust_stack_ != nullptr) {
rust::stack_stop(**rust_stack_);
diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc
new file mode 100644
index 0000000..2c7bebd
--- /dev/null
+++ b/stack/btm/btm_acl.cc
@@ -0,0 +1,2595 @@
+/******************************************************************************
+ *
+ * Copyright 2000-2012 Broadcom Corporation
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/*****************************************************************************
+ *
+ * Name: btm_acl.cc
+ *
+ * Description: This file contains functions that handle ACL connections.
+ * This includes operations such as hold and sniff modes,
+ * supported packet types.
+ *
+ * This module contains both internal and external (API)
+ * functions. External (API) functions are distinguishable
+ * by their names beginning with uppercase BTM.
+ *
+ *
+ *****************************************************************************/
+
+#define LOG_TAG "btm_acl"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt_common.h"
+#include "bt_target.h"
+#include "bt_types.h"
+#include "bt_utils.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "btu.h"
+#include "common/metrics.h"
+#include "device/include/controller.h"
+#include "device/include/interop.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+#include "l2c_int.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+static void btm_read_remote_features(uint16_t handle);
+static void btm_read_remote_ext_features(uint16_t handle, uint8_t page_number);
+static void btm_process_remote_ext_features(tACL_CONN* p_acl_cb,
+ uint8_t num_read_pages);
+
+/* 3 seconds timeout waiting for responses */
+#define BTM_DEV_REPLY_TIMEOUT_MS (3 * 1000)
+
+/*******************************************************************************
+ *
+ * Function btm_acl_init
+ *
+ * Description This function is called at BTM startup to initialize
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_init(void) {
+ BTM_TRACE_DEBUG("btm_acl_init");
+ /* Initialize nonzero defaults */
+ btm_cb.btm_def_link_super_tout = HCI_DEFAULT_INACT_TOUT;
+ btm_cb.acl_disc_reason = 0xff;
+}
+
+/*******************************************************************************
+ *
+ * Function btm_bda_to_acl
+ *
+ * Description This function returns the FIRST acl_db entry for the passed
+ * BDA.
+ *
+ * Parameters bda : BD address of the remote device
+ * transport : Physical transport used for ACL connection
+ * (BR/EDR or LE)
+ *
+ * Returns Returns pointer to the ACL DB for the requested BDA if found.
+ * NULL if not found.
+ *
+ ******************************************************************************/
+tACL_CONN* btm_bda_to_acl(const RawAddress& bda, tBT_TRANSPORT transport) {
+ tACL_CONN* p = &btm_cb.acl_db[0];
+ uint16_t xx;
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) {
+ if ((p->in_use) && p->remote_addr == bda && p->transport == transport) {
+ BTM_TRACE_DEBUG("btm_bda_to_acl found");
+ return (p);
+ }
+ }
+
+ /* If here, no BD Addr found */
+ return ((tACL_CONN*)NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_handle_to_acl_index
+ *
+ * Description This function returns the FIRST acl_db entry for the passed
+ * hci_handle.
+ *
+ * Returns index to the acl_db or MAX_L2CAP_LINKS.
+ *
+ ******************************************************************************/
+uint8_t btm_handle_to_acl_index(uint16_t hci_handle) {
+ tACL_CONN* p = &btm_cb.acl_db[0];
+ uint8_t xx;
+ BTM_TRACE_DEBUG("btm_handle_to_acl_index");
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) {
+ if ((p->in_use) && (p->hci_handle == hci_handle)) {
+ break;
+ }
+ }
+
+ /* If here, no BD Addr found */
+ return (xx);
+}
+
+#if (BLE_PRIVACY_SPT == TRUE)
+/*******************************************************************************
+ *
+ * Function btm_ble_get_acl_remote_addr
+ *
+ * Description This function reads the active remote address used for the
+ * connection.
+ *
+ * Returns success return true, otherwise false.
+ *
+ ******************************************************************************/
+bool btm_ble_get_acl_remote_addr(tBTM_SEC_DEV_REC* p_dev_rec,
+ RawAddress& conn_addr,
+ tBLE_ADDR_TYPE* p_addr_type) {
+ bool st = true;
+
+ if (p_dev_rec == NULL) {
+ BTM_TRACE_ERROR("%s can not find device with matching address", __func__);
+ return false;
+ }
+
+ switch (p_dev_rec->ble.active_addr_type) {
+ case BTM_BLE_ADDR_PSEUDO:
+ conn_addr = p_dev_rec->bd_addr;
+ *p_addr_type = p_dev_rec->ble.ble_addr_type;
+ break;
+
+ case BTM_BLE_ADDR_RRA:
+ conn_addr = p_dev_rec->ble.cur_rand_addr;
+ *p_addr_type = BLE_ADDR_RANDOM;
+ break;
+
+ case BTM_BLE_ADDR_STATIC:
+ conn_addr = p_dev_rec->ble.identity_addr;
+ *p_addr_type = p_dev_rec->ble.identity_addr_type;
+ break;
+
+ default:
+ BTM_TRACE_ERROR("Unknown active address: %d",
+ p_dev_rec->ble.active_addr_type);
+ st = false;
+ break;
+ }
+
+ return st;
+}
+#endif
+/*******************************************************************************
+ *
+ * Function btm_acl_created
+ *
+ * Description This function is called by L2CAP when an ACL connection
+ * is created.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_created(const RawAddress& bda, DEV_CLASS dc, BD_NAME bdn,
+ uint16_t hci_handle, uint8_t link_role,
+ tBT_TRANSPORT transport) {
+ tBTM_SEC_DEV_REC* p_dev_rec = NULL;
+ tACL_CONN* p;
+ uint8_t xx;
+
+ BTM_TRACE_DEBUG("%s: peer %s hci_handle=%d link_role=%d transport=%d",
+ __func__, bda.ToString().c_str(), hci_handle, link_role,
+ transport);
+ /* Ensure we don't have duplicates */
+ p = btm_bda_to_acl(bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ p->hci_handle = hci_handle;
+ p->link_role = link_role;
+ p->transport = transport;
+ VLOG(1) << "Duplicate btm_acl_created: RemBdAddr: " << bda;
+ BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy);
+ return;
+ }
+
+ /* Allocate acl_db entry */
+ for (xx = 0, p = &btm_cb.acl_db[0]; xx < MAX_L2CAP_LINKS; xx++, p++) {
+ if (!p->in_use) {
+ p->in_use = true;
+ p->hci_handle = hci_handle;
+ p->link_role = link_role;
+ p->link_up_issued = false;
+ p->remote_addr = bda;
+
+ p->transport = transport;
+#if (BLE_PRIVACY_SPT == TRUE)
+ if (transport == BT_TRANSPORT_LE)
+ btm_ble_refresh_local_resolvable_private_addr(
+ bda, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr);
+#else
+ p->conn_addr_type = BLE_ADDR_PUBLIC;
+ p->conn_addr = *controller_get_interface()->get_address();
+
+#endif
+ p->switch_role_failed_attempts = 0;
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+
+ btm_pm_sm_alloc(xx);
+
+ if (dc) memcpy(p->remote_dc, dc, DEV_CLASS_LEN);
+
+ if (bdn) memcpy(p->remote_name, bdn, BTM_MAX_REM_BD_NAME_LEN);
+
+ /* if BR/EDR do something more */
+ if (transport == BT_TRANSPORT_BR_EDR) {
+ btsnd_hcic_read_rmt_clk_offset(p->hci_handle);
+ btsnd_hcic_rmt_ver_req(p->hci_handle);
+ }
+ p_dev_rec = btm_find_dev_by_handle(hci_handle);
+
+ if (p_dev_rec) {
+ BTM_TRACE_DEBUG("%s: peer %s device_type=0x%x", __func__,
+ bda.ToString().c_str(), p_dev_rec->device_type);
+ }
+
+ if (p_dev_rec && !(transport == BT_TRANSPORT_LE)) {
+ /* If remote features already known, copy them and continue connection
+ * setup */
+ if ((p_dev_rec->num_read_pages) &&
+ (p_dev_rec->num_read_pages <= (HCI_EXT_FEATURES_PAGE_MAX + 1))) {
+ memcpy(p->peer_lmp_feature_pages, p_dev_rec->feature_pages,
+ (HCI_FEATURE_BYTES_PER_PAGE * p_dev_rec->num_read_pages));
+ p->num_read_pages = p_dev_rec->num_read_pages;
+
+ const uint8_t req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND);
+
+ /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */
+ btm_sec_set_peer_sec_caps(p, p_dev_rec);
+
+ BTM_TRACE_API("%s: pend:%d", __func__, req_pend);
+ if (req_pend) {
+ /* Request for remaining Security Features (if any) */
+ l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
+ }
+ btm_establish_continue(p);
+ return;
+ }
+ }
+
+ /* If here, features are not known yet */
+ if (p_dev_rec && transport == BT_TRANSPORT_LE) {
+#if (BLE_PRIVACY_SPT == TRUE)
+ btm_ble_get_acl_remote_addr(p_dev_rec, p->active_remote_addr,
+ &p->active_remote_addr_type);
+#endif
+
+ if (HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(
+ controller_get_interface()->get_features_ble()->as_array) ||
+ link_role == HCI_ROLE_MASTER) {
+ btsnd_hcic_ble_read_remote_feat(p->hci_handle);
+ } else {
+ btm_establish_continue(p);
+ }
+ }
+
+ /* read page 1 - on rmt feature event for buffer reasons */
+ return;
+ }
+ }
+}
+
+void btm_acl_update_conn_addr(uint16_t conn_handle, const RawAddress& address) {
+ uint8_t idx = btm_handle_to_acl_index(conn_handle);
+ if (idx != MAX_L2CAP_LINKS) {
+ btm_cb.acl_db[idx].conn_addr = address;
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_report_role_change
+ *
+ * Description This function is called when the local device is deemed
+ * to be down. It notifies L2CAP of the failure.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_report_role_change(uint8_t hci_status, const RawAddress* bda) {
+ tBTM_ROLE_SWITCH_CMPL ref_data;
+ BTM_TRACE_DEBUG("btm_acl_report_role_change");
+ if (btm_cb.devcb.p_switch_role_cb &&
+ (bda && btm_cb.devcb.switch_role_ref_data.remote_bd_addr == *bda)) {
+ memcpy(&ref_data, &btm_cb.devcb.switch_role_ref_data,
+ sizeof(tBTM_ROLE_SWITCH_CMPL));
+ ref_data.hci_status = hci_status;
+ (*btm_cb.devcb.p_switch_role_cb)(&ref_data);
+ memset(&btm_cb.devcb.switch_role_ref_data, 0,
+ sizeof(tBTM_ROLE_SWITCH_CMPL));
+ btm_cb.devcb.p_switch_role_cb = NULL;
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_removed
+ *
+ * Description This function is called by L2CAP when an ACL connection
+ * is removed. Since only L2CAP creates ACL links, we use
+ * the L2CAP link index as our index into the control blocks.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_removed(const RawAddress& bda, tBT_TRANSPORT transport) {
+ tACL_CONN* p;
+ tBTM_SEC_DEV_REC* p_dev_rec = NULL;
+ BTM_TRACE_DEBUG("btm_acl_removed");
+ p = btm_bda_to_acl(bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ p->in_use = false;
+
+ /* if the disconnected channel has a pending role switch, clear it now */
+ btm_acl_report_role_change(HCI_ERR_NO_CONNECTION, &bda);
+
+ /* Only notify if link up has had a chance to be issued */
+ if (p->link_up_issued) {
+ p->link_up_issued = false;
+
+ /* If anyone cares, indicate the database changed */
+ if (btm_cb.p_bl_changed_cb) {
+ tBTM_BL_EVENT_DATA evt_data;
+ evt_data.event = BTM_BL_DISCN_EVT;
+ evt_data.discn.p_bda = &bda;
+ evt_data.discn.handle = p->hci_handle;
+ evt_data.discn.transport = p->transport;
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ }
+
+ btm_acl_update_busy_level(BTM_BLI_ACL_DOWN_EVT);
+ }
+
+ BTM_TRACE_DEBUG(
+ "acl hci_handle=%d transport=%d connectable_mode=0x%0x link_role=%d",
+ p->hci_handle, p->transport, btm_cb.ble_ctr_cb.inq_var.connectable_mode,
+ p->link_role);
+
+ p_dev_rec = btm_find_dev(bda);
+ if (p_dev_rec) {
+ BTM_TRACE_DEBUG("before update p_dev_rec->sec_flags=0x%x",
+ p_dev_rec->sec_flags);
+ if (p->transport == BT_TRANSPORT_LE) {
+ BTM_TRACE_DEBUG("LE link down");
+ p_dev_rec->sec_flags &= ~(BTM_SEC_LE_ENCRYPTED | BTM_SEC_ROLE_SWITCHED);
+ if ((p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) == 0) {
+ BTM_TRACE_DEBUG("Not Bonded");
+ p_dev_rec->sec_flags &=
+ ~(BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_LE_AUTHENTICATED);
+ } else {
+ BTM_TRACE_DEBUG("Bonded");
+ }
+ } else {
+ BTM_TRACE_DEBUG("Bletooth link down");
+ p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED |
+ BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED);
+ }
+ BTM_TRACE_DEBUG("after update p_dev_rec->sec_flags=0x%x",
+ p_dev_rec->sec_flags);
+ } else {
+ BTM_TRACE_ERROR("Device not found");
+ }
+
+ /* Clear the ACL connection data */
+ memset(p, 0, sizeof(tACL_CONN));
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_device_down
+ *
+ * Description This function is called when the local device is deemed
+ * to be down. It notifies L2CAP of the failure.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_device_down(void) {
+ tACL_CONN* p = &btm_cb.acl_db[0];
+ uint16_t xx;
+ BTM_TRACE_DEBUG("btm_acl_device_down");
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) {
+ if (p->in_use) {
+ BTM_TRACE_DEBUG("hci_handle=%d HCI_ERR_HW_FAILURE ", p->hci_handle);
+ l2c_link_hci_disc_comp(p->hci_handle, HCI_ERR_HW_FAILURE);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_update_busy_level
+ *
+ * Description This function is called to update the busy level of the
+ * system.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_update_busy_level(tBTM_BLI_EVENT event) {
+ bool old_inquiry_state = btm_cb.is_inquiry;
+ tBTM_BL_UPDATE_DATA evt;
+ evt.busy_level_flags = 0;
+ switch (event) {
+ case BTM_BLI_ACL_UP_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_ACL_UP_EVT");
+ break;
+ case BTM_BLI_ACL_DOWN_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_ACL_DOWN_EVT");
+ break;
+ case BTM_BLI_PAGE_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_PAGE_EVT");
+ btm_cb.is_paging = true;
+ evt.busy_level_flags = BTM_BL_PAGING_STARTED;
+ break;
+ case BTM_BLI_PAGE_DONE_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_PAGE_DONE_EVT");
+ btm_cb.is_paging = false;
+ evt.busy_level_flags = BTM_BL_PAGING_COMPLETE;
+ break;
+ case BTM_BLI_INQ_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_INQ_EVT");
+ btm_cb.is_inquiry = true;
+ evt.busy_level_flags = BTM_BL_INQUIRY_STARTED;
+ break;
+ case BTM_BLI_INQ_CANCEL_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_INQ_CANCEL_EVT");
+ btm_cb.is_inquiry = false;
+ evt.busy_level_flags = BTM_BL_INQUIRY_CANCELLED;
+ break;
+ case BTM_BLI_INQ_DONE_EVT:
+ BTM_TRACE_DEBUG("BTM_BLI_INQ_DONE_EVT");
+ btm_cb.is_inquiry = false;
+ evt.busy_level_flags = BTM_BL_INQUIRY_COMPLETE;
+ break;
+ }
+
+ uint8_t busy_level;
+ if (btm_cb.is_paging || btm_cb.is_inquiry)
+ busy_level = 10;
+ else
+ busy_level = BTM_GetNumAclLinks();
+
+ if ((busy_level != btm_cb.busy_level) ||
+ (old_inquiry_state != btm_cb.is_inquiry)) {
+ evt.event = BTM_BL_UPDATE_EVT;
+ evt.busy_level = busy_level;
+ btm_cb.busy_level = busy_level;
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_UPDATE_MASK)) {
+ tBTM_BL_EVENT_DATA btm_bl_event_data;
+ btm_bl_event_data.update = evt;
+ (*btm_cb.p_bl_changed_cb)(&btm_bl_event_data);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_GetRole
+ *
+ * Description This function is called to get the role of the local device
+ * for the ACL connection with the specified remote device
+ *
+ * Returns BTM_SUCCESS if connection exists.
+ * BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_GetRole(const RawAddress& remote_bd_addr, uint8_t* p_role) {
+ tACL_CONN* p;
+ BTM_TRACE_DEBUG("BTM_GetRole");
+ p = btm_bda_to_acl(remote_bd_addr, BT_TRANSPORT_BR_EDR);
+ if (p == NULL) {
+ *p_role = BTM_ROLE_UNDEFINED;
+ return (BTM_UNKNOWN_ADDR);
+ }
+
+ /* Get the current role */
+ *p_role = p->link_role;
+ return (BTM_SUCCESS);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_SwitchRole
+ *
+ * Description This function is called to switch role between master and
+ * slave. If role is already set it will do nothing. If the
+ * command was initiated, the callback function is called upon
+ * completion.
+ *
+ * Returns BTM_SUCCESS if already in specified role.
+ * BTM_CMD_STARTED if command issued to controller.
+ * BTM_NO_RESOURCES if couldn't allocate memory to issue
+ * command
+ * BTM_UNKNOWN_ADDR if no active link with bd addr specified
+ * BTM_MODE_UNSUPPORTED if local device does not support role
+ * switching
+ * BTM_BUSY if the previous command is not completed
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SwitchRole(const RawAddress& remote_bd_addr, uint8_t new_role,
+ tBTM_CMPL_CB* p_cb) {
+ tACL_CONN* p;
+ tBTM_SEC_DEV_REC* p_dev_rec = NULL;
+ bool is_sco_active;
+ tBTM_STATUS status;
+ tBTM_PM_MODE pwr_mode;
+ tBTM_PM_PWR_MD settings;
+
+ LOG_INFO(LOG_TAG, "%s: peer %s new_role=0x%x p_cb=%p p_switch_role_cb=%p",
+ __func__, remote_bd_addr.ToString().c_str(), new_role, p_cb,
+ btm_cb.devcb.p_switch_role_cb);
+
+ /* Make sure the local device supports switching */
+ if (!controller_get_interface()->supports_master_slave_role_switch())
+ return (BTM_MODE_UNSUPPORTED);
+
+ if (btm_cb.devcb.p_switch_role_cb && p_cb) {
+ VLOG(2) << "Role switch on other device is in progress "
+ << btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ return (BTM_BUSY);
+ }
+
+ p = btm_bda_to_acl(remote_bd_addr, BT_TRANSPORT_BR_EDR);
+ if (p == NULL) return (BTM_UNKNOWN_ADDR);
+
+ /* Finished if already in desired role */
+ if (p->link_role == new_role) return (BTM_SUCCESS);
+
+ if (interop_match_addr(INTEROP_DISABLE_ROLE_SWITCH, &remote_bd_addr))
+ return BTM_DEV_BLACKLISTED;
+
+ /* Check if there is any SCO Active on this BD Address */
+ is_sco_active = btm_is_sco_active_by_bdaddr(remote_bd_addr);
+
+ if (is_sco_active) return (BTM_NO_RESOURCES);
+
+ /* Ignore role switch request if the previous request was not completed */
+ if (p->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) {
+ BTM_TRACE_DEBUG("BTM_SwitchRole busy: %d", p->switch_role_state);
+ return (BTM_BUSY);
+ }
+
+ if (interop_match_addr(INTEROP_DYNAMIC_ROLE_SWITCH, &remote_bd_addr)) {
+ BTM_TRACE_DEBUG("%s, Device blacklisted under INTEROP_DYNAMIC_ROLE_SWITCH.",
+ __func__);
+ return BTM_DEV_BLACKLISTED;
+ }
+
+ status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode);
+ if (status != BTM_SUCCESS) return (status);
+
+ /* Wake up the link if in sniff or park before attempting switch */
+ if (pwr_mode == BTM_PM_MD_PARK || pwr_mode == BTM_PM_MD_SNIFF) {
+ memset((void*)&settings, 0, sizeof(settings));
+ settings.mode = BTM_PM_MD_ACTIVE;
+ status = BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, p->remote_addr, &settings);
+ if (status != BTM_CMD_STARTED) return (BTM_WRONG_MODE);
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+ /* some devices do not support switch while encryption is on */
+ else {
+ p_dev_rec = btm_find_dev(remote_bd_addr);
+ if ((p_dev_rec != NULL) &&
+ ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) &&
+ !BTM_EPR_AVAILABLE(p)) {
+ /* bypass turning off encryption if change link key is already doing it */
+ if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF) {
+ btsnd_hcic_set_conn_encrypt(p->hci_handle, false);
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF;
+ }
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+ } else {
+ btsnd_hcic_switch_role(remote_bd_addr, new_role);
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+
+#if (BTM_DISC_DURING_RS == TRUE)
+ if (p_dev_rec) p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+#endif
+ }
+ }
+
+ /* Initialize return structure in case request fails */
+ if (p_cb) {
+ btm_cb.devcb.switch_role_ref_data.remote_bd_addr = remote_bd_addr;
+ btm_cb.devcb.switch_role_ref_data.role = new_role;
+ /* initialized to an error code */
+ btm_cb.devcb.switch_role_ref_data.hci_status = HCI_ERR_UNSUPPORTED_VALUE;
+ btm_cb.devcb.p_switch_role_cb = p_cb;
+ }
+ return (BTM_CMD_STARTED);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_encrypt_change
+ *
+ * Description This function is when encryption of the connection is
+ * completed by the LM. Checks to see if a role switch or
+ * change of link key was active and initiates or continues
+ * process if needed.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_encrypt_change(uint16_t handle, uint8_t status,
+ uint8_t encr_enable) {
+ tACL_CONN* p;
+ uint8_t xx;
+ tBTM_SEC_DEV_REC* p_dev_rec;
+
+ BTM_TRACE_DEBUG("btm_acl_encrypt_change handle=%d status=%d encr_enabl=%d",
+ handle, status, encr_enable);
+ xx = btm_handle_to_acl_index(handle);
+ /* don't assume that we can never get a bad hci_handle */
+ if (xx < MAX_L2CAP_LINKS)
+ p = &btm_cb.acl_db[xx];
+ else
+ return;
+
+ /* Process Role Switch if active */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF) {
+ /* if encryption turn off failed we still will try to switch role */
+ if (encr_enable) {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ } else {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_SWITCHING;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC;
+ }
+
+ btsnd_hcic_switch_role(p->remote_addr, (uint8_t)!p->link_role);
+#if (BTM_DISC_DURING_RS == TRUE)
+ p_dev_rec = btm_find_dev(p->remote_addr);
+ if (p_dev_rec != NULL) p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+#endif
+
+ }
+ /* Finished enabling Encryption after role switch */
+ else if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON) {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status,
+ &p->remote_addr);
+
+ /* if role change event is registered, report it now */
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) {
+ tBTM_BL_ROLE_CHG_DATA evt;
+ evt.event = BTM_BL_ROLE_CHG_EVT;
+ evt.new_role = btm_cb.devcb.switch_role_ref_data.role;
+ evt.p_bda = &btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ evt.hci_status = btm_cb.devcb.switch_role_ref_data.hci_status;
+ tBTM_BL_EVENT_DATA btm_bl_event_data;
+ btm_bl_event_data.role_chg = evt;
+ (*btm_cb.p_bl_changed_cb)(&btm_bl_event_data);
+
+ BTM_TRACE_DEBUG(
+ "%s: Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
+ __func__, evt.new_role, evt.hci_status, p->switch_role_state);
+ }
+
+#if (BTM_DISC_DURING_RS == TRUE)
+ /* If a disconnect is pending, issue it now that role switch has completed
+ */
+ p_dev_rec = btm_find_dev(p->remote_addr);
+ if (p_dev_rec != NULL) {
+ if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) {
+ BTM_TRACE_WARNING(
+ "btm_acl_encrypt_change -> Issuing delayed HCI_Disconnect!!!");
+ btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
+ }
+ BTM_TRACE_ERROR(
+ "btm_acl_encrypt_change: tBTM_SEC_DEV:0x%x rs_disc_pending=%d",
+ PTR_TO_UINT(p_dev_rec), p_dev_rec->rs_disc_pending);
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
+ }
+#endif
+ }
+}
+/*******************************************************************************
+ *
+ * Function BTM_SetLinkPolicy
+ *
+ * Description Create and send HCI "Write Policy Set" command
+ *
+ * Returns status of the operation
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetLinkPolicy(const RawAddress& remote_bda,
+ uint16_t* settings) {
+ tACL_CONN* p;
+ uint8_t* localFeatures = BTM_ReadLocalFeatures();
+ BTM_TRACE_DEBUG("%s", __func__);
+ /* BTM_TRACE_API ("%s: requested settings: 0x%04x", __func__, *settings ); */
+
+ /* First, check if hold mode is supported */
+ if (*settings != HCI_DISABLE_ALL_LM_MODES) {
+ if ((*settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) &&
+ (!HCI_SWITCH_SUPPORTED(localFeatures))) {
+ *settings &= (~HCI_ENABLE_MASTER_SLAVE_SWITCH);
+ BTM_TRACE_API("BTM_SetLinkPolicy switch not supported (settings: 0x%04x)",
+ *settings);
+ }
+ if ((*settings & HCI_ENABLE_HOLD_MODE) &&
+ (!HCI_HOLD_MODE_SUPPORTED(localFeatures))) {
+ *settings &= (~HCI_ENABLE_HOLD_MODE);
+ BTM_TRACE_API("BTM_SetLinkPolicy hold not supported (settings: 0x%04x)",
+ *settings);
+ }
+ if ((*settings & HCI_ENABLE_SNIFF_MODE) &&
+ ((!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) ||
+ interop_match_addr(INTEROP_DISABLE_SNIFF, &remote_bda))) {
+ *settings &= (~HCI_ENABLE_SNIFF_MODE);
+ BTM_TRACE_API("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)",
+ *settings);
+ }
+ if ((*settings & HCI_ENABLE_PARK_MODE) &&
+ (!HCI_PARK_MODE_SUPPORTED(localFeatures))) {
+ *settings &= (~HCI_ENABLE_PARK_MODE);
+ BTM_TRACE_API("BTM_SetLinkPolicy park not supported (settings: 0x%04x)",
+ *settings);
+ }
+ }
+
+ p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+ if (p != NULL) {
+ btsnd_hcic_write_policy_set(p->hci_handle, *settings);
+ return BTM_CMD_STARTED;
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_SetDefaultLinkPolicy
+ *
+ * Description Set the default value for HCI "Write Policy Set" command
+ * to use when an ACL link is created.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void BTM_SetDefaultLinkPolicy(uint16_t settings) {
+ uint8_t* localFeatures = BTM_ReadLocalFeatures();
+
+ BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy setting:0x%04x", settings);
+
+ if ((settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) &&
+ (!HCI_SWITCH_SUPPORTED(localFeatures))) {
+ settings &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ BTM_TRACE_DEBUG(
+ "BTM_SetDefaultLinkPolicy switch not supported (settings: 0x%04x)",
+ settings);
+ }
+ if ((settings & HCI_ENABLE_HOLD_MODE) &&
+ (!HCI_HOLD_MODE_SUPPORTED(localFeatures))) {
+ settings &= ~HCI_ENABLE_HOLD_MODE;
+ BTM_TRACE_DEBUG(
+ "BTM_SetDefaultLinkPolicy hold not supported (settings: 0x%04x)",
+ settings);
+ }
+ if ((settings & HCI_ENABLE_SNIFF_MODE) &&
+ (!HCI_SNIFF_MODE_SUPPORTED(localFeatures))) {
+ settings &= ~HCI_ENABLE_SNIFF_MODE;
+ BTM_TRACE_DEBUG(
+ "BTM_SetDefaultLinkPolicy sniff not supported (settings: 0x%04x)",
+ settings);
+ }
+ if ((settings & HCI_ENABLE_PARK_MODE) &&
+ (!HCI_PARK_MODE_SUPPORTED(localFeatures))) {
+ settings &= ~HCI_ENABLE_PARK_MODE;
+ BTM_TRACE_DEBUG(
+ "BTM_SetDefaultLinkPolicy park not supported (settings: 0x%04x)",
+ settings);
+ }
+ BTM_TRACE_DEBUG("Set DefaultLinkPolicy:0x%04x", settings);
+
+ btm_cb.btm_def_link_policy = settings;
+
+ /* Set the default Link Policy of the controller */
+ btsnd_hcic_write_def_policy_set(settings);
+}
+
+void btm_use_preferred_conn_params(const RawAddress& bda) {
+ tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE);
+ tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(bda);
+
+ /* If there are any preferred connection parameters, set them now */
+ if ((p_lcb != NULL) && (p_dev_rec != NULL) &&
+ (p_dev_rec->conn_params.min_conn_int >= BTM_BLE_CONN_INT_MIN) &&
+ (p_dev_rec->conn_params.min_conn_int <= BTM_BLE_CONN_INT_MAX) &&
+ (p_dev_rec->conn_params.max_conn_int >= BTM_BLE_CONN_INT_MIN) &&
+ (p_dev_rec->conn_params.max_conn_int <= BTM_BLE_CONN_INT_MAX) &&
+ (p_dev_rec->conn_params.slave_latency <= BTM_BLE_CONN_LATENCY_MAX) &&
+ (p_dev_rec->conn_params.supervision_tout >= BTM_BLE_CONN_SUP_TOUT_MIN) &&
+ (p_dev_rec->conn_params.supervision_tout <= BTM_BLE_CONN_SUP_TOUT_MAX) &&
+ ((p_lcb->min_interval < p_dev_rec->conn_params.min_conn_int &&
+ p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ||
+ (p_lcb->min_interval > p_dev_rec->conn_params.max_conn_int) ||
+ (p_lcb->latency > p_dev_rec->conn_params.slave_latency) ||
+ (p_lcb->timeout > p_dev_rec->conn_params.supervision_tout))) {
+ BTM_TRACE_DEBUG(
+ "%s: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d "
+ "supervision_tout=%d",
+ __func__, p_lcb->handle, p_dev_rec->conn_params.min_conn_int,
+ p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency,
+ p_dev_rec->conn_params.supervision_tout);
+
+ p_lcb->min_interval = p_dev_rec->conn_params.min_conn_int;
+ p_lcb->max_interval = p_dev_rec->conn_params.max_conn_int;
+ p_lcb->timeout = p_dev_rec->conn_params.supervision_tout;
+ p_lcb->latency = p_dev_rec->conn_params.slave_latency;
+
+ btsnd_hcic_ble_upd_ll_conn_params(
+ p_lcb->handle, p_dev_rec->conn_params.min_conn_int,
+ p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency,
+ p_dev_rec->conn_params.supervision_tout, 0, 0);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_version_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the remote version info.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_version_complete(uint8_t* p) {
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+ uint8_t status;
+ uint16_t handle;
+ int xx;
+ BTM_TRACE_DEBUG("btm_read_remote_version_complete");
+
+ STREAM_TO_UINT8(status, p);
+ STREAM_TO_UINT16(handle, p);
+
+ /* Look up the connection by handle and copy features */
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle)) {
+ if (status == HCI_SUCCESS) {
+ STREAM_TO_UINT8(p_acl_cb->lmp_version, p);
+ STREAM_TO_UINT16(p_acl_cb->manufacturer, p);
+ STREAM_TO_UINT16(p_acl_cb->lmp_subversion, p);
+
+ if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) {
+ btm_read_remote_features(p_acl_cb->hci_handle);
+ }
+ bluetooth::common::LogRemoteVersionInfo(
+ handle, status, p_acl_cb->lmp_version, p_acl_cb->manufacturer,
+ p_acl_cb->lmp_subversion);
+ } else {
+ bluetooth::common::LogRemoteVersionInfo(handle, status, 0, 0, 0);
+ }
+
+ if (p_acl_cb->transport == BT_TRANSPORT_LE) {
+ l2cble_notify_le_connection(p_acl_cb->remote_addr);
+ btm_use_preferred_conn_params(p_acl_cb->remote_addr);
+ }
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_process_remote_ext_features
+ *
+ * Description Local function called to process all extended features pages
+ * read from a remote device.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_process_remote_ext_features(tACL_CONN* p_acl_cb,
+ uint8_t num_read_pages) {
+ uint16_t handle = p_acl_cb->hci_handle;
+ tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
+ uint8_t page_idx;
+
+ BTM_TRACE_DEBUG("btm_process_remote_ext_features");
+
+ /* Make sure we have the record to save remote features information */
+ if (p_dev_rec == NULL) {
+ /* Get a new device; might be doing dedicated bonding */
+ p_dev_rec = btm_find_or_alloc_dev(p_acl_cb->remote_addr);
+ }
+
+ p_acl_cb->num_read_pages = num_read_pages;
+ p_dev_rec->num_read_pages = num_read_pages;
+
+ /* Move the pages to placeholder */
+ for (page_idx = 0; page_idx < num_read_pages; page_idx++) {
+ if (page_idx > HCI_EXT_FEATURES_PAGE_MAX) {
+ BTM_TRACE_ERROR("%s: page=%d unexpected", __func__, page_idx);
+ break;
+ }
+ memcpy(p_dev_rec->feature_pages[page_idx],
+ p_acl_cb->peer_lmp_feature_pages[page_idx],
+ HCI_FEATURE_BYTES_PER_PAGE);
+ }
+
+ if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) ||
+ p_dev_rec->is_originator) {
+ BTM_TRACE_DEBUG("%s: Calling Next Security Procedure", __func__);
+ uint8_t status = btm_sec_execute_procedure(p_dev_rec);
+ if (status != BTM_CMD_STARTED) {
+ BTM_TRACE_ERROR("%s: Security procedure not started! status %d", __func__,
+ status);
+ btm_sec_dev_rec_cback_event(p_dev_rec, status, false);
+ }
+ }
+ const uint8_t req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND);
+
+ /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */
+ btm_sec_set_peer_sec_caps(p_acl_cb, p_dev_rec);
+
+ BTM_TRACE_API("%s: pend:%d", __func__, req_pend);
+ if (req_pend) {
+ /* Request for remaining Security Features (if any) */
+ l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_features
+ *
+ * Description Local function called to send a read remote supported
+ * features/remote extended features page[0].
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_features(uint16_t handle) {
+ uint8_t acl_idx;
+ tACL_CONN* p_acl_cb;
+
+ BTM_TRACE_DEBUG("btm_read_remote_features() handle: %d", handle);
+
+ acl_idx = btm_handle_to_acl_index(handle);
+ if (acl_idx >= MAX_L2CAP_LINKS) {
+ BTM_TRACE_ERROR("btm_read_remote_features handle=%d invalid", handle);
+ return;
+ }
+
+ p_acl_cb = &btm_cb.acl_db[acl_idx];
+ p_acl_cb->num_read_pages = 0;
+ memset(p_acl_cb->peer_lmp_feature_pages, 0,
+ sizeof(p_acl_cb->peer_lmp_feature_pages));
+
+ /* first send read remote supported features HCI command */
+ /* because we don't know whether the remote support extended feature command
+ */
+ btsnd_hcic_rmt_features_req(handle);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_ext_features
+ *
+ * Description Local function called to send a read remote extended
+ * features
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_ext_features(uint16_t handle, uint8_t page_number) {
+ BTM_TRACE_DEBUG("btm_read_remote_ext_features() handle: %d page: %d", handle,
+ page_number);
+
+ btsnd_hcic_rmt_ext_features(handle, page_number);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_features_complete
+ *
+ * Description This function is called when the remote supported features
+ * complete event is received from the HCI.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_features_complete(uint8_t* p) {
+ tACL_CONN* p_acl_cb;
+ uint8_t status;
+ uint16_t handle;
+ uint8_t acl_idx;
+
+ BTM_TRACE_DEBUG("btm_read_remote_features_complete");
+ STREAM_TO_UINT8(status, p);
+
+ if (status != HCI_SUCCESS) {
+ BTM_TRACE_ERROR("btm_read_remote_features_complete failed (status 0x%02x)",
+ status);
+ return;
+ }
+
+ STREAM_TO_UINT16(handle, p);
+
+ acl_idx = btm_handle_to_acl_index(handle);
+ if (acl_idx >= MAX_L2CAP_LINKS) {
+ BTM_TRACE_ERROR("btm_read_remote_features_complete handle=%d invalid",
+ handle);
+ return;
+ }
+
+ p_acl_cb = &btm_cb.acl_db[acl_idx];
+
+ /* Copy the received features page */
+ STREAM_TO_ARRAY(p_acl_cb->peer_lmp_feature_pages[0], p,
+ HCI_FEATURE_BYTES_PER_PAGE);
+
+ if ((HCI_LMP_EXTENDED_SUPPORTED(p_acl_cb->peer_lmp_feature_pages[0])) &&
+ (controller_get_interface()
+ ->supports_reading_remote_extended_features())) {
+ /* if the remote controller has extended features and local controller
+ supports HCI_Read_Remote_Extended_Features command then start reading
+ these feature starting with extended features page 1 */
+ BTM_TRACE_DEBUG("Start reading remote extended features");
+ btm_read_remote_ext_features(handle, 1);
+ return;
+ }
+
+ /* Remote controller has no extended features. Process remote controller
+ supported features (features page 0). */
+ btm_process_remote_ext_features(p_acl_cb, 1);
+
+ /* Continue with HCI connection establishment */
+ btm_establish_continue(p_acl_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_ext_features_complete
+ *
+ * Description This function is called when the remote extended features
+ * complete event is received from the HCI.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_ext_features_complete(uint8_t* p, uint8_t evt_len) {
+ tACL_CONN* p_acl_cb;
+ uint8_t page_num, max_page;
+ uint16_t handle;
+ uint8_t acl_idx;
+
+ BTM_TRACE_DEBUG("btm_read_remote_ext_features_complete");
+
+ if (evt_len < HCI_EXT_FEATURES_SUCCESS_EVT_LEN) {
+ android_errorWriteLog(0x534e4554, "141552859");
+ BTM_TRACE_ERROR(
+ "btm_read_remote_ext_features_complete evt length too short. length=%d",
+ evt_len);
+ return;
+ }
+
+ ++p;
+ STREAM_TO_UINT16(handle, p);
+ STREAM_TO_UINT8(page_num, p);
+ STREAM_TO_UINT8(max_page, p);
+
+ /* Validate parameters */
+ acl_idx = btm_handle_to_acl_index(handle);
+ if (acl_idx >= MAX_L2CAP_LINKS) {
+ BTM_TRACE_ERROR("btm_read_remote_ext_features_complete handle=%d invalid",
+ handle);
+ return;
+ }
+
+ if (max_page > HCI_EXT_FEATURES_PAGE_MAX) {
+ BTM_TRACE_ERROR("btm_read_remote_ext_features_complete page=%d unknown",
+ max_page);
+ return;
+ }
+
+ if (page_num > HCI_EXT_FEATURES_PAGE_MAX) {
+ android_errorWriteLog(0x534e4554, "141552859");
+ BTM_TRACE_ERROR("btm_read_remote_ext_features_complete num_page=%d invalid",
+ page_num);
+ return;
+ }
+
+ if (page_num > max_page) {
+ BTM_TRACE_WARNING(
+ "btm_read_remote_ext_features_complete num_page=%d, max_page=%d "
+ "invalid", page_num, max_page);
+ }
+
+ p_acl_cb = &btm_cb.acl_db[acl_idx];
+
+ /* Copy the received features page */
+ STREAM_TO_ARRAY(p_acl_cb->peer_lmp_feature_pages[page_num], p,
+ HCI_FEATURE_BYTES_PER_PAGE);
+
+ /* If there is the next remote features page and
+ * we have space to keep this page data - read this page */
+ if ((page_num < max_page) && (page_num < HCI_EXT_FEATURES_PAGE_MAX)) {
+ page_num++;
+ BTM_TRACE_DEBUG("BTM reads next remote extended features page (%d)",
+ page_num);
+ btm_read_remote_ext_features(handle, page_num);
+ return;
+ }
+
+ /* Reading of remote feature pages is complete */
+ BTM_TRACE_DEBUG("BTM reached last remote extended features page (%d)",
+ page_num);
+
+ /* Process the pages */
+ btm_process_remote_ext_features(p_acl_cb, (uint8_t)(page_num + 1));
+
+ /* Continue with HCI connection establishment */
+ btm_establish_continue(p_acl_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_remote_ext_features_failed
+ *
+ * Description This function is called when the remote extended features
+ * complete event returns a failed status.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_remote_ext_features_failed(uint8_t status, uint16_t handle) {
+ tACL_CONN* p_acl_cb;
+ uint8_t acl_idx;
+
+ BTM_TRACE_WARNING(
+ "btm_read_remote_ext_features_failed (status 0x%02x) for handle %d",
+ status, handle);
+
+ acl_idx = btm_handle_to_acl_index(handle);
+ if (acl_idx >= MAX_L2CAP_LINKS) {
+ BTM_TRACE_ERROR("btm_read_remote_ext_features_failed handle=%d invalid",
+ handle);
+ return;
+ }
+
+ p_acl_cb = &btm_cb.acl_db[acl_idx];
+
+ /* Process supported features only */
+ btm_process_remote_ext_features(p_acl_cb, 1);
+
+ /* Continue HCI connection establishment */
+ btm_establish_continue(p_acl_cb);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_establish_continue
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read local link policy
+ * request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_establish_continue(tACL_CONN* p_acl_cb) {
+ tBTM_BL_EVENT_DATA evt_data;
+ BTM_TRACE_DEBUG("btm_establish_continue");
+#if (BTM_BYPASS_EXTRA_ACL_SETUP == FALSE)
+ if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) {
+ /* For now there are a some devices that do not like sending */
+ /* commands events and data at the same time. */
+ /* Set the packet types to the default allowed by the device */
+ btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported);
+
+ if (btm_cb.btm_def_link_policy)
+ BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy);
+ }
+#endif
+ if (p_acl_cb->link_up_issued) {
+ BTM_TRACE_ERROR("%s: Already link is up ", __func__);
+ return;
+ }
+ p_acl_cb->link_up_issued = true;
+
+ /* If anyone cares, tell him database changed */
+ if (btm_cb.p_bl_changed_cb) {
+ evt_data.event = BTM_BL_CONN_EVT;
+ evt_data.conn.p_bda = &p_acl_cb->remote_addr;
+ evt_data.conn.p_bdn = p_acl_cb->remote_name;
+ evt_data.conn.p_dc = p_acl_cb->remote_dc;
+ evt_data.conn.p_features = p_acl_cb->peer_lmp_feature_pages[0];
+ evt_data.conn.handle = p_acl_cb->hci_handle;
+ evt_data.conn.transport = p_acl_cb->transport;
+
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ }
+ btm_acl_update_busy_level(BTM_BLI_ACL_UP_EVT);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_SetDefaultLinkSuperTout
+ *
+ * Description Set the default value for HCI "Write Link Supervision
+ * Timeout"
+ * command to use when an ACL link is created.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void BTM_SetDefaultLinkSuperTout(uint16_t timeout) {
+ BTM_TRACE_DEBUG("BTM_SetDefaultLinkSuperTout");
+ btm_cb.btm_def_link_super_tout = timeout;
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_GetLinkSuperTout
+ *
+ * Description Read the link supervision timeout value of the connection
+ *
+ * Returns status of the operation
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_GetLinkSuperTout(const RawAddress& remote_bda,
+ uint16_t* p_timeout) {
+ tACL_CONN* p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+
+ BTM_TRACE_DEBUG("BTM_GetLinkSuperTout");
+ if (p != (tACL_CONN*)NULL) {
+ *p_timeout = p->link_super_tout;
+ return (BTM_SUCCESS);
+ }
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_SetLinkSuperTout
+ *
+ * Description Create and send HCI "Write Link Supervision Timeout" command
+ *
+ * Returns status of the operation
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_SetLinkSuperTout(const RawAddress& remote_bda,
+ uint16_t timeout) {
+ tACL_CONN* p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+
+ BTM_TRACE_DEBUG("BTM_SetLinkSuperTout");
+ if (p != (tACL_CONN*)NULL) {
+ p->link_super_tout = timeout;
+
+ /* Only send if current role is Master; 2.0 spec requires this */
+ if (p->link_role == BTM_ROLE_MASTER) {
+ btsnd_hcic_write_link_super_tout(LOCAL_BR_EDR_CONTROLLER_ID,
+ p->hci_handle, timeout);
+ return (BTM_CMD_STARTED);
+ } else {
+ return (BTM_SUCCESS);
+ }
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_IsAclConnectionUp
+ *
+ * Description This function is called to check if an ACL connection exists
+ * to a specific remote BD Address.
+ *
+ * Returns true if connection is up, else false.
+ *
+ ******************************************************************************/
+bool BTM_IsAclConnectionUp(const RawAddress& remote_bda,
+ tBT_TRANSPORT transport) {
+ tACL_CONN* p;
+
+ VLOG(2) << __func__ << " RemBdAddr: " << remote_bda;
+
+ p = btm_bda_to_acl(remote_bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ return (true);
+ }
+
+ /* If here, no BD Addr found */
+ return (false);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_GetNumAclLinks
+ *
+ * Description This function is called to count the number of
+ * ACL links that are active.
+ *
+ * Returns uint16_t Number of active ACL links
+ *
+ ******************************************************************************/
+uint16_t BTM_GetNumAclLinks(void) {
+ uint16_t num_acl = 0;
+
+ for (uint16_t i = 0; i < MAX_L2CAP_LINKS; ++i) {
+ if (btm_cb.acl_db[i].in_use) ++num_acl;
+ }
+
+ return num_acl;
+}
+
+/*******************************************************************************
+ *
+ * Function btm_get_acl_disc_reason_code
+ *
+ * Description This function is called to get the disconnection reason code
+ * returned by the HCI at disconnection complete event.
+ *
+ * Returns true if connection is up, else false.
+ *
+ ******************************************************************************/
+uint16_t btm_get_acl_disc_reason_code(void) {
+ uint8_t res = btm_cb.acl_disc_reason;
+ BTM_TRACE_DEBUG("btm_get_acl_disc_reason_code");
+ return (res);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_GetHCIConnHandle
+ *
+ * Description This function is called to get the handle for an ACL
+ * connection to a specific remote BD Address.
+ *
+ * Returns the handle of the connection, or 0xFFFF if none.
+ *
+ ******************************************************************************/
+uint16_t BTM_GetHCIConnHandle(const RawAddress& remote_bda,
+ tBT_TRANSPORT transport) {
+ tACL_CONN* p;
+ BTM_TRACE_DEBUG("BTM_GetHCIConnHandle");
+ p = btm_bda_to_acl(remote_bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ return (p->hci_handle);
+ }
+
+ /* If here, no BD Addr found */
+ return (0xFFFF);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_process_clk_off_comp_evt
+ *
+ * Description This function is called when clock offset command completes.
+ *
+ * Input Parms hci_handle - connection handle associated with the change
+ * clock offset
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_process_clk_off_comp_evt(uint16_t hci_handle, uint16_t clock_offset) {
+ uint8_t xx;
+ BTM_TRACE_DEBUG("btm_process_clk_off_comp_evt");
+ /* Look up the connection by handle and set the current mode */
+ xx = btm_handle_to_acl_index(hci_handle);
+ if (xx < MAX_L2CAP_LINKS) btm_cb.acl_db[xx].clock_offset = clock_offset;
+}
+
+/*******************************************************************************
+*
+* Function btm_blacklist_role_change_device
+*
+* Description This function is used to blacklist the device if the role
+* switch fails for maximum number of times. It also removes
+* the device from the black list if the role switch succeeds.
+*
+* Input Parms bd_addr - remote BD addr
+* hci_status - role switch status
+*
+* Returns void
+*
+*******************************************************************************/
+void btm_blacklist_role_change_device(const RawAddress& bd_addr,
+ uint8_t hci_status) {
+ tACL_CONN* p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_BR_EDR);
+ tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
+
+ if (!p || !p_dev_rec) {
+ return;
+ }
+ if (hci_status == HCI_SUCCESS) {
+ p->switch_role_failed_attempts = 0;
+ return;
+ }
+
+ /* check for carkits */
+ const uint32_t cod_audio_device =
+ (BTM_COD_SERVICE_AUDIO | BTM_COD_MAJOR_AUDIO) << 8;
+ const uint32_t cod =
+ ((p_dev_rec->dev_class[0] << 16) | (p_dev_rec->dev_class[1] << 8) |
+ p_dev_rec->dev_class[2]) &
+ 0xffffff;
+ if ((hci_status != HCI_SUCCESS) &&
+ ((p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING) ||
+ (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS)) &&
+ ((cod & cod_audio_device) == cod_audio_device) &&
+ (!interop_match_addr(INTEROP_DYNAMIC_ROLE_SWITCH, &bd_addr))) {
+ p->switch_role_failed_attempts++;
+ if (p->switch_role_failed_attempts == BTM_MAX_SW_ROLE_FAILED_ATTEMPTS) {
+ BTM_TRACE_WARNING(
+ "%s: Device %s blacklisted for role switching - "
+ "multiple role switch failed attempts: %u",
+ __func__, bd_addr.ToString().c_str(), p->switch_role_failed_attempts);
+ interop_database_add(INTEROP_DYNAMIC_ROLE_SWITCH, &bd_addr, 3);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_role_changed
+ *
+ * Description This function is called whan a link's master/slave role
+ * change event or command status event (with error) is
+ * received. It updates the link control block, and calls the
+ * registered callback with status and role (if registered).
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_acl_role_changed(uint8_t hci_status, const RawAddress* bd_addr,
+ uint8_t new_role) {
+ const RawAddress* p_bda =
+ (bd_addr) ? bd_addr : &btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ tACL_CONN* p = btm_bda_to_acl(*p_bda, BT_TRANSPORT_BR_EDR);
+ tBTM_ROLE_SWITCH_CMPL* p_data = &btm_cb.devcb.switch_role_ref_data;
+ tBTM_SEC_DEV_REC* p_dev_rec;
+
+ BTM_TRACE_DEBUG("%s: peer %s hci_status:0x%x new_role:%d", __func__,
+ (p_bda != nullptr) ? bd_addr->ToString().c_str() : "nullptr",
+ hci_status, new_role);
+ /* Ignore any stray events */
+ if (p == NULL) {
+ /* it could be a failure */
+ if (hci_status != HCI_SUCCESS)
+ btm_acl_report_role_change(hci_status, bd_addr);
+ return;
+ }
+
+ p_data->hci_status = hci_status;
+
+ if (hci_status == HCI_SUCCESS) {
+ p_data->role = new_role;
+ p_data->remote_bd_addr = *p_bda;
+
+ /* Update cached value */
+ p->link_role = new_role;
+
+ /* Reload LSTO: link supervision timeout is reset in the LM after a role
+ * switch */
+ if (new_role == BTM_ROLE_MASTER) {
+ BTM_SetLinkSuperTout(p->remote_addr, p->link_super_tout);
+ }
+ } else {
+ /* so the BTM_BL_ROLE_CHG_EVT uses the old role */
+ new_role = p->link_role;
+ }
+
+ /* Check if any SCO req is pending for role change */
+ btm_sco_chk_pend_rolechange(p->hci_handle);
+
+ /* if switching state is switching we need to turn encryption on */
+ /* if idle, we did not change encryption */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING) {
+ btsnd_hcic_set_conn_encrypt(p->hci_handle, true);
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON;
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON;
+ return;
+ }
+
+ /* Set the switch_role_state to IDLE since the reply received from HCI */
+ /* regardless of its result either success or failed. */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS) {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ }
+
+ /* if role switch complete is needed, report it now */
+ btm_acl_report_role_change(hci_status, bd_addr);
+
+ /* if role change event is registered, report it now */
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) {
+ tBTM_BL_ROLE_CHG_DATA evt;
+ evt.event = BTM_BL_ROLE_CHG_EVT;
+ evt.new_role = new_role;
+ evt.p_bda = p_bda;
+ evt.hci_status = hci_status;
+ tBTM_BL_EVENT_DATA btm_bl_event_data;
+ btm_bl_event_data.role_chg = evt;
+ (*btm_cb.p_bl_changed_cb)(&btm_bl_event_data);
+ }
+
+ BTM_TRACE_DEBUG(
+ "%s: peer %s Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, "
+ "rs_st:%d",
+ __func__, (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr",
+ p_data->role, p_data->hci_status, p->switch_role_state);
+
+#if (BTM_DISC_DURING_RS == TRUE)
+ /* If a disconnect is pending, issue it now that role switch has completed */
+ p_dev_rec = btm_find_dev(*p_bda);
+ if (p_dev_rec != NULL) {
+ if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) {
+ BTM_TRACE_WARNING(
+ "%s peer %s Issuing delayed HCI_Disconnect!!!", __func__,
+ (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr");
+ btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
+ }
+ BTM_TRACE_ERROR("%s: peer %s tBTM_SEC_DEV:0x%x rs_disc_pending=%d",
+ __func__,
+ (p_bda != nullptr) ? p_bda->ToString().c_str() : "nullptr",
+ PTR_TO_UINT(p_dev_rec), p_dev_rec->rs_disc_pending);
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
+ }
+
+#endif
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_AllocateSCN
+ *
+ * Description Look through the Server Channel Numbers for a free one.
+ *
+ * Returns Allocated SCN number or 0 if none.
+ *
+ ******************************************************************************/
+
+uint8_t BTM_AllocateSCN(void) {
+ uint8_t x;
+ BTM_TRACE_DEBUG("BTM_AllocateSCN");
+
+ // stack reserves scn 1 for HFP, HSP we still do the correct way
+ for (x = 1; x < BTM_MAX_SCN; x++) {
+ if (!btm_cb.btm_scn[x]) {
+ btm_cb.btm_scn[x] = true;
+ return (x + 1);
+ }
+ }
+
+ return (0); /* No free ports */
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_TryAllocateSCN
+ *
+ * Description Try to allocate a fixed server channel
+ *
+ * Returns Returns true if server channel was available
+ *
+ ******************************************************************************/
+
+bool BTM_TryAllocateSCN(uint8_t scn) {
+ /* Make sure we don't exceed max port range.
+ * Stack reserves scn 1 for HFP, HSP we still do the correct way.
+ */
+ if ((scn >= BTM_MAX_SCN) || (scn == 1) || (scn == 0)) return false;
+
+ /* check if this port is available */
+ if (!btm_cb.btm_scn[scn - 1]) {
+ btm_cb.btm_scn[scn - 1] = true;
+ return true;
+ }
+
+ return (false); /* Port was busy */
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_FreeSCN
+ *
+ * Description Free the specified SCN.
+ *
+ * Returns true or false
+ *
+ ******************************************************************************/
+bool BTM_FreeSCN(uint8_t scn) {
+ BTM_TRACE_DEBUG("BTM_FreeSCN ");
+ if (scn <= BTM_MAX_SCN && scn > 0) {
+ btm_cb.btm_scn[scn - 1] = false;
+ return (true);
+ } else {
+ return (false); /* Illegal SCN passed in */
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_set_packet_types
+ *
+ * Description This function sets the packet types used for a specific
+ * ACL connection. It is called internally by btm_acl_created
+ * or by an application/profile by BTM_SetPacketTypes.
+ *
+ * Returns status of the operation
+ *
+ ******************************************************************************/
+tBTM_STATUS btm_set_packet_types(tACL_CONN* p, uint16_t pkt_types) {
+ uint16_t temp_pkt_types;
+ BTM_TRACE_DEBUG("btm_set_packet_types");
+ /* Save in the ACL control blocks, types that we support */
+ temp_pkt_types = (pkt_types & BTM_ACL_SUPPORTED_PKTS_MASK &
+ btm_cb.btm_acl_pkt_types_supported);
+
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ temp_pkt_types |=
+ ((pkt_types & BTM_ACL_EXCEPTION_PKTS_MASK) |
+ (btm_cb.btm_acl_pkt_types_supported & BTM_ACL_EXCEPTION_PKTS_MASK));
+
+ /* Exclude packet types not supported by the peer */
+ btm_acl_chk_peer_pkt_type_support(p, &temp_pkt_types);
+
+ BTM_TRACE_DEBUG("SetPacketType Mask -> 0x%04x", temp_pkt_types);
+
+ btsnd_hcic_change_conn_type(p->hci_handle, temp_pkt_types);
+ p->pkt_types_mask = temp_pkt_types;
+
+ return (BTM_CMD_STARTED);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_get_max_packet_size
+ *
+ * Returns Returns maximum packet size that can be used for current
+ * connection, 0 if connection is not established
+ *
+ ******************************************************************************/
+uint16_t btm_get_max_packet_size(const RawAddress& addr) {
+ tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
+ uint16_t pkt_types = 0;
+ uint16_t pkt_size = 0;
+ BTM_TRACE_DEBUG("btm_get_max_packet_size");
+ if (p != NULL) {
+ pkt_types = p->pkt_types_mask;
+ } else {
+ /* Special case for when info for the local device is requested */
+ if (addr == *controller_get_interface()->get_address()) {
+ pkt_types = btm_cb.btm_acl_pkt_types_supported;
+ }
+ }
+
+ if (pkt_types) {
+ if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH5))
+ pkt_size = HCI_EDR3_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH5))
+ pkt_size = HCI_EDR2_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH3))
+ pkt_size = HCI_EDR3_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH5)
+ pkt_size = HCI_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH3))
+ pkt_size = HCI_EDR2_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM5)
+ pkt_size = HCI_DM5_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH3)
+ pkt_size = HCI_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM3)
+ pkt_size = HCI_DM3_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH1))
+ pkt_size = HCI_EDR3_DH1_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH1))
+ pkt_size = HCI_EDR2_DH1_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH1)
+ pkt_size = HCI_DH1_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM1)
+ pkt_size = HCI_DM1_PACKET_SIZE;
+ }
+
+ return (pkt_size);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadRemoteVersion
+ *
+ * Returns If connected report peer device info
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadRemoteVersion(const RawAddress& addr, uint8_t* lmp_version,
+ uint16_t* manufacturer,
+ uint16_t* lmp_sub_version) {
+ tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
+ BTM_TRACE_DEBUG("BTM_ReadRemoteVersion");
+ if (p == NULL) return (BTM_UNKNOWN_ADDR);
+
+ if (lmp_version) *lmp_version = p->lmp_version;
+
+ if (manufacturer) *manufacturer = p->manufacturer;
+
+ if (lmp_sub_version) *lmp_sub_version = p->lmp_subversion;
+
+ return (BTM_SUCCESS);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadRemoteFeatures
+ *
+ * Returns pointer to the remote supported features mask (8 bytes)
+ *
+ ******************************************************************************/
+uint8_t* BTM_ReadRemoteFeatures(const RawAddress& addr) {
+ tACL_CONN* p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR);
+ BTM_TRACE_DEBUG("BTM_ReadRemoteFeatures");
+ if (p == NULL) {
+ return (NULL);
+ }
+
+ return (p->peer_lmp_feature_pages[0]);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_RegBusyLevelNotif
+ *
+ * Description This function is called to register a callback to receive
+ * busy level change events.
+ *
+ * Returns BTM_SUCCESS if successfully registered, otherwise error
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_RegBusyLevelNotif(tBTM_BL_CHANGE_CB* p_cb, uint8_t* p_level,
+ tBTM_BL_EVENT_MASK evt_mask) {
+ BTM_TRACE_DEBUG("BTM_RegBusyLevelNotif");
+ if (p_level) *p_level = btm_cb.busy_level;
+
+ btm_cb.bl_evt_mask = evt_mask;
+
+ if (!p_cb)
+ btm_cb.p_bl_changed_cb = NULL;
+ else if (btm_cb.p_bl_changed_cb)
+ return (BTM_BUSY);
+ else
+ btm_cb.p_bl_changed_cb = p_cb;
+
+ return (BTM_SUCCESS);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_qos_setup_timeout
+ *
+ * Description Callback when QoS setup times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_qos_setup_timeout(UNUSED_ATTR void* data) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_qos_setup_cmpl_cb;
+ btm_cb.devcb.p_qos_setup_cmpl_cb = NULL;
+ if (p_cb) (*p_cb)((void*)NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_qos_setup_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the qos setup request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_qos_setup_complete(uint8_t status, uint16_t handle,
+ FLOW_SPEC* p_flow) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_qos_setup_cmpl_cb;
+ tBTM_QOS_SETUP_CMPL qossu;
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.qos_setup_timer);
+ btm_cb.devcb.p_qos_setup_cmpl_cb = NULL;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ memset(&qossu, 0, sizeof(tBTM_QOS_SETUP_CMPL));
+ qossu.status = status;
+ qossu.handle = handle;
+ if (p_flow != NULL) {
+ qossu.flow.qos_flags = p_flow->qos_flags;
+ qossu.flow.service_type = p_flow->service_type;
+ qossu.flow.token_rate = p_flow->token_rate;
+ qossu.flow.peak_bandwidth = p_flow->peak_bandwidth;
+ qossu.flow.latency = p_flow->latency;
+ qossu.flow.delay_variation = p_flow->delay_variation;
+ }
+ BTM_TRACE_DEBUG("BTM: p_flow->delay_variation: 0x%02x",
+ qossu.flow.delay_variation);
+ (*p_cb)(&qossu);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadRSSI
+ *
+ * Description This function is called to read the link policy settings.
+ * The address of link policy results are returned in the
+ * callback.
+ * (tBTM_RSSI_RESULT)
+ *
+ * Returns BTM_CMD_STARTED if successfully initiated or error code
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadRSSI(const RawAddress& remote_bda, tBTM_CMPL_CB* p_cb) {
+ tACL_CONN* p = NULL;
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type;
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_rssi_cmpl_cb) return (BTM_BUSY);
+
+ BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type);
+
+ if (dev_type & BT_DEVICE_TYPE_BLE) {
+ p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE);
+ }
+
+ if (p == NULL && dev_type & BT_DEVICE_TYPE_BREDR) {
+ p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+ }
+
+ if (p) {
+ btm_cb.devcb.p_rssi_cmpl_cb = p_cb;
+ alarm_set_on_mloop(btm_cb.devcb.read_rssi_timer, BTM_DEV_REPLY_TIMEOUT_MS,
+ btm_read_rssi_timeout, NULL);
+
+ btsnd_hcic_read_rssi(p->hci_handle);
+ return (BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadFailedContactCounter
+ *
+ * Description This function is called to read the failed contact counter.
+ * The result is returned in the callback.
+ * (tBTM_FAILED_CONTACT_COUNTER_RESULT)
+ *
+ * Returns BTM_CMD_STARTED if successfully initiated or error code
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadFailedContactCounter(const RawAddress& remote_bda,
+ tBTM_CMPL_CB* p_cb) {
+ tACL_CONN* p;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type;
+
+ /* If someone already waiting on the result, do not allow another */
+ if (btm_cb.devcb.p_failed_contact_counter_cmpl_cb) return (BTM_BUSY);
+
+ BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type);
+ if (dev_type == BT_DEVICE_TYPE_BLE) transport = BT_TRANSPORT_LE;
+
+ p = btm_bda_to_acl(remote_bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ btm_cb.devcb.p_failed_contact_counter_cmpl_cb = p_cb;
+ alarm_set_on_mloop(btm_cb.devcb.read_failed_contact_counter_timer,
+ BTM_DEV_REPLY_TIMEOUT_MS,
+ btm_read_failed_contact_counter_timeout, NULL);
+
+ btsnd_hcic_read_failed_contact_counter(p->hci_handle);
+ return (BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadAutomaticFlushTimeout
+ *
+ * Description This function is called to read the automatic flush timeout.
+ * The result is returned in the callback.
+ * (tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT)
+ *
+ * Returns BTM_CMD_STARTED if successfully initiated or error code
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadAutomaticFlushTimeout(const RawAddress& remote_bda,
+ tBTM_CMPL_CB* p_cb) {
+ tACL_CONN* p;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type;
+
+ /* If someone already waiting on the result, do not allow another */
+ if (btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb) return (BTM_BUSY);
+
+ BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type);
+ if (dev_type == BT_DEVICE_TYPE_BLE) transport = BT_TRANSPORT_LE;
+
+ p = btm_bda_to_acl(remote_bda, transport);
+ if (!p) return BTM_UNKNOWN_ADDR;
+
+ btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb = p_cb;
+ alarm_set_on_mloop(btm_cb.devcb.read_automatic_flush_timeout_timer,
+ BTM_DEV_REPLY_TIMEOUT_MS,
+ btm_read_automatic_flush_timeout_timeout, nullptr);
+
+ btsnd_hcic_read_automatic_flush_timeout(p->hci_handle);
+ return BTM_CMD_STARTED;
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_ReadTxPower
+ *
+ * Description This function is called to read the current
+ * TX power of the connection. The tx power level results
+ * are returned in the callback.
+ * (tBTM_RSSI_RESULT)
+ *
+ * Returns BTM_CMD_STARTED if successfully initiated or error code
+ *
+ ******************************************************************************/
+tBTM_STATUS BTM_ReadTxPower(const RawAddress& remote_bda,
+ tBT_TRANSPORT transport, tBTM_CMPL_CB* p_cb) {
+ tACL_CONN* p;
+#define BTM_READ_RSSI_TYPE_CUR 0x00
+#define BTM_READ_RSSI_TYPE_MAX 0X01
+
+ VLOG(2) << __func__ << ": RemBdAddr: " << remote_bda;
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_tx_power_cmpl_cb) return (BTM_BUSY);
+
+ p = btm_bda_to_acl(remote_bda, transport);
+ if (p != (tACL_CONN*)NULL) {
+ btm_cb.devcb.p_tx_power_cmpl_cb = p_cb;
+ alarm_set_on_mloop(btm_cb.devcb.read_tx_power_timer,
+ BTM_DEV_REPLY_TIMEOUT_MS, btm_read_tx_power_timeout,
+ NULL);
+
+ if (p->transport == BT_TRANSPORT_LE) {
+ btm_cb.devcb.read_tx_pwr_addr = remote_bda;
+ btsnd_hcic_ble_read_adv_chnl_tx_power();
+ } else {
+ btsnd_hcic_read_tx_power(p->hci_handle, BTM_READ_RSSI_TYPE_CUR);
+ }
+
+ return (BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_tx_power_timeout
+ *
+ * Description Callback when reading the tx power times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_tx_power_timeout(UNUSED_ATTR void* data) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_tx_power_cmpl_cb;
+ btm_cb.devcb.p_tx_power_cmpl_cb = NULL;
+ if (p_cb) (*p_cb)((void*)NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_tx_power_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read tx power request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_tx_power_complete(uint8_t* p, bool is_ble) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_tx_power_cmpl_cb;
+ tBTM_TX_POWER_RESULT result;
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.read_tx_power_timer);
+ btm_cb.devcb.p_tx_power_cmpl_cb = NULL;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ STREAM_TO_UINT8(result.hci_status, p);
+
+ if (result.hci_status == HCI_SUCCESS) {
+ result.status = BTM_SUCCESS;
+
+ if (!is_ble) {
+ uint16_t handle;
+ STREAM_TO_UINT16(handle, p);
+ STREAM_TO_UINT8(result.tx_power, p);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (uint16_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) {
+ result.rem_bda = p_acl_cb->remote_addr;
+ break;
+ }
+ }
+ } else {
+ STREAM_TO_UINT8(result.tx_power, p);
+ result.rem_bda = btm_cb.devcb.read_tx_pwr_addr;
+ }
+ BTM_TRACE_DEBUG("BTM TX power Complete: tx_power %d, hci status 0x%02x",
+ result.tx_power, result.hci_status);
+ } else {
+ result.status = BTM_ERR_PROCESSING;
+ }
+
+ (*p_cb)(&result);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_rssi_timeout
+ *
+ * Description Callback when reading the RSSI times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_rssi_timeout(UNUSED_ATTR void* data) {
+ tBTM_RSSI_RESULT result;
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_rssi_cmpl_cb;
+ btm_cb.devcb.p_rssi_cmpl_cb = NULL;
+ result.status = BTM_DEVICE_TIMEOUT;
+ if (p_cb) (*p_cb)(&result);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_rssi_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read rssi request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_rssi_complete(uint8_t* p) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_rssi_cmpl_cb;
+ tBTM_RSSI_RESULT result;
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.read_rssi_timer);
+ btm_cb.devcb.p_rssi_cmpl_cb = NULL;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ STREAM_TO_UINT8(result.hci_status, p);
+
+ if (result.hci_status == HCI_SUCCESS) {
+ uint16_t handle;
+ result.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16(handle, p);
+
+ STREAM_TO_UINT8(result.rssi, p);
+ BTM_TRACE_DEBUG("BTM RSSI Complete: rssi %d, hci status 0x%02x",
+ result.rssi, result.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (uint16_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) {
+ result.rem_bda = p_acl_cb->remote_addr;
+ break;
+ }
+ }
+ } else {
+ result.status = BTM_ERR_PROCESSING;
+ }
+
+ (*p_cb)(&result);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_failed_contact_counter_timeout
+ *
+ * Description Callback when reading the failed contact counter times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_failed_contact_counter_timeout(UNUSED_ATTR void* data) {
+ tBTM_FAILED_CONTACT_COUNTER_RESULT result;
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_failed_contact_counter_cmpl_cb;
+ btm_cb.devcb.p_failed_contact_counter_cmpl_cb = NULL;
+ result.status = BTM_DEVICE_TIMEOUT;
+ if (p_cb) (*p_cb)(&result);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_failed_contact_counter_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read failed contact
+ * counter request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_failed_contact_counter_complete(uint8_t* p) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_failed_contact_counter_cmpl_cb;
+ tBTM_FAILED_CONTACT_COUNTER_RESULT result;
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.read_failed_contact_counter_timer);
+ btm_cb.devcb.p_failed_contact_counter_cmpl_cb = NULL;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ uint16_t handle;
+ STREAM_TO_UINT8(result.hci_status, p);
+
+ if (result.hci_status == HCI_SUCCESS) {
+ result.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16(handle, p);
+
+ STREAM_TO_UINT16(result.failed_contact_counter, p);
+ BTM_TRACE_DEBUG(
+ "BTM Failed Contact Counter Complete: counter %u, hci status 0x%02x",
+ result.failed_contact_counter, result.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (uint16_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) {
+ result.rem_bda = p_acl_cb->remote_addr;
+ break;
+ }
+ }
+ } else {
+ result.status = BTM_ERR_PROCESSING;
+ }
+
+ (*p_cb)(&result);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_automatic_flush_timeout_timeout
+ *
+ * Description Callback when reading the automatic flush timeout times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_automatic_flush_timeout_timeout(UNUSED_ATTR void* data) {
+ tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT result;
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb;
+ btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb = nullptr;
+ result.status = BTM_DEVICE_TIMEOUT;
+ if (p_cb) (*p_cb)(&result);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_automatic_flush_timeout_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read automatic flush
+ * timeout request.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_automatic_flush_timeout_complete(uint8_t* p) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb;
+ tBTM_AUTOMATIC_FLUSH_TIMEOUT_RESULT result;
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.read_automatic_flush_timeout_timer);
+ btm_cb.devcb.p_automatic_flush_timeout_cmpl_cb = nullptr;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ uint16_t handle;
+ STREAM_TO_UINT8(result.hci_status, p);
+
+ if (result.hci_status == HCI_SUCCESS) {
+ result.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16(handle, p);
+
+ STREAM_TO_UINT16(result.automatic_flush_timeout, p);
+ BTM_TRACE_DEBUG(
+ "BTM Automatic Flush Timeout Complete: timeout %u, hci status 0x%02x",
+ result.automatic_flush_timeout, result.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (uint16_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) {
+ result.rem_bda = p_acl_cb->remote_addr;
+ break;
+ }
+ }
+ } else {
+ result.status = BTM_ERR_PROCESSING;
+ }
+
+ (*p_cb)(&result);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_link_quality_timeout
+ *
+ * Description Callback when reading the link quality times out.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_link_quality_timeout(UNUSED_ATTR void* data) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_link_qual_cmpl_cb;
+ btm_cb.devcb.p_link_qual_cmpl_cb = NULL;
+ if (p_cb) (*p_cb)((void*)NULL);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_read_link_quality_complete
+ *
+ * Description This function is called when the command complete message
+ * is received from the HCI for the read link quality.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_read_link_quality_complete(uint8_t* p) {
+ tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_link_qual_cmpl_cb;
+ tBTM_LINK_QUALITY_RESULT result;
+ tACL_CONN* p_acl_cb = &btm_cb.acl_db[0];
+
+ BTM_TRACE_DEBUG("%s", __func__);
+ alarm_cancel(btm_cb.devcb.read_link_quality_timer);
+ btm_cb.devcb.p_link_qual_cmpl_cb = NULL;
+
+ /* If there was a registered callback, call it */
+ if (p_cb) {
+ STREAM_TO_UINT8(result.hci_status, p);
+
+ if (result.hci_status == HCI_SUCCESS) {
+ uint16_t handle;
+ result.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16(handle, p);
+
+ STREAM_TO_UINT8(result.link_quality, p);
+ BTM_TRACE_DEBUG(
+ "BTM Link Quality Complete: Link Quality %d, hci status 0x%02x",
+ result.link_quality, result.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (uint16_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) {
+ result.rem_bda = p_acl_cb->remote_addr;
+ break;
+ }
+ }
+ } else {
+ result.status = BTM_ERR_PROCESSING;
+ }
+
+ (*p_cb)(&result);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_remove_acl
+ *
+ * Description This function is called to disconnect an ACL connection
+ *
+ * Returns BTM_SUCCESS if successfully initiated, otherwise
+ * BTM_NO_RESOURCES.
+ *
+ ******************************************************************************/
+tBTM_STATUS btm_remove_acl(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
+ uint16_t hci_handle = BTM_GetHCIConnHandle(bd_addr, transport);
+ tBTM_STATUS status = BTM_SUCCESS;
+
+ BTM_TRACE_DEBUG("btm_remove_acl");
+#if (BTM_DISC_DURING_RS == TRUE)
+ tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
+
+ /* Role Switch is pending, postpone until completed */
+ if (p_dev_rec && (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING)) {
+ p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING;
+ } else /* otherwise can disconnect right away */
+#endif
+ {
+ if (hci_handle != 0xFFFF && p_dev_rec &&
+ p_dev_rec->sec_state != BTM_SEC_STATE_DISCONNECTING) {
+ btsnd_hcic_disconnect(hci_handle, HCI_ERR_PEER_USER);
+ } else {
+ status = BTM_UNKNOWN_ADDR;
+ }
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+ *
+ * Function BTM_SetTraceLevel
+ *
+ * Description This function sets the trace level for BTM. If called with
+ * a value of 0xFF, it simply returns the current trace level.
+ *
+ * Returns The new or current trace level
+ *
+ ******************************************************************************/
+uint8_t BTM_SetTraceLevel(uint8_t new_level) {
+ BTM_TRACE_DEBUG("BTM_SetTraceLevel");
+ if (new_level != 0xFF) btm_cb.trace_level = new_level;
+
+ return (btm_cb.trace_level);
+}
+
+/*******************************************************************************
+ *
+ * Function btm_cont_rswitch
+ *
+ * Description This function is called to continue processing an active
+ * role switch. It first disables encryption if enabled and
+ * EPR is not supported
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btm_cont_rswitch(tACL_CONN* p, tBTM_SEC_DEV_REC* p_dev_rec,
+ uint8_t hci_status) {
+ BTM_TRACE_DEBUG("btm_cont_rswitch");
+ /* Check to see if encryption needs to be turned off if pending
+ change of link key or role switch */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) {
+ /* Must turn off Encryption first if necessary */
+ /* Some devices do not support switch or change of link key while encryption
+ * is on */
+ if (p_dev_rec != NULL &&
+ ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) &&
+ !BTM_EPR_AVAILABLE(p)) {
+ btsnd_hcic_set_conn_encrypt(p->hci_handle, false);
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF;
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+ } else /* Encryption not used or EPR supported, continue with switch
+ and/or change of link key */
+ {
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+#if (BTM_DISC_DURING_RS == TRUE)
+ if (p_dev_rec) p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+#endif
+ btsnd_hcic_switch_role(p->remote_addr, (uint8_t)!p->link_role);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_resubmit_page
+ *
+ * Description send pending page request
+ *
+ ******************************************************************************/
+void btm_acl_resubmit_page(void) {
+ tBTM_SEC_DEV_REC* p_dev_rec;
+ BT_HDR* p_buf;
+ uint8_t* pp;
+ BTM_TRACE_DEBUG("btm_acl_resubmit_page");
+ /* If there were other page request schedule can start the next one */
+ p_buf = (BT_HDR*)fixed_queue_try_dequeue(btm_cb.page_queue);
+ if (p_buf != NULL) {
+ /* skip 3 (2 bytes opcode and 1 byte len) to get to the bd_addr
+ * for both create_conn and rmt_name */
+ pp = (uint8_t*)(p_buf + 1) + p_buf->offset + 3;
+
+ RawAddress bda;
+ STREAM_TO_BDADDR(bda, pp);
+
+ p_dev_rec = btm_find_or_alloc_dev(bda);
+
+ btm_cb.connecting_bda = p_dev_rec->bd_addr;
+ memcpy(btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN);
+
+ btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p_buf);
+ } else {
+ btm_cb.paging = false;
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_reset_paging
+ *
+ * Description set paging to false and free the page queue - called at
+ * hci_reset
+ *
+ ******************************************************************************/
+void btm_acl_reset_paging(void) {
+ BT_HDR* p;
+ BTM_TRACE_DEBUG("btm_acl_reset_paging");
+ /* If we sent reset we are definitely not paging any more */
+ while ((p = (BT_HDR*)fixed_queue_try_dequeue(btm_cb.page_queue)) != NULL)
+ osi_free(p);
+
+ btm_cb.paging = false;
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_paging
+ *
+ * Description send a paging command or queue it in btm_cb
+ *
+ ******************************************************************************/
+void btm_acl_paging(BT_HDR* p, const RawAddress& bda) {
+ tBTM_SEC_DEV_REC* p_dev_rec;
+
+ VLOG(2) << __func__ << ":" << btm_cb.discing << " , paging:" << btm_cb.paging
+ << " BDA: " << bda;
+
+ if (btm_cb.discing) {
+ btm_cb.paging = true;
+ fixed_queue_enqueue(btm_cb.page_queue, p);
+ } else {
+ if (!BTM_ACL_IS_CONNECTED(bda)) {
+ VLOG(1) << "connecting_bda: " << btm_cb.connecting_bda;
+ if (btm_cb.paging && bda == btm_cb.connecting_bda) {
+ fixed_queue_enqueue(btm_cb.page_queue, p);
+ } else {
+ p_dev_rec = btm_find_or_alloc_dev(bda);
+ btm_cb.connecting_bda = p_dev_rec->bd_addr;
+ memcpy(btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN);
+
+ btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
+ }
+
+ btm_cb.paging = true;
+ } else /* ACL is already up */
+ {
+ btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_notif_conn_collision
+ *
+ * Description Send connection collision event to upper layer if registered
+ *
+ * Returns true if sent out to upper layer,
+ * false if no one needs the notification.
+ *
+ ******************************************************************************/
+bool btm_acl_notif_conn_collision(const RawAddress& bda) {
+ /* Report possible collision to the upper layer. */
+ if (btm_cb.p_bl_changed_cb) {
+ VLOG(1) << __func__ << " RemBdAddr: " << bda;
+
+ tBTM_BL_EVENT_DATA evt_data;
+ evt_data.event = BTM_BL_COLLISION_EVT;
+ evt_data.conn.p_bda = &bda;
+ evt_data.conn.transport = BT_TRANSPORT_BR_EDR;
+ evt_data.conn.handle = BTM_INVALID_HCI_HANDLE;
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function btm_acl_chk_peer_pkt_type_support
+ *
+ * Description Check if peer supports requested packets
+ *
+ ******************************************************************************/
+void btm_acl_chk_peer_pkt_type_support(tACL_CONN* p, uint16_t* p_pkt_type) {
+ /* 3 and 5 slot packets? */
+ if (!HCI_3_SLOT_PACKETS_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3);
+
+ if (!HCI_5_SLOT_PACKETS_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5);
+
+ /* 2 and 3 MPS support? */
+ if (!HCI_EDR_ACL_2MPS_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ /* Not supported. Add 'not_supported' mask for all 2MPS packet types */
+ *p_pkt_type |=
+ (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 +
+ BTM_ACL_PKT_TYPES_MASK_NO_2_DH5);
+
+ if (!HCI_EDR_ACL_3MPS_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ /* Not supported. Add 'not_supported' mask for all 3MPS packet types */
+ *p_pkt_type |=
+ (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 +
+ BTM_ACL_PKT_TYPES_MASK_NO_3_DH5);
+
+ /* EDR 3 and 5 slot support? */
+ if (HCI_EDR_ACL_2MPS_SUPPORTED(p->peer_lmp_feature_pages[0]) ||
+ HCI_EDR_ACL_3MPS_SUPPORTED(p->peer_lmp_feature_pages[0])) {
+ if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ /* Not supported. Add 'not_supported' mask for all 3-slot EDR packet types
+ */
+ *p_pkt_type |=
+ (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3);
+
+ if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p->peer_lmp_feature_pages[0]))
+ /* Not supported. Add 'not_supported' mask for all 5-slot EDR packet types
+ */
+ *p_pkt_type |=
+ (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5);
+ }
+}
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index 0358a23..ae19638 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -133,6 +133,11 @@
"-fvisibility=hidden",
"-DLOG_NDEBUG=1",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
// Linux RootCanal Executable
@@ -190,6 +195,9 @@
all_undefined: false,
},
},
+ darwin: {
+ enabled: false,
+ },
},
}