diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl index a7386718d5ae5..555e9b5883e83 100644 --- a/core/java/android/net/vcn/IVcnStatusCallback.aidl +++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl @@ -19,4 +19,9 @@ package android.net.vcn; /** @hide */ interface IVcnStatusCallback { void onEnteredSafeMode(); + void onGatewayConnectionError( + in int[] gatewayNetworkCapabilities, + int errorCode, + in String exceptionClass, + in String exceptionMessage); } \ No newline at end of file diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index aed64de52cd02..aea0ea988f509 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -17,7 +17,9 @@ package android.net.vcn; import static java.util.Objects.requireNonNull; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; @@ -32,6 +34,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -262,6 +266,42 @@ public class VcnManager { } } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + VCN_ERROR_CODE_INTERNAL_ERROR, + VCN_ERROR_CODE_CONFIG_ERROR, + VCN_ERROR_CODE_NETWORK_ERROR + }) + public @interface VcnErrorCode {} + + /** + * Value indicating that an internal failure occurred in this Gateway Connection. + * + * @hide + */ + public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; + + /** + * Value indicating that an error with this Gateway Connection's configuration occurred. + * + *
For example, this error code will be returned after authentication failures. + * + * @hide + */ + public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; + + /** + * Value indicating that a Network error occurred with this Gateway Connection. + * + *
For example, this error code will be returned if an underlying {@link android.net.Network}
+ * for this Gateway Connection is lost, or if an error occurs while resolving the connection
+ * endpoint address.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2;
+
// TODO: make VcnStatusCallback @SystemApi
/**
* VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
@@ -285,6 +325,24 @@ public class VcnManager {
* via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
*/
public abstract void onEnteredSafeMode();
+
+ /**
+ * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
+ * encounters an error.
+ *
+ * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway
+ * Connection that encountered the error for identification purposes. These will be a
+ * sorted list with no duplicates, matching one of the {@link
+ * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
+ * group.
+ * @param errorCode {@link VcnErrorCode} to indicate the error that occurred
+ * @param detail Throwable to provide additional information about the error, or {@code
+ * null} if none
+ */
+ public abstract void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable Throwable detail);
}
/**
@@ -385,11 +443,12 @@ public class VcnManager {
*
* @hide
*/
- private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
@NonNull private final Executor mExecutor;
@NonNull private final VcnStatusCallback mCallback;
- private VcnStatusCallbackBinder(
+ public VcnStatusCallbackBinder(
@NonNull Executor executor, @NonNull VcnStatusCallback callback) {
mExecutor = executor;
mCallback = callback;
@@ -400,5 +459,36 @@ public class VcnManager {
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
}
+
+ // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage);
+
+ Binder.withCleanCallingIdentity(
+ () ->
+ mExecutor.execute(
+ () ->
+ mCallback.onGatewayConnectionError(
+ networkCapabilities, errorCode, cause)));
+ }
+
+ private static Throwable createThrowableByClassName(
+ @Nullable String className, @Nullable String message) {
+ if (className == null) {
+ return null;
+ }
+
+ try {
+ Class> c = Class.forName(className);
+ return (Throwable) c.getConstructor(String.class).newInstance(message);
+ } catch (ReflectiveOperationException | ClassCastException e) {
+ return new RuntimeException(className + ": " + message);
+ }
+ }
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 1a90fc319bce2..708767605508b 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -33,6 +34,7 @@ import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnStatusCallback;
+import android.net.vcn.VcnManager.VcnStatusCallbackBinder;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
import android.os.ParcelUuid;
@@ -40,11 +42,15 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import java.net.UnknownHostException;
import java.util.UUID;
import java.util.concurrent.Executor;
public class VcnManagerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int[] UNDERLYING_NETWORK_CAPABILITIES = {
+ NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_INTERNET
+ };
private static final Executor INLINE_EXECUTOR = Runnable::run;
private IVcnManagementService mMockVcnManagementService;
@@ -144,14 +150,8 @@ public class VcnManagerTest {
public void testRegisterVcnStatusCallback() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
- ArgumentCaptor