Merge changes I0cadd145,I3be3cac4,I9ea6e9e8

* changes:
  Correct comment for safe mode alarm in VcnGatewayConnection.
  Implement VCN error callback use.
  Define VcnStatusCallback#onGatewayConnectionError callback.
This commit is contained in:
Cody Kesting
2021-02-18 03:07:14 +00:00
committed by Gerrit Code Review
9 changed files with 360 additions and 67 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

@@ -33,6 +33,7 @@ import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
@@ -304,8 +305,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafeModeCallback safeModeCallback) {
return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback);
@NonNull VcnCallback vcnCallback) {
return new Vcn(vcnContext, subscriptionGroup, config, snapshot, vcnCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -457,12 +458,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
final VcnSafeModeCallbackImpl safeModeCallback =
new VcnSafeModeCallbackImpl(subscriptionGroup);
final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
final Vcn newInstance =
mDeps.newVcn(
mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback);
mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -784,20 +783,47 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
/** Callback for signalling when a Vcn has entered safe mode. */
public interface VcnSafeModeCallback {
/** Callback for Vcn signals sent up to VcnManagementService. */
public interface VcnCallback {
/** Called by a Vcn to signal that it has entered safe mode. */
void onEnteredSafeMode();
/** Called by a Vcn to signal that an error occurred. */
void onGatewayConnectionError(
@NonNull int[] networkCapabilities,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
}
/** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */
private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback {
/** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */
private class VcnCallbackImpl implements VcnCallback {
@NonNull private final ParcelUuid mSubGroup;
private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) {
private VcnCallbackImpl(@NonNull final ParcelUuid subGroup) {
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
}
private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
if (!mSubGroup.equals(cbInfo.mSubGroup)) {
return false;
}
if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
mSubGroup, cbInfo.mPkgName)) {
return false;
}
if (!mLocationPermissionChecker.checkLocationPermission(
cbInfo.mPkgName,
"VcnStatusCallback" /* featureId */,
cbInfo.mUid,
null /* message */)) {
return false;
}
return true;
}
@Override
public void onEnteredSafeMode() {
synchronized (mLock) {
@@ -810,23 +836,36 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (!mSubGroup.equals(cbInfo.mSubGroup)) {
continue;
}
if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
mSubGroup, cbInfo.mPkgName)) {
continue;
if (isCallbackPermissioned(cbInfo)) {
Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
}
}
}
}
if (!mLocationPermissionChecker.checkLocationPermission(
cbInfo.mPkgName,
"VcnStatusCallback" /* featureId */,
cbInfo.mUid,
null /* message */)) {
continue;
}
@Override
public void onGatewayConnectionError(
@NonNull int[] networkCapabilities,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage) {
synchronized (mLock) {
// Ignore if this subscription group doesn't exist anymore
if (!mVcns.containsKey(mSubGroup)) {
return;
}
Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo)) {
Binder.withCleanCallingIdentity(
() ->
cbInfo.mCallback.onGatewayConnectionError(
networkCapabilities,
errorCode,
exceptionClass,
exceptionMessage));
}
}
}
}

View File

