Show "Available via..." for captive portals even if Wi-Fi is default

Captive portal indicates that the wifi network isn't providing internet
access, even if wifi is the default network. Thus we should show
"Available via app / Sign in to network" instead of "Connected via
app / Sign in to network" for suggestion networks with captive portals
with wifi as the default network.

Bug: 293946849
Test: atest WifiTrackerLibTests
Change-Id: I1ed148a33e98e6c230808c74008d7a39f4568fc5
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
index f7785ff..c83a369 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
@@ -181,6 +181,12 @@
                     connectedStateDescription = getConnectingDescription(mContext, mNetworkInfo);
                     break;
                 case CONNECTED_STATE_CONNECTED:
+                    if (mNetworkCapabilities == null) {
+                        Log.e(TAG, "Tried to get CONNECTED description, but mNetworkCapabilities"
+                                + " was unexpectedly null!");
+                        connectedStateDescription = null;
+                        break;
+                    }
                     connectedStateDescription = getConnectedDescription(mContext,
                             mWifiConfig,
                             mNetworkCapabilities,
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
index 6e64ad7..b4f00ee 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
@@ -198,6 +198,12 @@
                 connectedStateDescription = getConnectingDescription(mContext, mNetworkInfo);
                 break;
             case CONNECTED_STATE_CONNECTED:
+                if (mNetworkCapabilities == null) {
+                    Log.e(TAG, "Tried to get CONNECTED description, but mNetworkCapabilities was"
+                            + " unexpectedly null!");
+                    connectedStateDescription = null;
+                    break;
+                }
                 connectedStateDescription = getConnectedDescription(mContext,
                         mTargetWifiConfig,
                         mNetworkCapabilities,
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
index ad5231d..0d45e66 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
@@ -246,84 +246,87 @@
 
     static String getConnectedDescription(@NonNull Context context,
             @Nullable WifiConfiguration wifiConfiguration,
-            @Nullable NetworkCapabilities networkCapabilities,
+            @NonNull NetworkCapabilities networkCapabilities,
             boolean isDefaultNetwork,
             boolean isLowQuality,
             @Nullable ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport) {
         final StringJoiner sj = new StringJoiner(context.getString(
                 R.string.wifitrackerlib_summary_separator));
 
-        boolean shouldShowConnected = isDefaultNetwork;
+        boolean isValidated = networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+        boolean isCaptivePortal = networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+        boolean isPartialConnectivity = networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+        boolean isNoInternetExpected = wifiConfiguration != null
+                && wifiConfiguration.isNoInternetAccessExpected();
+        boolean isPrivateDnsBroken = !isValidated && networkCapabilities.isPrivateDnsBroken();
+        boolean isCheckingForInternetAccess = !isValidated && !isPartialConnectivity
+                && connectivityReport == null && !isNoInternetExpected;
+        boolean isOemNetwork = NonSdkApiWrapper.isOemCapabilities(networkCapabilities);
+        String suggestionOrSpecifierLabel = null;
         if (wifiConfiguration != null
                 && (wifiConfiguration.fromWifiNetworkSuggestion
                 || wifiConfiguration.fromWifiNetworkSpecifier)) {
-            // For suggestion or specifier networks to show "Connected via ..."
-            final String suggestionOrSpecifierLabel =
-                    getSuggestionOrSpecifierLabel(context, wifiConfiguration);
-            if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) {
-                if (isDefaultNetwork || (networkCapabilities != null
-                        && NonSdkApiWrapper.isOemCapabilities(networkCapabilities))) {
-                    sj.add(context.getString(R.string.wifitrackerlib_connected_via_app,
-                            suggestionOrSpecifierLabel));
-                } else {
-                    // Pretend that non-default, non-OEM networks are unconnected.
-                    sj.add(context.getString(R.string.wifitrackerlib_available_via_app,
-                            suggestionOrSpecifierLabel));
-                }
-                shouldShowConnected = false;
+            suggestionOrSpecifierLabel = getSuggestionOrSpecifierLabel(context, wifiConfiguration);
+        }
+        final boolean shouldShowConnected;
+        if (isValidated) {
+            shouldShowConnected = isDefaultNetwork;
+        } else {
+            // Show "Connected" even if we aren't validated specifically for the
+            // "Connected / No internet access" case, and for OEM-specified networks which aren't
+            // expected to be fully validated.
+            shouldShowConnected = !isCheckingForInternetAccess && !isCaptivePortal
+                    && !isPrivateDnsBroken && !isNoInternetExpected || isOemNetwork;
+        }
+
+        if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) {
+            if (shouldShowConnected || (isDefaultNetwork && isPartialConnectivity)) {
+                // "Connected via app"
+                sj.add(context.getString(R.string.wifitrackerlib_connected_via_app,
+                        suggestionOrSpecifierLabel));
+            } else {
+                // "Available via app"
+                sj.add(context.getString(R.string.wifitrackerlib_available_via_app,
+                        suggestionOrSpecifierLabel));
             }
+        } else if (shouldShowConnected) {
+            // "Connected"
+            sj.add(context.getResources().getStringArray(
+                    R.array.wifitrackerlib_wifi_status)[DetailedState.CONNECTED.ordinal()]);
         }
 
         if (isLowQuality) {
+            // "Low quality"
             sj.add(context.getString(R.string.wifi_connected_low_quality));
-            shouldShowConnected = false;
         }
 
-        // For displaying network capability info, such as captive portal or no internet
-        if (networkCapabilities != null) {
-            if (networkCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) {
-                // "Sign in to network"
-                sj.add(context.getString(context.getResources()
-                        .getIdentifier("network_available_sign_in", "string", "android")));
-                shouldShowConnected = false;
-            } else if (networkCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
-                // "Limited connection..."
+        if (isCaptivePortal) {
+            // "Sign in to network"
+            sj.add(context.getString(context.getResources().getIdentifier(
+                    "network_available_sign_in", "string", "android")));
+        } else if (isPartialConnectivity) {
+            // "Limited connection..."
+            sj.add(context.getString(R.string.wifitrackerlib_wifi_limited_connection));
+        } else if (isCheckingForInternetAccess) {
+            // "Checking for internet access..."
+            sj.add(context.getString(R.string.wifitrackerlib_checking_for_internet_access));
+        } else if (isPrivateDnsBroken) {
+            // "Private DNS server cannot be accessed"
+            sj.add(context.getString(R.string.wifitrackerlib_private_dns_broken));
+        } else if (!isValidated) {
+            if (isNoInternetExpected) {
+                // "Connected to device. Can't provide internet."
                 sj.add(context.getString(
-                        R.string.wifitrackerlib_wifi_limited_connection));
-                shouldShowConnected = false;
-            } else if (!networkCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
-                boolean noInternetExpected = wifiConfiguration != null
-                        && wifiConfiguration.isNoInternetAccessExpected();
-                if (connectivityReport == null && !noInternetExpected) {
-                    // "Checking for internet access..."
-                    sj.add(context.getString(R.string.wifitrackerlib_checking_for_internet_access));
-                    shouldShowConnected = false;
-                } else if (networkCapabilities.isPrivateDnsBroken()) {
-                    // "Private DNS server cannot be accessed"
-                    sj.add(context.getString(R.string.wifitrackerlib_private_dns_broken));
-                    shouldShowConnected = false;
-                } else if (noInternetExpected) {
-                    // "Connected to device. Can't provide internet."
-                    sj.add(context.getString(
-                            R.string.wifitrackerlib_wifi_connected_cannot_provide_internet));
-                    shouldShowConnected = false;
-                } else {
-                    // "No internet access"
-                    sj.add(context.getString(R.string.wifitrackerlib_wifi_no_internet));
-                }
+                        R.string.wifitrackerlib_wifi_connected_cannot_provide_internet));
+            } else {
+                // "No internet access"
+                sj.add(context.getString(R.string.wifitrackerlib_wifi_no_internet));
             }
         }
 
-        // Show "Connected" first if we haven't hidden it due to other strings.
-        if (shouldShowConnected) {
-            return new StringJoiner(context.getString(R.string.wifitrackerlib_summary_separator))
-                    .add(context.getResources().getStringArray(R.array.wifitrackerlib_wifi_status)
-                            [DetailedState.CONNECTED.ordinal()]).merge(sj).toString();
-        }
-
         return sj.toString();
     }
 
diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
index eb7afc8..448bd2e 100644
--- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
+++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
@@ -743,20 +743,30 @@
         ConnectivityDiagnosticsManager.ConnectivityReport connectivityReport = mock(
                 ConnectivityDiagnosticsManager.ConnectivityReport.class);
 
-        // Not default. Do not show "Connected"
+        // Checking for internet access...
+        assertThat(Utils.getConnectedDescription(
+                mMockContext,
+                wifiConfig,
+                networkCapabilities,
+                true,
+                false,
+                null)).isEqualTo(STRING_CHECKING_FOR_INTERNET_ACCESS);
+        // Checking for internet access... without WifiConfiguration (i.e. Passpoint)
         assertThat(Utils.getConnectedDescription(
                 mMockContext,
                 null,
-                null,
+                networkCapabilities,
                 false,
                 false,
-                null)).isEmpty();
+                null)).isEqualTo(STRING_CHECKING_FOR_INTERNET_ACCESS);
 
