Move existing UWB API classes to UWB apex

Following instructions from go/mainline-java-lib to move the existing
UWB API classes to the new apex.

Bug: 194906542
Test: Compiles
Change-Id: I08801b418c7ba42c76952f5a80745cd0057ca329
This commit is contained in:
Roshan Pius
2021-07-29 21:31:23 -07:00
parent 7b641488b9
commit 6c580dd473
35 changed files with 16 additions and 3996 deletions

View File

@@ -156,6 +156,7 @@ java_library {
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
sdk_version: "module_current",
@@ -178,6 +179,7 @@ java_library {
"framework-sdkextensions.impl",
"framework-statsd.impl",
"framework-tethering.impl",
"framework-uwb.impl",
"framework-wifi.impl",
"updatable-media",
],

View File

@@ -250,6 +250,7 @@ modules_public_stubs = [
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
"framework-tethering.stubs",
"framework-uwb.stubs",
"framework-wifi.stubs",
"i18n.module.public.api.stubs",
]
@@ -269,6 +270,7 @@ modules_system_stubs = [
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
"framework-tethering.stubs.system",
"framework-uwb.stubs.system",
"framework-wifi.stubs.system",
"i18n.module.public.api.stubs", // Only has public stubs
]

View File

@@ -90,6 +90,7 @@ genrule {
":framework-sdkextensions{.public.api.txt}",
":framework-statsd{.public.api.txt}",
":framework-tethering{.public.api.txt}",
":framework-uwb{.public.api.txt}",
":framework-wifi{.public.api.txt}",
":i18n.module.public.api{.public.api.txt}",
":non-updatable-current.txt",
@@ -149,6 +150,7 @@ genrule {
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
":i18n.module.public.api{.public.stubs.source}",
],
@@ -175,6 +177,7 @@ genrule {
":framework-sdkextensions{.public.removed-api.txt}",
":framework-statsd{.public.removed-api.txt}",
":framework-tethering{.public.removed-api.txt}",
":framework-uwb{.public.removed-api.txt}",
":framework-wifi{.public.removed-api.txt}",
":i18n.module.public.api{.public.removed-api.txt}",
":non-updatable-removed.txt",
@@ -215,6 +218,7 @@ genrule {
":framework-sdkextensions{.system.api.txt}",
":framework-statsd{.system.api.txt}",
":framework-tethering{.system.api.txt}",
":framework-uwb{.system.api.txt}",
":framework-wifi{.system.api.txt}",
":non-updatable-system-current.txt",
],
@@ -273,6 +277,7 @@ genrule {
":framework-sdkextensions{.system.removed-api.txt}",
":framework-statsd{.system.removed-api.txt}",
":framework-tethering{.system.removed-api.txt}",
":framework-uwb{.system.removed-api.txt}",
":framework-wifi{.system.removed-api.txt}",
":non-updatable-system-removed.txt",
],
@@ -313,6 +318,7 @@ genrule {
":framework-sdkextensions{.module-lib.api.txt}",
":framework-statsd{.module-lib.api.txt}",
":framework-tethering{.module-lib.api.txt}",
":framework-uwb{.module-lib.api.txt}",
":framework-wifi{.module-lib.api.txt}",
":non-updatable-module-lib-current.txt",
],
@@ -373,6 +379,7 @@ genrule {
":framework-sdkextensions{.module-lib.removed-api.txt}",
":framework-statsd{.module-lib.removed-api.txt}",
":framework-tethering{.module-lib.removed-api.txt}",
":framework-uwb{.module-lib.removed-api.txt}",
":framework-wifi{.module-lib.removed-api.txt}",
":non-updatable-module-lib-removed.txt",
],
@@ -491,6 +498,7 @@ genrule {
":framework-sdkextensions.stubs{.jar}",
":framework-statsd.stubs{.jar}",
":framework-tethering.stubs{.jar}",
":framework-uwb.stubs{.jar}",
":framework-wifi.stubs{.jar}",
":i18n.module.public.api.stubs{.jar}",
],

View File

@@ -99,6 +99,10 @@ platform_bootclasspath {
apex: "com.android.tethering",
module: "com.android.tethering-bootclasspath-fragment",
},
{
apex: "com.android.uwb",
module: "com.android.uwb-bootclasspath-fragment",
},
{
apex: "com.android.wifi",
module: "com.android.wifi-bootclasspath-fragment",

View File

@@ -14267,148 +14267,6 @@ package android.util {
}
package android.uwb {
public final class AngleMeasurement implements android.os.Parcelable {
ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double);
method public int describeContents();
method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
method @FloatRange(from=-3.141592653589793, to=3.141592653589793) public double getRadians();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
}
public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.uwb.AngleMeasurement getAltitude();
method @NonNull public android.uwb.AngleMeasurement getAzimuth();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleOfArrivalMeasurement> CREATOR;
}
public static final class AngleOfArrivalMeasurement.Builder {
ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
}
public final class DistanceMeasurement implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
method @FloatRange(from=0.0) public double getErrorMeters();
method public double getMeters();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
}
public static final class DistanceMeasurement.Builder {
ctor public DistanceMeasurement.Builder();
method @NonNull public android.uwb.DistanceMeasurement build();
method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(@FloatRange(from=0.0, to=1.0) double);
method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(@FloatRange(from=0.0) double);
method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
}
public final class RangingMeasurement implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.uwb.AngleOfArrivalMeasurement getAngleOfArrivalMeasurement();
method @Nullable public android.uwb.DistanceMeasurement getDistanceMeasurement();
method public long getElapsedRealtimeNanos();
method @NonNull public android.uwb.UwbAddress getRemoteDeviceAddress();
method public int getStatus();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingMeasurement> CREATOR;
field public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; // 0x1
field public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; // 0xffffffff
field public static final int RANGING_STATUS_SUCCESS = 0; // 0x0
}
public static final class RangingMeasurement.Builder {
ctor public RangingMeasurement.Builder();
method @NonNull public android.uwb.RangingMeasurement build();
method @NonNull public android.uwb.RangingMeasurement.Builder setAngleOfArrivalMeasurement(@NonNull android.uwb.AngleOfArrivalMeasurement);
method @NonNull public android.uwb.RangingMeasurement.Builder setDistanceMeasurement(@NonNull android.uwb.DistanceMeasurement);
method @NonNull public android.uwb.RangingMeasurement.Builder setElapsedRealtimeNanos(long);
method @NonNull public android.uwb.RangingMeasurement.Builder setRemoteDeviceAddress(@NonNull android.uwb.UwbAddress);
method @NonNull public android.uwb.RangingMeasurement.Builder setStatus(int);
}
public final class RangingReport implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.uwb.RangingMeasurement> getMeasurements();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingReport> CREATOR;
}
public static final class RangingReport.Builder {
ctor public RangingReport.Builder();
method @NonNull public android.uwb.RangingReport.Builder addMeasurement(@NonNull android.uwb.RangingMeasurement);
method @NonNull public android.uwb.RangingReport.Builder addMeasurements(@NonNull java.util.List<android.uwb.RangingMeasurement>);
method @NonNull public android.uwb.RangingReport build();
}
public final class RangingSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void close();
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void reconfigure(@NonNull android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void start(@NonNull android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void stop();
}
public static interface RangingSession.Callback {
method public void onClosed(int, @NonNull android.os.PersistableBundle);
method public void onOpenFailed(int, @NonNull android.os.PersistableBundle);
method public void onOpened(@NonNull android.uwb.RangingSession);
method public void onReconfigureFailed(int, @NonNull android.os.PersistableBundle);
method public void onReconfigured(@NonNull android.os.PersistableBundle);
method public void onReportReceived(@NonNull android.uwb.RangingReport);
method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
method public void onStarted(@NonNull android.os.PersistableBundle);
method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
method public void onStopped(int, @NonNull android.os.PersistableBundle);
field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
field public static final int REASON_GENERIC_ERROR = 4; // 0x4
field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
field public static final int REASON_MAX_SESSIONS_REACHED = 5; // 0x5
field public static final int REASON_PROTOCOL_SPECIFIC_ERROR = 7; // 0x7
field public static final int REASON_REMOTE_REQUEST = 2; // 0x2
field public static final int REASON_SYSTEM_POLICY = 6; // 0x6
field public static final int REASON_UNKNOWN = 0; // 0x0
}
public final class UwbAddress implements android.os.Parcelable {
method public int describeContents();
method @NonNull public static android.uwb.UwbAddress fromBytes(@NonNull byte[]);
method public int size();
method @NonNull public byte[] toBytes();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.UwbAddress> CREATOR;
field public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; // 0x8
field public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; // 0x2
}
public final class UwbManager {
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.UWB_PRIVILEGED, android.Manifest.permission.UWB_RANGING}) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
}
public static interface UwbManager.AdapterStateCallback {
method public void onStateChanged(int, int);
field public static final int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; // 0x1
field public static final int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; // 0x4
field public static final int STATE_CHANGED_REASON_SESSION_STARTED = 0; // 0x0
field public static final int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; // 0x3
field public static final int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; // 0x2
field public static final int STATE_DISABLED = 0; // 0x0
field public static final int STATE_ENABLED_ACTIVE = 2; // 0x2
field public static final int STATE_ENABLED_INACTIVE = 1; // 0x1
}
}
package android.view {
public abstract class Window {

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2021 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;
/**
* @hide
*/
@Backing(type="int")
enum AdapterState {
/**
* The state when UWB is disabled.
*/
STATE_DISABLED,
/**
* The state when UWB is enabled but has no active sessions.
*/
STATE_ENABLED_INACTIVE,
/**
* The state when UWB is enabled and has active sessions.
*/
STATE_ENABLED_ACTIVE,
}

View File

@@ -1,199 +0,0 @@
/*
* 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.NonNull;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
import android.uwb.UwbManager.AdapterStateCallback;
import android.uwb.UwbManager.AdapterStateCallback.State;
import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* @hide
*/
public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
private static final String TAG = "Uwb.StateListener";
private final IUwbAdapter mAdapter;
private boolean mIsRegistered = false;
private final Map<AdapterStateCallback, Executor> mCallbackMap = new HashMap<>();
@StateChangedReason
private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
@State
private int mAdapterState = AdapterStateCallback.STATE_DISABLED;
public AdapterStateListener(@NonNull IUwbAdapter adapter) {
mAdapter = adapter;
}
/**
* Register an {@link AdapterStateCallback} with this {@link AdapterStateListener}
*
* @param executor an {@link Executor} to execute given callback
* @param callback user implementation of the {@link AdapterStateCallback}
*/
public void register(@NonNull Executor executor, @NonNull AdapterStateCallback callback) {
synchronized (this) {
if (mCallbackMap.containsKey(callback)) {
return;
}
mCallbackMap.put(callback, executor);
if (!mIsRegistered) {
try {
mAdapter.registerAdapterStateCallbacks(this);
mIsRegistered = true;
} catch (RemoteException e) {
Log.w(TAG, "Failed to register adapter state callback");
throw e.rethrowFromSystemServer();
}
} else {
sendCurrentState(callback);
}
}
}
/**
* Unregister the specified {@link AdapterStateCallback}
*
* @param callback user implementation of the {@link AdapterStateCallback}
*/
public void unregister(@NonNull AdapterStateCallback callback) {
synchronized (this) {
if (!mCallbackMap.containsKey(callback)) {
return;
}
mCallbackMap.remove(callback);
if (mCallbackMap.isEmpty() && mIsRegistered) {
try {
mAdapter.unregisterAdapterStateCallbacks(this);
} catch (RemoteException e) {
Log.w(TAG, "Failed to unregister AdapterStateCallback with service");
throw e.rethrowFromSystemServer();
}
mIsRegistered = false;
}
}
}
/**
* Sets the adapter enabled state
*
* @param isEnabled value of new adapter state
*/
public void setEnabled(boolean isEnabled) {
synchronized (this) {
try {
mAdapter.setEnabled(isEnabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set adapter state");
throw e.rethrowFromSystemServer();
}
}
}
/**
* Gets the adapter enabled state
*
* @return integer representing adapter enabled state
*/
public int getAdapterState() {
synchronized (this) {
try {
return mAdapter.getAdapterState();
} catch (RemoteException e) {
Log.w(TAG, "Failed to get adapter state");
throw e.rethrowFromSystemServer();
}
}
}
private void sendCurrentState(@NonNull AdapterStateCallback callback) {
synchronized (this) {
Executor executor = mCallbackMap.get(callback);
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.onStateChanged(
mAdapterState, mAdapterStateChangeReason));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public void onAdapterStateChanged(int state, int reason) {
synchronized (this) {
@StateChangedReason int localReason =
convertToStateChangedReason(reason);
@State int localState = convertToState(state);
mAdapterStateChangeReason = localReason;
mAdapterState = localState;
for (AdapterStateCallback cb : mCallbackMap.keySet()) {
sendCurrentState(cb);
}
}
}
private static @StateChangedReason int convertToStateChangedReason(
@StateChangeReason int reason) {
switch (reason) {
case StateChangeReason.ALL_SESSIONS_CLOSED:
return AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED;
case StateChangeReason.SESSION_STARTED:
return AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED;
case StateChangeReason.SYSTEM_POLICY:
return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY;
case StateChangeReason.SYSTEM_BOOT:
return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT;
case StateChangeReason.UNKNOWN:
default:
return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
}
}
private static @State int convertToState(@AdapterState int state) {
switch (state) {
case AdapterState.STATE_ENABLED_INACTIVE:
return AdapterStateCallback.STATE_ENABLED_INACTIVE;
case AdapterState.STATE_ENABLED_ACTIVE:
return AdapterStateCallback.STATE_ENABLED_ACTIVE;
case AdapterState.STATE_DISABLED:
default:
return AdapterStateCallback.STATE_DISABLED;
}
}
}

View File

@@ -1,157 +0,0 @@
/*
* 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.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Angle measurement
*
* <p>The actual angle is interpreted as:
* {@link #getRadians()} +/- {@link #getErrorRadians()} ()} at {@link #getConfidenceLevel()}
*
* @hide
*/
@SystemApi
public final class AngleMeasurement implements Parcelable {
private final double mRadians;
private final double mErrorRadians;
private final double mConfidenceLevel;
/**
* Constructs a new {@link AngleMeasurement} object
*
* @param radians the angle in radians
* @param errorRadians the error of the angle measurement in radians
* @param confidenceLevel confidence level of the angle measurement
*
* @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
* allowed range
*/
public AngleMeasurement(
@FloatRange(from = -Math.PI, to = +Math.PI) double radians,
@FloatRange(from = 0.0, to = +Math.PI) double errorRadians,
@FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
if (radians < -Math.PI || radians > Math.PI) {
throw new IllegalArgumentException("Invalid radians: " + radians);
}
mRadians = radians;
if (errorRadians < 0.0 || errorRadians > Math.PI) {
throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
}
mErrorRadians = errorRadians;
if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
}
mConfidenceLevel = confidenceLevel;
}
/**
* Angle measurement in radians
*
* @return angle in radians
*/
@FloatRange(from = -Math.PI, to = +Math.PI)
public double getRadians() {
return mRadians;
}
/**
* Error of angle measurement in radians
*
* <p>Must be a positive value
*
* @return angle measurement error in radians
*/
@FloatRange(from = 0.0, to = +Math.PI)
public double getErrorRadians() {
return mErrorRadians;
}
/**
* Angle measurement confidence level expressed as a value between
* 0.0 to 1.0.
*
* <p>A value of 0.0 indicates there is no confidence in the measurement. A value of 1.0
* indicates there is maximum confidence in the measurement.
*
* @return the confidence level of the angle measurement
*/
@FloatRange(from = 0.0, to = 1.0)
public double getConfidenceLevel() {
return mConfidenceLevel;
}
/**
* @hide
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AngleMeasurement) {
AngleMeasurement other = (AngleMeasurement) obj;
return mRadians == other.getRadians()
&& mErrorRadians == other.getErrorRadians()
&& mConfidenceLevel == other.getConfidenceLevel();
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mRadians, mErrorRadians, mConfidenceLevel);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeDouble(mRadians);
dest.writeDouble(mErrorRadians);
dest.writeDouble(mConfidenceLevel);
}
public static final @android.annotation.NonNull Creator<AngleMeasurement> CREATOR =
new Creator<AngleMeasurement>() {
@Override
public AngleMeasurement createFromParcel(Parcel in) {
return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
}
@Override
public AngleMeasurement[] newArray(int size) {
return new AngleMeasurement[size];
}
};
}

View File

@@ -1,169 +0,0 @@
/*
* 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.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Represents an angle of arrival measurement between two devices using Ultra Wideband
*
* @hide
*/
@SystemApi
public final class AngleOfArrivalMeasurement implements Parcelable {
private final AngleMeasurement mAzimuthAngleMeasurement;
private final AngleMeasurement mAltitudeAngleMeasurement;
private AngleOfArrivalMeasurement(@NonNull AngleMeasurement azimuthAngleMeasurement,
@Nullable AngleMeasurement altitudeAngleMeasurement) {
mAzimuthAngleMeasurement = azimuthAngleMeasurement;
mAltitudeAngleMeasurement = altitudeAngleMeasurement;
}
/**
* Azimuth angle measurement
* <p>Azimuth {@link AngleMeasurement} of remote device in horizontal coordinate system, this is
* the angle clockwise from the meridian when viewing above the north pole.
*
* <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
*
* <p>On an Android device, azimuth north is defined as the angle perpendicular away from the
* back of the device when holding it in portrait mode upright.
*
* <p>Azimuth angle must be supported when Angle of Arrival is supported
*
* @return the azimuth {@link AngleMeasurement}
*/
@NonNull
public AngleMeasurement getAzimuth() {
return mAzimuthAngleMeasurement;
}
/**
* Altitude angle measurement
* <p>Altitude {@link AngleMeasurement} of remote device in horizontal coordinate system, this
* is the angle above the equator when the north pole is up.
*
* <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
*
* <p>On an Android device, altitude is defined as the angle vertical from ground when holding
* the device in portrait mode upright.
*
* @return altitude {@link AngleMeasurement} or null when this is not available
*/
@Nullable
public AngleMeasurement getAltitude() {
return mAltitudeAngleMeasurement;
}
/**
* @hide
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AngleOfArrivalMeasurement) {
AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj;
return mAzimuthAngleMeasurement.equals(other.getAzimuth())
&& mAltitudeAngleMeasurement.equals(other.getAltitude());
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mAzimuthAngleMeasurement, mAltitudeAngleMeasurement);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mAzimuthAngleMeasurement, flags);
dest.writeParcelable(mAltitudeAngleMeasurement, flags);
}
public static final @android.annotation.NonNull Creator<AngleOfArrivalMeasurement> CREATOR =
new Creator<AngleOfArrivalMeasurement>() {
@Override
public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
Builder builder =
new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
return builder.build();
}
@Override
public AngleOfArrivalMeasurement[] newArray(int size) {
return new AngleOfArrivalMeasurement[size];
}
};
/**
* Builder class for {@link AngleOfArrivalMeasurement}.
*/
public static final class Builder {
private final AngleMeasurement mAzimuthAngleMeasurement;
private AngleMeasurement mAltitudeAngleMeasurement = null;
/**
* Constructs an {@link AngleOfArrivalMeasurement} object
*
* @param azimuthAngle the azimuth angle of the measurement
*/
public Builder(@NonNull AngleMeasurement azimuthAngle) {
mAzimuthAngleMeasurement = azimuthAngle;
}
/**
* Set the altitude angle
*
* @param altitudeAngle altitude angle
*/
@NonNull
public Builder setAltitude(@NonNull AngleMeasurement altitudeAngle) {
mAltitudeAngleMeasurement = altitudeAngle;
return this;
}
/**
* Build the {@link AngleOfArrivalMeasurement} object
*/
@NonNull
public AngleOfArrivalMeasurement build() {
return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
mAltitudeAngleMeasurement);
}
}
}

View File

@@ -1,214 +0,0 @@
/*
* 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.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* A data point for the distance measurement
*
* <p>The actual distance is interpreted as:
* {@link #getMeters()} +/- {@link #getErrorMeters()} at {@link #getConfidenceLevel()}
*
* @hide
*/
@SystemApi
public final class DistanceMeasurement implements Parcelable {
private final double mMeters;
private final double mErrorMeters;
private final double mConfidenceLevel;
private DistanceMeasurement(double meters, double errorMeters, double confidenceLevel) {
mMeters = meters;
mErrorMeters = errorMeters;
mConfidenceLevel = confidenceLevel;
}
/**
* Distance measurement in meters
*
* @return distance in meters
*/
public double getMeters() {
return mMeters;
}
/**
* Error of distance measurement in meters
* <p>Must be positive
*
* @return error of distance measurement in meters
*/
@FloatRange(from = 0.0)
public double getErrorMeters() {
return mErrorMeters;
}
/**
* Distance measurement confidence level expressed as a value between 0.0 to 1.0.
*
* <p>A value of 0.0 indicates no confidence in the measurement. A value of 1.0 represents
* maximum confidence in the measurement
*
* @return confidence level
*/
@FloatRange(from = 0.0, to = 1.0)
public double getConfidenceLevel() {
return mConfidenceLevel;
}
/**
* @hide
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DistanceMeasurement) {
DistanceMeasurement other = (DistanceMeasurement) obj;
return mMeters == other.getMeters()
&& mErrorMeters == other.getErrorMeters()
&& mConfidenceLevel == other.getConfidenceLevel();
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mMeters, mErrorMeters, mConfidenceLevel);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeDouble(mMeters);
dest.writeDouble(mErrorMeters);
dest.writeDouble(mConfidenceLevel);
}
public static final @android.annotation.NonNull Creator<DistanceMeasurement> CREATOR =
new Creator<DistanceMeasurement>() {
@Override
public DistanceMeasurement createFromParcel(Parcel in) {
Builder builder = new Builder();
builder.setMeters(in.readDouble());
builder.setErrorMeters(in.readDouble());
builder.setConfidenceLevel(in.readDouble());
return builder.build();
}
@Override
public DistanceMeasurement[] newArray(int size) {
return new DistanceMeasurement[size];
}
};
/**
* Builder to get a {@link DistanceMeasurement} object.
*/
public static final class Builder {
private double mMeters = Double.NaN;
private double mErrorMeters = Double.NaN;
private double mConfidenceLevel = Double.NaN;
/**
* Set the distance measurement in meters
*
* @param meters distance in meters
* @throws IllegalArgumentException if meters is NaN
*/
@NonNull
public Builder setMeters(double meters) {
if (Double.isNaN(meters)) {
throw new IllegalArgumentException("meters cannot be NaN");
}
mMeters = meters;
return this;
}
/**
* Set the distance error in meters
*
* @param errorMeters distance error in meters
* @throws IllegalArgumentException if error is negative or NaN
*/
@NonNull
public Builder setErrorMeters(@FloatRange(from = 0.0) double errorMeters) {
if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
throw new IllegalArgumentException(
"errorMeters must be >= 0.0 and not NaN: " + errorMeters);
}
mErrorMeters = errorMeters;
return this;
}
/**
* Set the confidence level
*
* @param confidenceLevel the confidence level in the distance measurement
* @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
*/
@NonNull
public Builder setConfidenceLevel(
@FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
throw new IllegalArgumentException(
"confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
}
mConfidenceLevel = confidenceLevel;
return this;
}
/**
* Builds the {@link DistanceMeasurement} object
*
* @throws IllegalStateException if meters, error, or confidence are not set
*/
@NonNull
public DistanceMeasurement build() {
if (Double.isNaN(mMeters)) {
throw new IllegalStateException("Meters cannot be NaN");
}
if (Double.isNaN(mErrorMeters)) {
throw new IllegalStateException("Error meters cannot be NaN");
}
if (Double.isNaN(mConfidenceLevel)) {
throw new IllegalStateException("Confidence level cannot be NaN");
}
return new DistanceMeasurement(mMeters, mErrorMeters, mConfidenceLevel);
}
}
}

View File

@@ -1,191 +0,0 @@
/*
* 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.content.AttributionSource;
import android.os.PersistableBundle;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.SessionHandle;
/**
* @hide
*/
interface IUwbAdapter {
/*
* Register the callbacks used to notify the framework of events and data
*
* The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
* function must be called immediately following registration with the current
* state of the UWB adapter.
*
* @param callbacks callback to provide range and status updates to the framework
*/
void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks);
/*
* Unregister the callbacks used to notify the framework of events and data
*
* Calling this function with an unregistered callback is a no-op
*
* @param callbacks callback to unregister
*/
void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
/**
* Get the accuracy of the ranging timestamps
*
* @return accuracy of the ranging timestamps in nanoseconds
*/
long getTimestampResolutionNanos();
/**
* Provides the capabilities and features of the device
*
* @return specification specific capabilities and features of the device
*/
PersistableBundle getSpecificationInfo();
/**
* Request to open a new ranging session
*
* This function does not start the ranging session, but all necessary
* components must be initialized and ready to start a new ranging
* session prior to calling IUwbAdapterCallback#onRangingOpened.
*
* IUwbAdapterCallbacks#onRangingOpened must be called within
* RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being
* called if the ranging session is opened successfully.
*
* IUwbAdapterCallbacks#onRangingOpenFailed must be called within
* RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
* if the ranging session fails to be opened.
*
* If the provided sessionHandle is already open for the calling client, then
* #onRangingOpenFailed must be called and the new session must not be opened.
*
* @param attributionSource AttributionSource to use for permission enforcement.
* @param sessionHandle the session handle to open ranging for
* @param rangingCallbacks the callbacks used to deliver ranging information
* @param parameters the configuration to use for ranging
*/
void openRanging(in AttributionSource attributionSource,
in SessionHandle sessionHandle,
in IUwbRangingCallbacks rangingCallbacks,
in PersistableBundle parameters);
/**
* Request to start ranging
*
* IUwbAdapterCallbacks#onRangingStarted must be called within
* RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
* called if the ranging session starts successfully.
*
* IUwbAdapterCallbacks#onRangingStartFailed must be called within
* RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
* called if the ranging session fails to be started.
*
* @param sessionHandle the session handle to start ranging for
* @param parameters additional configuration required to start ranging
*/
void startRanging(in SessionHandle sessionHandle,
in PersistableBundle parameters);
/**
* Request to reconfigure ranging
*
* IUwbAdapterCallbacks#onRangingReconfigured must be called after
* successfully reconfiguring the session.
*
* IUwbAdapterCallbacks#onRangingReconfigureFailed must be called after
* failing to reconfigure the session.
*
* A session must not be modified by a failed call to #reconfigureRanging.
*
* @param sessionHandle the session handle to start ranging for
* @param parameters the parameters to reconfigure and their new values
*/
void reconfigureRanging(in SessionHandle sessionHandle,
in PersistableBundle parameters);
/**
* Request to stop ranging
*
* IUwbAdapterCallbacks#onRangingStopped must be called after
* successfully stopping the session.
*
* IUwbAdapterCallbacks#onRangingStopFailed must be called after failing
* to stop the session.
*
* @param sessionHandle the session handle to stop ranging for
*/
void stopRanging(in SessionHandle sessionHandle);
/**
* Close ranging for the session associated with the given handle
*
* Calling with an invalid handle or a handle that has already been closed
* is a no-op.
*
* IUwbAdapterCallbacks#onRangingClosed must be called within
* RANGING_SESSION_CLOSE_THRESHOLD_MS of #closeRanging being called.
*
* @param sessionHandle the session handle to close ranging for
*/
void closeRanging(in SessionHandle sessionHandle);
/**
* Disables or enables UWB for a user
*
* The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
* function must be called immediately following state change.
*
* @param enabled value representing intent to disable or enable UWB. If
* true, any subsequent calls to #openRanging will be allowed. If false,
* all active ranging sessions will be closed and subsequent calls to
* #openRanging will be disallowed.
*/
void setEnabled(boolean enabled);
/**
* Returns the current enabled/disabled UWB state.
*
* Possible values are:
* IUwbAdapterState#STATE_DISABLED
* IUwbAdapterState#STATE_ENABLED_ACTIVE
* IUwbAdapterState#STATE_ENABLED_INACTIVE
*
* @return value representing enabled/disabled UWB state.
*/
int getAdapterState();
/**
* The maximum allowed time to open a ranging session.
*/
const int RANGING_SESSION_OPEN_THRESHOLD_MS = 3000; // Value TBD
/**
* The maximum allowed time to start a ranging session.
*/
const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD
/**
* The maximum allowed time to notify the framework that a session has been
* closed.
*/
const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
}

