/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.uwb; import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest.permission; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.Binder; import android.os.Build; import android.os.CancellationSignal; import android.os.PersistableBundle; import android.os.RemoteException; import android.util.Log; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides a way to perform Ultra Wideband (UWB) operations such as querying the * device's capabilities and determining the distance and angle between the local device and a * remote device. * *

To get a {@link UwbManager}, call the Context.getSystemService(UwbManager.class). * *

Note: This API surface uses opaque {@link PersistableBundle} params. These params are to be * created using the provided UWB support library. The support library is present in this * location on AOSP: packages/modules/Uwb/service/support_lib/ * * @hide */ @SystemApi @SystemService(Context.UWB_SERVICE) public final class UwbManager { private static final String TAG = "UwbManager"; private final Context mContext; private final IUwbAdapter mUwbAdapter; private final AdapterStateListener mAdapterStateListener; private final RangingManager mRangingManager; private final UwbVendorUciCallbackListener mUwbVendorUciCallbackListener; private final UwbOemExtensionCallbackListener mUwbOemExtensionCallbackListener; /** * Interface for receiving UWB adapter state changes */ public interface AdapterStateCallback { /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { STATE_CHANGED_REASON_SESSION_STARTED, STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED, STATE_CHANGED_REASON_SYSTEM_POLICY, STATE_CHANGED_REASON_SYSTEM_BOOT, STATE_CHANGED_REASON_ERROR_UNKNOWN, STATE_CHANGED_REASON_SYSTEM_REGULATION}) @interface StateChangedReason {} /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { STATE_ENABLED_INACTIVE, STATE_ENABLED_ACTIVE, STATE_DISABLED, STATE_ENABLED_HW_IDLE}) @interface State {} /** * Indicates that the state change was due to opening of first UWB session */ int STATE_CHANGED_REASON_SESSION_STARTED = 0; /** * Indicates that the state change was due to closure of all UWB sessions */ int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; /** * Indicates that the state change was due to changes in system policy */ int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; /** * Indicates that the current state is due to a system boot */ int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; /** * Indicates that the state change was due to some unknown error */ int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; /** * Indicates that the state change is due to a system regulation. */ int STATE_CHANGED_REASON_SYSTEM_REGULATION = 5; /** * Indicates that UWB is disabled on device */ int STATE_DISABLED = 0; /** * Indicates that UWB is enabled on device but has no active ranging sessions */ int STATE_ENABLED_INACTIVE = 1; /** * Indicates that UWB is enabled and has active ranging session */ int STATE_ENABLED_ACTIVE = 2; /** * The state when UWB is enabled by user but the hardware is not enabled since no clients * have requested for it. * Only sent if the device supports {@link #isUwbHwIdleTurnOffEnabled()} feature. */ @FlaggedApi("com.android.uwb.flags.hw_state") int STATE_ENABLED_HW_IDLE = 3; /** * Invoked when underlying UWB adapter's state is changed *

Invoked with the adapter's current state after registering an * {@link AdapterStateCallback} using * {@link UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback)}. * *

Possible reasons for the state to change are * {@link #STATE_CHANGED_REASON_SESSION_STARTED}, * {@link #STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED}, * {@link #STATE_CHANGED_REASON_SYSTEM_POLICY}, * {@link #STATE_CHANGED_REASON_SYSTEM_BOOT}, * {@link #STATE_CHANGED_REASON_ERROR_UNKNOWN}. * {@link #STATE_CHANGED_REASON_SYSTEM_REGULATION}. * *

Possible values for the UWB state are * {@link #STATE_ENABLED_INACTIVE}, * {@link #STATE_ENABLED_ACTIVE}, * {@link #STATE_DISABLED}. * * @param state the UWB state; inactive, active or disabled * @param reason the reason for the state change */ void onStateChanged(@State int state, @StateChangedReason int reason); } /** * Abstract class for receiving ADF provisioning state. * Should be extended by applications and set when calling * {@link UwbManager#provisionProfileAdfByScript(PersistableBundle, Executor, * AdfProvisionStateCallback)} */ public abstract static class AdfProvisionStateCallback { private final AdfProvisionStateCallbackProxy mAdfProvisionStateCallbackProxy; public AdfProvisionStateCallback() { mAdfProvisionStateCallbackProxy = new AdfProvisionStateCallbackProxy(); } /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { REASON_INVALID_OID, REASON_SE_FAILURE, REASON_UNKNOWN }) @interface Reason { } /** * Indicates that the OID provided was not valid. */ public static final int REASON_INVALID_OID = 1; /** * Indicates that there was some SE (secure element) failure while provisioning. */ public static final int REASON_SE_FAILURE = 2; /** * No known reason for the failure. */ public static final int REASON_UNKNOWN = 3; /** * Invoked when {@link UwbManager#provisionProfileAdfByScript(PersistableBundle, Executor, * AdfProvisionStateCallback)} is successful. * * @param params protocol specific params that provide the caller with provisioning info **/ public abstract void onProfileAdfsProvisioned(@NonNull PersistableBundle params); /** * Invoked when {@link UwbManager#provisionProfileAdfByScript(PersistableBundle, Executor, * AdfProvisionStateCallback)} fails. * * @param reason Reason for failure * @param params protocol specific parameters to indicate failure reason */ public abstract void onProfileAdfsProvisionFailed( @Reason int reason, @NonNull PersistableBundle params); /*package*/ @NonNull AdfProvisionStateCallbackProxy getProxy() { return mAdfProvisionStateCallbackProxy; } private static class AdfProvisionStateCallbackProxy extends IUwbAdfProvisionStateCallbacks.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private AdfProvisionStateCallback mCallback; AdfProvisionStateCallbackProxy() { mCallback = null; mExecutor = null; } /*package*/ void initProxy(@NonNull Executor executor, @NonNull AdfProvisionStateCallback callback) { synchronized (mLock) { mExecutor = executor; mCallback = callback; } } /*package*/ void cleanUpProxy() { synchronized (mLock) { mExecutor = null; mCallback = null; } } @Override public void onProfileAdfsProvisioned(@NonNull PersistableBundle params) { Log.v(TAG, "AdfProvisionStateCallbackProxy: onProfileAdfsProvisioned : " + params); AdfProvisionStateCallback callback; Executor executor; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (callback == null || executor == null) { return; } Binder.clearCallingIdentity(); executor.execute(() -> callback.onProfileAdfsProvisioned(params)); cleanUpProxy(); } @Override public void onProfileAdfsProvisionFailed(@AdfProvisionStateCallback.Reason int reason, @NonNull PersistableBundle params) { Log.v(TAG, "AdfProvisionStateCallbackProxy: onProfileAdfsProvisionFailed : " + reason + ", " + params); AdfProvisionStateCallback callback; Executor executor; synchronized (mLock) { executor = mExecutor; callback = mCallback; } if (callback == null || executor == null) { return; } Binder.clearCallingIdentity(); executor.execute(() -> callback.onProfileAdfsProvisionFailed(reason, params)); cleanUpProxy(); } } } /** * Interface for receiving vendor UCI responses and notifications. */ public interface UwbVendorUciCallback { /** * Invoked when a vendor specific UCI response is received. * * @param gid Group ID of the command. This needs to be one of the vendor reserved GIDs from * the UCI specification. * @param oid Opcode ID of the command. This is left to the OEM / vendor to decide. * @param payload containing vendor Uci message payload. */ void onVendorUciResponse( @IntRange(from = 0, to = 15) int gid, int oid, @NonNull byte[] payload); /** * Invoked when a vendor specific UCI notification is received. * * @param gid Group ID of the command. This needs to be one of the vendor reserved GIDs from * the UCI specification. * @param oid Opcode ID of the command. This is left to the OEM / vendor to decide. * @param payload containing vendor Uci message payload. */ void onVendorUciNotification( @IntRange(from = 9, to = 15) int gid, int oid, @NonNull byte[] payload); } /** * @hide * Vendor configuration successful for the session */ public static final int VENDOR_SET_SESSION_CONFIGURATION_SUCCESS = 0; /** * @hide * Failure to set vendor configuration for the session */ public static final int VENDOR_SET_SESSION_CONFIGURATION_FAILURE = 1; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { VENDOR_SET_SESSION_CONFIGURATION_SUCCESS, VENDOR_SET_SESSION_CONFIGURATION_FAILURE, }) @interface VendorConfigStatus {} /** * Interface for Oem extensions on ongoing session */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public interface UwbOemExtensionCallback { /** * Invoked when session status changes. * * @param sessionStatusBundle session related info */ void onSessionStatusNotificationReceived(@NonNull PersistableBundle sessionStatusBundle); /** * Invoked when DeviceStatusNotification is received from UCI. * * @param deviceStatusBundle device state */ void onDeviceStatusNotificationReceived(@NonNull PersistableBundle deviceStatusBundle); /** * Invoked when session configuration is complete. * * @param openSessionBundle Session Params * @return Error code */ @NonNull @VendorConfigStatus int onSessionConfigurationComplete( @NonNull PersistableBundle openSessionBundle); /** * Invoked when ranging report is generated. * * @param rangingReport ranging report generated * @return Oem modified ranging report */ @NonNull RangingReport onRangingReportReceived( @NonNull RangingReport rangingReport); /** * Invoked to check pointed target decision by Oem. * * @param pointedTargetBundle pointed target params * @return Oem pointed status */ boolean onCheckPointedTarget(@NonNull PersistableBundle pointedTargetBundle); } /** * Use Context.getSystemService(UwbManager.class) to get an instance. * * @param ctx Context of the client. * @param adapter an instance of an {@link android.uwb.IUwbAdapter} * @hide */ public UwbManager(@NonNull Context ctx, @NonNull IUwbAdapter adapter) { mContext = ctx; mUwbAdapter = adapter; mAdapterStateListener = new AdapterStateListener(adapter); mRangingManager = new RangingManager(adapter); mUwbVendorUciCallbackListener = new UwbVendorUciCallbackListener(adapter); mUwbOemExtensionCallbackListener = new UwbOemExtensionCallbackListener(adapter); } /** * Register an {@link AdapterStateCallback} to listen for UWB adapter state changes *

The provided callback will be invoked by the given {@link Executor}. * *

When first registering a callback, the callbacks's * {@link AdapterStateCallback#onStateChanged(int, int)} is immediately invoked to indicate * the current state of the underlying UWB adapter with the most recent * {@link AdapterStateCallback.StateChangedReason} that caused the change. * * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link AdapterStateCallback} */ @RequiresPermission(permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor, @NonNull AdapterStateCallback callback) { mAdapterStateListener.register(executor, callback); } /** * Unregister the specified {@link AdapterStateCallback} *

The same {@link AdapterStateCallback} object used when calling * {@link #registerAdapterStateCallback(Executor, AdapterStateCallback)} must be used. * *

Callbacks are automatically unregistered when application process goes away * * @param callback user implementation of the {@link AdapterStateCallback} */ @RequiresPermission(permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) { mAdapterStateListener.unregister(callback); } /** * Register an {@link UwbVendorUciCallback} to listen for UWB vendor responses and notifications *

The provided callback will be invoked by the given {@link Executor}. * *

When first registering a callback, the callbacks's * {@link UwbVendorUciCallback#onVendorUciCallBack(byte[])} is immediately invoked to * notify the vendor notification. * * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link UwbVendorUciCallback} */ @RequiresPermission(permission.UWB_PRIVILEGED) public void registerUwbVendorUciCallback(@NonNull @CallbackExecutor Executor executor, @NonNull UwbVendorUciCallback callback) { mUwbVendorUciCallbackListener.register(executor, callback); } /** * Unregister the specified {@link UwbVendorUciCallback} * *

The same {@link UwbVendorUciCallback} object used when calling * {@link #registerUwbVendorUciCallback(Executor, UwbVendorUciCallback)} must be used. * *

Callbacks are automatically unregistered when application process goes away * * @param callback user implementation of the {@link UwbVendorUciCallback} */ public void unregisterUwbVendorUciCallback(@NonNull UwbVendorUciCallback callback) { mUwbVendorUciCallbackListener.unregister(callback); } /** * Register an {@link UwbOemExtensionCallback} to listen for UWB oem extension callbacks *

The provided callback will be invoked by the given {@link Executor}. * * @param executor an {@link Executor} to execute given callback * @param callback oem implementation of {@link UwbOemExtensionCallback} */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(permission.UWB_PRIVILEGED) public void registerUwbOemExtensionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull UwbOemExtensionCallback callback) { mUwbOemExtensionCallbackListener.register(executor, callback); } /** * Unregister the specified {@link UwbOemExtensionCallback} * *

The same {@link UwbOemExtensionCallback} object used when calling * {@link #registerUwbOemExtensionCallback(Executor, UwbOemExtensionCallback)} must be used. * *

Callbacks are automatically unregistered when an application process goes away * * @param callback oem implementation of {@link UwbOemExtensionCallback} */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(permission.UWB_PRIVILEGED) public void unregisterUwbOemExtensionCallback(@NonNull UwbOemExtensionCallback callback) { mUwbOemExtensionCallbackListener.unregister(callback); } /** * Get a {@link PersistableBundle} with the supported UWB protocols and parameters. *

The {@link PersistableBundle} should be parsed using a support library * *

Android reserves the '^android.*' namespace

* * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters */ @NonNull @RequiresPermission(permission.UWB_PRIVILEGED) public PersistableBundle getSpecificationInfo() { return getSpecificationInfoInternal(/* chipId= */ null); } /** * Get a {@link PersistableBundle} with the supported UWB protocols and parameters. * * @see #getSpecificationInfo() if you don't need multi-HAL support * * @param chipId identifier of UWB chip for multi-HAL devices * * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters */ // TODO(b/205614701): Add documentation about how to find the relevant chipId @NonNull @RequiresPermission(permission.UWB_PRIVILEGED) public PersistableBundle getSpecificationInfo(@NonNull String chipId) { checkNotNull(chipId); return getSpecificationInfoInternal(chipId); } private PersistableBundle getSpecificationInfoInternal(String chipId) { try { return mUwbAdapter.getSpecificationInfo(chipId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get uwbs timestamp in micros. * * @return uwb device timestamp in micros. */ @NonNull @RequiresPermission(permission.UWB_PRIVILEGED) @FlaggedApi("com.android.uwb.flags.query_timestamp_micros") public long queryUwbsTimestampMicros() { try { return mUwbAdapter.queryUwbsTimestampMicros(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the timestamp resolution for events in nanoseconds *

