【问题标题】:Android BLE Passive scanAndroid BLE 被动扫描
【发布时间】:2020-01-20 15:17:34
【问题描述】:

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

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

问题:是否可以在 Android 上使用“PASSIVE SCAN”?如果可以,如何使用这个功能?

【问题讨论】:

    标签: android bluetooth-lowenergy network-scan


    【解决方案1】:

    activepassive 扫描之间的区别在于 active 扫描向广告商请求 SCAN_RESPONSE 包。这是通过在检测到广告后发送SCAN_REQUEST paket 来完成的。两者的信息(有效负载)都将在设备找到回调的scanRecord 参数中。

    来自core spec

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

    因此,对于任何用例,这两种扫描类型之间都没有必要区分。

    但如果您想在后台收听广告,那么您需要自己创建一个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
    
      }
    }
    

    【讨论】:

    • 您可以将AlarmManager设置为定期启动一个PendingIntent,它会调用一个服务,然后扫描一段时间并监听找到的设备。然后可以通过意图将此信息发送给感兴趣的广播接收者。然后,这些接收者将处理必须对这些信息执行的任何操作。
    • 在 Android 操作系统能够杀死您的应用程序的限制下,因此扫描服务/警报可能......将用一个例子更新我的帖子
    • @matcauthon 谢谢你的回答。这是否意味着被动扫描有什么棘手的方法?
    • @RichardLeMesurier 谢谢。你的编辑听起来比我的好。
    • @matcauthon “对于任何用例,这两种扫描类型之间没有必要区别” - 不完全正确,对于持续发送可扫描广告的设备(例如,营销设备定期发送优惠券),您不喜欢有主动扫描的超负荷。不幸的是,Android 没有得到 iOS 似乎得到的东西。
    【解决方案2】:

    在 AOSP 存储库中找到 btif_gatt_client.c, 编辑它,

    替换

    BTM_BleSetScanParams(p_cb->scan_interval, p_cb->scan_window, BTM_BLE_SCAN_MODE_ACTI);
    

    BTM_BleSetScanParams(p_cb->scan_interval, p_cb->scan_window, BTM_BLE_SCAN_MODE_PASS);
    

    然后构建 AOSP,将图像闪存到手机,然后被动扫描工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多