View File

@@ -1,33 +0,0 @@
/*
* 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.uwb.StateChangeReason;
import android.uwb.AdapterState;
/**
* @hide
*/
interface IUwbAdapterStateCallbacks {
/**
* Called whenever the adapter state changes
*
* @param state UWB state; enabled_active, enabled_inactive, or disabled.
* @param reason the reason that the state has changed
*/
void onAdapterStateChanged(AdapterState state, StateChangeReason reason);
}

View File

@@ -1,136 +0,0 @@
/*
* 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.os.PersistableBundle;
import android.uwb.RangingChangeReason;
import android.uwb.RangingReport;
import android.uwb.SessionHandle;
/**
* @hide
*/
oneway interface IUwbRangingCallbacks {
/**
* Called when the ranging session has been opened
*
* @param sessionHandle the session the callback is being invoked for
*/
void onRangingOpened(in SessionHandle sessionHandle);
/**
* Called when a ranging session fails to start
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session failed to start
* @param parameters protocol specific parameters
*/
void onRangingOpenFailed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Called when ranging has started
*
* May output parameters generated by the lower layers that must be sent to the
* remote device(s). The PersistableBundle must be constructed using the UWB
* support library.
*
* @param sessionHandle the session the callback is being invoked for
* @param rangingOutputParameters parameters generated by the lower layer that
* should be sent to the remote device.
*/
void onRangingStarted(in SessionHandle sessionHandle,
in PersistableBundle parameters);
/**
* Called when a ranging session fails to start
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session failed to start
* @param parameters protocol specific parameters
*/
void onRangingStartFailed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Called when ranging has been reconfigured
*
* @param sessionHandle the session the callback is being invoked for
* @param parameters the updated ranging configuration
*/
void onRangingReconfigured(in SessionHandle sessionHandle,
in PersistableBundle parameters);
/**
* Called when a ranging session fails to be reconfigured
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session failed to reconfigure
* @param parameters protocol specific parameters
*/
void onRangingReconfigureFailed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Called when the ranging session has been stopped
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session was stopped
* @param parameters protocol specific parameters
*/
void onRangingStopped(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Called when a ranging session fails to stop
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session failed to stop
* @param parameters protocol specific parameters
*/
void onRangingStopFailed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Called when a ranging session is closed
*
* @param sessionHandle the session the callback is being invoked for
* @param reason the reason the session was closed
* @param parameters protocol specific parameters
*/
void onRangingClosed(in SessionHandle sessionHandle,
RangingChangeReason reason,
in PersistableBundle parameters);
/**
* Provides a new RangingResult to the framework
*
* The reported timestamp for a ranging measurement must be calculated as the
* time which the ranging round that generated this measurement concluded.
*
* @param sessionHandle an identifier to associate the ranging results with a
* session that is active
* @param result the ranging report
*/
void onRangingResult(in SessionHandle sessionHandle, in RangingReport result);
}

View File

@@ -1,39 +0,0 @@
/*
* 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;
/**
* @hide
*/
@Backing(type="int")
enum MeasurementStatus {
/**
* Ranging was successful
*/
SUCCESS,
/**
* The remote device is out of range
*/
FAILURE_OUT_OF_RANGE,
/**
* An unknown failure has occurred.
*/
FAILURE_UNKNOWN,
}

View File

@@ -1,6 +0,0 @@
bstack@google.com
eliptus@google.com
jsolnit@google.com
matbev@google.com
siyuanh@google.com
zachoverflow@google.com

View File

@@ -1,63 +0,0 @@
/*
* 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;
/**
* @hide
*/
@Backing(type="int")
enum RangingChangeReason {
/**
* Unknown reason
*/
UNKNOWN,
/**
* A local API call triggered the change, such as a call to
* IUwbAdapter.closeRanging.
*/
LOCAL_API,
/**
* The maximum number of sessions has been reached. This may be generated for
* an active session if a higher priority session begins.
*/
MAX_SESSIONS_REACHED,
/**
* The system state has changed resulting in the session changing (e.g. the
* user disables UWB, or the user's locale changes and an active channel is no
* longer permitted to be used).
*/
SYSTEM_POLICY,
/**
* The remote device has requested to change the session
*/
REMOTE_REQUEST,
/**
* The session changed for a protocol specific reason
*/
PROTOCOL_SPECIFIC,
/**
* The provided parameters were invalid
*/
BAD_PARAMETERS,
}

View File

@@ -1,256 +0,0 @@
/*
* 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.NonNull;
import android.content.AttributionSource;
import android.os.CancellationSignal;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Log;
import java.util.Hashtable;
import java.util.concurrent.Executor;
/**
* @hide
*/
public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
private static final String TAG = "Uwb.RangingManager";
private final IUwbAdapter mAdapter;
private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
private int mNextSessionId = 1;
public RangingManager(IUwbAdapter adapter) {
mAdapter = adapter;
}
/**
* Open a new ranging session
*
* @param attributionSource Attribution source to use for the enforcement of
* {@link android.Manifest.permission#ULTRAWIDEBAND_RANGING} runtime
* permission.
* @param params 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 a {@link CancellationSignal} that may be used to cancel the opening of the
* {@link RangingSession}.
*/
public CancellationSignal openSession(@NonNull AttributionSource attributionSource,
@NonNull PersistableBundle params,
@NonNull Executor executor,
@NonNull RangingSession.Callback callbacks) {
synchronized (this) {
SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
RangingSession session =
new RangingSession(executor, callbacks, mAdapter, sessionHandle);
mRangingSessionTable.put(sessionHandle, session);
try {
mAdapter.openRanging(attributionSource, sessionHandle, this, params);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> session.close());
return cancellationSignal;
}
}
private boolean hasSession(SessionHandle sessionHandle) {
return mRangingSessionTable.containsKey(sessionHandle);
}
@Override
public void onRangingOpened(SessionHandle sessionHandle) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
"onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingOpened();
}
}
@Override
public void onRangingOpenFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle parameters) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
"onRangingOpenedFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingOpenFailed(convertToReason(reason), parameters);
mRangingSessionTable.remove(sessionHandle);
}
}
@Override
public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
"onRangingReconfigured - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingReconfigured(parameters);
}
}
@Override
public void onRangingReconfigureFailed(SessionHandle sessionHandle,
@RangingChangeReason int reason, PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingReconfigureFailed(convertToReason(reason), params);
}
}
@Override
public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
"onRangingStarted - received unexpected SessionHandle: " + sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingStarted(parameters);
}
}
@Override
public void onRangingStartFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingStartFailed(convertToReason(reason), params);
}
}
@Override
public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingStopped(convertToReason(reason), params);
}
}
@Override
public void onRangingStopFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle parameters) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingStopFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingStopFailed(convertToReason(reason), parameters);
}
}
@Override
public void onRangingClosed(SessionHandle sessionHandle, @RangingChangeReason int reason,
PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingClosed(convertToReason(reason), params);
mRangingSessionTable.remove(sessionHandle);
}
}
@Override
public void onRangingResult(SessionHandle sessionHandle, RangingReport result) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingResult - received unexpected SessionHandle: " + sessionHandle);
return;
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
session.onRangingResult(result);
}
}
@RangingSession.Callback.Reason
private static int convertToReason(@RangingChangeReason int reason) {
switch (reason) {
case RangingChangeReason.LOCAL_API:
return RangingSession.Callback.REASON_LOCAL_REQUEST;
case RangingChangeReason.MAX_SESSIONS_REACHED:
return RangingSession.Callback.REASON_MAX_SESSIONS_REACHED;
case RangingChangeReason.SYSTEM_POLICY:
return RangingSession.Callback.REASON_SYSTEM_POLICY;
case RangingChangeReason.REMOTE_REQUEST:
return RangingSession.Callback.REASON_REMOTE_REQUEST;
case RangingChangeReason.PROTOCOL_SPECIFIC:
return RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR;
case RangingChangeReason.BAD_PARAMETERS:
return RangingSession.Callback.REASON_BAD_PARAMETERS;
case RangingChangeReason.UNKNOWN:
default:
return RangingSession.Callback.REASON_UNKNOWN;
}
}
}

