diff options
author | Nate(Qiang) Jiang <qiangjiang@google.com> | 2020-01-10 09:13:34 -0800 |
---|---|---|
committer | Nate(Qiang) Jiang <qiangjiang@google.com> | 2020-01-29 09:30:18 -0800 |
commit | c7668957e0944b894a8e8ddbbc17f09beba9fcdf (patch) | |
tree | f9debd0df6838a1f4697f8abcceadf96382aa9df | |
parent | e44c5c010c744aeabd78eb3621cc37184606a284 (diff) |
IMSI protection notification
When a SIM-based network in the range, if it has no IMSI privacy
protection, will send a notifiction to user. If user allow exemption,
this network will be considered auto connect next time.
Bug: 142001564
Test: atest com.android.server.wifi
Change-Id: If94893eb9d6b11abafa197a83c4f3d5f0245f973
12 files changed, 999 insertions, 166 deletions
diff --git a/service/java/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreData.java b/service/java/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreData.java new file mode 100644 index 000000000..acfba80c5 --- /dev/null +++ b/service/java/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreData.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; + +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; +import com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * This class performs serialization and parsing of XML data block that contain the map of IMSI + * protection exemption user approval info. + */ +public class ImsiPrivacyProtectionExemptionStoreData implements WifiConfigStore.StoreData { + private static final String TAG = "ImsiPrivacyProtectionExemptionStoreData"; + private static final String XML_TAG_SECTION_HEADER_IMSI_PROTECTION_EXEMPTION_CARRIER_MAP = + "ImsiPrivacyProtectionExemptionMap"; + private static final String XML_TAG_CARRIER_EXEMPTION_MAP = "CarrierExemptionMap"; + + /** + * Interface define the data source for the carrier IMSI protection exemption map store data. + */ + public interface DataSource { + /** + * Retrieve the IMSI protection exemption map from the data source to serialize to disk. + * + * @return Map of carrier Id to if allowed. + */ + Map<Integer, Boolean> toSerialize(); + + /** + * Set the IMSI protection exemption map in the data source after serializing them from disk + * + * @param imsiProtectionExemptionMap Map of carrier Id to allowed or not. + */ + void fromDeserialized(Map<Integer, Boolean> imsiProtectionExemptionMap); + + /** + * Clear internal data structure in preparation for user switch or initial store read. + */ + void reset(); + + /** + * Indicates whether there is new data to serialize. + */ + boolean hasNewDataToSerialize(); + } + + private final DataSource mDataSource; + + /** + * Set the data source fot store data. + */ + public ImsiPrivacyProtectionExemptionStoreData(@NonNull DataSource dataSource) { + mDataSource = dataSource; + } + + @Override + public void serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil) + throws XmlPullParserException, IOException { + Map<String, Boolean> dataToSerialize = integerMapToStringMap(mDataSource.toSerialize()); + XmlUtil.writeNextValue(out, XML_TAG_CARRIER_EXEMPTION_MAP, dataToSerialize); + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, int version, + WifiConfigStoreEncryptionUtil encryptionUtil) + throws XmlPullParserException, IOException { + // Ignore empty reads. + if (in == null) { + return; + } + + mDataSource.fromDeserialized(parseCarrierImsiProtectionExemptionMap(in, outerTagDepth, + version, encryptionUtil)); + + } + + private Map<Integer, Boolean> parseCarrierImsiProtectionExemptionMap(XmlPullParser in, + int outerTagDepth, + @WifiConfigStore.Version int version, + @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) + throws XmlPullParserException, IOException { + Map<String, Boolean> protectionExemptionMap = new HashMap<>(); + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (valueName[0] == null) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_CARRIER_EXEMPTION_MAP: + if (value instanceof Map) { + protectionExemptionMap = (Map<String, Boolean>) value; + } + break; + default: + Log.w(TAG, "Unknown tag under " + + XML_TAG_SECTION_HEADER_IMSI_PROTECTION_EXEMPTION_CARRIER_MAP + + ": " + valueName[0]); + break; + } + } + return stringMapToIntegerMap(protectionExemptionMap); + } + + private Map<String, Boolean> integerMapToStringMap(Map<Integer, Boolean> input) { + Map<String, Boolean> output = new HashMap<>(); + if (input == null) { + return output; + } + for (Map.Entry<Integer, Boolean> entry : input.entrySet()) { + output.put(Integer.toString(entry.getKey()), entry.getValue()); + } + return output; + } + + private Map<Integer, Boolean> stringMapToIntegerMap(Map<String, Boolean> input) { + Map<Integer, Boolean> output = new HashMap<>(); + if (input == null) { + return output; + } + for (Map.Entry<String, Boolean> entry : input.entrySet()) { + try { + output.put(Integer.valueOf(entry.getKey()), entry.getValue()); + } catch (NumberFormatException e) { + Log.e(TAG, "Failed to Integer convert: " + entry.getKey()); + } + } + return output; + } + + + @Override + public void resetData() { + mDataSource.reset(); + } + + @Override + public boolean hasNewDataToSerialize() { + return mDataSource.hasNewDataToSerialize(); + } + + @Override + public String getName() { + return XML_TAG_SECTION_HEADER_IMSI_PROTECTION_EXEMPTION_CARRIER_MAP; + } + + @Override + public int getStoreFileId() { + // Suggestion Store. + return WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS; + } + +} diff --git a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java index d7d08578f..281791631 100644 --- a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java +++ b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java @@ -25,6 +25,7 @@ import android.util.Pair; import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion; import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper; import com.android.server.wifi.util.ScanResultUtil; +import com.android.server.wifi.util.TelephonyUtil; import java.util.ArrayList; import java.util.Arrays; @@ -54,14 +55,16 @@ public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNo private final WifiConfigManager mWifiConfigManager; private final PasspointNetworkNominateHelper mPasspointNetworkNominateHelper; private final LocalLog mLocalLog; + private final TelephonyUtil mTelephonyUtil; NetworkSuggestionNominator(WifiNetworkSuggestionsManager networkSuggestionsManager, WifiConfigManager wifiConfigManager, PasspointNetworkNominateHelper nominateHelper, - LocalLog localLog) { + LocalLog localLog, TelephonyUtil telephonyUtil) { mWifiNetworkSuggestionsManager = networkSuggestionsManager; mWifiConfigManager = wifiConfigManager; mPasspointNetworkNominateHelper = nominateHelper; mLocalLog = localLog; + mTelephonyUtil = telephonyUtil; } @Override @@ -117,6 +120,9 @@ public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNo if (autoJoinEnabledExtSuggestions.isEmpty()) { continue; } + if (!isSimBasedNetworkAvailableToAutoConnect(candidate.second)) { + continue; + } matchMetaInfo.putAll(autoJoinEnabledExtSuggestions, candidate.second, candidate.first); } @@ -131,14 +137,15 @@ public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNo if (matchingExtNetworkSuggestions == null || matchingExtNetworkSuggestions.isEmpty()) { continue; } - Set<ExtendedWifiNetworkSuggestion> autojoinEnableSuggestions = - matchingExtNetworkSuggestions.stream() - .filter(ewns -> ewns.isAutoJoinEnabled) - .collect(Collectors.toSet()); - autoJoinDisabledSuggestions.addAll( - matchingExtNetworkSuggestions.stream() - .filter(ewns -> !ewns.isAutoJoinEnabled) - .collect(Collectors.toSet())); + Set<ExtendedWifiNetworkSuggestion> autojoinEnableSuggestions = new HashSet<>(); + for (ExtendedWifiNetworkSuggestion ewns : matchingExtNetworkSuggestions) { + if (ewns.isAutoJoinEnabled + && isSimBasedNetworkAvailableToAutoConnect(ewns.wns.wifiConfiguration)) { + autojoinEnableSuggestions.add(ewns); + } else { + autoJoinDisabledSuggestions.add(ewns); + } + } if (autojoinEnableSuggestions.isEmpty()) { continue; @@ -147,19 +154,29 @@ public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNo // them to lookup/add the credentials to WifiConfigManager. // Note: Apps could provide different credentials (password, ceritificate) for the same // network, need to handle that in the future. - ExtendedWifiNetworkSuggestion matchingExtNetworkSuggestion = - autojoinEnableSuggestions.stream().findAny().get(); + String configKey = autojoinEnableSuggestions.stream().findAny().get() + .wns.wifiConfiguration.getKey(); // Check if we already have a network with the same credentials in WifiConfigManager // database. WifiConfiguration wCmConfiguredNetwork = - mWifiConfigManager.getConfiguredNetwork( - matchingExtNetworkSuggestion.wns.wifiConfiguration.getKey()); + mWifiConfigManager.getConfiguredNetwork(configKey); if (wCmConfiguredNetwork != null) { // If existing network is not from suggestion, ignore. if (!(wCmConfiguredNetwork.fromWifiNetworkSuggestion && wCmConfiguredNetwork.allowAutojoin)) { continue; } + int creatorUid = wCmConfiguredNetwork.creatorUid; + Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestionsFromSamePackage = + autojoinEnableSuggestions.stream() + .filter(ewns -> ewns.wns.wifiConfiguration.creatorUid + == creatorUid) + .collect(Collectors.toSet()); + if (matchingExtNetworkSuggestionsFromSamePackage.isEmpty()) { + continue; + } + ExtendedWifiNetworkSuggestion matchingExtNetworkSuggestion = + matchingExtNetworkSuggestionsFromSamePackage.stream().findFirst().get(); // Update the WifiConfigManager with the latest WifiConfig WifiConfiguration config = createConfigForAddingToWifiConfigManager( matchingExtNetworkSuggestion, true); @@ -178,11 +195,28 @@ public class NetworkSuggestionNominator implements WifiNetworkSelector.NetworkNo + WifiNetworkSelector.toNetworkString(wCmConfiguredNetwork)); continue; } + matchingExtNetworkSuggestions = matchingExtNetworkSuggestionsFromSamePackage; } matchMetaInfo.putAll(matchingExtNetworkSuggestions, wCmConfiguredNetwork, scanDetail); } } + private boolean isSimBasedNetworkAvailableToAutoConnect(WifiConfiguration config) { + if (config.enterpriseConfig == null + || !config.enterpriseConfig.isAuthenticationSimBased()) { + return true; + } + int subId = mTelephonyUtil.getBestMatchSubscriptionId(config); + if (!mTelephonyUtil.isSimPresent(subId)) { + mLocalLog.log("SIM is not present for subId: " + subId); + return false; + } + if (mTelephonyUtil.requiresImsiEncryption(subId)) { + return mTelephonyUtil.isImsiEncryptionInfoAvailable(subId); + } + return true; + } + // Add auto-join disabled suggestions also to WifiConfigManager if the app allows credential // sharing.This will surface these networks on the UI, to allow the user manually connect to it. private void addAutojoinDisabledSuggestionToWifiConfigManager( diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 4c6c67068..fdcbb2b10 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -294,7 +294,7 @@ public class WifiInjector { mSavedNetworkNominator = new SavedNetworkNominator( mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mTelephonyUtil); mNetworkSuggestionNominator = new NetworkSuggestionNominator(mWifiNetworkSuggestionsManager, - mWifiConfigManager, nominateHelper, mConnectivityLocalLog); + mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mTelephonyUtil); mScoredNetworkNominator = new ScoredNetworkNominator(mContext, wifiHandler, mFrameworkFacade, mNetworkScoreManager, mContext.getPackageManager(), mWifiConfigManager, mConnectivityLocalLog, @@ -652,6 +652,14 @@ public class WifiInjector { } /** + * + */ + public ImsiPrivacyProtectionExemptionStoreData makeImsiProtectionExemptionStoreData( + ImsiPrivacyProtectionExemptionStoreData.DataSource dataSource) { + return new ImsiPrivacyProtectionExemptionStoreData(dataSource); + } + + /** * Construct an instance of {@link SoftApStoreData}. */ public SoftApStoreData makeSoftApStoreData( diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java index 4ae1378ae..00ac1b34c 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java +++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java @@ -47,7 +47,6 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -102,6 +101,23 @@ public class WifiNetworkSuggestionsManager { @VisibleForTesting public static final String EXTRA_UID = "com.android.server.wifi.extra.NetworkSuggestion.UID"; + + @VisibleForTesting + public static final String EXTRA_CARRIER_NAME = + "com.android.server.wifi.extra.NetworkSuggestion.CARRIER_NAME"; + @VisibleForTesting + public static final String EXTRA_CARRIER_ID = + "com.android.server.wifi.extra.NetworkSuggestion.CARRIER_ID"; + + /** Intent when user tapped action button to allow the app. */ + @VisibleForTesting + public static final String NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION = + "com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_CARRIER"; + /** Intent when user tapped action button to disallow the app. */ + @VisibleForTesting + public static final String NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION = + "com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_CARRIER"; + /** * Limit number of hidden networks attach to scan */ @@ -295,6 +311,8 @@ public class WifiNetworkSuggestionsManager { private final HashMap<String, ExternalCallbackTracker<ISuggestionConnectionStatusListener>> mSuggestionStatusListenerPerApp = new HashMap<>(); + private final Map<Integer, Boolean> mImsiPrivacyProtectionExemptionMap = new HashMap<>(); + /** * Intent filter for processing notification actions. */ @@ -403,27 +421,61 @@ public class WifiNetworkSuggestionsManager { } } + /** + * Module to interact with the wifi config store. + */ + private class ImsiProtectionExemptionDataSource implements + ImsiPrivacyProtectionExemptionStoreData.DataSource { + @Override + public Map<Integer, Boolean> toSerialize() { + // Clear the flag after writing to disk. + // TODO(b/115504887): Don't reset the flag on write failure. + mHasNewDataToSerialize = false; + return mImsiPrivacyProtectionExemptionMap; + } + + @Override + public void fromDeserialized(Map<Integer, Boolean> imsiProtectionExemptionMap) { + mImsiPrivacyProtectionExemptionMap.putAll(imsiProtectionExemptionMap); + } + + @Override + public void reset() { + mImsiPrivacyProtectionExemptionMap.clear(); + } + + @Override + public boolean hasNewDataToSerialize() { + return mHasNewDataToSerialize; + } + } + + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); - if (packageName == null) { - Log.e(TAG, "No package name found in intent"); - return; - } + String carrierName = intent.getStringExtra(EXTRA_CARRIER_NAME); int uid = intent.getIntExtra(EXTRA_UID, -1); - if (uid == -1) { - Log.e(TAG, "No uid found in intent"); - return; - } + int carrierId = intent.getIntExtra(EXTRA_CARRIER_ID, -1); + switch (intent.getAction()) { case NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION: + if (packageName == null || uid == -1) { + Log.e(TAG, "No package name or uid found in intent"); + return; + } Log.i(TAG, "User clicked to allow app"); // Set the user approved flag. setHasUserApprovedForApp(true, packageName); break; case NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION: + if (packageName == null || uid == -1) { + Log.e(TAG, "No package name or uid found in intent"); + return; + } Log.i(TAG, "User clicked to disallow app"); // Set the user approved flag. setHasUserApprovedForApp(false, packageName); @@ -431,6 +483,22 @@ public class WifiNetworkSuggestionsManager { mAppOps.setMode(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, uid, packageName, MODE_IGNORED); break; + case NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION: + if (carrierName == null || carrierId == -1) { + Log.e(TAG, "No carrier name or carrier id found in intent"); + return; + } + Log.i(TAG, "User clicked to allow carrier"); + setHasUserApprovedImsiPrivacyExemptionForCarrier(true, carrierId); + break; + case NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION: + if (carrierName == null || carrierId == -1) { + Log.e(TAG, "No carrier name or carrier id found in intent"); + return; + } + Log.i(TAG, "User clicked to disallow carrier"); + setHasUserApprovedImsiPrivacyExemptionForCarrier(false, carrierId); + break; case NOTIFICATION_USER_DISMISSED_INTENT_ACTION: Log.i(TAG, "User dismissed the notification"); mUserApprovalNotificationActive = false; @@ -469,12 +537,17 @@ public class WifiNetworkSuggestionsManager { // register the data store for serializing/deserializing data. wifiConfigStore.registerStoreData( wifiInjector.makeNetworkSuggestionStoreData(new NetworkSuggestionDataSource())); + wifiConfigStore.registerStoreData(wifiInjector.makeImsiProtectionExemptionStoreData( + new ImsiProtectionExemptionDataSource())); // Register broadcast receiver for UI interactions. mIntentFilter = new IntentFilter(); mIntentFilter.addAction(NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION); mIntentFilter.addAction(NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION); mIntentFilter.addAction(NOTIFICATION_USER_DISMISSED_INTENT_ACTION); + mIntentFilter.addAction(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION); + mIntentFilter.addAction(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION); + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); } @@ -709,6 +782,7 @@ public class WifiNetworkSuggestionsManager { // Start tracking app-op changes from the app if they have active suggestions. startTrackingAppOpsChange(packageName, uid); } + for (ExtendedWifiNetworkSuggestion ewns: extNetworkSuggestions) { if (ewns.wns.passpointConfiguration == null) { if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { @@ -729,6 +803,16 @@ public class WifiNetworkSuggestionsManager { } addToPasspointInfoMap(ewns); } + // If network has no IMSI protection and user didn't approve exemption, make it initial + // auto join disabled + if (isSimBasedSuggestion(ewns)) { + int subId = mTelephonyUtil.getMatchingSubId(getCarrierIdFromSuggestion(ewns)); + if (!(mTelephonyUtil.requiresImsiEncryption(subId) + || hasUserApprovedImsiPrivacyExemptionForCarrier( + getCarrierIdFromSuggestion(ewns)))) { + ewns.isAutoJoinEnabled = false; + } + } perAppInfo.extNetworkSuggestions.remove(ewns); perAppInfo.extNetworkSuggestions.add(ewns); } @@ -740,6 +824,22 @@ public class WifiNetworkSuggestionsManager { return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; } + private int getCarrierIdFromSuggestion(ExtendedWifiNetworkSuggestion ewns) { + if (ewns.wns.passpointConfiguration == null) { + return ewns.wns.wifiConfiguration.carrierId; + } + return ewns.wns.passpointConfiguration.getCarrierId(); + } + + private boolean isSimBasedSuggestion(ExtendedWifiNetworkSuggestion ewns) { + if (ewns.wns.passpointConfiguration == null) { + return ewns.wns.wifiConfiguration.enterpriseConfig != null + && ewns.wns.wifiConfiguration.enterpriseConfig.isAuthenticationSimBased(); + } else { + return ewns.wns.passpointConfiguration.getCredential().getSimCredential() != null; + } + } + private boolean validateNetworkSuggestions(List<WifiNetworkSuggestion> networkSuggestions) { for (WifiNetworkSuggestion wns : networkSuggestions) { if (wns.passpointConfiguration == null) { @@ -900,7 +1000,6 @@ public class WifiNetworkSuggestionsManager { return networkSuggestionList; } - /** * Clear all internal state (for network settings reset). */ @@ -913,6 +1012,7 @@ public class WifiNetworkSuggestionsManager { iter.remove(); } mSuggestionStatusListenerPerApp.clear(); + mImsiPrivacyProtectionExemptionMap.clear(); saveToStore(); Log.i(TAG, "Cleared all internal state"); } @@ -935,13 +1035,60 @@ public class WifiNetworkSuggestionsManager { if (perAppInfo == null) return; if (mVerboseLoggingEnabled) { - Log.v(TAG, "Setting the app " + (approved ? "approved" : "not approved")); + Log.v(TAG, "Setting the app " + packageName + + (approved ? " approved" : " not approved")); } perAppInfo.hasUserApproved = approved; saveToStore(); } /** + * Clear the Imsi Privacy Exemption user approval info the target carrier. + */ + public void clearImsiPrivacyExemptionForCarrier(int carrierId) { + mImsiPrivacyProtectionExemptionMap.remove(carrierId); + saveToStore(); + } + + /** + * Check if carrier have user approved exemption for IMSI protection + */ + public boolean hasUserApprovedImsiPrivacyExemptionForCarrier(int carrierId) { + return mImsiPrivacyProtectionExemptionMap.getOrDefault(carrierId, false); + } + + /** + * Enable or disable exemption on IMSI protection. + */ + public void setHasUserApprovedImsiPrivacyExemptionForCarrier(boolean approved, int carrierId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Setting Imsi privacy exemption for carrier " + carrierId + + (approved ? " approved" : " not approved")); + } + mImsiPrivacyProtectionExemptionMap.put(carrierId, approved); + // If user approved the exemption restore to initial auto join configure. + if (approved) { + restoreInitialAutojoinForCarrierId(carrierId); + } + saveToStore(); + } + + /** + * When user approve the IMSI protection exemption for carrier, restore the initial auto join + * configure. If user already change it to enabled, keep that choice. + */ + private void restoreInitialAutojoinForCarrierId(int carrierId) { + for (PerAppInfo appInfo : mActiveNetworkSuggestionsPerApp.values()) { + for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions) { + if (isSimBasedSuggestion(ewns) + && getCarrierIdFromSuggestion(ewns) == carrierId) { + ewns.isAutoJoinEnabled |= ewns.wns.isInitialAutoJoinEnabled; + } + } + } + } + + /** * Returns a set of all network suggestions across all apps. */ @VisibleForTesting @@ -960,12 +1107,12 @@ public class WifiNetworkSuggestionsManager { .collect(Collectors.toList()); } - private PendingIntent getPrivateBroadcast(@NonNull String action, @NonNull String packageName, - int uid) { + private PendingIntent getPrivateBroadcast(@NonNull String action, + @NonNull Pair<String, String> extra1, @NonNull Pair<String, Integer> extra2) { Intent intent = new Intent(action) .setPackage(mWifiInjector.getWifiStackPackageName()) - .putExtra(EXTRA_PACKAGE_NAME, packageName) - .putExtra(EXTRA_UID, uid); + .putExtra(extra1.first, extra1.second) + .putExtra(extra2.first, extra2.second); return mFrameworkFacade.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } @@ -988,13 +1135,15 @@ public class WifiNetworkSuggestionsManager { new Notification.Action.Builder(null, mResources.getText(R.string.wifi_suggestion_action_allow_app), getPrivateBroadcast(NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION, - packageName, uid)) + Pair.create(EXTRA_PACKAGE_NAME, packageName), + Pair.create(EXTRA_UID, uid))) .build(); Notification.Action userDisallowAppNotificationAction = new Notification.Action.Builder(null, mResources.getText(R.string.wifi_suggestion_action_disallow_app), getPrivateBroadcast(NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, - packageName, uid)) + Pair.create(EXTRA_PACKAGE_NAME, packageName), + Pair.create(EXTRA_UID, uid))) .build(); CharSequence appName = getAppName(packageName, uid); @@ -1007,7 +1156,7 @@ public class WifiNetworkSuggestionsManager { .setStyle(new Notification.BigTextStyle() .bigText(mResources.getString(R.string.wifi_suggestion_content, appName))) .setDeleteIntent(getPrivateBroadcast(NOTIFICATION_USER_DISMISSED_INTENT_ACTION, - packageName, uid)) + Pair.create(EXTRA_PACKAGE_NAME, packageName), Pair.create(EXTRA_UID, uid))) .setShowWhen(false) .setLocalOnly(true) .setColor(mResources.getColor(android.R.color.system_notification_accent_color, @@ -1023,6 +1172,50 @@ public class WifiNetworkSuggestionsManager { mUserApprovalNotificationPackageName = packageName; } + private void sendImsiPrivacyNotification(@NonNull String carrierName, int carrierId) { + Notification.Action userAllowAppNotificationAction = + new Notification.Action.Builder(null, + mResources.getText(R.string + .wifi_suggestion_action_allow_imsi_privacy_exemption_carrier), + getPrivateBroadcast(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION, + Pair.create(EXTRA_CARRIER_NAME, carrierName), + Pair.create(EXTRA_CARRIER_ID, carrierId))) + .build(); + Notification.Action userDisallowAppNotificationAction = + new Notification.Action.Builder(null, + mResources.getText(R.string + .wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier), + getPrivateBroadcast(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION, + Pair.create(EXTRA_CARRIER_NAME, carrierName), + Pair.create(EXTRA_CARRIER_ID, carrierId))) + .build(); + + Notification notification = new Notification.Builder( + mContext, WifiService.NOTIFICATION_NETWORK_STATUS) + .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME, + com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range)) + .setTicker(mResources.getString(R.string.wifi_suggestion_imsi_privacy_title)) + .setContentTitle(mResources.getString(R.string.wifi_suggestion_imsi_privacy_title)) + .setStyle(new Notification.BigTextStyle() + .bigText(mResources.getString(R.string.wifi_suggestion_imsi_privacy_content, + carrierName))) + .setDeleteIntent(getPrivateBroadcast(NOTIFICATION_USER_DISMISSED_INTENT_ACTION, + Pair.create(EXTRA_CARRIER_NAME, carrierName), + Pair.create(EXTRA_CARRIER_ID, carrierId))) + .setShowWhen(false) + .setLocalOnly(true) + .setColor(mResources.getColor(android.R.color.system_notification_accent_color, + mContext.getTheme())) + .addAction(userAllowAppNotificationAction) + .addAction(userDisallowAppNotificationAction) + .build(); + + // Post the notification. + mNotificationManager.notify( + SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE, notification); + mUserApprovalNotificationActive = true; + } + /** * Send user approval notification if the app is not approved * @param packageName app package name @@ -1047,6 +1240,24 @@ public class WifiNetworkSuggestionsManager { return true; } + /** + * Send notification for exemption of IMSI protection if user never made choice before. + */ + private void sendImsiProtectionExemptionNotificationIfRequired(int carrierId) { + int subId = mTelephonyUtil.getMatchingSubId(carrierId); + if (mTelephonyUtil.requiresImsiEncryption(subId)) { + return; + } + if (mImsiPrivacyProtectionExemptionMap.containsKey(carrierId)) { + return; + } + if (mUserApprovalNotificationActive) { + return; + } + Log.i(TAG, "Sending IMSI protection notification for " + carrierId); + sendImsiPrivacyNotification(mTelephonyUtil.getCarrierNameforSubId(subId), carrierId); + } + private @Nullable Set<ExtendedWifiNetworkSuggestion> getNetworkSuggestionsForScanResultMatchInfo( @NonNull ScanResultMatchInfo scanResultMatchInfo, @Nullable MacAddress bssid) { @@ -1089,9 +1300,17 @@ public class WifiNetworkSuggestionsManager { } Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = new HashSet<>(); for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { - if (isNetworkSuggestionUserApprovedAndAvailableToConnect(ewns)) { - approvedExtNetworkSuggestions.add(ewns); + if (!ewns.perAppInfo.hasUserApproved + && ewns.perAppInfo.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { + sendUserApprovalNotificationIfNotApproved(ewns.perAppInfo.packageName, + ewns.perAppInfo.uid); + continue; + } + if (isSimBasedSuggestion(ewns)) { + int carrierId = getCarrierIdFromSuggestion(ewns); + sendImsiProtectionExemptionNotificationIfRequired(carrierId); } + approvedExtNetworkSuggestions.add(ewns); } if (approvedExtNetworkSuggestions.isEmpty()) { @@ -1128,9 +1347,17 @@ public class WifiNetworkSuggestionsManager { } Set<ExtendedWifiNetworkSuggestion> approvedExtNetworkSuggestions = new HashSet<>(); for (ExtendedWifiNetworkSuggestion ewns : extNetworkSuggestions) { - if (isNetworkSuggestionUserApprovedAndAvailableToConnect(ewns)) { - approvedExtNetworkSuggestions.add(ewns); + if (!ewns.perAppInfo.hasUserApproved + && ewns.perAppInfo.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { + sendUserApprovalNotificationIfNotApproved(ewns.perAppInfo.packageName, + ewns.perAppInfo.uid); + continue; + } + if (isSimBasedSuggestion(ewns)) { + int carrierId = getCarrierIdFromSuggestion(ewns); + sendImsiProtectionExemptionNotificationIfRequired(carrierId); } + approvedExtNetworkSuggestions.add(ewns); } if (approvedExtNetworkSuggestions.isEmpty()) { @@ -1144,57 +1371,6 @@ public class WifiNetworkSuggestionsManager { return approvedExtNetworkSuggestions; } - private boolean isNetworkSuggestionUserApprovedAndAvailableToConnect( - ExtendedWifiNetworkSuggestion ewns) { - if (!ewns.perAppInfo.hasUserApproved - && ewns.perAppInfo.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { - sendUserApprovalNotificationIfNotApproved(ewns.perAppInfo.packageName, - ewns.perAppInfo.uid); - return false; - } - WifiConfiguration config = ewns.wns.wifiConfiguration; - PasspointConfiguration passpointConfiguration = ewns.wns.passpointConfiguration; - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - if (config == null) { - Log.e(TAG, "WifiConfiguration is missing for " + ewns); - return false; - } - if (passpointConfiguration != null) { - // If passpoint config is not SIM based return true. - if (passpointConfiguration.getCredential().getSimCredential() == null) { - return true; - } - subId = mTelephonyUtil.getMatchingSubId(passpointConfiguration.getCarrierId()); - } else { - // If Wifi Config is not SIM based return true. - if (config.enterpriseConfig == null - || !config.enterpriseConfig.isAuthenticationSimBased()) { - return true; - } - subId = mTelephonyUtil.getBestMatchSubscriptionId(config); - } - - if (!mTelephonyUtil.isSimPresent(subId)) { - if (mVerboseLoggingEnabled) { - Log.v(TAG, "SIM is not present for subId: " + subId); - } - return false; - } - if (mTelephonyUtil.requiresImsiEncryption(subId) - && !mTelephonyUtil.isImsiEncryptionInfoAvailable(subId)) { - if (mVerboseLoggingEnabled) { - Log.v(TAG, "IMSI encryption is required but info is missing for subId: " - + subId); - } - return false; - } - if (!mTelephonyUtil.requiresImsiEncryption(subId)) { - // TODO(142001564): sendImsiProtectionWarningNotification(); - return false; - } - return true; - } - /** * Returns a set of all network suggestions matching the provided the WifiConfiguration. */ diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java index da207b561..2a5a33e55 100644 --- a/service/java/com/android/server/wifi/WifiShellCommand.java +++ b/service/java/com/android/server/wifi/WifiShellCommand.java @@ -182,6 +182,63 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(hasUserApproved ? "yes" : "no"); return 0; } + case "imsi-protection-exemption-set-user-approved-for-carrier": { + String arg1 = getNextArgRequired(); + String arg2 = getNextArgRequired(); + int carrierId = -1; + boolean approved; + try { + carrierId = Integer.parseInt(arg1); + } catch (NumberFormatException e) { + pw.println("Invalid argument to " + + "'imsi-protection-exemption-set-user-approved-for-carrier' " + + "- carrierId must be an Integer"); + return -1; + } + if ("yes".equals(arg2)) { + approved = true; + } else if ("no".equals(arg2)) { + approved = false; + } else { + pw.println("Invalid argument to " + + "'imsi-protection-exemption-set-user-approved-for-carrier' " + + "- must be 'yes' or 'no'"); + return -1; + } + mWifiNetworkSuggestionsManager + .setHasUserApprovedImsiPrivacyExemptionForCarrier(approved, carrierId); + return 0; + } + case "imsi-protection-exemption-has-user-approved-for-carrier": { + String arg1 = getNextArgRequired(); + int carrierId = -1; + try { + carrierId = Integer.parseInt(arg1); + } catch (NumberFormatException e) { + pw.println("Invalid argument to " + + "'imsi-protection-exemption-has-user-approved-for-carrier' " + + "- 'carrierId' must be an Integer"); + return -1; + } + boolean hasUserApproved = mWifiNetworkSuggestionsManager + .hasUserApprovedImsiPrivacyExemptionForCarrier(carrierId); + pw.println(hasUserApproved ? "yes" : "no"); + return 0; + } + case "imsi-protection-exemption-clear-user-approved-for-carrier": { + String arg1 = getNextArgRequired(); + int carrierId = -1; + try { + carrierId = Integer.parseInt(arg1); + } catch (NumberFormatException e) { + pw.println("Invalid argument to " + + "'imsi-protection-exemption-clear-user-approved-for-carrier' " + + "- 'carrierId' must be an Integer"); + return -1; + } + mWifiNetworkSuggestionsManager.clearImsiPrivacyExemptionForCarrier(carrierId); + return 0; + } case "network-requests-remove-user-approved-access-points": { String packageName = getNextArgRequired(); mClientModeImpl.removeNetworkRequestUserApprovedAccessPointsForApp(packageName); @@ -398,6 +455,12 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(" Sets whether network suggestions from the app is approved or not."); pw.println(" network-suggestions-has-user-approved <package name>"); pw.println(" Queries whether network suggestions from the app is approved or not."); + pw.println(" imsi-protection-exemption-set-user-approved-for-carrier <carrier id> yes|no"); + pw.println(" Sets whether Imsi protection exemption for carrier is approved or not"); + pw.println(" imsi-protection-exemption-has-user-approved-for-carrier <carrier id>"); + pw.println(" Queries whether Imsi protection exemption for carrier is approved or not"); + pw.println(" imsi-protection-exemption-clear-user-approved-for-carrier <carrier id>"); + pw.println(" Clear the user choice on Imsi protection exemption for carrier"); pw.println(" network-requests-remove-user-approved-access-points <package name>"); pw.println(" Removes all user approved network requests for the app."); pw.println(" clear-deleted-ephemeral-networks"); diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java index 95f01a8bc..1dfa3812b 100644 --- a/service/java/com/android/server/wifi/util/TelephonyUtil.java +++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java @@ -1142,4 +1142,20 @@ public class TelephonyUtil { } return TelephonyManager.UNKNOWN_CARRIER_ID; } + + /** + * Get the carrier name for target subscription id. + * @param subId Subscription id + * @return String of carrier name. + */ + public String getCarrierNameforSubId(int subId) { + TelephonyManager specifiedTm = + mTelephonyManager.createForSubscriptionId(subId); + + CharSequence name = specifiedTm.getSimCarrierIdName(); + if (name == null) { + return null; + } + return name.toString(); + } } diff --git a/service/res/values/overlayable.xml b/service/res/values/overlayable.xml index 266c3c8fc..56562dddb 100644 --- a/service/res/values/overlayable.xml +++ b/service/res/values/overlayable.xml @@ -142,6 +142,10 @@ <item type="string" name="wifi_suggestion_content" /> <item type="string" name="wifi_suggestion_action_allow_app" /> <item type="string" name="wifi_suggestion_action_disallow_app" /> + <item type="string" name="wifi_suggestion_imsi_privacy_title" /> + <item type="string" name="wifi_suggestion_imsi_privacy_content" /> + <item type="string" name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier" /> + <item type="string" name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier" /> <item type="string" name="wifi_wakeup_onboarding_title" /> <item type="string" name="wifi_wakeup_onboarding_subtext" /> <item type="string" name="wifi_wakeup_onboarding_action_disable" /> diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml index 1040260d6..df49c4bc8 100644 --- a/service/res/values/strings.xml +++ b/service/res/values/strings.xml @@ -59,6 +59,15 @@ <!-- Notification action for disallowing app specified in the notification body.--> <string name="wifi_suggestion_action_disallow_app">No thanks</string> + <!-- Notification title for a connection to a SIM-based carrier network without IMSI privacy protection. --> + <string name="wifi_suggestion_imsi_privacy_title">Privacy warning for IMSI protection</string> + <!-- Notification content for a connection to a SIM-based carrier network without IMSI privacy protection.--> + <string name="wifi_suggestion_imsi_privacy_content"><xliff:g id="carrierName" example="xxxMobile">%s</xliff:g> suggested networks using your SIM card info without without privacy protection, allow network to be connected automatically? </string> + <!-- Notification action for allowing carrier specified in the notification body.--> + <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier">Allow</string> + <!-- Notification action for disallowing carrier specified in the notification body.--> + <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier">Disallow</string> + <!--Notification title for Wi-Fi Wake onboarding. This is displayed the first time a user disables Wi-Fi with the feature enabled. --> <string name="wifi_wakeup_onboarding_title">Wi\u2011Fi will turn on automatically</string> <!--Notification subtext for Wi-Fi Wake onboarding.--> diff --git a/tests/wifitests/src/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreDataTest.java new file mode 100644 index 000000000..f9e05cb32 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/ImsiPrivacyProtectionExemptionStoreDataTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class ImsiPrivacyProtectionExemptionStoreDataTest { + private static final int TEST_CARRIER_ID = 1911; + + private @Mock ImsiPrivacyProtectionExemptionStoreData.DataSource mDataSource; + private ImsiPrivacyProtectionExemptionStoreData mImsiPrivacyProtectionExemptionStoreData; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mImsiPrivacyProtectionExemptionStoreData = + new ImsiPrivacyProtectionExemptionStoreData(mDataSource); + } + + /** + * Helper function for serializing configuration data to a XML block. + */ + private byte[] serializeData() throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mImsiPrivacyProtectionExemptionStoreData.serializeData(out, null); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + */ + private void deserializeData(byte[] data) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mImsiPrivacyProtectionExemptionStoreData.deserializeData(in, in.getDepth(), + WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION, null); + } + + /** + * Verify store file Id. + */ + @Test + public void verifyStoreFileId() throws Exception { + assertEquals(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS, + mImsiPrivacyProtectionExemptionStoreData.getStoreFileId()); + } + + /** + * Verify serialize and deserialize Protection exemption map. + */ + @Test + public void testSerializeDeserialize() throws Exception { + Map<Integer, Boolean> imsiPrivacyProtectionExemptionMap = new HashMap<>(); + imsiPrivacyProtectionExemptionMap.put(TEST_CARRIER_ID, true); + assertSerializeDeserialize(imsiPrivacyProtectionExemptionMap); + } + + /** + * Verify serialize and deserialize empty protection exemption map. + */ + @Test + public void testSerializeDeserializeEmpty() throws Exception { + Map<Integer, Boolean> imsiPrivacyProtectionExemptionMap = new HashMap<>(); + assertSerializeDeserialize(imsiPrivacyProtectionExemptionMap); + } + + private Map<Integer, Boolean> assertSerializeDeserialize( + Map<Integer, Boolean> mImsiPrivacyProtectionExemptionMap) throws Exception { + // Setup the data to serialize. + when(mDataSource.toSerialize()).thenReturn(mImsiPrivacyProtectionExemptionMap); + + // Serialize/deserialize data. + deserializeData(serializeData()); + + // Verify the deserialized data. + ArgumentCaptor<HashMap> deserializedNetworkSuggestionsMap = + ArgumentCaptor.forClass(HashMap.class); + verify(mDataSource).fromDeserialized(deserializedNetworkSuggestionsMap.capture()); + assertEquals(mImsiPrivacyProtectionExemptionMap, + deserializedNetworkSuggestionsMap.getValue()); + return deserializedNetworkSuggestionsMap.getValue(); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java index e47b2bcf7..01cdee812 100644 --- a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java @@ -20,6 +20,7 @@ import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus .NETWORK_SELECTION_TEMPORARY_DISABLED; +import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_EAP; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig; @@ -27,6 +28,7 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiSsid; import android.util.LocalLog; @@ -37,6 +39,7 @@ import androidx.test.filters.SmallTest; import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion; import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo; import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper; +import com.android.server.wifi.util.TelephonyUtil; import org.junit.Before; import org.junit.Test; @@ -62,11 +65,16 @@ public class NetworkSuggestionNominatorTest extends WifiBaseTest { private static final String TEST_PACKAGE = "com.test"; private static final String TEST_PACKAGE_OTHER = "com.test.other"; private static final String TEST_FQDN = "fqdn"; + private static final int TEST_CARRIER_ID = 1911; + private static final String TEST_CARRIER_NAME = "testCarrier"; + private static final int TEST_SUB_ID = 2020; + private @Mock WifiConfigManager mWifiConfigManager; private @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; private @Mock PasspointNetworkNominateHelper mPasspointNetworkNominateHelper; private @Mock Clock mClock; + private @Mock TelephonyUtil mTelephonyUtil; private NetworkSuggestionNominator mNetworkSuggestionNominator; /** Sets up test. */ @@ -75,7 +83,7 @@ public class NetworkSuggestionNominatorTest extends WifiBaseTest { MockitoAnnotations.initMocks(this); mNetworkSuggestionNominator = new NetworkSuggestionNominator( mWifiNetworkSuggestionsManager, mWifiConfigManager, mPasspointNetworkNominateHelper, - new LocalLog(100)); + new LocalLog(100), mTelephonyUtil); } /** @@ -752,6 +760,98 @@ public class NetworkSuggestionNominatorTest extends WifiBaseTest { suggestions[1].wns.wifiConfiguration)), anyInt(), anyString()); } + @Test + public void testSelectNetworkSuggestionForOneMatchSimBasedWithNoSim() { + String[] scanSsids = {"test1"}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {2470}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {-67}; + String[] suggestionSsids = {"\"" + scanSsids[0] + "\""}; + int[] securities = {SECURITY_EAP}; + boolean[] appInteractions = {true}; + boolean[] meteredness = {true}; + int[] priorities = {-1}; + int[] uids = {TEST_UID}; + String[] packageNames = {TEST_PACKAGE}; + boolean[] autojoin = {true}; + boolean[] shareWithUser = {true}; + + ScanDetail[] scanDetails = + buildScanDetails(scanSsids, bssids, freqs, caps, levels, mClock); + ExtendedWifiNetworkSuggestion[] suggestions = buildNetworkSuggestions(suggestionSsids, + securities, appInteractions, meteredness, priorities, uids, + packageNames, autojoin, shareWithUser); + WifiConfiguration eapSimConfig = suggestions[0].wns.wifiConfiguration; + eapSimConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); + eapSimConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); + eapSimConfig.carrierId = TEST_CARRIER_ID; + when(mTelephonyUtil.getBestMatchSubscriptionId(eapSimConfig)).thenReturn(TEST_SUB_ID); + when(mTelephonyUtil.isSimPresent(TEST_SUB_ID)).thenReturn(false); + // Link the scan result with suggestions. + linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions); + // setup config manager interactions. + setupAddToWifiConfigManager(suggestions[0].wns.wifiConfiguration); + + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>(); + mNetworkSuggestionNominator.nominateNetworks( + Arrays.asList(scanDetails), null, null, true, false, + (ScanDetail scanDetail, WifiConfiguration configuration) -> { + connectableNetworks.add(Pair.create(scanDetail, configuration)); + }); + + // Verify no network is nominated. + assertTrue(connectableNetworks.isEmpty()); + verifyAddToWifiConfigManager(suggestions[0].wns.wifiConfiguration); + } + + @Test + public void testSelectNetworkSuggestionForOneMatchSimBasedWithEncryptionInfoNotAvailabele() { + String[] scanSsids = {"test1"}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {2470}; + String[] caps = {"[WPA2-EAP-CCMP][ESS]"}; + int[] levels = {-67}; + String[] suggestionSsids = {"\"" + scanSsids[0] + "\""}; + int[] securities = {SECURITY_EAP}; + boolean[] appInteractions = {true}; + boolean[] meteredness = {true}; + int[] priorities = {-1}; + int[] uids = {TEST_UID}; + String[] packageNames = {TEST_PACKAGE}; + boolean[] autojoin = {true}; + boolean[] shareWithUser = {true}; + + ScanDetail[] scanDetails = + buildScanDetails(scanSsids, bssids, freqs, caps, levels, mClock); + ExtendedWifiNetworkSuggestion[] suggestions = buildNetworkSuggestions(suggestionSsids, + securities, appInteractions, meteredness, priorities, uids, + packageNames, autojoin, shareWithUser); + WifiConfiguration eapSimConfig = suggestions[0].wns.wifiConfiguration; + eapSimConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM); + eapSimConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); + eapSimConfig.carrierId = TEST_CARRIER_ID; + when(mTelephonyUtil.getBestMatchSubscriptionId(eapSimConfig)).thenReturn(TEST_SUB_ID); + when(mTelephonyUtil.isSimPresent(TEST_SUB_ID)).thenReturn(true); + when(mTelephonyUtil.requiresImsiEncryption(TEST_SUB_ID)).thenReturn(true); + when(mTelephonyUtil.isImsiEncryptionInfoAvailable(TEST_SUB_ID)).thenReturn(false); + // Link the scan result with suggestions. + linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions); + // setup config manager interactions. + setupAddToWifiConfigManager(suggestions[0].wns.wifiConfiguration); + + List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>(); + mNetworkSuggestionNominator.nominateNetworks( + Arrays.asList(scanDetails), null, null, true, false, + (ScanDetail scanDetail, WifiConfiguration configuration) -> { + connectableNetworks.add(Pair.create(scanDetail, configuration)); + }); + + // Verify no network is nominated. + assertTrue(connectableNetworks.isEmpty()); + verifyAddToWifiConfigManager(suggestions[0].wns.wifiConfiguration); + } + private void setupAddToWifiConfigManager(WifiConfiguration...candidates) { for (int i = 0; i < candidates.length; i++) { WifiConfiguration candidate = candidates[i]; diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java index e623e7573..874b5240a 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java @@ -24,7 +24,9 @@ import static android.app.Notification.EXTRA_BIG_TEXT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION; +import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION; +import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISMISSED_INTENT_ACTION; import static org.junit.Assert.assertEquals; @@ -126,6 +128,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { private @Mock WifiConfigStore mWifiConfigStore; private @Mock WifiConfigManager mWifiConfigManager; private @Mock NetworkSuggestionStoreData mNetworkSuggestionStoreData; + private @Mock ImsiPrivacyProtectionExemptionStoreData mImsiPrivacyProtectionExemptionStoreData; private @Mock WifiMetrics mWifiMetrics; private @Mock TelephonyUtil mTelephonyUtil; private @Mock PasspointManager mPasspointManager; @@ -142,6 +145,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; private NetworkSuggestionStoreData.DataSource mDataSource; + private ImsiPrivacyProtectionExemptionStoreData.DataSource mImsiDataSource; /** * Setup the mocks. @@ -155,6 +159,8 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { when(mWifiInjector.makeNetworkSuggestionStoreData(any())) .thenReturn(mNetworkSuggestionStoreData); + when(mWifiInjector.makeImsiProtectionExemptionStoreData(any())) + .thenReturn(mImsiPrivacyProtectionExemptionStoreData); when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade); when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager); when(mFrameworkFacade.getBroadcast(any(), anyInt(), any(), anyInt())) @@ -177,6 +183,18 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { when(mResources.getText(eq(R.string.wifi_suggestion_action_disallow_app))) .thenReturn("blah"); + // setup resource strings for IMSI protection notification. + when(mResources.getString(eq(R.string.wifi_suggestion_imsi_privacy_title))) + .thenReturn("blah"); + when(mResources.getString(eq(R.string.wifi_suggestion_imsi_privacy_content), anyString())) + .thenAnswer(s -> "blah" + s.getArguments()[1]); + when(mResources.getText( + eq(R.string.wifi_suggestion_action_allow_imsi_privacy_exemption_carrier))) + .thenReturn("blah"); + when(mResources.getText( + eq(R.string.wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier))) + .thenReturn("blah"); + // Our app Info. Needed for notification builder. ApplicationInfo ourAppInfo = new ApplicationInfo(); when(mContext.getApplicationInfo()).thenReturn(ourAppInfo); @@ -209,6 +227,14 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { mDataSource = dataSourceArgumentCaptor.getValue(); assertNotNull(mDataSource); + ArgumentCaptor<ImsiPrivacyProtectionExemptionStoreData.DataSource> + imsiDataSourceArgumentCaptor = + ArgumentCaptor.forClass(ImsiPrivacyProtectionExemptionStoreData.DataSource.class); + verify(mWifiInjector).makeImsiProtectionExemptionStoreData(imsiDataSourceArgumentCaptor + .capture()); + mImsiDataSource = imsiDataSourceArgumentCaptor.getValue(); + assertNotNull(mImsiDataSource); + mWifiNetworkSuggestionsManager.enableVerboseLogging(1); } @@ -538,39 +564,6 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { } /** - * Do not evaluate the suggested network which requires SIM card, but the SIM is absent. - */ - @Test - public void testGetNetworkSuggestionsForScanDtailIgnoreEapSimNetworkForAbsentSim() { - WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE); - config.carrierId = VALID_CARRIER_ID; - WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - config, null, false, false, true, true, false); - List<WifiNetworkSuggestion> networkSuggestionList1 = - new ArrayList<WifiNetworkSuggestion>() {{ - add(networkSuggestion); - }}; - when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(eq(TEST_UID_1))) - .thenReturn(true); - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, - mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1, - TEST_PACKAGE_1, TEST_FEATURE)); - mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1); - - ScanDetail scanDetail = createScanDetailForNetwork(networkSuggestion.wifiConfiguration); - - when(mTelephonyUtil.getBestMatchSubscriptionId(any(WifiConfiguration.class))) - .thenReturn(TEST_SUBID); - when(mTelephonyUtil.isSimPresent(eq(TEST_SUBID))).thenReturn(false); - - Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = - mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail); - - assertNull(matchingExtNetworkSuggestions); - } - - /** * Verify a successful lookup of a single network suggestion matching the provided scan detail. */ @Test @@ -1209,7 +1202,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { * {@link WifiNetworkSuggestion#isAppInteractionRequired} flag set. * b) The app holds location permission. * c) App has not been approved by the user. - * This should trigger a broadcast to the app. + * This should not trigger a broadcast to the app. */ @Test public void testOnNetworkConnectionWhenAppNotApproved() { @@ -2038,7 +2031,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { TEST_PACKAGE_1, TEST_FEATURE)); validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user dismissal notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); reset(mNotificationManger); @@ -2049,7 +2042,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user dismissal notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); reset(mNotificationManger); @@ -2080,7 +2073,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user dismissal notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); reset(mNotificationManger); @@ -2091,7 +2084,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user clicking on allow in the notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Cancel the notification. verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); @@ -2128,7 +2121,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user dismissal notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISMISSED_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); reset(mNotificationManger); @@ -2139,7 +2132,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user clicking on disallow in the notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Ensure we turn off CHANGE_WIFI_STATE app-ops. verify(mAppOpsManager).setMode( @@ -2313,7 +2306,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user clicking on allow in the notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Cancel the notification. verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); @@ -2350,7 +2343,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { validateUserApprovalNotification(TEST_APP_NAME_1); // Simulate user clicking on disallow in the notification. - sendBroadcastForUserAction( + sendBroadcastForUserActionOnApp( NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1); // Ensure we turn off CHANGE_WIFI_STATE app-ops. verify(mAppOpsManager).setMode( @@ -2461,8 +2454,22 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { private boolean checkUserApprovalNotificationParams( Notification notification, String expectedAppName) { - if (!notification.extras.getString(EXTRA_BIG_TEXT).contains(expectedAppName)) return false; - return true; + return notification.extras.getString(EXTRA_BIG_TEXT).contains(expectedAppName); + } + + private boolean checkImsiProtectionNotificationParams( + Notification notification, String carrierName) { + return notification.extras.getString(EXTRA_BIG_TEXT).contains(carrierName); + } + + private void validateImsiProtectionNotification(String carrierName) { + ArgumentCaptor<Notification> notificationArgumentCaptor = + ArgumentCaptor.forClass(Notification.class); + verify(mNotificationManger).notify(eq(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE), + notificationArgumentCaptor.capture()); + Notification notification = notificationArgumentCaptor.getValue(); + assertNotNull(notification); + assertTrue(checkImsiProtectionNotificationParams(notification, carrierName)); } private void validateUserApprovalNotification(String... anyOfExpectedAppNames) { @@ -2482,7 +2489,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { assertTrue(foundMatch); } - private void sendBroadcastForUserAction(String action, String packageName, int uid) { + private void sendBroadcastForUserActionOnApp(String action, String packageName, int uid) { Intent intent = new Intent() .setAction(action) .putExtra(WifiNetworkSuggestionsManager.EXTRA_PACKAGE_NAME, packageName) @@ -2491,6 +2498,16 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); } + private void sendBroadcastForUserActionOnImsi(String action, String carrierName, + int carrierId) { + Intent intent = new Intent() + .setAction(action) + .putExtra(WifiNetworkSuggestionsManager.EXTRA_CARRIER_NAME, carrierName) + .putExtra(WifiNetworkSuggestionsManager.EXTRA_CARRIER_ID, carrierId); + assertNotNull(mBroadcastReceiverCaptor.getValue()); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + } + @Test public void testAddSuggestionWithValidCarrierIdWithCarrierProvisionPermission() { WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); @@ -2803,7 +2820,7 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { .thenReturn(false); when(mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE_1)) .thenReturn(VALID_CARRIER_ID); - when(mTelephonyUtil.getBestMatchSubscriptionId(config)).thenReturn(TEST_SUBID); + when(mTelephonyUtil.getMatchingSubId(VALID_CARRIER_ID)).thenReturn(TEST_SUBID); when(mTelephonyUtil.isSimPresent(TEST_SUBID)).thenReturn(true); when(mTelephonyUtil.requiresImsiEncryption(TEST_SUBID)).thenReturn(true); when(mTelephonyUtil.isImsiEncryptionInfoAvailable(TEST_SUBID)).thenReturn(true); @@ -2823,38 +2840,6 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { } /** - * Verify matched SIM-based network won't return when imsi protection isn't available. - * Todo(142001564): verify user approval notification. - */ - @Test - public void testMatchSimBasedNetworkWithoutImsiProtection() { - WifiConfiguration config = - WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM, - WifiEnterpriseConfig.Phase2.NONE); - WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( - config, null, true, false, true, true, false); - List<WifiNetworkSuggestion> networkSuggestionList = new ArrayList<>(); - networkSuggestionList.add(networkSuggestion); - when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1)) - .thenReturn(false); - when(mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE_1)) - .thenReturn(VALID_CARRIER_ID); - when(mTelephonyUtil.getBestMatchSubscriptionId(config)).thenReturn(TEST_SUBID); - when(mTelephonyUtil.isSimPresent(TEST_SUBID)).thenReturn(true); - when(mTelephonyUtil.requiresImsiEncryption(TEST_SUBID)).thenReturn(false); - when(mTelephonyUtil.isImsiEncryptionInfoAvailable(TEST_SUBID)).thenReturn(false); - ScanDetail scanDetail = createScanDetailForNetwork(config); - int status = mWifiNetworkSuggestionsManager - .add(networkSuggestionList, TEST_UID_1, TEST_PACKAGE_1, TEST_FEATURE); - assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, status); - - Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions = - mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail); - //Todo(142001564): should send out user notification. - assertNull(matchingExtNetworkSuggestions); - } - - /** * Verify when SIM changes, the app loses carrier privilege. Suggestions from this app will be * removed. If this app suggests again, it will be considered as normal suggestor. */ @@ -3108,6 +3093,132 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { } /** + * Test the IMSI protection notification and user click on the allow. + */ + @Test + public void testSendImsiProtectionNotificationOnUserAllowed() { + when(mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE_1)) + .thenReturn(TEST_CARRIER_ID); + when(mTelephonyUtil.getMatchingSubId(TEST_CARRIER_ID)).thenReturn(TEST_SUBID); + when(mTelephonyUtil.getCarrierNameforSubId(TEST_SUBID)).thenReturn(TEST_CARRIER_NAME); + when(mTelephonyUtil.requiresImsiEncryption(TEST_SUBID)).thenReturn(false); + WifiConfiguration eapSimConfig = WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE); + WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( + eapSimConfig, null, true, false, true, true, + false); + List<WifiNetworkSuggestion> networkSuggestionList = + new ArrayList<WifiNetworkSuggestion>() {{ + add(networkSuggestion); + }}; + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, + TEST_PACKAGE_1, TEST_FEATURE)); + verifyNoMoreInteractions(mNotificationManger); + Set<ExtendedWifiNetworkSuggestion> matchedSuggestion = mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForScanDetail(createScanDetailForNetwork(eapSimConfig)); + validateImsiProtectionNotification(TEST_CARRIER_NAME); + for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestion) { + assertFalse(ewns.isAutoJoinEnabled); + } + sendBroadcastForUserActionOnImsi(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION, + TEST_CARRIER_NAME, TEST_CARRIER_ID); + verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); + + verify(mWifiConfigManager, times(2)).saveToStore(true); + assertTrue(mImsiDataSource.hasNewDataToSerialize()); + matchedSuggestion = mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForScanDetail(createScanDetailForNetwork(eapSimConfig)); + verifyNoMoreInteractions(mNotificationManger); + assertTrue(mWifiNetworkSuggestionsManager + .hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID)); + + for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestion) { + assertTrue(ewns.isAutoJoinEnabled); + } + } + + /** + * Test the IMSI protection notification and user click on the disallow. + */ + @Test + public void testSendImsiProtectionNotificationOnUserDisallowed() { + when(mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE_1)) + .thenReturn(TEST_CARRIER_ID); + when(mTelephonyUtil.getMatchingSubId(TEST_CARRIER_ID)).thenReturn(TEST_SUBID); + when(mTelephonyUtil.getCarrierNameforSubId(TEST_SUBID)).thenReturn(TEST_CARRIER_NAME); + when(mTelephonyUtil.requiresImsiEncryption(TEST_SUBID)).thenReturn(false); + WifiConfiguration eapSimConfig = WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE); + WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( + eapSimConfig, null, true, false, true, true, + false); + List<WifiNetworkSuggestion> networkSuggestionList = + new ArrayList<WifiNetworkSuggestion>() {{ + add(networkSuggestion); + }}; + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, + TEST_PACKAGE_1, TEST_FEATURE)); + Set<ExtendedWifiNetworkSuggestion> matchedSuggestion = mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForScanDetail(createScanDetailForNetwork(eapSimConfig)); + validateImsiProtectionNotification(TEST_CARRIER_NAME); + for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestion) { + assertFalse(ewns.isAutoJoinEnabled); + } + sendBroadcastForUserActionOnImsi(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION, + TEST_CARRIER_NAME, TEST_CARRIER_ID); + verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE); + + verify(mWifiConfigManager, times(2)).saveToStore(true); + assertTrue(mImsiDataSource.hasNewDataToSerialize()); + matchedSuggestion = mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForScanDetail(createScanDetailForNetwork(eapSimConfig)); + verifyNoMoreInteractions(mNotificationManger); + assertFalse(mWifiNetworkSuggestionsManager + .hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID)); + + for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestion) { + assertFalse(ewns.isAutoJoinEnabled); + } + } + + /** + * Test when carrier start to support IMSI protection, imsiExemptionMap will update too. + */ + @Test + public void testUpdateImsiExemptionMapWhenCarrierFromWithoutProtectionToWithProtection() { + // Simulate user click on disallow before. + mWifiNetworkSuggestionsManager + .setHasUserApprovedImsiPrivacyExemptionForCarrier(false, TEST_CARRIER_ID); + verifyNoMoreInteractions(mNotificationManger); + // Now carrier upgrade to support Imsi protection + when(mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE_1)) + .thenReturn(TEST_CARRIER_ID); + when(mTelephonyUtil.getMatchingSubId(TEST_CARRIER_ID)).thenReturn(TEST_SUBID); + when(mTelephonyUtil.requiresImsiEncryption(TEST_SUBID)).thenReturn(true); + WifiConfiguration eapSimConfig = WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE); + WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion( + eapSimConfig, null, true, false, true, true, + false); + List<WifiNetworkSuggestion> networkSuggestionList = + new ArrayList<WifiNetworkSuggestion>() {{ + add(networkSuggestion); + }}; + assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, + mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1, + TEST_PACKAGE_1, TEST_FEATURE)); + Set<ExtendedWifiNetworkSuggestion> matchedSuggestion = mWifiNetworkSuggestionsManager + .getNetworkSuggestionsForScanDetail(createScanDetailForNetwork(eapSimConfig)); + // Should be no more notification and suggestion restore to the initial auto join configure. + verifyNoMoreInteractions(mNotificationManger); + for (ExtendedWifiNetworkSuggestion ewns : matchedSuggestion) { + assertTrue(ewns.isAutoJoinEnabled); + } + } + + /** * Verify adding invalid suggestions will return right error reason code. */ @Test diff --git a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java index 26db7cc30..794eb8423 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java @@ -95,6 +95,7 @@ public class TelephonyUtilTest extends WifiBaseTest { private static final String NO_MATCH_OPERATOR_NUMERIC = "654321"; private static final String TEST_PACKAGE = "com.test12345"; private static final String ANONYMOUS_IDENTITY = "anonymous@wlan.mnc456.mcc123.3gppnetwork.org"; + private static final String CARRIER_NAME = "Google"; @Mock CarrierConfigManager mCarrierConfigManager; @@ -153,6 +154,8 @@ public class TelephonyUtilTest extends WifiBaseTest { when(mDataTelephonyManager.getSubscriberId()).thenReturn(DATA_FULL_IMSI); when(mNonDataTelephonyManager.getSubscriberId()).thenReturn(NON_DATA_FULL_IMSI); when(mDataTelephonyManager.getSimOperator()).thenReturn(DATA_OPERATOR_NUMERIC); + when(mDataTelephonyManager.getSimCarrierIdName()).thenReturn(CARRIER_NAME); + when(mNonDataTelephonyManager.getSimCarrierIdName()).thenReturn(null); when(mNonDataTelephonyManager.getSimOperator()) .thenReturn(NON_DATA_OPERATOR_NUMERIC); when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); @@ -1376,4 +1379,13 @@ public class TelephonyUtilTest extends WifiBaseTest { assertEquals(DATA_CARRIER_ID, mTelephonyUtil.getCarrierIdForPackageWithCarrierPrivileges(TEST_PACKAGE)); } + + /** + * Verify getCarrierNameforSubId returns right value. + */ + @Test + public void getCarrierNameFromSubId() { + assertEquals(CARRIER_NAME, mTelephonyUtil.getCarrierNameforSubId(DATA_SUBID)); + assertNull(mTelephonyUtil.getCarrierNameforSubId(NON_DATA_SUBID)); + } } |