Android BLE can't receive Gatt Characteristic

2019-04-01 07:32发布

问题:

I'm trying to receive a notification from the device when I write a value on a characteristic, but I don´t receive anything. I enable the notification on the characteristic and then I write the value. I've seen that the characteristic in the device have changed its value but I can't get the notification. Here is my code:

DeviceActivity:

public class DevicesActivity extends Activity {

private BLEService mBluetoothLeService;
private String mDeviceAddress;
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;

private final ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        mBluetoothLeService = ((BLEService.LocalBinder) service).getService();
        if (!mBluetoothLeService.initialize()) {
            finish();
        }
        mBluetoothLeService.context = DevicesActivity.this;
        mBluetoothLeService.connect(mDeviceAddress);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BLEService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            invalidateOptionsMenu();
        } else if (BLEService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            invalidateOptionsMenu();
        } else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            List<BluetoothGattService> servicesList;
            servicesList = mBluetoothLeService.getSupportedGattServices();
            Iterator<BluetoothGattService> iter = servicesList.iterator();
            while (iter.hasNext()) {
                BluetoothGattService bService = (BluetoothGattService) iter.next();
                if (bService.getUuid().toString().equals(BLEUUID.SERVICE)){
                    mService = bService;
                }
            }
        } else if (BLEService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
        }
    }
};

private static final String TAG = "BLEDevice";
public static final String EXTRA_BLUETOOTH_DEVICE = "BT_DEVICE";
private BluetoothAdapter mBTAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private int mStatus;
BluetoothGattService mService;

private EditText pinTxt;
private Button cancelBtn;
private Button unlockBtn;
private Button changePinBtn;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_devices);

    pinTxt = (EditText) findViewById(R.id.pin_txt);
    cancelBtn = (Button) findViewById(R.id.cancel_btn);
    unlockBtn = (Button) findViewById(R.id.unlock_btn);
    changePinBtn = (Button) findViewById(R.id.change_pin_btn);

    unlockBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String aux = pinTxt.getText().toString();
            mBluetoothLeService.sendCharacteristic(aux, getBTDeviceExtra());
        }
    });

    mDevice = getBTDeviceExtra();
    mDeviceAddress = mDevice.getAddress();
    if (mServiceConnection == null){
        Log.v("NULL", "mServiceConnection NULL");
    }

    Intent gattServiceIntent = new Intent(this, BLEService.class);
    if (gattServiceIntent==null){
        Log.v("NULL", "mServiceConnection NULL");
    }
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    mStatus = BluetoothProfile.STATE_DISCONNECTED;
}

private void displayData(String data) {
    if (data != null) {
        Toast.makeText(DevicesActivity.this, data, Toast.LENGTH_LONG);
    }
}
@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(mServiceConnection);
    mBluetoothLeService = null;
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mGattUpdateReceiver);
}

private static IntentFilter makeGattUpdateIntentFilter() {
    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
    intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
    intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
    return intentFilter;
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null) {
        final boolean result = mBluetoothLeService.connect(mDeviceAddress);
    }

}

private BluetoothDevice getBTDeviceExtra() {
    Intent intent = getIntent();
    if (intent
        == null) {            return null;
    }

    Bundle extras = intent.getExtras();
    if (extras == null) {
        return null;
    }

    BluetoothDevice aux = extras.getParcelable(EXTRA_BLUETOOTH_DEVICE);
    return aux;
}