View File

@@ -1,308 +0,0 @@
/*
* 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.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Representation of a ranging measurement between the local device and a remote device
*
* @hide
*/
@SystemApi
public final class RangingMeasurement implements Parcelable {
private final UwbAddress mRemoteDeviceAddress;
private final @Status int mStatus;
private final long mElapsedRealtimeNanos;
private final DistanceMeasurement mDistanceMeasurement;
private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement;
private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status,
long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement,
@Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
mRemoteDeviceAddress = remoteDeviceAddress;
mStatus = status;
mElapsedRealtimeNanos = elapsedRealtimeNanos;
mDistanceMeasurement = distanceMeasurement;
mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
}
/**
* Get the remote device's {@link UwbAddress}
*
* @return the remote device's {@link UwbAddress}
*/
@NonNull
public UwbAddress getRemoteDeviceAddress() {
return mRemoteDeviceAddress;
}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
RANGING_STATUS_SUCCESS,
RANGING_STATUS_FAILURE_OUT_OF_RANGE,
RANGING_STATUS_FAILURE_UNKNOWN_ERROR})
public @interface Status {}
/**
* Ranging attempt was successful for this device
*/
public static final int RANGING_STATUS_SUCCESS = 0;
/**
* Ranging failed for this device because it is out of range
*/
public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1;
/**
* Ranging failed for this device because of unknown error
*/
public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1;
/**
* Get the status of this ranging measurement
*
* <p>Possible values are
* {@link #RANGING_STATUS_SUCCESS},
* {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE},
* {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}.
*
* @return the status of the ranging measurement
*/
@Status
public int getStatus() {
return mStatus;
}
/**
* Timestamp of this ranging measurement in time since boot nanos in the same namespace as
* {@link SystemClock#elapsedRealtimeNanos()}
*
* @return timestamp of ranging measurement in nanoseconds
*/
@SuppressLint("MethodNameUnits")
public long getElapsedRealtimeNanos() {
return mElapsedRealtimeNanos;
}
/**
* Get the distance measurement
*
* @return a {@link DistanceMeasurement} or null if {@link #getStatus()} !=
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
public DistanceMeasurement getDistanceMeasurement() {
return mDistanceMeasurement;
}
/**
* Get the angle of arrival measurement
*
* @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} !=
* {@link #RANGING_STATUS_SUCCESS}
*/
@Nullable
public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
return mAngleOfArrivalMeasurement;
}
/**
* @hide
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RangingMeasurement) {
RangingMeasurement other = (RangingMeasurement) obj;
return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress())
&& mStatus == other.getStatus()
&& mElapsedRealtimeNanos == other.getElapsedRealtimeNanos()
&& mDistanceMeasurement.equals(other.getDistanceMeasurement())
&& mAngleOfArrivalMeasurement.equals(other.getAngleOfArrivalMeasurement());
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
mDistanceMeasurement, mAngleOfArrivalMeasurement);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mRemoteDeviceAddress, flags);
dest.writeInt(mStatus);
dest.writeLong(mElapsedRealtimeNanos);
dest.writeParcelable(mDistanceMeasurement, flags);
dest.writeParcelable(mAngleOfArrivalMeasurement, flags);
}
public static final @android.annotation.NonNull Creator<RangingMeasurement> CREATOR =
new Creator<RangingMeasurement>() {
@Override
public RangingMeasurement createFromParcel(Parcel in) {
Builder builder = new Builder();
builder.setRemoteDeviceAddress(
in.readParcelable(UwbAddress.class.getClassLoader()));
builder.setStatus(in.readInt());
builder.setElapsedRealtimeNanos(in.readLong());
builder.setDistanceMeasurement(
in.readParcelable(DistanceMeasurement.class.getClassLoader()));
builder.setAngleOfArrivalMeasurement(
in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader()));
return builder.build();
}
@Override
public RangingMeasurement[] newArray(int size) {
return new RangingMeasurement[size];
}
};
/**
* Builder for a {@link RangingMeasurement} object.
*/
public static final class Builder {
private UwbAddress mRemoteDeviceAddress = null;
private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR;
private long mElapsedRealtimeNanos = -1L;
private DistanceMeasurement mDistanceMeasurement = null;
private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null;
/**
* Set the remote device address that this measurement is for
*
* @param remoteDeviceAddress remote device's address
*/
@NonNull
public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
mRemoteDeviceAddress = remoteDeviceAddress;
return this;
}
/**
* Set the status of ranging measurement
*
* @param status the status of the ranging measurement
*/
@NonNull
public Builder setStatus(@Status int status) {
mStatus = status;
return this;
}
/**
* Set the elapsed realtime in nanoseconds when the ranging measurement occurred
*
* @param elapsedRealtimeNanos time the ranging measurement occurred
*/
@NonNull
public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
if (elapsedRealtimeNanos < 0) {
throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0");
}
mElapsedRealtimeNanos = elapsedRealtimeNanos;
return this;
}
/**
* Set the {@link DistanceMeasurement}
*
* @param distanceMeasurement the distance measurement for this ranging measurement
*/
@NonNull
public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) {
mDistanceMeasurement = distanceMeasurement;
return this;
}
/**
* Set the {@link AngleOfArrivalMeasurement}
*
* @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging
* measurement
*/
@NonNull
public Builder setAngleOfArrivalMeasurement(
@NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
return this;
}
/**
* Build the {@link RangingMeasurement} object
*
* @throws IllegalStateException if a distance or angle of arrival measurement is provided
* but the measurement was not successful, if the
* elapsedRealtimeNanos of the measurement is invalid, or
* if no remote device address is set
*/
@NonNull
public RangingMeasurement build() {
if (mStatus != RANGING_STATUS_SUCCESS) {
if (mDistanceMeasurement != null) {
throw new IllegalStateException(
"Distance Measurement must be null if ranging is not successful");
}
if (mAngleOfArrivalMeasurement != null) {
throw new IllegalStateException(
"Angle of Arrival must be null if ranging is not successful");
}
}
if (mRemoteDeviceAddress == null) {
throw new IllegalStateException("No remote device address was set");
}
if (mElapsedRealtimeNanos < 0) {
throw new IllegalStateException(
"elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos);
}
return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
mDistanceMeasurement, mAngleOfArrivalMeasurement);
}
}
}

