写入特定特征时出现BleGattException

时间:2016-10-13 22:20:44

标签: android android-bluetooth android-ble rxandroidble

写入特定特征会使应用程序崩溃并引发以下异常:

Caused by: BleGattException{status=8, bleGattOperation=BleGattOperation{description='CHARACTERISTIC_WRITE'}}
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.propagateStatusErrorIfGattErrorOccurred(RxBleGattCallback.java:245)
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback.access$100(RxBleGattCallback.java:26)
      at com.polidea.rxandroidble.internal.connection.RxBleGattCallback$1.onCharacteristicWrite(RxBleGattCallback.java:110)
      at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:407)
      at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:279)

建立与设备的连接,其他读写方法似乎都能正常工作。

正在使用的代码:

mConnectoin.writeCharacteristic(UUID, bytes)
    .observeOn(AndroidSchedulers.mainThread());

我的第一个想法是,或许该特性没有启用写入权限, 但是characteristic.getProperties()的以下日志语句返回8,表明它确实具有写权限:

.getCharacteristic(CharacteristicUUID)
                    .subscribe(new Action1<BluetoothGattCharacteristic>() {
                        @Override
                        public void call(BluetoothGattCharacteristic characteristic) {
                            Log.d(TAG, "characteristic permissions: " + characteristic.getPermissions());
                            Log.d(TAG, "characteristic properties: " + characteristic.getProperties());
                        }
                    });

那么问题可能是什么?

2 个答案:

答案 0 :(得分:2)

BluetoothGattCallbackonCharacteristicWrite()RxBleGattCallback回调方法中发出。 (它在status类内)。

status=8来自Android OS BLE堆栈。例如,这些描述如下:https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h

#define GATT_INSUF_AUTHORIZATION 0x08表示BluetoothCharacteristic - 因此您似乎正在尝试编写需要加密连接的RxAndroidBle(配对设备)。

不幸的是,目前 @Override @TargetApi(15) public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; if (row == null) { row = mActivity.getLayoutInflater().inflate(mLayoutId, null, false); } Cursor list_cursor = getCursor(); list_cursor.moveToPosition(position); if(!list_cursor.isLast()){ list_cursor.moveToNext(); TextView txtSecondCell = (TextView) row.findViewById(R.id.recname_row); String recipename = list_cursor.getString(list_cursor.getColumnIndex(Recipe.NAME_NIC)); txtSecondCell.setText(recipename); Log.i(TAG, "Showing List"); } return row; } 无法帮助配对设备。

答案 1 :(得分:1)

正如@s_noopy指出的那样,对于在库中配对设备没有特别的支持,但是有一些选项可以创建你的小助手,可以在连接之前为你提供帮助。

RxBluetooth library支持以反应方式进行债券。

我提取了特定的代码片段,并创建了一个小小的要点来分享(只有绑定过程)。随意分叉或改进它(代码基于RxBluetooth)。此外,如果有人找到更好的方法或者出现问题,请指出来!

original gist

<强>更新

我修改了RxBluetooth(之前命名为RxBluetoothHelper)的源代码,因为在进行取消绑定时存在错误。在Android中,当你绑定时,你必须在意图中监听绑定过程(但是当你不需要unbond时,因为系统只删除存储的键)。此更新版本解决了以前没有的问题。此外,如果由于某种原因调用bond时方法返回false,则observable将发出onError。要点也更新了!


public class BluetoothCompat {

    public static boolean createBondCompat(final BluetoothDevice device)
    throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            return device.createBond();
            } else {
            Method method = device.getClass().getMethod("createBond", (Class[]) null);
            final Object invoke = method.invoke(device, (Object[]) null);
            return (Boolean) invoke;
        }
    }

    public static boolean removeBondCompat(final BluetoothDevice device) throws NoSuchMethodException, InvocationTargetException,
    IllegalAccessException {
        Method method = device.getClass().getMethod("removeBond", (Class[]) null);
        final Object invoke = method.invoke(device, (Object[]) null);
        return (Boolean) invoke;
    }

    private BluetoothCompat() {
        throw new AssertionError("No instances!");
    }

}

