diff --git a/android/app/jni/com_android_bluetooth_hid_host.cpp b/android/app/jni/com_android_bluetooth_hid_host.cpp index 7a164233bc1b5d7e315691c80f0e5364db3b3666..18ba315129c1376610fb79264cbe4ad14b8d6e22 100644 --- a/android/app/jni/com_android_bluetooth_hid_host.cpp +++ b/android/app/jni/com_android_bluetooth_hid_host.cpp @@ -282,7 +282,8 @@ static jboolean connectHidNative(JNIEnv* env, jobject object, } static jboolean disconnectHidNative(JNIEnv* env, jobject object, - jbyteArray address) { + jbyteArray address, + jboolean reconnect_allowed) { jbyte* addr; jboolean ret = JNI_TRUE; if (!sBluetoothHidInterface) return JNI_FALSE; @@ -293,7 +294,8 @@ static jboolean disconnectHidNative(JNIEnv* env, jobject object, return JNI_FALSE; } - bt_status_t status = sBluetoothHidInterface->disconnect((RawAddress*)addr); + bt_status_t status = + sBluetoothHidInterface->disconnect((RawAddress*)addr, reconnect_allowed); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed disconnect hid channel, status: %d", status); ret = JNI_FALSE; @@ -509,7 +511,7 @@ static JNINativeMethod sMethods[] = { {"initializeNative", "()V", (void*)initializeNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"connectHidNative", "([B)Z", (void*)connectHidNative}, - {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative}, + {"disconnectHidNative", "([BZ)Z", (void*)disconnectHidNative}, {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative}, {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative}, {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative}, diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java index dfd7dd20028d2ed8a26b3605eeff80dd15fb18f9..eae47c18811f7b4a5707abfa3440bc5467a80c87 100644 --- a/android/app/src/com/android/bluetooth/hid/HidHostService.java +++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java @@ -190,7 +190,10 @@ public class HidHostService extends ProfileService { break; case MESSAGE_DISCONNECT: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if (!disconnectHidNative(getByteAddress(device))) { + int connectionPolicy = getConnectionPolicy(device); + boolean reconnectAllowed = + connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED; + if (!disconnectHidNative(getByteAddress(device), reconnectAllowed)) { broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING); broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); break; @@ -326,9 +329,7 @@ public class HidHostService extends ProfileService { } }; - /** - * Handlers for incoming service calls - */ + /** Handlers for incoming service calls */ @VisibleForTesting static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub implements IProfileServiceBinder { @@ -1032,7 +1033,7 @@ public class HidHostService extends ProfileService { private native boolean connectHidNative(byte[] btAddress); - private native boolean disconnectHidNative(byte[] btAddress); + private native boolean disconnectHidNative(byte[] btAddress, boolean reconnect_allowed); private native boolean getProtocolModeNative(byte[] btAddress); diff --git a/system/btif/include/btif_hh.h b/system/btif/include/btif_hh.h index d5246871499c7567e31b8a0d666556ba7d0d1049..6ee848771225359b2d855bd46972b4a64a274871 100644 --- a/system/btif/include/btif_hh.h +++ b/system/btif/include/btif_hh.h @@ -111,6 +111,7 @@ typedef struct { uint8_t dev_handle; RawAddress bd_addr; tBTA_HH_ATTR_MASK attr_mask; + bool reconnect_allowed; } btif_hh_added_device_t; /** @@ -134,7 +135,8 @@ extern btif_hh_cb_t btif_hh_cb; btif_hh_device_t* btif_hh_find_connected_dev_by_handle(uint8_t handle); void btif_hh_remove_device(RawAddress bd_addr); -bool btif_hh_add_added_dev(const RawAddress& bda, tBTA_HH_ATTR_MASK attr_mask); +bool btif_hh_add_added_dev(const RawAddress& bda, tBTA_HH_ATTR_MASK attr_mask, + bool reconnect_allowed); bt_status_t btif_hh_virtual_unplug(const RawAddress* bd_addr); void btif_hh_disconnect(RawAddress* bd_addr); void btif_hh_setreport(btif_hh_device_t* p_dev, bthh_report_type_t r_type, diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h index a8bf67eac158c483d2a4144ef0179203f1c7a8c1..392d99e4a3f13e17b7e4dc721f5f484bc40d2912 100644 --- a/system/btif/include/btif_storage.h +++ b/system/btif/include/btif_storage.h @@ -216,6 +216,29 @@ void btif_storage_load_le_devices(void); ******************************************************************************/ bt_status_t btif_storage_load_bonded_devices(void); +/******************************************************************************* + * + * Function btif_storage_set_hid_connection_policy + * + * Description Stores connection policy info in nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btif_storage_set_hid_connection_policy(const RawAddress& addr, + bool reconnect_allowed); +/******************************************************************************* + * + * Function btif_storage_get_hid_connection_policy + * + * Description get connection policy info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btif_storage_get_hid_connection_policy(const RawAddress& addr, + bool* reconnect_allowed); + /******************************************************************************* * * Function btif_storage_add_hid_device_info diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc index fea75ef0cbbb1d7b303f3de8975a0157520daa9c..e9d157b3eef147a6a3e9551d49283a6aa4cd5dd5 100644 --- a/system/btif/src/btif_hh.cc +++ b/system/btif/src/btif_hh.cc @@ -309,6 +309,24 @@ btif_hh_device_t* btif_hh_find_connected_dev_by_handle(uint8_t handle) { return NULL; } +/******************************************************************************* + * + * Function btif_hh_find_added_dev + * + * Description Return the added device pointer of the specified address + * + * Returns Added device entry + ******************************************************************************/ +btif_hh_added_device_t* btif_hh_find_added_dev(const RawAddress& addr) { + for (int i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { + btif_hh_added_device_t* added_dev = &btif_hh_cb.added_devices[i]; + if (added_dev->bd_addr == addr) { + return added_dev; + } + } + return nullptr; +} + /******************************************************************************* * * Function btif_hh_find_dev_by_bda @@ -398,9 +416,38 @@ static void hh_connect_complete(uint8_t handle, RawAddress& bda, HAL_CBACK(bt_hh_callbacks, connection_state_cb, &bda, state); } +static bool hh_connection_allowed(const RawAddress& bda) { + /* Accept connection only if reconnection is allowed for the known device, or + * outgoing connection was requested */ + btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(bda); + if (added_dev != nullptr && added_dev->reconnect_allowed) { + LOG_VERBOSE("Connection allowed %s", ADDRESS_TO_LOGGABLE_CSTR(bda)); + return true; + } else if (btif_hh_cb.pending_conn_address == bda) { + LOG_VERBOSE("Device connection was pending for: %s, status: %s", + ADDRESS_TO_LOGGABLE_CSTR(bda), + btif_hh_status_text(btif_hh_cb.status).c_str()); + return true; + } + + return false; +} + static void hh_open_handler(tBTA_HH_CONN& conn) { LOG_DEBUG("status = %d, handle = %d", conn.status, conn.handle); + if (!hh_connection_allowed(conn.bda)) { + LOG_WARN("Reject unexpected incoming HID Connection, device: %s", + ADDRESS_TO_LOGGABLE_CSTR(conn.bda)); + btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_handle(conn.handle); + if (p_dev != nullptr) { + p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED; + } + + hh_connect_complete(conn.handle, conn.bda, BTIF_HH_DEV_DISCONNECTED); + return; + } + HAL_CBACK(bt_hh_callbacks, connection_state_cb, (RawAddress*)&conn.bda, BTHH_CONN_STATE_CONNECTING); btif_hh_cb.pending_conn_address = RawAddress::kEmpty; @@ -454,7 +501,8 @@ static void hh_open_handler(tBTA_HH_CONN& conn) { * * Returns true if add successfully, otherwise false. ******************************************************************************/ -bool btif_hh_add_added_dev(const RawAddress& bda, tBTA_HH_ATTR_MASK attr_mask) { +bool btif_hh_add_added_dev(const RawAddress& bda, tBTA_HH_ATTR_MASK attr_mask, + bool reconnect_allowed) { int i; for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { if (btif_hh_cb.added_devices[i].bd_addr == bda) { @@ -464,10 +512,12 @@ bool btif_hh_add_added_dev(const RawAddress& bda, tBTA_HH_ATTR_MASK attr_mask) { } for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) { if (btif_hh_cb.added_devices[i].bd_addr.IsEmpty()) { - LOG(WARNING) << " Added device " << ADDRESS_TO_LOGGABLE_STR(bda); + LOG(WARNING) << " Added device " << ADDRESS_TO_LOGGABLE_STR(bda) + << " reconnection allowed: " << reconnect_allowed; btif_hh_cb.added_devices[i].bd_addr = bda; btif_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; btif_hh_cb.added_devices[i].attr_mask = attr_mask; + btif_hh_cb.added_devices[i].reconnect_allowed = reconnect_allowed; return true; } } @@ -1025,7 +1075,7 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { p_data->dscp_info.version, p_data->dscp_info.ctry_code, len, p_data->dscp_info.descriptor.dsc_list); - if (btif_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask)) { + if (btif_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask, true)) { tBTA_HH_DEV_DSCP_INFO dscp_info; bt_status_t ret; btif_hh_copy_hid_info(&dscp_info, &p_data->dscp_info); @@ -1042,6 +1092,8 @@ static void btif_hh_upstreams_evt(uint16_t event, char* p_param) { p_data->dscp_info.ssr_min_tout, len, p_data->dscp_info.descriptor.dsc_list); + btif_storage_set_hid_connection_policy(p_dev->bd_addr, true); + ASSERTC(ret == BT_STATUS_SUCCESS, "storing hid info failed", ret); BTIF_TRACE_WARNING("BTA_HH_GET_DSCP_EVT: Called add device"); @@ -1341,12 +1393,20 @@ static bt_status_t connect(RawAddress* bd_addr) { return BT_STATUS_NOT_READY; } + /* If the device was already added, ensure that reconnections are allowed */ + btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(*bd_addr); + if (added_dev != nullptr && !added_dev->reconnect_allowed) { + added_dev->reconnect_allowed = true; + btif_storage_set_hid_connection_policy(*bd_addr, true); + } + p_dev = btif_hh_find_connected_dev_by_bda(*bd_addr); if (p_dev) { if (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED || p_dev->dev_status == BTHH_CONN_STATE_CONNECTING) { BTIF_TRACE_ERROR("%s: Error, device %s already connected.", __func__, ADDRESS_TO_LOGGABLE_CSTR(*bd_addr)); + return BT_STATUS_DONE; } else if (p_dev->dev_status == BTHH_CONN_STATE_DISCONNECTING) { BTIF_TRACE_ERROR("%s: Error, device %s is busy with (dis)connecting.", @@ -1368,7 +1428,7 @@ static bt_status_t connect(RawAddress* bd_addr) { * Returns bt_status_t * ******************************************************************************/ -static bt_status_t disconnect(RawAddress* bd_addr) { +static bt_status_t disconnect(RawAddress* bd_addr, bool reconnect_allowed) { CHECK_BTHH_INIT(); BTIF_TRACE_EVENT("BTHH: %s", __func__); btif_hh_device_t* p_dev; @@ -1380,6 +1440,16 @@ static bt_status_t disconnect(RawAddress* bd_addr) { return BT_STATUS_UNHANDLED; } + if (!reconnect_allowed) { + LOG_INFO("Incoming reconnections disabled for device %s", + ADDRESS_TO_LOGGABLE_CSTR(*bd_addr)); + btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(*bd_addr); + if (added_dev != nullptr && added_dev->reconnect_allowed) { + added_dev->reconnect_allowed = false; + btif_storage_set_hid_connection_policy(added_dev->bd_addr, false); + } + } + p_dev = btif_hh_find_connected_dev_by_bda(*bd_addr); if (!p_dev) { BTIF_TRACE_ERROR("%s: Error, device %s not opened.", __func__, @@ -1524,9 +1594,10 @@ static bt_status_t set_info(RawAddress* bd_addr, bthh_hid_info_t hid_info) { (uint8_t*)osi_malloc(dscp_info.descriptor.dl_len); memcpy(dscp_info.descriptor.dsc_list, &(hid_info.dsc_list), hid_info.dl_len); - if (btif_hh_add_added_dev(*bd_addr, hid_info.attr_mask)) { + if (btif_hh_add_added_dev(*bd_addr, hid_info.attr_mask, true)) { BTA_HhAddDev(*bd_addr, hid_info.attr_mask, hid_info.sub_class, hid_info.app_id, dscp_info); + btif_storage_set_hid_connection_policy(*bd_addr, true); } osi_free_and_reset((void**)&dscp_info.descriptor.dsc_list); diff --git a/system/btif/src/btif_profile_storage.cc b/system/btif/src/btif_profile_storage.cc index eb423ba06da515a4c72762589795002ec7835553..92ad612c9f1f09aeb545e6c848c69c3a32b55fd5 100644 --- a/system/btif/src/btif_profile_storage.cc +++ b/system/btif/src/btif_profile_storage.cc @@ -80,6 +80,7 @@ using bluetooth::groups::DeviceGroups; #define BTIF_STORAGE_LEAUDIO_SOURCE_SUPPORTED_CONTEXT_TYPE \ "SourceSupportedContextType" #define BTIF_STORAGE_DEVICE_GROUP_BIN "DeviceGroupBin" +#define BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED "HidReConnectAllowed" #define STORAGE_HID_ATRR_MASK_SIZE (4) #define STORAGE_HID_SUB_CLASS_SIZE (2) @@ -104,6 +105,49 @@ using bluetooth::groups::DeviceGroups; STORAGE_HID_VERSION_SIZE + 1 + STORAGE_HID_CTRY_CODE_SIZE + 1 + \ STORAGE_HID_DESC_LEN_SIZE + 1 + STORAGE_HID_DESC_MAX_SIZE + 1) +/******************************************************************************* + * + * Function btif_storage_set_hid_connection_policy + * + * Description Stores connection policy info in nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btif_storage_set_hid_connection_policy(const RawAddress& addr, + bool reconnect_allowed) { + std::string bdstr = addr.ToString(); + + if (btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED, + reconnect_allowed)) { + return BT_STATUS_SUCCESS; + } else { + return BT_STATUS_FAIL; + } +} + +/******************************************************************************* + * + * Function btif_storage_get_hid_connection_policy + * + * Description get connection policy info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btif_storage_get_hid_connection_policy(const RawAddress& addr, + bool* reconnect_allowed) { + std::string bdstr = addr.ToString(); + + // For backward compatibility, assume that the reconnection is allowed in the + // absence of the key + int value = 1; + btif_config_get_int(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED, &value); + *reconnect_allowed = (value != 0); + + return BT_STATUS_SUCCESS; +} + /******************************************************************************* * * Function btif_storage_add_hid_device_info @@ -198,8 +242,11 @@ bt_status_t btif_storage_load_bonded_hid_info(void) { (uint8_t*)dscp_info.descriptor.dsc_list, &len); } + bool reconnect_allowed = false; + btif_storage_get_hid_connection_policy(bd_addr, &reconnect_allowed); + // add extracted information to BTA HH - if (btif_hh_add_added_dev(bd_addr, attr_mask)) { + if (btif_hh_add_added_dev(bd_addr, attr_mask, reconnect_allowed)) { BTA_HhAddDev(bd_addr, attr_mask, sub_class, app_id, dscp_info); } } @@ -231,6 +278,7 @@ bt_status_t btif_storage_remove_hid_info(const RawAddress& remote_bd_addr) { btif_config_remove(bdstr, "HidSSRMaxLatency"); btif_config_remove(bdstr, "HidSSRMinTimeout"); btif_config_remove(bdstr, "HidDescriptor"); + btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED); return BT_STATUS_SUCCESS; } diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs index 910d04af7846fb8b94ca962df3b849bca8687cd7..bec54c5f441527e8c8258bdd7d2b6ad2dd85234b 100644 --- a/system/gd/rust/linux/stack/src/bluetooth.rs +++ b/system/gd/rust/linux/stack/src/bluetooth.rs @@ -2446,7 +2446,7 @@ impl IBluetooth for Bluetooth { if UuidHelper::is_profile_supported(&p) { match p { Profile::Hid | Profile::Hogp => { - self.hh.as_ref().unwrap().disconnect(&mut addr.unwrap()); + self.hh.as_ref().unwrap().disconnect(&mut addr.unwrap(), true); } Profile::A2dpSink @@ -2574,7 +2574,7 @@ impl BtifHHCallbacks for Bluetooth { "[{}]: Rejecting a unbonded device's attempt to connect to HID/HOG profiles", DisplayAddress(&address) ); - self.hh.as_ref().unwrap().disconnect(&mut address); + self.hh.as_ref().unwrap().disconnect(&mut address, true); } } diff --git a/system/gd/rust/topshim/src/profiles/hid_host.rs b/system/gd/rust/topshim/src/profiles/hid_host.rs index ce60d93354c75a4b5f08ff624821f716bd80a8f5..76c5c685c46cce98469c0f1b66636bff2652ac44 100644 --- a/system/gd/rust/topshim/src/profiles/hid_host.rs +++ b/system/gd/rust/topshim/src/profiles/hid_host.rs @@ -238,7 +238,7 @@ impl HidHost { #[profile_enabled_or(BtStatus::NotReady)] pub fn disconnect(&self, addr: &mut RawAddress) -> BtStatus { let addr_ptr = LTCheckedPtrMut::from_ref(addr); - BtStatus::from(ccall!(self, disconnect, addr_ptr.into())) + BtStatus::from(ccall!(self, disconnect, addr_ptr.into(), true)) } #[profile_enabled_or(BtStatus::NotReady)] diff --git a/system/include/hardware/bt_hh.h b/system/include/hardware/bt_hh.h index f6e6e83abd4a2b4effa36324eb9445e5f219f129..0700ed4a204468a9201e4436bae61abf8208ecc9 100644 --- a/system/include/hardware/bt_hh.h +++ b/system/include/hardware/bt_hh.h @@ -179,7 +179,7 @@ typedef struct { bt_status_t (*connect)(RawAddress* bd_addr); /** dis-connect from hid device */ - bt_status_t (*disconnect)(RawAddress* bd_addr); + bt_status_t (*disconnect)(RawAddress* bd_addr, bool reconnect_allowed); /** Virtual UnPlug (VUP) the specified HID device */ bt_status_t (*virtual_unplug)(RawAddress* bd_addr);