Merge changes Ib5f541ed,Iff6a3cb1,I2ebd5c7a into main
* changes: [remoteauth] Add UwbRangingSession [remoteauth] Implement RangingSession [remoteauth] Implement RangingManager
This commit is contained in:
commit
fafedb40c9
15 changed files with 1012 additions and 10 deletions
|
|
@ -47,6 +47,7 @@ java_library {
|
|||
"modules-utils-preconditions",
|
||||
"modules-utils-backgroundthread",
|
||||
"presence-lite-protos",
|
||||
"uwb_androidx_backend",
|
||||
],
|
||||
sdk_version: "system_server_current",
|
||||
// This is included in service-connectivity which is 30+
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ Provides the connectivity manager to manage connections with the peer device.
|
|||
## Ranging
|
||||
Provides the ranging manager to perform ranging with the peer devices.
|
||||
|
||||
## Util
|
||||
Common utilities.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package com.android.server.remoteauth.ranging;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
|
@ -43,6 +46,8 @@ public class RangingCapabilities {
|
|||
public static final int RANGING_METHOD_UWB = 0x1;
|
||||
|
||||
private final ImmutableList<Integer> mSupportedRangingMethods;
|
||||
private final androidx.core.uwb.backend.impl.internal.RangingCapabilities
|
||||
mUwbRangingCapabilities;
|
||||
|
||||
/**
|
||||
* Gets the list of supported ranging methods of the device.
|
||||
|
|
@ -53,13 +58,28 @@ public class RangingCapabilities {
|
|||
return mSupportedRangingMethods;
|
||||
}
|
||||
|
||||
private RangingCapabilities(List<Integer> supportedRangingMethods) {
|
||||
/**
|
||||
* Gets the UWB ranging capabilities of the device.
|
||||
*
|
||||
* @return UWB ranging capabilities, null if UWB is not a supported {@link RangingMethod} in
|
||||
* {@link #getSupportedRangingMethods}.
|
||||
*/
|
||||
@Nullable
|
||||
public androidx.core.uwb.backend.impl.internal.RangingCapabilities getUwbRangingCapabilities() {
|
||||
return mUwbRangingCapabilities;
|
||||
}
|
||||
|
||||
private RangingCapabilities(
|
||||
List<Integer> supportedRangingMethods,
|
||||
androidx.core.uwb.backend.impl.internal.RangingCapabilities uwbRangingCapabilities) {
|
||||
mSupportedRangingMethods = ImmutableList.copyOf(supportedRangingMethods);
|
||||
mUwbRangingCapabilities = uwbRangingCapabilities;
|
||||
}
|
||||
|
||||
/** Builder class for {@link RangingCapabilities}. */
|
||||
public static final class Builder {
|
||||
private List<Integer> mSupportedRangingMethods = new ArrayList<>();
|
||||
private androidx.core.uwb.backend.impl.internal.RangingCapabilities mUwbRangingCapabilities;
|
||||
|
||||
/** Adds a supported {@link RangingMethod} */
|
||||
public Builder addSupportedRangingMethods(@RangingMethod int rangingMethod) {
|
||||
|
|
@ -67,9 +87,18 @@ public class RangingCapabilities {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Sets the uwb ranging capabilities. */
|
||||
public Builder setUwbRangingCapabilities(
|
||||
@NonNull
|
||||
androidx.core.uwb.backend.impl.internal.RangingCapabilities
|
||||
uwbRangingCapabilities) {
|
||||
mUwbRangingCapabilities = uwbRangingCapabilities;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds {@link RangingCapabilities}. */
|
||||
public RangingCapabilities build() {
|
||||
return new RangingCapabilities(mSupportedRangingMethods);
|
||||
return new RangingCapabilities(mSupportedRangingMethods, mUwbRangingCapabilities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,21 @@
|
|||
*/
|
||||
package com.android.server.remoteauth.ranging;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_UWB;
|
||||
|
||||
import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_METHOD_UNKNOWN;
|
||||
import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_METHOD_UWB;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.uwb.backend.impl.internal.UwbFeatureFlags;
|
||||
import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Manages the creation of generic device to device ranging session and obtaining device's ranging
|
||||
|
|
@ -25,16 +39,46 @@ import android.content.Context;
|
|||
* outside of this class.
|
||||
*/
|
||||
public class RangingManager {
|
||||
private static final String TAG = "RangingManager";
|
||||
|
||||
public RangingManager(Context context) {}
|
||||
private Context mContext;
|
||||
@NonNull private RangingCapabilities mCachedRangingCapabilities;
|
||||
@NonNull private UwbServiceImpl mUwbServiceImpl;
|
||||
|
||||
public RangingManager(@NonNull Context context) {
|
||||
mContext = context;
|
||||
if (mContext.getPackageManager().hasSystemFeature(FEATURE_UWB)) {
|
||||
initiateUwb();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown and stop all listeners and tasks. After shutdown, RangingManager shall not be used
|
||||
* anymore.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (mUwbServiceImpl != null) {
|
||||
mUwbServiceImpl.shutdown();
|
||||
}
|
||||
Log.i(TAG, "shutdown");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RangingCapabilities} of this device.
|
||||
*
|
||||
* @return RangingCapabilities.
|
||||
*/
|
||||
@NonNull
|
||||
public RangingCapabilities getRangingCapabilities() {
|
||||
return null;
|
||||
if (mCachedRangingCapabilities == null) {
|
||||
RangingCapabilities.Builder builder = new RangingCapabilities.Builder();
|
||||
if (mUwbServiceImpl != null) {
|
||||
builder.addSupportedRangingMethods(RANGING_METHOD_UWB);
|
||||
builder.setUwbRangingCapabilities(mUwbServiceImpl.getRangingCapabilities());
|
||||
}
|
||||
mCachedRangingCapabilities = builder.build();
|
||||
}
|
||||
return mCachedRangingCapabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,9 +86,35 @@ public class RangingManager {
|
|||
* provided based on the rangingCapabilities of the device.
|
||||
*
|
||||
* @param sessionParameters parameters used to setup the session.
|
||||
* @return the created RangingSession.
|
||||
* @return the created RangingSession. Null if session creation failed.
|
||||
* @throws IllegalArgumentException if sessionParameters is invalid.
|
||||
*/
|
||||
public RangingSession createSession(SessionParameters sessionParameters) {
|
||||
@Nullable
|
||||
public RangingSession createSession(@NonNull SessionParameters sessionParameters) {
|
||||
Preconditions.checkNotNull(sessionParameters, "sessionParameters must not be null");
|
||||
switch (sessionParameters.getRangingMethod()) {
|
||||
case RANGING_METHOD_UWB:
|
||||
if (mUwbServiceImpl == null) {
|
||||
Log.w(TAG, "createSession with UWB failed - UWB not supported");
|
||||
break;
|
||||
}
|
||||
return new UwbRangingSession(mContext, sessionParameters, mUwbServiceImpl);
|
||||
case RANGING_METHOD_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Initiation required for ranging with UWB. */
|
||||
private void initiateUwb() {
|
||||
UwbFeatureFlags uwbFeatureFlags =
|
||||
new UwbFeatureFlags.Builder()
|
||||
.setSkipRangingCapabilitiesCheck(
|
||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2)
|
||||
.setReversedByteOrderFiraParams(
|
||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU)
|
||||
.build();
|
||||
mUwbServiceImpl = new UwbServiceImpl(mContext, uwbFeatureFlags);
|
||||
Log.i(TAG, "RangingManager initiateUwb complete");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,16 @@
|
|||
package com.android.server.remoteauth.ranging;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.remoteauth.util.Crypto;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.Executor;
|
||||
|
|
@ -35,18 +42,50 @@ import java.util.concurrent.Executor;
|
|||
* <p>Ranging method specific implementation shall be implemented in the extended class.
|
||||
*/
|
||||
public abstract class RangingSession {
|
||||
private static final String TAG = "RangingSession";
|
||||
|
||||
/** Types of ranging error. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(
|
||||
value = {
|
||||
RANGING_ERROR_UNKNOWN,
|
||||
RANGING_ERROR_INVALID_PARAMETERS,
|
||||
RANGING_ERROR_STOPPED_BY_REQUEST,
|
||||
RANGING_ERROR_STOPPED_BY_PEER,
|
||||
RANGING_ERROR_FAILED_TO_START,
|
||||
RANGING_ERROR_FAILED_TO_STOP,
|
||||
RANGING_ERROR_SYSTEM_ERROR,
|
||||
RANGING_ERROR_SYSTEM_TIMEOUT,
|
||||
})
|
||||
public @interface RangingError {}
|
||||
|
||||
/** Unknown ranging error type. */
|
||||
public static final int RANGING_ERROR_UNKNOWN = 0x0;
|
||||
|
||||
/** Ranging error due to invalid parameters. */
|
||||
public static final int RANGING_ERROR_INVALID_PARAMETERS = 0x1;
|
||||
|
||||
/** Ranging error due to stopped by calling {@link #stop}. */
|
||||
public static final int RANGING_ERROR_STOPPED_BY_REQUEST = 0x2;
|
||||
|
||||
/** Ranging error due to stopped by the peer device. */
|
||||
public static final int RANGING_ERROR_STOPPED_BY_PEER = 0x3;
|
||||
|
||||
/** Ranging error due to failure to start ranging. */
|
||||
public static final int RANGING_ERROR_FAILED_TO_START = 0x4;
|
||||
|
||||
/** Ranging error due to failure to stop ranging. */
|
||||
public static final int RANGING_ERROR_FAILED_TO_STOP = 0x5;
|
||||
|
||||
/**
|
||||
* Ranging error due to system error cause by changes such as privacy policy, power management
|
||||
* policy, permissions, and more.
|
||||
*/
|
||||
public static final int RANGING_ERROR_SYSTEM_ERROR = 0x6;
|
||||
|
||||
/** Ranging error due to system timeout in retry attempts. */
|
||||
public static final int RANGING_ERROR_SYSTEM_TIMEOUT = 0x7;
|
||||
|
||||
/** Interface for ranging update callbacks. */
|
||||
public interface RangingCallback {
|
||||
/**
|
||||
|
|
@ -55,7 +94,8 @@ public abstract class RangingSession {
|
|||
* @param sessionInfo info about this ranging session.
|
||||
* @param rangingReport new ranging report
|
||||
*/
|
||||
void onRangingReport(SessionInfo sessionInfo, RangingReport rangingReport);
|
||||
void onRangingReport(
|
||||
@NonNull SessionInfo sessionInfo, @NonNull RangingReport rangingReport);
|
||||
|
||||
/**
|
||||
* Call upon any ranging error events.
|
||||
|
|
@ -63,7 +103,49 @@ public abstract class RangingSession {
|
|||
* @param sessionInfo info about this ranging session.
|
||||
* @param rangingError error type
|
||||
*/
|
||||
void onError(SessionInfo sessionInfo, @RangingError int rangingError);
|
||||
void onError(@NonNull SessionInfo sessionInfo, @RangingError int rangingError);
|
||||
}
|
||||
|
||||
protected Context mContext;
|
||||
protected SessionInfo mSessionInfo;
|
||||
protected float mLowerProximityBoundaryM;
|
||||
protected float mUpperProximityBoundaryM;
|
||||
protected boolean mAutoDeriveParams;
|
||||
protected byte[] mBaseKey;
|
||||
protected byte[] mSyncData;
|
||||
protected int mSyncCounter;
|
||||
protected byte[] mDerivedData;
|
||||
protected int mDerivedDataLength;
|
||||
|
||||
protected RangingSession(
|
||||
@NonNull Context context,
|
||||
@NonNull SessionParameters sessionParameters,
|
||||
int derivedDataLength) {
|
||||
Preconditions.checkNotNull(context);
|
||||
Preconditions.checkNotNull(sessionParameters);
|
||||
mContext = context;
|
||||
mSessionInfo =
|
||||
new SessionInfo.Builder()
|
||||
.setDeviceId(sessionParameters.getDeviceId())
|
||||
.setRangingMethod(sessionParameters.getRangingMethod())
|
||||
.build();
|
||||
mLowerProximityBoundaryM = sessionParameters.getLowerProximityBoundaryM();
|
||||
mUpperProximityBoundaryM = sessionParameters.getUpperProximityBoundaryM();
|
||||
mAutoDeriveParams = sessionParameters.getAutoDeriveParams();
|
||||
Log.i(
|
||||
TAG,
|
||||
"Creating a new RangingSession {info = "
|
||||
+ mSessionInfo
|
||||
+ ", autoDeriveParams = "
|
||||
+ mAutoDeriveParams
|
||||
+ "}");
|
||||
if (mAutoDeriveParams) {
|
||||
Preconditions.checkArgument(
|
||||
derivedDataLength > 0, "derivedDataLength must be greater than 0");
|
||||
mDerivedDataLength = derivedDataLength;
|
||||
resetBaseKey(sessionParameters.getBaseKey());
|
||||
resetSyncData(sessionParameters.getSyncData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,6 +157,8 @@ public abstract class RangingSession {
|
|||
* @param rangingParameters parameters to start the ranging.
|
||||
* @param executor Executor to run the rangingCallback.
|
||||
* @param rangingCallback callback to notify of ranging events.
|
||||
* @throws NullPointerException if params are null.
|
||||
* @throws IllegalArgumentException if rangingParameters is invalid.
|
||||
*/
|
||||
public abstract void start(
|
||||
@NonNull RangingParameters rangingParameters,
|
||||
|
|
@ -94,8 +178,22 @@ public abstract class RangingSession {
|
|||
* the secure connection between the devices is lost.
|
||||
*
|
||||
* @param baseKey new baseKey must be 16 or 32 bytes.
|
||||
* @throws NullPointerException if baseKey is null.
|
||||
* @throws IllegalArgumentException if baseKey has invalid length.
|
||||
*/
|
||||
public void resetBaseKey(byte[] baseKey) {}
|
||||
public void resetBaseKey(@NonNull byte[] baseKey) {
|
||||
if (!mAutoDeriveParams) {
|
||||
Log.w(TAG, "autoDeriveParams is disabled, new baseKey is ignored.");
|
||||
return;
|
||||
}
|
||||
Preconditions.checkNotNull(baseKey);
|
||||
if (baseKey.length != 16 && baseKey.length != 32) {
|
||||
throw new IllegalArgumentException("Invalid baseKey length: " + baseKey.length);
|
||||
}
|
||||
mBaseKey = baseKey;
|
||||
updateDerivedData();
|
||||
Log.i(TAG, "resetBaseKey");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the synchronization by giving a new syncData used for ranging parameters derivation.
|
||||
|
|
@ -105,6 +203,52 @@ public abstract class RangingSession {
|
|||
* manner.
|
||||
*
|
||||
* @param syncData new syncData must be 16 bytes.
|
||||
* @throws NullPointerException if baseKey is null.
|
||||
* @throws IllegalArgumentException if syncData has invalid length.
|
||||
*/
|
||||
public void resetSyncData(byte[] syncData) {}
|
||||
public void resetSyncData(@NonNull byte[] syncData) {
|
||||
if (!mAutoDeriveParams) {
|
||||
Log.w(TAG, "autoDeriveParams is disabled, new syncData is ignored.");
|
||||
return;
|
||||
}
|
||||
Preconditions.checkNotNull(syncData);
|
||||
if (syncData.length != 16) {
|
||||
throw new IllegalArgumentException("Invalid syncData length: " + syncData.length);
|
||||
}
|
||||
mSyncData = syncData;
|
||||
mSyncCounter = 0;
|
||||
updateDerivedData();
|
||||
Log.i(TAG, "resetSyncData");
|
||||
}
|
||||
|
||||
/** Recomputes mDerivedData using the latest mBaseKey, mSyncData, and mSyncCounter. */
|
||||
protected boolean updateDerivedData() {
|
||||
if (!mAutoDeriveParams) {
|
||||
Log.w(TAG, "autoDeriveParams is disabled, updateDerivedData is skipped.");
|
||||
return false;
|
||||
}
|
||||
if (mBaseKey == null
|
||||
|| mBaseKey.length == 0
|
||||
|| mSyncData == null
|
||||
|| mSyncData.length == 0) {
|
||||
Log.w(TAG, "updateDerivedData: Missing baseKey/syncData");
|
||||
return false;
|
||||
}
|
||||
byte[] hashedSyncData =
|
||||
Hashing.sha256()
|
||||
.newHasher()
|
||||
.putBytes(mSyncData)
|
||||
.putInt(mSyncCounter)
|
||||
.hash()
|
||||
.asBytes();
|
||||
byte[] newDerivedData = Crypto.computeHkdf(mBaseKey, hashedSyncData, mDerivedDataLength);
|
||||
if (newDerivedData == null) {
|
||||
Log.w(TAG, "updateDerivedData: computeHkdf failed");
|
||||
return false;
|
||||
}
|
||||
mDerivedData = newDerivedData;
|
||||
mSyncCounter++;
|
||||
Log.i(TAG, "updateDerivedData");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@ public class SessionInfo {
|
|||
mRangingMethod = rangingMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SessionInfo { "
|
||||
+ "DeviceId = "
|
||||
+ mDeviceId
|
||||
+ "RangingMethod = "
|
||||
+ mRangingMethod
|
||||
+ " }";
|
||||
}
|
||||
|
||||
/** Builder class for {@link SessionInfo}. */
|
||||
public static final class Builder {
|
||||
private String mDeviceId = "";
|
||||
|
|
|
|||
|
|
@ -19,9 +19,14 @@ import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_
|
|||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.remoteauth.ranging.RangingCapabilities.RangingMethod;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* The set of parameters to create a ranging session.
|
||||
*
|
||||
|
|
@ -31,9 +36,29 @@ import com.android.server.remoteauth.ranging.RangingCapabilities.RangingMethod;
|
|||
*/
|
||||
public class SessionParameters {
|
||||
|
||||
/** Ranging device role. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(
|
||||
value = {
|
||||
DEVICE_ROLE_UNKNOWN,
|
||||
DEVICE_ROLE_INITIATOR,
|
||||
DEVICE_ROLE_RESPONDER,
|
||||
})
|
||||
public @interface DeviceRole {}
|
||||
|
||||
/** Unknown device role. */
|
||||
public static final int DEVICE_ROLE_UNKNOWN = 0x0;
|
||||
|
||||
/** Device that initiates the ranging. */
|
||||
public static final int DEVICE_ROLE_INITIATOR = 0x1;
|
||||
|
||||
/** Device that responds to ranging. */
|
||||
public static final int DEVICE_ROLE_RESPONDER = 0x2;
|
||||
|
||||
/* Required parameters */
|
||||
private final String mDeviceId;
|
||||
@RangingMethod private final int mRangingMethod;
|
||||
@DeviceRole private final int mDeviceRole;
|
||||
|
||||
/* Optional parameters */
|
||||
private final float mLowerProximityBoundaryM;
|
||||
|
|
@ -51,6 +76,11 @@ public class SessionParameters {
|
|||
return mRangingMethod;
|
||||
}
|
||||
|
||||
@DeviceRole
|
||||
public int getDeviceRole() {
|
||||
return mDeviceRole;
|
||||
}
|
||||
|
||||
public float getLowerProximityBoundaryM() {
|
||||
return mLowerProximityBoundaryM;
|
||||
}
|
||||
|
|
@ -74,6 +104,7 @@ public class SessionParameters {
|
|||
private SessionParameters(
|
||||
String deviceId,
|
||||
@RangingMethod int rangingMethod,
|
||||
@DeviceRole int deviceRole,
|
||||
float lowerProximityBoundaryM,
|
||||
float upperProximityBoundaryM,
|
||||
boolean autoDeriveParams,
|
||||
|
|
@ -81,6 +112,7 @@ public class SessionParameters {
|
|||
byte[] syncData) {
|
||||
mDeviceId = deviceId;
|
||||
mRangingMethod = rangingMethod;
|
||||
mDeviceRole = deviceRole;
|
||||
mLowerProximityBoundaryM = lowerProximityBoundaryM;
|
||||
mUpperProximityBoundaryM = upperProximityBoundaryM;
|
||||
mAutoDeriveParams = autoDeriveParams;
|
||||
|
|
@ -92,6 +124,7 @@ public class SessionParameters {
|
|||
public static final class Builder {
|
||||
private String mDeviceId = new String("");
|
||||
@RangingMethod private int mRangingMethod = RANGING_METHOD_UNKNOWN;
|
||||
@DeviceRole private int mDeviceRole = DEVICE_ROLE_UNKNOWN;
|
||||
private float mLowerProximityBoundaryM;
|
||||
private float mUpperProximityBoundaryM;
|
||||
private boolean mAutoDeriveParams = false;
|
||||
|
|
@ -120,6 +153,12 @@ public class SessionParameters {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Sets the {@link DeviceRole} to be used for the {@link RangingSession}. */
|
||||
public Builder setDeviceRole(@DeviceRole int deviceRole) {
|
||||
mDeviceRole = deviceRole;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lower proximity boundary in meters, must be greater than or equals to zero.
|
||||
*
|
||||
|
|
@ -191,6 +230,7 @@ public class SessionParameters {
|
|||
Preconditions.checkArgument(!mDeviceId.isEmpty(), "deviceId must not be empty.");
|
||||
Preconditions.checkArgument(
|
||||
mRangingMethod != RANGING_METHOD_UNKNOWN, "Unknown rangingMethod");
|
||||
Preconditions.checkArgument(mDeviceRole != DEVICE_ROLE_UNKNOWN, "Unknown deviceRole");
|
||||
Preconditions.checkArgument(
|
||||
mLowerProximityBoundaryM >= 0,
|
||||
"Negative lowerProximityBoundaryM: " + mLowerProximityBoundaryM);
|
||||
|
|
@ -212,6 +252,7 @@ public class SessionParameters {
|
|||
return new SessionParameters(
|
||||
mDeviceId,
|
||||
mRangingMethod,
|
||||
mDeviceRole,
|
||||
mLowerProximityBoundaryM,
|
||||
mUpperProximityBoundaryM,
|
||||
mAutoDeriveParams,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.remoteauth.ranging;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.core.uwb.backend.impl.internal.UwbServiceImpl;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** UWB (ultra wide-band) implementation of {@link RangingSession}. */
|
||||
public class UwbRangingSession extends RangingSession {
|
||||
private static final int DERIVED_DATA_LENGTH = 1;
|
||||
|
||||
public UwbRangingSession(
|
||||
@NonNull Context context,
|
||||
@NonNull SessionParameters sessionParameters,
|
||||
@NonNull UwbServiceImpl uwbServiceImpl) {
|
||||
super(context, sessionParameters, DERIVED_DATA_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(
|
||||
@NonNull RangingParameters rangingParameters,
|
||||
@NonNull Executor executor,
|
||||
@NonNull RangingCallback rangingCallback) {}
|
||||
|
||||
@Override
|
||||
public void stop() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.remoteauth.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/** Utility class of cryptographic functions. */
|
||||
public final class Crypto {
|
||||
private static final String TAG = "Crypto";
|
||||
private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
|
||||
|
||||
/**
|
||||
* A HAMC sha256 based HKDF algorithm to pseudo randomly hash data and salt into a byte array of
|
||||
* given size.
|
||||
*
|
||||
* @param ikm the input keying material.
|
||||
* @param salt A possibly non-secret random value.
|
||||
* @param size The length of the generated pseudorandom string in bytes. The maximal size is
|
||||
* 255.DigestSize, where DigestSize is the size of the underlying HMAC.
|
||||
* @return size pseudorandom bytes, null if failed.
|
||||
*/
|
||||
// Based on
|
||||
// google3/third_party/tink/java_src/src/main/java/com/google/crypto/tink/subtle/Hkdf.java
|
||||
@Nullable
|
||||
public static byte[] computeHkdf(byte[] ikm, byte[] salt, int size) {
|
||||
Mac mac;
|
||||
try {
|
||||
mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w(TAG, "HMAC_SHA256_ALGORITHM is not supported.", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (size > 255 * mac.getMacLength()) {
|
||||
Log.w(TAG, "Size too large. " + size + " > " + 255 * mac.getMacLength());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ikm == null || ikm.length == 0) {
|
||||
Log.w(TAG, "Ikm cannot be empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (salt == null || salt.length == 0) {
|
||||
Log.w(TAG, "Salt cannot be empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
mac.init(new SecretKeySpec(salt, HMAC_SHA256_ALGORITHM));
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w(TAG, "Invalid key.", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] prk = mac.doFinal(ikm);
|
||||
byte[] result = new byte[size];
|
||||
try {
|
||||
mac.init(new SecretKeySpec(prk, HMAC_SHA256_ALGORITHM));
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w(TAG, "Invalid key.", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] digest = new byte[0];
|
||||
int ctr = 1;
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
mac.update(digest);
|
||||
mac.update((byte) ctr);
|
||||
digest = mac.doFinal();
|
||||
if (pos + digest.length < size) {
|
||||
System.arraycopy(digest, 0, result, pos, digest.length);
|
||||
pos += digest.length;
|
||||
ctr++;
|
||||
} else {
|
||||
System.arraycopy(digest, 0, result, pos, size - pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -35,13 +35,20 @@ android_test {
|
|||
static_libs: [
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.test.rules",
|
||||
"com.uwb.support.generic",
|
||||
"framework-remoteauth-static",
|
||||
"junit",
|
||||
"libprotobuf-java-lite",
|
||||
"mockito-target-extended-minus-junit4",
|
||||
"platform-test-annotations",
|
||||
"service-remoteauth-pre-jarjar",
|
||||
"truth-prebuilt",
|
||||
],
|
||||
// these are needed for Extended Mockito
|
||||
jni_libs: [
|
||||
"libdexmakerjvmtiagent",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"mts-tethering",
|
||||
|
|
|
|||
|
|
@ -28,16 +28,25 @@ import org.junit.runner.RunWith;
|
|||
/** Unit test for {@link RangingCapabilities}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RangingCapabilitiesTest {
|
||||
private static final androidx.core.uwb.backend.impl.internal.RangingCapabilities
|
||||
TEST_UWB_RANGING_CAPABILITIES =
|
||||
new androidx.core.uwb.backend.impl.internal.RangingCapabilities(
|
||||
/* supportsDistance= */ true,
|
||||
/* supportsAzimuthalAngle= */ true,
|
||||
/* supportsElevationAngle= */ true);
|
||||
|
||||
@Test
|
||||
public void testBuildingRangingCapabilities_success() {
|
||||
final RangingCapabilities rangingCapabilities =
|
||||
new RangingCapabilities.Builder()
|
||||
.addSupportedRangingMethods(RANGING_METHOD_UWB)
|
||||
.setUwbRangingCapabilities(TEST_UWB_RANGING_CAPABILITIES)
|
||||
.build();
|
||||
|
||||
assertEquals(rangingCapabilities.getSupportedRangingMethods().size(), 1);
|
||||
assertEquals(
|
||||
(int) rangingCapabilities.getSupportedRangingMethods().get(0), RANGING_METHOD_UWB);
|
||||
assertEquals(
|
||||
rangingCapabilities.getUwbRangingCapabilities(), TEST_UWB_RANGING_CAPABILITIES);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.remoteauth.ranging;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_UWB;
|
||||
import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
|
||||
|
||||
import static androidx.core.uwb.backend.impl.internal.RangingCapabilities.FIRA_DEFAULT_SUPPORTED_CONFIG_IDS;
|
||||
import static androidx.core.uwb.backend.impl.internal.Utils.CONFIG_PROVISIONED_INDIVIDUAL_MULTICAST_DS_TWR;
|
||||
import static androidx.core.uwb.backend.impl.internal.Utils.CONFIG_PROVISIONED_MULTICAST_DS_TWR;
|
||||
import static androidx.core.uwb.backend.impl.internal.Utils.CONFIG_PROVISIONED_UNICAST_DS_TWR;
|
||||
import static androidx.core.uwb.backend.impl.internal.Utils.CONFIG_PROVISIONED_UNICAST_DS_TWR_NO_AOA;
|
||||
|
||||
import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_METHOD_UWB;
|
||||
import static com.android.server.remoteauth.ranging.SessionParameters.DEVICE_ROLE_INITIATOR;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.uwb.UwbManager;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.server.remoteauth.ranging.RangingCapabilities.RangingMethod;
|
||||
import com.android.server.remoteauth.ranging.SessionParameters.DeviceRole;
|
||||
|
||||
import com.google.uwb.support.fira.FiraParams;
|
||||
import com.google.uwb.support.fira.FiraSpecificationParams;
|
||||
import com.google.uwb.support.generic.GenericSpecificationParams;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/** Unit test for {@link RangingManager}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RangingManagerTest {
|
||||
private static final List<Integer> TEST_UWB_SUPPORTED_CHANNELS = List.of(8, 9);
|
||||
private static final FiraSpecificationParams TEST_FIRA_SPEC =
|
||||
new FiraSpecificationParams.Builder()
|
||||
.setSupportedChannels(TEST_UWB_SUPPORTED_CHANNELS)
|
||||
.setStsCapabilities(EnumSet.allOf(FiraParams.StsCapabilityFlag.class))
|
||||
.build();
|
||||
private static final GenericSpecificationParams TEST_GENERIC_SPEC =
|
||||
new GenericSpecificationParams.Builder()
|
||||
.setFiraSpecificationParams(TEST_FIRA_SPEC)
|
||||
.build();
|
||||
private static final String TEST_DEVICE_ID = "test_device_id";
|
||||
@RangingMethod private static final int TEST_RANGING_METHOD = RANGING_METHOD_UWB;
|
||||
@DeviceRole private static final int TEST_DEVICE_ROLE = DEVICE_ROLE_INITIATOR;
|
||||
private static final float TEST_LOWER_PROXIMITY_BOUNDARY_M = 1.0f;
|
||||
private static final float TEST_UPPER_PROXIMITY_BOUNDARY_M = 2.5f;
|
||||
private static final boolean TEST_AUTO_DERIVE_PARAMS = true;
|
||||
private static final byte[] TEST_BASE_KEY =
|
||||
new byte[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f
|
||||
};
|
||||
private static final byte[] TEST_SYNC_DATA =
|
||||
new byte[] {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x00
|
||||
};
|
||||
private static final SessionParameters TEST_SESSION_PARAMETER =
|
||||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
.setSyncData(TEST_SYNC_DATA)
|
||||
.build();
|
||||
|
||||
@Mock private Context mContext;
|
||||
@Mock private PackageManager mPackageManager;
|
||||
@Mock private UwbManager mUwbManager;
|
||||
|
||||
private RangingManager mRangingManager;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mContext.getSystemService(UwbManager.class)).thenReturn(mUwbManager);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(false);
|
||||
when(mUwbManager.getAdapterState()).thenReturn(STATE_ENABLED_INACTIVE);
|
||||
when(mUwbManager.getSpecificationInfo()).thenReturn(TEST_GENERIC_SPEC.toBundle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstruction() {
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
verifyZeroInteractions(mUwbManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstruction_withUwbEnabled() {
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(true);
|
||||
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
|
||||
verify(mUwbManager).getAdapterState();
|
||||
verify(mUwbManager).registerAdapterStateCallback(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShutdown_withUwbEnabled() {
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(true);
|
||||
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
mRangingManager.shutdown();
|
||||
|
||||
verify(mUwbManager).registerAdapterStateCallback(any(), any());
|
||||
verify(mUwbManager).unregisterAdapterStateCallback(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangingCapabilities() {
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
RangingCapabilities capabilities = mRangingManager.getRangingCapabilities();
|
||||
|
||||
assertEquals(capabilities.getSupportedRangingMethods().size(), 0);
|
||||
assertEquals(capabilities.getUwbRangingCapabilities(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangingCapabilities_withUwbEnabled() {
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(true);
|
||||
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
RangingCapabilities capabilities = mRangingManager.getRangingCapabilities();
|
||||
|
||||
List<Integer> supportedConfigIds = new ArrayList<>(FIRA_DEFAULT_SUPPORTED_CONFIG_IDS);
|
||||
supportedConfigIds.add(CONFIG_PROVISIONED_UNICAST_DS_TWR);
|
||||
supportedConfigIds.add(CONFIG_PROVISIONED_MULTICAST_DS_TWR);
|
||||
supportedConfigIds.add(CONFIG_PROVISIONED_UNICAST_DS_TWR_NO_AOA);
|
||||
supportedConfigIds.add(CONFIG_PROVISIONED_INDIVIDUAL_MULTICAST_DS_TWR);
|
||||
|
||||
verify(mUwbManager, times(1)).getSpecificationInfo();
|
||||
assertEquals(capabilities.getSupportedRangingMethods().size(), 1);
|
||||
assertEquals((int) capabilities.getSupportedRangingMethods().get(0), RANGING_METHOD_UWB);
|
||||
androidx.core.uwb.backend.impl.internal.RangingCapabilities uwbCapabilities =
|
||||
capabilities.getUwbRangingCapabilities();
|
||||
assertNotNull(uwbCapabilities);
|
||||
assertArrayEquals(
|
||||
uwbCapabilities.getSupportedChannels().toArray(),
|
||||
TEST_UWB_SUPPORTED_CHANNELS.toArray());
|
||||
assertArrayEquals(
|
||||
uwbCapabilities.getSupportedConfigIds().toArray(), supportedConfigIds.toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRangingCapabilities_multipleCalls() {
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(true);
|
||||
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
RangingCapabilities capabilities1 = mRangingManager.getRangingCapabilities();
|
||||
RangingCapabilities capabilities2 = mRangingManager.getRangingCapabilities();
|
||||
RangingCapabilities capabilities3 = mRangingManager.getRangingCapabilities();
|
||||
|
||||
verify(mUwbManager, times(1)).getSpecificationInfo();
|
||||
assertEquals(capabilities1, capabilities2);
|
||||
assertEquals(capabilities2, capabilities3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSession_nullSessionParameters() {
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
|
||||
assertThrows(NullPointerException.class, () -> mRangingManager.createSession(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSession_uwbSessionWithUwbDisabled() {
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
|
||||
assertNull(mRangingManager.createSession(TEST_SESSION_PARAMETER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSession_uwbSession() {
|
||||
when(mPackageManager.hasSystemFeature(FEATURE_UWB)).thenReturn(true);
|
||||
mRangingManager = new RangingManager(mContext);
|
||||
|
||||
assertNotNull(mRangingManager.createSession(TEST_SESSION_PARAMETER));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.remoteauth.ranging;
|
||||
|
||||
import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_METHOD_UWB;
|
||||
import static com.android.server.remoteauth.ranging.SessionParameters.DEVICE_ROLE_INITIATOR;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.server.remoteauth.ranging.RangingCapabilities.RangingMethod;
|
||||
import com.android.server.remoteauth.ranging.SessionParameters.DeviceRole;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** Unit test for {@link RangingSession}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RangingSessionTest {
|
||||
|
||||
private static final String TEST_DEVICE_ID = "test_device_id";
|
||||
@RangingMethod private static final int TEST_RANGING_METHOD = RANGING_METHOD_UWB;
|
||||
@DeviceRole private static final int TEST_DEVICE_ROLE = DEVICE_ROLE_INITIATOR;
|
||||
private static final float TEST_LOWER_PROXIMITY_BOUNDARY_M = 1.0f;
|
||||
private static final float TEST_UPPER_PROXIMITY_BOUNDARY_M = 2.5f;
|
||||
private static final byte[] TEST_BASE_KEY =
|
||||
new byte[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f
|
||||
};
|
||||
private static final byte[] TEST_BASE_KEY2 =
|
||||
new byte[] {
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0,
|
||||
0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
|
||||
};
|
||||
private static final byte[] TEST_SYNC_DATA =
|
||||
new byte[] {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x00
|
||||
};
|
||||
private static final byte[] TEST_SYNC_DATA2 =
|
||||
new byte[] {
|
||||
0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x00
|
||||
};
|
||||
|
||||
private static final SessionParameters TEST_SESSION_PARAMETER_WITH_AD =
|
||||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(true)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
.setSyncData(TEST_SYNC_DATA)
|
||||
.build();
|
||||
private static final SessionParameters TEST_SESSION_PARAMETER_WO_AD =
|
||||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(false)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
.setSyncData(TEST_SYNC_DATA)
|
||||
.build();
|
||||
private static final int TEST_DERIVE_DATA_LENGTH = 40;
|
||||
|
||||
/** Wrapper class for testing {@link RangingSession}. */
|
||||
public static class RangingSessionWrapper extends RangingSession {
|
||||
public RangingSessionWrapper(
|
||||
Context context, SessionParameters sessionParameters, int derivedDataLength) {
|
||||
super(context, sessionParameters, derivedDataLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(
|
||||
RangingParameters rangingParameters,
|
||||
Executor executor,
|
||||
RangingCallback rangingCallback) {}
|
||||
|
||||
@Override
|
||||
public void stop() {}
|
||||
|
||||
@Override
|
||||
public boolean updateDerivedData() {
|
||||
return super.updateDerivedData();
|
||||
}
|
||||
|
||||
public byte[] baseKey() {
|
||||
return mBaseKey;
|
||||
}
|
||||
|
||||
public byte[] syncData() {
|
||||
return mSyncData;
|
||||
}
|
||||
|
||||
public byte[] derivedData() {
|
||||
return mDerivedData;
|
||||
}
|
||||
|
||||
public int syncCounter() {
|
||||
return mSyncCounter;
|
||||
}
|
||||
}
|
||||
|
||||
@Mock private Context mContext;
|
||||
|
||||
private RangingSessionWrapper mRangingSessionWithAD;
|
||||
private RangingSessionWrapper mRangingSessionWithoutAD;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mRangingSessionWithAD =
|
||||
new RangingSessionWrapper(
|
||||
mContext, TEST_SESSION_PARAMETER_WITH_AD, TEST_DERIVE_DATA_LENGTH);
|
||||
mRangingSessionWithoutAD =
|
||||
new RangingSessionWrapper(mContext, TEST_SESSION_PARAMETER_WO_AD, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetBaseKey_autoDeriveDisabled() {
|
||||
assertNull(mRangingSessionWithoutAD.baseKey());
|
||||
mRangingSessionWithoutAD.resetBaseKey(TEST_BASE_KEY2);
|
||||
assertNull(mRangingSessionWithoutAD.baseKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetBaseKey_nullBaseKey() {
|
||||
assertThrows(NullPointerException.class, () -> mRangingSessionWithAD.resetBaseKey(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetBaseKey_invalidBaseKey() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> mRangingSessionWithAD.resetBaseKey(new byte[] {0x1, 0x2, 0x3, 0x4}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetBaseKey_success() {
|
||||
mRangingSessionWithAD.resetBaseKey(TEST_BASE_KEY2);
|
||||
assertArrayEquals(mRangingSessionWithAD.baseKey(), TEST_BASE_KEY2);
|
||||
assertEquals(mRangingSessionWithAD.syncCounter(), 2);
|
||||
|
||||
mRangingSessionWithAD.resetBaseKey(TEST_BASE_KEY);
|
||||
assertArrayEquals(mRangingSessionWithAD.baseKey(), TEST_BASE_KEY);
|
||||
assertEquals(mRangingSessionWithAD.syncCounter(), 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetSyncData_autoDeriveDisabled() {
|
||||
assertNull(mRangingSessionWithoutAD.syncData());
|
||||
mRangingSessionWithoutAD.resetSyncData(TEST_SYNC_DATA2);
|
||||
assertNull(mRangingSessionWithoutAD.syncData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetSyncData_nullSyncData() {
|
||||
assertThrows(NullPointerException.class, () -> mRangingSessionWithAD.resetSyncData(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetSyncData_invalidSyncData() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> mRangingSessionWithAD.resetSyncData(new byte[] {0x1, 0x2, 0x3, 0x4}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResetSyncData_success() {
|
||||
mRangingSessionWithAD.resetSyncData(TEST_SYNC_DATA2);
|
||||
assertArrayEquals(mRangingSessionWithAD.syncData(), TEST_SYNC_DATA2);
|
||||
assertEquals(mRangingSessionWithAD.syncCounter(), 1);
|
||||
|
||||
mRangingSessionWithAD.resetSyncData(TEST_SYNC_DATA);
|
||||
assertArrayEquals(mRangingSessionWithAD.syncData(), TEST_SYNC_DATA);
|
||||
assertEquals(mRangingSessionWithAD.syncCounter(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDerivedData_autoDeriveDisabled() {
|
||||
assertFalse(mRangingSessionWithoutAD.updateDerivedData());
|
||||
assertEquals(mRangingSessionWithoutAD.syncCounter(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDerivedData_hkdfFailed() {
|
||||
// Max derivedDataLength is 32*255
|
||||
RangingSessionWrapper rangingSession =
|
||||
new RangingSessionWrapper(
|
||||
mContext, TEST_SESSION_PARAMETER_WITH_AD, /* derivedDataLength= */ 10000);
|
||||
assertNull(rangingSession.derivedData());
|
||||
assertFalse(rangingSession.updateDerivedData());
|
||||
assertEquals(rangingSession.syncCounter(), 0);
|
||||
assertNull(rangingSession.derivedData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDerivedData_success() {
|
||||
assertNotNull(mRangingSessionWithAD.derivedData());
|
||||
assertTrue(mRangingSessionWithAD.updateDerivedData());
|
||||
assertEquals(mRangingSessionWithAD.syncCounter(), 2);
|
||||
assertNotNull(mRangingSessionWithAD.derivedData());
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.server.remoteauth.ranging;
|
||||
|
||||
import static com.android.server.remoteauth.ranging.RangingCapabilities.RANGING_METHOD_UWB;
|
||||
import static com.android.server.remoteauth.ranging.SessionParameters.DEVICE_ROLE_INITIATOR;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
|
@ -25,6 +26,7 @@ import static org.junit.Assert.assertThrows;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.server.remoteauth.ranging.RangingCapabilities.RangingMethod;
|
||||
import com.android.server.remoteauth.ranging.SessionParameters.DeviceRole;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -35,6 +37,7 @@ public class SessionParametersTest {
|
|||
|
||||
private static final String TEST_DEVICE_ID = "test_device_id";
|
||||
@RangingMethod private static final int TEST_RANGING_METHOD = RANGING_METHOD_UWB;
|
||||
@DeviceRole private static final int TEST_DEVICE_ROLE = DEVICE_ROLE_INITIATOR;
|
||||
private static final float TEST_LOWER_PROXIMITY_BOUNDARY_M = 1.0f;
|
||||
private static final float TEST_UPPER_PROXIMITY_BOUNDARY_M = 2.5f;
|
||||
private static final boolean TEST_AUTO_DERIVE_PARAMS = true;
|
||||
|
|
@ -55,6 +58,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
|
|
@ -82,6 +86,7 @@ public class SessionParametersTest {
|
|||
final SessionParameters.Builder builder =
|
||||
new SessionParameters.Builder()
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
|
|
@ -95,6 +100,21 @@ public class SessionParametersTest {
|
|||
final SessionParameters.Builder builder =
|
||||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
.setSyncData(TEST_SYNC_DATA);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildingSessionParameters_invalidDeviceRole() {
|
||||
final SessionParameters.Builder builder =
|
||||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
|
|
@ -109,6 +129,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(-1.0f)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
|
|
@ -123,6 +144,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M - 0.1f)
|
||||
.setBaseKey(TEST_BASE_KEY)
|
||||
|
|
@ -138,6 +160,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(autoDeriveParams)
|
||||
|
|
@ -154,6 +177,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
|
|
@ -168,6 +192,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
|
|
@ -183,6 +208,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
|
|
@ -197,6 +223,7 @@ public class SessionParametersTest {
|
|||
new SessionParameters.Builder()
|
||||
.setDeviceId(TEST_DEVICE_ID)
|
||||
.setRangingMethod(TEST_RANGING_METHOD)
|
||||
.setDeviceRole(TEST_DEVICE_ROLE)
|
||||
.setLowerProximityBoundaryM(TEST_LOWER_PROXIMITY_BOUNDARY_M)
|
||||
.setUpperProximityBoundaryM(TEST_UPPER_PROXIMITY_BOUNDARY_M)
|
||||
.setAutoDeriveParams(TEST_AUTO_DERIVE_PARAMS)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 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 com.android.server.remoteauth.util;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link Crypto}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CryptoTest {
|
||||
private static final byte[] TEST_IKM =
|
||||
new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||
private static final byte[] TEST_SALT =
|
||||
new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00};
|
||||
private static final int TEST_SIZE = 40;
|
||||
|
||||
@Test
|
||||
public void testComputeHkdf_exceedMaxSize() {
|
||||
// Max size is 32*255
|
||||
assertNull(Crypto.computeHkdf(TEST_IKM, TEST_SALT, /* size= */ 10000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeHkdf_emptySalt() {
|
||||
assertNull(Crypto.computeHkdf(TEST_IKM, new byte[] {}, TEST_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeHkdf_emptyIkm() {
|
||||
assertNull(Crypto.computeHkdf(new byte[] {}, TEST_SALT, TEST_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeHkdf_success() {
|
||||
assertNotNull(Crypto.computeHkdf(TEST_IKM, TEST_SALT, TEST_SIZE));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue