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

Commit d8220c20 authored by Varun Anand's avatar Varun Anand
Browse files

Revert "Take all VPN underlying networks into account when migrating traffic for"

This reverts commit 95aa6d44.

Reason for revert: This change has been implicated in 4-way deadlocks as seen in b/134244752.

Bug: 134244752
Change-Id: I2f1839d7776a613ca571af8a542755ddc5fc8760
Merged-In: Ibdaad3a4cbf0d8ef1ed53cfab1e454b9b878bae9
parent 6a04d49f
Loading
Loading
Loading
Loading
+115 −207
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package android.net;

import static android.os.Process.CLAT_UID;

import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1176,217 +1175,133 @@ public class NetworkStats implements Parcelable {
    /**
     * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface.
     *
     * <p>This method should only be called on delta NetworkStats. Do not call this method on a
     * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change
     * over time.
     * This method should only be called on delta NetworkStats. Do not call this method on a
     * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may
     * change over time.
     *
     * <p>This method performs adjustments for one active VPN package and one VPN iface at a time.
     * This method performs adjustments for one active VPN package and one VPN iface at a time.
     *
     * It is possible for the VPN software to use multiple underlying networks. This method
     * only migrates traffic for the primary underlying network.
     *
     * @param tunUid uid of the VPN application
     * @param tunIface iface of the vpn tunnel
     * @param underlyingIfaces underlying network ifaces used by the VPN application
     * @param underlyingIface the primary underlying network iface used by the VPN application
     * @return true if it successfully adjusts the accounting for VPN, false otherwise
     */
    public void migrateTun(int tunUid, @NonNull String tunIface,
            @NonNull String[] underlyingIfaces) {
        // Combined usage by all apps using VPN.
        final Entry tunIfaceTotal = new Entry();
        // Usage by VPN, grouped by its {@code underlyingIfaces}.
        final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.length];
        // Usage by VPN, summed across all its {@code underlyingIfaces}.
        final Entry underlyingIfacesTotal = new Entry();

        for (int i = 0; i < perInterfaceTotal.length; i++) {
            perInterfaceTotal[i] = new Entry();
        }
    public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) {
        Entry tunIfaceTotal = new Entry();
        Entry underlyingIfaceTotal = new Entry();

        tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal,
                underlyingIfacesTotal);
        tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal);

        // If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app.
        // If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression.
        // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app.
        // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression.
        // Negative stats should be avoided.
        final Entry[] moved =
                addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal,
                        perInterfaceTotal, underlyingIfacesTotal);
        deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved);
        Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal);
        if (pool.isEmpty()) {
            return true;
        }
        Entry moved =
                addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool);
        deductTrafficFromVpnApp(tunUid, underlyingIface, moved);

        if (!moved.isEmpty()) {
            Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved="
                    + moved);
            return false;
        }
        return true;
    }

    /**
     * Initializes the data used by the migrateTun() method.
     *
     * <p>This is the first pass iteration which does the following work:
     *
     * <ul>
     *   <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and
     *       background).
     *   <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
     * </ul>
     *
     * @param tunUid uid of the VPN application
     * @param tunIface iface of the vpn tunnel
     * @param underlyingIfaces underlying network ifaces used by the VPN application
     * @param tunIfaceTotal output parameter; combined data usage by all apps using VPN
     * @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code
     *     underlyingIfaces}
     * @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its
     *     {@code underlyingIfaces}
     * This is the first pass iteration which does the following work:
     * (1) Adds up all the traffic through the tunUid's underlyingIface
     *     (both foreground and background).
     * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself.
     */
    private void tunAdjustmentInit(int tunUid, @NonNull String tunIface,
            @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
            @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
        final Entry recycle = new Entry();
    private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface,
            Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
        Entry recycle = new Entry();
        for (int i = 0; i < size; i++) {
            getValues(i, recycle);
            if (recycle.uid == UID_ALL) {
                throw new IllegalStateException(
                        "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
            }
            if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
            } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
                throw new IllegalStateException(
                        "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
            }
            if (recycle.tag != TAG_NONE) {
                // TODO(b/123666283): Take all tags for tunUid into account.
                continue;
            }

            if (recycle.uid == tunUid) {
                // Add up traffic through tunUid's underlying interfaces.
                for (int j = 0; j < underlyingIfaces.length; j++) {
                    if (Objects.equals(underlyingIfaces[j], recycle.iface)) {
                        perInterfaceTotal[j].add(recycle);
                        underlyingIfacesTotal.add(recycle);
                        break;
                    }
            if (recycle.uid == tunUid && recycle.tag == TAG_NONE
                    && Objects.equals(underlyingIface, recycle.iface)) {
                underlyingIfaceTotal.add(recycle);
            }
            } else if (tunIface.equals(recycle.iface)) {

            if (recycle.uid != tunUid && recycle.tag == TAG_NONE
                    && Objects.equals(tunIface, recycle.iface)) {
                // Add up all tunIface traffic excluding traffic from the vpn app itself.
                tunIfaceTotal.add(recycle);
            }
        }
    }

    /**
     * Distributes traffic across apps that are using given {@code tunIface}, and returns the total
     * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}.
     *
     * @param tunUid uid of the VPN application
     * @param tunIface iface of the vpn tunnel
     * @param underlyingIfaces underlying network ifaces used by the VPN application
     * @param tunIfaceTotal combined data usage across all apps using {@code tunIface}
     * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces}
     * @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code
     *     underlyingIfaces}
     */
    private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface,
            @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal,
            @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) {
        // Traffic that should be moved off of each underlying interface for tunUid (see
        // deductTrafficFromVpnApp below).
        final Entry[] moved = new Entry[underlyingIfaces.length];
        for (int i = 0; i < underlyingIfaces.length; i++) {
            moved[i] = new Entry();
    private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) {
        Entry pool = new Entry();
        pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes);
        pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets);
        pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes);
        pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets);
        pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations);
        return pool;
    }

        final Entry tmpEntry = new Entry();
    private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface,
            Entry tunIfaceTotal, Entry pool) {
        Entry moved = new Entry();
        Entry tmpEntry = new Entry();
        tmpEntry.iface = underlyingIface;
        for (int i = 0; i < size; i++) {
            if (!Objects.equals(iface[i], tunIface)) {
                // Consider only entries that go onto the VPN interface.
                continue;
            }
            if (uid[i] == tunUid) {
                // Exclude VPN app from the redistribution, as it can choose to create packet
                // streams by writing to itself.
                continue;
            }
            tmpEntry.uid = uid[i];
            tmpEntry.tag = tag[i];
            tmpEntry.metered = metered[i];
            tmpEntry.roaming = roaming[i];
            tmpEntry.defaultNetwork = defaultNetwork[i];

            // In a first pass, compute each UID's total share of data across all underlyingIfaces.
            // This is computed on the basis of the share of each UID's usage over tunIface.
            // TODO: Consider refactoring first pass into a separate helper method.
            long totalRxBytes = 0;
            // the vpn app is excluded from the redistribution but all moved traffic will be
            // deducted from the vpn app (see deductTrafficFromVpnApp below).
            if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) {
                if (tunIfaceTotal.rxBytes > 0) {
                // Note - The multiplication below should not overflow since NetworkStatsService
                // processes this every time device has transmitted/received amount equivalent to
                // global threshold alert (~ 2MB) across all interfaces.
                final long rxBytesAcrossUnderlyingIfaces =
                        underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
                // app must not be blamed for more than it consumed on tunIface
                totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
            }
            long totalRxPackets = 0;
            if (tunIfaceTotal.rxPackets > 0) {
                final long rxPacketsAcrossUnderlyingIfaces =
                        underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
                totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
            }
            long totalTxBytes = 0;
            if (tunIfaceTotal.txBytes > 0) {
                final long txBytesAcrossUnderlyingIfaces =
                        underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
                totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
            }
            long totalTxPackets = 0;
            if (tunIfaceTotal.txPackets > 0) {
                final long txPacketsAcrossUnderlyingIfaces =
                        underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
                totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
            }
            long totalOperations = 0;
            if (tunIfaceTotal.operations > 0) {
                final long operationsAcrossUnderlyingIfaces =
                        underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
                totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
            }
            // In a second pass, distribute these values across interfaces in the proportion that
            // each interface represents of the total traffic of the underlying interfaces.
            for (int j = 0; j < underlyingIfaces.length; j++) {
                tmpEntry.iface = underlyingIfaces[j];
                    tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
                } else {
                    tmpEntry.rxBytes = 0;
                // Reset 'set' to correct value since it gets updated when adding debug info below.
                tmpEntry.set = set[i];
                if (underlyingIfacesTotal.rxBytes > 0) {
                    tmpEntry.rxBytes =
                            totalRxBytes
                                    * perInterfaceTotal[j].rxBytes
                                    / underlyingIfacesTotal.rxBytes;
                }
                if (tunIfaceTotal.rxPackets > 0) {
                    tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
                } else {
                    tmpEntry.rxPackets = 0;
                if (underlyingIfacesTotal.rxPackets > 0) {
                    tmpEntry.rxPackets =
                            totalRxPackets
                                    * perInterfaceTotal[j].rxPackets
                                    / underlyingIfacesTotal.rxPackets;
                }
                if (tunIfaceTotal.txBytes > 0) {
                    tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
                } else {
                    tmpEntry.txBytes = 0;
                if (underlyingIfacesTotal.txBytes > 0) {
                    tmpEntry.txBytes =
                            totalTxBytes
                                    * perInterfaceTotal[j].txBytes
                                    / underlyingIfacesTotal.txBytes;
                }
                if (tunIfaceTotal.txPackets > 0) {
                    tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
                } else {
                    tmpEntry.txPackets = 0;
                if (underlyingIfacesTotal.txPackets > 0) {
                    tmpEntry.txPackets =
                            totalTxPackets
                                    * perInterfaceTotal[j].txPackets
                                    / underlyingIfacesTotal.txPackets;
                }
                tmpEntry.operations = 0;
                if (underlyingIfacesTotal.operations > 0) {
                if (tunIfaceTotal.operations > 0) {
                    tmpEntry.operations =
                            totalOperations
                                    * perInterfaceTotal[j].operations
                                    / underlyingIfacesTotal.operations;
                            pool.operations * operations[i] / tunIfaceTotal.operations;
                } else {
                    tmpEntry.operations = 0;
                }

                tmpEntry.uid = uid[i];
                tmpEntry.tag = tag[i];
                tmpEntry.set = set[i];
                tmpEntry.metered = metered[i];
                tmpEntry.roaming = roaming[i];
                tmpEntry.defaultNetwork = defaultNetwork[i];
                combineValues(tmpEntry);
                if (tag[i] == TAG_NONE) {
                    moved[j].add(tmpEntry);
                    moved.add(tmpEntry);
                    // Add debug info
                    tmpEntry.set = SET_DBG_VPN_IN;
                    combineValues(tmpEntry);
@@ -1396,45 +1311,38 @@ public class NetworkStats implements Parcelable {
        return moved;
    }

    private void deductTrafficFromVpnApp(
            int tunUid,
            @NonNull String[] underlyingIfaces,
            @NonNull Entry[] moved) {
        for (int i = 0; i < underlyingIfaces.length; i++) {
    private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
        // Add debug info
            moved[i].uid = tunUid;
            moved[i].set = SET_DBG_VPN_OUT;
            moved[i].tag = TAG_NONE;
            moved[i].iface = underlyingIfaces[i];
            moved[i].metered = METERED_ALL;
            moved[i].roaming = ROAMING_ALL;
            moved[i].defaultNetwork = DEFAULT_NETWORK_ALL;
            combineValues(moved[i]);
        moved.uid = tunUid;
        moved.set = SET_DBG_VPN_OUT;
        moved.tag = TAG_NONE;
        moved.iface = underlyingIface;
        moved.metered = METERED_ALL;
        moved.roaming = ROAMING_ALL;
        moved.defaultNetwork = DEFAULT_NETWORK_ALL;
        combineValues(moved);

        // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
        // the TAG_NONE traffic.
        //
            // Relies on the fact that the underlying traffic only has state ROAMING_NO and
            // METERED_NO, which should be the case as it comes directly from the /proc file.
            // We only blend in the roaming data after applying these adjustments, by checking the
            // NetworkIdentity of the underlying iface.
            final int idxVpnBackground = findIndex(underlyingIfaces[i], tunUid, SET_DEFAULT,
                            TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
        // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO,
        // which should be the case as it comes directly from the /proc file. We only blend in the
        // roaming data after applying these adjustments, by checking the NetworkIdentity of the
        // underlying iface.
        int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
        if (idxVpnBackground != -1) {
                // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed
                // from foreground usage.
                tunSubtract(idxVpnBackground, this, moved[i]);
            tunSubtract(idxVpnBackground, this, moved);
        }

            final int idxVpnForeground = findIndex(underlyingIfaces[i], tunUid, SET_FOREGROUND,
                            TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
        int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
        if (idxVpnForeground != -1) {
                tunSubtract(idxVpnForeground, this, moved[i]);
            }
            tunSubtract(idxVpnForeground, this, moved);
        }
    }

    private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) {
    private static void tunSubtract(int i, NetworkStats left, Entry right) {
        long rxBytes = Math.min(left.rxBytes[i], right.rxBytes);
        left.rxBytes[i] -= rxBytes;
        right.rxBytes -= rxBytes;
+4 −6
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ package com.android.internal.net;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Arrays;

/**
 * A lightweight container used to carry information of the ongoing VPN.
 * Internal use only..
@@ -30,14 +28,14 @@ import java.util.Arrays;
public class VpnInfo implements Parcelable {
    public int ownerUid;
    public String vpnIface;
    public String[] underlyingIfaces;
    public String primaryUnderlyingIface;

    @Override
    public String toString() {
        return "VpnInfo{"
                + "ownerUid=" + ownerUid
                + ", vpnIface='" + vpnIface + '\''
                + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\''
                + ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\''
                + '}';
    }

@@ -50,7 +48,7 @@ public class VpnInfo implements Parcelable {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(ownerUid);
        dest.writeString(vpnIface);
        dest.writeStringArray(underlyingIfaces);
        dest.writeString(primaryUnderlyingIface);
    }

    public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
@@ -59,7 +57,7 @@ public class VpnInfo implements Parcelable {
            VpnInfo info = new VpnInfo();
            info.ownerUid = source.readInt();
            info.vpnIface = source.readString();
            info.underlyingIfaces = source.readStringArray();
            info.primaryUnderlyingIface = source.readString();
            return info;
        }

+13 −41
Original line number Diff line number Diff line
@@ -19,22 +19,13 @@ package android.net;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Param;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class NetworkStatsBenchmark {
    private static final String[] UNDERLYING_IFACES = {"wlan0", "rmnet0"};
    private static final String UNDERLYING_IFACE = "wlan0";
    private static final String TUN_IFACE = "tun0";
    private static final int TUN_UID = 999999999;

    @Param({"100", "1000"})
    private int mSize;
    /**
     * Should not be more than the length of {@link #UNDERLYING_IFACES}.
     */
    @Param({"1", "2"})
    private int mNumUnderlyingIfaces;
    private NetworkStats mNetworkStats;

    @BeforeExperiment
@@ -42,10 +33,8 @@ public class NetworkStatsBenchmark {
        mNetworkStats = new NetworkStats(0, mSize + 2);
        int uid = 0;
        NetworkStats.Entry recycle = new NetworkStats.Entry();
        final List<String> allIfaces = getAllIfacesForBenchmark(); // also contains TUN_IFACE.
        final int totalIfaces = allIfaces.size();
        for (int i = 0; i < mSize; i++) {
            recycle.iface = allIfaces.get(i % totalIfaces);
            recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE;
            recycle.uid = uid;
            recycle.set = i % 2;
            recycle.tag = NetworkStats.TAG_NONE;
@@ -59,9 +48,7 @@ public class NetworkStatsBenchmark {
                uid++;
            }
        }

        for (int i = 0; i < mNumUnderlyingIfaces; i++) {
            recycle.iface = UNDERLYING_IFACES[i];
        recycle.iface = UNDERLYING_IFACE;
        recycle.uid = TUN_UID;
        recycle.set = NetworkStats.SET_FOREGROUND;
        recycle.tag = NetworkStats.TAG_NONE;
@@ -72,26 +59,11 @@ public class NetworkStatsBenchmark {
        recycle.operations = 0;
        mNetworkStats.addValues(recycle);
    }
    }

    private String[] getVpnUnderlyingIfaces() {
        return Arrays.copyOf(UNDERLYING_IFACES, mNumUnderlyingIfaces);
    }

    /**
     * Same as {@link #getVpnUnderlyingIfaces}, but also contains {@link #TUN_IFACE}.
     */
    private List<String> getAllIfacesForBenchmark() {
        List<String> ifaces = new ArrayList<>();
        ifaces.add(TUN_IFACE);
        ifaces.addAll(Arrays.asList(getVpnUnderlyingIfaces()));
        return ifaces;
    }

    public void timeMigrateTun(int reps) {
        for (int i = 0; i < reps; i++) {
            NetworkStats stats = mNetworkStats.clone();
            stats.migrateTun(TUN_UID, TUN_IFACE, getVpnUnderlyingIfaces());
            stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE);
        }
    }

+9 −16
Original line number Diff line number Diff line
@@ -4374,7 +4374,7 @@ public class ConnectivityService extends IConnectivityManager.Stub

    /**
     * @return VPN information for accounting, or null if we can't retrieve all required
     *         information, e.g underlying ifaces.
     *         information, e.g primary underlying iface.
     */
    @Nullable
    private VpnInfo createVpnInfo(Vpn vpn) {
@@ -4386,24 +4386,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
        // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
        // the underlyingNetworks list.
        if (underlyingNetworks == null) {
            NetworkAgentInfo defaultNai = getDefaultNetwork();
            if (defaultNai != null && defaultNai.linkProperties != null) {
                underlyingNetworks = new Network[] { defaultNai.network };
            }
        }
        if (underlyingNetworks != null && underlyingNetworks.length > 0) {
            List<String> interfaces = new ArrayList<>();
            for (Network network : underlyingNetworks) {
                LinkProperties lp = getLinkProperties(network);
                if (lp != null) {
                    interfaces.add(lp.getInterfaceName());
                }
            NetworkAgentInfo defaultNetwork = getDefaultNetwork();
            if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
                info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
            }
            if (!interfaces.isEmpty()) {
                info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
        } else if (underlyingNetworks.length > 0) {
            LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
            if (linkProperties != null) {
                info.primaryUnderlyingIface = linkProperties.getInterfaceName();
            }
        }
        return info.underlyingIfaces == null ? null : info;
        return info.primaryUnderlyingIface == null ? null : info;
    }

    /**
+3 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading