diff options
5 files changed, 162 insertions, 29 deletions
diff --git a/service/java/com/android/server/wifi/SavedNetworkNominator.java b/service/java/com/android/server/wifi/SavedNetworkNominator.java index cbc2c323e..2d91881a0 100644 --- a/service/java/com/android/server/wifi/SavedNetworkNominator.java +++ b/service/java/com/android/server/wifi/SavedNetworkNominator.java @@ -19,10 +19,12 @@ package com.android.server.wifi; import android.annotation.NonNull; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Pair; import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper; +import com.android.server.wifi.util.WifiPermissionsUtil; import java.util.List; @@ -36,14 +38,17 @@ public class SavedNetworkNominator implements WifiNetworkSelector.NetworkNominat private final LocalLog mLocalLog; private final WifiCarrierInfoManager mWifiCarrierInfoManager; private final PasspointNetworkNominateHelper mPasspointNetworkNominateHelper; + private final WifiPermissionsUtil mWifiPermissionsUtil; SavedNetworkNominator(WifiConfigManager configManager, - PasspointNetworkNominateHelper nominateHelper, - LocalLog localLog, WifiCarrierInfoManager wifiCarrierInfoManager) { + PasspointNetworkNominateHelper nominateHelper, LocalLog localLog, + WifiCarrierInfoManager wifiCarrierInfoManager, + WifiPermissionsUtil wifiPermissionsUtil) { mWifiConfigManager = configManager; mPasspointNetworkNominateHelper = nominateHelper; mLocalLog = localLog; mWifiCarrierInfoManager = wifiCarrierInfoManager; + mWifiPermissionsUtil = wifiPermissionsUtil; } private void localLog(String log) { @@ -127,7 +132,8 @@ public class SavedNetworkNominator implements WifiNetworkSelector.NetworkNominat if (!status.isNetworkEnabled()) { continue; - } else if (network.BSSID != null && !network.BSSID.equals("any") + } + if (network.BSSID != null && !network.BSSID.equals("any") && !network.BSSID.equals(scanResult.BSSID)) { // App has specified the only BSSID to connect for this // configuration. So only the matching ScanResult can be a candidate. @@ -135,20 +141,9 @@ public class SavedNetworkNominator implements WifiNetworkSelector.NetworkNominat + " has specified BSSID " + network.BSSID + ". Skip " + scanResult.BSSID); continue; - } else if (network.enterpriseConfig != null - && network.enterpriseConfig.isAuthenticationSimBased()) { - int subId = mWifiCarrierInfoManager.getBestMatchSubscriptionId(network); - if (!mWifiCarrierInfoManager.isSimPresent(subId)) { - // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present. - localLog("No SIM card is good for Network " - + WifiNetworkSelector.toNetworkString(network)); - continue; - } - // Ignore metered network with non-data Sim, ignore. - if (WifiConfiguration.isMetered(network, null) - && mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(network)) { - continue; - } + } + if (isNetworkSimBasedCredential(network) && !isSimBasedNetworkAbleToAutoJoin(network)) { + continue; } // If the network is marked to use external scores, or is an open network with @@ -170,12 +165,60 @@ public class SavedNetworkNominator implements WifiNetworkSelector.NetworkNominat mPasspointNetworkNominateHelper.getPasspointNetworkCandidates(scanDetails, false); for (Pair<ScanDetail, WifiConfiguration> candidate : candidates) { WifiConfiguration config = candidate.second; - // Ignore metered network with non-data Sim, ignore. - if (WifiConfiguration.isMetered(config, null) - && mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(config)) { + if (isNetworkSimBasedCredential(config) && !isSimBasedNetworkAbleToAutoJoin(config)) { continue; } onConnectableListener.onConnectable(candidate.first, config); } } + + private boolean isSimBasedNetworkAbleToAutoJoin(WifiConfiguration network) { + int carrierId = network.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID + ? mWifiCarrierInfoManager.getDefaultDataSimCarrierId() : network.carrierId; + int subId = mWifiCarrierInfoManager.getMatchingSubId(carrierId); + // Ignore security type is EAP SIM/AKA/AKA' when SIM is not present. + if (!mWifiCarrierInfoManager.isSimPresent(subId)) { + localLog("No SIM card is good for Network " + + WifiNetworkSelector.toNetworkString(network)); + return false; + } + // Ignore IMSI info not available or protection exemption pending network. + if (mWifiCarrierInfoManager.requiresImsiEncryption(subId)) { + if (!mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(subId)) { + localLog("Imsi protection required but not available for Network " + + WifiNetworkSelector.toNetworkString(network)); + return false; + } + } else if (isImsiProtectionApprovalNeeded(network.creatorUid, carrierId)) { + localLog("Imsi protection exemption needed for Network " + + WifiNetworkSelector.toNetworkString(network)); + return false; + } + // Ignore metered network with non-data Sim. + if (WifiConfiguration.isMetered(network, null) + && mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(network)) { + localLog("No default SIM is used for metered Network: " + + WifiNetworkSelector.toNetworkString(network)); + return false; + } + return true; + } + + private boolean isNetworkSimBasedCredential(WifiConfiguration network) { + return network != null && network.enterpriseConfig != null + && network.enterpriseConfig.isAuthenticationSimBased(); + } + + private boolean isImsiProtectionApprovalNeeded(int creatorUid, int carrierId) { + // User saved network got exemption. + if (mWifiPermissionsUtil.checkNetworkSettingsPermission(creatorUid) + || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(creatorUid)) { + return false; + } + if (mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId)) { + return false; + } + mWifiCarrierInfoManager.sendImsiProtectionExemptionNotificationIfRequired(carrierId); + return true; + } } diff --git a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java index 63786f920..a0dcfe9f5 100644 --- a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java +++ b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java @@ -146,8 +146,9 @@ public class WifiCarrierInfoManager { mOnUserApproveCarrierListeners = new ArrayList<>(); - private boolean mUserApprovalUiActive; - private boolean mHasNewDataToSerialize; + private boolean mUserApprovalUiActive = false; + private boolean mHasNewDataToSerialize = false; + private boolean mUserDataLoaded = false; /** * Interface for other modules to listen to the user approve IMSI protection exemption. @@ -175,11 +176,13 @@ public class WifiCarrierInfoManager { @Override public void fromDeserialized(Map<Integer, Boolean> imsiProtectionExemptionMap) { + mUserDataLoaded = true; mImsiPrivacyProtectionExemptionMap.putAll(imsiProtectionExemptionMap); } @Override public void reset() { + mUserDataLoaded = false; mImsiPrivacyProtectionExemptionMap.clear(); } @@ -268,7 +271,6 @@ public class WifiCarrierInfoManager { mIntentFilter.addAction(NOTIFICATION_USER_DISMISSED_INTENT_ACTION); mIntentFilter.addAction(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION); mIntentFilter.addAction(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION); - mUserApprovalUiActive = false; mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, handler); configStore.registerStoreData(wifiInjector.makeImsiProtectionExemptionStoreData( @@ -1341,6 +1343,15 @@ public class WifiCarrierInfoManager { } /** + * Get the carrier Id of the default data sim. + */ + public int getDefaultDataSimCarrierId() { + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId); + return specifiedTm.getSimCarrierId(); + } + + /** * Add a listener to monitor user approval IMSI protection exemption. */ public void addImsiExemptionUserApprovalListener( @@ -1463,6 +1474,10 @@ public class WifiCarrierInfoManager { */ public void sendImsiProtectionExemptionNotificationIfRequired(int carrierId) { int subId = getMatchingSubId(carrierId); + // If user data isn't loaded, don't send notification. + if (!mUserDataLoaded) { + return; + } if (requiresImsiEncryption(subId)) { return; } diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 78bacc56d..23bb75ad0 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -309,7 +309,8 @@ public class WifiInjector { new PasspointNetworkNominateHelper(mPasspointManager, mWifiConfigManager, mConnectivityLocalLog); mSavedNetworkNominator = new SavedNetworkNominator( - mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mWifiCarrierInfoManager); + mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mWifiCarrierInfoManager, + mWifiPermissionsUtil); mNetworkSuggestionNominator = new NetworkSuggestionNominator(mWifiNetworkSuggestionsManager, mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mWifiCarrierInfoManager); mScoredNetworkNominator = new ScoredNetworkNominator(mContext, wifiHandler, diff --git a/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java b/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java index cdc6762ba..839b2e5fe 100644 --- a/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java @@ -29,6 +29,7 @@ import android.util.Pair; import com.android.server.wifi.WifiNetworkSelector.NetworkNominator.OnConnectableListener; import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs; import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper; +import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.After; import org.junit.Before; @@ -52,9 +53,14 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { MockitoAnnotations.initMocks(this); mLocalLog = new LocalLog(512); mSavedNetworkNominator = new SavedNetworkNominator(mWifiConfigManager, - mPasspointNetworkNominateHelper, mLocalLog, mWifiCarrierInfoManager); + mPasspointNetworkNominateHelper, mLocalLog, mWifiCarrierInfoManager, + mWifiPermissionsUtil); when(mWifiCarrierInfoManager.isSimPresent(anyInt())).thenReturn(true); - when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any())).thenReturn(1); + when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any())).thenReturn(VALID_SUBID); + when(mWifiCarrierInfoManager.requiresImsiEncryption(VALID_SUBID)).thenReturn(true); + when(mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(anyInt())).thenReturn(true); + when(mWifiCarrierInfoManager.getMatchingSubId(TEST_CARRIER_ID)).thenReturn(VALID_SUBID); + } /** Cleans up test. */ @@ -65,9 +71,11 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { private ArgumentCaptor<WifiConfiguration> mWifiConfigurationArgumentCaptor = ArgumentCaptor.forClass(WifiConfiguration.class); + private static final int VALID_SUBID = 10; private static final int INVALID_SUBID = 1; private static final int TEST_CARRIER_ID = 100; private static final int RSSI_LEVEL = -50; + private static final int TEST_UID = 1001; private SavedNetworkNominator mSavedNetworkNominator; @Mock private WifiConfigManager mWifiConfigManager; @@ -75,6 +83,7 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { @Mock private OnConnectableListener mOnConnectableListener; @Mock private WifiCarrierInfoManager mWifiCarrierInfoManager; @Mock private PasspointNetworkNominateHelper mPasspointNetworkNominateHelper; + @Mock private WifiPermissionsUtil mWifiPermissionsUtil; private LocalLog mLocalLog; /** @@ -121,8 +130,7 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); savedConfigs[0].carrierId = TEST_CARRIER_ID; // SIM is absent - when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any(WifiConfiguration.class))) - .thenReturn(INVALID_SUBID); + when(mWifiCarrierInfoManager.getMatchingSubId(TEST_CARRIER_ID)).thenReturn(INVALID_SUBID); when(mWifiCarrierInfoManager.isSimPresent(eq(INVALID_SUBID))).thenReturn(false); mSavedNetworkNominator.nominateNetworks(scanDetails, @@ -238,7 +246,7 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { * Verify if a network is metered and with non-data sim, will not nominate as a candidate. */ @Test - public void ignoreNetworksIfMeteredAndFromNonDataSim() { + public void testIgnoreNetworksIfMeteredAndFromNonDataSim() { String[] ssids = {"\"test1\""}; String[] bssids = {"6c:f3:7f:ae:8c:f3"}; int[] freqs = {2470}; @@ -249,6 +257,7 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { freqs, levels, mWifiConfigManager, mClock); List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].carrierId = TEST_CARRIER_ID; when(mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(savedConfigs[0])) .thenReturn(false); mSavedNetworkNominator.nominateNetworks(scanDetails, @@ -259,4 +268,43 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { .thenReturn(true); verify(mOnConnectableListener, never()).onConnectable(any(), any()); } + + /** + * Verify a saved network is from app not user, if IMSI privacy protection is not required, will + * send notification for user to approve exemption, and not consider as a candidate. + */ + @Test + public void testIgnoreNetworksFromAppIfNoImsiProtection() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {2470}; + int[] levels = {RSSI_LEVEL}; + when(mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(any())) + .thenReturn(false); + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigForEapSimNetwork(ssids, bssids, + freqs, levels, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs(); + savedConfigs[0].carrierId = TEST_CARRIER_ID; + // Doesn't require Imsi protection and user didn't approved + when(mWifiCarrierInfoManager.requiresImsiEncryption(VALID_SUBID)).thenReturn(false); + when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID)) + .thenReturn(false); + mSavedNetworkNominator.nominateNetworks(scanDetails, + null, null, true, false, mOnConnectableListener); + verify(mOnConnectableListener, never()).onConnectable(any(), any()); + verify(mWifiCarrierInfoManager) + .sendImsiProtectionExemptionNotificationIfRequired(TEST_CARRIER_ID); + // Simulate user approved + when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID)) + .thenReturn(true); + mSavedNetworkNominator.nominateNetworks(scanDetails, + null, null, true, false, mOnConnectableListener); + verify(mOnConnectableListener).onConnectable(any(), any()); + // If from settings app, will bypass the IMSI check. + when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID)) + .thenReturn(false); + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCarrierInfoManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCarrierInfoManagerTest.java index 1053d733f..28bffa149 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiCarrierInfoManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiCarrierInfoManagerTest.java @@ -75,6 +75,7 @@ import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import javax.crypto.BadPaddingException; @@ -243,6 +244,7 @@ public class WifiCarrierInfoManagerTest extends WifiBaseTest { eq(R.string.wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation))) .thenReturn("blah"); mWifiCarrierInfoManager.addImsiExemptionUserApprovalListener(mListener); + mImsiDataSource.fromDeserialized(new HashMap<>()); } @After @@ -1698,6 +1700,30 @@ public class WifiCarrierInfoManagerTest extends WifiBaseTest { verify(mListener, never()).onUserAllowed(DATA_CARRIER_ID); } + @Test + public void testUserDataStoreIsNotLoadedNotificationWillNotBeSent() { + // reset data source to unloaded state. + mImsiDataSource.reset(); + // Setup carrier without IMSI privacy protection + when(mCarrierConfigManager.getConfigForSubId(DATA_SUBID)) + .thenReturn(generateTestCarrierConfig(false)); + ArgumentCaptor<BroadcastReceiver> receiver = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(receiver.capture(), any(IntentFilter.class)); + + receiver.getValue().onReceive(mContext, + new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + assertFalse(mWifiCarrierInfoManager.requiresImsiEncryption(DATA_SUBID)); + + mWifiCarrierInfoManager.sendImsiProtectionExemptionNotificationIfRequired(DATA_CARRIER_ID); + verifyNoMoreInteractions(mNotificationManger); + + // Loaded user data store, notification should be sent + mImsiDataSource.fromDeserialized(new HashMap<>()); + mWifiCarrierInfoManager.sendImsiProtectionExemptionNotificationIfRequired(DATA_CARRIER_ID); + validateImsiProtectionNotification(CARRIER_NAME); + } + private void validateImsiProtectionNotification(String carrierName) { verify(mNotificationManger, atLeastOnce()).notify( eq(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE), |