/* * Copyright (c) 2018, Nordic Semiconductor * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package no.nordicsemi.android.ble; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import androidx.annotation.IntRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import no.nordicsemi.android.ble.callback.FailCallback; import no.nordicsemi.android.ble.callback.SuccessCallback; import no.nordicsemi.android.ble.exception.BluetoothDisabledException; import no.nordicsemi.android.ble.exception.DeviceDisconnectedException; import no.nordicsemi.android.ble.exception.InvalidRequestException; import no.nordicsemi.android.ble.exception.RequestFailedException; /** * A value request that requires a {@link android.bluetooth.BluetoothGattCallback callback} or * can't have timeout for any other reason. This class defines the {@link #await()} methods. */ @SuppressWarnings({"WeakerAccess", "unused"}) public abstract class TimeoutableValueRequest extends TimeoutableRequest { T valueCallback; TimeoutableValueRequest(@NonNull final Type type) { super(type); } TimeoutableValueRequest(@NonNull final Type type, @Nullable final BluetoothGattCharacteristic characteristic) { super(type, characteristic); } TimeoutableValueRequest(@NonNull final Type type, @Nullable final BluetoothGattDescriptor descriptor) { super(type, descriptor); } @NonNull @Override public TimeoutableValueRequest timeout(@IntRange(from = 0) final long timeout) { super.timeout(timeout); return this; } /** * Sets the value callback. When the request is invoked synchronously, this callback will * be ignored and the received value will be returned by the await(...) method; * * @param callback the callback. * @return The request. */ @NonNull public TimeoutableValueRequest with(@NonNull final T callback) { this.valueCallback = callback; return this; } /** * Synchronously waits until the request is done. *

* When the timeout, set with {@link #timeout(long)} occurs, the {@link InterruptedException} * will be thrown. *

* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and * {@link #with(E)} will be ignored. *

* This method may not be called from the main (UI) thread. * * @param response the response object. * @param a response class. * @return The response with a response. * @throws RequestFailedException thrown when the BLE request finished with status other * than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}. * @throws InterruptedException thrown if the timeout occurred before the request has * finished. * @throws IllegalStateException thrown when you try to call this method from the main * (UI) thread, or when the trigger was already enqueued. * @throws DeviceDisconnectedException thrown when the device disconnected before the request * was completed. * @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). */ @NonNull public E await(@NonNull final E response) throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException, InterruptedException { assertNotMainThread(); final T vc = valueCallback; try { with(response).await(); return response; } finally { valueCallback = vc; } } /** * Synchronously waits until the request is done. *

* Callbacks set using {@link #done(SuccessCallback)} and {@link #fail(FailCallback)} and * {@link #with(T)} will be ignored. *

* This method may not be called from the main (UI) thread. * * @param responseClass the response class. This class will be instantiate, therefore it has * to have a default constructor. * @return The response with a response. * @throws RequestFailedException thrown when the BLE request finished with status other * than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}. * @throws InterruptedException thrown if the timeout occurred before the request has * finished. * @throws IllegalStateException thrown when you try to call this method from the main * (UI) thread. * @throws IllegalArgumentException thrown when the response class could not be instantiated. * @throws DeviceDisconnectedException thrown when the device disconnected before the request * was completed. * @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @see #await(Object) */ @NonNull public E await(@NonNull final Class responseClass) throws RequestFailedException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException, InterruptedException { assertNotMainThread(); try { final E response = responseClass.newInstance(); return await(response); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Couldn't instantiate " + responseClass.getCanonicalName() + " class. Is the default constructor accessible?"); } catch (InstantiationException e) { throw new IllegalArgumentException("Couldn't instantiate " + responseClass.getCanonicalName() + " class. Does it have a default constructor with no arguments?"); } } /** * Synchronously waits until the request is done, for at most given number of milliseconds * after which the {@link InterruptedException} will be thrown. *

* Callbacks set using {@link #done(SuccessCallback)}, {@link #fail(FailCallback)} and * {@link #with(E)} will be ignored. *

* This method may not be called from the main (UI) thread. * * @param responseClass the response class. This class will be instantiate, therefore it has * to have a default constructor. * @param timeout optional timeout in milliseconds. This value will override one set * in {@link #timeout(long)}. * @param a response class that extends {@link T}. * @return The object with a response. * @throws RequestFailedException thrown when the BLE request finished with status other * than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}. * @throws InterruptedException thrown if the timeout occurred before the request has * finished. * @throws IllegalStateException thrown when you try to call this method from the main * (UI) thread. * @throws IllegalArgumentException thrown when the response class could not be instantiated. * @throws DeviceDisconnectedException thrown when the device disconnected before the request * was completed. * @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @deprecated Use {@link #timeout(long)} and {@link #await(Class)} instead. */ @NonNull @Deprecated public E await(@NonNull final Class responseClass, @IntRange(from = 0) final long timeout) throws RequestFailedException, InterruptedException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException { return timeout(timeout).await(responseClass); } /** * Synchronously waits until the request is done, for at most given number of milliseconds * after which the {@link InterruptedException} will be thrown. *

* Callbacks set using {@link #done(SuccessCallback)}, {@link #fail(FailCallback)} and * {@link #with(E)} will be ignored. *

* This method may not be called from the main (UI) thread. * * @param response the response object. * @param timeout optional timeout in milliseconds. * @param a response class that extends {@link T}. * @return The object with a response. * @throws RequestFailedException thrown when the BLE request finished with status other * than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}. * @throws InterruptedException thrown if the timeout occurred before the request has * finished. * @throws IllegalStateException thrown when you try to call this method from the main * (UI) thread. * @throws DeviceDisconnectedException thrown when the device disconnected before the request * was completed. * @throws BluetoothDisabledException thrown when the Bluetooth adapter is disabled. * @throws InvalidRequestException thrown when the request was called before the device was * connected at least once (unknown device). * @deprecated Use {@link #timeout(long)} and {@link #await(E)} instead. */ @NonNull @Deprecated public E await(@NonNull final E response, @IntRange(from = 0) final long timeout) throws RequestFailedException, InterruptedException, DeviceDisconnectedException, BluetoothDisabledException, InvalidRequestException { return timeout(timeout).await(response); } }