[SB Refactor] Account for underlying wifi networks in old pipeline.

This makes `WifiStatusTracker`'s logic match the logic in
`ConnectivityRepositoryImpl`.

Bug: 225902574
Test: atest WifiStatusTrackerTest
Change-Id: I01a91e38c7b07c9891a47c4cec1ca7cabcae4f49
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 45c0d78..adaf4a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -14,7 +14,9 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -26,6 +28,8 @@
 import android.net.NetworkRequest;
 import android.net.NetworkScoreManager;
 import android.net.ScoredNetwork;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
@@ -34,8 +38,9 @@
 import android.os.Looper;
 import android.provider.Settings;
 
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.R;
-import com.android.settingslib.Utils;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
@@ -46,6 +51,7 @@
 /**
  * Track status of Wi-Fi for the Sys UI.
  */
+@SuppressLint("MissingPermission")
 public class WifiStatusTracker {
     private static final int HISTORY_SIZE = 32;
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -66,8 +72,9 @@
     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
             .clearCapabilities()
             .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
+            .addTransportType(TRANSPORT_WIFI)
+            .addTransportType(TRANSPORT_CELLULAR)
+            .build();
     private final NetworkCallback mNetworkCallback =
             new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
         // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
@@ -75,18 +82,10 @@
         @Override
         public void onCapabilitiesChanged(
                 Network network, NetworkCapabilities networkCapabilities) {
-            boolean isVcnOverWifi = false;
-            boolean isWifi = false;
-            WifiInfo wifiInfo = null;
-            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
-                wifiInfo = Utils.tryGetWifiInfoForVcn(networkCapabilities);
-                isVcnOverWifi = (wifiInfo != null);
-            } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
-                wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
-                isWifi = true;
-            }
+            WifiInfo wifiInfo = getMainOrUnderlyingWifiInfo(networkCapabilities);
+            boolean isWifi = connectionIsWifi(networkCapabilities, wifiInfo);
             // As long as it is a WiFi network, we will log it in the dumpsys for debugging.
