Define VcnStatusCallback#onGatewayConnectionError callback.

This CL updates VcnStatusCallback to have a onGatewayConnctionError()
function. This function will be used to notify callers when Gateway
Connection errors occur, such as authentication failures or the session
unexpectedly dying.

Bug: 163433613
Test: atest FrameworksVcnTests
Change-Id: I9ea6e9e850eb9d8b0d374c5447895f1f04121696
This commit is contained in:
Cody Kesting
2021-02-09 18:40:27 -08:00
parent bf8113b93d
commit fe0523949b
3 changed files with 124 additions and 9 deletions

View File

@@ -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);
}

View File

@@ -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.
*
* <p>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.
*
* <p>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);
}
}
}
}

View File

@@ -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<IVcnStatusCallback> captor =
ArgumentCaptor.forClass(IVcnStatusCallback.class);
verify(mMockVcnManagementService)
.registerVcnStatusCallback(eq(SUB_GROUP), captor.capture(), any());
IVcnStatusCallback callbackWrapper = captor.getValue();
callbackWrapper.onEnteredSafeMode();
verify(mMockStatusCallback).onEnteredSafeMode();
.registerVcnStatusCallback(eq(SUB_GROUP), notNull(), any());
}
@Test(expected = IllegalStateException.class)
@@ -195,4 +195,24 @@ public class VcnManagerTest {
public void testUnregisterNullVcnStatusCallback() throws Exception {
mVcnManager.unregisterVcnStatusCallback(null);
}
@Test
public void testVcnStatusCallbackBinder() throws Exception {
IVcnStatusCallback cbBinder =
new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
cbBinder.onEnteredSafeMode();
verify(mMockStatusCallback).onEnteredSafeMode();
cbBinder.onGatewayConnectionError(
UNDERLYING_NETWORK_CAPABILITIES,
VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
"java.net.UnknownHostException",
"exception_message");
verify(mMockStatusCallback)
.onGatewayConnectionError(
eq(UNDERLYING_NETWORK_CAPABILITIES),
eq(VcnManager.VCN_ERROR_CODE_NETWORK_ERROR),
any(UnknownHostException.class));
}
}