blob: 15ee5b5f22ebd77a0d4dedb8dbadcca444c11f37 [file] [log] [blame]
/*
* 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 android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemService;
import android.content.Context;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* 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.
*
* <p>To get a {@link UwbManager}, call the <code>Context.getSystemService(UwbManager.class)</code>.
*
* @hide
*/
@SystemService(Context.UWB_SERVICE)
public final class UwbManager {
private IUwbAdapter mUwbAdapter;
private static final String SERVICE_NAME = "uwb";
private final AdapterStateListener mAdapterStateListener;
private final RangingManager mRangingManager;
/**
* 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})
@interface StateChangedReason {}
/**
* 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;
/**
* Invoked when underlying UWB adapter's state is changed
* <p>Invoked with the adapter's current state after registering an
* {@link AdapterStateCallback} using
* {@link UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback)}.
*
* <p>Possible values 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}.
*
* @param isEnabled true when UWB adapter is enabled, false when it is disabled
* @param reason the reason for the state change
*/
void onStateChanged(boolean isEnabled, @StateChangedReason int reason);
}
/**
* Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
*
* @param adapter an instance of an {@link android.uwb.IUwbAdapter}
*/
private UwbManager(IUwbAdapter adapter) {
mUwbAdapter = adapter;
mAdapterStateListener = new AdapterStateListener(adapter);
mRangingManager = new RangingManager(adapter);
}
/**
* @hide
*/
public static UwbManager getInstance() {
IBinder b = ServiceManager.getService(SERVICE_NAME);
if (b == null) {
return null;
}
IUwbAdapter adapter = IUwbAdapter.Stub.asInterface(b);
if (adapter == null) {
return null;
}
return new UwbManager(adapter);
}
/**
* Register an {@link AdapterStateCallback} to listen for UWB adapter state changes
* <p>The provided callback will be invoked by the given {@link Executor}.
*
* <p>When first registering a callback, the callbacks's
* {@link AdapterStateCallback#onStateChanged(boolean, 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}
*/
public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull AdapterStateCallback callback) {
mAdapterStateListener.register(executor, callback);
}
/**
* Unregister the specified {@link AdapterStateCallback}
* <p>The same {@link AdapterStateCallback} object used when calling
* {@link #registerAdapterStateCallback(Executor, AdapterStateCallback)} must be used.
*
* <p>Callbacks are automatically unregistered when application process goes away
*
* @param callback user implementation of the {@link AdapterStateCallback}
*/
public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) {
mAdapterStateListener.unregister(callback);
}
/**
* Get a {@link PersistableBundle} with the supported UWB protocols and parameters.
* <p>The {@link PersistableBundle} should be parsed using a support library
*
* <p>Android reserves the '^android.*' namespace</p>
*
* @return {@link PersistableBundle} of the device's supported UWB protocols and parameters
*/
@NonNull
public PersistableBundle getSpecificationInfo() {
try {
return mUwbAdapter.getSpecificationInfo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Check if ranging is supported, regardless of ranging method
*
* @return true if ranging is supported
*/
public boolean isRangingSupported() {
try {
return mUwbAdapter.isRangingSupported();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D,
ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL,
ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL})
public @interface AngleOfArrivalSupportType {}
/**
* Indicate absence of support for angle of arrival measurement
*/
public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1;
/**
* Indicate support for planar angle of arrival measurement, due to antenna
* limitation. Typically requires at least two antennas.
*/
public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2;
/**
* Indicate support for three dimensional angle of arrival measurement.
* Typically requires at least three antennas. However, due to antenna
* arrangement, a platform may only support hemi-spherical azimuth angles
* ranging from -pi/2 to pi/2
*/
public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3;
/**
* Indicate support for three dimensional angle of arrival measurement.
* Typically requires at least three antennas. This mode supports full
* azimuth angles ranging from -pi to pi.
*/
public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4;
/**
* Gets the {@link AngleOfArrivalSupportType} supported on this platform
* <p>Possible return values are
* {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE},
* {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D},
* {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL},
* {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}.
*
* @return angle of arrival type supported
*/
@AngleOfArrivalSupportType
public int getAngleOfArrivalSupport() {
try {
switch (mUwbAdapter.getAngleOfArrivalSupport()) {
case AngleOfArrivalSupport.TWO_DIMENSIONAL:
return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D;
case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL:
return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL;
case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL:
return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL;
case AngleOfArrivalSupport.NONE:
default:
return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE;
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get a {@link List} of supported channel numbers based on the device's current location
* <p>The returned values are ordered by the system's desired ordered of use, with the first
* entry being the most preferred.
*
* <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB.
*
* @return {@link List} of supported channel numbers ordered by preference
*/
@NonNull
public List<Integer> getSupportedChannelNumbers() {
List<Integer> channels = new ArrayList<>();
try {
for (int channel : mUwbAdapter.getSupportedChannels()) {
channels.add(channel);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return channels;
}
/**
* Get a {@link List} of supported preamble code indices
* <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB.
*
* @return {@link List} of supported preamble code indices
*/
@NonNull
public Set<Integer> getSupportedPreambleCodeIndices() {
Set<Integer> preambles = new HashSet<>();
try {
for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) {
preambles.add(preamble);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return preambles;
}
/**
* Get the timestamp resolution for events in nanoseconds
* <p>This value defines the maximum error of all timestamps for events reported to
* {@link RangingSession.Callback}.
*
* @return the timestamp resolution in nanoseconds
*/
@SuppressLint("MethodNameUnits")
public long elapsedRealtimeResolutionNanos() {
try {
return mUwbAdapter.getTimestampResolutionNanos();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get the number of simultaneous sessions allowed in the system
*
* @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
*/
public int getMaxSimultaneousSessions() {
try {
return mUwbAdapter.getMaxSimultaneousSessions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get the maximum number of remote devices in a {@link RangingSession} when the local device
* is the initiator.
*
* @return the maximum number of remote devices per {@link RangingSession}
*/
public int getMaxRemoteDevicesPerInitiatorSession() {
try {
return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get the maximum number of remote devices in a {@link RangingSession} when the local device
* is a responder.
*
* @return the maximum number of remote devices per {@link RangingSession}
*/
public int getMaxRemoteDevicesPerResponderSession() {
try {
return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Open a {@link RangingSession} with the given parameters
* <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
* {@link RangingSession} object used to control ranging when the session is successfully
* opened.
*
* <p>If a session cannot be opened, then
* {@link RangingSession.Callback#onClosed(int, PersistableBundle)} will be invoked with the
* appropriate {@link RangingSession.Callback.Reason}.
*
* <p>An open {@link RangingSession} will be automatically closed if client application process
* dies.
*
* <p>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 AutoCloseable} that is able to be used to close or 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
public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
@NonNull @CallbackExecutor Executor executor,
@NonNull RangingSession.Callback callbacks) {
return mRangingManager.openSession(parameters, executor, callbacks);
}
}