Android BLE 被动扫描

2024-02-16

我想在我的 Android 应用程序中被动扫描 BLE 广告商。
但我找不到如何做到这一点。

  • 根据蓝牙4.0核心规范,存在被动扫描模式。
    第6卷:核心系统包[低能耗控制器卷],
    D 部分:4.1 被动扫描
    https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=282159 https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=282159
    “设备可以使用被动扫描来查找该区域的广告设备。”

  • 而且,android 有确定扫描类型的参数。 (主动/被动)
    http://androidxref.com/4.3_r2.1/xref/external/bluetooth/bluedroid/stack/btm/btm_ble_gap.c#555 http://androidxref.com/4.3_r2.1/xref/external/bluetooth/bluedroid/stack/btm/btm_ble_gap.c#555
    “scan_type:主动扫描或被动扫描”

  • 同时,iOS可以被动扫描ble广告商。 (遗憾的是,仅限后台模式)
    http://lists.apple.com/archives/bluetooth-dev/2012/May/msg00041.html http://lists.apple.com/archives/bluetooth-dev/2012/May/msg00041.html
    “当应用程序在后台运行时,iOS 会执行被动扫描。”

但我不知道是否可以使用被动扫描模式。

Question:可以在 Android 上使用“被动扫描”吗?如果可以的话,如何使用这个功能?


和...之间的不同active and passive扫描是这样的active扫描请求SCAN_RESPONSE来自广告商的数据包。这是通过发送一个来完成的SCAN_REQUEST检测到广告后发送数据包。两者的信息(有效负载)将在scanRecord设备发现回调的参数。

来自核心规格 https://www.bluetooth.org/en-us/specification/adopted-specifications:

设备可以使用主动扫描来获取更多信息关于设备 这对于填充用户界面可能有用。主动扫描涉及更多 链路层广告消息。

因此,对于任何用例,这两种扫描类型都没有必要有所不同。

但是,如果您想在后台收听广告,那么您需要自己创建一个Service- 没有内置功能(从 Android 4.4 开始)。


对于后台扫描,请以此为例。但扫描将在您的应用程序被系统终止(或被用户停止)时结束。

通过 AlarmManager 启动 PendingIntent(在应用程序的任何位置,必须至少运行一次才能启动服务......)

AlarmManager alarmMgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), BleScanService.class);
PendingIntent scanIntent = PendingIntent.getService(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), intervalMillis, scanIntent);

BleScan服务

public class BleScanService extends Service implements LeScanCallback {

private final static String TAG = BleScanService.class.getSimpleName();

private final IBinder mBinder = new LocalBinder();

private BluetoothManager mBluetoothManager;

private BluetoothAdapter mBluetoothAdapter;

public class LocalBinder extends Binder {
        public BleScanService getService() {
            return BleScanService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initialize();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        long timeToScan = preferences.scanLength().get();
        startScan(timeToScan);

        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Initializes a reference to the local bluetooth adapter.
     * 
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter
        // through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        if (mBluetoothAdapter == null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
                return false;
            }
        }

