/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import static android.bluetooth.le.BluetoothLeUtils.getSyncTimeout; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.content.AttributionSource; import android.os.CancellationSignal; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; import com.android.modules.utils.SynchronousResultReceiver; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; /** * This class provides methods to perform distance measurement related * operations. An application can start distance measurement by using * {@link DistanceMeasurementManager#startMeasurementSession}. *
* Use {@link BluetoothAdapter#getDistanceMeasurementManager()} to get an instance of
* {@link DistanceMeasurementManager}.
*
* @hide
*/
@SystemApi
public final class DistanceMeasurementManager {
private static final String TAG = "DistanceMeasurementManager";
private final ConcurrentHashMap This can be used to check supported methods before start distance measurement.
*
* @return a list of {@link DistanceMeasurementMethod}
*
* @hide
*/
@SystemApi
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public @NonNull List> recv =
SynchronousResultReceiver.get();
gatt.getSupportedDistanceMeasurementMethods(mAttributionSource, recv);
return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(new ArrayList<>());
} catch (TimeoutException | RemoteException e) {
Log.e(TAG, "Failed to get supported methods - ", e);
}
return supportedMethods;
}
/**
* Start distance measurement and create a {@link DistanceMeasurementSession} for this
* operation. Once the session is started, a {@link DistanceMeasurementSession} object is
* provided through
* {@link DistanceMeasurementSession.Callback#onStarted(DistanceMeasurementSession)}.
* If starting a session fails, the failure is reported through
* {@link DistanceMeasurementSession.Callback#onStartFail(int)} with the failure reason.
*
* @param params parameters of this operation
* @param executor Executor to run callback
* @param callback callback to associate with the
* {@link DistanceMeasurementSession} that is being started. The callback is
* registered by this function and unregisted when
* {@link DistanceMeasurementSession.Callback#onStartFail(int)} or
* {@link DistanceMeasurementSession
* .Callback#onStopped(DistanceMeasurementSession, int)}
* @return a CancellationSignal that may be used to cancel the starting of the
* {@link DistanceMeasurementSession}
* @throws NullPointerException if any input parameter is null
* @throws IllegalStateException if the session is already registered
* @hide
*/
@SystemApi
@Nullable
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public CancellationSignal startMeasurementSession(
@NonNull DistanceMeasurementParams params,
@NonNull Executor executor,
@NonNull DistanceMeasurementSession.Callback callback) {
Objects.requireNonNull(params, "params is null");
Objects.requireNonNull(executor, "executor is null");
Objects.requireNonNull(callback, "callback is null");
try {
IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt();
if (gatt == null) {
Log.e(TAG, "Bluetooth GATT is null");
return null;
}
DistanceMeasurementSession session = new DistanceMeasurementSession(gatt, mUuid,
params, executor, mAttributionSource, callback);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> session.stopSession());
if (mSessionMap.containsKey(params.getDevice())) {
throw new IllegalStateException(params.getDevice().getAnonymizedAddress()
+ " already registered");
}
mSessionMap.put(params.getDevice(), session);
final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
gatt.startDistanceMeasurement(mUuid, params, mCallbackWrapper, mAttributionSource,
recv);
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return cancellationSignal;
} catch (TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
return null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@SuppressLint("AndroidFrameworkBluetoothPermission")
private final IDistanceMeasurementCallback mCallbackWrapper =
new IDistanceMeasurementCallback.Stub() {
@Override
public void onStarted(BluetoothDevice device) {
DistanceMeasurementSession session = mSessionMap.get(device);
session.onStarted();
}
@Override
public void onStartFail(BluetoothDevice device, int reason) {
DistanceMeasurementSession session = mSessionMap.get(device);
session.onStartFail(reason);
mSessionMap.remove(device);
}
@Override
public void onStopped(BluetoothDevice device, int reason) {
DistanceMeasurementSession session = mSessionMap.get(device);
session.onStopped(reason);
mSessionMap.remove(device);
}
@Override
public void onResult(BluetoothDevice device, DistanceMeasurementResult result) {
DistanceMeasurementSession session = mSessionMap.get(device);
session.onResult(device, result);
}
};
}