View File

@@ -1,19 +0,0 @@
/*
* 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;
parcelable RangingReport;

View File

@@ -1,160 +0,0 @@
/*
* 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.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* This class contains the UWB ranging data
*
* @hide
*/
@SystemApi
public final class RangingReport implements Parcelable {
private final List<RangingMeasurement> mRangingMeasurements;
private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements) {
mRangingMeasurements = rangingMeasurements;
}
/**
* Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval
* <p>The underlying UWB adapter may choose to do multiple measurements in each ranging
* interval.
*
* <p>The entries in the {@link List} are ordered in ascending order based on
* {@link RangingMeasurement#getElapsedRealtimeNanos()}
*
* @return a {@link List} of {@link RangingMeasurement} objects
*/
@NonNull
public List<RangingMeasurement> getMeasurements() {
return mRangingMeasurements;
}
/**
* @hide
*/
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RangingReport) {
RangingReport other = (RangingReport) obj;
return mRangingMeasurements.equals(other.getMeasurements());
}
return false;
}
/**
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mRangingMeasurements);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedList(mRangingMeasurements);
}
public static final @android.annotation.NonNull Creator<RangingReport> CREATOR =
new Creator<RangingReport>() {
@Override
public RangingReport createFromParcel(Parcel in) {
Builder builder = new Builder();
builder.addMeasurements(in.createTypedArrayList(RangingMeasurement.CREATOR));
return builder.build();
}
@Override
public RangingReport[] newArray(int size) {
return new RangingReport[size];
}
};
/**
* Builder for {@link RangingReport} object
*/
public static final class Builder {
List<RangingMeasurement> mMeasurements = new ArrayList<>();
/**
* Add a single {@link RangingMeasurement}
*
* @param rangingMeasurement a ranging measurement
*/
@NonNull
public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
mMeasurements.add(rangingMeasurement);
return this;
}
/**
* Add a {@link List} of {@link RangingMeasurement}s
*
* @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
*/
@NonNull
public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
mMeasurements.addAll(rangingMeasurements);
return this;
}
/**
* Build the {@link RangingReport} object
*
* @throws IllegalStateException if measurements are not in monotonically increasing order
*/
@NonNull
public RangingReport build() {
// Verify that all measurement timestamps are monotonically increasing
RangingMeasurement prevMeasurement = null;
for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) {
RangingMeasurement curMeasurement = mMeasurements.get(curIndex);
if (prevMeasurement != null
&& (prevMeasurement.getElapsedRealtimeNanos()
> curMeasurement.getElapsedRealtimeNanos())) {
throw new IllegalStateException(
"Timestamp (" + curMeasurement.getElapsedRealtimeNanos()
+ ") at index " + curIndex + " is less than previous timestamp ("
+ prevMeasurement.getElapsedRealtimeNanos() + ")");
}
prevMeasurement = curMeasurement;
}
return new RangingReport(mMeasurements);
}
}
}

