Associations par programme avec un périphérique BLE sur Android 4.4+

Quelqu'un a-t-il un exemple complet de travail de paire par programme avec un périphérique BLE ( pas Bluetooth Classic) qui utilise l'entrée de clé (c'est-à-dire un code PIN à 6 chiffres) ou une comparaison numérique sur Android 4.4 ou version ultérieure? Par «programme», je veux dire que je dis à Android le code PIN – l'utilisateur n'est pas invité.

Il existe de nombreuses questions similaires à propos de ce sujet, mais elles sont soit a) sur Bluetooth Classic, b) anciennes (avant que setPin() et createBond() soient publics), ou c) sans réponse.

  • Avertissement SSL de Google Play
  • ActionBarActivity de "android-support-v7-appcompat" et ListActivity dans la même activité
  • L'émulateur VS pour Android cesse de répondre au clavier
  • Ajouter un marqueur sur l'emplacement touché en utilisant google map dans Android
  • Android ajoutant des animations simples pendant la configuration (view.Gone)
  • "ERROR getting 'android: icône' attribut: l'attribut n'est pas une valeur de chaîne" en essayant de télécharger sur Android Market
  • Ma compréhension est la suivante.

    1. Vous vous connectez à l'appareil et découvrez ses services.
    2. Vous essayez de lire une caractéristique «protégée».
    3. L'appareil renvoie une erreur d'authentification.
    4. Android démarre d'une manière ou d'une autre et vous indiquez le NIP.
    5. Vous pouvez maintenant lire la caractéristique.

    J'ai créé un périphérique utilisant mBed fonctionnant sur le nRF51-DK et lui a donné une seule caractéristique.

    J'ai configuré les paramètres de sécurité de la manière suivante:

     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 

    Et puis, dans la caractéristique que j'ai utilisée

     requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM); 

    Maintenant, lorsque j'essaie de le lire avec le panneau de commande Nordic Master , je reçois une notification de demande d'appariement comme ceci:

    Demande d'appariement

    Entrée de clé

    Et je peux mettre ce code PIN, puis MCP dit que je suis lié, et je peux lire la caractéristique.

    Cependant, dans mon application, je souhaite éviter que l'utilisateur entre le NIP, car je le sais déjà. Quelqu'un at-il un exemple récent et complet de la façon de le faire?

    Edit : En passant, c'est la question la plus pertinente que j'ai trouvée sur SO, mais la réponse ne semble pas fonctionner.

  • Barre de recherche de carte dans Google Maps v2
  • Comment se débarrasser du soulignement dans une chaîne Spannable avec un objet cliquable?
  • RecyclerView - Où puis-je gérer ses événements de clic?
  • GridLayout (pas GridView) comment étirer uniformément tous les enfants
  • Android Studio installe un APK pour chaque module
  • Comment mettre à jour le plugin com.android.application?
  • One Solution collect form web for “Associations par programme avec un périphérique BLE sur Android 4.4+”

    Je l'ai presque fonctionné. Il se combine par programme mais je ne peux pas me débarrasser de la notification "Demande d'appariement". Certaines réponses à cette question prétend pouvoir le masquer juste après, il est montré en utilisant la méthode cachée cancelPairingUserInput() mais cela ne semble pas fonctionner pour moi.

    Edit: Success!

    J'ai finalement eu recours à la lecture du code source de BluetoothPairingRequest et du code qui envoie la diffusion de la demande d'appariement et j'ai réalisé que je devrais intercepter l' ACTION_PAIRING_REQUEST . Heureusement, c'est une émission d'intention ordonnée afin que vous puissiez l'intercepter avant que le système ne le fasse.

    Voici la procédure.

    1. Enregistrez-vous pour recevoir BluetoothDevice.ACTION_PAIRING_REQUEST modifié les intentions de diffusion. Utilisez une priorité élevée!
    2. Connectez-vous à l'appareil.
    3. Découvrez les services.
    4. Si vous avez déconnecté maintenant, c'est probablement parce que l'information sur les liens est incorrecte (par exemple, le périphérique l'a purgé). Dans ce cas, supprimez les informations de la liaison en utilisant une méthode cachée (sérieusement Google) et reconnectez-vous.
    5. Essayez de lire une caractéristique qui nécessite une protection MitM de cryptage.
    6. Dans le ACTION_PAIRING_REQUEST diffusion ACTION_PAIRING_REQUEST , vérifiez que le type d'appariement est BluetoothDevice.PAIRING_VARIANT_PIN et, dans l'affirmative, appelez setPin() et abortBroadcast() . Sinon, vous pouvez simplement laisser le système le gérer, ou afficher une erreur ou autre.

    Voici le code.

     /* This implements the BLE connection logic. Things to watch out for: 1. If the bond information is wrong (eg 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 { Lw("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) { Le(e.getMessage()); } } } 
    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.