• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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.uwb;
18 
19 import android.Manifest;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.os.Binder;
25 import android.os.PersistableBundle;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.concurrent.Executor;
32 
33 /**
34  * This class provides a way to control an active UWB ranging session.
35  * <p>It also defines the required {@link RangingSession.Callback} that must be implemented
36  * in order to be notified of UWB ranging results and status events related to the
37  * {@link RangingSession}.
38  *
39  * <p>To get an instance of {@link RangingSession}, first use
40  * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
41  * session. Once the session is opened, a {@link RangingSession} object is provided through
42  * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure
43  * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the
44  * failure reason.
45  *
46  * @hide
47  */
48 @SystemApi
49 public final class RangingSession implements AutoCloseable {
50     private static final String TAG = "Uwb.RangingSession";
51     private final SessionHandle mSessionHandle;
52     private final IUwbAdapter mAdapter;
53     private final Executor mExecutor;
54     private final Callback mCallback;
55 
56     private enum State {
57         /**
58          * The state of the {@link RangingSession} until
59          * {@link RangingSession.Callback#onOpened(RangingSession)} is invoked
60          */
61         INIT,
62 
63         /**
64          * The {@link RangingSession} is initialized and ready to begin ranging
65          */
66         IDLE,
67 
68         /**
69          * The {@link RangingSession} is actively ranging
70          */
71         ACTIVE,
72 
73         /**
74          * The {@link RangingSession} is closed and may not be used for ranging.
75          */
76         CLOSED
77     }
78 
79     private State mState = State.INIT;
80 
81     /**
82      * Interface for receiving {@link RangingSession} events
83      */
84     public interface Callback {
85         /**
86          * @hide
87          */
88         @Retention(RetentionPolicy.SOURCE)
89         @IntDef(value = {
90                 REASON_UNKNOWN,
91                 REASON_LOCAL_REQUEST,
92                 REASON_REMOTE_REQUEST,
93                 REASON_BAD_PARAMETERS,
94                 REASON_GENERIC_ERROR,
95                 REASON_MAX_SESSIONS_REACHED,
96                 REASON_SYSTEM_POLICY,
97                 REASON_PROTOCOL_SPECIFIC_ERROR})
98         @interface Reason {}
99 
100         /**
101          * Indicates that the session was closed or failed to open due to an unknown reason
102          */
103         int REASON_UNKNOWN = 0;
104 
105         /**
106          * Indicates that the session was closed or failed to open because
107          * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
108          */
109         int REASON_LOCAL_REQUEST = 1;
110 
111         /**
112          * Indicates that the session was closed or failed to open due to an explicit request from
113          * the remote device.
114          */
115         int REASON_REMOTE_REQUEST = 2;
116 
117         /**
118          * Indicates that the session was closed or failed to open due to erroneous parameters
119          */
120         int REASON_BAD_PARAMETERS = 3;
121 
122         /**
123          * Indicates an error on this device besides the error code already listed
124          */
125         int REASON_GENERIC_ERROR = 4;
126 
127         /**
128          * Indicates that the number of currently open sessions is equal to
129          * {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be
130          * opened.
131          */
132         int REASON_MAX_SESSIONS_REACHED = 5;
133 
134         /**
135          * Indicates that the local system policy caused the change, such
136          * as privacy policy, power management policy, permissions, and more.
137          */
138         int REASON_SYSTEM_POLICY = 6;
139 
140         /**
141          * Indicates a protocol specific error. The associated {@link PersistableBundle} should be
142          * consulted for additional information.
143          */
144         int REASON_PROTOCOL_SPECIFIC_ERROR = 7;
145 
146         /**
147          * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
148          * is successful
149          *
150          * @param session the newly opened {@link RangingSession}
151          */
onOpened(@onNull RangingSession session)152         void onOpened(@NonNull RangingSession session);
153 
154         /**
155          * Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}}
156          * fails
157          *
158          * @param reason the failure reason
159          * @param params protocol specific parameters
160          */
onOpenFailed(@eason int reason, @NonNull PersistableBundle params)161         void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params);
162 
163         /**
164          * Invoked when {@link RangingSession#start(PersistableBundle)} is successful
165          * @param sessionInfo session specific parameters from the lower layers
166          */
onStarted(@onNull PersistableBundle sessionInfo)167         void onStarted(@NonNull PersistableBundle sessionInfo);
168 
169         /**
170          * Invoked when {@link RangingSession#start(PersistableBundle)} fails
171          *
172          * @param reason the failure reason
173          * @param params protocol specific parameters
174          */
onStartFailed(@eason int reason, @NonNull PersistableBundle params)175         void onStartFailed(@Reason int reason, @NonNull PersistableBundle params);
176 
177         /**
178          * Invoked when a request to reconfigure the session succeeds
179          *
180          * @param params the updated ranging configuration
181          */
onReconfigured(@onNull PersistableBundle params)182         void onReconfigured(@NonNull PersistableBundle params);
183 
184         /**
185          * Invoked when a request to reconfigure the session fails
186          *
187          * @param reason reason the session failed to be reconfigured
188          * @param params protocol specific failure reasons
189          */
onReconfigureFailed(@eason int reason, @NonNull PersistableBundle params)190         void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params);
191 
192         /**
193          * Invoked when a request to stop the session succeeds
194          *
195          * @param reason reason for the session stop
196          * @param parameters protocol specific parameters related to the stop reason
197          */
onStopped(@eason int reason, @NonNull PersistableBundle parameters)198         void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
199 
200         /**
201          * Invoked when a request to stop the session fails
202          *
203          * @param reason reason the session failed to be stopped
204          * @param params protocol specific failure reasons
205          */
onStopFailed(@eason int reason, @NonNull PersistableBundle params)206         void onStopFailed(@Reason int reason, @NonNull PersistableBundle params);
207 
208        /**
209          * Invoked when session is either closed spontaneously, or per user request via
210          * {@link RangingSession#close()} or {@link AutoCloseable#close()}.
211          *
212          * @param reason reason for the session closure
213          * @param parameters protocol specific parameters related to the close reason
214          */
onClosed(@eason int reason, @NonNull PersistableBundle parameters)215         void onClosed(@Reason int reason, @NonNull PersistableBundle parameters);
216 
217         /**
218          * Called once per ranging interval even when a ranging measurement fails
219          *
220          * @param rangingReport ranging report for this interval's measurements
221          */
onReportReceived(@onNull RangingReport rangingReport)222         void onReportReceived(@NonNull RangingReport rangingReport);
223     }
224 
225     /**
226      * @hide
227      */
RangingSession(Executor executor, Callback callback, IUwbAdapter adapter, SessionHandle sessionHandle)228     public RangingSession(Executor executor, Callback callback, IUwbAdapter adapter,
229             SessionHandle sessionHandle) {
230         mState = State.INIT;
231         mExecutor = executor;
232         mCallback = callback;
233         mAdapter = adapter;
234         mSessionHandle = sessionHandle;
235     }
236 
237     /**
238      * @hide
239      */
isOpen()240     public boolean isOpen() {
241         return mState == State.IDLE || mState == State.ACTIVE;
242     }
243 
244     /**
245      * Begins ranging for the session.
246      *
247      * <p>On successfully starting a ranging session,
248      * {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked.
249      *
250      * <p>On failure to start the session,
251      * {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked.
252      *
253      * @param params configuration parameters for starting the session
254      */
255     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
start(@onNull PersistableBundle params)256     public void start(@NonNull PersistableBundle params) {
257         if (mState != State.IDLE) {
258             throw new IllegalStateException();
259         }
260 
261         try {
262             mAdapter.startRanging(mSessionHandle, params);
263         } catch (RemoteException e) {
264             throw e.rethrowFromSystemServer();
265         }
266     }
267 
268     /**
269      * Attempts to reconfigure the session with the given parameters
270      * <p>This call may be made when the session is open.
271      *
272      * <p>On successfully reconfiguring the session
273      * {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked.
274      *
275      * <p>On failure to reconfigure the session,
276      * {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked.
277      *
278      * @param params the parameters to reconfigure and their new values
279      */
280     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
reconfigure(@onNull PersistableBundle params)281     public void reconfigure(@NonNull PersistableBundle params) {
282         if (mState != State.ACTIVE && mState != State.IDLE) {
283             throw new IllegalStateException();
284         }
285 
286         try {
287             mAdapter.reconfigureRanging(mSessionHandle, params);
288         } catch (RemoteException e) {
289             throw e.rethrowFromSystemServer();
290         }
291     }
292 
293     /**
294      * Stops actively ranging
295      *
296      * <p>A session that has been stopped may be resumed by calling
297      * {@link RangingSession#start(PersistableBundle)} without the need to open a new session.
298      *
299      * <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard
300      * the parameters of the session, or when a session needs to be able to be resumed quickly.
301      *
302      * <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to
303      * completely close the session and allow lower layers of the stack to perform necessarily
304      * cleanup.
305      *
306      * <p>Stopped sessions may be closed by the system at any time. In such a case,
307      * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked.
308      *
309      * <p>On failure to stop the session,
310      * {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked.
311      */
312     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
stop()313     public void stop() {
314         if (mState != State.ACTIVE) {
315             throw new IllegalStateException();
316         }
317 
318         try {
319             mAdapter.stopRanging(mSessionHandle);
320         } catch (RemoteException e) {
321             throw e.rethrowFromSystemServer();
322         }
323     }
324 
325     /**
326      * Close the ranging session
327      *
328      * <p>After calling this function, in order resume ranging, a new {@link RangingSession} must
329      * be opened by calling
330      * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}.
331      *
332      * <p>If this session is currently ranging, it will stop and close the session.
333      * <p>If the session is in the process of being opened, it will attempt to stop the session from
334      * being opened.
335      * <p>If the session is already closed, the registered
336      * {@link Callback#onClosed(int, PersistableBundle)} callback will still be invoked.
337      *
338      * <p>{@link Callback#onClosed(int, PersistableBundle)} will be invoked using the same callback
339      * object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
340      * when the {@link RangingSession} was opened. The callback will be invoked after each call to
341      * {@link #close()}, even if the {@link RangingSession} is already closed.
342      */
343     @Override
344     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
close()345     public void close() {
346         if (mState == State.CLOSED) {
347             mExecutor.execute(() -> mCallback.onClosed(
348                     Callback.REASON_LOCAL_REQUEST, new PersistableBundle()));
349             return;
350         }
351 
352         try {
353             mAdapter.closeRanging(mSessionHandle);
354         } catch (RemoteException e) {
355             throw e.rethrowFromSystemServer();
356         }
357     }
358 
359     /**
360      * @hide
361      */
onRangingOpened()362     public void onRangingOpened() {
363         if (mState == State.CLOSED) {
364             Log.w(TAG, "onRangingOpened invoked for a closed session");
365             return;
366         }
367 
368         mState = State.IDLE;
369         executeCallback(() -> mCallback.onOpened(this));
370     }
371 
372     /**
373      * @hide
374      */
onRangingOpenFailed(@allback.Reason int reason, @NonNull PersistableBundle params)375     public void onRangingOpenFailed(@Callback.Reason int reason,
376             @NonNull PersistableBundle params) {
377         if (mState == State.CLOSED) {
378             Log.w(TAG, "onRangingOpenFailed invoked for a closed session");
379             return;
380         }
381 
382         mState = State.CLOSED;
383         executeCallback(() -> mCallback.onOpenFailed(reason, params));
384     }
385 
386     /**
387      * @hide
388      */
onRangingStarted(@onNull PersistableBundle parameters)389     public void onRangingStarted(@NonNull PersistableBundle parameters) {
390         if (mState == State.CLOSED) {
391             Log.w(TAG, "onRangingStarted invoked for a closed session");
392             return;
393         }
394 
395         mState = State.ACTIVE;
396         executeCallback(() -> mCallback.onStarted(parameters));
397     }
398 
399     /**
400      * @hide
401      */
onRangingStartFailed(@allback.Reason int reason, @NonNull PersistableBundle params)402     public void onRangingStartFailed(@Callback.Reason int reason,
403             @NonNull PersistableBundle params) {
404         if (mState == State.CLOSED) {
405             Log.w(TAG, "onRangingStartFailed invoked for a closed session");
406             return;
407         }
408 
409         executeCallback(() -> mCallback.onStartFailed(reason, params));
410     }
411 
412     /**
413      * @hide
414      */
onRangingReconfigured(@onNull PersistableBundle params)415     public void onRangingReconfigured(@NonNull PersistableBundle params) {
416         if (mState == State.CLOSED) {
417             Log.w(TAG, "onRangingReconfigured invoked for a closed session");
418             return;
419         }
420 
421         executeCallback(() -> mCallback.onReconfigured(params));
422     }
423 
424     /**
425      * @hide
426      */
onRangingReconfigureFailed(@allback.Reason int reason, @NonNull PersistableBundle params)427     public void onRangingReconfigureFailed(@Callback.Reason int reason,
428             @NonNull PersistableBundle params) {
429         if (mState == State.CLOSED) {
430             Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session");
431             return;
432         }
433 
434         executeCallback(() -> mCallback.onReconfigureFailed(reason, params));
435     }
436 
437     /**
438      * @hide
439      */
onRangingStopped(@allback.Reason int reason, @NonNull PersistableBundle params)440     public void onRangingStopped(@Callback.Reason int reason,
441             @NonNull PersistableBundle params) {
442         if (mState == State.CLOSED) {
443             Log.w(TAG, "onRangingStopped invoked for a closed session");
444             return;
445         }
446 
447         mState = State.IDLE;
448         executeCallback(() -> mCallback.onStopped(reason, params));
449     }
450 
451     /**
452      * @hide
453      */
onRangingStopFailed(@allback.Reason int reason, @NonNull PersistableBundle params)454     public void onRangingStopFailed(@Callback.Reason int reason,
455             @NonNull PersistableBundle params) {
456         if (mState == State.CLOSED) {
457             Log.w(TAG, "onRangingStopFailed invoked for a closed session");
458             return;
459         }
460 
461         executeCallback(() -> mCallback.onStopFailed(reason, params));
462     }
463 
464     /**
465      * @hide
466      */
onRangingClosed(@allback.Reason int reason, @NonNull PersistableBundle parameters)467     public void onRangingClosed(@Callback.Reason int reason,
468             @NonNull PersistableBundle parameters) {
469         mState = State.CLOSED;
470         executeCallback(() -> mCallback.onClosed(reason, parameters));
471     }
472 
473     /**
474      * @hide
475      */
onRangingResult(@onNull RangingReport report)476     public void onRangingResult(@NonNull RangingReport report) {
477         if (!isOpen()) {
478             Log.w(TAG, "onRangingResult invoked for non-open session");
479             return;
480         }
481 
482         executeCallback(() -> mCallback.onReportReceived(report));
483     }
484 
485     /**
486      * @hide
487      */
executeCallback(@onNull Runnable runnable)488     private void executeCallback(@NonNull Runnable runnable) {
489         final long identity = Binder.clearCallingIdentity();
490         try {
491             mExecutor.execute(runnable);
492         } finally {
493             Binder.restoreCallingIdentity(identity);
494         }
495     }
496 }
497