@@ -19,10 +19,12 @@ package com.android.server.vcn;
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -30,7 +32,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -97,7 +99,7 @@ public class Vcn extends Handler {
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
@NonNull private final VcnSafeModeCallback mVcnSafeModeCallback;
@NonNull private final VcnCallback mVcnCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -125,14 +127,8 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafeModeCallback vcnSafeModeCallback) {
this(
vcnContext,
subscriptionGroup,
config,
snapshot,
vcnSafeModeCallback,
new Dependencies());
@NonNull VcnCallback vcnCallback) {
this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -141,13 +137,12 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafeModeCallback vcnSafeModeCallback,
@NonNull VcnCallback vcnCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mVcnSafeModeCallback =
Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback");
mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -246,7 +241,7 @@ public class Vcn extends Handler {
private void handleEnterSafeMode() {
handleTeardown();
mVcnSafeModeCallback.onEnteredSafeMode();
mVcnCallback.onEnteredSafeMode();
}
private void handleNetworkRequested(
@@ -337,6 +332,13 @@ public class Vcn extends Handler {
public interface VcnGatewayStatusCallback {
/** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
void onEnteredSafeMode();
/** Callback by a VcnGatewayConnection to indicate that an error occurred. */
void onGatewayConnectionError(
@NonNull int[] networkCapabilities,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
@@ -344,6 +346,16 @@ public class Vcn extends Handler {
public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
}
@Override
public void onGatewayConnectionError(
@NonNull int[] networkCapabilities,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage) {
mVcnCallback.onGatewayConnectionError(
networkCapabilities, errorCode, exceptionClass, exceptionMessage);
}
}
/** External dependencies used by Vcn, for injection in tests */

View File

@@ -22,6 +22,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.VcnManagementService.VDBG;
@@ -52,7 +55,9 @@ import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
@@ -951,15 +956,68 @@ public class VcnGatewayConnection extends StateMachine {
removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
}
private void sessionLost(int token, @Nullable Exception exception) {
private void sessionLostWithoutCallback(int token, @Nullable Exception exception) {
sendMessageAndAcquireWakeLock(
EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
}
private void sessionLost(int token, @Nullable Exception exception) {
// Only notify mGatewayStatusCallback if the session was lost with an error. All
// authentication and DNS failures are sent through
// IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
if (exception != null) {
mGatewayStatusCallback.onGatewayConnectionError(
mConnectionConfig.getRequiredUnderlyingCapabilities(),
VCN_ERROR_CODE_INTERNAL_ERROR,
"java.lang.RuntimeException",
"Received "
+ exception.getClass().getSimpleName()
+ " with message: "
+ exception.getMessage());
}
sessionLostWithoutCallback(token, exception);
}
private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) {
final int errorCode;
final String exceptionClass;
final String exceptionMessage;
if (exception instanceof AuthenticationFailedException) {
errorCode = VCN_ERROR_CODE_CONFIG_ERROR;
exceptionClass = exception.getClass().getName();
exceptionMessage = exception.getMessage();
} else if (exception instanceof IkeInternalException
&& exception.getCause() instanceof IOException) {
errorCode = VCN_ERROR_CODE_NETWORK_ERROR;
exceptionClass = "java.io.IOException";
exceptionMessage = exception.getCause().getMessage();
} else {
errorCode = VCN_ERROR_CODE_INTERNAL_ERROR;
exceptionClass = "java.lang.RuntimeException";
exceptionMessage =
"Received "
+ exception.getClass().getSimpleName()
+ " with message: "
+ exception.getMessage();
}
mGatewayStatusCallback.onGatewayConnectionError(
mConnectionConfig.getRequiredUnderlyingCapabilities(),
errorCode,
exceptionClass,
exceptionMessage);
}
private void sessionClosed(int token, @Nullable Exception exception) {
if (exception != null) {
notifyStatusCallbackForSessionClosed(exception);
}
// SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the
// Disconnecting state.
sessionLost(token, exception);
sessionLostWithoutCallback(token, exception);
sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
}
@@ -1084,6 +1142,8 @@ public class VcnGatewayConnection extends StateMachine {
}
protected void handleDisconnectRequested(String msg) {
// TODO(b/180526152): notify VcnStatusCallback for Network loss
Slog.v(TAG, "Tearing down. Cause: " + msg);
mIsRunning = false;
@@ -1228,6 +1288,8 @@ public class VcnGatewayConnection extends StateMachine {
String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
// TODO(b/180526152): notify VcnStatusCallback for Network loss
// Will trigger EVENT_SESSION_CLOSED immediately.
mIkeSession.kill();
break;
@@ -1573,8 +1635,7 @@ public class VcnGatewayConnection extends StateMachine {
@Override
protected void exitState() {
// Attempt to set the safe mode alarm - this requires the Vcn Network being validated
// while in ConnectedState (which cancels the previous alarm)
// Will only set a new alarm if no safe mode alarm is currently scheduled.
setSafeModeAlarm();
}
}

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

View File

@@ -75,7 +75,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
@@ -156,8 +156,8 @@ public class VcnManagementServiceTest {
private final LocationPermissionChecker mLocationPermissionChecker =
mock(LocationPermissionChecker.class);
private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor =
ArgumentCaptor.forClass(VcnSafeModeCallback.class);
private final ArgumentCaptor<VcnCallback> mVcnCallbackCaptor =
ArgumentCaptor.forClass(VcnCallback.class);
private final VcnManagementService mVcnMgmtSvc;
@@ -721,7 +721,7 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
private void verifyVcnSafeModeCallback(
private void verifyVcnCallback(
@NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
throws Exception {
verify(mMockDeps)
@@ -730,22 +730,22 @@ public class VcnManagementServiceTest {
eq(subGroup),
eq(TEST_VCN_CONFIG),
eq(snapshot),
mSafeModeCallbackCaptor.capture());
mVcnCallbackCaptor.capture());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue();
safeModeCallback.onEnteredSafeMode();
VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
vcnCallback.onEnteredSafeMode();
verify(mMockPolicyListener).onPolicyChanged();
}
@Test
public void testVcnSafeModeCallbackOnEnteredSafeMode() throws Exception {
public void testVcnCallbackOnEnteredSafeMode() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verifyVcnSafeModeCallback(TEST_UUID_1, snapshot);
verifyVcnCallback(TEST_UUID_1, snapshot);
}
private void triggerVcnStatusCallbackOnEnteredSafeMode(
@@ -771,7 +771,7 @@ public class VcnManagementServiceTest {
// Trigger systemReady() to set up LocationPermissionChecker
mVcnMgmtSvc.systemReady();
verifyVcnSafeModeCallback(subGroup, snapshot);
verifyVcnCallback(subGroup, snapshot);
}
@Test

View File

@@ -20,6 +20,9 @@ import static android.net.IpSecManager.DIRECTION_IN;
import static android.net.IpSecManager.DIRECTION_OUT;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
@@ -39,6 +42,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.TemporaryFailureException;
import android.net.vcn.VcnManager.VcnErrorCode;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,6 +56,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Collections;
/** Tests for VcnGatewayConnection.ConnectedState */
@@ -208,6 +218,25 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
// The child session was closed without exception, so verify that the GatewayStatusCallback
// was not notified
verifyNoMoreInteractions(mGatewayStatusCallback);
}
@Test
public void testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback()
throws Exception {
final IkeInternalException exception = new IkeInternalException(mock(IOException.class));
getChildSessionCallback().onClosedExceptionally(exception);
mTestLooper.dispatchAll();
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
eq(mConfig.getRequiredUnderlyingCapabilities()),
eq(VCN_ERROR_CODE_INTERNAL_ERROR),
any(),
any());
}
@Test
@@ -223,5 +252,42 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
// IkeSession closed with no error, so verify that the GatewayStatusCallback was not
// notified
verifyNoMoreInteractions(mGatewayStatusCallback);
}
private void verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
IkeException cause, @VcnErrorCode int expectedErrorType) {
getIkeSessionCallback().onClosedExceptionally(cause);
mTestLooper.dispatchAll();
verify(mIkeSession).close();
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
eq(mConfig.getRequiredUnderlyingCapabilities()),
eq(expectedErrorType),
any(),
any());
}
@Test
public void testIkeSessionClosedExceptionallyAuthenticationFailure() throws Exception {
verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
new AuthenticationFailedException("vcn test"), VCN_ERROR_CODE_CONFIG_ERROR);
}
@Test
public void testIkeSessionClosedExceptionallyDnsFailure() throws Exception {
verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
new IkeInternalException(new UnknownHostException()), VCN_ERROR_CODE_NETWORK_ERROR);
}
@Test
public void testIkeSessionClosedExceptionallyInternalFailure() throws Exception {
verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR);
}
}

View File

@@ -34,7 +34,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
@@ -56,7 +56,7 @@ public class VcnTest {
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
private VcnSafeModeCallback mVcnSafeModeCallback;
private VcnCallback mVcnCallback;
private Vcn.Dependencies mDeps;
private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
@@ -72,7 +72,7 @@ public class VcnTest {
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnSafeModeCallback = mock(VcnSafeModeCallback.class);
mVcnCallback = mock(VcnCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -104,7 +104,7 @@ public class VcnTest {
TEST_SUB_GROUP,
mConfig,
mSubscriptionSnapshot,
mVcnSafeModeCallback,
mVcnCallback,
mDeps);
}
@@ -179,6 +179,6 @@ public class VcnTest {
verify(gatewayConnection).teardownAsynchronously();
}
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnSafeModeCallback).onEnteredSafeMode();
verify(mVcnCallback).onEnteredSafeMode();
}
}