web-dev-qa-db-fra.com

Android BLE- Comment la méthode onScanResult est-elle appelée dans ScanCallback?

C'est la première fois que je fais du Bluetooth Low Energy dans le projet Android. Le projet que je fais est essentiellement de détecter tous les appareils Bluetooth LE et de les connecter pour découvrir leurs services.

Je voudrais demander si quelqu'un sait comment les méthodes onScanResult (), onBatchScanResults () et onScanFailed () sont appelées dans ScanCallback?

Dans un premier temps, exécutez la méthode scanLeDevice ().

BluetoothLeScanner mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
ScanSettings settings = new ScanSettings.Builder()
           .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
           .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
           .build();
List<ScanFilter> filters = new ArrayList<ScanFilter>();

scanLeDevice(true);

Dans cette méthode, il lancera Scan. Je suppose donc que les résultats de l'analyse sont fournis à l'aide de ces rappels.

@TargetApi(21)
private void scanLeDevice(final boolean enable) {
    if (enable) {
        //stops scanning after a pre-defined scan period
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                    System.out.println("BLE// mLEScanner.stopScan(mScanCallback) ");
                    mLEScanner.stopScan(mScanCallback);
                }
            }
        }, SCAN_PERIOD);
            System.out.println("BLE// mLEScanner.startScan(filters, settings, mScanCallback)");
            mLEScanner.startScan(filters, settings, mScanCallback);

    } else {
            System.out.println("BLE// mLEScanner.stopScan(mScanCallback)");
            mLEScanner.stopScan(mScanCallback);
    }
}

Cependant, dans ScanCallback, je n'ai aucune idée de la façon dont il déclenche onScanResult et fournit le résultat de l'analyse en utilisant le rappel. Dans mes tests (comme indiqué ci-dessous), ni onScanResult () ni onBatchScanResults () et onScanFailed () ne seront appelés. Quelqu'un peut-il m'expliquer le concept? Cela m'aidera beaucoup!

 /* Scan result for SDK >= 21 */
 private ScanCallback mScanCallback = new ScanCallback() {

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        System.out.println("BLE// onScanResult");
        Log.i("callbackType", String.valueOf(callbackType));
        Log.i("result", result.toString());
        BluetoothDevice btDevice = result.getDevice();
        connectToDevice(btDevice);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        System.out.println("BLE// onBatchScanResults");
        for (ScanResult sr : results) {
            Log.i("ScanResult - Results", sr.toString());
        }
    }

    @Override
    public void onScanFailed(int errorCode) {
        System.out.println("BLE// onScanFailed");
        Log.e("Scan Failed", "Error Code: " + errorCode);
    }

};

02-17 10:38:38.513 878-895/? D/BluetoothManagerService: Added callback: Android.bluetooth.IBluetoothManagerCallback$Stub$Proxy@8334cf4:true
02-17 10:38:38.520 782-782/? D/BluetoothAdapter: STATE_ON
02-17 10:38:38.529 21554-21590/? D/BtGatt.GattService: registerClient() - UUID=835342c6-81eb-4e09-9729-5bbe1c22bc86
02-17 10:38:38.529 21554-21570/? D/BtGatt.GattService: onClientRegistered() - UUID=835342c6-81eb-4e09-9729-5bbe1c22bc86, clientIf=5
02-17 10:38:38.530 782-793/? D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=5
02-17 10:38:38.530 21554-21599/? D/BtGatt.GattService: start scan with filters

02-17 10:38:38.532 782-782/? I/System.out: BLE// mLEScanner.startScan(filters, settings, mScanCallback)
02-17 10:38:38.532 21554-21573/? D/BtGatt.ScanManager: handling starting scan
02-17 10:38:38.534 21576-21577/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK ON using UART driver's ioctl()
02-17 10:38:38.542 21554-21570/? D/BtGatt.GattService: onScanFilterEnableDisabled() - clientIf=5, status=0, action=1
02-17 10:38:38.543 21554-21570/? D/BtGatt.ScanManager: callback done for clientIf - 5 status - 0
02-17 10:38:38.543 21554-21573/? D/BtGatt.ScanManager: configureFilterParamter 500 10000 1 0
02-17 10:38:38.547 21554-21570/? D/BtGatt.GattService: onScanFilterParamsConfigured() - clientIf=5, status=0, action=0, availableSpace=15
02-17 10:38:38.547 21554-21570/? D/BtGatt.ScanManager: callback done for clientIf - 5 status - 0
02-17 10:38:38.548 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams() - queue=1
02-17 10:38:38.548 487-2827/? I/ACDB-LOADER: ACDB AFE returned = -19
02-17 10:38:38.549 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams() - ScanSetting Scan mode=0 mLastConfiguredScanSetting=-2147483648
02-17 10:38:38.549 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams - scanInterval = 8000configureRegularScanParams - scanWindow = 800
02-17 10:38:38.549 21554-21570/? D/BtGatt.GattService: onScanParamSetupCompleted : 0
02-17 10:38:38.568 21554-21574/? W/bt_hci: filter_incoming_event command complete event with no matching command. opcode: 0x0.
02-17 10:38:38.603 21554-21570/? D/bt_btif_gattc: btif_gattc_update_properties BLE device name=Polar HR Sensor len=15 dev_type=2
02-17 10:38:39.571 21576-21585/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK OFF using UART driver's ioctl()

