網站首頁 編程語言 正文
一、APP端調用
1、注冊廣播監聽查找結果
//藍牙發現設備和查找結束廣播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(btReceiver, intentFilter);
BroadcastReceiver btReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device != null) {
//這里收到的是單條設備信息,可以放到List中進行刷新列表
//設備名稱:device.getName()
//設備地址:device.getAddress()
if(device.getBondState() == BluetoothDevice.BOND_BONDED) {
//已配對設備
}
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
//查找結束
}
}
};
2、開始查找附近設備
btAdapter.startDiscovery();
3、異常處理
上面代碼無法接收到 BluetoothDevice.ACTION_FOUND 廣播,查找資料發現 Android 6.0 后需要增加兩個權限并且需要動態申請。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M
&& checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {//請求權限
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1);
}
這里的權限動態申請就簡單處理一下,不做詳細介紹了。
另一個問題是收到發現設備廣播數據,很多數據的 getName() 為 null,這里還需要做一些判空處理。
二、查找設備源碼分析
1、BluetoothAdapter.startDiscovery()
private IBluetooth mService;
public boolean startDiscovery() {
if (getState() != STATE_ON) {
return false;
}
try {
mServiceLock.readLock().lock();
if (mService != null) {
return mService.startDiscovery(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
} finally {
mServiceLock.readLock().unlock();
}
return false;
}
可以看到這里直接調用 mService.startDiscovery(),IBluetooth 的實現類為 AdapterService,相較于藍牙開關功能,省去了調用 BluetoothManagerService 的部分。
2、AdapterService.startDiscovery()
public boolean startDiscovery(AttributionSource attributionSource) {
AdapterService service = getService();
if (service == null || !callerIsSystemOrActiveUser(TAG, "startDiscovery")) {
return false;
}
if (!Utils.checkScanPermissionForDataDelivery(
service, attributionSource, "Starting discovery.")) {
return false;
}
return service.startDiscovery(attributionSource);
}
boolean startDiscovery(AttributionSource attributionSource) {
UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
debugLog("startDiscovery");
String callingPackage = attributionSource.getPackageName();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
boolean isQApp = Utils.isQApp(this, callingPackage);
boolean hasDisavowedLocation = Utils.hasDisavowedLocationForScan(this, attributionSource, mTestModeEnabled);
String permission = null;
if (Utils.checkCallerHasNetworkSettingsPermission(this)) {
permission = android.Manifest.permission.NETWORK_SETTINGS;
} else if (Utils.checkCallerHasNetworkSetupWizardPermission(this)) {
permission = android.Manifest.permission.NETWORK_SETUP_WIZARD;
} else if (!hasDisavowedLocation) {
if (isQApp) {
if (!Utils.checkCallerHasFineLocation(this, attributionSource, callingUser)) {
return false;
}
permission = android.Manifest.permission.ACCESS_FINE_LOCATION;
} else {
if (!Utils.checkCallerHasCoarseLocation(this, attributionSource, callingUser)) {
return false;
}
permission = android.Manifest.permission.ACCESS_COARSE_LOCATION;
}
}
synchronized (mDiscoveringPackages) {
mDiscoveringPackages.add(new DiscoveringPackage(callingPackage, permission, hasDisavowedLocation));
}
return startDiscoveryNative();
}
這里可以看到權限驗證相關的內容。最后調用 startDiscoveryNative() 進入 JNI 層,在com_android_bluetooth_btservice_AdapterService.cpp文件中,調用startDiscoveryNative方法。
3、startDiscoveryNative()
源碼位置:packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
ALOGV("%s", __func__);
if (!sBluetoothInterface) return JNI_FALSE;
int ret = sBluetoothInterface->start_discovery();
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
4、start_discovery() 掃描入口
源碼位置:system/bt/btif/src/bluetooth.cc
static int start_discovery(void) {
if (!interface_ready()) return BT_STATUS_NOT_READY;
do_in_main_thread(FROM_HERE, base::BindOnce(btif_dm_start_discovery));
return BT_STATUS_SUCCESS;
}
5、btif_dm_start_discovery() 配置參數
源碼位置:/system/bt/btif/src/btif_dm.cc
設備管理(DM)相關的功能
void btif_dm_start_discovery(void) {
......
/* Will be enabled to true once inquiry busy level has been received */
btif_dm_inquiry_in_progress = false;
/* find nearby devices */
BTA_DmSearch(btif_dm_search_devices_evt, is_bonding_or_sdp());
}
6、BTA_DmSearch() 搜索對等藍牙設備
源碼位置:/system/bt/bta/dm/bta_dm_api.cc
它執行查詢并獲取設備的遠程名稱。
void BTA_DmSearch(tBTA_DM_SEARCH_CBACK* p_cback, bool is_bonding_or_sdp) {
tBTA_DM_API_SEARCH* p_msg = (tBTA_DM_API_SEARCH*)osi_calloc(sizeof(tBTA_DM_API_SEARCH));
/* Queue request if a device is bonding or performing service discovery */
if (is_bonding_or_sdp) {
p_msg->hdr.event = BTA_DM_API_QUEUE_SEARCH_EVT;
} else {
p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
}
p_msg->p_cback = p_cback;
bta_sys_sendmsg(p_msg);
}
7、bta_sys_sendmsg() 發送掃描消息
源碼位置:/system/bt/bta/sys/bta_sys_main.cc
向 BTU TASK 發送掃描消息,由藍牙設備管理模塊處理。
void bta_sys_sendmsg(void* p_msg) {
if (do_in_main_thread(
FROM_HERE,
base::Bind(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
BT_STATUS_SUCCESS) {
LOG(ERROR) << __func__ << ": do_in_main_thread failed";
}
}
這里調用的是 do_in_main_thread() 方法,這個方法其實在 4 中已經調用過一次。這個方法只是返回一個狀態。
位置:/system/bt/stack/btu/btu_task.cc
bt_status_t do_in_main_thread(const base::Location& from_here, base::OnceClosure task) {
if (!main_thread.DoInThread(from_here, std::move(task))) {
LOG(ERROR) << __func__ << ": failed from " << from_here.ToString();
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
BTU TASK收到消息后,調用 bta_dm_main.c 的(用于DM的狀態機事件處理函數) bta_dm_search_sm_execute() 執行狀態切換和 inquiry 流程。這里就不往下分析了。
原文鏈接:https://blog.csdn.net/c19344881x/article/details/128723883
相關推薦
- 2022-05-22 nginx共享內存的機制詳解_nginx
- 2022-08-05 基于redis+lua進行限流
- 2022-01-16 Jquery+Css+Html實現返選、批量刪除、高亮顯示功能
- 2022-07-08 C#使用WebClient實現上傳下載_C#教程
- 2022-11-08 Go讀取文件與寫入文件的三種方法操作指南_Golang
- 2022-11-16 python中內置類型添加屬性問題詳解_python
- 2022-10-21 C#中匿名方法與委托的關系介紹_C#教程
- 2022-07-07 C++實現數組中元素組合出最大值_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支