Android藍牙服務(wù)查找附近設(shè)備分析探索
一、APP端調(diào)用
1、注冊廣播監(jiān)聽查找結(jié)果
//藍牙發(fā)現(xiàn)設(shè)備和查找結(jié)束廣播 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) { //這里收到的是單條設(shè)備信息,可以放到List中進行刷新列表 //設(shè)備名稱:device.getName() //設(shè)備地址:device.getAddress() if(device.getBondState() == BluetoothDevice.BOND_BONDED) { //已配對設(shè)備 } } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { //查找結(jié)束 } } };
2、開始查找附近設(shè)備
btAdapter.startDiscovery();
3、異常處理
上面代碼無法接收到 BluetoothDevice.ACTION_FOUND 廣播,查找資料發(fā)現(xiàn) Android 6.0 后需要增加兩個權(quán)限并且需要動態(tài)申請。
<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) {//請求權(quán)限 requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1); }
這里的權(quán)限動態(tài)申請就簡單處理一下,不做詳細介紹了。
另一個問題是收到發(fā)現(xiàn)設(shè)備廣播數(shù)據(jù),很多數(shù)據(jù)的 getName() 為 null,這里還需要做一些判空處理。
二、查找設(shè)備源碼分析
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; }
可以看到這里直接調(diào)用 mService.startDiscovery(),IBluetooth 的實現(xiàn)類為 AdapterService,相較于藍牙開關(guān)功能,省去了調(diào)用 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(); }
這里可以看到權(quán)限驗證相關(guān)的內(nèi)容。最后調(diào)用 startDiscoveryNative() 進入 JNI 層,在com_android_bluetooth_btservice_AdapterService.cpp文件中,調(diào)用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() 配置參數(shù)
源碼位置:/system/bt/btif/src/btif_dm.cc
設(shè)備管理(DM)相關(guān)的功能
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() 搜索對等藍牙設(shè)備
源碼位置:/system/bt/bta/dm/bta_dm_api.cc
它執(zhí)行查詢并獲取設(shè)備的遠程名稱。
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() 發(fā)送掃描消息
源碼位置:/system/bt/bta/sys/bta_sys_main.cc
向 BTU TASK 發(fā)送掃描消息,由藍牙設(shè)備管理模塊處理。
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"; } }
這里調(diào)用的是 do_in_main_thread() 方法,這個方法其實在 4 中已經(jīng)調(diào)用過一次。這個方法只是返回一個狀態(tài)。
位置:/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收到消息后,調(diào)用 bta_dm_main.c 的(用于DM的狀態(tài)機事件處理函數(shù)) bta_dm_search_sm_execute() 執(zhí)行狀態(tài)切換和 inquiry 流程。這里就不往下分析了。
到此這篇關(guān)于Android藍牙服務(wù)查找附近設(shè)備分析探索的文章就介紹到這了,更多相關(guān)Android查找附近設(shè)備內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android 中使用TableLayout實現(xiàn)表單布局效果示例
本篇文章主要介紹了android 中使用TableLayout實現(xiàn)表單布局效果示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06Android RadarView雷達圖(蜘蛛網(wǎng)圖)的實現(xiàn)代碼
這篇文章主要介紹了Android RadarView雷達圖(蜘蛛網(wǎng)圖)的實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03Android開發(fā)之自定義刮刮卡實現(xiàn)代碼
本篇文章主要介紹了Android開發(fā)之自定義刮刮卡實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07ActivityManagerService廣播并行發(fā)送與串行發(fā)送示例解析
這篇文章主要為大家介紹了ActivityManagerService廣播并行發(fā)送與串行發(fā)送示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03