        Log.d(TAG, "Initialzed scanner.");
        return true;
    }

    /**
     * Checks if bluetooth is correctly set up.
     * 
     * @return
     */
    protected boolean isInitialized() {
        return mBluetoothManager != null && mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
    }

    /**
     * Checks if ble is ready and bluetooth is correctly setup.
     * 
     * @return
     */
    protected boolean isReady() {
        return isInitialized() && isBleReady();
    }

    /**
     * Checks if the device is ble ready.
     * 
     * @return
     */
    protected boolean isBleReady() {
        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        Log.d(TAG, "Found ble device " + device.getName() + " " + device.getAddress());
        broadcastOnDeviceFound(device, scanRecord);
    }

    /**
     * Broadcasts a message with the given device.
     * 
     * @param device
     * @param scanRecord 
     */
    protected void broadcastOnDeviceFound(final BluetoothDevice device, byte[] scanRecord) {
        assert device != null : "Device should not be null.";

        Intent intent = new Intent(BleServiceConstants.ACTION_DEVICE_DISCOVERED);
        intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE, device);
        intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_SCAN_RECORD, scanRecord);
        sendBroadcast(intent);
    }

    /**
     * Starts the bluetooth low energy scan It scans at least the
     * delayStopTimeInMillis.
     * 
     * @param delayStopTimeInMillis
     *            the duration of the scan
     * @return <code>true</code> if the scan is successfully started.
     */
    public boolean startScan(long delayStopTimeInMillis) {
        if (!isReady())
            return false;

        if (preferences.shouldScan().get()) {
            if (delayStopTimeInMillis <= 0) {
                Log.w(TAG, "Did not start scanning with automatic stop delay time of " + delayStopTimeInMillis);
                return false;
            }

            Log.d(TAG, "Auto-Stop scan after " + delayStopTimeInMillis + " ms");
            getMainHandler().postDelayed(new Runnable() {

                @Override
                public void run() {
                    Log.d(TAG, "Stopped scan.");
                    stopScan();
                }
            }, delayStopTimeInMillis);
        }
        return startScan();
    }

    /**
     * @return an handler with the main (ui) looper.
     */
    private Handler getMainHandler() {
        return new Handler(getMainLooper());
    }

    /**
     * Starts the bluetooth low energy scan. It scans without time limit.
     * 
     * @return <code>true</code> if the scan is successfully started.
     */
    public boolean startScan() {
        if (!isReady())
            return false;

        if (preferences.shouldScan().get()) {
            if (mBluetoothAdapter != null) {
                Log.d(TAG, "Started scan.");
                return mBluetoothAdapter.startLeScan(this);
            } else {
                Log.d(TAG, "BluetoothAdapter is null.");
                return false;
            }
        }
        return false;
    }

    /**
     * Stops the bluetooth low energy scan.
     */
    public void stopScan() {
        if (!isReady())
            return;

        if (mBluetoothAdapter != null)
            mBluetoothAdapter.stopLeScan(this);
        else {
            Log.d(TAG, "BluetoothAdapter is null.");
        }
    }

    @Override
    public void onDestroy() {
        preferences.edit().shouldScan().put(false).apply();
        super.onDestroy();
    }
}

这些常量只是用于分配意图操作和额外名称的字符串。还有另一个首选项存储存储扫描阶段应该持续多长时间...您可以根据需要轻松替换它。

然后,您必须使用与上述操作名称匹配的意图过滤器来注册广播接收器(BleServiceConstants.ACTION_DEVICE_DISCOVERED)

public class DeviceWatcher extends BroadcastReceiver {

  @Override
    public void onReceive(Context context, Intent intent) {
        BluetoothDevice device =  intent.getParcelableExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE);

  // do anything with this information

  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android BLE 被动扫描 的相关文章

随机推荐

  • 使用更少的代码进行脑筋急转弯

    这是我尝试编写的 R 问题的一个小脑筋急转弯 假设桌子上有 15 支蜡烛 在三个不同的回合中 您将根据蜡烛是否已经点燃来点燃或熄灭给定的蜡烛 因此 如果给定的蜡烛已经点燃 那么您的行动就是将其熄灭 另一方面 如果蜡烛没有点燃 那么你的行动就
  • CreateDC() 导致 glutInit() 失败?

    我编写了一段代码来创建一个窗口并在其中绘制一个形状 include
  • Jquery - 克隆表行时禁用 select2 下拉列表

    我有一个带有四个 select2 下拉列表的表 当我克隆该行以复制它时 新行的下拉列表被禁用 我无法单击它们 我必须在代码中添加什么才能激活它们 HTML 表格 table width 100 tbody tr th class tab h
  • 如何匹配不包含单词的行[重复]

    这个问题在这里已经有答案了 我想知道如何使用Python风格的正则表达式来匹配不包含特定单词的行 只需使用正则表达式 不涉及Python函数 Example PART ONE OVERVIEW 1 Chapter 1 Introductio
  • Postman中如何自动获取token

    我使用 Postman 桌面应用程序进行 Web API 测试 我有很多控制器 每个控制器都需要一个令牌 首先我得到Bearer token然后将其复制到其他请求中 该令牌有时间限制 我可以自动获取令牌 然后自动将其设置为所有其他请求吗 好
  • 生成的 JsonResult 中的属性名称大小写不一致

    我已经为此摸不着头脑有一段时间了 并决定让 SO 社区来尝试一下 我有一些响应客户端 POST 的操作 这些操作执行简单的任务并返回JsonResult从具有简单布尔 Success 属性的匿名类构建 如果成功 或者返回PartialVie
  • 错误:sonar.sources 的值无效