02-17 10:38:43.526 782-782/? I/System.out: BLE// mLEScanner.stopScan(mScanCallback) 
02-17 10:38:43.599 21576-21576/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK ON using UART driver's ioctl()
02-17 10:38:43.967 21576-21576/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK OFF using UART driver's ioctl()

Utilisation de Android Phone with API 23

Le code que j'ai écrit ici est mentionné: http://www.truiton.com/2015/04/Android-bluetooth-low-energy-ble-example/



[Code V1 mis à jour] - Ne fonctionne pas

Voici tout mon code que j'ai créé un périphérique virtuel et il est en mode publicité. Le périphérique virtuel est créé via une application appelée LightBlue: https://iTunes.Apple.com/us/app/lightblue-Explorer-bluetooth/id557428110?mt=8 Veuillez m'aider à vérifier mon code :)

@TargetApi(21)
public class BluetoothLE extends Fragment {

View view;

private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 5000;  // Stops scanning after 5 seconds
private BluetoothLeScanner mLEScanner;
private BluetoothGatt mGatt; //To provide bluetooth communication
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
private int permissionCheck;

public BluetoothLE(){
    //empty constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment

    view = inflater.inflate(R.layout.fragment_bluetooth, container, false);
    mHandler = new Handler();

    /* check if BLE is supported in this phone */
    if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(getActivity(), "BLE Not Supported", Toast.LENGTH_SHORT).show();
        getActivity().finish();
    }

    /* Enable bluetooth without leaving app */
    final BluetoothManager bluetoothManager =
            (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();

    /* Build ScanSetting */
    ScanSettings.Builder scanSetting = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
            .setReportDelay(5000);

    settings = scanSetting.build();


    return view;
}

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onResume() {
    super.onResume();

    /* Ensures Bluetooth is available on the device and it is enabled. If not, displays a dialog requesting user permission to enable Bluetooth. */
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {  //Unable to obtain a BluetoothAdapter
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); //trigger onActivityResult

    } else {
        if (Build.VERSION.SDK_INT >= 21) {
            mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
            settings = new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                    .build();
            filters = new ArrayList<ScanFilter>();
        }

        if(Build.VERSION.SDK_INT >= 23){
            checkLocationPermission();
        }

        scanLeDevice(true);
    }
}

@Override
public void onPause() {
    super.onPause();
    if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
        scanLeDevice(false);
    }
}

@Override
public void onDestroy() {
    if (mGatt == null) {
        return;
    }
    mGatt.close();
    mGatt = null;
    super.onDestroy();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    System.out.println("BLE// onActivityResult");
    if (requestCode == REQUEST_ENABLE_BT) {
        if (resultCode == Activity.RESULT_CANCELED) {
            //Bluetooth not enabled.
            getActivity().finish();
            return;
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

private void scanLeDevice(final boolean enable) {
    if (enable) {
        //stops scanning after a pre-defined scan period
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (Build.VERSION.SDK_INT < 21) {
                    System.out.println("BLE// mBluetoothAdapter.stopLeScan(mLeScanCallback) ");
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } else {
                    mLEScanner.stopScan(mScanCallback);
                    System.out.println("BLE// mLEScanner.stopScan(mScanCallback) ");
                }
            }
        }, SCAN_PERIOD);

        if (Build.VERSION.SDK_INT < 21) {
            System.out.println("BLE// mBluetoothAdapter.startLeScan(mLeScanCallback)");
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {

            mLEScanner.startScan(mScanCallback);
            //mLEScanner.startScan(filters, settings, mScanCallback);
            System.out.println("BLE// mLEScanner.startScan(mScanCallback) ");
        }
    } else {
        if (Build.VERSION.SDK_INT < 21) {
            System.out.println("BLE// mBluetoothAdapter.stopLeScan(mLeScanCallback)");
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        } else {
            System.out.println("BLE// mLEScanner.stopScan(mScanCallback)");
            mLEScanner.stopScan(mScanCallback);
        }
    }
}

/* Scan result for SDK >= 21 */
private ScanCallback mScanCallback = new ScanCallback() {

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        System.out.println("BLE// onScanResult");
        super.onScanResult(callbackType, result);

        Log.i("callbackType", String.valueOf(callbackType));
        Log.i("result", result.toString());
        Log.i("Device Name: ", result.getDevice().getName());
        System.out.println("Signal: " + result.getRssi());

        BluetoothDevice btDevice = result.getDevice();
        connectToDevice(btDevice);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        System.out.println("BLE// onBatchScanResults");
        for (ScanResult sr : results) {
            Log.i("ScanResult - Results", sr.toString());
        }
    }

    @Override
    public void onScanFailed(int errorCode) {
        System.out.println("BLE// onScanFailed");
        Log.e("Scan Failed", "Error Code: " + errorCode);
    }

};

