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

Commit a90ea9c8 authored by Yan Yan's avatar Yan Yan Committed by Automerger Merge Worker
Browse files

Merge "Add VcnUtils to easily get WifiInfo and subId from NetworkCapabilities"...

Merge "Add VcnUtils to easily get WifiInfo and subId from NetworkCapabilities" into main am: 13ba8acf am: c595ed6a

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/3286259



Change-Id: I8e3302f83d9f1b2144dfb1700b165d1e5c053f55
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 109eb275 c595ed6a
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));
    }
}