-        // Default but no info, return "Connected"
+        // Connected
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
+                .thenReturn(true);
         assertThat(Utils.getConnectedDescription(
                 mMockContext,
                 null,
-                null,
+                networkCapabilities,
                 true,
                 false,
                 null)).isEqualTo(STRING_WIFI_STATUS_CONNECTED);
@@ -765,15 +775,17 @@
         assertThat(Utils.getConnectedDescription(
                 mMockContext,
                 null,
-                null,
+                networkCapabilities,
                 false,
                 true,
                 null)).isEqualTo(STRING_CONNECTED_LOW_QUALITY);
 
-        // No internet access
+        // Connected / No internet access
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
+                .thenReturn(false);
         assertThat(Utils.getConnectedDescription(
                 mMockContext,
-                null,
+                wifiConfig,
                 networkCapabilities,
                 true,
                 false,
@@ -800,16 +812,6 @@
                 false,
                 connectivityReport)).isEqualTo(STRING_PRIVATE_DNS_BROKEN);
 
-        // Checking for internet access...
-        when(wifiConfig.isNoInternetAccessExpected()).thenReturn(false);
-        assertThat(Utils.getConnectedDescription(
-                mMockContext,
-                wifiConfig,
-                networkCapabilities,
-                true,
-                false,
-                null)).isEqualTo(STRING_CHECKING_FOR_INTERNET_ACCESS);
-
         // Limited connection
         when(networkCapabilities.hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)).thenReturn(true);