// scan results are returned here SDK < 21
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
                         byte[] scanRecord) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                System.out.println("BLE// DEVICDE FOUND");

                Log.i("onLeScan", device.toString());

                connectToDevice(device);
            }
        });
    }
};

public void connectToDevice(BluetoothDevice device) {
    System.out.println("BLE// connectToDevice()");
    if (mGatt == null) {
        mGatt = device.connectGatt(getActivity(), false, gattCallback); //Connect to a GATT Server
        //scanLeDevice(false);// will stop after first device detection
    }
}

private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        System.out.println("BLE// BluetoothGattCallback");
        Log.i("onConnectionStateChange", "Status: " + status);
        switch (newState) {
            case BluetoothProfile.STATE_CONNECTED:
                Log.i("gattCallback", "STATE_CONNECTED");
                gatt.discoverServices();
                break;
            case BluetoothProfile.STATE_CONNECTING:
                Log.i("gattCallback", "STATE_CONNECTING");
                break;
            case BluetoothProfile.STATE_DISCONNECTED:
                Log.e("gattCallback", "STATE_DISCONNECTED");
                break;
            default:
                Log.e("gattCallback", "STATE_OTHER");
        }
    }

    @Override
    //New services discovered
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        List<BluetoothGattService> services = gatt.getServices();
        Log.i("onServicesDiscovered", services.toString());
        gatt.readCharacteristic(services.get(1).getCharacteristics().get
                (0));
    }

    @Override
    //Result of a characteristic read operation
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic
                                             characteristic, int status) {
        Log.i("onCharacteristicRead", characteristic.toString());
        gatt.disconnect();
    }
};

public void checkLocationPermission(){
    permissionCheck = ContextCompat.checkSelfPermission(getActivity(), Android.Manifest.permission.ACCESS_COARSE_LOCATION);

    switch(permissionCheck){
        case PackageManager.PERMISSION_GRANTED:
            break;

        case PackageManager.PERMISSION_DENIED:

            if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Android.Manifest.permission.ACCESS_COARSE_LOCATION)){
                //Show an explanation to user *asynchronouselly* -- don't block
                //this thread waiting for the user's response! After user sees the explanation, try again to request the permission

                Snackbar.make(view, "Location access is required to show Bluetooth devices nearby.",
                        Snackbar.LENGTH_LONG).setAction("Action", null).show();

            }
            else{
                //No explanation needed, we can request the permission
                ActivityCompat.requestPermissions(getActivity(), new String[]{Android.Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            }
            break;
    }
}
}
15
janice chau

Je vois que vous utilisez la méthode public void startScan (List<ScanFilter> filters, ScanSettings settings, ScanCallback callback) de startScan(), mais vous ne définissez jamais de filtres. Au lieu de cela, vous passez une ArrayList vide de ScanFilters. Ainsi, vous ne recevez jamais de rappels, car aucun critère de filtrage ne vous est fourni.

Puisque vous avez dit que vous souhaitez rechercher tous les périphériques BLE, il n'est pas nécessaire d'utiliser de filtres. À la place, utilisez la méthode public void startScan (ScanCallback callback) plus simple, qui n'utilise aucun filtre ni paramètre spécialisé.

En ce qui concerne votre demande de comprendre comment tout cela fonctionne - je pense que vous avez le concept en fonction de votre code et de votre attente que les rappels soient déclenchés. Vous démarrez l'analyse et le système s'éteint et effectue l'analyse sans bloquer l'exécution de votre code (c'est-à-dire qu'il le fait de manière asynchrone). Pendant que l'analyse se produit, elle appellera l'une des trois méthodes de votre objet de rappel chaque fois que cela sera approprié (comme décrit dans la documentation de l'API). C'est à peu près ça.

UPDATE : Assurez-vous de demander les autorisations BLUETOOTH, BLUETOOTH_ADMIN, ainsi que les autorisations ACCESS_COARSE_LOCATION o ACCESS_FINE_LOCATION. Ceux-ci sont requis pour recevoir des rappels de la méthode startScan(). Malheureusement, si vous ne demandez pas ces persmissions, l'analyse échoue silencieusement. Je préférerais que le système fournisse un message d'avertissement dans les journaux ou déclenche un rappel à la méthode onScanFailed() avec le code d'erreur indiquant le problème.

11
rothloup

Alors .. j'ai finalement trouvé la réponse. Pour Android Appareils qui sont Android 6.0 ou supérieur (comme mon téléphone est Nexus 5x), les deux GPS et Bluetooth dans les paramètres de votre téléphone doit être activé, plus dans votre manifeste, vous devez ajouter BLUETOOTHBLUETOOTH_ADMINACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION autorisation.

Maintenant, tout fonctionne bien pour moi :)

13
janice chau