public class RxBluetooth {

    private final Context context;
    private final BluetoothAdapter adapter;

    private static final Observable.Transformer BOND_STATUS_TRANSFORMER = statusObservable -> statusObservable.map(status -> {
        switch (status) {
            case BluetoothDevice.BOND_NONE:
            default:
            return BondStatus.NONE;
            case BluetoothDevice.BOND_BONDING:
            return BondStatus.BONDING;
            case BluetoothDevice.BOND_BONDED:
            return BondStatus.BONDED;
        }
    });

    public enum BondStatus {
        NONE,
        BONDING,
        BONDED
    }

    public RxBluetooth(Context context, BluetoothAdapter bluetoothAdapter) {
        this.context = context.getApplicationContext();
        this.adapter = bluetoothAdapter;
    }

    public Observable bondStatus(@NonNull final BluetoothDevice device) {
        return Observable.defer(() -> Observable.just(device.getBondState()).compose(BOND_STATUS_TRANSFORMER));
    }

    public Observable bond(@NonNull final BluetoothDevice device) {
        return Observable.create(subscriber -> {
            bondStatus(device).subscribe(bondStatus -> {
                switch (bondStatus) {
                    case NONE:
                    observeDeviceBonding(context, device).compose(BOND_STATUS_TRANSFORMER).subscribe(subscriber);
                    try {
                        final boolean bonding = BluetoothCompat.createBondCompat(device);
                        if (!bonding) {
                            subscriber.onError(new BluetoothBondingException("Can't initiate a bonding operation!"));
                        }
                        } catch (Exception e) {
                        subscriber.onError(new BluetoothIncompatibleBondingException(e));
                    }
                    break;
                    case BONDING:
                    subscriber.onError(new BluetoothBondingException("device is already in the process of bonding"));
                    break;
                    case BONDED:
                    subscriber.onNext(BondStatus.BONDED);
                    subscriber.onCompleted();
                    break;
                }
            });
        });
    }

    public Observable removeBond(@NonNull final BluetoothDevice device) {
        return Observable.defer(() -> {

            for (BluetoothDevice bondedDevice : adapter.getBondedDevices()) {
                if (bondedDevice.getAddress().equals(device.getAddress())) {
                    try {
                        final boolean removeBond = BluetoothCompat.removeBondCompat(device);
                        if (!removeBond) {
                            return Observable.error(new BluetoothBondingException("Can't delete the bonding for this device!"));
                        }
                        } catch (Exception e) {
                        return Observable.error(new BluetoothIncompatibleBondingException(e));
                    }
                }
            }

            return Observable.just(BondStatus.NONE);
        });
    }

    private static Observable observeDeviceBonding(@NonNull final Context context, @NonNull final BluetoothDevice device) {
        return observeBroadcast(context, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)).filter(pair -> {
            BluetoothDevice bondingDevice = pair.getValue1().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            return bondingDevice.equals(device);
        })
        .map(pair1 -> pair1.getValue1().getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1))
        .skipWhile(state -> state != BluetoothDevice.BOND_BONDING)
        .takeUntil(state -> state == BluetoothDevice.BOND_BONDED || state == BluetoothDevice.BOND_NONE);
    }

    private static Observable> observeBroadcast(final Context context, final IntentFilter filter) {
        return Observable.create(new Observable.OnSubscribe>() {

            @Override public void call(Subscriber> subscriber) {
                Enforcer.onMainThread();

                final BroadcastReceiver receiver = new BroadcastReceiver() {
                    @Override public void onReceive(Context context, Intent intent) {
                        subscriber.onNext(Pair.with(context, intent));
                    }
                };

                context.registerReceiver(receiver, filter);

                subscriber.add(Subscriptions.create(() -> context.unregisterReceiver(receiver)));
            }
        });
    }

}