Add RemoteAuthService
Add RemoteAuthService APIs and Stubs (hide) for Settings of D2DA
This CL introduces new SystemApi allow user to discover remote devices
compatible to be registered as remote authenticators via RemoteAuthManager
Design doc: go/remote-auth-manager-fishfood-design
Test: built successfully.
Bug: 290092977
API-Coverage-Bug: 294934095
Change-Id: Iaaae1126065fdc3db469eeb8d85ac654b8199a12
diff --git a/remoteauth/TEST_MAPPING b/remoteauth/TEST_MAPPING
index 5ad8da6..5061319 100644
--- a/remoteauth/TEST_MAPPING
+++ b/remoteauth/TEST_MAPPING
@@ -7,7 +7,7 @@
// TODO(b/193602229): uncomment once it's supported.
//"mainline-presubmit": [
// {
- // "name": "RemoteAuthUnitTests[com.google.android.tethering.apex]"
+ // "name": "RemoteAuthUnitTests[com.google.android.remoteauth.apex]"
// }
//]
}
diff --git a/remoteauth/framework/Android.bp b/remoteauth/framework/Android.bp
index 48d10b6..71b621a 100644
--- a/remoteauth/framework/Android.bp
+++ b/remoteauth/framework/Android.bp
@@ -25,7 +25,7 @@
],
path: "java",
visibility: [
- "//packages/modules/Connectivity/framework:__subpackages__",
+ "//packages/modules/Connectivity/framework-t:__subpackages__",
],
}
@@ -43,7 +43,13 @@
name: "framework-remoteauth-static",
srcs: [":framework-remoteauth-java-sources"],
sdk_version: "module_current",
- libs: [],
- static_libs: [],
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-annotations-lib",
+ "framework-bluetooth",
+ ],
+ static_libs: [
+ "modules-utils-preconditions",
+ ],
visibility: ["//packages/modules/Connectivity/remoteauth/tests:__subpackages__"],
}
diff --git a/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
new file mode 100644
index 0000000..f53e2dc
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/DeviceDiscoveryCallback.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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 android.remoteauth;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Reports newly discovered remote devices.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+public interface DeviceDiscoveryCallback {
+ /** The device is no longer seen in the discovery process. */
+ int STATE_LOST = 0;
+ /** The device is seen in the discovery process */
+ int STATE_SEEN = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATE_LOST, STATE_SEEN})
+ @interface State {}
+
+ /**
+ * Invoked for every change in remote device state.
+ *
+ * @param device remote device
+ * @param state indicates if found or lost
+ */
+ void onDeviceUpdate(@NonNull RemoteDevice device, @State int state);
+
+ /** Invoked when discovery is stopped due to timeout. */
+ void onTimeout();
+}
diff --git a/remoteauth/framework/java/android/remoteauth/IDeviceDiscoveryListener.aidl b/remoteauth/framework/java/android/remoteauth/IDeviceDiscoveryListener.aidl
new file mode 100644
index 0000000..2ad6a6a
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/IDeviceDiscoveryListener.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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 android.remoteauth;
+
+import android.remoteauth.RemoteDevice;
+
+/**
+ * Binder callback for DeviceDiscoveryCallback.
+ *
+ * {@hide}
+ */
+oneway interface IDeviceDiscoveryListener {
+ /** Reports a {@link RemoteDevice} being discovered. */
+ void onDiscovered(in RemoteDevice remoteDevice);
+
+ /** Reports a {@link RemoteDevice} is no longer within range. */
+ void onLost(in RemoteDevice remoteDevice);
+
+ /** Reports a timeout of {@link RemoteDevice} was reached. */
+ void onTimeout();
+}
diff --git a/remoteauth/framework/java/android/remoteauth/IRemoteAuthService.aidl b/remoteauth/framework/java/android/remoteauth/IRemoteAuthService.aidl
new file mode 100644
index 0000000..f4387e3
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/IRemoteAuthService.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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 android.remoteauth;
+
+import android.remoteauth.IDeviceDiscoveryListener;
+
+/**
+ * Interface for communicating with the RemoteAuthService.
+ * These methods are all require MANAGE_REMOTE_AUTH signature permission.
+ * @hide
+ */
+interface IRemoteAuthService {
+ // This is protected by the MANAGE_REMOTE_AUTH signature permission.
+ boolean isRemoteAuthSupported();
+
+ // This is protected by the MANAGE_REMOTE_AUTH signature permission.
+ boolean registerDiscoveryListener(in IDeviceDiscoveryListener deviceDiscoveryListener,
+ int userId,
+ int timeoutMs,
+ String packageName,
+ @nullable String attributionTag);
+
+ // This is protected by the MANAGE_REMOTE_AUTH signature permission.
+ void unregisterDiscoveryListener(in IDeviceDiscoveryListener deviceDiscoveryListener,
+ int userId,
+ String packageName,
+ @nullable String attributionTag);
+}
\ No newline at end of file
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
new file mode 100644
index 0000000..dfd7726
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthFrameworkInitializer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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 android.remoteauth;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for initializing RemoteAuth service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class RemoteAuthFrameworkInitializer {
+ private RemoteAuthFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all Nearby
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides {@link
+ * SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ // TODO(b/290092977): Change to Context.REMOTE_AUTH_SERVICE after aosp/2681375
+ // is automerges from aosp-main to udc-mainline-prod
+ SystemServiceRegistry.registerContextAwareService(
+ RemoteAuthManager.REMOTE_AUTH_SERVICE,
+ RemoteAuthManager.class,
+ (context, serviceBinder) -> {
+ IRemoteAuthService service = IRemoteAuthService.Stub.asInterface(serviceBinder);
+ return new RemoteAuthManager(context, service);
+ });
+ }
+}
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
new file mode 100644
index 0000000..c025a55
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/RemoteAuthManager.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 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 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;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * A system service providing a way to perform remote authentication-related operations such as
+ * discovering, registering and authenticating via remote authenticator.
+ *
+ * <p>To get a {@link RemoteAuthManager} instance, call the <code>
+ * Context.getSystemService(Context.REMOTE_AUTH_SERVICE)</code>.
+ *
+ * @hide
+ */
+@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)
+public class RemoteAuthManager {
+ private static final String TAG = "RemoteAuthManager";
+
+ /** @hide */
+ public static final String REMOTE_AUTH_SERVICE = "remote_auth";
+
+ private final Context mContext;
+ private final IRemoteAuthService mService;
+
+ @GuardedBy("mDiscoveryListeners")
+ private final WeakHashMap<
+ DeviceDiscoveryCallback, WeakReference<DeviceDiscoveryListenerTransport>>
+ mDiscoveryListeners = new WeakHashMap<>();
+
+ /** @hide */
+ public RemoteAuthManager(@NonNull Context context, @NonNull IRemoteAuthService service) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(service);
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Returns if this device can be enrolled in the feature.
+ *
+ * @return true if this device can be enrolled
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
+ public boolean isRemoteAuthSupported() {
+ try {
+ return mService.isRemoteAuthSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts remote authenticator discovery process with timeout. Devices that are capable to
+ * operate as remote authenticators are reported via callback. The discovery stops by calling
+ * stopDiscovery or after a timeout.
+ *
+ * @param timeoutMs the duration in milliseconds after which discovery will stop automatically
+ * @param executor the callback will be executed in the executor thread
+ * @param callback to be used by the caller to get notifications about remote devices
+ * @return {@code true} if discovery began successfully, {@code false} otherwise
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ // TODO(b/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
+ public boolean startDiscovery(
+ int timeoutMs,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull DeviceDiscoveryCallback callback) {
+ try {
+ Preconditions.checkNotNull(callback, "invalid null callback");
+ Preconditions.checkArgument(timeoutMs > 0, "invalid timeoutMs, must be > 0");
+ Preconditions.checkNotNull(executor, "invalid null executor");
+ DeviceDiscoveryListenerTransport transport;
+ synchronized (mDiscoveryListeners) {
+ WeakReference<DeviceDiscoveryListenerTransport> reference =
+ mDiscoveryListeners.get(callback);
+ transport = (reference != null) ? reference.get() : null;
+ if (transport == null) {
+ transport =
+ new DeviceDiscoveryListenerTransport(
+ callback, mContext.getUser().getIdentifier(), executor);
+ }
+
+ boolean result =
+ mService.registerDiscoveryListener(
+ transport,
+ mContext.getUser().getIdentifier(),
+ timeoutMs,
+ mContext.getPackageName(),
+ mContext.getAttributionTag());
+ if (result) {
+ mDiscoveryListeners.put(callback, new WeakReference<>(transport));
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
+ * Removes this listener from device discovery notifications. The given callback is guaranteed
+ * not to receive any invocations that happen after this method is invoked.
+ *
+ * @param callback the callback for the previously started discovery to be ended
+ * @hide
+ */
+ // 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/297301535): @RequiresPermission(MANAGE_REMOTE_AUTH)
+ public void stopDiscovery(@NonNull DeviceDiscoveryCallback callback) {
+ Preconditions.checkNotNull(callback, "invalid null scanCallback");
+ try {
+ DeviceDiscoveryListenerTransport transport;
+ synchronized (mDiscoveryListeners) {
+ WeakReference<DeviceDiscoveryListenerTransport> reference =
+ mDiscoveryListeners.remove(callback);
+ transport = (reference != null) ? reference.get() : null;
+ }
+ if (transport != null) {
+ mService.unregisterDiscoveryListener(
+ transport,
+ transport.getUserId(),
+ mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } else {
+ Log.d(
+ TAG,
+ "Cannot stop discovery with this callback "
+ + "because it is not registered.");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private class DeviceDiscoveryListenerTransport extends IDeviceDiscoveryListener.Stub {
+
+ private volatile @NonNull DeviceDiscoveryCallback mDeviceDiscoveryCallback;
+ private Executor mExecutor;
+ private @UserIdInt int mUserId;
+
+ DeviceDiscoveryListenerTransport(
+ DeviceDiscoveryCallback deviceDiscoveryCallback,
+ @UserIdInt int userId,
+ @CallbackExecutor Executor executor) {
+ Preconditions.checkNotNull(deviceDiscoveryCallback, "invalid null callback");
+ mDeviceDiscoveryCallback = deviceDiscoveryCallback;
+ mUserId = userId;
+ mExecutor = executor;
+ }
+
+ @UserIdInt
+ int getUserId() {
+ return mUserId;
+ }
+
+ @Override
+ public void onDiscovered(RemoteDevice remoteDevice) throws RemoteException {
+ if (remoteDevice == null) {
+ Log.w(TAG, "onDiscovered is called with null device");
+ return;
+ }
+ Log.i(TAG, "Notifying the caller about discovered: " + remoteDevice);
+ mExecutor.execute(
+ () -> {
+ mDeviceDiscoveryCallback.onDeviceUpdate(remoteDevice, STATE_SEEN);
+ });
+ }
+
+ @Override
+ public void onLost(RemoteDevice remoteDevice) throws RemoteException {
+ if (remoteDevice == null) {
+ Log.w(TAG, "onLost is called with null device");
+ return;
+ }
+ Log.i(TAG, "Notifying the caller about lost: " + remoteDevice);
+ mExecutor.execute(
+ () -> {
+ mDeviceDiscoveryCallback.onDeviceUpdate(remoteDevice, STATE_LOST);
+ });
+ }
+
+ @Override
+ public void onTimeout() {
+ Log.i(TAG, "Notifying the caller about discovery timeout");
+ mExecutor.execute(
+ () -> {
+ mDeviceDiscoveryCallback.onTimeout();
+ });
+ synchronized (mDiscoveryListeners) {
+ mDiscoveryListeners.remove(mDeviceDiscoveryCallback);
+ }
+ mDeviceDiscoveryCallback = null;
+ }
+ }
+}
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteDevice.aidl b/remoteauth/framework/java/android/remoteauth/RemoteDevice.aidl
new file mode 100644
index 0000000..ea38be2
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/RemoteDevice.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.remoteauth;
+
+parcelable RemoteDevice;
\ No newline at end of file
diff --git a/remoteauth/framework/java/android/remoteauth/RemoteDevice.java b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
new file mode 100644
index 0000000..4cd2399
--- /dev/null
+++ b/remoteauth/framework/java/android/remoteauth/RemoteDevice.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 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 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;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Remote device that can be registered as remote authenticator.
+ *
+ * @hide
+ */
+// TODO(b/295407748) Change to use @DataClass
+@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;
+ /** The remote device is registered as remote authenticator. */
+ public static final int STATE_REGISTERED = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATE_NOT_REGISTERED, STATE_REGISTERED})
+ @interface RegistrationState {}
+
+ @NonNull private final String mName;
+ private final @RegistrationState int mRegistrationState;
+ private final int mConnectionId;
+
+ public static final @NonNull Creator<RemoteDevice> CREATOR =
+ new Creator<>() {
+ @Override
+ public RemoteDevice createFromParcel(Parcel in) {
+ RemoteDevice.Builder builder = new RemoteDevice.Builder();
+ builder.setName(in.readString());
+ builder.setRegistrationState(in.readInt());
+ builder.setConnectionId(in.readInt());
+
+ return builder.build();
+ }
+
+ @Override
+ public RemoteDevice[] newArray(int size) {
+ return new RemoteDevice[size];
+ }
+ };
+
+ private RemoteDevice(
+ @Nullable String name,
+ @RegistrationState int registrationState,
+ @NonNull int connectionId) {
+ this.mName = name;
+ this.mRegistrationState = registrationState;
+ this.mConnectionId = connectionId;
+ }
+
+ /** Gets the name of the {@link RemoteDevice} device. */
+ @Nullable
+ public String getName() {
+ return mName;
+ }
+
+ /** Returns registration state of the {@link RemoteDevice}. */
+ public @RegistrationState int getRegistrationState() {
+ return mRegistrationState;
+ }
+
+ /** Returns connection id of the {@link RemoteDevice}. */
+ @NonNull
+ public int getConnectionId() {
+ return mConnectionId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Returns a string representation of {@link RemoteDevice}. */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("RemoteDevice [");
+ sb.append("name=").append(mName).append(", ");
+ sb.append("registered=").append(mRegistrationState).append(", ");
+ sb.append("connectionId=").append(mConnectionId);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /** Returns true if this {@link RemoteDevice} object is equals to other. */
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof RemoteDevice) {
+ RemoteDevice otherDevice = (RemoteDevice) other;
+ return Objects.equals(this.mName, otherDevice.mName)
+ && this.getRegistrationState() == otherDevice.getRegistrationState()
+ && this.mConnectionId == otherDevice.mConnectionId;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mRegistrationState, mConnectionId);
+ }
+
+ /**
+ * Helper function for writing {@link RemoteDevice} to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ String name = getName();
+ dest.writeString(name);
+ dest.writeInt(getRegistrationState());
+ dest.writeInt(getConnectionId());
+ }
+
+ /** Builder for {@link RemoteDevice} objects. */
+ public static final class Builder {
+ @Nullable private String mName;
+ // represents if device is already registered
+ private @RegistrationState int mRegistrationState;
+ private int mConnectionId;
+
+ private Builder() {
+ }
+
+ public Builder(final int connectionId) {
+ this.mConnectionId = connectionId;
+ }
+
+ /**
+ * Sets the name of the {@link RemoteDevice} device.
+ *
+ * @param name of the {@link RemoteDevice}. Can be {@code null} if there is no name.
+ */
+ @NonNull
+ public RemoteDevice.Builder setName(@Nullable String name) {
+ this.mName = name;
+ return this;
+ }
+
+ /**
+ * Sets the registration state of the {@link RemoteDevice} device.
+ *
+ * @param registrationState of the {@link RemoteDevice}.
+ */
+ @NonNull
+ public RemoteDevice.Builder setRegistrationState(@RegistrationState int registrationState) {
+ this.mRegistrationState = registrationState;
+ return this;
+ }
+
+ /**
+ * Sets the connectionInfo of the {@link RemoteDevice} device.
+ *
+ * @param connectionId of the RemoteDevice.
+ */
+ @NonNull
+ public RemoteDevice.Builder setConnectionId(int connectionId) {
+ this.mConnectionId = connectionId;
+ return this;
+ }
+
+ /**
+ * Creates the {@link RemoteDevice} instance.
+ *
+ * @return the configured {@link RemoteDevice} instance.
+ */
+ @NonNull
+ public RemoteDevice build() {
+ return new RemoteDevice(mName, mRegistrationState, mConnectionId);
+ }
+ }
+}
diff --git a/remoteauth/service/Android.bp b/remoteauth/service/Android.bp
index 5c5a2fb..c3a9fb3 100644
--- a/remoteauth/service/Android.bp
+++ b/remoteauth/service/Android.bp
@@ -29,8 +29,24 @@
defaults: [
"framework-system-server-module-defaults"
],
- libs: [],
- static_libs: [],
+ libs: [
+ "androidx.annotation_annotation",
+ "framework-bluetooth",
+ "error_prone_annotations",
+ "framework-configinfrastructure",
+ "framework-connectivity-pre-jarjar",
+ "framework-connectivity-t-pre-jarjar",
+ "framework-statsd",
+ ],
+ static_libs: [
+ "libprotobuf-java-lite",
+ "fast-pair-lite-protos",
+ "modules-utils-build",
+ "modules-utils-handlerexecutor",
+ "modules-utils-preconditions",
+ "modules-utils-backgroundthread",
+ "presence-lite-protos",
+ ],
sdk_version: "system_server_current",
// This is included in service-connectivity which is 30+
// TODO (b/293613362): allow APEXes to have service jars with higher min_sdk than the APEX
diff --git a/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java b/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java
new file mode 100644
index 0000000..41ce89a
--- /dev/null
+++ b/remoteauth/service/java/com/android/server/remoteauth/RemoteAuthService.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.remoteauth.IDeviceDiscoveryListener;
+import android.remoteauth.IRemoteAuthService;
+
+import com.android.internal.util.Preconditions;
+
+/** Service implementing remoteauth functionality. */
+public class RemoteAuthService extends IRemoteAuthService.Stub {
+ public static final String TAG = "RemoteAuthService";
+
+ public RemoteAuthService(Context context) {
+ Preconditions.checkNotNull(context);
+ // TODO(b/290280702): Create here RemoteConnectivityManager and RangingManager
+ }
+
+ @Override
+ public boolean isRemoteAuthSupported() {
+ // TODO(b/297301535): checkPermission(mContext, MANAGE_REMOTE_AUTH);
+ // TODO(b/290676192): integrate with RangingManager
+ // (check if UWB is supported by this device)
+ return true;
+ }
+
+ @Override
+ public boolean registerDiscoveryListener(
+ IDeviceDiscoveryListener deviceDiscoveryListener,
+ @UserIdInt int userId,
+ int timeoutMs,
+ String packageName,
+ @Nullable String attributionTag) {
+ // TODO(b/297301535): checkPermission(mContext, MANAGE_REMOTE_AUTH);
+ // TODO(b/290280702): implement register discovery logic
+ return true;
+ }
+
+ @Override
+ public void unregisterDiscoveryListener(
+ IDeviceDiscoveryListener deviceDiscoveryListener,
+ @UserIdInt int userId,
+ String packageName,
+ @Nullable String attributionTag) {
+ // TODO(b/297301535): checkPermission(mContext, MANAGE_REMOTE_AUTH);
+ // TODO(b/290094221): implement unregister logic
+ }
+
+ private static void checkPermission(Context context, String permission) {
+ context.enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+}
diff --git a/remoteauth/tests/unit/Android.bp b/remoteauth/tests/unit/Android.bp
index 8c08a1b..4b92d84 100644
--- a/remoteauth/tests/unit/Android.bp
+++ b/remoteauth/tests/unit/Android.bp
@@ -37,6 +37,7 @@
"androidx.test.rules",
"framework-remoteauth-static",
"junit",
+ "libprotobuf-java-lite",
"platform-test-annotations",
"service-remoteauth-pre-jarjar",
"truth-prebuilt",
diff --git a/remoteauth/tests/unit/src/android/remoteauth/RemoteAuthManagerTest.java b/remoteauth/tests/unit/src/android/remoteauth/RemoteAuthManagerTest.java
index 5cf3e6b..6b43355 100644
--- a/remoteauth/tests/unit/src/android/remoteauth/RemoteAuthManagerTest.java
+++ b/remoteauth/tests/unit/src/android/remoteauth/RemoteAuthManagerTest.java
@@ -23,6 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +32,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RemoteAuthManagerTest {
+ @Before
+ public void setUp() {}
+
@Test
public void testStub() {
assertTrue(true);