• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 com.googlecode.android_scripting.facade.uwb;
18 
19 import android.app.Service;
20 import android.content.Context;
21 import android.os.CancellationSignal;
22 import android.os.PersistableBundle;
23 import android.uwb.RangingMeasurement;
24 import android.uwb.RangingReport;
25 import android.uwb.RangingSession;
26 import android.uwb.UwbAddress;
27 import android.uwb.UwbManager;
28 
29 import com.google.uwb.support.ccc.CccOpenRangingParams;
30 import com.google.uwb.support.ccc.CccParams;
31 import com.google.uwb.support.ccc.CccPulseShapeCombo;
32 import com.google.uwb.support.ccc.CccRangingStartedParams;
33 import com.google.uwb.support.fira.FiraOpenSessionParams;
34 import com.google.uwb.support.fira.FiraParams;
35 import com.google.uwb.support.fira.FiraRangingReconfigureParams;
36 import com.googlecode.android_scripting.Log;
37 import com.googlecode.android_scripting.facade.EventFacade;
38 import com.googlecode.android_scripting.facade.FacadeManager;
39 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
40 import com.googlecode.android_scripting.rpc.Rpc;
41 import com.googlecode.android_scripting.rpc.RpcParameter;
42 
43 import org.json.JSONArray;
44 import org.json.JSONException;
45 import org.json.JSONObject;
46 
47 import java.util.Arrays;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.concurrent.Executor;
51 import java.util.concurrent.Executors;
52 
53 /**
54  * SL4A for UwbManager and Ranging APIs.
55  */
56 public class UwbManagerFacade extends RpcReceiver {
57 
58     private static final String TAG = "UwbManagerFacade: ";
59     private final Service mService;
60     private final Context mContext;
61     private final UwbManager mUwbManager;
62     private final Executor mExecutor = Executors.newSingleThreadExecutor();
63     private final EventFacade mEventFacade;
64     private static HashMap<String, RangingSessionCallback> sRangingSessionCallbackMap =
65             new HashMap<String, RangingSessionCallback>();
66     private static HashMap<String, UwbAdapterStateCallback> sUwbAdapterStateCallbackMap =
67             new HashMap<String, UwbAdapterStateCallback>();
68 
69     private enum Event {
70         Invalid(0),
71         Opened(1 << 0),
72         Started(1 << 1),
73         Reconfigured(1 << 2),
74         Stopped(1 << 3),
75         Closed(1 << 4),
76         OpenFailed(1 << 5),
77         StartFailed(1 << 6),
78         ReconfigureFailed(1 << 7),
79         StopFailed(1 << 8),
80         CloseFailed(1 << 9),
81         ReportReceived(1 << 10),
82         EventAll(
83                 1 << 0
84                 | 1 << 1
85                 | 1 << 2
86                 | 1 << 3
87                 | 1 << 4
88                 | 1 << 5
89                 | 1 << 6
90                 | 1 << 7
91                 | 1 << 8
92                 | 1 << 9
93                 | 1 << 10);
94 
95         private int mType;
Event(int type)96         Event(int type) {
97             mType = type;
98         }
getType()99         private int getType() {
100             return mType;
101         }
102     }
103 
104     private static class UwbAdapterStateCallback implements UwbManager.AdapterStateCallback {
105 
106         private final String mId;
107         private final EventFacade mEventFacade;
108 
UwbAdapterStateCallback(EventFacade eventFacade)109         UwbAdapterStateCallback(EventFacade eventFacade) {
110             mId = this.toString();
111             mEventFacade = eventFacade;
112         }
113 
toString(int state)114         public String toString(int state) {
115             switch (state) {
116                 case 1: return "Inactive";
117                 case 2: return "Active";
118                 default: return "Disabled";
119             }
120         }
121 
122         @Override
onStateChanged(int state, int reason)123         public void onStateChanged(int state, int reason) {
124             Log.d(TAG + "UwbAdapterStateCallback#onStateChanged() called");
125             Log.d(TAG + "Adapter state changed reason " + String.valueOf(reason));
126             mEventFacade.postEvent(
127                     UwbConstants.EventUwbAdapterStateCallback,
128                     new UwbEvents.UwbAdapterStateEvent(mId, toString(state)));
129         }
130     }
131 
132 
133     class RangingSessionCallback implements RangingSession.Callback {
134 
135         public RangingSession rangingSession;
136         public PersistableBundle persistableBundle;
137         public PersistableBundle sessionInfo;
138         public RangingReport rangingReport;
139         public String mId;
140 
RangingSessionCallback(int events)141         RangingSessionCallback(int events) {
142             mId = this.toString();
143         }
144 
handleEvent(Event e)145         private void handleEvent(Event e) {
146             Log.d(TAG + "RangingSessionCallback#handleEvent() for " + e.toString());
147             mEventFacade.postEvent(
148                     UwbConstants.EventRangingSessionCallback,
149                     new UwbEvents.RangingSessionEvent(mId, e.toString()));
150         }
151 
152         @Override
onOpened(RangingSession session)153         public void onOpened(RangingSession session) {
154             Log.d(TAG + "RangingSessionCallback#onOpened() called");
155             rangingSession = session;
156             handleEvent(Event.Opened);
157         }
158 
159         @Override
onOpenFailed(@eason int reason, PersistableBundle params)160         public void onOpenFailed(@Reason int reason, PersistableBundle params) {
161             Log.d(TAG + "RangingSessionCallback#onOpenedFailed() called");
162             Log.d(TAG + "OpenFailed reason " + String.valueOf(reason));
163             persistableBundle = params;
164             handleEvent(Event.OpenFailed);
165         }
166 
167         @Override
onStarted(PersistableBundle info)168         public void onStarted(PersistableBundle info) {
169             Log.d(TAG + "RangingSessionCallback#onStarted() called");
170             sessionInfo = info;
171             handleEvent(Event.Started);
172         }
173 
174         @Override
onStartFailed(@eason int reason, PersistableBundle params)175         public void onStartFailed(@Reason int reason, PersistableBundle params) {
176             Log.d(TAG + "RangingSessionCallback#onStartFailed() called");
177             Log.d(TAG + "StartFailed reason " + String.valueOf(reason));
178             persistableBundle = params;
179             handleEvent(Event.StartFailed);
180         }
181 
182         @Override
onReconfigured(PersistableBundle params)183         public void onReconfigured(PersistableBundle params) {
184             Log.d(TAG + "RangingSessionCallback#oniReconfigured() called");
185             persistableBundle = params;
186             handleEvent(Event.Reconfigured);
187         }
188 
189         @Override
onReconfigureFailed(@eason int reason, PersistableBundle params)190         public void onReconfigureFailed(@Reason int reason, PersistableBundle params) {
191             Log.d(TAG + "RangingSessionCallback#onReconfigureFailed() called");
192             Log.d(TAG + "ReconfigureFailed reason " + String.valueOf(reason));
193             persistableBundle = params;
194             handleEvent(Event.ReconfigureFailed);
195         }
196 
197         @Override
onStopped(@eason int reason, PersistableBundle params)198         public void onStopped(@Reason int reason, PersistableBundle params) {
199             Log.d(TAG + "RangingSessionCallback#onStopped() called");
200             Log.d(TAG + "Stopped reason " + String.valueOf(reason));
201             persistableBundle = params;
202             handleEvent(Event.Stopped);
203         }
204 
205         @Override
onStopFailed(@eason int reason, PersistableBundle params)206         public void onStopFailed(@Reason int reason, PersistableBundle params) {
207             Log.d(TAG + "RangingSessionCallback#onStopFailed() called");
208             Log.d(TAG + "StopFailed reason " + String.valueOf(reason));
209             persistableBundle = params;
210             handleEvent(Event.StopFailed);
211         }
212 
213         @Override
onClosed(@eason int reason, PersistableBundle params)214         public void onClosed(@Reason int reason, PersistableBundle params) {
215             Log.d(TAG + "RangingSessionCallback#onClosed() called");
216             Log.d(TAG + "Closed reason " + String.valueOf(reason));
217             persistableBundle = params;
218             handleEvent(Event.Closed);
219         }
220 
221         @Override
onReportReceived(RangingReport report)222         public void onReportReceived(RangingReport report) {
223             Log.d(TAG + "RangingSessionCallback#onReportReceived() called");
224             rangingReport = report;
225             handleEvent(Event.ReportReceived);
226         }
227     }
228 
UwbManagerFacade(FacadeManager manager)229     public UwbManagerFacade(FacadeManager manager) {
230         super(manager);
231         mService = manager.getService();
232         mContext = mService.getBaseContext();
233         mUwbManager = (UwbManager) mService.getSystemService(Context.UWB_SERVICE);
234         mEventFacade = manager.getReceiver(EventFacade.class);
235     }
236 
237     /**
238      * Get Uwb adapter state.
239      */
240     @Rpc(description = "Get Uwb adapter state")
getAdapterState()241     public int getAdapterState() {
242         return mUwbManager.getAdapterState();
243     }
244 
245     /**
246      * Get the UWB state.
247      */
248     @Rpc(description = "Get Uwb state")
isUwbEnabled()249     public boolean isUwbEnabled() {
250         return mUwbManager.isUwbEnabled();
251     }
252 
253     /**
254      * Set Uwb state to enabled or disabled.
255      * @param enabled : boolean - true to enable, false to disable.
256      */
257     @Rpc(description = "Change Uwb state to enabled or disabled")
setUwbEnabled(@pcParametername = "enabled") Boolean enabled)258     public void setUwbEnabled(@RpcParameter(name = "enabled") Boolean enabled) {
259         Log.d(TAG + "Setting Uwb state to " + enabled);
260         mUwbManager.setUwbEnabled(enabled);
261     }
262 
263     /**
264      * Register uwb adapter state callback.
265      */
266     @Rpc(description = "Register uwb adapter state callback")
registerUwbAdapterStateCallback()267     public String registerUwbAdapterStateCallback() {
268         UwbAdapterStateCallback uwbAdapterStateCallback = new UwbAdapterStateCallback(mEventFacade);
269         String key = uwbAdapterStateCallback.mId;
270         sUwbAdapterStateCallbackMap.put(key, uwbAdapterStateCallback);
271         mUwbManager.registerAdapterStateCallback(mExecutor, uwbAdapterStateCallback);
272         return key;
273     }
274 
275     /**
276      * Unregister uwb adapter state callback.
277      */
278     @Rpc(description = "Unregister uwb adapter state callback.")
unregisterUwbAdapterStateCallback(String key)279     public void unregisterUwbAdapterStateCallback(String key) {
280         UwbAdapterStateCallback uwbAdapterStateCallback = sUwbAdapterStateCallbackMap.get(key);
281         mUwbManager.unregisterAdapterStateCallback(uwbAdapterStateCallback);
282         sUwbAdapterStateCallbackMap.remove(key);
283     }
284 
285     /**
286      * Get UWB specification info.
287      */
288     @Rpc(description = "Get Uwb specification info")
getSpecificationInfo()289     public PersistableBundle getSpecificationInfo() {
290         return mUwbManager.getSpecificationInfo();
291     }
292 
convertJSONArrayToByteArray(JSONArray jArray)293     private byte[] convertJSONArrayToByteArray(JSONArray jArray) throws JSONException {
294         if (jArray == null) {
295             return null;
296         }
297         byte[] bArray = new byte[jArray.length()];
298         for (int i = 0; i < jArray.length(); i++) {
299             bArray[i] = (byte) jArray.getInt(i);
300         }
301         return bArray;
302     }
303 
generateFiraRangingReconfigureParams(JSONObject j)304     private FiraRangingReconfigureParams generateFiraRangingReconfigureParams(JSONObject j)
305             throws JSONException {
306         if (j == null) {
307             return null;
308         }
309         FiraRangingReconfigureParams.Builder builder = new FiraRangingReconfigureParams.Builder();
310         if (j.has("action")) {
311             builder.setAction(j.getInt("action"));
312         }
313         if (j.has("addressList")) {
314             JSONArray jArray = j.getJSONArray("addressList");
315             UwbAddress[] addressList = new UwbAddress[jArray.length()];
316             for (int i = 0; i < jArray.length(); i++) {
317                 addressList[i] = UwbAddress.fromBytes(
318                         convertJSONArrayToByteArray(jArray.getJSONArray(i)));
319             }
320             builder.setAddressList(addressList);
321         }
322         return builder.build();
323     }
324 
generateCccRangingStartedParams(JSONObject j)325     private CccRangingStartedParams generateCccRangingStartedParams(JSONObject j)
326             throws JSONException {
327         if (j == null) {
328             return null;
329         }
330         CccRangingStartedParams.Builder builder = new CccRangingStartedParams.Builder();
331         if (j.has("stsIndex")) {
332             builder.setStartingStsIndex(j.getInt("stsIndex"));
333         }
334         if (j.has("uwbTime")) {
335             builder.setUwbTime0(j.getInt("uwbTime"));
336         }
337         if (j.has("hopModeKey")) {
338             builder.setHopModeKey(j.getInt("hopModeKey"));
339         }
340         if (j.has("syncCodeIndex")) {
341             builder.setSyncCodeIndex(j.getInt("syncCodeIndex"));
342         }
343         if (j.has("ranMultiplier")) {
344             builder.setRanMultiplier(j.getInt("ranMultiplier"));
345         }
346 
347         return builder.build();
348     }
349 
generateCccOpenRangingParams(JSONObject j)350     private CccOpenRangingParams generateCccOpenRangingParams(JSONObject j) throws JSONException {
351         if (j == null) {
352             return null;
353         }
354         CccOpenRangingParams.Builder builder = new CccOpenRangingParams.Builder();
355         builder.setProtocolVersion(CccParams.PROTOCOL_VERSION_1_0);
356         if (j.has("sessionId")) {
357             builder.setSessionId(j.getInt("sessionId"));
358         }
359         if (j.has("uwbConfig")) {
360             builder.setUwbConfig(j.getInt("uwbConfig"));
361         }
362         if (j.has("ranMultiplier")) {
363             builder.setRanMultiplier(j.getInt("ranMultiplier"));
364         }
365         if (j.has("channel")) {
366             builder.setChannel(j.getInt("channel"));
367         }
368         if (j.has("chapsPerSlot")) {
369             builder.setNumChapsPerSlot(j.getInt("chapsPerSlot"));
370         }
371         if (j.has("responderNodes")) {
372             builder.setNumResponderNodes(j.getInt("responderNodes"));
373         }
374         if (j.has("slotsPerRound")) {
375             builder.setNumSlotsPerRound(j.getInt("slotsPerRound"));
376         }
377         if (j.has("hoppingMode")) {
378             builder.setHoppingConfigMode(j.getInt("hoppingMode"));
379         }
380         if (j.has("hoppingSequence")) {
381             builder.setHoppingSequence(j.getInt("hoppingSequence"));
382         }
383         if (j.has("syncCodeIndex")) {
384             builder.setSyncCodeIndex(j.getInt("syncCodeIndex"));
385         }
386         if (j.has("pulseShapeCombo")) {
387             JSONObject pulseShapeCombo = j.getJSONObject("pulseShapeCombo");
388             builder.setPulseShapeCombo(new CccPulseShapeCombo(
389                     pulseShapeCombo.getInt("pulseShapeComboTx"),
390                     pulseShapeCombo.getInt("pulseShapeComboRx")));
391         }
392 
393         return builder.build();
394     }
395 
generateFiraOpenSessionParams(JSONObject j)396     private FiraOpenSessionParams generateFiraOpenSessionParams(JSONObject j) throws JSONException {
397         if (j == null) {
398             return null;
399         }
400         FiraOpenSessionParams.Builder builder = new FiraOpenSessionParams.Builder();
401         builder.setProtocolVersion(FiraParams.PROTOCOL_VERSION_1_1);
402         if (j.has("sessionId")) {
403             builder.setSessionId(j.getInt("sessionId"));
404         }
405         if (j.has("deviceType")) {
406             builder.setDeviceType(j.getInt("deviceType"));
407         }
408         if (j.has("deviceRole")) {
409             builder.setDeviceRole(j.getInt("deviceRole"));
410         }
411         if (j.has("rangingRoundUsage")) {
412             builder.setRangingRoundUsage(j.getInt("rangingRoundUsage"));
413         }
414         if (j.has("multiNodeMode")) {
415             builder.setMultiNodeMode(j.getInt("multiNodeMode"));
416         }
417         if (j.has("deviceAddress")) {
418             JSONArray jArray = j.getJSONArray("deviceAddress");
419             byte[] bArray = convertJSONArrayToByteArray(jArray);
420             UwbAddress deviceAddress = UwbAddress.fromBytes(bArray);
421             builder.setDeviceAddress(deviceAddress);
422         }
423         if (j.has("destinationAddresses")) {
424             JSONArray jArray = j.getJSONArray("destinationAddresses");
425             UwbAddress[] destinationUwbAddresses = new UwbAddress[jArray.length()];
426             for (int i = 0; i < jArray.length(); i++) {
427                 destinationUwbAddresses[i] = UwbAddress.fromBytes(
428                         convertJSONArrayToByteArray(jArray.getJSONArray(i)));
429             }
430             builder.setDestAddressList(Arrays.asList(destinationUwbAddresses));
431         }
432         if (j.has("initiationTimeMs")) {
433             builder.setInitiationTime(j.getInt("initiationTimeMs"));
434         }
435         if (j.has("slotDurationRstu")) {
436             builder.setSlotDurationRstu(j.getInt("slotDurationRstu"));
437         }
438         if (j.has("slotsPerRangingRound")) {
439             builder.setSlotsPerRangingRound(j.getInt("slotsPerRangingRound"));
440         }
441         if (j.has("rangingIntervalMs")) {
442             builder.setRangingIntervalMs(j.getInt("rangingIntervalMs"));
443         }
444         if (j.has("blockStrideLength")) {
445             builder.setBlockStrideLength(j.getInt("blockStrideLength"));
446         }
447         if (j.has("hoppingMode")) {
448             builder.setHoppingMode(j.getInt("hoppingMode"));
449         }
450         if (j.has("maxRangingRoundRetries")) {
451             builder.setMaxRangingRoundRetries(j.getInt("maxRangingRoundRetries"));
452         }
453         if (j.has("sessionPriority")) {
454             builder.setSessionPriority(j.getInt("sessionPriority"));
455         }
456         if (j.has("macAddressMode")) {
457             builder.setMacAddressMode(j.getInt("macAddressMode"));
458         }
459         if (j.has("inBandTerminationAttemptCount")) {
460             builder.setInBandTerminationAttemptCount(j.getInt("inBandTerminationAttemptCount"));
461         }
462         if (j.has("channel")) {
463             builder.setChannelNumber(j.getInt("channel"));
464         }
465         if (j.has("preamble")) {
466             builder.setPreambleCodeIndex(j.getInt("preamble"));
467         }
468         if (j.has("vendorId")) {
469             JSONArray jArray = j.getJSONArray("vendorId");
470             byte[] bArray = convertJSONArrayToByteArray(jArray);
471             builder.setVendorId(bArray);
472         }
473         if (j.has("staticStsIV")) {
474             JSONArray jArray = j.getJSONArray("staticStsIV");
475             byte[] bArray = convertJSONArrayToByteArray(jArray);
476             builder.setStaticStsIV(bArray);
477         }
478         if (j.has("aoaResultRequest")) {
479             builder.setAoaResultRequest(j.getInt("aoaResultRequest"));
480         }
481 
482         return builder.build();
483     }
484 
485     /**
486      * Open UWB ranging session.
487      */
488     @Rpc(description = "Open UWB ranging session")
openRangingSession(@pcParametername = "config") JSONObject config)489     public String openRangingSession(@RpcParameter(name = "config") JSONObject config)
490             throws JSONException {
491         RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(
492                 Event.EventAll.getType());
493         FiraOpenSessionParams params = generateFiraOpenSessionParams(config);
494         CancellationSignal cancellationSignal = mUwbManager.openRangingSession(
495                 params.toBundle(), mExecutor, rangingSessionCallback);
496         String key = rangingSessionCallback.mId;
497         sRangingSessionCallbackMap.put(key, rangingSessionCallback);
498         return key;
499     }
500 
501     /**
502      * Open CCC UWB ranging session.
503      */
504     @Rpc(description = "Open CCC UWB ranging session")
openCccRangingSession(@pcParametername = "config") JSONObject config)505     public String openCccRangingSession(@RpcParameter(name = "config") JSONObject config)
506             throws JSONException {
507         RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(
508                 Event.EventAll.getType());
509         CccOpenRangingParams params = generateCccOpenRangingParams(config);
510         CancellationSignal cancellationSignal = mUwbManager.openRangingSession(
511                 params.toBundle(), mExecutor, rangingSessionCallback);
512         String key = rangingSessionCallback.mId;
513         sRangingSessionCallbackMap.put(key, rangingSessionCallback);
514         return key;
515     }
516 
517     /**
518      * Start UWB ranging.
519      */
520     @Rpc(description = "Start UWB ranging")
startRangingSession(String key)521     public void startRangingSession(String key) {
522         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
523         rangingSessionCallback.rangingSession.start(new PersistableBundle());
524     }
525 
526     /**
527      * Start CCC UWB ranging.
528      */
529     @Rpc(description = "Start CCC UWB ranging")
startCccRangingSession(String key, JSONObject config)530     public void startCccRangingSession(String key, JSONObject config) throws JSONException {
531         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
532         CccRangingStartedParams params = generateCccRangingStartedParams(config);
533         rangingSessionCallback.rangingSession.start(params.toBundle());
534     }
535 
536     /**
537      * Reconfigures UWB ranging session.
538      */
539     @Rpc(description = "Reconfigure UWB ranging session")
reconfigureRangingSession(String key, @RpcParameter(name = "config") JSONObject config)540     public void reconfigureRangingSession(String key,
541             @RpcParameter(name = "config") JSONObject config) throws JSONException {
542         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
543         FiraRangingReconfigureParams params = generateFiraRangingReconfigureParams(config);
544         rangingSessionCallback.rangingSession.reconfigure(params.toBundle());
545     }
546 
getRangingMeasurement(String key, JSONArray jArray)547     private RangingMeasurement getRangingMeasurement(String key, JSONArray jArray)
548             throws JSONException {
549         byte[] bArray = convertJSONArrayToByteArray(jArray);
550         UwbAddress peerAddress = UwbAddress.fromBytes(bArray);
551         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
552         List<RangingMeasurement> rangingMeasurements =
553                 rangingSessionCallback.rangingReport.getMeasurements();
554         for (RangingMeasurement r: rangingMeasurements) {
555             if (r.getStatus() == RangingMeasurement.RANGING_STATUS_SUCCESS
556                     && r.getRemoteDeviceAddress().equals(peerAddress)) {
557                 Log.d(TAG + "Found peer " + peerAddress.toString());
558                 return r;
559             }
560         }
561         Log.w(TAG + "Invalid ranging status or peer not found.");
562         return null;
563     }
564 
565     /**
566      * Find if UWB peer is found.
567      */
568     @Rpc(description = "Find if UWB peer is found")
isUwbPeerFound(String key, JSONArray jArray)569     public boolean isUwbPeerFound(String key, JSONArray jArray) throws JSONException {
570         return getRangingMeasurement(key, jArray) != null;
571     }
572 
573     /**
574      * Get UWB distance measurement.
575      */
576     @Rpc(description = "Get UWB ranging distance measurement with peer.")
getDistanceMeasurement(String key, JSONArray jArray)577     public double getDistanceMeasurement(String key, JSONArray jArray) throws JSONException {
578         RangingMeasurement rangingMeasurement = getRangingMeasurement(key, jArray);
579         if (rangingMeasurement == null || rangingMeasurement.getDistanceMeasurement() == null) {
580             throw new NullPointerException("Cannot get Distance Measurement on null object.");
581         }
582         return rangingMeasurement.getDistanceMeasurement().getMeters();
583     }
584 
585     /**
586      * Get angle of arrival azimuth measurement.
587      */
588     @Rpc(description = "Get UWB AoA Azimuth measurement.")
getAoAAzimuthMeasurement(String key, JSONArray jArray)589     public double getAoAAzimuthMeasurement(String key, JSONArray jArray) throws JSONException {
590         RangingMeasurement rangingMeasurement = getRangingMeasurement(key, jArray);
591         if (rangingMeasurement == null
592                 || rangingMeasurement.getAngleOfArrivalMeasurement() == null
593                 || rangingMeasurement.getAngleOfArrivalMeasurement().getAzimuth() == null) {
594             throw new NullPointerException("Cannot get AoA azimuth measurement on null object.");
595         }
596         return rangingMeasurement.getAngleOfArrivalMeasurement().getAzimuth().getRadians();
597     }
598 
599     /**
600      * Get angle of arrival altitude measurement.
601      */
602     @Rpc(description = "Get UWB AoA Altitude measurement.")
getAoAAltitudeMeasurement(String key, JSONArray jArray)603     public double getAoAAltitudeMeasurement(String key, JSONArray jArray) throws JSONException {
604         RangingMeasurement rangingMeasurement = getRangingMeasurement(key, jArray);
605         if (rangingMeasurement == null
606                 || rangingMeasurement.getAngleOfArrivalMeasurement() == null
607                 || rangingMeasurement.getAngleOfArrivalMeasurement().getAltitude() == null) {
608             throw new NullPointerException("Cannot get AoA altitude measurement on null object.");
609         }
610         return rangingMeasurement.getAngleOfArrivalMeasurement().getAltitude().getRadians();
611     }
612 
613     /**
614      * Stop UWB ranging.
615      */
616     @Rpc(description = "Stop UWB ranging")
stopRangingSession(String key)617     public void stopRangingSession(String key) {
618         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
619         rangingSessionCallback.rangingSession.stop();
620     }
621 
622     /**
623      * Close UWB ranging session.
624      */
625     @Rpc(description = "Close UWB ranging session")
closeRangingSession(String key)626     public void closeRangingSession(String key) {
627         RangingSessionCallback rangingSessionCallback = sRangingSessionCallbackMap.get(key);
628         rangingSessionCallback.rangingSession.close();
629         sRangingSessionCallbackMap.remove(key);
630     }
631 
632     @Override
shutdown()633     public void shutdown() {}
634 }
635