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