@@ -851,18 +853,9 @@
                 connectivityReport)).isEqualTo(STRING_CONNECTED_VIA_APP + "appLabel"
                 + STRING_SUMMARY_SEPARATOR + STRING_NO_INTERNET);
 
-        // Connected via app + Low quality + No internet access
-        assertThat(Utils.getConnectedDescription(
-                mMockContext,
-                suggestionConfig,
-                networkCapabilities,
-                true,
-                true,
-                connectivityReport)).isEqualTo(STRING_CONNECTED_VIA_APP + "appLabel"
-                + STRING_SUMMARY_SEPARATOR + STRING_CONNECTED_LOW_QUALITY
-                + STRING_SUMMARY_SEPARATOR + STRING_NO_INTERNET);
-
-        // Available via app + Low quality + No internet access
+        // Available via app + Low quality
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
+                .thenReturn(true);
         assertThat(Utils.getConnectedDescription(
                 mMockContext,
                 suggestionConfig,
@@ -870,8 +863,35 @@
                 false,
                 true,
                 connectivityReport)).isEqualTo(STRING_AVAILABLE_VIA_APP + "appLabel"
-                + STRING_SUMMARY_SEPARATOR + STRING_CONNECTED_LOW_QUALITY
-                + STRING_SUMMARY_SEPARATOR + STRING_NO_INTERNET);
+                + STRING_SUMMARY_SEPARATOR + STRING_CONNECTED_LOW_QUALITY);
+
+        // Available via app + Sign in to network
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
+                .thenReturn(false);
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL))
+                .thenReturn(true);
+        assertThat(Utils.getConnectedDescription(
+                mMockContext,
+                suggestionConfig,
+                networkCapabilities,
+                true,
+                false,
+                connectivityReport)).isEqualTo(STRING_AVAILABLE_VIA_APP + "appLabel"
+                + STRING_SUMMARY_SEPARATOR + STRING_NETWORK_AVAILABLE_SIGN_IN);
+
+        // Connected via app + Limited connection...
+        when(networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)).thenReturn(true);
+        when(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL))
+                .thenReturn(false);
+        assertThat(Utils.getConnectedDescription(
+                mMockContext,
+                suggestionConfig,
+                networkCapabilities,
+                true,
+                false,
+                connectivityReport)).isEqualTo(STRING_CONNECTED_VIA_APP + "appLabel"
+                + STRING_SUMMARY_SEPARATOR + STRING_LIMITED_CONNECTION);
     }
 
     @Test