tutorial things studio low example ble android bluetooth bluetooth-lowenergy pairing

things - Vinculación programática con un dispositivo BLE en Android 4.4+



bluetooth low energy android example (2)

¿Alguien tiene un ejemplo completo de cómo combinar de manera programática un dispositivo BLE ( no Bluetooth Classic) que utiliza una clave de acceso (es decir, un PIN de 6 dígitos) o una comparación numérica en Android 4.4 o posterior? Por "programáticamente" quiero decir que le digo a Android el PIN, no se le pregunta al usuario.

Hay muchas preguntas similares sobre esto en SO, pero son a) acerca de Bluetooth Classic, b) antiguas (antes de que setPin() y createBond() fueran públicas), o c) sin respuesta.

Mi comprensión es la siguiente.

  1. Te conectas al dispositivo y descubres sus servicios.
  2. Intenta leer una característica ''protegida''.
  3. El dispositivo devuelve un error de autenticación.
  4. Android de alguna manera inicia el emparejamiento y le dices el PIN.
  5. Ahora puedes leer la característica.

He creado un dispositivo con mBed ejecuta en el nRF51-DK y le nRF51-DK dado una única característica.

Configuré los parámetros de seguridad así:

ble.securityManager().init( true, // Enable bonding (though I don''t really need this) true, // Require MitM protection. I assume you don''t get a PIN prompt without this, though I''m not 100% sure. SecurityManager::IO_CAPS_DISPLAY_ONLY, // This makes it us the Passkey Entry (PIN) pairing method. "123456"); // Static PIN

Y luego en la característica que utilicé.

requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);

Ahora, cuando intento leerlo con el Nordic Master Control Panel , recibo una notificación de solicitud de emparejamiento como esta:

Y puedo poner este PIN, y luego MCP dice que estoy vinculado y que puedo leer la característica.

Sin embargo, en mi aplicación me gustaría evitar que el usuario ingrese el PIN, ya que ya lo sé. ¿Alguien tiene un ejemplo reciente completo de cómo hacer esto?

Edición : Por cierto, this es la pregunta más relevante que encontré en SO, pero la respuesta no parece funcionar.


También enfrenté el mismo problema y, después de toda la investigación, descubrí la siguiente solución para vincularme a un BLE sin ninguna intervención manual.

(Probado y funcionando !!!)

Básicamente estoy buscando un dispositivo Bluetooth en particular (conozco la dirección MAC) y lo aparto una vez encontrado. Lo primero que debe hacer es crear una solicitud de par utilizando un receptor de difusión y manejar la solicitud como se muestra a continuación.

IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST); intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); registerReceiver(broadCastReceiver,intentFilter);

Debe escribir el broadcastReceiver y manejarlo como se muestra a continuación.

String BLE_PIN = "1234" private BroadcastReceiver broadCastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) { BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); bluetoothDevice.setPin(BLE_PIN.getBytes()); Log.e(TAG,"Auto-entering pin: " + BLE_PIN); bluetoothDevice.createBond(); Log.e(TAG,"pin entered and request sent..."); } } };

Voila! Debe poder emparejarse con un dispositivo Bluetooth sin CUALQUIER INTERVENCIÓN MANUAL.

Espero que esto ayude :-) Por favor, responda correctamente si funciona para usted.


Casi lo tengo funcionando. Se empareja programáticamente pero no puedo deshacerme de la notificación de "Solicitud de emparejamiento". Algunas respuestas a esta pregunta dicen poder ocultarla justo después de que se muestre mediante el método oculto cancelPairingUserInput() pero eso no parece funcionar para mí.

Editar: ¡Éxito!

Finalmente, recurrí a leer el código fuente de BluetoothPairingRequest y el código que envía la transmisión de solicitud de emparejamiento y me di cuenta de que debería estar interceptando el ACTION_PAIRING_REQUEST . Afortunadamente, se trata de una transmisión con intención ordenada, por lo que puede interceptarla antes de que lo haga el sistema.

Aquí está el procedimiento.

  1. Regístrese para recibir intenciones de emisión cambiadas de DispositivoDevice.ACTION_PAIRING_REQUEST. ¡Usa una alta prioridad!
  2. Conectar al dispositivo.
  3. Descubrir servicios.
  4. Si ya se ha desconectado, probablemente se deba a que la información del enlace es incorrecta (por ejemplo, el periférico lo ha purgado). En ese caso, elimine la información de enlace usando un método oculto (en serio Google) y vuelva a conectarse.
  5. Intente leer una característica que requiere cifrado de protección MitM.
  6. En el receptor de difusión ACTION_PAIRING_REQUEST , verifique que el tipo de emparejamiento sea BluetoothDevice.PAIRING_VARIANT_PIN y, si es así, llame a setPin() y abortBroadcast() . De lo contrario, puede dejar que el sistema lo maneje, o mostrar un error o lo que sea.

Aquí está el código.

