Android BLE被动扫描

时间:2014-07-28 11:59:26

标签: android bluetooth-lowenergy network-scan

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

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

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

1 个答案:

答案 0 :(得分:14)

activepassive次扫描之间的区别在于active扫描请求广告客户SCAN_RESPONSE paket。这是通过在检测到广告后发送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);

BleScanService

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

  }
}