当前位置: 首页 > news >正文

Android 蓝牙开发——查找附近设备(四)

一、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 流程。这里就不往下分析了。

相关文章:

  • 定制网站建设公司推荐/今日发生的重大国际新闻
  • 石家庄做网络推广的网站/100个免费推广b站
  • 零售客户电商网站登录/搜索引擎营销的模式有哪些
  • 网站平台做推广方案设计/如何建网站要什么条件
  • 企业网站建设的内容/网站要怎么创建
  • 推广网站的方法/学校招生网络营销方案
  • 【6s965-fall2022】深度学习的效率指标
  • 【markdown】语法 添加`emoji`表情
  • Java——全排列
  • 威纶通触摸屏配方功能的使用方法示例
  • IB地理学什么?适合什么人学习?
  • MS SQL Server 日志审核工具
  • ES6 课程概述⑦
  • 19/365 java 多线程
  • Java设计模式-观察者模式Observer
  • 开源项目介绍
  • 开源PPP软件PRIDE-PPPAR使用记录(二)解算网友发来的GNSS观测文件
  • 数据分析面试题--SQL面试题