View File

@@ -1,496 +0,0 @@
/*
* 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.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* This class provides a way to control an active UWB ranging session.
* <p>It also defines the required {@link RangingSession.Callback} that must be implemented
* in order to be notified of UWB ranging results and status events related to the
* {@link RangingSession}.
*
* <p>To get an instance of {@link RangingSession}, first use
* {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
* session. Once the session is opened, a {@link RangingSession} object is provided through
* {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure
* is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the
* failure reason.
*
* @hide
*/
@SystemApi
public final class RangingSession implements AutoCloseable {
private static final String TAG = "Uwb.RangingSession";
private final SessionHandle mSessionHandle;
private final IUwbAdapter mAdapter;
private final Executor mExecutor;
private final Callback mCallback;
private enum State {
/**
* The state of the {@link RangingSession} until
* {@link RangingSession.Callback#onOpened(RangingSession)} is invoked
*/
INIT,
/**
* The {@link RangingSession} is initialized and ready to begin ranging
*/
IDLE,
/**
* The {@link RangingSession} is actively ranging
*/
ACTIVE,
/**
* The {@link RangingSession} is closed and may not be used for ranging.
*/
CLOSED
}
private State mState = State.INIT;
/**
* Interface for receiving {@link RangingSession} events
*/
public interface Callback {
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
REASON_UNKNOWN,
REASON_LOCAL_REQUEST,
REASON_REMOTE_REQUEST,
REASON_BAD_PARAMETERS,
REASON_GENERIC_ERROR,
REASON_MAX_SESSIONS_REACHED,
REASON_SYSTEM_POLICY,
REASON_PROTOCOL_SPECIFIC_ERROR})
@interface Reason {}
/**
* Indicates that the session was closed or failed to open due to an unknown reason
*/
int REASON_UNKNOWN = 0;
/**
* Indicates that the session was closed or failed to open because
* {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
*/
int REASON_LOCAL_REQUEST = 1;
/**
* Indicates that the session was closed or failed to open due to an explicit request from
* the remote device.
*/
int REASON_REMOTE_REQUEST = 2;
/**
* Indicates that the session was closed or failed to open due to erroneous parameters
*/
int REASON_BAD_PARAMETERS = 3;
/**
* Indicates an error on this device besides the error code already listed
*/
int REASON_GENERIC_ERROR = 4;
/**
* Indicates that the number of currently open sessions is equal to
* {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be
* opened.
*/
int REASON_MAX_SESSIONS_REACHED = 5;
/**
* Indicates that the local system policy caused the change, such
* as privacy policy, power management policy, permissions, and more.
*/
int REASON_SYSTEM_POLICY = 6;
/**
* Indicates a protocol specific error. The associated {@link PersistableBundle} should be
* consulted for additional information.
*/
int REASON_PROTOCOL_SPECIFIC_ERROR = 7;
/**
* Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
* is successful
*
* @param session the newly opened {@link RangingSession}
*/
void onOpened(@NonNull RangingSession session);
/**
* Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}}
* fails
*
* @param reason the failure reason
* @param params protocol specific parameters
*/
void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params);
/**
* Invoked when {@link RangingSession#start(PersistableBundle)} is successful
* @param sessionInfo session specific parameters from the lower layers
*/
void onStarted(@NonNull PersistableBundle sessionInfo);
/**
* Invoked when {@link RangingSession#start(PersistableBundle)} fails
*
* @param reason the failure reason
* @param params protocol specific parameters
*/
void onStartFailed(@Reason int reason, @NonNull PersistableBundle params);
/**
* Invoked when a request to reconfigure the session succeeds
*
* @param params the updated ranging configuration
*/
void onReconfigured(@NonNull PersistableBundle params);
/**
* Invoked when a request to reconfigure the session fails
*
* @param reason reason the session failed to be reconfigured
* @param params protocol specific failure reasons
*/
void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params);
/**
* Invoked when a request to stop the session succeeds
*
* @param reason reason for the session stop
* @param parameters protocol specific parameters related to the stop reason
*/
void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
/**
* Invoked when a request to stop the session fails
*
* @param reason reason the session failed to be stopped
* @param params protocol specific failure reasons
*/
void onStopFailed(@Reason int reason, @NonNull PersistableBundle params);
/**
* Invoked when session is either closed spontaneously, or per user request via
* {@link RangingSession#close()} or {@link AutoCloseable#close()}.
*
* @param reason reason for the session closure
* @param parameters protocol specific parameters related to the close reason
*/
void onClosed(@Reason int reason, @NonNull PersistableBundle parameters);
/**
* Called once per ranging interval even when a ranging measurement fails
*
* @param rangingReport ranging report for this interval's measurements
*/
void onReportReceived(@NonNull RangingReport rangingReport);
}
/**
* @hide
*/
public RangingSession(Executor executor, Callback callback, IUwbAdapter adapter,
SessionHandle sessionHandle) {
mState = State.INIT;
mExecutor = executor;
mCallback = callback;
mAdapter = adapter;
mSessionHandle = sessionHandle;
}
/**
* @hide
*/
public boolean isOpen() {
return mState == State.IDLE || mState == State.ACTIVE;
}
/**
* Begins ranging for the session.
*
* <p>On successfully starting a ranging session,
* {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked.
*
* <p>On failure to start the session,
* {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked.
*
* @param params configuration parameters for starting the session
*/
@RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void start(@NonNull PersistableBundle params) {
if (mState != State.IDLE) {
throw new IllegalStateException();
}
try {
mAdapter.startRanging(mSessionHandle, params);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Attempts to reconfigure the session with the given parameters
* <p>This call may be made when the session is open.
*
* <p>On successfully reconfiguring the session
* {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked.
*
* <p>On failure to reconfigure the session,
* {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked.
*
* @param params the parameters to reconfigure and their new values
*/
@RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void reconfigure(@NonNull PersistableBundle params) {
if (mState != State.ACTIVE && mState != State.IDLE) {
throw new IllegalStateException();
}
try {
mAdapter.reconfigureRanging(mSessionHandle, params);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Stops actively ranging
*
* <p>A session that has been stopped may be resumed by calling
* {@link RangingSession#start(PersistableBundle)} without the need to open a new session.
*
* <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard
* the parameters of the session, or when a session needs to be able to be resumed quickly.
*
* <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to
* completely close the session and allow lower layers of the stack to perform necessarily
* cleanup.
*
* <p>Stopped sessions may be closed by the system at any time. In such a case,
* {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked.
*
* <p>On failure to stop the session,
* {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked.
*/
@RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void stop() {
if (mState != State.ACTIVE) {
throw new IllegalStateException();
}
try {
mAdapter.stopRanging(mSessionHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Close the ranging session
*
* <p>After calling this function, in order resume ranging, a new {@link RangingSession} must
* be opened by calling
* {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}.
*
* <p>If this session is currently ranging, it will stop and close the session.
* <p>If the session is in the process of being opened, it will attempt to stop the session from
* being opened.
* <p>If the session is already closed, the registered
* {@link Callback#onClosed(int, PersistableBundle)} callback will still be invoked.
*
* <p>{@link Callback#onClosed(int, PersistableBundle)} will be invoked using the same callback
* object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
* when the {@link RangingSession} was opened. The callback will be invoked after each call to
* {@link #close()}, even if the {@link RangingSession} is already closed.
*/
@Override
@RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void close() {
if (mState == State.CLOSED) {
mExecutor.execute(() -> mCallback.onClosed(
Callback.REASON_LOCAL_REQUEST, new PersistableBundle()));
return;
}
try {
mAdapter.closeRanging(mSessionHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
*/
public void onRangingOpened() {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingOpened invoked for a closed session");
return;
}
mState = State.IDLE;
executeCallback(() -> mCallback.onOpened(this));
}
/**
* @hide
*/
public void onRangingOpenFailed(@Callback.Reason int reason,
@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingOpenFailed invoked for a closed session");
return;
}
mState = State.CLOSED;
executeCallback(() -> mCallback.onOpenFailed(reason, params));
}
/**
* @hide
*/
public void onRangingStarted(@NonNull PersistableBundle parameters) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStarted invoked for a closed session");
return;
}
mState = State.ACTIVE;
executeCallback(() -> mCallback.onStarted(parameters));
}
/**
* @hide
*/
public void onRangingStartFailed(@Callback.Reason int reason,
@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStartFailed invoked for a closed session");
return;
}
executeCallback(() -> mCallback.onStartFailed(reason, params));
}
/**
* @hide
*/
public void onRangingReconfigured(@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingReconfigured invoked for a closed session");
return;
}
executeCallback(() -> mCallback.onReconfigured(params));
}
/**
* @hide
*/
public void onRangingReconfigureFailed(@Callback.Reason int reason,
@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session");
return;
}
executeCallback(() -> mCallback.onReconfigureFailed(reason, params));
}
/**
* @hide
*/
public void onRangingStopped(@Callback.Reason int reason,
@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStopped invoked for a closed session");
return;
}
mState = State.IDLE;
executeCallback(() -> mCallback.onStopped(reason, params));
}
/**
* @hide
*/
public void onRangingStopFailed(@Callback.Reason int reason,
@NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStopFailed invoked for a closed session");
return;
}
executeCallback(() -> mCallback.onStopFailed(reason, params));
}
/**
* @hide
*/
public void onRangingClosed(@Callback.Reason int reason,
@NonNull PersistableBundle parameters) {
mState = State.CLOSED;
executeCallback(() -> mCallback.onClosed(reason, parameters));
}
/**
* @hide
*/
public void onRangingResult(@NonNull RangingReport report) {
if (!isOpen()) {
Log.w(TAG, "onRangingResult invoked for non-open session");
return;
}
executeCallback(() -> mCallback.onReportReceived(report));
}
/**
* @hide
*/
private void executeCallback(@NonNull Runnable runnable) {
final long identity = Binder.clearCallingIdentity();
try {
mExecutor.execute(runnable);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}

View File

@@ -1,19 +0,0 @@
/*
* 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;
parcelable SessionHandle;

View File

@@ -1,86 +0,0 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* @hide
*/
public final class SessionHandle implements Parcelable {
private final int mId;
public SessionHandle(int id) {
mId = id;
}
protected SessionHandle(Parcel in) {
mId = in.readInt();
}
public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() {
@Override
public SessionHandle createFromParcel(Parcel in) {
return new SessionHandle(in);
}
@Override
public SessionHandle[] newArray(int size) {
return new SessionHandle[size];
}
};
public int getId() {
return mId;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof SessionHandle) {
SessionHandle other = (SessionHandle) obj;
return mId == other.mId;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(mId);
}
@Override
public String toString() {
return "SessionHandle [id=" + mId + "]";
}
}

View File

@@ -1,50 +0,0 @@
/*
* 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;
/**
* @hide
*/
@Backing(type="int")
enum StateChangeReason {
/**
* The state changed for an unknown reason
*/
UNKNOWN,
/**
* The adapter state changed because a session started.
*/
SESSION_STARTED,
/**
* The adapter state changed because all sessions were closed.
*/
ALL_SESSIONS_CLOSED,
/**
* The adapter state changed because of a device system change.
*/
SYSTEM_POLICY,
/**
* Used to signal the first adapter state message after boot
*/
SYSTEM_BOOT,
}

View File

@@ -1,10 +0,0 @@
{
"presubmit": [
{
"name": "UwbManagerTests"
},
{
"name": "CtsUwbTestCases"
}
]
}

View File

@@ -1,19 +0,0 @@
/*
* 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;
parcelable UwbAddress;

View File

@@ -1,131 +0,0 @@
/*
* 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.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
/**
* A class representing a UWB address
*
* @hide
*/
@SystemApi
public final class UwbAddress implements Parcelable {
public static final int SHORT_ADDRESS_BYTE_LENGTH = 2;
public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8;
private final byte[] mAddressBytes;
private UwbAddress(byte[] address) {
mAddressBytes = address;
}
/**
* Create a {@link UwbAddress} from a byte array.
*
* <p>If the provided array is {@link #SHORT_ADDRESS_BYTE_LENGTH} bytes, a short address is
* created. If the provided array is {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes, then an
* extended address is created.
*
* @param address a byte array to convert to a {@link UwbAddress}
* @return a {@link UwbAddress} created from the input byte array
* @throws IllegalArgumentException when the length is not one of
* {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes
*/
@NonNull
public static UwbAddress fromBytes(@NonNull byte[] address) {
if (address.length != SHORT_ADDRESS_BYTE_LENGTH
&& address.length != EXTENDED_ADDRESS_BYTE_LENGTH) {
throw new IllegalArgumentException("Invalid UwbAddress length " + address.length);
}
return new UwbAddress(address);
}
/**
* Get the address as a byte array
*
* @return the byte representation of this {@link UwbAddress}
*/
@NonNull
public byte[] toBytes() {
return mAddressBytes;
}
/**
* The length of the address in bytes
* <p>Possible values are {@link #SHORT_ADDRESS_BYTE_LENGTH} and
* {@link #EXTENDED_ADDRESS_BYTE_LENGTH}.
*/
public int size() {
return mAddressBytes.length;
}
@NonNull
@Override
public String toString() {
StringBuilder builder = new StringBuilder("0x");
for (byte addressByte : mAddressBytes) {
builder.append(String.format("%02X", addressByte));
}
return builder.toString();
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof UwbAddress) {
return Arrays.equals(mAddressBytes, ((UwbAddress) obj).toBytes());
}
return false;
}
@Override
public int hashCode() {
return Arrays.hashCode(mAddressBytes);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mAddressBytes.length);
dest.writeByteArray(mAddressBytes);
}
public static final @android.annotation.NonNull Creator<UwbAddress> CREATOR =
new Creator<UwbAddress>() {
@Override
public UwbAddress createFromParcel(Parcel in) {
byte[] address = new byte[in.readInt()];
in.readByteArray(address);
return UwbAddress.fromBytes(address);
}
@Override
public UwbAddress[] newArray(int size) {
return new UwbAddress[size];
}
};
}

View File

@@ -1,308 +0,0 @@
/*
* 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.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.CancellationSignal;
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.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
*/
@SystemApi
@SystemService(Context.UWB_SERVICE)
public final class UwbManager {
private static final String SERVICE_NAME = Context.UWB_SERVICE;
private final Context mContext;
private final IUwbAdapter mUwbAdapter;
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 {}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
STATE_ENABLED_INACTIVE,
STATE_ENABLED_ACTIVE,
STATE_DISABLED})
@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 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;
/**
* 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 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}.
*
* <p>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);
}
/**
* Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
*
* @param ctx Context of the client.
* @param adapter an instance of an {@link android.uwb.IUwbAdapter}
*/
private UwbManager(@NonNull Context ctx, @NonNull IUwbAdapter adapter) {
mContext = ctx;
mUwbAdapter = adapter;
mAdapterStateListener = new AdapterStateListener(adapter);
mRangingManager = new RangingManager(adapter);
}
/**
* @hide
*/
public static UwbManager getInstance(@NonNull Context ctx) {
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(ctx, 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(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}
* <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}
*/
@RequiresPermission(permission.UWB_PRIVILEGED)
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
@RequiresPermission(permission.UWB_PRIVILEGED)
public PersistableBundle getSpecificationInfo() {
try {
return mUwbAdapter.getSpecificationInfo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* 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")
@RequiresPermission(permission.UWB_PRIVILEGED)
public long elapsedRealtimeResolutionNanos() {
try {
return mUwbAdapter.getTimestampResolutionNanos();
} 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 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 mRangingManager.openSession(
mContext.getAttributionSource(), parameters, executor, callbacks);
}
/**
* 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.
* @hide
*/
public @AdapterStateCallback.State int getAdapterState() {
return mAdapterStateListener.getAdapterState();
}
/**
* Disables or enables UWB for a user
*
* @param enabled value representing intent to disable or enable UWB. If true any subsequent
* calls to IUwbAdapter#openRanging will be allowed. If false, all active ranging sessions will
* be closed and subsequent calls to IUwbAdapter#openRanging will be disallowed.
*
* @hide
*/
public void setUwbEnabled(boolean enabled) {
mAdapterStateListener.setEnabled(enabled);
}
}

View File

@@ -1,38 +0,0 @@
// 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "UwbManagerTests",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
"mockito-target-minus-junit4",
],
libs: [
"android.test.runner",
],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.uwb">
<application>
<uses-library android:name="android.test.runner" />
</application>
<!-- This is a self-instrumenting test package. -->
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.uwb"
android:label="UWB Manager Tests">
</instrumentation>
</manifest>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<configuration description="Config for UWB Manager test cases">
<option name="test-suite-tag" value="apct"/>
<option name="test-suite-tag" value="apct-instrumentation"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="UwbManagerTests.apk" />
</target_preparer>
<option name="test-suite-tag" value="apct"/>
<option name="test-tag" value="UwbManagerTests"/>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.uwb" />
<option name="hidden-api-checks" value="false"/>
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
</test>
</configuration>

View File

@@ -1 +0,0 @@
include /core/java/android/uwb/OWNERS

View File

@@ -1,288 +0,0 @@
/*
* 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 org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.RemoteException;
import android.uwb.UwbManager.AdapterStateCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Test of {@link AdapterStateListener}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AdapterStateListenerTest {
IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class);
Answer mRegisterSuccessAnswer = new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(AdapterState.STATE_DISABLED, StateChangeReason.UNKNOWN);
} catch (RemoteException e) {
// Nothing to do
}
return new Object();
}
};
Throwable mThrowRemoteException = new RemoteException("RemoteException");
private static Executor getExecutor() {
return new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
};
}
private static void verifyCallbackStateChangedInvoked(
AdapterStateCallback callback, int numTimes) {
verify(callback, times(numTimes)).onStateChanged(anyInt(), anyInt());
}
@Test
public void testRegister_RegisterUnregister() throws RemoteException {
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
// Verify that the adapter state listener registered with the UWB Adapter
adapterStateListener.register(getExecutor(), callback1);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 0);
// Register a second client and no new call to UWB Adapter
adapterStateListener.register(getExecutor(), callback2);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
// Unregister first callback
adapterStateListener.unregister(callback1);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
// Unregister second callback
adapterStateListener.unregister(callback2);
verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any());
verifyCallbackStateChangedInvoked(callback1, 1);
verifyCallbackStateChangedInvoked(callback2, 1);
}
@Test
public void testRegister_RegisterSameCallbackTwice() throws RemoteException {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
adapterStateListener.register(getExecutor(), callback);
verifyCallbackStateChangedInvoked(callback, 1);
adapterStateListener.register(getExecutor(), callback);
verifyCallbackStateChangedInvoked(callback, 1);
// Invoke a state change and ensure the callback is only called once
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verifyCallbackStateChangedInvoked(callback, 2);
}
@Test
public void testCallback_RunViaExecutor_Success() throws RemoteException {
// Verify that the callbacks are invoked on the executor when successful
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
runViaExecutor();
}
private void runViaExecutor() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
Executor executor = mock(Executor.class);
// Do not run commands received and ensure that the callback is not invoked
doAnswer(new ExecutorAnswer(false)).when(executor).execute(any());
adapterStateListener.register(executor, callback);
verify(executor, times(1)).execute(any());
verifyCallbackStateChangedInvoked(callback, 0);
// Manually invoke the callback and ensure callback is not invoked
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(2)).execute(any());
verifyCallbackStateChangedInvoked(callback, 0);
// Run the command that the executor receives
doAnswer(new ExecutorAnswer(true)).when(executor).execute(any());
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(3)).execute(any());
verifyCallbackStateChangedInvoked(callback, 1);
}
class ExecutorAnswer implements Answer {
final boolean mShouldRun;
ExecutorAnswer(boolean shouldRun) {
mShouldRun = shouldRun;
}
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
if (mShouldRun) {
((Runnable) invocation.getArgument(0)).run();
}
return null;
}
}
@Test
public void testNotify_AllCallbacksNotified() throws RemoteException {
doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
List<AdapterStateCallback> callbacks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
callbacks.add(callback);
}
// Ensure every callback got the initial state
for (AdapterStateCallback callback : callbacks) {
verifyCallbackStateChangedInvoked(callback, 1);
}
// Invoke a state change and ensure all callbacks are invoked
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.ALL_SESSIONS_CLOSED);
for (AdapterStateCallback callback : callbacks) {
verifyCallbackStateChangedInvoked(callback, 2);
}
}
@Test
public void testStateChange_CorrectValue() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED,
AdapterState.STATE_ENABLED_INACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
AdapterStateCallback.STATE_ENABLED_INACTIVE);
runStateChangeValue(StateChangeReason.SESSION_STARTED, AdapterState.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED,
AdapterStateCallback.STATE_ENABLED_ACTIVE);
runStateChangeValue(StateChangeReason.SYSTEM_BOOT, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.SYSTEM_POLICY, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.UNKNOWN, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN,
AdapterStateCallback.STATE_DISABLED);
}
private void runStateChangeValue(@StateChangeReason int reasonIn, @AdapterState int stateIn,
@AdapterStateCallback.StateChangedReason int reasonOut,
@AdapterStateCallback.State int stateOut) {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
adapterStateListener.onAdapterStateChanged(stateIn, reasonIn);
verify(callback, times(1)).onStateChanged(stateOut, reasonOut);
}
@Test
public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
Answer registerAnswer = new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SESSION_STARTED);
} catch (RemoteException e) {
// Nothing to do
}
return new Object();
}
};
doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
adapterStateListener.register(getExecutor(), callback);
verify(callback).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
}
@Test
public void testStateChange_SecondRegisterGetsCorrectState() {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback1);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SYSTEM_BOOT);
adapterStateListener.register(getExecutor(), callback2);
verify(callback2).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
}
}

View File

@@ -1,267 +0,0 @@
/*
* 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 org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.AttributionSource;
import android.os.PersistableBundle;
import android.os.RemoteException;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.concurrent.Executor;
/**
* Test of {@link AdapterStateListener}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RangingManagerTest {
private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
private static final PersistableBundle PARAMS = new PersistableBundle();
private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
private static final int UID = 343453;
private static final String PACKAGE_NAME = "com.uwb.test";
private static final AttributionSource ATTRIBUTION_SOURCE =
new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
@Test
public void testOpenSession_OpenRangingInvoked() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), any(), any(), any());
}
@Test
public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
rangingManager.onRangingOpened(new SessionHandle(2));
verify(callback, times(0)).onOpened(any());
}
@Test
public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
RangingManager rangingManager = new RangingManager(adapter);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
verify(adapter, times(2)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingOpened(sessionHandle1);
verify(callback1, times(1)).onOpened(any());
verify(callback2, times(0)).onOpened(any());
rangingManager.onRangingOpened(sessionHandle2);
verify(callback1, times(1)).onOpened(any());
verify(callback2, times(1)).onOpened(any());
}
@Test
public void testCorrectCallbackInvoked() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle handle = sessionHandleCaptor.getValue();
rangingManager.onRangingOpened(handle);
verify(callback, times(1)).onOpened(any());
rangingManager.onRangingStarted(handle, PARAMS);
verify(callback, times(1)).onStarted(eq(PARAMS));
rangingManager.onRangingStartFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onStartFailed(eq(REASON), eq(PARAMS));
RangingReport report = UwbTestUtils.getRangingReports(1);
rangingManager.onRangingResult(handle, report);
verify(callback, times(1)).onReportReceived(eq(report));
rangingManager.onRangingReconfigured(handle, PARAMS);
verify(callback, times(1)).onReconfigured(eq(PARAMS));
rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
rangingManager.onRangingStopped(handle, REASON, PARAMS);
verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
rangingManager.onRangingClosed(handle, REASON, PARAMS);
verify(callback, times(1)).onClosed(eq(REASON), eq(PARAMS));
}
@Test
public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
// Verify that if multiple sessions are registered, only the session that is
// requested to close receives the associated callbacks
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
RangingManager rangingManager = new RangingManager(adapter);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
verify(adapter, times(2)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
verify(callback1, times(1)).onClosed(anyInt(), any());
verify(callback2, times(0)).onClosed(anyInt(), any());
rangingManager.onRangingClosed(sessionHandle2, REASON, PARAMS);
verify(callback1, times(1)).onClosed(anyInt(), any());
verify(callback2, times(1)).onClosed(anyInt(), any());
}
@Test
public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
RangingManager rangingManager = new RangingManager(adapter);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
rangingManager.onRangingStarted(sessionHandle1, PARAMS);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
verify(adapter, times(2)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
rangingManager.onRangingStarted(sessionHandle2, PARAMS);
rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
verify(callback1, times(1)).onReportReceived(any());
verify(callback2, times(0)).onReportReceived(any());
rangingManager.onRangingResult(sessionHandle2, UwbTestUtils.getRangingReports(1));
verify(callback1, times(1)).onReportReceived(any());
verify(callback2, times(1)).onReportReceived(any());
}
@Test
public void testReasons() throws RemoteException {
runReason(RangingChangeReason.LOCAL_API,
RangingSession.Callback.REASON_LOCAL_REQUEST);
runReason(RangingChangeReason.MAX_SESSIONS_REACHED,
RangingSession.Callback.REASON_MAX_SESSIONS_REACHED);
runReason(RangingChangeReason.PROTOCOL_SPECIFIC,
RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR);
runReason(RangingChangeReason.REMOTE_REQUEST,
RangingSession.Callback.REASON_REMOTE_REQUEST);
runReason(RangingChangeReason.SYSTEM_POLICY,
RangingSession.Callback.REASON_SYSTEM_POLICY);
runReason(RangingChangeReason.BAD_PARAMETERS,
RangingSession.Callback.REASON_BAD_PARAMETERS);
runReason(RangingChangeReason.UNKNOWN,
RangingSession.Callback.REASON_UNKNOWN);
}
private void runReason(@RangingChangeReason int reasonIn,
@RangingSession.Callback.Reason int reasonOut) throws RemoteException {
IUwbAdapter adapter = mock(IUwbAdapter.class);
RangingManager rangingManager = new RangingManager(adapter);
RangingSession.Callback callback = mock(RangingSession.Callback.class);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
verify(adapter, times(1)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
SessionHandle handle = sessionHandleCaptor.getValue();
rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
// Open a new session
rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
verify(adapter, times(2)).openRanging(
eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
handle = sessionHandleCaptor.getValue();
rangingManager.onRangingOpened(handle);
rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onStartFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingReconfigureFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onReconfigureFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingStopFailed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onStopFailed(eq(reasonOut), eq(PARAMS));
rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
}
}

View File

@@ -1,90 +0,0 @@
/*
* 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.os.SystemClock;
import java.util.concurrent.Executor;
public class UwbTestUtils {
private UwbTestUtils() {}
public static AngleMeasurement getAngleMeasurement() {
return new AngleMeasurement(
getDoubleInRange(-Math.PI, Math.PI),
getDoubleInRange(0, Math.PI),
getDoubleInRange(0, 1));
}
public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
.setAltitude(getAngleMeasurement())
.build();
}
public static DistanceMeasurement getDistanceMeasurement() {
return new DistanceMeasurement.Builder()
.setMeters(getDoubleInRange(0, 100))
.setErrorMeters(getDoubleInRange(0, 10))
.setConfidenceLevel(getDoubleInRange(0, 1))
.build();
}
public static RangingMeasurement getRangingMeasurement() {
return getRangingMeasurement(getUwbAddress(false));
}
public static RangingMeasurement getRangingMeasurement(UwbAddress address) {
return new RangingMeasurement.Builder()
.setDistanceMeasurement(getDistanceMeasurement())
.setAngleOfArrivalMeasurement(getAngleOfArrivalMeasurement())
.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos())
.setRemoteDeviceAddress(address != null ? address : getUwbAddress(false))
.setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
.build();
}
public static RangingReport getRangingReports(int numMeasurements) {
RangingReport.Builder builder = new RangingReport.Builder();
for (int i = 0; i < numMeasurements; i++) {
builder.addMeasurement(getRangingMeasurement());
}
return builder.build();
}
private static double getDoubleInRange(double min, double max) {
return min + (max - min) * Math.random();
}
public static UwbAddress getUwbAddress(boolean isShortAddress) {
byte[] addressBytes = new byte[isShortAddress ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH :
UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH];
for (int i = 0; i < addressBytes.length; i++) {
addressBytes[i] = (byte) getDoubleInRange(1, 255);
}
return UwbAddress.fromBytes(addressBytes);
}
public static Executor getExecutor() {
return new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
};
}
}