Merge "Delay frozen app sockets close until the cellular modem wakes up" into main
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index d3b01ea..bb3dc24 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -110,7 +110,6 @@
],
apps: [
"ServiceConnectivityResources",
- "HalfSheetUX",
],
prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index bb09d0d..eadba58 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -283,6 +283,7 @@
private List<TetheredClient> mDhcpLeases = Collections.emptyList();
private int mLastIPv6UpstreamIfindex = 0;
+ private boolean mUpstreamSupportsBpf = false;
private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
public void accept(NeighborEvent e) {
@@ -779,15 +780,15 @@
// If v6only is null, we pass in null to setRaParams(), which handles
// deprecation of any existing RA data.
-
setRaParams(params);
- // Be aware that updateIpv6ForwardingRules use mLastIPv6LinkProperties, so this line should
- // be eariler than updateIpv6ForwardingRules.
- // TODO: avoid this dependencies and move this logic into BpfCoordinator.
- mLastIPv6LinkProperties = v6only;
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
+ // Not support BPF on virtual upstream interface
+ final boolean upstreamSupportsBpf = upstreamIface != null && !isVcnInterface(upstreamIface);
+ updateIpv6ForwardingRules(
+ mLastIPv6UpstreamIfindex, upstreamIfIndex, upstreamSupportsBpf, null);
+ mLastIPv6LinkProperties = v6only;
mLastIPv6UpstreamIfindex = upstreamIfIndex;
+ mUpstreamSupportsBpf = upstreamSupportsBpf;
if (mDadProxy != null) {
mDadProxy.setUpstreamIface(upstreamIfaceParams);
}
@@ -921,20 +922,14 @@
mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex);
}
- private boolean isIpv6VcnNetworkInterface() {
- if (mLastIPv6LinkProperties == null) return false;
-
- return isVcnInterface(mLastIPv6LinkProperties.getInterfaceName());
- }
-
// Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
// changes or if a neighbor event is received.
private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
- NeighborEvent e) {
- // If no longer have an upstream or it is virtual network, clear forwarding rules and do
+ boolean upstreamSupportsBpf, NeighborEvent e) {
+ // If no longer have an upstream or upstream not supports BPF, clear forwarding rules and do
// nothing else.
// TODO: Rather than always clear rules, ensure whether ipv6 ever enable first.
- if (upstreamIfindex == 0 || isIpv6VcnNetworkInterface()) {
+ if (upstreamIfindex == 0 || !upstreamSupportsBpf) {
clearIpv6ForwardingRules();
return;
}
@@ -995,7 +990,8 @@
if (mInterfaceParams != null
&& mInterfaceParams.index == e.ifindex
&& mInterfaceParams.hasMacAddress) {
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex,
+ mUpstreamSupportsBpf, e);
updateClientInfoIpv4(e);
}
}
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index dacdaf2..5ae1ef9 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -19,6 +19,14 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+framework_remoteauth_srcs = [":framework-remoteauth-java-sources"]
+framework_remoteauth_api_srcs = []
+
+java_defaults {
+ name: "enable-remoteauth-targets",
+ enabled: true,
+}
+
// Include build rules from Sources.bp
build = ["Sources.bp"]
@@ -43,8 +51,7 @@
":framework-connectivity-tiramisu-updatable-sources",
":framework-nearby-java-sources",
":framework-thread-sources",
- ":framework-remoteauth-java-sources",
- ],
+ ] + framework_remoteauth_srcs,
libs: [
"unsupportedappusage",
"app-compat-annotations",
@@ -115,6 +122,7 @@
"framework-connectivity-t-defaults",
"enable-framework-connectivity-t-targets",
],
+ api_srcs: framework_remoteauth_api_srcs,
// Do not add static_libs to this library: put them in framework-connectivity instead.
// The jarjar rules are only so that references to jarjared utils in
// framework-connectivity-pre-jarjar match at runtime.
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 42c83d8..5a8d47b 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -207,43 +207,3 @@
}
-package android.remoteauth {
-
- public interface DeviceDiscoveryCallback {
- method public void onDeviceUpdate(@NonNull android.remoteauth.RemoteDevice, int);
- method public void onTimeout();
- field public static final int STATE_LOST = 0; // 0x0
- field public static final int STATE_SEEN = 1; // 0x1
- }
-
- public final class RemoteAuthFrameworkInitializer {
- method public static void registerServiceWrappers();
- }
-
- public class RemoteAuthManager {
- method public boolean isRemoteAuthSupported();
- method public boolean startDiscovery(int, @NonNull java.util.concurrent.Executor, @NonNull android.remoteauth.DeviceDiscoveryCallback);
- method public void stopDiscovery(@NonNull android.remoteauth.DeviceDiscoveryCallback);
- }
-
- public final class RemoteDevice implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public int getConnectionId();
- method @Nullable public String getName();
- method public int getRegistrationState();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.remoteauth.RemoteDevice> CREATOR;
- field public static final int STATE_NOT_REGISTERED = 0; // 0x0
- field public static final int STATE_REGISTERED = 1; // 0x1
- }
-
- public static final class RemoteDevice.Builder {
- ctor public RemoteDevice.Builder(int);
- method @NonNull public android.remoteauth.RemoteDevice build();
- method @NonNull public android.remoteauth.RemoteDevice.Builder setConnectionId(int);
- method @NonNull public android.remoteauth.RemoteDevice.Builder setName(@Nullable String);
- method @NonNull public android.remoteauth.RemoteDevice.Builder setRegistrationState(int);
- }
-
-}
-
diff --git a/framework/Android.bp b/framework/Android.bp
index e663764..e577e6d 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -80,6 +80,7 @@
impl_only_libs: [
// TODO: figure out why just using "framework-tethering" uses the stubs, even though both
// framework-connectivity and framework-tethering are in the same APEX.
+ "framework-location.stubs.module_lib",
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
@@ -127,6 +128,7 @@
// to generate the connectivity stubs. That would create a circular dependency
// because the tethering impl depend on the connectivity stubs (e.g.,
// TetheringRequest depends on LinkAddress).
+ "framework-location.stubs.module_lib",
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
diff --git a/nearby/TEST_MAPPING b/nearby/TEST_MAPPING
index d68bcc9..7e9a375 100644
--- a/nearby/TEST_MAPPING
+++ b/nearby/TEST_MAPPING
@@ -2,20 +2,17 @@
"presubmit": [
{
"name": "NearbyUnitTests"
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "NearbyUnitTests"
},
{
"name": "NearbyIntegrationPrivilegedTests"
},
{
"name": "NearbyIntegrationUntrustedTests"
- },
- {
- "name": "NearbyIntegrationUiTests"
- }
- ],
- "postsubmit": [
- {
- "name": "NearbyUnitTests"
}
]
// TODO(b/193602229): uncomment once it's supported.
diff --git a/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
index f53e2dc..1414f7e 100644
--- a/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
+++ b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
@@ -16,10 +16,7 @@
package android.remoteauth;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import androidx.annotation.IntDef;
@@ -31,7 +28,7 @@
*
* @hide
*/
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
public interface DeviceDiscoveryCallback {
/** The device is no longer seen in the discovery process. */
int STATE_LOST = 0;
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
index dfd7726..112ffa8 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
@@ -16,7 +16,6 @@
package android.remoteauth;
-import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.content.Context;
@@ -25,7 +24,7 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
public final class RemoteAuthFrameworkInitializer {
private RemoteAuthFrameworkInitializer() {}
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
index c025a55..038af2a 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
@@ -16,14 +16,12 @@
package android.remoteauth;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.remoteauth.DeviceDiscoveryCallback.STATE_LOST;
import static android.remoteauth.DeviceDiscoveryCallback.STATE_SEEN;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserIdInt;
import android.content.Context;
@@ -47,7 +45,7 @@
*
* @hide
*/
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
// TODO(b/290092977): Change to Context.REMOTE_AUTH_SERVICE after aosp/2681375
// is automerges from aosp-main to udc-mainline-prod
@SystemService(RemoteAuthManager.REMOTE_AUTH_SERVICE)
@@ -79,7 +77,7 @@
* @return true if this device can be enrolled
* @hide
*/
- @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
// TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
public boolean isRemoteAuthSupported() {
try {
@@ -100,7 +98,7 @@
* @return {@code true} if discovery began successfully, {@code false} otherwise
* @hide
*/
- @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
// TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
public boolean startDiscovery(
int timeoutMs,
@@ -149,7 +147,7 @@
// Suppressed lint: Registration methods should have overload that accepts delivery Executor.
// Already have executor in startDiscovery() method.
@SuppressLint("ExecutorRegistration")
- @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
// TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
public void stopDiscovery(@NonNull DeviceDiscoveryCallback callback) {
Preconditions.checkNotNull(callback, "invalid null scanCallback");
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteDevice.java b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
index 4cd2399..b6ede2e 100644
--- a/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
+++ b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
@@ -16,12 +16,9 @@
package android.remoteauth;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,7 +32,7 @@
* @hide
*/
// TODO(b/295407748) Change to use @DataClass
-@SystemApi(client = MODULE_LIBRARIES)
+// TODO(b/290092977): Add back after M-2023-11 release - @SystemApi(client = MODULE_LIBRARIES)
public final class RemoteDevice implements Parcelable {
/** The remote device is not registered as remote authenticator. */
public static final int STATE_NOT_REGISTERED = 0;
diff --git a/remoteauth/service/Android.bp b/remoteauth/service/Android.bp
index 2ba59da..dfaf8cf 100644
--- a/remoteauth/service/Android.bp
+++ b/remoteauth/service/Android.bp
@@ -27,6 +27,7 @@
srcs: [":remoteauth-service-srcs"],
required: ["libremoteauth_jni_rust_defaults"],
defaults: [
+ "enable-remoteauth-targets",
"framework-system-server-module-defaults",
],
libs: [
@@ -47,6 +48,7 @@
"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+
diff --git a/remoteauth/service/java/com/android/server/remoteauth/README.md b/remoteauth/service/java/com/android/server/remoteauth/README.md
index b2b5aab..b659bf7 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/README.md
+++ b/remoteauth/service/java/com/android/server/remoteauth/README.md
@@ -6,3 +6,5 @@
## Ranging
Provides the ranging manager to perform ranging with the peer devices.
+## Util
+Common utilities.
diff --git a/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java
index 41ce89a..9374ace 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java
@@ -27,6 +27,7 @@
/** Service implementing remoteauth functionality. */
public class RemoteAuthService extends IRemoteAuthService.Stub {
public static final String TAG = "RemoteAuthService";
+ public static final String SERVICE_NAME = Context.REMOTE_AUTH_SERVICE;
public RemoteAuthService(Context context) {
Preconditions.checkNotNull(context);
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java
new file mode 100644
index 0000000..f79ec7e
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/INativeRemoteAuthService.java
@@ -0,0 +1,67 @@
+/*
+ * 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.jni;
+
+/**
+ * Interface defining a proxy between Rust and Java implementation of RemoteAuth protocol.
+ *
+ * @hide
+ */
+public interface INativeRemoteAuthService {
+ /**
+ * Interface for RemoteAuth PAL
+ *
+ * @hide
+ */
+ interface IPlatform {
+ /**
+ * Sends message to the remote authenticator
+ *
+ * @param connectionId connection ID of the {@link android.remoteauth.RemoteAuthenticator}
+ * @param request payload of the request
+ * @param callback to be used to pass the response result
+ *
+ * @hide
+ */
+ void sendRequest(int connectionId, byte[] request, ResponseCallback callback);
+
+ /**
+ * Interface for a callback to send a response back.
+ *
+ * @hide
+ */
+ interface ResponseCallback {
+ /**
+ * Invoked when message sending succeeds.
+ *
+ * @param response contains response
+ *
+ * @hide
+ */
+ void onSuccess(byte[] response);
+
+ /**
+ * Invoked when message sending fails.
+ *
+ * @param errorCode indicating the error
+ *
+ * @hide
+ */
+ void onFailure(int errorCode);
+ }
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java
new file mode 100644
index 0000000..39c2a74
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/NativeRemoteAuthService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.jni;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.remoteauth.jni.INativeRemoteAuthService.IPlatform;
+
+/**
+ * A service providing a proxy between Rust implementation and {@link
+ * com.android.server.remoteauth.RemoteAuthService}.
+ *
+ * @hide
+ */
+public class NativeRemoteAuthService {
+ private static final String TAG = NativeRemoteAuthService.class.getSimpleName();
+
+ private IPlatform mPlatform;
+ public final Object mNativeLock = new Object();
+
+ // Constructor should receive pointers to:
+ // ConnectivityManager, RangingManager and DB
+ public NativeRemoteAuthService() {
+ System.loadLibrary("remoteauth_jni_rust");
+ synchronized (mNativeLock) {
+ native_init();
+ }
+ }
+
+ public void setDeviceListener(final IPlatform platform) {
+ mPlatform = platform;
+ }
+
+ /**
+ * Sends message to the remote authenticator
+ *
+ * @param connectionId connection ID of the {@link android.remoteauth.RemoteAuthenticator}
+ * @param request payload of the request
+ * @param responseHandle a handle associated with the request, used to pass the response to the
+ * platform
+ * @param platformHandle a handle associated with the platform object, used to pass the response
+ * to the specific platform
+ *
+ * @hide
+ */
+ @Keep
+ public void sendRequest(
+ int connectionId, byte[] request, long responseHandle, long platformHandle) {
+ mPlatform.sendRequest(
+ connectionId,
+ request,
+ new IPlatform.ResponseCallback() {
+ @Override
+ public void onSuccess(byte[] response) {
+ synchronized (mNativeLock) {
+ native_on_send_request_success(
+ response, platformHandle, responseHandle);
+ }
+ }
+
+ @Override
+ public void onFailure(int errorCode) {
+ synchronized (mNativeLock) {
+ native_on_send_request_error(errorCode, platformHandle, responseHandle);
+ }
+ }
+ });
+ }
+
+ /* Native functions implemented in JNI */
+ // This function should be implemented in remoteauth_jni_android_protocol
+ private native boolean native_init();
+
+ private native void native_on_send_request_success(
+ byte[] appResponse, long platformHandle, long responseHandle);
+
+ private native void native_on_send_request_error(
+ int errorCode, long platformHandle, long responseHandle);
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java b/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java
new file mode 100644
index 0000000..3ae9838
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/jni/PlatformBadHandleException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+/**
+ * Represents an unrecoverable error (invalid handle) that has occurred during accessing the
+ * platform.
+ */
+package com.android.server.remoteauth.jni;
+
+import com.android.internal.annotations.Keep;
+/**
+ * Exception thrown by native platform rust implementation of {@link
+ * com.android.server.remoteauth.RemoteAuthService}.
+ *
+ * @hide
+ */
+@Keep
+public class PlatformBadHandleException extends Exception {
+ public PlatformBadHandleException(final String message) {
+ super(message);
+ }
+
+ public PlatformBadHandleException(final Exception e) {
+ super(e);
+ }
+
+ public PlatformBadHandleException(final String message, final Exception e) {
+ super(message, e);
+ }
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingCapabilities.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingCapabilities.java
index 2b5efff..36aa585 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingCapabilities.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingCapabilities.java
@@ -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 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 @@
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 @@
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);
}
}
}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingManager.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingManager.java
index 989b5ed..cf0db76 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingManager.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingManager.java
@@ -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 @@
* 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 @@
* 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");
+ }
}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingSession.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingSession.java
index 9ef6bda..adb36c5 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingSession.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/RangingSession.java
@@ -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 @@
* <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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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;
+ }
}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionInfo.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionInfo.java
index 5e4fc48..0ec640c 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionInfo.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionInfo.java
@@ -41,6 +41,16 @@
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 = "";
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionParameters.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionParameters.java
index 33c3203..2f71244 100644
--- a/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionParameters.java
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/SessionParameters.java
@@ -19,9 +19,14 @@
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 @@
*/
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 @@
return mRangingMethod;
}
+ @DeviceRole
+ public int getDeviceRole() {
+ return mDeviceRole;
+ }
+
public float getLowerProximityBoundaryM() {
return mLowerProximityBoundaryM;
}
@@ -74,6 +104,7 @@
private SessionParameters(
String deviceId,
@RangingMethod int rangingMethod,
+ @DeviceRole int deviceRole,
float lowerProximityBoundaryM,
float upperProximityBoundaryM,
boolean autoDeriveParams,
@@ -81,6 +112,7 @@
byte[] syncData) {
mDeviceId = deviceId;
mRangingMethod = rangingMethod;
+ mDeviceRole = deviceRole;
mLowerProximityBoundaryM = lowerProximityBoundaryM;
mUpperProximityBoundaryM = upperProximityBoundaryM;
mAutoDeriveParams = autoDeriveParams;
@@ -92,6 +124,7 @@
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 @@
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 @@
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 @@
return new SessionParameters(
mDeviceId,
mRangingMethod,
+ mDeviceRole,
mLowerProximityBoundaryM,
mUpperProximityBoundaryM,
mAutoDeriveParams,
diff --git a/remoteauth/service/java/com/android/server/remoteauth/ranging/UwbRangingSession.java b/remoteauth/service/java/com/android/server/remoteauth/ranging/UwbRangingSession.java
new file mode 100644
index 0000000..2015b66
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/ranging/UwbRangingSession.java
@@ -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() {}
+}
diff --git a/remoteauth/service/java/com/android/server/remoteauth/util/Crypto.java b/remoteauth/service/java/com/android/server/remoteauth/util/Crypto.java
new file mode 100644
index 0000000..573597f
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/util/Crypto.java
@@ -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;
+ }
+}
diff --git a/remoteauth/service/jni/src/lib.rs b/remoteauth/service/jni/src/lib.rs
index 0c18679..a816c94 100644
--- a/remoteauth/service/jni/src/lib.rs
+++ b/remoteauth/service/jni/src/lib.rs
@@ -19,6 +19,7 @@
mod jnames;
mod unique_jvm;
+mod utils;
-//pub mod remoteauth_jni_android_protocol;
pub mod remoteauth_jni_android_platform;
+pub mod remoteauth_jni_android_protocol;
diff --git a/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs b/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
index 4597561..1967c1a 100644
--- a/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
+++ b/remoteauth/service/jni/src/remoteauth_jni_android_platform.rs
@@ -15,7 +15,6 @@
use crate::jnames::{SEND_REQUEST_MNAME, SEND_REQUEST_MSIG};
use crate::unique_jvm;
use anyhow::anyhow;
-use async_trait::async_trait;
use jni::errors::Error as JNIError;
use jni::objects::{GlobalRef, JMethodID, JObject, JValue};
use jni::signature::TypeSignature;
@@ -26,11 +25,7 @@
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicI64, Ordering},
- Arc,
-};
-use tokio::{
- runtime::Runtime,
- sync::{mpsc, Mutex},
+ Arc, Mutex,
};
/// Macro capturing the name of the function calling this macro.
@@ -65,7 +60,7 @@
HANDLE_RN.fetch_add(1, Ordering::SeqCst)
}
-async fn insert_platform_handle(handle: i64, item: Arc<Mutex<JavaPlatform>>) {
+fn insert_platform_handle(handle: i64, item: Arc<Mutex<JavaPlatform>>) {
if 0 == handle {
// Init once
logger::init(
@@ -75,14 +70,22 @@
.with_filter("trace,jni=info"),
);
}
- HANDLE_MAPPING.lock().await.insert(handle, Arc::clone(&item));
+ HANDLE_MAPPING.lock().unwrap().insert(handle, Arc::clone(&item));
}
-#[async_trait]
+pub trait ResponseCallback {
+ fn on_response(&mut self, response: Vec<u8>);
+ fn on_error(&mut self, error_code: i32);
+}
+
pub trait Platform {
/// Send a binary message to the remote with the given connection id and return the response.
- async fn send_request(&mut self, connection_id: i32, request: &[u8])
- -> anyhow::Result<Vec<u8>>;
+ fn send_request(
+ &mut self,
+ connection_id: i32,
+ request: &[u8],
+ callback: Box<dyn ResponseCallback + Send>,
+ ) -> anyhow::Result<()>;
}
//////////////////////////////////
@@ -91,25 +94,22 @@
vm: &'static Arc<JavaVM>,
platform_native_obj: GlobalRef,
send_request_method_id: JMethodID,
- map_futures: Mutex<HashMap<i64, mpsc::Sender<Vec<u8>>>>,
+ map_futures: Mutex<HashMap<i64, Box<dyn ResponseCallback + Send>>>,
atomic_handle: AtomicI64,
}
impl JavaPlatform {
// Method to create JavaPlatform
- pub async fn create<'a>(
- env: JNIEnv<'a>,
- java_platform_native: JObject<'a>,
+ pub fn create(
+ java_platform_native: JObject<'_>,
) -> Result<Arc<Mutex<impl Platform>>, JNIError> {
- let jvm = env.get_java_vm()?;
- let _ = unique_jvm::set_once(jvm);
let platform_handle = generate_platform_handle();
let platform = Arc::new(Mutex::new(JavaPlatform::new(
platform_handle,
unique_jvm::get_static_ref().ok_or(JNIError::InvalidCtorReturn)?,
java_platform_native,
)?));
- insert_platform_handle(platform_handle, Arc::clone(&platform)).await;
+ insert_platform_handle(platform_handle, Arc::clone(&platform));
Ok(Arc::clone(&platform))
}
@@ -136,19 +136,18 @@
}
}
-#[async_trait]
impl Platform for JavaPlatform {
- async fn send_request(
+ fn send_request(
&mut self,
connection_id: i32,
request: &[u8],
- ) -> anyhow::Result<Vec<u8>> {
+ callback: Box<dyn ResponseCallback + Send>,
+ ) -> anyhow::Result<()> {
let type_signature = TypeSignature::from_str(SEND_REQUEST_MSIG)
.map_err(|e| anyhow!("JNI: Invalid type signature: {:?}", e))?;
- let (tx, mut rx) = mpsc::channel(1);
let response_handle = self.atomic_handle.fetch_add(1, Ordering::SeqCst);
- self.map_futures.lock().await.insert(response_handle, tx);
+ self.map_futures.lock().unwrap().insert(response_handle, callback);
self.vm
.attach_current_thread()
.and_then(|env| {
@@ -175,21 +174,20 @@
))
})
.map_err(|e| anyhow!("JNI: Failed to attach current thread: {:?}", e))?;
-
- rx.recv().await.ok_or(anyhow!("{} failed in awaiting for a result", function_name!()))
+ Ok(())
}
}
impl JavaPlatform {
- async fn on_send_request_success(&mut self, response: &[u8], response_handle: i64) {
+ fn on_send_request_success(&mut self, response: &[u8], response_handle: i64) {
info!(
"{} completed successfully {}:{}",
function_name!(),
self.platform_handle,
response_handle
);
- if let Some(tx) = self.map_futures.lock().await.remove(&response_handle) {
- let _ = tx.send(response.to_vec()).await;
+ if let Some(mut callback) = self.map_futures.lock().unwrap().remove(&response_handle) {
+ callback.on_response(response.to_vec());
} else {
error!(
"Failed to find TX for {} and {}:{}",
@@ -200,7 +198,7 @@
}
}
- async fn on_send_request_error(&self, error_code: i32, response_handle: i64) {
+ fn on_send_request_error(&self, error_code: i32, response_handle: i64) {
error!(
"{} completed with error {} {}:{}",
function_name!(),
@@ -208,12 +206,11 @@
self.platform_handle,
response_handle
);
- if let Some(tx) = self.map_futures.lock().await.remove(&response_handle) {
- // `rx.recv()` ends with `Err`
- drop(tx);
+ if let Some(mut callback) = self.map_futures.lock().unwrap().remove(&response_handle) {
+ callback.on_error(error_code);
} else {
error!(
- "Failed to find TX for {} and {}:{}",
+ "Failed to find callback for {} and {}:{}",
function_name!(),
self.platform_handle,
response_handle
@@ -231,25 +228,20 @@
response_handle: jlong,
) {
debug!("{}: enter", function_name!());
- Runtime::new().unwrap().block_on(native_on_send_request_success(
- env,
- app_response,
- platform_handle,
- response_handle,
- ));
+ native_on_send_request_success(env, app_response, platform_handle, response_handle);
}
-async fn native_on_send_request_success(
+fn native_on_send_request_success(
env: JNIEnv<'_>,
app_response: jbyteArray,
platform_handle: jlong,
response_handle: jlong,
) {
- if let Some(platform) = HANDLE_MAPPING.lock().await.get(&platform_handle) {
+ if let Some(platform) = HANDLE_MAPPING.lock().unwrap().get(&platform_handle) {
let response =
env.convert_byte_array(app_response).map_err(|_| JNIError::InvalidCtorReturn).unwrap();
- let mut platform = (*platform).lock().await;
- platform.on_send_request_success(&response, response_handle).await;
+ let mut platform = (*platform).lock().unwrap();
+ platform.on_send_request_success(&response, response_handle);
} else {
let _ = env.throw_new(
"com/android/server/remoteauth/jni/BadHandleException",
@@ -267,23 +259,18 @@
response_handle: jlong,
) {
debug!("{}: enter", function_name!());
- Runtime::new().unwrap().block_on(native_on_send_request_error(
- env,
- error_code,
- platform_handle,
- response_handle,
- ));
+ native_on_send_request_error(env, error_code, platform_handle, response_handle);
}
-async fn native_on_send_request_error(
+fn native_on_send_request_error(
env: JNIEnv<'_>,
error_code: jint,
platform_handle: jlong,
response_handle: jlong,
) {
- if let Some(platform) = HANDLE_MAPPING.lock().await.get(&platform_handle) {
- let platform = (*platform).lock().await;
- platform.on_send_request_error(error_code, response_handle).await;
+ if let Some(platform) = HANDLE_MAPPING.lock().unwrap().get(&platform_handle) {
+ let platform = (*platform).lock().unwrap();
+ platform.on_send_request_error(error_code, response_handle);
} else {
let _ = env.throw_new(
"com/android/server/remoteauth/jni/BadHandleException",
diff --git a/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs b/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs
new file mode 100644
index 0000000..1f73207
--- /dev/null
+++ b/remoteauth/service/jni/src/remoteauth_jni_android_protocol.rs
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+use crate::unique_jvm;
+use crate::utils::get_boolean_result;
+use jni::objects::JObject;
+use jni::sys::jboolean;
+use jni::JNIEnv;
+
+#[no_mangle]
+pub extern "system" fn Java_com_android_server_remoteauth_jni_NativeRemoteAuthJavaPlatform_native_init(
+ env: JNIEnv,
+ _: JObject,
+) -> jboolean {
+ logger::init(
+ logger::Config::default()
+ .with_tag_on_device("remoteauth")
+ .with_min_level(log::Level::Trace)
+ .with_filter("trace,jni=info"),
+ );
+ get_boolean_result(native_init(env), "native_init")
+}
+
+fn native_init(env: JNIEnv) -> anyhow::Result<()> {
+ let jvm = env.get_java_vm()?;
+ unique_jvm::set_once(jvm)
+}
diff --git a/remoteauth/service/jni/src/utils.rs b/remoteauth/service/jni/src/utils.rs
new file mode 100644
index 0000000..e61b895
--- /dev/null
+++ b/remoteauth/service/jni/src/utils.rs
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+use jni::sys::jboolean;
+use log::error;
+
+pub(crate) fn get_boolean_result<T>(result: anyhow::Result<T>, error_msg: &str) -> jboolean {
+ match result {
+ Ok(_) => true,
+ Err(e) => {
+ error!("{} failed with {:?}", error_msg, &e);
+ false
+ }
+ }
+ .into()
+}
diff --git a/remoteauth/tests/unit/Android.bp b/remoteauth/tests/unit/Android.bp
index 4b92d84..37c78c7 100644
--- a/remoteauth/tests/unit/Android.bp
+++ b/remoteauth/tests/unit/Android.bp
@@ -18,7 +18,10 @@
android_test {
name: "RemoteAuthUnitTests",
- defaults: ["mts-target-sdk-version-current"],
+ defaults: [
+ "enable-remoteauth-targets",
+ "mts-target-sdk-version-current"
+ ],
sdk_version: "test_current",
min_sdk_version: "31",
@@ -35,13 +38,20 @@
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",
diff --git a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingCapabilitiesTest.java b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingCapabilitiesTest.java
index e6b6e3b..8135b4f 100644
--- a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingCapabilitiesTest.java
+++ b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingCapabilitiesTest.java
@@ -28,16 +28,25 @@
/** 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);
}
}
diff --git a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingManagerTest.java b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingManagerTest.java
new file mode 100644
index 0000000..6e343bb
--- /dev/null
+++ b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingManagerTest.java
@@ -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));
+ }
+}
diff --git a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingSessionTest.java b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingSessionTest.java
new file mode 100644
index 0000000..0e547d6
--- /dev/null
+++ b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/RangingSessionTest.java
@@ -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());
+ }
+}
diff --git a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/SessionParametersTest.java b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/SessionParametersTest.java
index 357fdf9..522623e 100644
--- a/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/SessionParametersTest.java
+++ b/remoteauth/tests/unit/src/com/android/server/remoteauth/ranging/SessionParametersTest.java
@@ -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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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)
diff --git a/remoteauth/tests/unit/src/com/android/server/remoteauth/util/CryptoTest.java b/remoteauth/tests/unit/src/com/android/server/remoteauth/util/CryptoTest.java
new file mode 100644
index 0000000..eb7a8c5
--- /dev/null
+++ b/remoteauth/tests/unit/src/com/android/server/remoteauth/util/CryptoTest.java
@@ -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));
+ }
+}
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 83caf35..08527a3 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -19,6 +19,8 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+service_remoteauth_pre_jarjar_lib = "service-remoteauth-pre-jarjar"
+
// Include build rules from Sources.bp
build = ["Sources.bp"]
@@ -56,7 +58,7 @@
"service-connectivity-pre-jarjar",
"service-nearby-pre-jarjar",
"service-thread-pre-jarjar",
- "service-remoteauth-pre-jarjar",
+ service_remoteauth_pre_jarjar_lib,
"ServiceConnectivityResources",
"unsupportedappusage",
],
diff --git a/service-t/src/com/android/server/ConnectivityServiceInitializer.java b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
index 2da067a..624c5df 100644
--- a/service-t/src/com/android/server/ConnectivityServiceInitializer.java
+++ b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
@@ -17,7 +17,6 @@
package com.android.server;
import android.content.Context;
-import android.remoteauth.RemoteAuthManager;
import android.util.Log;
import com.android.modules.utils.build.SdkLevel;
@@ -90,8 +89,8 @@
}
if (mRemoteAuthService != null) {
- Log.i(TAG, "Registering " + RemoteAuthManager.REMOTE_AUTH_SERVICE);
- publishBinderService(RemoteAuthManager.REMOTE_AUTH_SERVICE, mRemoteAuthService,
+ Log.i(TAG, "Registering " + RemoteAuthService.SERVICE_NAME);
+ publishBinderService(RemoteAuthService.SERVICE_NAME, mRemoteAuthService,
/* allowIsolated= */ false);
}
}
@@ -157,8 +156,7 @@
} catch (UnsupportedOperationException e) {
// RemoteAuth is not yet supported in all branches
// TODO: remove catch clause when it is available.
- Log.i(TAG, "Skipping unsupported service "
- + RemoteAuthManager.REMOTE_AUTH_SERVICE);
+ Log.i(TAG, "Skipping unsupported service " + RemoteAuthService.SERVICE_NAME);
return null;
}
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 6485e99..27e97f1 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -17,15 +17,18 @@
package com.android.server;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
+import static com.android.networkstack.apishim.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
@@ -75,6 +78,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.metrics.NetworkNsdReportedMetrics;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.InetAddressUtils;
@@ -1191,7 +1195,7 @@
// TODO: Limits the number of registrations created by a given class.
mOffloadEngines.register(offloadEngineInfo.mOffloadEngine,
offloadEngineInfo);
- // TODO: Sends all the existing OffloadServiceInfos back.
+ sendAllOffloadServiceInfos(offloadEngineInfo);
break;
case NsdManager.UNREGISTER_OFFLOAD_ENGINE:
mOffloadEngines.unregister((IOffloadEngine) msg.obj);
@@ -1877,6 +1881,21 @@
}
}
+ private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) {
+ final String targetInterface = offloadEngineInfo.mInterfaceName;
+ final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine;
+ final List<MdnsAdvertiser.OffloadServiceInfoWrapper> offloadWrappers =
+ mAdvertiser.getAllInterfaceOffloadServiceInfos(targetInterface);
+ for (MdnsAdvertiser.OffloadServiceInfoWrapper wrapper : offloadWrappers) {
+ try {
+ offloadEngine.onOffloadServiceUpdated(wrapper.mOffloadServiceInfo);
+ } catch (RemoteException e) {
+ // Can happen in regular cases, do not log a stacktrace
+ Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
+ }
+ }
+ }
+
private void sendOffloadServiceInfosUpdate(@NonNull String targetInterfaceName,
@NonNull OffloadServiceInfo offloadServiceInfo, boolean isRemove) {
final int count = mOffloadEngines.beginBroadcast();
@@ -1900,7 +1919,7 @@
}
} catch (RemoteException e) {
// Can happen in regular cases, do not log a stacktrace
- Log.i(TAG, "Failed to send offload callback, remote died", e);
+ Log.i(TAG, "Failed to send offload callback, remote died: " + e.getMessage());
}
}
} finally {
@@ -2083,9 +2102,7 @@
public void registerOffloadEngine(String ifaceName, IOffloadEngine cb,
@OffloadEngine.OffloadCapability long offloadCapabilities,
@OffloadEngine.OffloadType long offloadTypes) {
- // TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
- // it may not be possible for all the callers of this API to have it.
- PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
+ checkOffloadEnginePermission(mContext);
Objects.requireNonNull(ifaceName);
Objects.requireNonNull(cb);
mNsdStateMachine.sendMessage(
@@ -2096,13 +2113,31 @@
@Override
public void unregisterOffloadEngine(IOffloadEngine cb) {
- // TODO: Relax the permission because NETWORK_SETTINGS is a signature permission, and
- // it may not be possible for all the callers of this API to have it.
- PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
+ checkOffloadEnginePermission(mContext);
Objects.requireNonNull(cb);
mNsdStateMachine.sendMessage(
mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_OFFLOAD_ENGINE, cb));
}
+
+ private static void checkOffloadEnginePermission(Context context) {
+ if (!SdkLevel.isAtLeastT()) {
+ throw new SecurityException("API is not available in before API level 33");
+ }
+ // REGISTER_NSD_OFFLOAD_ENGINE was only added to the SDK in V, but may
+ // be back ported to older builds: accept it as long as it's signature-protected
+ if (PermissionUtils.checkAnyPermissionOf(context, REGISTER_NSD_OFFLOAD_ENGINE)
+ && (SdkLevel.isAtLeastV() || PermissionUtils.isSystemSignaturePermission(
+ context, REGISTER_NSD_OFFLOAD_ENGINE))) {
+ return;
+ }
+ if (PermissionUtils.checkAnyPermissionOf(context, NETWORK_STACK,
+ PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) {
+ return;
+ }
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", List.of(REGISTER_NSD_OFFLOAD_ENGINE, NETWORK_STACK,
+ PERMISSION_MAINLINE_NETWORK_STACK, NETWORK_SETTINGS)) + ".");
+ }
}
private void sendNsdStateChangeBroadcast(boolean isEnabled) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index dd72d11..f43df45 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -37,6 +37,7 @@
import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -117,6 +118,17 @@
}
}
+ /**
+ * Gets the current status of the OffloadServiceInfos per interface.
+ * @param interfaceName the target interfaceName
+ * @return the list of current offloaded services.
+ */
+ @NonNull
+ public List<OffloadServiceInfoWrapper> getAllInterfaceOffloadServiceInfos(
+ @NonNull String interfaceName) {
+ return mInterfaceOffloadServices.getOrDefault(interfaceName, Collections.emptyList());
+ }
+
private final MdnsInterfaceAdvertiser.Callback mInterfaceAdvertiserCb =
new MdnsInterfaceAdvertiser.Callback() {
@Override
@@ -385,9 +397,12 @@
}
}
- private static class OffloadServiceInfoWrapper {
- private final @NonNull OffloadServiceInfo mOffloadServiceInfo;
- private final int mServiceId;
+ /**
+ * The wrapper class for OffloadServiceInfo including the serviceId.
+ */
+ public static class OffloadServiceInfoWrapper {
+ public final @NonNull OffloadServiceInfo mOffloadServiceInfo;
+ public final int mServiceId;
OffloadServiceInfoWrapper(int serviceId, OffloadServiceInfo offloadServiceInfo) {
mOffloadServiceInfo = offloadServiceInfo;
diff --git a/service/Android.bp b/service/Android.bp
index 9ae3d6c..8e59e86 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -19,6 +19,12 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+service_remoteauth_pre_jarjar_lib = "service-remoteauth-pre-jarjar"
+
+// The above variables may have different values
+// depending on the branch, and this comment helps
+// separate them from the rest of the file to avoid merge conflicts
+
aidl_interface {
name: "connectivity_native_aidl_interface",
local_include_dir: "binder",
@@ -236,7 +242,7 @@
"service-connectivity-pre-jarjar",
"service-connectivity-tiramisu-pre-jarjar",
"service-nearby-pre-jarjar",
- "service-remoteauth-pre-jarjar",
+ service_remoteauth_pre_jarjar_lib,
"service-thread-pre-jarjar",
],
// The below libraries are not actually needed to build since no source is compiled
@@ -361,7 +367,7 @@
java_genrule {
name: "service-remoteauth-jarjar-gen",
tool_files: [
- ":service-remoteauth-pre-jarjar{.jar}",
+ ":" + service_remoteauth_pre_jarjar_lib + "{.jar}",
"jarjar-excludes.txt",
],
tools: [
@@ -369,7 +375,7 @@
],
out: ["service_remoteauth_jarjar_rules.txt"],
cmd: "$(location jarjar-rules-generator) " +
- "$(location :service-remoteauth-pre-jarjar{.jar}) " +
+ "$(location :" + service_remoteauth_pre_jarjar_lib + "{.jar}) " +
"--prefix com.android.server.remoteauth " +
"--excludes $(location jarjar-excludes.txt) " +
"--output $(out)",
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index b89ab1f..3358fd7 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -48,7 +48,7 @@
@BeforeClassWithInfo
public static void setUpOnceBase(TestInformation testInfo) throws Exception {
DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(testInfo.getDevice());
- String testApk = deviceSdkLevel.isDeviceAtLeastT() ? TEST_APK_NEXT : TEST_APK;
+ String testApk = deviceSdkLevel.isDeviceAtLeastV() ? TEST_APK_NEXT : TEST_APK;
uninstallPackage(testInfo, TEST_PKG, false);
installPackage(testInfo, testApk);
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 49a6ef1..508e924 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -163,6 +163,7 @@
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
+ private val serviceName2 = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
private val serviceType = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
private val ctsNetUtils by lazy{ CtsNetUtils(context) }
@@ -890,10 +891,11 @@
}
}
- fun checkOffloadServiceInfo(serviceInfo: OffloadServiceInfo) {
- assertEquals(serviceName, serviceInfo.key.serviceName)
- assertEquals(serviceType, serviceInfo.key.serviceType)
- assertEquals(listOf<String>("_subtype"), serviceInfo.subtypes)
+ fun checkOffloadServiceInfo(serviceInfo: OffloadServiceInfo, si: NsdServiceInfo) {
+ val expectedServiceType = si.serviceType.split(",")[0]
+ assertEquals(si.serviceName, serviceInfo.key.serviceName)
+ assertEquals(expectedServiceType, serviceInfo.key.serviceType)
+ assertEquals(listOf("_subtype"), serviceInfo.subtypes)
assertTrue(serviceInfo.hostname.startsWith("Android_"))
assertTrue(serviceInfo.hostname.endsWith("local"))
assertEquals(0, serviceInfo.priority)
@@ -907,36 +909,63 @@
// The offload callbacks are only supported with the new backend,
// enabled with target SDK U+.
assumeTrue(isAtLeastU() || targetSdkVersion > Build.VERSION_CODES.TIRAMISU)
+
+ // TODO: also have a test that use an executor that runs in a different thread, and pass
+ // in the thread ID NsdServiceInfo to check it
+ val si1 = NsdServiceInfo()
+ si1.serviceType = "$serviceType,_subtype"
+ si1.serviceName = serviceName
+ si1.network = testNetwork1.network
+ si1.port = 23456
+ val record1 = NsdRegistrationRecord()
+
+ val si2 = NsdServiceInfo()
+ si2.serviceType = "$serviceType,_subtype"
+ si2.serviceName = serviceName2
+ si2.network = testNetwork1.network
+ si2.port = 12345
+ val record2 = NsdRegistrationRecord()
val offloadEngine = TestNsdOffloadEngine()
- runAsShell(NETWORK_SETTINGS) {
- nsdManager.registerOffloadEngine(testNetwork1.iface.interfaceName,
- OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(),
- OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK.toLong(),
- { it.run() }, offloadEngine)
- }
- val si = NsdServiceInfo()
- si.serviceType = "$serviceType,_subtype"
- si.serviceName = serviceName
- si.network = testNetwork1.network
- si.port = 12345
- val record = NsdRegistrationRecord()
- nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, record)
- val addOrUpdateEvent = offloadEngine
- .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
- it.info.key.serviceName == serviceName
+ tryTest {
+ // Register service before the OffloadEngine is registered.
+ nsdManager.registerService(si1, NsdManager.PROTOCOL_DNS_SD, record1)
+ record1.expectCallback<ServiceRegistered>()
+ runAsShell(NETWORK_SETTINGS) {
+ nsdManager.registerOffloadEngine(testNetwork1.iface.interfaceName,
+ OffloadEngine.OFFLOAD_TYPE_REPLY.toLong(),
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK.toLong(),
+ { it.run() }, offloadEngine)
}
- checkOffloadServiceInfo(addOrUpdateEvent.info)
+ val addOrUpdateEvent1 = offloadEngine
+ .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
+ it.info.key.serviceName == si1.serviceName
+ }
+ checkOffloadServiceInfo(addOrUpdateEvent1.info, si1)
- nsdManager.unregisterService(record)
- val unregisterEvent = offloadEngine
- .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.RemoveEvent> {
- it.info.key.serviceName == serviceName
+ // Register service after OffloadEngine is registered.
+ nsdManager.registerService(si2, NsdManager.PROTOCOL_DNS_SD, record2)
+ record2.expectCallback<ServiceRegistered>()
+ val addOrUpdateEvent2 = offloadEngine
+ .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.AddOrUpdateEvent> {
+ it.info.key.serviceName == si2.serviceName
+ }
+ checkOffloadServiceInfo(addOrUpdateEvent2.info, si2)
+
+ nsdManager.unregisterService(record2)
+ record2.expectCallback<ServiceUnregistered>()
+ val unregisterEvent = offloadEngine
+ .expectCallbackEventually<TestNsdOffloadEngine.OffloadEvent.RemoveEvent> {
+ it.info.key.serviceName == si2.serviceName
+ }
+ checkOffloadServiceInfo(unregisterEvent.info, si2)
+ } cleanupStep {
+ runAsShell(NETWORK_SETTINGS) {
+ nsdManager.unregisterOffloadEngine(offloadEngine)
}
- checkOffloadServiceInfo(unregisterEvent.info)
-
- runAsShell(NETWORK_SETTINGS) {
- nsdManager.unregisterOffloadEngine(offloadEngine)
+ } cleanup {
+ nsdManager.unregisterService(record1)
+ record1.expectCallback<ServiceUnregistered>()
}
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index f0c7dcc..695cfe8 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,20 +16,27 @@
package com.android.server;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
+import static com.android.networkstack.apishim.api33.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE;
import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF;
import static com.android.server.NsdService.MdnsListener;
import static com.android.server.NsdService.NO_TRANSACTION;
@@ -68,6 +75,8 @@
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
import android.net.INetd;
import android.net.Network;
import android.net.mdns.aidl.DiscoveryInfo;
@@ -84,6 +93,7 @@
import android.net.nsd.NsdManager.ResolveListener;
import android.net.nsd.NsdManager.ServiceInfoCallback;
import android.net.nsd.NsdServiceInfo;
+import android.net.nsd.OffloadEngine;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
@@ -161,6 +171,7 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext;
+ @Mock PackageManager mPackageManager;
@Mock ContentResolver mResolver;
@Mock MDnsManager mMockMDnsM;
@Mock Dependencies mDeps;
@@ -198,6 +209,7 @@
mockService(mContext, MDnsManager.class, MDnsManager.MDNS_SERVICE, mMockMDnsM);
mockService(mContext, WifiManager.class, Context.WIFI_SERVICE, mWifiManager);
mockService(mContext, ActivityManager.class, Context.ACTIVITY_SERVICE, mActivityManager);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
if (mContext.getSystemService(MDnsManager.class) == null) {
// Test is using mockito-extended
doCallRealMethod().when(mContext).getSystemService(MDnsManager.class);
@@ -1664,6 +1676,33 @@
assertThrows(IllegalArgumentException.class, () -> new NsdManager(mContext, service));
}
+ @Test
+ @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
+ public void testRegisterOffloadEngine_checkPermission()
+ throws PackageManager.NameNotFoundException {
+ final NsdManager client = connectClient(mService);
+ final OffloadEngine offloadEngine = mock(OffloadEngine.class);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
+ PERMISSION_MAINLINE_NETWORK_STACK);
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS);
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
+ REGISTER_NSD_OFFLOAD_ENGINE);
+
+ PermissionInfo permissionInfo = new PermissionInfo("");
+ permissionInfo.packageName = "android";
+ permissionInfo.protectionLevel = PROTECTION_SIGNATURE;
+ doReturn(permissionInfo).when(mPackageManager).getPermissionInfo(
+ REGISTER_NSD_OFFLOAD_ENGINE, 0);
+ client.registerOffloadEngine("iface1", OffloadEngine.OFFLOAD_TYPE_REPLY,
+ OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK,
+ Runnable::run, offloadEngine);
+ client.unregisterOffloadEngine(offloadEngine);
+
+ // TODO: add checks to test the packageName other than android
+ }
+
+
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
}