/* * Copyright 2019 Fitbit, Inc. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ package com.fitbit.bluetooth.fbgatt.tx; import com.fitbit.bluetooth.fbgatt.GattServerConnection; import com.fitbit.bluetooth.fbgatt.GattServerTransaction; import com.fitbit.bluetooth.fbgatt.GattState; import com.fitbit.bluetooth.fbgatt.GattTransactionCallback; import com.fitbit.bluetooth.fbgatt.Situation; import com.fitbit.bluetooth.fbgatt.TransactionResult; import com.fitbit.bluetooth.fbgatt.strategies.Strategy; import com.fitbit.bluetooth.fbgatt.util.GattStatus; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import androidx.annotation.Nullable; import timber.log.Timber; /** * Will write a characteristic descriptor from a local gatt server and populate a transaction result with * response. This and {@link WriteGattServerCharacteristicValueTransaction} are sort of conveniences * for testing, there is no clear reason why one wouldn't perform these operations in Java, they have * no impact to the state machine. But there is no harm in mainstream code using these transactions. * * Created by iowens on 12/4/17. */ public class WriteGattServerCharacteristicDescriptorValueTransaction extends GattServerTransaction { private static final String TAG = "WriteGattServerCharacteristicDescriptorTransaction"; private BluetoothGattCharacteristic characteristic; private BluetoothGattService service; private BluetoothGattDescriptor descriptor; private byte[] data; public WriteGattServerCharacteristicDescriptorValueTransaction(@Nullable GattServerConnection connection, GattState successEndState, BluetoothGattService service, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, byte[] data) { super(connection, successEndState); this.descriptor = descriptor; this.characteristic = characteristic; this.service = service; this.data = data; } public WriteGattServerCharacteristicDescriptorValueTransaction(@Nullable GattServerConnection connection, GattState successEndState, BluetoothGattService service, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, byte[] data, long timeoutMillis) { super(connection, successEndState, timeoutMillis); this.descriptor = descriptor; this.characteristic = characteristic; this.service = service; this.data = data; } @Override protected void transaction(GattTransactionCallback callback) { super.transaction(callback); getGattServer().setState(GattState.WRITING_DESCRIPTOR); BluetoothGattCharacteristic localCharacteristic = service.getCharacteristic(this.characteristic.getUuid()); if(localCharacteristic == null) { respondWithError(null, null, callback); return; } BluetoothGattDescriptor localDescriptor = localCharacteristic.getDescriptor(this.descriptor.getUuid()); if(localDescriptor == null) { respondWithError(null, null, callback); return; } TransactionResult.Builder builder = new TransactionResult.Builder().transactionName(getName()); try { if (localDescriptor.setValue(data)) { // success builder.responseStatus(GattStatus.GATT_SUCCESS.ordinal()); getGattServer().setState(GattState.WRITE_DESCRIPTOR_SUCCESS); builder.data(localCharacteristic.getValue()) .gattState(getGattServer().getGattState()) .resultStatus(TransactionResult.TransactionResultStatus.SUCCESS) .serviceUuid(service.getUuid()) .characteristicUuid(localCharacteristic.getUuid()) .descriptorUuid(localDescriptor.getUuid()); callCallbackWithTransactionResultAndRelease(callback, builder.build()); getGattServer().setState(GattState.IDLE); } else { // failure respondWithError(localCharacteristic, localDescriptor, callback); } } catch (NullPointerException ex) { Timber.w(ex,"[%s] We are going to fail this tx due to the stack NPE, this is probably poor peripheral behavior, this should become a FW bug.", getDevice()); respondWithError(localCharacteristic, localDescriptor, callback); Strategy strategy = strategyProvider. getStrategyForPhoneAndGattConnection(null, getConnection(), Situation.TRACKER_WENT_AWAY_DURING_GATT_OPERATION); if (strategy != null) { strategy.applyStrategy(); } } } private void respondWithError(@Nullable BluetoothGattCharacteristic characteristic, @Nullable BluetoothGattDescriptor descriptor, GattTransactionCallback callback) { TransactionResult.Builder builder = new TransactionResult.Builder().transactionName(getName()); builder.responseStatus(GattStatus.GATT_ERROR.ordinal()); getGattServer().setState(GattState.WRITE_DESCRIPTOR_FAILURE); builder.data(descriptor == null ? new byte[0] : descriptor.getValue()) .gattState(getGattServer().getGattState()) .resultStatus(TransactionResult.TransactionResultStatus.FAILURE) .serviceUuid(service.getUuid()) .characteristicUuid(characteristic == null ? null : characteristic.getUuid()) .descriptorUuid(descriptor == null ? null : descriptor.getUuid()); callCallbackWithTransactionResultAndRelease(callback, builder.build()); getGattServer().setState(GattState.IDLE); } @Override public String getName() { return TAG; } }