• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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