Android藍芽協議-藍芽掃描 startDiscovery
藍芽開啟位於setting中,即 package:com.android.settings.bluetooth
BluetoothSettings.java 中。
選單選項MENU_ID_SCAN:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ID_SCAN:
if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_SCAN);
startScanning();
}
return true;
case MENU_ID_RENAME_DEVICE:
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_RENAME);
new BluetoothNameDialogFragment().show(
getFragmentManager(), "rename device");
return true;
case MENU_ID_SHOW_RECEIVED:
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_FILES);
Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
getActivity().sendBroadcast(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
呼叫startScanning,新增可用裝置列表,並將快取的裝置中沒有配對的裝置清除。呼叫LocalBluetoothAdapter.startScanning方法開始掃描。
LocalBluetoothAdapter.startScanning
public void startScanning(boolean force) {
// Only start if we're not already scanning
if (!mAdapter.isDiscovering()) {
//如果不是強制掃描,在SCAN_EXPIRATION_MS間隔內,之掃描一次
if (!force) {
// Don't scan more than frequently than SCAN_EXPIRATION_MS,
// unless forced
if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
return;
}
// If we are playing music, don't scan unless forced.
//播放音樂時,不掃描
A2dpProfile a2dp = mProfileManager.getA2dpProfile();
if (a2dp != null && a2dp.isA2dpPlaying()) {
return;
}
}
//開始掃描
if (mAdapter.startDiscovery()) {
mLastScan = System.currentTimeMillis();
}
}
}
- 真在掃描則跳過
- 如果不是強制開始掃描,在SCAN_EXPIRATION_MS間隔時間內,只掃描一次;如果在播放音樂時,也不執行掃描
- 呼叫BluetoothAdapter.startDiscovery方法,啟動掃描任務
BluetoothAdapter.startDiscovery方法呼叫遠端服務AdapterServiceBinder.startDiscovery方法,開啟掃描。
AdapterServiceBinder是AdapterService內IBluetooth.Stub介面類,主要用來處理藍芽相關的服務呼叫;AdapterServiceBinder將傳入的服務呼叫轉交AdapterService處理。
AdapterServiceBinder.startDiscovery
public boolean startDiscovery() {
if (!Utils.checkCaller()) {
Log.w(TAG, "startDiscovery() - Not allowed for non-active user");
return false;
}
AdapterService service = getService();
if (service == null) return false;
return service.startDiscovery();
}
AdapterService.startDiscovery
boolean startDiscovery() {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
return startDiscoveryNative();
}
進入JNI層
startDiscoveryNative
在com_android_bluetooth_btservice_AdapterService.cpp檔案中,呼叫startDiscoveryNative方法,
//藍芽掃描
static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
ALOGV("%s:",__FUNCTION__);
jboolean result = JNI_FALSE;
if (!sBluetoothInterface) return result;
int ret = sBluetoothInterface->start_discovery();
result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
return result;
}
start_discovery
startDiscoveryNative方法呼叫bluetooth.c中start_discovery方法
//藍芽掃描
static int start_discovery(void)
{
/* sanity check */ //完整性檢查
if (interface_ready() == FALSE)
return BT_STATUS_NOT_READY;
return btif_dm_start_discovery();
}
start_discovery方法呼叫btif_dm.c的btif_dm_start_discovery方法
btif_dm_start_discovery
btif_dm.c 裝置管理(DM)相關的功能
btif_dm_start_discovery方法中最終呼叫 BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
{
tBTA_DM_API_SEARCH *p_msg;
if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
{
memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));
p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
p_msg->services = services;
p_msg->p_cback = p_cback;
p_msg->rs_res = BTA_DM_RS_NONE;
bta_sys_sendmsg(p_msg);
}
}
呼叫bta_sys_sendmsg向BTA傳送掃描任務訊息。
bte_search_devices_evt方法作為掃描任務的回撥函式,
當掃描到裝置時,回撥bte_search_devices_evt方法,將上下文從BTE切換到BTIF
static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
UINT16 param_len = 0;
if (p_data)
param_len += sizeof(tBTA_DM_SEARCH);
/* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
switch (event)
{
case BTA_DM_INQ_RES_EVT:
{
if (p_data->inq_res.p_eir)
param_len += HCI_EXT_INQ_RESPONSE_LEN;
}
break;
case BTA_DM_DISC_RES_EVT:
{
if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data)
param_len += p_data->disc_res.raw_data_size;
}
break;
}
BTIF_TRACE_DEBUG("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);
/* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
if (event == BTA_DM_INQ_RES_EVT)
p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);
btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
(param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
}
btif_dm_search_devices_evt
再呼叫btif_dm_search_devices_evt,btif_dm_search_devices_evt並將掃描到的裝置通過HAL_CBACK方式返回。
static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
{
tBTA_DM_SEARCH *p_search_data;
BTIF_TRACE_EVENT("%s event=%s", __FUNCTION__, dump_dm_search_event(event));
switch (event)
{
case BTA_DM_DISC_RES_EVT:
{
p_search_data = (tBTA_DM_SEARCH *)p_param;
/* Remote name update */
if (strlen((const char *) p_search_data->disc_res.bd_name))
{
bt_property_t properties[1];
bt_bdaddr_t bdaddr;
bt_status_t status;
properties[0].type = BT_PROPERTY_BDNAME;
properties[0].val = p_search_data->disc_res.bd_name;
properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);
bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);
status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);
ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);
HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
status, &bdaddr, 1, properties);
}
/* TODO: Services? */
}
..........................省略............................
}
}
掃描到裝置時,呼叫的是HAL_CBACK(bt_hal_cbacks, device_found_cb,
num_properties, properties);
device_found_cb即是device_found_callback方法,在com_android_bluetooth_btservice_AdapterService.cpp中實現
static void device_found_callback(int num_properties, bt_property_t *properties) {
jbyteArray addr = NULL;
int addr_index;
for (int i = 0; i < num_properties; i++) {
if (properties[i].type == BT_PROPERTY_BDADDR) {
addr = callbackEnv->NewByteArray(properties[i].len);
if (addr) {
callbackEnv->SetByteArrayRegion(addr, 0, properties[i].len,
(jbyte*)properties[i].val);
addr_index = i;
} else {
ALOGE("Address is NULL (unable to allocate) in %s", __FUNCTION__);
return;
}
}
}
if (addr == NULL) {
ALOGE("Address is NULL in %s", __FUNCTION__);
return;
}
ALOGV("%s: Properties: %d, Address: %s", __FUNCTION__, num_properties,
(const char *)properties[addr_index].val);
remote_device_properties_callback(BT_STATUS_SUCCESS, (bt_bdaddr_t *)properties[addr_index].val,
num_properties, properties);
callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr);
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
}
該方法對應JNI方法為method_deviceFoundCallback,即
jclass jniCallbackClass =
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
..................
method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
對應JniCallbacks.java的deviceFoundCallback方法。
deviceFoundCallback
void deviceFoundCallback(byte[] address) {
mRemoteDevices.deviceFoundCallback(address);
}
deviceFoundCallback將掃描到的裝置通過廣播傳送出去
void deviceFoundCallback(byte[] address) {
// The device properties are already registered - we can send the intent
// now
BluetoothDevice device = getDevice(address);
debugLog("deviceFoundCallback: Remote Address is:" + device);
DeviceProperties deviceProp = getDeviceProperties(device);
if (deviceProp == null) {
errorLog("Device Properties is null for Device:" + device);
return;
}
Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothDevice.EXTRA_CLASS,
new BluetoothClass(deviceProp.mBluetoothClass));
intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
mAdapterService.sendBroadcastMultiplePermissions(intent,
new String[] {AdapterService.BLUETOOTH_PERM,
android.Manifest.permission.ACCESS_COARSE_LOCATION});
}
註冊android.bluetooth.device.action.FOUND的廣播接收器都可以收到該廣播。
相關文章
- android藍芽BLE(一) —— 掃描Android藍芽
- MASA MAUI Plugin 安卓藍芽低功耗(一)藍芽掃描UIPlugin安卓藍芽
- MASA MAUI Plugin IOS藍芽低功耗(三)藍芽掃描UIPluginiOS藍芽
- 藍芽(Bluetooth)音訊協議藍芽音訊協議
- 藍芽掃描工具btscanner修復暴力掃描模式藍芽模式
- 藍芽協議體系結構藍芽協議
- Android藍芽使用詳解(普通藍芽)Android藍芽
- Android藍芽那點事——深入瞭解藍芽BlE藍芽 《總結篇》Android藍芽
- 藍芽藍芽
- 基於圖文介面的藍芽掃描工具btscanner藍芽
- 藍芽4.0BLE中協議棧詳解藍芽協議
- “藍芽”——無線上網協議 (轉)藍芽協議
- 藍芽模組藍芽模組
- iOS 藍芽iOS藍芽
- ios 藍芽iOS藍芽
- iOS藍芽開發 Bluetooth藍芽CoreBluetooth 藍芽中心裝置的實現 藍芽外設的實現 有DemoiOS藍芽
- Android藍芽那點事——深入瞭解Android藍芽Bluetooth《進階篇》Android藍芽
- ESP32藍芽學習--GATT協議學習藍芽協議
- BLE藍芽那些事—深入瞭解Android藍芽Bluetooth基礎篇藍芽Android
- Android開發--藍芽操作Android藍芽
- Android-藍芽聊天demoAndroid藍芽
- Android Ble藍芽入門Android藍芽
- Android 開啟藍芽流程Android藍芽
- Android 4.2藍芽介紹Android藍芽
- Android BLE 藍芽開發——掃碼槍基於BLESSEDAndroid藍芽
- 藍芽HFP 藍芽電話剖析(二) --- AT command集合藍芽
- ESP32藍芽學習--藍芽概念學習藍芽
- java+藍芽Java藍芽
- iOSBluetooth(藍芽)iOS藍芽
- 藍芽4.0 BLE藍芽
- 白色藍芽耳機藍芽
- android藍芽BLE(三) —— 廣播Android藍芽
- android藍芽BLE(二) —— 通訊Android藍芽
- Android 傳統藍芽開發Android藍芽
- Android 藍芽音響開發Android藍芽
- android 4.0 藍芽分析之一Android藍芽
- android 4.0 藍芽分析之二Android藍芽
- Android藍芽串列埠通訊Android藍芽串列埠