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

Commit be63c9da authored by Yan Yan's avatar Yan Yan
Browse files

Add VcnUtils to easily get WifiInfo and subId from NetworkCapabilities

This is a preparation CL for mainlining VCN. This patch adds utility
methods for callers to easily get WifiInfo and subId. It also adds
a Builder class that will be exposed as a @SystemApi in a followup
CL.

Bug: 369710077
Test: atest FrameworksVcnTests(new tests) && atest CtsVcnTestCases
Flag: Exempt low risk utilities for pure refactoring
Change-Id: I257b031dabfc48286e8bae2a0854c81aca2f189c
parent bc7a8588
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
package android.net.vcn;

import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS;
import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
@@ -29,6 +31,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SubscriptionManager;

import com.android.internal.util.Preconditions;

import java.util.Objects;

/**
@@ -47,6 +51,7 @@ import java.util.Objects;
 *
 * @hide
 */
// TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore
public class VcnTransportInfo implements TransportInfo, Parcelable {
    @Nullable private final WifiInfo mWifiInfo;
    private final int mSubId;
@@ -195,4 +200,42 @@ public class VcnTransportInfo implements TransportInfo, Parcelable {
                    return new VcnTransportInfo[size];
                }
            };

    /** This class can be used to construct a {@link VcnTransportInfo}. */
    public static final class Builder {
        private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;

        /** Construct Builder */
        public Builder() {}

        /**
         * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout.
         *
         * <p>This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs,
         * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data.
         *
         * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN
         *     Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN
         *     Gateway.
         * @return this {@link Builder} instance, for chaining
         */
        @NonNull
        public Builder setMinUdpPort4500NatTimeoutSeconds(
                @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
                        int minUdpPort4500NatTimeoutSeconds) {
            Preconditions.checkArgument(
                    minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
                    "Timeout must be at least 120s");

            mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
            return Builder.this;
        }

        /** Build a VcnTransportInfo instance */
        @NonNull
        public VcnTransportInfo build() {
            return new VcnTransportInfo(
                    null /* wifiInfo */, INVALID_SUBSCRIPTION_ID, mMinUdpPort4500NatTimeoutSeconds);
        }
    }
}
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.net.vcn;

import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.TransportInfo;
import android.net.wifi.WifiInfo;

import java.util.List;

/**
 * Utility class for VCN callers get information from VCN network
 *
 * @hide
 */
public class VcnUtils {
    /** Get the WifiInfo of the VCN's underlying WiFi network */
    @Nullable
    public static WifiInfo getWifiInfoFromVcnCaps(
            @NonNull ConnectivityManager connectivityMgr,
            @NonNull NetworkCapabilities networkCapabilities) {
        final NetworkCapabilities underlyingCaps =
                getVcnUnderlyingCaps(connectivityMgr, networkCapabilities);

        if (underlyingCaps == null) {
            return null;
        }

        final TransportInfo underlyingTransportInfo = underlyingCaps.getTransportInfo();
        if (!(underlyingTransportInfo instanceof WifiInfo)) {
            return null;
        }

        return (WifiInfo) underlyingTransportInfo;
    }

    /** Get the subscription ID of the VCN's underlying Cell network */
    public static int getSubIdFromVcnCaps(
            @NonNull ConnectivityManager connectivityMgr,
            @NonNull NetworkCapabilities networkCapabilities) {
        final NetworkCapabilities underlyingCaps =
                getVcnUnderlyingCaps(connectivityMgr, networkCapabilities);

        if (underlyingCaps == null) {
            return INVALID_SUBSCRIPTION_ID;
        }

        final NetworkSpecifier underlyingNetworkSpecifier = underlyingCaps.getNetworkSpecifier();
        if (!(underlyingNetworkSpecifier instanceof TelephonyNetworkSpecifier)) {
            return INVALID_SUBSCRIPTION_ID;
        }

        return ((TelephonyNetworkSpecifier) underlyingNetworkSpecifier).getSubscriptionId();
    }

