La devolución de llamada del GATT no se registra


Estoy tratando de escribir una aplicación para enviar mensajes a través de Bluetooth de baja energía, que luego serán transmitidos por 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 mediante el BluetoothGatt = BluetoothDevice.El método connectGatt (context, autoconnect, callback) falla, con logcat diciendo "Failed to register callback".

Llamada realizada 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 el Gatt devolución de llamada:

//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 LEDES de indicación) 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 de la API 18, parece que el mensaje" No se pudo registrar la devolución de llamada " se entrega porque el método registerApp () devuelve false porque el método registerClient () del IBluetoothGatt "mService" lanza una excepción remota, probablemente en la línea:

enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

Porque el mensaje de registro en la línea siguiente nunca se ve. Así que podría ser una cosa permisos, excepto que la aplicación tiene bluetooth y bluetooth_admin permisos.

Author: emlai, 2013-11-19

4 answers

Finalmente me di cuenta de este problema. El dispositivo que estoy utilizando es un Samsung Galaxy S4 y el problema real (gracias Wibble para la orientación en su respuesta, pero usted está un poco apagado en su conclusión) parece ser un problema de rosca.

En la respuesta de Wibble, afirmó que agregar un botón para conectar solucionó su problema. Empecé a preguntarme por qué eso importa, y también puedo conectar y desconectar bien durante toda una sesión sin un botón GUI usando subprocesos de trabajo en segundo plano. Tan pronto como forzar el cierre de mi aplicación, reiniciarla, y tratar de conectarse, empiezo a recibir el error " No se pudo registrar la devolución de llamada."y ya nada funciona. Casi me tiré del pelo sobre este:)

Ver mi post 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 (dispositivo#connectGatt, conectar, desconectar, etc.) código en el UIThread (con un controlador, servicio local o Actividad#runOnUiThread). Siga esta regla general y con suerte evitará este terrible problema.

En el fondo de nuestra biblioteca, solo tenía acceso al contexto de la aplicación. Puedes crear un controlador a partir de un contexto que se publicará en el hilo principal usando new Handler(ctx.getMainLooper());

Si tiene otros problemas de conexión, implemente la aplicación de ejemplo en samples\android-18\legacy\BluetoothLeGatt y vea si esa aplicación funciona. Esa fue mi línea de base para darme cuenta de que BLE realmente funciona con mi periférico, y me dio la esperanza de que si cavara lo suficiente en nuestra biblioteca, eventualmente encontraría la respuesta.

EDIT: No vi este problema de 'No se pudo registrar la devolución de llamada' en el Nexus 4, Nexus 5 o Nexus 7 2013 al usar subprocesos en segundo plano para realizar operaciones BLE. Puede ser solo un problema en la implementación de Samsungs 4.3.

 99
Author: Lo-Tan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-10 23:20:23

Entonces, mi problema era ejecutarlo desde un servicio recursivo. connectGatt funcionó bien con lollipop, pero las versiones anteriores devolvieron null. ejecutar 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
            }
        }
    });
}
 3
Author: TacoEater,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-19 16:24:55

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

Aquí está la lista de cosas que hacer:

  1. Fabricante seguro de que utiliza nuevo Controlador (Looper.getMainLooper ()).post (new Runnable) en cualquier operación gatt (conectar, desconectar, cerrar) pero también en las operaciones del escáner (startScan, stopScan sucesivamente.).

  2. Hay condición de carrera para la conexión directa en Android 6 (o tal vez 5) así que trate de 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. Cuando desconecte el gatt, llame a disconnect() primero y close () después en la rutina GattCallback.

 1
Author: HED Tech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 15:50:40

Para conectarse automáticamente a un dispositivo Bluetooth, es decir, sin la entrada 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

 -2
Author: Wibble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-19 16:54:38