1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth.le; 18 19 import static android.bluetooth.le.BluetoothLeUtils.getSyncTimeout; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.bluetooth.BluetoothDevice; 26 import android.bluetooth.BluetoothStatusCodes; 27 import android.bluetooth.IBluetoothGatt; 28 import android.content.AttributionSource; 29 import android.os.Binder; 30 import android.os.ParcelUuid; 31 import android.os.RemoteException; 32 import android.util.Log; 33 34 import com.android.modules.utils.SynchronousResultReceiver; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.util.Objects; 39 import java.util.concurrent.Executor; 40 import java.util.concurrent.TimeoutException; 41 42 /** 43 * This class provides a way to control an active distance measurement session. 44 * <p>It also defines the required {@link DistanceMeasurementSession.Callback} that must be 45 * implemented in order to be notified of distance measurement results and status events related to 46 * the {@link DistanceMeasurementSession}. 47 * 48 * <p>To get an instance of {@link DistanceMeasurementSession}, first use 49 * {@link DistanceMeasurementManager#startMeasurementSession(DistanceMeasurementParams, Executor, 50 * DistanceMeasurementSession.Callback)} to request to start a session. Once the session is started, 51 * a {@link DistanceMeasurementSession} object is provided through 52 * {@link DistanceMeasurementSession.Callback#onStarted(DistanceMeasurementSession)}. 53 * If starting a session fails, the failure is reported through 54 * {@link DistanceMeasurementSession.Callback#onStartFail(int)} with the failure reason. 55 * 56 * @hide 57 */ 58 @SystemApi 59 public final class DistanceMeasurementSession { 60 private static final String TAG = "DistanceMeasurementSession"; 61 62 private final IBluetoothGatt mGatt; 63 private final ParcelUuid mUuid; 64 private final DistanceMeasurementParams mDistanceMeasurementParams; 65 private final Executor mExecutor; 66 private final Callback mCallback; 67 private final AttributionSource mAttributionSource; 68 69 /** @hide */ 70 @Retention(RetentionPolicy.SOURCE) 71 @IntDef(value = { 72 BluetoothStatusCodes.SUCCESS, 73 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 74 BluetoothStatusCodes.ERROR_DISTANCE_MEASUREMENT_INTERNAL, 75 }) 76 public @interface StopSessionReturnValues{} 77 78 /** 79 * @hide 80 */ DistanceMeasurementSession(IBluetoothGatt gatt, ParcelUuid uuid, DistanceMeasurementParams params, Executor executor, AttributionSource attributionSource, Callback callback)81 public DistanceMeasurementSession(IBluetoothGatt gatt, ParcelUuid uuid, 82 DistanceMeasurementParams params, Executor executor, 83 AttributionSource attributionSource, Callback callback) { 84 Objects.requireNonNull(gatt, "gatt is null"); 85 Objects.requireNonNull(params, "params is null"); 86 Objects.requireNonNull(executor, "executor is null"); 87 Objects.requireNonNull(callback, "callback is null"); 88 mGatt = gatt; 89 mUuid = uuid; 90 mDistanceMeasurementParams = params; 91 mExecutor = executor; 92 mAttributionSource = attributionSource; 93 mCallback = callback; 94 } 95 96 /** 97 * Stops actively ranging, {@link Callback#onStopped} will be invoked if this succeeds. 98 * 99 * @return whether successfully stop or not 100 * 101 * @hide 102 */ 103 @SystemApi 104 @RequiresPermission(allOf = { 105 android.Manifest.permission.BLUETOOTH_CONNECT, 106 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 107 }) stopSession()108 public @StopSessionReturnValues int stopSession() { 109 final int defaultValue = BluetoothStatusCodes.ERROR_TIMEOUT; 110 try { 111 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 112 mGatt.stopDistanceMeasurement(mUuid, mDistanceMeasurementParams.getDevice(), 113 mDistanceMeasurementParams.getMethodId(), mAttributionSource, recv); 114 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); 115 } catch (TimeoutException e) { 116 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 117 } catch (RemoteException e) { 118 throw e.rethrowFromSystemServer(); 119 } 120 return defaultValue; 121 } 122 123 /** 124 * @hide 125 */ onStarted()126 void onStarted() { 127 executeCallback(() -> mCallback.onStarted(this)); 128 } 129 130 /** 131 * @hide 132 */ onStartFail(int reason)133 void onStartFail(int reason) { 134 executeCallback(() -> mCallback.onStartFail(reason)); 135 } 136 137 138 /** 139 * @hide 140 */ onStopped(int reason)141 void onStopped(int reason) { 142 executeCallback(() -> mCallback.onStopped(this, reason)); 143 } 144 145 /** 146 * @hide 147 */ onResult(@onNull BluetoothDevice device, @NonNull DistanceMeasurementResult result)148 void onResult(@NonNull BluetoothDevice device, 149 @NonNull DistanceMeasurementResult result) { 150 executeCallback(() -> mCallback.onResult(device, result)); 151 } 152 153 154 /** 155 * @hide 156 */ executeCallback(@onNull Runnable runnable)157 private void executeCallback(@NonNull Runnable runnable) { 158 final long identity = Binder.clearCallingIdentity(); 159 try { 160 mExecutor.execute(runnable); 161 } finally { 162 Binder.restoreCallingIdentity(identity); 163 } 164 } 165 166 /** 167 * Interface for receiving {@link DistanceMeasurementSession} events. 168 * 169 * @hide 170 */ 171 @SystemApi 172 public interface Callback { 173 /** 174 * @hide 175 */ 176 @Retention(RetentionPolicy.SOURCE) 177 @IntDef(value = { 178 BluetoothStatusCodes.ERROR_UNKNOWN, 179 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 180 BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, 181 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST, 182 BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, 183 BluetoothStatusCodes.REASON_REMOTE_REQUEST, 184 BluetoothStatusCodes.ERROR_TIMEOUT, 185 BluetoothStatusCodes.ERROR_NO_LE_CONNECTION, 186 BluetoothStatusCodes.ERROR_BAD_PARAMETERS, 187 BluetoothStatusCodes.ERROR_DISTANCE_MEASUREMENT_INTERNAL, 188 }) 189 @interface Reason {} 190 191 /** 192 * Invoked when {@link DistanceMeasurementManager#startMeasurementSession( 193 * DistanceMeasurementParams, Executor, DistanceMeasurementSession.Callback)} is successful. 194 * 195 * @param session the started {@link DistanceMeasurementSession} 196 * 197 * @hide 198 */ 199 @SystemApi onStarted(@onNull DistanceMeasurementSession session)200 void onStarted(@NonNull DistanceMeasurementSession session); 201 202 /** 203 * Invoked if {@link DistanceMeasurementManager#startMeasurementSession( 204 * DistanceMeasurementParams, Executor, DistanceMeasurementSession.Callback)} fails. 205 * 206 * @param reason the failure reason 207 * 208 * @hide 209 */ 210 @SystemApi onStartFail(@onNull @eason int reason)211 void onStartFail(@NonNull @Reason int reason); 212 213 /** 214 * Invoked when a distance measurement session stopped. 215 * 216 * @param reason reason for the session stop 217 * 218 * @hide 219 */ 220 @SystemApi onStopped(@onNull DistanceMeasurementSession session, @NonNull @Reason int reason)221 void onStopped(@NonNull DistanceMeasurementSession session, @NonNull @Reason int reason); 222 223 /** 224 * Invoked when get distance measurement result. 225 * 226 * @param device remote device 227 * @param result {@link DistanceMeasurementResult} for this device 228 * 229 * @hide 230 */ 231 @SystemApi onResult(@onNull BluetoothDevice device, @NonNull DistanceMeasurementResult result)232 void onResult(@NonNull BluetoothDevice device, 233 @NonNull DistanceMeasurementResult result); 234 } 235 } 236