    @Nullable
    private static NetworkCapabilities getVcnUnderlyingCaps(
            @NonNull ConnectivityManager connectivityMgr,
            @NonNull NetworkCapabilities networkCapabilities) {
        // Return null if it is not a VCN network
        if (networkCapabilities.getTransportInfo() == null
                || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) {
            return null;
        }

        // As of Android 16, VCN has one underlying network, and only one. If there are more
        // than one networks due to future changes in the VCN mainline code, just take the first
        // network
        final List<Network> underlyingNws = networkCapabilities.getUnderlyingNetworks();
        if (underlyingNws == null) {
            return null;
        }

        return connectivityMgr.getNetworkCapabilities(underlyingNws.get(0));
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;

import android.net.NetworkCapabilities;
import android.net.wifi.WifiConfiguration;
@@ -39,6 +40,7 @@ public class VcnTransportInfoTest {
    private static final int SUB_ID = 1;
    private static final int NETWORK_ID = 5;
    private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
    private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID = 119;
    private static final WifiInfo WIFI_INFO =
            new WifiInfo.Builder().setNetworkId(NETWORK_ID).build();

@@ -47,6 +49,27 @@ public class VcnTransportInfoTest {
    private static final VcnTransportInfo WIFI_UNDERLYING_INFO =
            new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT);

    @Test
    public void testBuilder() {
        final VcnTransportInfo transportInfo =
                new VcnTransportInfo.Builder()
                        .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT)
                        .build();

        assertEquals(
                MIN_UDP_PORT_4500_NAT_TIMEOUT, transportInfo.getMinUdpPort4500NatTimeoutSeconds());
    }

    @Test
    public void testBuilder_withInvalidNatTimeout() {
        try {
            new VcnTransportInfo.Builder()
                    .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT_INVALID);
            fail("Expected to fail due to invalid NAT timeout");
        } catch (Exception expected) {
        }
    }

    @Test
    public void testGetWifiInfo() {
        assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
+136 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.net.vcn;

import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;

import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;

public class VcnUtilsTest {
    private static final int SUB_ID = 1;

    private static final WifiInfo WIFI_INFO = new WifiInfo.Builder().build();
    private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
            new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
    private static final VcnTransportInfo VCN_TRANSPORT_INFO =
            new VcnTransportInfo.Builder().build();

    private ConnectivityManager mMockConnectivityManager;
    private Network mMockWifiNetwork;
    private Network mMockCellNetwork;

    private NetworkCapabilities mVcnCapsWithUnderlyingWifi;
    private NetworkCapabilities mVcnCapsWithUnderlyingCell;

    @Before
    public void setUp() {
        mMockConnectivityManager = mock(ConnectivityManager.class);

        mMockWifiNetwork = mock(Network.class);
        mVcnCapsWithUnderlyingWifi = newVcnCaps(VCN_TRANSPORT_INFO, mMockWifiNetwork);
        final NetworkCapabilities wifiCaps =
                new NetworkCapabilities.Builder()
                        .addTransportType(TRANSPORT_WIFI)
                        .setTransportInfo(WIFI_INFO)
                        .build();
        when(mMockConnectivityManager.getNetworkCapabilities(mMockWifiNetwork))
                .thenReturn(wifiCaps);

        mMockCellNetwork = mock(Network.class);
        mVcnCapsWithUnderlyingCell = newVcnCaps(VCN_TRANSPORT_INFO, mMockCellNetwork);
        final NetworkCapabilities cellCaps =
                new NetworkCapabilities.Builder()
                        .addTransportType(TRANSPORT_CELLULAR)
                        .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
                        .build();
        when(mMockConnectivityManager.getNetworkCapabilities(mMockCellNetwork))
                .thenReturn(cellCaps);
    }

    private static NetworkCapabilities newVcnCaps(
            VcnTransportInfo vcnTransportInfo, Network underlyingNetwork) {
        return new NetworkCapabilities.Builder()
                .setTransportInfo(vcnTransportInfo)
                .setUnderlyingNetworks(Collections.singletonList(underlyingNetwork))
                .build();
    }

    @Test
    public void getWifiInfoFromVcnCaps() {
        assertEquals(
                WIFI_INFO,
                VcnUtils.getWifiInfoFromVcnCaps(
                        mMockConnectivityManager, mVcnCapsWithUnderlyingWifi));
    }

    @Test
    public void getWifiInfoFromVcnCaps_onVcnWithUnderlyingCell() {
        assertNull(
                VcnUtils.getWifiInfoFromVcnCaps(
                        mMockConnectivityManager, mVcnCapsWithUnderlyingCell));
    }

    @Test
    public void getSubIdFromVcnCaps() {
        assertEquals(
                SUB_ID,
                VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingCell));
    }

    @Test
    public void getSubIdFromVcnCaps_onVcnWithUnderlyingWifi() {
        assertEquals(
                INVALID_SUBSCRIPTION_ID,
                VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, mVcnCapsWithUnderlyingWifi));
    }

    @Test
    public void getSubIdFromVcnCaps_onNonVcnNetwork() {
        assertEquals(
                INVALID_SUBSCRIPTION_ID,
                VcnUtils.getSubIdFromVcnCaps(
                        mMockConnectivityManager, new NetworkCapabilities.Builder().build()));
    }

    @Test
    public void getSubIdFromVcnCaps_withMultipleUnderlyingNetworks() {
        final NetworkCapabilities vcnCaps =
                new NetworkCapabilities.Builder(mVcnCapsWithUnderlyingCell)
                        .setUnderlyingNetworks(
                                Arrays.asList(
                                        new Network[] {mMockCellNetwork, mock(Network.class)}))
                        .build();
        assertEquals(SUB_ID, VcnUtils.getSubIdFromVcnCaps(mMockConnectivityManager, vcnCaps));
    }
}