BLEService:

 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        String intentAction;
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            intentAction = ACTION_GATT_CONNECTED;
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(intentAction);
            Log.i(TAG, "Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.i(TAG, "Attempting to start service discovery:" +
                    mBluetoothGatt.discoverServices());

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            intentAction = ACTION_GATT_DISCONNECTED;
            mConnectionState = STATE_DISCONNECTED;
            Log.i(TAG, "Disconnected from GATT server.");
            broadcastUpdate(intentAction);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            for (BluetoothGattService service : gatt.getServices()) {
                if ((service == null) || (service.getUuid() == null)) {
                    continue;
                }
                if (BLEUUID.SERVICE.equalsIgnoreCase(service.getUuid().toString())) {
                    mService = service;
                }
            }
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            broadcastUpdate(EXTRA_DATA, characteristic);
            Log.i("CARAC","CARACTERISTICA LEIDA onCharacteristicRead()");
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
        readCharacteristic(characteristic);
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        Log.i("CARAC","CAMBIO EN CARACTERISTICA");
    }
};

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())) {

        final String pin = characteristic.getStringValue(0);
        intent.putExtra(EXTRA_DATA, String.valueOf(pin));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            Log.i("RECIBIDO", "RECIBIDOS DATOS");
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

public class LocalBinder extends Binder {
    BLEService getService() {
        return BLEService.this;
    }
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

@Override
public boolean onUnbind(Intent intent) {
    // After using a given device, you should make sure that BluetoothGatt.close() is called
    // such that resources are cleaned up properly.  In this particular example, close() is
    // invoked when the UI is disconnected from the Service.
    close();
    return super.onUnbind(intent);
}

private final IBinder mBinder = new LocalBinder();

public boolean initialize() {
    if (mBluetoothManager == null) {
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            return false;
        }
    }
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        return false;
    }
    return true;
}

public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        return false;
    }

    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && mBluetoothGatt != null) {
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        return false;
    }

    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.disconnect();
}

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
    Log.e("CIERRE", "CONEXION CERRADA");
}

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
    Log.i("READ", "CARACTERISTICA LEIDA");
}

public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())){
        BluetoothGattDescriptor descriptor =
                new BluetoothGattDescriptor(UUID.nameUUIDFromBytes(BLEUUID.PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR.getBytes()),
                        BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED);

        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
}

public List<BluetoothGattService> getSupportedGattServices() {
    if (mBluetoothGatt == null) return null;
    return mBluetoothGatt.getServices();
}

public void sendCharacteristic(String pin, BluetoothDevice device){

    byte[] pinByte = pin.getBytes();
    int pinInt = Integer.valueOf(pin);

    BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mService.getCharacteristic(UUID
            .fromString(BLEUUID.PIN_CHARACTERISTIC_UUID));

    ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);

    ch.setValue(pin);

    Toast.makeText(context, "CARACTERISTICA ASIGNADA", Toast.LENGTH_SHORT).show();
    connect(device.getAddress());
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    setCharacteristicNotification(ch, true);

    if (mBluetoothGatt.writeCharacteristic(ch)) {
        Toast.makeText(context, "CARACTERISTICA ESCRITA", Toast.LENGTH_SHORT).show();
    }


}

}

BLEUUID

public class BLEUUID {

// CARACTERISTICA PIN
public  static final String SERVICE ="0000fff0-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_UUID="0000fff9-0000-1000-8000-00805f9b34fb";
public static final String PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR="0x2902";

private static HashMap<String, String> attributes = new HashMap();
static {
    attributes.put(SERVICE, "Service");
    attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");
}

When I debug, onCharacteristicChange() never executes.

Somebody knows where's the problem

回答1:

Here is the general pattern for how things need to work with BLE on Android:

  1. You try to connect
  2. You get a callback indicating it is connected
  3. You discover services
  4. You are told services are discovered
  5. You get the characteristics
  6. For each characteristic you get the descriptors
  7. For the descriptor you set it to enable notification/indication with BluetoothGattDescriptor.setValue()
  8. You write the descriptor with BluetoothGatt.writeDescriptor()
  9. You enable notifications for the characteristic locally with BluetoothGatt.setCharacteristicNotification(). Without this you won't get called back.
  10. You get notification that the descriptor was written
  11. Now you can write data to the characteristic. All of the characteristic and descriptor configuration has do be done before anything is written to any characteristic.