-            if (isVcnOverWifi || isWifi) {
+            if (isWifi) {
                 String log = new StringBuilder()
                         .append(SSDF.format(System.currentTimeMillis())).append(",")
                         .append("onCapabilitiesChanged: ")
@@ -303,17 +302,8 @@
             return;
         }
         NetworkCapabilities networkCapabilities;
-        isDefaultNetwork = false;
-        if (mDefaultNetworkCapabilities != null) {
-            boolean isWifi = mDefaultNetworkCapabilities.hasTransport(
-                    NetworkCapabilities.TRANSPORT_WIFI);
-            boolean isVcnOverWifi = mDefaultNetworkCapabilities.hasTransport(
-                    NetworkCapabilities.TRANSPORT_CELLULAR)
-                            && (Utils.tryGetWifiInfoForVcn(mDefaultNetworkCapabilities) != null);
-            if (isWifi || isVcnOverWifi) {
-                isDefaultNetwork = true;
-            }
-        }
+        isDefaultNetwork = mDefaultNetworkCapabilities != null
+                && connectionIsWifi(mDefaultNetworkCapabilities);
         if (isDefaultNetwork) {
             // Wifi is connected and the default network.
             networkCapabilities = mDefaultNetworkCapabilities;
@@ -352,6 +342,70 @@
                 ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
     }
 
+    @Nullable
+    private WifiInfo getMainOrUnderlyingWifiInfo(NetworkCapabilities networkCapabilities) {
+        WifiInfo mainWifiInfo = getMainWifiInfo(networkCapabilities);
+        if (mainWifiInfo != null) {
+            return mainWifiInfo;
+        }
+
+        // Only CELLULAR networks may have underlying wifi information that's relevant to SysUI,
+        // so skip the underlying network check if it's not CELLULAR.
+        if (!networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            return mainWifiInfo;
+        }
+
+        List<Network> underlyingNetworks = networkCapabilities.getUnderlyingNetworks();
+        if (underlyingNetworks == null) {
+            return null;
+        }
+
+        // Some connections, like VPN connections, may have underlying networks that are
+        // eventually traced to a wifi or carrier merged connection. So, check those underlying
+        // networks for possible wifi information as well. See b/225902574.
+        for (Network underlyingNetwork : underlyingNetworks) {
+            NetworkCapabilities underlyingNetworkCapabilities =
+                    mConnectivityManager.getNetworkCapabilities(underlyingNetwork);
+            WifiInfo underlyingWifiInfo = getMainWifiInfo(underlyingNetworkCapabilities);
+            if (underlyingWifiInfo != null) {
+                return underlyingWifiInfo;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private WifiInfo getMainWifiInfo(NetworkCapabilities networkCapabilities) {
+        boolean canHaveWifiInfo = networkCapabilities.hasTransport(TRANSPORT_WIFI)
+                || networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+        if (!canHaveWifiInfo) {
+            return null;
+        }
+
+        TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+        if (transportInfo instanceof VcnTransportInfo) {
+            // This VcnTransportInfo logic is copied from
+            // [com.android.settingslib.Utils.tryGetWifiInfoForVcn]. It's copied instead of
+            // re-used because it makes the logic here clearer.
+            return ((VcnTransportInfo) transportInfo).getWifiInfo();
+        } else if (transportInfo instanceof WifiInfo) {
+            return (WifiInfo) transportInfo;
+        } else {
+            return null;
+        }
+    }
+
+    private boolean connectionIsWifi(NetworkCapabilities networkCapabilities) {
+        return connectionIsWifi(
+                networkCapabilities,
+                getMainOrUnderlyingWifiInfo(networkCapabilities));
+    }
+
+    private boolean connectionIsWifi(NetworkCapabilities networkCapabilities, WifiInfo wifiInfo) {
+        return wifiInfo != null || networkCapabilities.hasTransport(TRANSPORT_WIFI);
+    }
+
     /** Refresh the status label on Locale changed. */
     public void refreshLocale() {
         updateStatusLabel();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
index dc7e313d..6e975cf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
@@ -40,6 +40,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Arrays;
+
 @RunWith(RobolectricTestRunner.class)
 public class WifiStatusTrackerTest {
     @Mock Context mContext;
@@ -48,13 +50,32 @@
     @Mock ConnectivityManager mConnectivityManager;
     @Mock Runnable mCallback;
 
+    private WifiStatusTracker mWifiStatusTracker;
+
     private final ArgumentCaptor<ConnectivityManager.NetworkCallback>
             mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
 
+    private final ArgumentCaptor<ConnectivityManager.NetworkCallback>
+            mDefaultNetworkCallbackCaptor =
+            ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        mWifiStatusTracker = new WifiStatusTracker(
+                mContext,
+                mWifiManager,
+                mNetworkScoreManager,
+                mConnectivityManager,
+                mCallback);
+        mWifiStatusTracker.setListening(true);
+
+        verify(mConnectivityManager)
+                .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any());
+        verify(mConnectivityManager)
+                .registerDefaultNetworkCallback(mDefaultNetworkCallbackCaptor.capture(), any());
     }
 
     /**
@@ -62,13 +83,6 @@
      */
     @Test
     public void testWifiInfoClearedOnPrimaryNetworkLost() {
-        WifiStatusTracker wifiStatusTracker = new WifiStatusTracker(mContext, mWifiManager,
-                mNetworkScoreManager, mConnectivityManager, mCallback);
-        wifiStatusTracker.setListening(true);
-
-        verify(mConnectivityManager)
-                .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any());
-
         // Trigger a validation callback for the primary Wifi network.
         WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
         when(primaryWifiInfo.makeCopy(anyLong())).thenReturn(primaryWifiInfo);
@@ -86,8 +100,8 @@
         mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
 
         // Verify primary wifi info is the one being used.
-        assertThat(wifiStatusTracker.connected).isTrue();
-        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+        assertThat(mWifiStatusTracker.connected).isTrue();
+        assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
 
         // Trigger a validation callback for the non-primary Wifi network.
         WifiInfo nonPrimaryWifiInfo = Mockito.mock(WifiInfo.class);
@@ -106,20 +120,189 @@
         mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(nonPrimaryNetwork, nonPrimaryCap);
 
         // Verify primary wifi info is still the one being used.
-        assertThat(wifiStatusTracker.connected).isTrue();
-        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+        assertThat(mWifiStatusTracker.connected).isTrue();
+        assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
 
         // Lose the non-primary network.
         mNetworkCallbackCaptor.getValue().onLost(nonPrimaryNetwork);
 
         // Verify primary wifi info is still the one being used.
-        assertThat(wifiStatusTracker.connected).isTrue();
-        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+        assertThat(mWifiStatusTracker.connected).isTrue();
+        assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
 
         // Lose the primary network.
         mNetworkCallbackCaptor.getValue().onLost(primaryNetwork);
 
         // Verify we aren't connected anymore.
-        assertThat(wifiStatusTracker.connected).isFalse();
+        assertThat(mWifiStatusTracker.connected).isFalse();
+    }
+
+    @Test
+    public void isCarrierMerged_typicalWifi_false() {
+        WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(primaryWifiInfo.isPrimary()).thenReturn(true);
+
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)).thenReturn(true);
+        when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isCarrierMerged).isFalse();
+    }
+
+    @Test
+    public void isCarrierMerged_typicalCellular_false() {
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isCarrierMerged).isFalse();
+    }
+
+    @Test
+    public void isCarrierMerged_cellularCarrierMergedWifi_true() {
+        WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(primaryWifiInfo.isPrimary()).thenReturn(true);
+        when(primaryWifiInfo.isCarrierMerged()).thenReturn(true);
+
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+        when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isCarrierMerged).isTrue();
+    }
+
+    /** Test for b/225902574. */
+    @Test
+    public void isCarrierMerged_cellularWithUnderlyingCarrierMergedWifi_true() {
+        WifiInfo underlyingCarrierMergedInfo = Mockito.mock(WifiInfo.class);
+        when(underlyingCarrierMergedInfo.isPrimary()).thenReturn(true);
+        when(underlyingCarrierMergedInfo.isCarrierMerged()).thenReturn(true);
+
+        NetworkCapabilities underlyingNetworkCapabilities = Mockito.mock(NetworkCapabilities.class);
+        when(underlyingNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+                .thenReturn(true);
+        when(underlyingNetworkCapabilities.getTransportInfo())
+                .thenReturn(underlyingCarrierMergedInfo);
+
+        Network underlyingNetwork = Mockito.mock(Network.class);
+        when(mConnectivityManager.getNetworkCapabilities(underlyingNetwork))
+                .thenReturn(underlyingNetworkCapabilities);
+
+        NetworkCapabilities mainCapabilities = Mockito.mock(NetworkCapabilities.class);
+        when(mainCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+                .thenReturn(true);
+        when(mainCapabilities.getTransportInfo()).thenReturn(null);
+        when(mainCapabilities.getUnderlyingNetworks())
+                .thenReturn(Arrays.asList(underlyingNetwork));
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, mainCapabilities);
+
+        assertThat(mWifiStatusTracker.isCarrierMerged).isTrue();
+    }
+
+    @Test
+    public void isDefaultNetwork_typicalWifi_true() {
+        WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(primaryWifiInfo.isPrimary()).thenReturn(true);
+
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)).thenReturn(true);
+        when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
+    }
+
+    @Test
+    public void isDefaultNetwork_typicalCellular_false() {
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isDefaultNetwork).isFalse();
+    }
+
+    @Test
+    public void isDefaultNetwork_cellularCarrierMergedWifi_true() {
+        WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(primaryWifiInfo.isPrimary()).thenReturn(true);
+        when(primaryWifiInfo.isCarrierMerged()).thenReturn(true);
+
+        NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+        when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+        when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
+    }
+
+    /** Test for b/225902574. */
+    @Test
+    public void isDefaultNetwork_cellularWithUnderlyingCarrierMergedWifi_true() {
+        WifiInfo underlyingCarrierMergedInfo = Mockito.mock(WifiInfo.class);
+        when(underlyingCarrierMergedInfo.isPrimary()).thenReturn(true);
+        when(underlyingCarrierMergedInfo.isCarrierMerged()).thenReturn(true);
+
+        NetworkCapabilities underlyingNetworkCapabilities = Mockito.mock(NetworkCapabilities.class);
+        when(underlyingNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+                .thenReturn(true);
+        when(underlyingNetworkCapabilities.getTransportInfo())
+                .thenReturn(underlyingCarrierMergedInfo);
+
+        Network underlyingNetwork = Mockito.mock(Network.class);
+        when(mConnectivityManager.getNetworkCapabilities(underlyingNetwork))
+                .thenReturn(underlyingNetworkCapabilities);
+
+        NetworkCapabilities mainCapabilities = Mockito.mock(NetworkCapabilities.class);
+        when(mainCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+                .thenReturn(true);
+        when(mainCapabilities.getTransportInfo()).thenReturn(null);
+        when(mainCapabilities.getUnderlyingNetworks())
+                .thenReturn(Arrays.asList(underlyingNetwork));
+
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+        mDefaultNetworkCallbackCaptor.getValue()
+                .onCapabilitiesChanged(primaryNetwork, mainCapabilities);
+
+        assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
     }
 }