Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe052394 authored by Cody Kesting's avatar Cody Kesting
Browse files

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
parent bf8113b9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
+92 −2
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }
}
+27 −7
Original line number Diff line number Diff line
@@ -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));
    }
}