This value defines the maximum error of all timestamps for events reported to * {@link RangingSession.Callback}. * * @return the timestamp resolution in nanoseconds */ @SuppressLint("MethodNameUnits") @RequiresPermission(permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos() { return elapsedRealtimeResolutionNanosInternal(/* chipId= */ null); } /** * Get the timestamp resolution for events in nanoseconds * * @see #elapsedRealtimeResolutionNanos() if you don't need multi-HAL support * * @param chipId identifier of UWB chip for multi-HAL devices * * @return the timestamp resolution in nanoseconds */ @SuppressLint("MethodNameUnits") @RequiresPermission(permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos(@NonNull String chipId) { checkNotNull(chipId); return elapsedRealtimeResolutionNanosInternal(chipId); } private long elapsedRealtimeResolutionNanosInternal(String chipId) { try { return mUwbAdapter.getTimestampResolutionNanos(chipId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Open a {@link RangingSession} with the given parameters *

The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a * {@link RangingSession} object used to control ranging when the session is successfully * opened. * * if this session uses FIRA defined profile (not custom profile), this triggers: * - OOB discovery using service UUID * - OOB connection establishment after discovery for session params * negotiation. * - Secure element interactions needed for dynamic STS based session establishment. * - Setup the UWB session based on the parameters negotiated via OOB. * - Note: The OOB flow requires additional BLE Permissions * {permission.BLUETOOTH_ADVERTISE/permission.BLUETOOTH_SCAN * and permission.BLUETOOTH_CONNECT}. * *

If a session cannot be opened, then * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} will be invoked with the * appropriate {@link RangingSession.Callback.Reason}. * *

An open {@link RangingSession} will be automatically closed if client application process * dies. * *

A UWB support library must be used in order to construct the {@code parameter} * {@link PersistableBundle}. * * @param parameters the parameters that define the ranging session * @param executor {@link Executor} to run callbacks * @param callbacks {@link RangingSession.Callback} to associate with the * {@link RangingSession} that is being opened. * * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a * {@link RangingSession} that has been requested through {@link #openRangingSession} * but has not yet been made available by * {@link RangingSession.Callback#onOpened(RangingSession)}. */ @NonNull @RequiresPermission(allOf = { permission.UWB_PRIVILEGED, permission.UWB_RANGING }) public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters, @NonNull @CallbackExecutor Executor executor, @NonNull RangingSession.Callback callbacks) { return openRangingSessionInternal(parameters, executor, callbacks, /* chipId= */ null); } /** * Open a {@link RangingSession} with the given parameters on a specific UWB subsystem * * @see #openRangingSession(PersistableBundle, Executor, RangingSession.Callback) if you don't * need multi-HAL support * * @param parameters the parameters that define the ranging session * @param executor {@link Executor} to run callbacks * @param callbacks {@link RangingSession.Callback} to associate with the * {@link RangingSession} that is being opened. * @param chipId identifier of UWB chip for multi-HAL devices * * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a * {@link RangingSession} that has been requested through {@link #openRangingSession} * but has not yet been made available by * {@link RangingSession.Callback#onOpened(RangingSession)}. */ @NonNull @RequiresPermission(allOf = { permission.UWB_PRIVILEGED, permission.UWB_RANGING }) public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters, @NonNull @CallbackExecutor Executor executor, @NonNull RangingSession.Callback callbacks, @SuppressLint("ListenerLast") @NonNull String chipId) { checkNotNull(chipId); return openRangingSessionInternal(parameters, executor, callbacks, chipId); } private CancellationSignal openRangingSessionInternal(PersistableBundle parameters, Executor executor, RangingSession.Callback callbacks, String chipId) { return mRangingManager.openSession( mContext.getAttributionSource(), parameters, executor, callbacks, chipId); } /** * Returns the current enabled/disabled state for UWB. * * Possible values are: * AdapterStateCallback#STATE_DISABLED * AdapterStateCallback#STATE_ENABLED_INACTIVE * AdapterStateCallback#STATE_ENABLED_ACTIVE * * @return value representing current enabled/disabled state for UWB. */ public @AdapterStateCallback.State int getAdapterState() { return mAdapterStateListener.getAdapterState(); } /** * Whether UWB is enabled or disabled. * *

* If disabled, this could indicate that either *

  • User has toggled UWB off from settings, OR
  • *
  • UWB subsystem has shut down due to a fatal error.
  • *

    * * @return true if enabled, false otherwise. * * @see #getAdapterState() * @see #setUwbEnabled(boolean) */ public boolean isUwbEnabled() { int adapterState = getAdapterState(); return adapterState != AdapterStateCallback.STATE_DISABLED; } /** * Disables or enables UWB by the user. * * If enabled any subsequent calls to * {@link #openRangingSession(PersistableBundle, Executor, RangingSession.Callback)} will be * allowed. If disabled, all active ranging sessions will be closed and subsequent calls to * {@link #openRangingSession(PersistableBundle, Executor, RangingSession.Callback)} will be * disallowed. * * @param enabled value representing intent to disable or enable UWB. */ @RequiresPermission(permission.UWB_PRIVILEGED) public void setUwbEnabled(boolean enabled) { mAdapterStateListener.setEnabled(enabled); } /** * Whether UWB hardware will automatically turn off when there are no clients requesting it. * This feature is only turned on non-phone form factor devices which needs to keep the UWB * hardware turned to avoid battery drain. * *

    * If the device supports automatically turning off UWB hardware, the state of UWB hardware * is controlled by: *

  • UWB user toggle state or Airplane mode state, AND
  • *
  • Whether any clients are actively enabling UWB
  • *

    * * @return true if enabled, false otherwise. * * @see #isUwbHwEnableRequested() * @see #requestUwbHwEnable(boolean) */ @FlaggedApi("com.android.uwb.flags.hw_state") @RequiresPermission(permission.UWB_PRIVILEGED) public boolean isUwbHwIdleTurnOffEnabled() { try { return mUwbAdapter.isHwIdleTurnOffEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Whether this client has requested for UWB hardware to be enabled or disabled. * Only supported on devices which supports hw idle turn off (indicated by * {@link #isUwbHwIdleTurnOffEnabled()}) * *

    * This does not indicate the global state of UWB, this only indicates whether this app * (identified by {@link Context#getAttributionSource()}) has requested for UWB hardware to be * enabled or disabled. *

    * * @return true if enabled, false otherwise. * @throws IllegalStateException if the device does not support this feature * * @see #isUwbHwIdleTurnOffEnabled() * @see #requestUwbHwEnable(boolean) */ @FlaggedApi("com.android.uwb.flags.hw_state") @RequiresPermission(permission.UWB_PRIVILEGED) public boolean isUwbHwEnableRequested() { try { return mUwbAdapter.isHwEnableRequested(mContext.getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This client has requested for UWB hardware to be enabled or disabled. * Only supported on devices which supports hw idle turn off (indicated by * {@link #isUwbHwIdleTurnOffEnabled()}) * *

    * This does not indicate the global state of UWB, this only indicates whether this app * (identified by {@link Context#getAttributionSource()}) has requested for UWB hardware to be * enabled or disabled. * If UWB is enabled by the user and has at least 1 privileged client requesting UWB toggle on, * then UWB hardware is enabled, else the UWB hardware is disabled. *

    * * @param enabled value representing intent to disable or enable UWB. * @throws IllegalStateException if the device does not support this feature * * @see #isUwbHwIdleTurnOffEnabled() * @see #isUwbHwEnableRequested() () */ @FlaggedApi("com.android.uwb.flags.hw_state") @RequiresPermission(permission.UWB_PRIVILEGED) public void requestUwbHwEnabled(boolean enabled) { try { mUwbAdapter.requestHwEnabled( enabled, mContext.getAttributionSource(), new Binder(mContext.getPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a list of UWB chip infos in a {@link PersistableBundle}. * * Callers can invoke methods on a specific UWB chip by passing its {@code chipId} to the * method, which can be determined by calling: *
         * List chipInfos = getChipInfos();
         * for (PersistableBundle chipInfo : chipInfos) {
         *     String chipId = ChipInfoParams.fromBundle(chipInfo).getChipId();
         * }
         * 
    * * @return list of {@link PersistableBundle} containing info about UWB chips for a multi-HAL * system, or a list of info for a single chip for a single HAL system. */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public List getChipInfos() { try { return mUwbAdapter.getChipInfos(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns the default UWB chip identifier. * * If callers do not pass a specific {@code chipId} to UWB methods, then the method will be * invoked on the default chip, which is determined at system initialization from a * configuration file. * * @return default UWB chip identifier for a multi-HAL system, or the identifier of the only UWB * chip in a single HAL system. */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public String getDefaultChipId() { try { return mUwbAdapter.getDefaultChipId(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Register the UWB service profile. * This profile instance is persisted by the platform until explicitly removed * using {@link #removeServiceProfile(PersistableBundle)} * * @param parameters the parameters that define the service profile. * @return Protocol specific params to be used as handle for triggering the profile. */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public PersistableBundle addServiceProfile(@NonNull PersistableBundle parameters) { try { return mUwbAdapter.addServiceProfile(parameters); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Successfully removed the service profile. */ public static final int REMOVE_SERVICE_PROFILE_SUCCESS = 0; /** * Failed to remove service since the service profile is unknown. */ public static final int REMOVE_SERVICE_PROFILE_ERROR_UNKNOWN_SERVICE = 1; /** * Failed to remove service due to some internal error while processing the request. */ public static final int REMOVE_SERVICE_PROFILE_ERROR_INTERNAL = 2; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { REMOVE_SERVICE_PROFILE_SUCCESS, REMOVE_SERVICE_PROFILE_ERROR_UNKNOWN_SERVICE, REMOVE_SERVICE_PROFILE_ERROR_INTERNAL }) @interface RemoveServiceProfile {} /** * Remove the service profile registered with {@link #addServiceProfile} and * all related resources. * * @param parameters the parameters that define the service profile. * * @return true if the service profile is removed, false otherwise. */ @RequiresPermission(permission.UWB_PRIVILEGED) public @RemoveServiceProfile int removeServiceProfile(@NonNull PersistableBundle parameters) { try { return mUwbAdapter.removeServiceProfile(parameters); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get all service profiles initialized with {@link #addServiceProfile} * * @return the parameters that define the service profiles. */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public PersistableBundle getAllServiceProfiles() { try { return mUwbAdapter.getAllServiceProfiles(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get the list of ADF (application defined file) provisioning authorities available for the UWB * applet in SE (secure element). * * @param serviceProfileBundle Parameters representing the profile to use. * @return The list of key information of ADF provisioning authority defined in FiRa * CSML 8.2.2.7.2.4 and 8.2.2.14.4.1.2. */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public PersistableBundle getAdfProvisioningAuthorities( @NonNull PersistableBundle serviceProfileBundle) { try { return mUwbAdapter.getAdfProvisioningAuthorities(serviceProfileBundle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Get certificate information for the UWB applet in SE (secure element) that can be used to * provision ADF (application defined file). * * @param serviceProfileBundle Parameters representing the profile to use. * @return The Fira applet certificate information defined in FiRa CSML 7.3.4.3 and * 8.2.2.14.4.1.1 */ @RequiresPermission(permission.UWB_PRIVILEGED) @NonNull public PersistableBundle getAdfCertificateInfo( @NonNull PersistableBundle serviceProfileBundle) { try { return mUwbAdapter.getAdfCertificateAndInfo(serviceProfileBundle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Mechanism to provision ADFs (application defined file) in the UWB applet present in SE * (secure element) for a profile instance. * * @param serviceProfileBundle Parameters representing the profile to use. * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link AdapterStateCallback} */ public void provisionProfileAdfByScript(@NonNull PersistableBundle serviceProfileBundle, @NonNull @CallbackExecutor Executor executor, @NonNull AdfProvisionStateCallback callback) { if (executor == null) throw new IllegalArgumentException("executor must not be null"); if (callback == null) throw new IllegalArgumentException("callback must not be null"); AdfProvisionStateCallback.AdfProvisionStateCallbackProxy proxy = callback.getProxy(); proxy.initProxy(executor, callback); try { mUwbAdapter.provisionProfileAdfByScript(serviceProfileBundle, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Successfully removed the profile ADF. */ public static final int REMOVE_PROFILE_ADF_SUCCESS = 0; /** * Failed to remove ADF since the service profile is unknown. */ public static final int REMOVE_PROFILE_ADF_ERROR_UNKNOWN_SERVICE = 1; /** * Failed to remove ADF due to some internal error while processing the request. */ public static final int REMOVE_PROFILE_ADF_ERROR_INTERNAL = 2; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { REMOVE_PROFILE_ADF_SUCCESS, REMOVE_PROFILE_ADF_ERROR_UNKNOWN_SERVICE, REMOVE_PROFILE_ADF_ERROR_INTERNAL }) @interface RemoveProfileAdf {} /** * Remove the ADF (application defined file) provisioned by {@link #provisionProfileAdfByScript} * * @param serviceProfileBundle Parameters representing the profile to use. * @return true if the ADF is removed, false otherwise. */ @RequiresPermission(permission.UWB_PRIVILEGED) public @RemoveProfileAdf int removeProfileAdf(@NonNull PersistableBundle serviceProfileBundle) { try { return mUwbAdapter.removeProfileAdf(serviceProfileBundle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Successfully sent the UCI message. */ public static final int SEND_VENDOR_UCI_SUCCESS = 0; /** * Failed to send the UCI message because of an error returned from the HAL interface. */ public static final int SEND_VENDOR_UCI_ERROR_HW = 1; /** * Failed to send the UCI message since UWB is toggled off. */ public static final int SEND_VENDOR_UCI_ERROR_OFF = 2; /** * Failed to send the UCI message since UWB UCI command is malformed. * GID. */ public static final int SEND_VENDOR_UCI_ERROR_INVALID_ARGS = 3; /** * Failed to send the UCI message since UWB GID used is invalid. */ public static final int SEND_VENDOR_UCI_ERROR_INVALID_GID = 4; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { SEND_VENDOR_UCI_SUCCESS, SEND_VENDOR_UCI_ERROR_HW, SEND_VENDOR_UCI_ERROR_OFF, SEND_VENDOR_UCI_ERROR_INVALID_ARGS, SEND_VENDOR_UCI_ERROR_INVALID_GID, }) @interface SendVendorUciStatus {} /** * Message Type for UCI Command. */ public static final int MESSAGE_TYPE_COMMAND = 1; /** * Message Type for C-APDU (Command - Application Protocol Data Unit), * used for communication with secure component. */ public static final int MESSAGE_TYPE_TEST_1 = 4; /** * Message Type for R-APDU (Response - Application Protocol Data Unit), * used for communication with secure component. */ public static final int MESSAGE_TYPE_TEST_2 = 5; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { MESSAGE_TYPE_COMMAND, MESSAGE_TYPE_TEST_1, MESSAGE_TYPE_TEST_2, }) @interface MessageType {} /** * Send Vendor specific Uci Messages. * * The format of the UCI messages are defined in the UCI specification. The platform is * responsible for fragmenting the payload if necessary. * * @param gid Group ID of the command. This needs to be one of the vendor reserved GIDs from * the UCI specification. * @param oid Opcode ID of the command. This is left to the OEM / vendor to decide. * @param payload containing vendor Uci message payload. */ @NonNull @RequiresPermission(permission.UWB_PRIVILEGED) public @SendVendorUciStatus int sendVendorUciMessage( @IntRange(from = 0, to = 15) int gid, int oid, @NonNull byte[] payload) { Objects.requireNonNull(payload, "Payload must not be null"); try { return mUwbAdapter.sendVendorUciMessage(MESSAGE_TYPE_COMMAND, gid, oid, payload); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Send Vendor specific Uci Messages with custom message type. * * The format of the UCI messages are defined in the UCI specification. The platform is * responsible for fragmenting the payload if necessary. * * Note that mt (message type) is added at the beginning of method parameters as it is more * distinctive than other parameters and was requested from vendor. * * @param mt Message Type of the command * @param gid Group ID of the command. This needs to be one of the vendor reserved GIDs from * the UCI specification * @param oid Opcode ID of the command. This is left to the OEM / vendor to decide * @param payload containing vendor Uci message payload */ @NonNull @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @RequiresPermission(permission.UWB_PRIVILEGED) public @SendVendorUciStatus int sendVendorUciMessage(@MessageType int mt, @IntRange(from = 0, to = 15) int gid, int oid, @NonNull byte[] payload) { Objects.requireNonNull(payload, "Payload must not be null"); try { return mUwbAdapter.sendVendorUciMessage(mt, gid, oid, payload); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private static class OnUwbActivityEnergyInfoProxy extends IOnUwbActivityEnergyInfoListener.Stub { private final Object mLock = new Object(); @Nullable @GuardedBy("mLock") private Executor mExecutor; @Nullable @GuardedBy("mLock") private Consumer mListener; OnUwbActivityEnergyInfoProxy(Executor executor, Consumer listener) { mExecutor = executor; mListener = listener; } @Override public void onUwbActivityEnergyInfo(UwbActivityEnergyInfo info) { Executor executor; Consumer listener; synchronized (mLock) { if (mExecutor == null || mListener == null) { return; } executor = mExecutor; listener = mListener; // null out to allow garbage collection, prevent triggering listener more than once mExecutor = null; mListener = null; } Binder.clearCallingIdentity(); executor.execute(() -> listener.accept(info)); } } /** * Request to get the current {@link UwbActivityEnergyInfo} asynchronously. * * @param executor the executor that the listener will be invoked on * @param listener the listener that will receive the {@link UwbActivityEnergyInfo} object * when it becomes available. The listener will be triggered at most once for * each call to this method. */ @RequiresPermission(permission.UWB_PRIVILEGED) public void getUwbActivityEnergyInfoAsync( @NonNull @CallbackExecutor Executor executor, @NonNull Consumer listener) { Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(listener, "listener cannot be null"); try { mUwbAdapter.getUwbActivityEnergyInfoAsync( new OnUwbActivityEnergyInfoProxy(executor, listener)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } }