Merge sc-qpr1-dev-plus-aosp-without-vendor@7810918

Bug: 205056467
Merged-In: I4ff1d8f4e81a3dd887954001d030b31cb5c7d642
Change-Id: If8e12e89f16cdbc8d4ffb7def51f7c2fc587e907
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java
index b041541..2b9bc78 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/BaseWifiTracker.java
@@ -35,6 +35,7 @@
 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;
@@ -225,9 +226,15 @@
                     }
                     final boolean oldWifiDefault = mIsWifiDefaultRoute;
                     final boolean oldCellDefault = mIsCellDefaultRoute;
-                    // raw Wifi or VPN-over-Wifi is default => Wifi is default.
-                    mIsWifiDefaultRoute = networkCapabilities.hasTransport(TRANSPORT_WIFI);
-                    mIsCellDefaultRoute = networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+                    TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+                    final boolean isVcnOverWifi = transportInfo != null
+                            && transportInfo instanceof VcnTransportInfo
+                            && ((VcnTransportInfo) transportInfo).getWifiInfo() != null;
+                    // raw Wifi or VPN-over-Wifi or VCN-over-Wifi is default => Wifi is default.
+                    mIsWifiDefaultRoute = networkCapabilities.hasTransport(TRANSPORT_WIFI)
+                            || isVcnOverWifi;
+                    mIsCellDefaultRoute = !mIsWifiDefaultRoute
+                            && networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
                     if (mIsWifiDefaultRoute != oldWifiDefault
                             || mIsCellDefaultRoute != oldCellDefault) {
                         if (isVerboseLoggingEnabled()) {
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/MergedCarrierEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/MergedCarrierEntry.java
index 5dea44c..1930cf9 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/MergedCarrierEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/MergedCarrierEntry.java
@@ -102,12 +102,27 @@
         return getConnectedState() == CONNECTED_STATE_DISCONNECTED && !mIsCellDefaultRoute;
     }
 
+    /**
+     * Connect to this merged carrier network and show the "Wi-Fi won't autoconnect for now" toast.
+     * @param callback callback for the connect result
+     */
     @Override
     public synchronized void connect(@Nullable ConnectCallback callback) {
+        connect(callback, true);
+    }
+
+    /**
+     * Connect to this merged carrier network.
+     * @param callback callback for the connect result
+     * @param showToast show the "Wi-Fi won't autoconnect for now" toast if {@code true}
+     */
+    public synchronized void connect(@Nullable ConnectCallback callback, boolean showToast) {
         mConnectCallback = callback;
         mWifiManager.startRestrictingAutoJoinToSubscriptionId(mSubscriptionId);
-        Toast.makeText(mContext,
-                R.string.wifitrackerlib_wifi_wont_autoconnect_for_now, Toast.LENGTH_SHORT).show();
+        if (showToast) {
+            Toast.makeText(mContext, R.string.wifitrackerlib_wifi_wont_autoconnect_for_now,
+                    Toast.LENGTH_SHORT).show();
+        }
         if (mConnectCallback != null) {
             mCallbackHandler.post(() -> {
                 final ConnectCallback connectCallback = mConnectCallback;
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java
index 6ec5d8b..15edf8a 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiEntry.java
@@ -476,6 +476,7 @@
         public String gateway;
         public String subnetMask;
         public int wifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN;
+        public NetworkCapabilities networkCapabilities;
 
         /**
          * Creates an empty ConnectedInfo
@@ -495,6 +496,7 @@
             gateway = other.gateway;
             subnetMask = other.subnetMask;
             wifiStandard = other.wifiStandard;
+            networkCapabilities = other.networkCapabilities;
         }
     }
 
@@ -952,6 +954,7 @@
         if (mConnectedInfo == null) {
             return;
         }
+        mConnectedInfo.networkCapabilities = mNetworkCapabilities;
         mIsValidated = mNetworkCapabilities != null
                 && mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
         notifyOnUpdated();
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
index d76a80a..e97aa28 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
@@ -500,25 +500,28 @@
     private void updateSuggestedWifiEntryScans(@NonNull List<ScanResult> scanResults) {
         checkNotNull(scanResults, "Scan Result list should not be null!");
 
+        // Get every ScanResultKey that is user shareable
+        final Set<StandardWifiEntryKey> userSharedEntryKeys =
+                mWifiManager.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults)
+                        .stream()
+                        .map(StandardWifiEntryKey::new)
+                        .collect(Collectors.toSet());
+
         // Group scans by ScanResultKey key
         final Map<ScanResultKey, List<ScanResult>> scanResultsByKey = scanResults.stream()
                 .filter(scan -> !TextUtils.isEmpty(scan.SSID))
                 .collect(Collectors.groupingBy(ScanResultKey::new));
 
-        // Iterate through current entries and update each entry's scan results
+        // Iterate through current entries and update each entry's scan results and shareability.
         final Set<StandardWifiEntryKey> seenEntryKeys = new ArraySet<>();
         mSuggestedWifiEntryCache.forEach(entry -> {
             final StandardWifiEntryKey entryKey = entry.getStandardWifiEntryKey();
             seenEntryKeys.add(entryKey);
             // Update scan results if available, or set to null.
             entry.updateScanResultInfo(scanResultsByKey.get(entryKey.getScanResultKey()));
+            entry.setUserShareable(userSharedEntryKeys.contains(entryKey));
         });
         // Create new StandardWifiEntry objects for each leftover config with scan results.
-        final Set<StandardWifiEntryKey> userSharedEntryKeys =
-                mWifiManager.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults)
-                        .stream()
-                        .map(StandardWifiEntryKey::new)
-                        .collect(Collectors.toSet());
         for (StandardWifiEntryKey entryKey : mSuggestedConfigCache.keySet()) {
             final ScanResultKey scanKey = entryKey.getScanResultKey();
             if (seenEntryKeys.contains(entryKey)
@@ -821,6 +824,7 @@
             mNetworkRequestEntry = new NetworkRequestEntry(mContext, mMainHandler, entryKey,
                     mWifiManager, mWifiNetworkScoreCache, false /* forSavedNetworksPage */);
             mNetworkRequestEntry.updateConfig(matchingConfigs);
+            updateNetworkRequestEntryScans(mScanResultUpdater.getScanResults());
         }
         mNetworkRequestEntry.updateConnectionInfo(wifiInfo, networkInfo);
     }
diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java
index 858075e..dd487e9 100644
--- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java
+++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/WifiPickerTrackerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -38,6 +39,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkScoreManager;
+import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -850,6 +852,50 @@
                 .count()).isEqualTo(1);
     }
 
+    /**
+     * Tests that a suggestion entry created before scan results are available will be updated to
+     * user shareable after scans become available.
+     */
+    @Test
+    public void testGetWifiEntries_preConnectedSuggestion_becomesUserShareable() {
+        WifiConfiguration suggestionConfig = new WifiConfiguration();
+        suggestionConfig.SSID = "\"ssid\"";
+        suggestionConfig.networkId = 1;
+        suggestionConfig.creatorName = "creator";
+        suggestionConfig.carrierId = 1;
+        suggestionConfig.subscriptionId = 1;
+        suggestionConfig.fromWifiNetworkSuggestion = true;
+        // Initial entries
+        when(mMockWifiManager.getPrivilegedConfiguredNetworks()).thenReturn(
+                Arrays.asList(suggestionConfig));
+        when(mMockWifiInfo.getNetworkId()).thenReturn(suggestionConfig.networkId);
+        when(mMockWifiInfo.getRssi()).thenReturn(-50);
+        when(mMockNetworkInfo.getDetailedState()).thenReturn(NetworkInfo.DetailedState.CONNECTED);
+        final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker();
+        wifiPickerTracker.onStart();
+        verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                any(), any(), any());
+        mTestLooper.dispatchAll();
+        WifiEntry suggestionEntry = wifiPickerTracker.getConnectedWifiEntry();
+        assertThat(suggestionEntry).isNotNull();
+
+        // Update with user-shareable scan results for the suggestion
+        when(mMockWifiManager.getScanResults()).thenReturn(Collections.singletonList(
+                buildScanResult("ssid", "bssid", START_MILLIS)));
+        when(mMockWifiManager.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any()))
+                .thenReturn(Arrays.asList(suggestionConfig));
+        mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
+                new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+        // Disconnect from network to verify its usershareability in the picker list
+        mBroadcastReceiverCaptor.getValue().onReceive(mMockContext,
+                new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+        mTestLooper.dispatchAll();
+
+        // Suggestion entry should be in picker list now
+        suggestionEntry = wifiPickerTracker.getWifiEntries().get(0);
+        assertThat(suggestionEntry.isSuggestion()).isTrue();
+    }
+
     @Test
     public void testGetConnectedEntry_alreadyConnectedToPasspoint_returnsPasspointEntry() {
         final String fqdn = "fqdn";
@@ -1244,4 +1290,40 @@
         // Now Wifi is default, so isDefaultNetwork returns true
         assertThat(mergedCarrierEntry.isDefaultNetwork()).isTrue();
     }
+
+    /**
+     * Tests that the MergedCarrierEntry is the default network when it is connected and
+     * VCN-over-Wifi is the default network.
+     */
+    @Test
+    public void testGetMergedCarrierEntry_vcnWifiIsDefault_entryIsDefaultNetwork() {
+        final int subId = 1;
+        when(mMockWifiInfo.isCarrierMerged()).thenReturn(true);
+        when(mMockWifiInfo.getSubscriptionId()).thenReturn(subId);
+        when(mMockNetworkInfo.getDetailedState()).thenReturn(NetworkInfo.DetailedState.CONNECTED);
+        final WifiPickerTracker wifiPickerTracker = createTestWifiPickerTracker();
+        wifiPickerTracker.onStart();
+        mTestLooper.dispatchAll();
+        verify(mMockContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                any(), any(), any());
+        final Intent intent = new Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        intent.putExtra("subscription", subId);
+        mBroadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+        verify(mMockConnectivityManager)
+                .registerDefaultNetworkCallback(mDefaultNetworkCallbackCaptor.capture(), any());
+        MergedCarrierEntry mergedCarrierEntry = wifiPickerTracker.getMergedCarrierEntry();
+        assertThat(mergedCarrierEntry.getConnectedState())
+                .isEqualTo(WifiEntry.CONNECTED_STATE_CONNECTED);
+        // Wifi isn't default yet, so isDefaultNetwork returns false
+        assertThat(mergedCarrierEntry.isDefaultNetwork()).isFalse();
+
+        mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork,
+                new NetworkCapabilities.Builder()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                        .setTransportInfo(new VcnTransportInfo(new WifiInfo.Builder().build()))
+                        .build());
+
+        // Now VCN-over-Wifi is default, so isDefaultNetwork returns true
+        assertThat(mergedCarrierEntry.isDefaultNetwork()).isTrue();
+    }
 }