    尝试运行 sonartest 但失败了 我的詹金斯控制台输出 Started by user Badal Singh Building on master in workspace C Program Files x86 Jenkins w
  • 从设备获取屏幕截图时出现意外错误:EOF

    当我尝试在 Android Studio 中从我的某些项目中截取屏幕截图 通过按相机图标 时 我收到以下消息 Unexpected error while obtaining screenshot from device EOF 如果我在打
  • 如何比较java中的多个类?

    现在 我已经编写了对整数和字符串数组进行排序的比较器 从代码中可以看出 如果两个类不相同 则 String 类将采用大于值 但是 这仅允许两个类 如果我想向数组添加另一个基本类型 例如 Float 怎么办 我必须向 if else 语句添加
  • x64 上类方法崩溃的地址

    The AddressOf运算符仅适用于标准 bas 模块内的方法 我使用以下代码来检索类方法的地址 Option Explicit If VBA7 Then Private Declare PtrSafe Function DispCal
  • 如何在 Delphi 中格式化 Unix 时间戳?

    I have var timestamp Longint timestamp Round Now 25569 0 Unix start date in Delphi terms 86400 我在一些 MySql 中使用它作为主键 但我也想格
  • Material UI 组件上的 CSS

    我在尝试使用 Material UI 时不断遇到麻烦 我有一个 Material UI 表 我正在尝试将文本居中对齐 Material UI 标准 CSS 有一个名为 MuiTableCell root 60 的元素 它有一个带有 text
  • 在笑话测试中将通用数据放在哪里

    我不知道如何组织笑话测试的代码 我所有的测试都在 tests 和我所有的嘲笑 mocks 现在我有一些想要在测试之间共享的数据 它们不是现有函数的模拟 它们只是我想在不同文件中使用的一些 javascript 对象 我应该创建一个 data
  • Pentaho j_spring_security_check 混合内容。 HTTPS 到 HTTP

    我已根据需要在 Tomcat 8 5 和 OpenJDK 1 8 上安装了 Pentaho 9 x 在它前面的是带有 mod proxy http 的 Apache 2 4 我的网站使用 HTTPS 提供服务 并且我有以下代理规则 Requ
  • 了解 OCR 的 Freeman 链码

    请注意 我确实在寻找问题的答案 我是not寻找一些源代码或一些学术论文的链接 我已经使用了源代码 并且我已经阅读了论文 但仍然没有弄清楚这个问题的最后部分 我正在研究一些快速屏幕字体 OCRing 并且取得了很好的进展 我已经找到基线 分离
  • 如何在浏览器刷新时清除会话存储,但这不应在单击浏览器后退按钮时清除

    我只想在页面刷新时清除会话存储 而不是在 单击浏览器后退按钮 或单击前进 想要使用 angularjs javascript 来实现这个 我在单击后退按钮时将数据保存在会话存储中 因此我想在单击浏览器刷新按钮时清除相同的数据 但不应清除后退
  • 如何在 ASP.NET MVC 应用程序的 Razor 引擎中使用 Html.Displar 渲染 ModelMetadata 对象?

    我正在尝试利用DisplayTemplatesrazor engine 中的功能可自动渲染我的显示视图 我扫描我的Model找到正确的ModelMetadata对于我想要显示的每个属性 但我无法使用以下方式渲染属性Html DisplayF
  • 如何在 docker 中启用/禁用 buildkit?

    我从文档中得到了这个命令 但我真的不知道如何使用它或者我应该从哪里开始移动 我是 docker 的新手 而且概念对我来说仍然很难消化 DOCKER BUILDKIT 1 docker build 如何使用此命令在 docker 引擎中启用
  • 如何循环遍历特定标签内的每个标签?

    我正在创建一个函数 它有一个 for 循环 该循环遍历特定 div 中的每个标签 例如 这里是 div div div
  • Android BLE 被动扫描

    我想在我的 Android 应用程序中被动扫描 BLE 广告商 但我找不到如何做到这一点 根据蓝牙4 0核心规范 存在被动扫描模式 第6卷 核心系统包 低能耗控制器卷 D 部分 4 1 被动扫描 https www bluetooth or