La callback del GATT no se registra

Intento escribir una aplicación para enviar mensajes a través de Bluetooth Low Energy, que luego pasará a UART en mi periférico. He seguido los pasos aquí y la aplicación busca y encuentra el dispositivo con éxito. Sin embargo, la conexión que utiliza el método BluetoothGatt = BluetoothDevice.connectGatt (contexto, conexión automática, callback) falla, y el logcat dice “Error al registrar la callback”.

Llamada hecha desde:

//device scan callback private BluetoothAdapter.LeScanCallback btScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { some stuff currBtGatt = device.connectGatt(parentActivity, false, btGattCallback); } }; 

Y la callback de Gatt:

 //GATT callback private BluetoothGattCallback btGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // if connected successfully if(newState == BluetoothProfile.STATE_CONNECTED) { //discover services updateStatus("Connected"); gatt.discoverServices(); } else if(newState == BluetoothProfile.STATE_DISCONNECTED) { updateStatus("Disconnected"); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if(status == BluetoothGatt.GATT_SUCCESS) { //pick out the (app side) transmit channel currBtService = gatt.getService(uartUuids[0]); currBtCharacteristic = currBtService.getCharacteristic(uartUuids[1]); } else { updateStatus("Service discovery failed"); } } }; 

Logcat dice:

 11-19 10:40:39.363: D/BluetoothAdapter(11717): stopLeScan() 11-19 10:40:39.373: D/BluetoothGatt(11717): connect() - device: DC:6D:75:0C:0F:F9, auto: false 11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() 11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() - UUID=3ba20989-5026-4715-add3-a5e31684009a 11-19 10:40:39.373: I/BluetoothGatt(11717): Client registered, waiting for callback 11-19 10:40:49.373: E/BluetoothGatt(11717): Failed to register callback 11-19 10:40:49.533: D/BluetoothGatt(11717): onClientRegistered() - status=0 clientIf=5 11-19 10:40:49.533: E/BluetoothGatt(11717): Bad connection state: 0 11-19 10:40:49.593: D/BluetoothGatt(11717): onClientConnectionState() - status=0 clientIf=5 device=DC:6D:75:0C:0F:F9 11-19 10:40:49.593: W/BluetoothGatt(11717): Unhandled exception: java.lang.NullPointerException 

Curiosamente, mi periférico se mueve a un estado “conectado” (tengo indicadores LED) y puedo conectarme a él desde el mismo teléfono con una aplicación de demostración o con un dongle PC BLE. Cualquier idea apreciada.

[EDIT] el método connectGatt devuelve null, que supongo que se espera.

[EDITAR] Al inspeccionar el código fuente API 18, parece que el mensaje “No se pudo registrar la callback” se entrega porque el método registerApp () devuelve falso porque el método registerClient () del IBluetoothGatt “mService” arroja una excepción remota, probablemente en la línea:

 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 

porque el mensaje de registro en la siguiente línea nunca se ve. Entonces podría ser una cuestión de permisos, excepto que la aplicación tiene permisos bluetooth y blue_admin.

Finalmente me di cuenta de este problema. El dispositivo que estoy usando es un Samsung Galaxy S4 y el problema real (gracias a Wibble como guía en tu respuesta, pero estás ligeramente apagado en tu conclusión) parece ser un problema de enhebrado.

En la respuesta de Wibble, afirmó que agregar un botón para conectar solucionó su problema. Comencé a preguntarme por qué eso es importante, y también puedo conectarme y desconectarme bien durante una sesión completa sin un botón de GUI usando hilos de trabajo en segundo plano. Tan pronto como fuerzo a cerrar mi aplicación, la reinicio y trato de conectarme, empiezo a recibir el error “No se pudo registrar la callback”. y nada funciona más. Casi me saqué el pelo por esto 🙂

Ver mi publicación en los foros de Samsung para más detalles sobre mis problemas exactos.

Solución: para evitar este problema, solo asegúrese de ejecutar cualquier código de interacción BLE (device # connectGatt, connect, disconnect, etc) en UIThread (con un controlador, servicio local o Activity # runOnUiThread). Siga esta regla práctica y con suerte evitará este terrible problema.

En lo profundo de nuestra biblioteca, solo tuve acceso al contexto de la aplicación. Puede crear un controlador a partir de un contexto que se publicará en el hilo principal utilizando el new Handler(ctx.getMainLooper());

Si enfrenta otros problemas de conexión, despliegue la aplicación de muestra en samples\android-18\legacy\BluetoothLeGatt y vea si esa aplicación funciona. Esa fue una especie de punto de partida para darme cuenta de que BLE realmente funciona con mi periférico, y me dio esperanza de que si cavaba lo suficiente en nuestra biblioteca eventualmente encontraría la respuesta.

EDITAR: No vi este problema de ‘No se pudo registrar la callback’ en el Nexus 4, Nexus 5 o Nexus 7 2013 cuando se utilizan subprocesos en segundo plano para realizar operaciones BLE. Puede ser solo un problema en la implementación de Samsung 4.3.

Entonces, mi problema fue ejecutarlo desde un servicio recursivo. connectGatt funcionó bien con lollipop, pero las versiones anteriores arrojaron null. corriendo en un hilo principal resolvió el problema. Esta es mi solución:

 public void connectToDevice( String deviceAddress) { mDeviceAddress = deviceAddress; final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { if (device != null) { mGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); scanLeDevice(false);// will stop after first device detection } } }); } 

También puedo confirmar que Lo-Tan es la respuesta para verificar primero. He probado muchos dispositivos, algunos de ellos se comportan bien cuando se ejecuta desde un hilo secundario. Algunos pueden bloquear después de un tiempo, el comportamiento es imprevisto.

Aquí está la lista de cosas que hacer:

  1. Maker asegúrese de utilizar el nuevo controlador (Looper.getMainLooper ()). Post (nuevo ejecutable) en cualquier operación de gatt (conectar, desconectar, cerrar), sino también en las operaciones del escáner (startScan, stopScan, etc.).

  2. Hay una condición de carrera para la conexión directa en Android 6 (o tal vez 5) así que intente conectar Gatt de esta manera:

      new Handler(getContext().get().getMainLooper()).post(() -> { if (CommonHelper.isNOrAbove()) { connectedGatt = connectedBLEDevice.connectGatt(context.get(), true, gattCallback, BluetoothDevice.TRANSPORT_AUTO); Timber.tag("HED-BT").d("Connecting BLE after N"); } else { try { Method connectGattMethod = connectedBLEDevice.getClass().getMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class); connectedGatt = (BluetoothGatt) connectGattMethod.invoke(connectedBLEDevice, context.get(), false, gattCallback, BluetoothDevice.TRANSPORT_AUTO); Timber.tag("HED-BT").d("Connecting BLE before N"); } catch (Exception e) { failedConnectingBLE(); } } }); 
  3. Al desconectar el gatt, llame a disconnect () primero y close () después en la rutina GattCallback.

Para poder conectarme automáticamente a un dispositivo Bluetooth, es decir, sin la intervención explícita del usuario como estaba intentando hacer, se requiere el permiso BLUETOOTH_PRIVILEDGE. Sin embargo, esto no está disponible para aplicaciones de terceros, por lo que mi código falló. Agregar una opción de menú para conectarse y usar el mismo código funciona bien.

http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED