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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothStatusCodes; 25 import android.bluetooth.IBluetoothGatt; 26 import android.content.AttributionSource; 27 import android.os.Binder; 28 import android.os.ParcelUuid; 29 import android.os.RemoteException; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.Objects; 34 import java.util.concurrent.Executor; 35 36 /** 37 * This class provides a way to control an active distance measurement session. 38 * 39 * <p>It also defines the required {@link DistanceMeasurementSession.Callback} that must be 40 * implemented in order to be notified of distance measurement results and status events related to 41 * the {@link DistanceMeasurementSession}. 42 * 43 * <p>To get an instance of {@link DistanceMeasurementSession}, first use {@link 44 * DistanceMeasurementManager#startMeasurementSession(DistanceMeasurementParams, Executor, 45 * DistanceMeasurementSession.Callback)} to request to start a session. Once the session is started, 46 * a {@link DistanceMeasurementSession} object is provided through {@link 47 * DistanceMeasurementSession.Callback#onStarted(DistanceMeasurementSession)}. If starting a session 48 * fails, the failure is reported through {@link 49 * DistanceMeasurementSession.Callback#onStartFail(int)} with the failure reason. 50 * 51 * @hide 52 */ 53 @SystemApi 54 public final class DistanceMeasurementSession { 55 private static final String TAG = "DistanceMeasurementSession"; 56 57 private final IBluetoothGatt mGatt; 58 private final ParcelUuid mUuid; 59 private final DistanceMeasurementParams mDistanceMeasurementParams; 60 private final Executor mExecutor; 61 private final Callback mCallback; 62 private final AttributionSource mAttributionSource; 63 64 /** @hide */ 65 @Retention(RetentionPolicy.SOURCE) 66 @IntDef( 67 value = { 68 BluetoothStatusCodes.SUCCESS, 69 BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, 70 BluetoothStatusCodes.ERROR_DISTANCE_MEASUREMENT_INTERNAL, 71 }) 72 public @interface StopSessionReturnValues {} 73 74 /** @hide */ DistanceMeasurementSession( IBluetoothGatt gatt, ParcelUuid uuid, DistanceMeasurementParams params, Executor executor, AttributionSource attributionSource, Callback callback)75 public DistanceMeasurementSession( 76 IBluetoothGatt gatt, 77 ParcelUuid uuid, 78 DistanceMeasurementParams params, 79 Executor executor, 80 AttributionSource attributionSource, 81 Callback callback) { 82 Objects.requireNonNull(gatt, "gatt is null"); 83 Objects.requireNonNull(params, "params is null"); 84 Objects.requireNonNull(executor, "executor is null"); 85 Objects.requireNonNull(callback, "callback is null"); 86 mGatt = gatt; 87 mUuid = uuid; 88 mDistanceMeasurementParams = params; 89 mExecutor = executor; 90 mAttributionSource = attributionSource; 91 mCallback = callback; 92 } 93 94 /** 95 * Stops actively ranging, {@link Callback#onStopped} will be invoked if this succeeds. 96 * 97 * @return whether successfully stop or not 98 * @hide 99 */ 100 @SystemApi 101 @RequiresPermission( 102 allOf = { 103 android.Manifest.permission.BLUETOOTH_CONNECT, 104 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 105 }) stopSession()106 public @StopSessionReturnValues int stopSession() { 107 try { 108 return mGatt.stopDistanceMeasurement( 109 mUuid, 110 mDistanceMeasurementParams.getDevice(), 111 mDistanceMeasurementParams.getMethodId(), 112 mAttributionSource); 113 } catch (RemoteException e) { 114 throw e.rethrowAsRuntimeException(); 115 } 116 } 117 118 /** @hide */ onStarted()119 void onStarted() { 120 executeCallback(() -> mCallback.onStarted(this)); 121 } 122 123 /** @hide */ onStartFail(int reason)124 void onStartFail(int reason) { 125 executeCallback(() -> mCallback.onStartFail(reason)); 126 } 127 128 /** @hide */ onStopped(int reason)129 void onStopped(int reason) { 130 executeCallback(() -> mCallback.onStopped(this, reason)); 131 } 132 133 /** @hide */ onResult(@onNull BluetoothDevice device, @NonNull DistanceMeasurementResult result)134 void onResult(@NonNull BluetoothDevice device, @NonNull DistanceMeasurementResult result) { 135 executeCallback(() -> mCallback.onResult(device, result)); 136 } 137 138 /** @hide */ executeCallback(@onNull Runnable runnable)139 private void executeCallback(@NonNull Runnable runnable) { 140 final long identity = Binder.clearCallingIdentity(); 141 try { 142 mExecutor.execute(runnable); 143 } finally { 144 Binder.restoreCallingIdentity(identity); 145 } 146 } 147 148 /** 149 * Interface for receiving {@link DistanceMeasurementSession} events. 150 * 151 * @hide 152 */ 153 @SystemApi 154 public interface Callback { 155 /** @hide */ 156 @Retention(RetentionPolicy.SOURCE) 157 @IntDef( 158 value = { 159 BluetoothStatusCodes.ERROR_UNKNOWN, 160 BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, 161 BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED, 162 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST, 163 BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST, 164 BluetoothStatusCodes.REASON_REMOTE_REQUEST, 165 BluetoothStatusCodes.ERROR_TIMEOUT, 166 BluetoothStatusCodes.ERROR_NO_LE_CONNECTION, 167 BluetoothStatusCodes.ERROR_BAD_PARAMETERS, 168 BluetoothStatusCodes.ERROR_DISTANCE_MEASUREMENT_INTERNAL, 169 }) 170 @interface Reason {} 171 172 /** 173 * Invoked when {@link DistanceMeasurementManager#startMeasurementSession( 174 * DistanceMeasurementParams, Executor, DistanceMeasurementSession.Callback)} is successful. 175 * 176 * @param session the started {@link DistanceMeasurementSession} 177 * @hide 178 */ 179 @SystemApi onStarted(@onNull DistanceMeasurementSession session)180 void onStarted(@NonNull DistanceMeasurementSession session); 181 182 /** 183 * Invoked if {@link DistanceMeasurementManager#startMeasurementSession( 184 * DistanceMeasurementParams, Executor, DistanceMeasurementSession.Callback)} fails. 185 * 186 * @param reason the failure reason 187 * @hide 188 */ 189 @SystemApi onStartFail(@onNull @eason int reason)190 void onStartFail(@NonNull @Reason int reason); 191 192 /** 193 * Invoked when a distance measurement session stopped. 194 * 195 * @param reason reason for the session stop 196 * @hide 197 */ 198 @SystemApi onStopped(@onNull DistanceMeasurementSession session, @NonNull @Reason int reason)199 void onStopped(@NonNull DistanceMeasurementSession session, @NonNull @Reason int reason); 200 201 /** 202 * Invoked when get distance measurement result. 203 * 204 * @param device remote device 205 * @param result {@link DistanceMeasurementResult} for this device 206 * @hide 207 */ 208 @SystemApi onResult(@onNull BluetoothDevice device, @NonNull DistanceMeasurementResult result)209 void onResult(@NonNull BluetoothDevice device, @NonNull DistanceMeasurementResult result); 210 } 211 } 212