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.android.server.uwb; 18 19 import static android.uwb.UwbManager.MESSAGE_TYPE_COMMAND; 20 21 import static com.android.server.uwb.data.UwbUciConstants.FIRA_VERSION_MAJOR_2; 22 import static com.android.server.uwb.data.UwbUciConstants.STATUS_CODE_OK; 23 24 import android.annotation.NonNull; 25 import android.content.AttributionSource; 26 import android.content.Context; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.PersistableBundle; 31 import android.os.PowerManager; 32 import android.os.RemoteCallbackList; 33 import android.os.RemoteException; 34 import android.os.SystemClock; 35 import android.os.Trace; 36 import android.util.ArraySet; 37 import android.util.Log; 38 import android.util.Pair; 39 import android.uwb.IOnUwbActivityEnergyInfoListener; 40 import android.uwb.IUwbAdapterStateCallbacks; 41 import android.uwb.IUwbOemExtensionCallback; 42 import android.uwb.IUwbRangingCallbacks; 43 import android.uwb.IUwbVendorUciCallback; 44 import android.uwb.RangingChangeReason; 45 import android.uwb.SessionHandle; 46 import android.uwb.StateChangeReason; 47 import android.uwb.UwbActivityEnergyInfo; 48 import android.uwb.UwbAddress; 49 import android.uwb.UwbManager.AdapterStateCallback; 50 51 import androidx.annotation.Nullable; 52 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.server.uwb.data.UwbUciConstants; 55 import com.android.server.uwb.data.UwbVendorUciResponse; 56 import com.android.server.uwb.info.UwbPowerStats; 57 import com.android.server.uwb.jni.INativeUwbManager; 58 import com.android.server.uwb.jni.NativeUwbManager; 59 60 import com.google.uwb.support.base.Params; 61 import com.google.uwb.support.ccc.CccOpenRangingParams; 62 import com.google.uwb.support.ccc.CccParams; 63 import com.google.uwb.support.ccc.CccRangingReconfiguredParams; 64 import com.google.uwb.support.ccc.CccStartRangingParams; 65 import com.google.uwb.support.fira.FiraControleeParams; 66 import com.google.uwb.support.fira.FiraOpenSessionParams; 67 import com.google.uwb.support.fira.FiraParams; 68 import com.google.uwb.support.fira.FiraRangingReconfigureParams; 69 import com.google.uwb.support.generic.GenericParams; 70 import com.google.uwb.support.generic.GenericSpecificationParams; 71 import com.google.uwb.support.oemextension.DeviceStatus; 72 import com.google.uwb.support.profile.UuidBundleWrapper; 73 74 import java.io.FileDescriptor; 75 import java.io.PrintWriter; 76 import java.util.HashMap; 77 import java.util.Map; 78 import java.util.Optional; 79 import java.util.Set; 80 import java.util.concurrent.ExecutionException; 81 import java.util.concurrent.FutureTask; 82 import java.util.concurrent.TimeoutException; 83 84 /** 85 * Core UWB stack. 86 */ 87 public class UwbServiceCore implements INativeUwbManager.DeviceNotification, 88 INativeUwbManager.VendorNotification, UwbCountryCode.CountryCodeChangedListener { 89 private static final String TAG = "UwbServiceCore"; 90 91 @VisibleForTesting 92 public static final int TASK_ENABLE = 1; 93 @VisibleForTesting 94 public static final int TASK_DISABLE = 2; 95 @VisibleForTesting 96 public static final int TASK_RESTART = 3; 97 @VisibleForTesting 98 public static final int TASK_GET_POWER_STATS = 4; 99 100 @VisibleForTesting 101 public static final int WATCHDOG_MS = 10000; 102 private static final int SEND_VENDOR_CMD_TIMEOUT_MS = 10000; 103 104 private boolean mIsDiagnosticsEnabled = false; 105 private byte mDiagramsFrameReportsFieldsFlags = 0; 106 107 private final PowerManager.WakeLock mUwbWakeLock; 108 private final Context mContext; 109 private final RemoteCallbackList<IUwbAdapterStateCallbacks> 110 mAdapterStateCallbacksList = new RemoteCallbackList<>(); 111 private final UwbTask mUwbTask; 112 113 private final UwbSessionManager mSessionManager; 114 private final UwbConfigurationManager mConfigurationManager; 115 private final NativeUwbManager mNativeUwbManager; 116 private final UwbMetrics mUwbMetrics; 117 private final UwbCountryCode mUwbCountryCode; 118 private final UwbInjector mUwbInjector; 119 private final Map<String, /* @UwbManager.AdapterStateCallback.State */ Integer> 120 mChipIdToStateMap; 121 private @StateChangeReason int mLastAdapterStateChangedReason = StateChangeReason.UNKNOWN; 122 private @AdapterStateCallback.State int mLastAdapterStateNotification = -1; 123 private IUwbVendorUciCallback mCallBack = null; 124 private IUwbOemExtensionCallback mOemExtensionCallback = null; 125 private final Handler mHandler; 126 private GenericSpecificationParams mCachedSpecificationParams; 127 private final Set<InitializationFailureListener> mListeners = new ArraySet<>(); 128 UwbServiceCore(Context uwbApplicationContext, NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics, UwbCountryCode uwbCountryCode, UwbSessionManager uwbSessionManager, UwbConfigurationManager uwbConfigurationManager, UwbInjector uwbInjector, Looper serviceLooper)129 public UwbServiceCore(Context uwbApplicationContext, NativeUwbManager nativeUwbManager, 130 UwbMetrics uwbMetrics, UwbCountryCode uwbCountryCode, 131 UwbSessionManager uwbSessionManager, UwbConfigurationManager uwbConfigurationManager, 132 UwbInjector uwbInjector, Looper serviceLooper) { 133 mContext = uwbApplicationContext; 134 135 Log.d(TAG, "Starting Uwb"); 136 137 mUwbWakeLock = mContext.getSystemService(PowerManager.class).newWakeLock( 138 PowerManager.PARTIAL_WAKE_LOCK, "UwbServiceCore:mUwbWakeLock"); 139 140 mNativeUwbManager = nativeUwbManager; 141 142 mNativeUwbManager.setDeviceListener(this); 143 mNativeUwbManager.setVendorListener(this); 144 mUwbMetrics = uwbMetrics; 145 mUwbCountryCode = uwbCountryCode; 146 mUwbCountryCode.addListener(this); 147 mSessionManager = uwbSessionManager; 148 mConfigurationManager = uwbConfigurationManager; 149 mUwbInjector = uwbInjector; 150 151 mChipIdToStateMap = new HashMap<>(); 152 mUwbInjector.getMultichipData().setOnInitializedListener( 153 () -> { 154 for (String chipId : mUwbInjector.getMultichipData().getChipIds()) { 155 updateState(AdapterStateCallback.STATE_DISABLED, chipId); 156 } 157 }); 158 159 mUwbTask = new UwbTask(serviceLooper); 160 mHandler = new Handler(serviceLooper); 161 } 162 163 /** 164 * Interface for external classes to listen for any initialization failures. 165 * Added to avoid introducing circular dependency between UwbServiceCore & UwbServiceImpl. 166 */ 167 public interface InitializationFailureListener { onFailure()168 void onFailure(); 169 } 170 addInitializationFailureListener(@onNull InitializationFailureListener listener)171 public void addInitializationFailureListener(@NonNull InitializationFailureListener listener) { 172 mListeners.add(listener); 173 } removeInitializationFailureListener( @onNull InitializationFailureListener listener)174 public void removeInitializationFailureListener( 175 @NonNull InitializationFailureListener listener) { 176 mListeners.remove(listener); 177 } 178 getHandler()179 public Handler getHandler() { 180 return mHandler; 181 } 182 isOemExtensionCbRegistered()183 public boolean isOemExtensionCbRegistered() { 184 return mOemExtensionCallback != null; 185 } 186 getOemExtensionCallback()187 public IUwbOemExtensionCallback getOemExtensionCallback() { 188 return mOemExtensionCallback; 189 } 190 updateState(int state, String chipId)191 private void updateState(int state, String chipId) { 192 Log.d(TAG, "updateState(): state=" + state + ", chipId=" + chipId); 193 synchronized (UwbServiceCore.this) { 194 mChipIdToStateMap.put(chipId, state); 195 Log.d(TAG, "chipIdToStateMap = " + mChipIdToStateMap); 196 } 197 } 198 isUwbEnabled()199 private boolean isUwbEnabled() { 200 synchronized (UwbServiceCore.this) { 201 return getInternalAdapterState() != AdapterStateCallback.STATE_DISABLED; 202 } 203 } 204 getDeviceStateString(int state)205 String getDeviceStateString(int state) { 206 String ret = ""; 207 switch (state) { 208 case UwbUciConstants.DEVICE_STATE_OFF: 209 ret = "OFF"; 210 break; 211 case UwbUciConstants.DEVICE_STATE_READY: 212 ret = "READY"; 213 break; 214 case UwbUciConstants.DEVICE_STATE_ACTIVE: 215 ret = "ACTIVE"; 216 break; 217 case UwbUciConstants.DEVICE_STATE_ERROR: 218 ret = "ERROR"; 219 break; 220 } 221 return ret; 222 } 223 224 @Override onVendorUciNotificationReceived(int gid, int oid, byte[] payload)225 public void onVendorUciNotificationReceived(int gid, int oid, byte[] payload) { 226 Log.i(TAG, "onVendorUciNotificationReceived"); 227 if (mCallBack != null) { 228 try { 229 mCallBack.onVendorNotificationReceived(gid, oid, payload); 230 } catch (RemoteException e) { 231 Log.e(TAG, "Failed to send vendor notification", e); 232 } 233 } 234 } 235 236 @Override onDeviceStatusNotificationReceived(int deviceState, String chipId)237 public void onDeviceStatusNotificationReceived(int deviceState, String chipId) { 238 Log.d(TAG, "onDeviceStatusNotificationReceived(): deviceState = " + deviceState 239 + ", current country code = " + mUwbCountryCode.getCountryCode()); 240 241 // If error status is received, toggle UWB off to reset stack state. 242 // TODO(b/227488208): Should we try to restart (like wifi) instead? 243 if (!mUwbInjector.getMultichipData().getChipIds().contains(chipId)) { 244 Log.e(TAG, "onDeviceStatusNotificationReceived with invalid chipId " + chipId 245 + ". Ignoring..."); 246 return; 247 } 248 249 if ((byte) deviceState == UwbUciConstants.DEVICE_STATE_ERROR) { 250 Log.e(TAG, "Error device status received. Restarting..."); 251 mUwbMetrics.incrementDeviceStatusErrorCount(); 252 takBugReportAfterDeviceError("UWB Bugreport: restarting UWB due to device error"); 253 mUwbTask.execute(TASK_RESTART); 254 oemExtensionDeviceStatusUpdate(deviceState, chipId); 255 return; 256 } 257 258 updateDeviceState(deviceState, chipId); 259 260 mUwbTask.computeAndNotifyAdapterStateChange( 261 getReasonFromDeviceState(deviceState), 262 mUwbCountryCode.getCountryCode(), 263 Optional.empty()); 264 } 265 updateDeviceState(int deviceState, String chipId)266 void updateDeviceState(int deviceState, String chipId) { 267 Log.i(TAG, "updateDeviceState(): deviceState = " + getDeviceStateString(deviceState) 268 + ", current internal adapter state = " + getInternalAdapterState()); 269 270 oemExtensionDeviceStatusUpdate(deviceState, chipId); 271 updateState(getAdapterStateFromDeviceState(deviceState), chipId); 272 } 273 oemExtensionDeviceStatusUpdate(int deviceState, String chipId)274 void oemExtensionDeviceStatusUpdate(int deviceState, String chipId) { 275 if (mOemExtensionCallback != null) { 276 PersistableBundle deviceStateBundle = new DeviceStatus.Builder() 277 .setDeviceState(deviceState) 278 .setChipId(chipId) 279 .build() 280 .toBundle(); 281 try { 282 mOemExtensionCallback.onDeviceStatusNotificationReceived(deviceStateBundle); 283 } catch (RemoteException e) { 284 Log.e(TAG, "Failed to send status notification to oem", e); 285 } 286 } 287 } 288 notifyAdapterState(int adapterState, int reason)289 void notifyAdapterState(int adapterState, int reason) { 290 // Check if the current adapter state is same as the state in the last adapter state 291 // notification, to avoid sending extra onAdapterStateChanged() notifications. For example, 292 // this can happen when UWB is toggled on and a valid country code is already set. 293 if (mLastAdapterStateNotification == adapterState 294 && mLastAdapterStateChangedReason == reason) { 295 return; 296 } 297 Log.d(TAG, "notifyAdapterState(): adapterState = " + adapterState + ", reason = " + reason); 298 299 synchronized (mAdapterStateCallbacksList) { 300 if (mAdapterStateCallbacksList.getRegisteredCallbackCount() > 0) { 301 final int count = mAdapterStateCallbacksList.beginBroadcast(); 302 for (int i = 0; i < count; i++) { 303 try { 304 mAdapterStateCallbacksList.getBroadcastItem(i) 305 .onAdapterStateChanged(adapterState, reason); 306 } catch (RemoteException e) { 307 Log.e(TAG, "onAdapterStateChanged is failed"); 308 } 309 } 310 mAdapterStateCallbacksList.finishBroadcast(); 311 } 312 } 313 314 mLastAdapterStateNotification = adapterState; 315 mLastAdapterStateChangedReason = reason; 316 } 317 getAdapterStateFromDeviceState(int deviceState)318 int getAdapterStateFromDeviceState(int deviceState) { 319 int adapterState = AdapterStateCallback.STATE_DISABLED; 320 if (deviceState == UwbUciConstants.DEVICE_STATE_OFF) { 321 adapterState = AdapterStateCallback.STATE_DISABLED; 322 } else if (deviceState == UwbUciConstants.DEVICE_STATE_READY) { 323 adapterState = AdapterStateCallback.STATE_ENABLED_INACTIVE; 324 } else if (deviceState == UwbUciConstants.DEVICE_STATE_ACTIVE) { 325 adapterState = AdapterStateCallback.STATE_ENABLED_ACTIVE; 326 } 327 return adapterState; 328 } 329 getReasonFromDeviceState(int deviceState)330 int getReasonFromDeviceState(int deviceState) { 331 int reason = StateChangeReason.UNKNOWN; 332 if (deviceState == UwbUciConstants.DEVICE_STATE_OFF) { 333 reason = StateChangeReason.SYSTEM_POLICY; 334 } else if (deviceState == UwbUciConstants.DEVICE_STATE_READY) { 335 reason = StateChangeReason.SYSTEM_POLICY; 336 } else if (deviceState == UwbUciConstants.DEVICE_STATE_ACTIVE) { 337 reason = StateChangeReason.SESSION_STARTED; 338 } 339 return reason; 340 } 341 342 @Override onCoreGenericErrorNotificationReceived(int status, String chipId)343 public void onCoreGenericErrorNotificationReceived(int status, String chipId) { 344 if (!mUwbInjector.getMultichipData().getChipIds().contains(chipId)) { 345 Log.e(TAG, "onCoreGenericErrorNotificationReceived with invalid chipId " 346 + chipId + ". Ignoring..."); 347 return; 348 } 349 Log.e(TAG, "onCoreGenericErrorNotificationReceived status = " + status); 350 mUwbMetrics.incrementUciGenericErrorCount(); 351 } 352 353 @Override onCountryCodeChanged(@ullable String countryCode)354 public void onCountryCodeChanged(@Nullable String countryCode) { 355 Log.i(TAG, "Received onCountryCodeChanged() with countryCode = " + countryCode); 356 357 // Notify the current UWB adapter state. For example: 358 // - If UWB was earlier enabled and at that time the country code was not valid (so 359 // STATE_DISABLED was notified), can now notify STATE_ENABLED_INACTIVE. 360 // - If UWB is in STATE_ENABLED_INACTIVE and country code is no longer valid, should 361 // notify STATE_DISABLED. 362 mUwbTask.computeAndNotifyAdapterStateChange( 363 getReasonFromDeviceState(getInternalAdapterState()), 364 countryCode, 365 Optional.empty()); 366 Log.d(TAG, "Resetting cached specifications"); 367 mCachedSpecificationParams = null; 368 } 369 registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)370 public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks) 371 throws RemoteException { 372 synchronized (mAdapterStateCallbacksList) { 373 mAdapterStateCallbacksList.register(adapterStateCallbacks); 374 } 375 376 int adapterState = getAdapterState(); 377 Log.d(TAG, "registerAdapterStateCallbacks(): notify adapterState = " + adapterState 378 + ", reason = " + mLastAdapterStateChangedReason); 379 // We have a new listener being registered (there is no UWB event), so we send the current 380 // adapter state with the last known StateChangeReason. 381 adapterStateCallbacks.onAdapterStateChanged(adapterState, mLastAdapterStateChangedReason); 382 } 383 unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks callbacks)384 public void unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks callbacks) { 385 synchronized (mAdapterStateCallbacksList) { 386 mAdapterStateCallbacksList.unregister(callbacks); 387 } 388 } 389 registerVendorExtensionCallback(IUwbVendorUciCallback callbacks)390 public void registerVendorExtensionCallback(IUwbVendorUciCallback callbacks) { 391 Log.e(TAG, "Register the callback"); 392 mCallBack = callbacks; 393 } 394 unregisterVendorExtensionCallback(IUwbVendorUciCallback callbacks)395 public void unregisterVendorExtensionCallback(IUwbVendorUciCallback callbacks) { 396 Log.e(TAG, "Unregister the callback"); 397 mCallBack = null; 398 } 399 registerOemExtensionCallback(IUwbOemExtensionCallback callback)400 public void registerOemExtensionCallback(IUwbOemExtensionCallback callback) { 401 if (isOemExtensionCbRegistered()) { 402 Log.w(TAG, "Oem extension callback being re-registered"); 403 } 404 Log.e(TAG, "Register Oem Extension callback"); 405 mOemExtensionCallback = callback; 406 } 407 unregisterOemExtensionCallback(IUwbOemExtensionCallback callback)408 public void unregisterOemExtensionCallback(IUwbOemExtensionCallback callback) { 409 Log.e(TAG, "Unregister Oem Extension callback"); 410 mOemExtensionCallback = null; 411 } 412 413 /** 414 * Get cached specification params 415 */ getCachedSpecificationParams(String chipId)416 public GenericSpecificationParams getCachedSpecificationParams(String chipId) { 417 if (mCachedSpecificationParams != null) return mCachedSpecificationParams; 418 // If nothing in cache, populate it. 419 getSpecificationInfo(chipId); 420 return mCachedSpecificationParams; 421 } 422 423 /** 424 * Get specification info 425 */ getSpecificationInfo(String chipId)426 public PersistableBundle getSpecificationInfo(String chipId) { 427 if (!isUwbEnabled()) { 428 throw new IllegalStateException("Uwb is not enabled"); 429 } 430 Trace.beginSection("UWB#getSpecificationInfo"); 431 // TODO(b/211445008): Consolidate to a single uwb thread. 432 Pair<Integer, GenericSpecificationParams> specificationParams = 433 mConfigurationManager.getCapsInfo( 434 GenericParams.PROTOCOL_NAME, GenericSpecificationParams.class, chipId); 435 Trace.endSection(); 436 if (specificationParams.first != UwbUciConstants.STATUS_CODE_OK 437 || specificationParams.second == null) { 438 Log.e(TAG, "Failed to retrieve specification params"); 439 return new PersistableBundle(); 440 } 441 mCachedSpecificationParams = specificationParams.second; 442 return specificationParams.second.toBundle(); 443 } 444 getTimestampResolutionNanos()445 public long getTimestampResolutionNanos() { 446 return mNativeUwbManager.getTimestampResolutionNanos(); 447 } 448 449 /** Set whether diagnostics is enabled and set enabled fields */ enableDiagnostics(boolean enabled, byte flags)450 public void enableDiagnostics(boolean enabled, byte flags) { 451 this.mIsDiagnosticsEnabled = enabled; 452 this.mDiagramsFrameReportsFieldsFlags = flags; 453 } 454 openRanging( AttributionSource attributionSource, SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks, PersistableBundle params, String chipId)455 public void openRanging( 456 AttributionSource attributionSource, 457 SessionHandle sessionHandle, 458 IUwbRangingCallbacks rangingCallbacks, 459 PersistableBundle params, 460 String chipId) throws RemoteException { 461 if (!isUwbEnabled()) { 462 throw new IllegalStateException("Uwb is not enabled"); 463 } 464 int sessionId = 0; 465 int sessionType = 0; 466 467 if (UuidBundleWrapper.isUuidBundle(params)) { 468 UuidBundleWrapper uuidBundleWrapper = UuidBundleWrapper.fromBundle(params); 469 mUwbInjector.getProfileManager().activateProfile( 470 attributionSource, 471 sessionHandle, 472 uuidBundleWrapper.getServiceInstanceID().get(), 473 rangingCallbacks, 474 chipId 475 ); 476 } else if (FiraParams.isCorrectProtocol(params)) { 477 FiraOpenSessionParams.Builder builder = 478 new FiraOpenSessionParams.Builder(FiraOpenSessionParams.fromBundle(params)); 479 if (getCachedSpecificationParams(chipId) 480 .getFiraSpecificationParams().hasRssiReportingSupport()) { 481 builder.setIsRssiReportingEnabled(true); 482 } 483 if (this.mIsDiagnosticsEnabled && getCachedSpecificationParams(chipId) 484 .getFiraSpecificationParams().hasDiagnosticsSupport()) { 485 builder.setIsDiagnosticsEnabled(true); 486 builder.setDiagramsFrameReportsFieldsFlags(mDiagramsFrameReportsFieldsFlags); 487 } 488 FiraOpenSessionParams firaOpenSessionParams = builder.build(); 489 sessionId = firaOpenSessionParams.getSessionId(); 490 sessionType = firaOpenSessionParams.getSessionType(); 491 mSessionManager.initSession(attributionSource, sessionHandle, sessionId, 492 (byte) sessionType, firaOpenSessionParams.getProtocolName(), 493 firaOpenSessionParams, rangingCallbacks, chipId); 494 } else if (CccParams.isCorrectProtocol(params)) { 495 CccOpenRangingParams cccOpenRangingParams = CccOpenRangingParams.fromBundle(params); 496 sessionId = cccOpenRangingParams.getSessionId(); 497 sessionType = cccOpenRangingParams.getSessionType(); 498 mSessionManager.initSession(attributionSource, sessionHandle, sessionId, 499 (byte) sessionType, cccOpenRangingParams.getProtocolName(), 500 cccOpenRangingParams, rangingCallbacks, chipId); 501 } else { 502 Log.e(TAG, "openRanging - Wrong parameters"); 503 try { 504 rangingCallbacks.onRangingOpenFailed(sessionHandle, 505 RangingChangeReason.BAD_PARAMETERS, new PersistableBundle()); 506 } catch (RemoteException e) { } 507 } 508 } 509 startRanging(SessionHandle sessionHandle, PersistableBundle params)510 public void startRanging(SessionHandle sessionHandle, PersistableBundle params) 511 throws IllegalStateException { 512 if (!isUwbEnabled()) { 513 throw new IllegalStateException("Uwb is not enabled"); 514 } 515 Params startRangingParams = null; 516 if (CccParams.isCorrectProtocol(params)) { 517 startRangingParams = CccStartRangingParams.fromBundle(params); 518 } 519 520 if (mUwbInjector.getProfileManager().hasSession(sessionHandle)) { 521 mUwbInjector.getProfileManager().startRanging(sessionHandle); 522 } else { 523 mSessionManager.startRanging(sessionHandle, startRangingParams); 524 } 525 } 526 reconfigureRanging(SessionHandle sessionHandle, PersistableBundle params)527 public void reconfigureRanging(SessionHandle sessionHandle, PersistableBundle params) { 528 if (!isUwbEnabled()) { 529 throw new IllegalStateException("Uwb is not enabled"); 530 } 531 Params reconfigureRangingParams = null; 532 if (FiraParams.isCorrectProtocol(params)) { 533 reconfigureRangingParams = FiraRangingReconfigureParams.fromBundle(params); 534 } else if (CccParams.isCorrectProtocol(params)) { 535 reconfigureRangingParams = CccRangingReconfiguredParams.fromBundle(params); 536 } 537 mSessionManager.reconfigure(sessionHandle, reconfigureRangingParams); 538 } 539 stopRanging(SessionHandle sessionHandle)540 public void stopRanging(SessionHandle sessionHandle) { 541 if (!isUwbEnabled()) { 542 throw new IllegalStateException("Uwb is not enabled"); 543 } 544 if (mUwbInjector.getProfileManager().hasSession(sessionHandle)) { 545 mUwbInjector.getProfileManager().stopRanging(sessionHandle); 546 } else { 547 mSessionManager.stopRanging(sessionHandle); 548 } 549 } 550 closeRanging(SessionHandle sessionHandle)551 public void closeRanging(SessionHandle sessionHandle) { 552 if (!isUwbEnabled()) { 553 throw new IllegalStateException("Uwb is not enabled"); 554 } 555 if (mUwbInjector.getProfileManager().hasSession(sessionHandle)) { 556 mUwbInjector.getProfileManager().closeRanging(sessionHandle); 557 } else { 558 mSessionManager.deInitSession(sessionHandle); 559 } 560 } 561 addControlee(SessionHandle sessionHandle, PersistableBundle params)562 public void addControlee(SessionHandle sessionHandle, PersistableBundle params) { 563 if (!isUwbEnabled()) { 564 throw new IllegalStateException("Uwb is not enabled"); 565 } 566 Params reconfigureRangingParams = null; 567 if (FiraParams.isCorrectProtocol(params)) { 568 FiraControleeParams controleeParams = FiraControleeParams.fromBundle(params); 569 reconfigureRangingParams = new FiraRangingReconfigureParams.Builder() 570 .setAction(controleeParams.getAction()) 571 .setAddressList(controleeParams.getAddressList()) 572 .setSubSessionIdList(controleeParams.getSubSessionIdList()) 573 .setSubSessionKeyList(controleeParams.getSubSessionKeyList()) 574 .build(); 575 } 576 mSessionManager.reconfigure(sessionHandle, reconfigureRangingParams); 577 } 578 removeControlee(SessionHandle sessionHandle, PersistableBundle params)579 public void removeControlee(SessionHandle sessionHandle, PersistableBundle params) { 580 if (!isUwbEnabled()) { 581 throw new IllegalStateException("Uwb is not enabled"); 582 } 583 Params reconfigureRangingParams = null; 584 if (FiraParams.isCorrectProtocol(params)) { 585 FiraControleeParams controleeParams = FiraControleeParams.fromBundle(params); 586 reconfigureRangingParams = new FiraRangingReconfigureParams.Builder() 587 .setAction(controleeParams.getAction()) 588 .setAddressList(controleeParams.getAddressList()) 589 .setSubSessionIdList(controleeParams.getSubSessionIdList()) 590 .setSubSessionKeyList(controleeParams.getSubSessionKeyList()) 591 .build(); 592 } 593 mSessionManager.reconfigure(sessionHandle, reconfigureRangingParams); 594 } 595 596 /** Send the payload data to a remote device in the UWB session */ sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress, PersistableBundle params, byte[] data)597 public void sendData(SessionHandle sessionHandle, UwbAddress remoteDeviceAddress, 598 PersistableBundle params, byte[] data) throws RemoteException { 599 if (!isUwbEnabled()) { 600 throw new IllegalStateException("Uwb is not enabled"); 601 } 602 603 mSessionManager.sendData(sessionHandle, remoteDeviceAddress, params, data); 604 } 605 606 /** 607 * Get the UWB Adapter State. 608 */ getAdapterState()609 public /* @UwbManager.AdapterStateCallback.State */ int getAdapterState() { 610 return computeAdapterState(mUwbCountryCode.getCountryCode(), Optional.empty()); 611 } 612 computeAdapterState(String countryCode, Optional<Integer> setCountryCodeStatus)613 private int computeAdapterState(String countryCode, Optional<Integer> setCountryCodeStatus) { 614 // When either the country code is not valid or setting it in UWBS failed with an error, 615 // notify the UWB stack state as DISABLED (even though internally the UWB device state 616 // may be stored as READY), so that applications wait for starting a ranging session. 617 if (!mUwbCountryCode.isValid(countryCode) 618 || (setCountryCodeStatus.isPresent() 619 && setCountryCodeStatus.get() != STATUS_CODE_OK)) { 620 return AdapterStateCallback.STATE_DISABLED; 621 } 622 return getInternalAdapterState(); 623 } 624 getInternalAdapterState()625 private /* @UwbManager.AdapterStateCallback.State */ int getInternalAdapterState() { 626 synchronized (UwbServiceCore.this) { 627 if (mChipIdToStateMap.isEmpty()) { 628 return AdapterStateCallback.STATE_DISABLED; 629 } 630 631 boolean isActive = false; 632 for (int state : mChipIdToStateMap.values()) { 633 if (state == AdapterStateCallback.STATE_DISABLED) { 634 return AdapterStateCallback.STATE_DISABLED; 635 } 636 if (state == AdapterStateCallback.STATE_ENABLED_ACTIVE) { 637 isActive = true; 638 } 639 } 640 return isActive ? AdapterStateCallback.STATE_ENABLED_ACTIVE 641 : AdapterStateCallback.STATE_ENABLED_INACTIVE; 642 } 643 } 644 setEnabled(boolean enabled)645 public synchronized void setEnabled(boolean enabled) { 646 int task = enabled ? TASK_ENABLE : TASK_DISABLE; 647 648 if (enabled && isUwbEnabled()) { 649 Log.w(TAG, "Uwb is already enabled"); 650 } else if (!enabled && !isUwbEnabled()) { 651 Log.w(TAG, "Uwb is already disabled"); 652 } 653 654 mUwbTask.execute(task); 655 } 656 sendVendorUciResponse(int gid, int oid, byte[] payload)657 private void sendVendorUciResponse(int gid, int oid, byte[] payload) { 658 Log.i(TAG, "onVendorUciResponseReceived"); 659 if (mCallBack != null) { 660 try { 661 mCallBack.onVendorResponseReceived(gid, oid, payload); 662 } catch (RemoteException e) { 663 Log.e(TAG, "Failed to send vendor response", e); 664 } 665 } 666 } 667 668 /** 669 * Send vendor UCI message 670 * 671 * @param chipId : Identifier of UWB chip for multi-HAL devices 672 */ sendVendorUciMessage(int mt, int gid, int oid, byte[] payload, String chipId)673 public synchronized int sendVendorUciMessage(int mt, int gid, int oid, byte[] payload, 674 String chipId) { 675 if ((!isUwbEnabled())) { 676 Log.e(TAG, "sendRawVendor : Uwb is not enabled"); 677 return UwbUciConstants.STATUS_CODE_FAILED; 678 } 679 // Testing message type is only allowed in version FiRa 2.0 and above. 680 if (mt != MESSAGE_TYPE_COMMAND && getCachedSpecificationParams(chipId) 681 .getFiraSpecificationParams() 682 .getMaxMacVersionSupported() 683 .getMajor() < FIRA_VERSION_MAJOR_2) { 684 Log.e(TAG, "Message Type " + mt + " not supported in this FiRa version"); 685 return UwbUciConstants.STATUS_CODE_FAILED; 686 } 687 // TODO(b/211445008): Consolidate to a single uwb thread. 688 FutureTask<Integer> sendVendorCmdTask = new FutureTask<>( 689 () -> { 690 UwbVendorUciResponse response = 691 mNativeUwbManager.sendRawVendorCmd(mt, gid, oid, payload, chipId); 692 if (response.status == UwbUciConstants.STATUS_CODE_OK) { 693 sendVendorUciResponse(response.gid, response.oid, response.payload); 694 } 695 return Integer.valueOf(response.status); 696 }); 697 int status = UwbUciConstants.STATUS_CODE_FAILED; 698 try { 699 status = mUwbInjector.runTaskOnSingleThreadExecutor(sendVendorCmdTask, 700 SEND_VENDOR_CMD_TIMEOUT_MS); 701 } catch (TimeoutException e) { 702 Log.i(TAG, "Failed to send vendor command - status : TIMEOUT"); 703 } catch (InterruptedException e) { 704 e.printStackTrace(); 705 } catch (ExecutionException e) { 706 e.printStackTrace(); 707 } 708 return status; 709 } 710 rangingRoundsUpdateDtTag(SessionHandle sessionHandle, PersistableBundle params)711 public void rangingRoundsUpdateDtTag(SessionHandle sessionHandle, 712 PersistableBundle params) throws RemoteException { 713 if (!isUwbEnabled()) { 714 throw new IllegalStateException("Uwb is not enabled"); 715 } 716 mSessionManager.rangingRoundsUpdateDtTag(sessionHandle, params); 717 } 718 719 /** 720 * Query max application data size that can be sent by UWBS in one ranging round. 721 */ queryMaxDataSizeBytes(SessionHandle sessionHandle)722 public int queryMaxDataSizeBytes(SessionHandle sessionHandle) { 723 if (!isUwbEnabled()) { 724 throw new IllegalStateException("Uwb is not enabled"); 725 } 726 727 return mSessionManager.queryMaxDataSizeBytes(sessionHandle); 728 } 729 730 /** 731 * Update the pose used by the filter engine to distinguish tag position changes from device 732 * position changes. 733 */ updatePose(SessionHandle sessionHandle, PersistableBundle params)734 public void updatePose(SessionHandle sessionHandle, PersistableBundle params) { 735 mSessionManager.updatePose(sessionHandle, params); 736 } 737 738 private class UwbTask extends Handler { 739 UwbTask(Looper looper)740 UwbTask(Looper looper) { 741 super(looper); 742 } 743 744 @Override handleMessage(Message msg)745 public void handleMessage(Message msg) { 746 int type = msg.what; 747 switch (type) { 748 case TASK_ENABLE: 749 handleEnable(); 750 break; 751 752 case TASK_DISABLE: 753 mSessionManager.deinitAllSession(); 754 handleDisable(); 755 break; 756 757 case TASK_RESTART: 758 mSessionManager.deinitAllSession(); 759 handleDisable(); 760 handleEnable(); 761 break; 762 763 case TASK_GET_POWER_STATS: 764 invokeUwbActivityEnergyInfoListener((IOnUwbActivityEnergyInfoListener) msg.obj); 765 break; 766 767 default: 768 Log.d(TAG, "UwbTask : Undefined Task"); 769 break; 770 } 771 } 772 execute(int task)773 public void execute(int task) { 774 Message msg = mUwbTask.obtainMessage(); 775 msg.what = task; 776 this.sendMessage(msg); 777 } execute(int task, int arg1, int arg2)778 public void execute(int task, int arg1, int arg2) { 779 Message msg = mUwbTask.obtainMessage(); 780 msg.what = task; 781 msg.arg1 = arg1; 782 msg.arg2 = arg2; 783 this.sendMessage(msg); 784 } 785 execute(int task, Object obj)786 public void execute(int task, Object obj) { 787 Message msg = mUwbTask.obtainMessage(); 788 msg.what = task; 789 msg.obj = obj; 790 this.sendMessage(msg); 791 } 792 executeUnique(int task, int arg1, int arg2)793 private void executeUnique(int task, int arg1, int arg2) { 794 mUwbTask.removeMessages(task); 795 Message msg = mUwbTask.obtainMessage(); 796 msg.what = task; 797 msg.arg1 = arg1; 798 msg.arg2 = arg2; 799 this.sendMessage(msg); 800 } 801 delayedExecute(int task, int arg1, int arg2, int delayMillis)802 private void delayedExecute(int task, int arg1, int arg2, int delayMillis) { 803 Message msg = mUwbTask.obtainMessage(); 804 msg.what = task; 805 msg.arg1 = arg1; 806 msg.arg2 = arg2; 807 this.sendMessageDelayed(msg, delayMillis); 808 } 809 handleEnable()810 private void handleEnable() { 811 if (isUwbEnabled()) { 812 Log.i(TAG, "UWB service is already enabled"); 813 return; 814 } 815 try { 816 WatchDogThread watchDog = new WatchDogThread("handleEnable", WATCHDOG_MS); 817 watchDog.start(); 818 819 Log.i(TAG, "Initialization start ..."); 820 synchronized (mUwbWakeLock) { 821 mUwbWakeLock.acquire(); 822 } 823 824 try { 825 if (!mNativeUwbManager.doInitialize()) { 826 Log.e(TAG, "Error enabling UWB"); 827 mUwbMetrics.incrementDeviceInitFailureCount(); 828 takBugReportAfterDeviceError("UWB Bugreport: error enabling UWB"); 829 for (String chipId : mUwbInjector.getMultichipData().getChipIds()) { 830 updateState(AdapterStateCallback.STATE_DISABLED, chipId); 831 } 832 for (InitializationFailureListener listener : mListeners) { 833 listener.onFailure(); 834 } 835 } else { 836 Log.i(TAG, "Initialization success"); 837 /* TODO : keep it until MW, FW fix b/196943897 */ 838 mUwbMetrics.incrementDeviceInitSuccessCount(); 839 840 for (String chipId : mUwbInjector.getMultichipData().getChipIds()) { 841 Log.d(TAG, "enabling chip " + chipId); 842 updateDeviceState(UwbUciConstants.DEVICE_STATE_READY, chipId); 843 } 844 845 // Set country code on every enable (example: for the scenario when the 846 // country code was determined/changed while the UWB stack was disabled). 847 // 848 // TODO(b/255977441): Handle the case when the countryCode is valid and 849 // setting the country code returned an error by doing a UWBS reset. 850 Pair<Integer, String> setCountryCodeResult = 851 mUwbCountryCode.setCountryCode(true); 852 Optional<Integer> setCountryCodeStatus = 853 Optional.of(setCountryCodeResult.first); 854 String countryCode = setCountryCodeResult.second; 855 Log.i(TAG, "Current country code = " + countryCode); 856 computeAndNotifyAdapterStateChange( 857 getReasonFromDeviceState(UwbUciConstants.DEVICE_STATE_READY), 858 countryCode, 859 setCountryCodeStatus); 860 } 861 } finally { 862 synchronized (mUwbWakeLock) { 863 if (mUwbWakeLock.isHeld()) { 864 mUwbWakeLock.release(); 865 } 866 } 867 watchDog.cancel(); 868 } 869 } catch (Exception e) { 870 e.printStackTrace(); 871 } 872 } 873 handleDisable()874 private void handleDisable() { 875 if (!isUwbEnabled()) { 876 Log.i(TAG, "UWB service is already disabled"); 877 return; 878 } 879 880 WatchDogThread watchDog = new WatchDogThread("handleDisable", WATCHDOG_MS); 881 watchDog.start(); 882 883 try { 884 Log.i(TAG, "Deinitialization start ..."); 885 synchronized (mUwbWakeLock) { 886 mUwbWakeLock.acquire(); 887 } 888 889 if (!mNativeUwbManager.doDeinitialize()) { 890 Log.w(TAG, "Error disabling UWB"); 891 } else { 892 Log.i(TAG, "Deinitialization success"); 893 } 894 /* UWBS_STATUS_OFF is not the valid state. so handle device state directly */ 895 for (String chipId : mUwbInjector.getMultichipData().getChipIds()) { 896 updateDeviceState(UwbUciConstants.DEVICE_STATE_OFF, chipId); 897 } 898 notifyAdapterState( 899 getAdapterStateFromDeviceState(UwbUciConstants.DEVICE_STATE_OFF), 900 getReasonFromDeviceState(UwbUciConstants.DEVICE_STATE_OFF)); 901 } finally { 902 synchronized (mUwbWakeLock) { 903 if (mUwbWakeLock.isHeld()) { 904 mUwbWakeLock.release(); 905 } 906 } 907 watchDog.cancel(); 908 } 909 } 910 computeAndNotifyAdapterStateChange(int reason, String countryCode, Optional<Integer> setCountryCodeStatus)911 private void computeAndNotifyAdapterStateChange(int reason, 912 String countryCode, Optional<Integer> setCountryCodeStatus) { 913 // When either the country code is not valid or setting it in UWBS failed with the error 914 // STATUS_CODE_ANDROID_REGULATION_UWB_OFF, notify with the reason SYSTEM_REGULATION. 915 if (!mUwbCountryCode.isValid(countryCode) 916 || (setCountryCodeStatus.isPresent() 917 && setCountryCodeStatus.get() 918 == UwbUciConstants.STATUS_CODE_ANDROID_REGULATION_UWB_OFF)) { 919 reason = StateChangeReason.SYSTEM_REGULATION; 920 } 921 922 notifyAdapterState(computeAdapterState(countryCode, setCountryCodeStatus), reason); 923 } 924 925 public class WatchDogThread extends Thread { 926 final Object mCancelWaiter = new Object(); 927 final int mTimeout; 928 boolean mCanceled = false; 929 WatchDogThread(String threadName, int timeout)930 WatchDogThread(String threadName, int timeout) { 931 super(threadName); 932 933 mTimeout = timeout; 934 } 935 936 @Override run()937 public void run() { 938 try { 939 synchronized (mCancelWaiter) { 940 mCancelWaiter.wait(mTimeout); 941 if (mCanceled) { 942 return; 943 } 944 } 945 } catch (InterruptedException e) { 946 e.printStackTrace(); 947 interrupt(); 948 } 949 950 synchronized (mUwbWakeLock) { 951 if (mUwbWakeLock.isHeld()) { 952 mUwbWakeLock.release(); 953 } 954 } 955 } 956 cancel()957 public synchronized void cancel() { 958 synchronized (mCancelWaiter) { 959 mCanceled = true; 960 mCancelWaiter.notify(); 961 } 962 } 963 } 964 } 965 takBugReportAfterDeviceError(String bugTitle)966 private void takBugReportAfterDeviceError(String bugTitle) { 967 if (mUwbInjector.getDeviceConfigFacade().isDeviceErrorBugreportEnabled()) { 968 mUwbInjector.getUwbDiagnostics().takeBugReport(bugTitle); 969 } 970 } 971 972 /** 973 * Dump the UWB session manager debug info 974 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)975 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 976 pw.println("---- Dump of UwbServiceCore ----"); 977 for (String chipId : mUwbInjector.getMultichipData().getChipIds()) { 978 pw.println("Device state = " + getDeviceStateString(mChipIdToStateMap.get(chipId)) 979 + " for chip id = " + chipId); 980 } 981 pw.println("mLastAdapterStateChangedReason = " + mLastAdapterStateChangedReason); 982 pw.println("mLastAdapterStateNotification = " + mLastAdapterStateNotification); 983 pw.println("---- Dump of UwbServiceCore ----"); 984 } 985 986 /** 987 * Report the UWB power stats to the listener 988 */ reportUwbActivityEnergyInfo( IOnUwbActivityEnergyInfoListener listener)989 public synchronized void reportUwbActivityEnergyInfo( 990 IOnUwbActivityEnergyInfoListener listener) { 991 mUwbTask.execute(TASK_GET_POWER_STATS, listener); 992 } 993 invokeUwbActivityEnergyInfoListener(IOnUwbActivityEnergyInfoListener listener)994 private void invokeUwbActivityEnergyInfoListener(IOnUwbActivityEnergyInfoListener listener) { 995 try { 996 listener.onUwbActivityEnergyInfo(getUwbActivityEnergyInfo()); 997 } catch (RemoteException e) { 998 Log.e(TAG, "onUwbActivityEnergyInfo: RemoteException -- ", e); 999 } 1000 } 1001 getUwbActivityEnergyInfo()1002 private UwbActivityEnergyInfo getUwbActivityEnergyInfo() { 1003 try { 1004 String chipId = mUwbInjector.getMultichipData().getDefaultChipId(); 1005 PersistableBundle bundle = getSpecificationInfo(chipId); 1006 GenericSpecificationParams params = GenericSpecificationParams.fromBundle(bundle); 1007 if (!isUwbEnabled() || params == null || !params.hasPowerStatsSupport()) { 1008 return null; 1009 } 1010 UwbPowerStats stats = mNativeUwbManager.getPowerStats(chipId); 1011 if (stats == null) { 1012 return null; 1013 } 1014 1015 Log.d(TAG, " getUwbActivityEnergyInfo: " 1016 + " tx_time_millis=" + stats.getTxTimeMs() 1017 + " rx_time_millis=" + stats.getRxTimeMs() 1018 + " rxIdleTimeMillis=" + stats.getIdleTimeMs() 1019 + " wake_count=" + stats.getTotalWakeCount()); 1020 1021 return new UwbActivityEnergyInfo.Builder() 1022 .setTimeSinceBootMillis(SystemClock.elapsedRealtime()) 1023 .setStackState(getInternalAdapterState()) 1024 .setControllerTxDurationMillis(stats.getTxTimeMs()) 1025 .setControllerRxDurationMillis(stats.getRxTimeMs()) 1026 .setControllerIdleDurationMillis(stats.getIdleTimeMs()) 1027 .setControllerWakeCount(stats.getTotalWakeCount()) 1028 .build(); 1029 } catch (Exception e) { 1030 e.printStackTrace(); 1031 return null; 1032 } 1033 } 1034 } 1035