/* This implements the BLE connection logic. Things to watch out for: 1. If the bond information is wrong (e.g. it has been deleted on the peripheral) then discoverServices() will cause a disconnect. You need to delete the bonding information and reconnect. 2. If the user ignores the PIN request, you get the undocumented GATT_AUTH_FAILED code. */ public class ConnectActivityLogic extends Fragment { // The connection to the device, if we are connected. private BluetoothGatt mGatt; // This is used to allow GUI fragments to subscribe to state change notifications. public static class StateObservable extends Observable { private void notifyChanged() { setChanged(); notifyObservers(); } }; // When the logic state changes, State.notifyObservers(this) is called. public final StateObservable State = new StateObservable(); public ConnectActivityLogic() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Tell the framework to try to keep this fragment around // during a configuration change. setRetainInstance(true); // Actually set it in response to ACTION_PAIRING_REQUEST. final IntentFilter pairingRequestFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST); pairingRequestFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1); getActivity().getApplicationContext().registerReceiver(mPairingRequestRecevier, pairingRequestFilter); // Update the UI. State.notifyChanged(); // Note that we don''t actually need to request permission - all apps get BLUETOOTH and BLUETOOTH_ADMIN permissions. // LOCATION_COARSE is only used for scanning which I don''t need (MAC is hard-coded). // Connect to the device. connectGatt(); } @Override public void onDestroy() { super.onDestroy(); // Disconnect from the device if we''re still connected. disconnectGatt(); // Unregister the broadcast receiver. getActivity().getApplicationContext().unregisterReceiver(mPairingRequestRecevier); } // The state used by the UI to show connection progress. public ConnectionState getConnectionState() { return mState; } // Internal state machine. public enum ConnectionState { IDLE, CONNECT_GATT, DISCOVER_SERVICES, READ_CHARACTERISTIC, FAILED, SUCCEEDED, } private ConnectionState mState = ConnectionState.IDLE; // When this fragment is created it is given the MAC address and PIN to connect to. public byte[] macAddress() { return getArguments().getByteArray("mac"); } public int pinCode() { return getArguments().getInt("pin", -1); } // Start the connection process. private void connectGatt() { // Disconnect if we are already connected. disconnectGatt(); // Update state. mState = ConnectionState.CONNECT_GATT; State.notifyChanged(); BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress()); // Connect! mGatt = device.connectGatt(getActivity(), false, mBleCallback); } private void disconnectGatt() { if (mGatt != null) { mGatt.disconnect(); mGatt.close(); mGatt = null; } } // See https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h private static final int GATT_ERROR = 0x85; private static final int GATT_AUTH_FAIL = 0x89; private android.bluetooth.BluetoothGattCallback mBleCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); switch (newState) { case BluetoothProfile.STATE_CONNECTED: // Connected to the device. Try to discover services. if (gatt.discoverServices()) { // Update state. mState = ConnectionState.DISCOVER_SERVICES; State.notifyChanged(); } else { // Couldn''t discover services for some reason. Fail. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); } break; case BluetoothProfile.STATE_DISCONNECTED: // If we try to discover services while bonded it seems to disconnect. // We need to debond and rebond... switch (mState) { case IDLE: // Do nothing in this case. break; case CONNECT_GATT: // This can happen if the bond information is incorrect. Delete it and reconnect. deleteBondInformation(gatt.getDevice()); connectGatt(); break; case DISCOVER_SERVICES: // This can also happen if the bond information is incorrect. Delete it and reconnect. deleteBondInformation(gatt.getDevice()); connectGatt(); break; case READ_CHARACTERISTIC: // Disconnected while reading the characteristic. Probably just a link failure. gatt.close(); mState = ConnectionState.FAILED; State.notifyChanged(); break; case FAILED: case SUCCEEDED: // Normal disconnection. break; } break; } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); // Services have been discovered. Now I try to read a characteristic that requires MitM protection. // This triggers pairing and bonding. BluetoothGattService nameService = gatt.getService(UUIDs.NAME_SERVICE); if (nameService == null) { // Service not found. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); return; } BluetoothGattCharacteristic characteristic = nameService.getCharacteristic(UUIDs.NAME_CHARACTERISTIC); if (characteristic == null) { // Characteristic not found. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); return; } // Read the characteristic. gatt.readCharacteristic(characteristic); mState = ConnectionState.READ_CHARACTERISTIC; State.notifyChanged(); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // Characteristic read. Check it is the right one. if (!UUIDs.NAME_CHARACTERISTIC.equals(characteristic.getUuid())) { // Read the wrong characteristic. This shouldn''t happen. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); return; } // Get the name (the characteristic I am reading just contains the device name). byte[] value = characteristic.getValue(); if (value == null) { // Hmm... } disconnectGatt(); mState = ConnectionState.SUCCEEDED; State.notifyChanged(); // Success! Save it to the database or whatever... } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { // This is where the tricky part comes if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { // Bonding required. // The broadcast receiver should be called. } else { // ? } } else if (status == GATT_AUTH_FAIL) { // This can happen because the user ignored the pairing request notification for too long. // Or presumably if they put the wrong PIN in. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); } else if (status == GATT_ERROR) { // I thought this happened if the bond information was wrong, but now I''m not sure. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); } else { // That''s weird. disconnectGatt(); mState = ConnectionState.FAILED; State.notifyChanged(); } } }; private final BroadcastReceiver mPairingRequestRecevier = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) { final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR); if (type == BluetoothDevice.PAIRING_VARIANT_PIN) { device.setPin(Util.IntToPasskey(pinCode())); abortBroadcast(); } else { L.w("Unexpected pairing type: " + type); } } } }; public static void deleteBondInformation(BluetoothDevice device) { try { // FFS Google, just unhide the method. Method m = device.getClass().getMethod("removeBond", (Class[]) null); m.invoke(device, (Object[]) null); } catch (Exception e) { L.e(e.getMessage()); } } }