• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static android.Manifest.permission.NETWORK_SETTINGS;
20 import static android.telephony.TelephonyManager.DATA_ENABLED_REASON_USER;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.app.AlertDialog;
25 import android.app.Notification;
26 import android.app.PendingIntent;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.PackageInfo;
32 import android.content.pm.PackageManager;
33 import android.content.res.Resources;
34 import android.database.ContentObserver;
35 import android.graphics.drawable.Icon;
36 import android.net.Uri;
37 import android.net.wifi.WifiConfiguration;
38 import android.net.wifi.WifiEnterpriseConfig;
39 import android.net.wifi.WifiInfo;
40 import android.net.wifi.WifiManager;
41 import android.net.wifi.hotspot2.PasspointConfiguration;
42 import android.net.wifi.hotspot2.pps.Credential;
43 import android.os.Build;
44 import android.os.Handler;
45 import android.os.HandlerExecutor;
46 import android.os.PersistableBundle;
47 import android.os.UserHandle;
48 import android.telephony.CarrierConfigManager;
49 import android.telephony.ImsiEncryptionInfo;
50 import android.telephony.SubscriptionInfo;
51 import android.telephony.SubscriptionManager;
52 import android.telephony.TelephonyCallback;
53 import android.telephony.TelephonyManager;
54 import android.text.TextUtils;
55 import android.util.Base64;
56 import android.util.Log;
57 import android.util.Pair;
58 import android.util.SparseArray;
59 import android.util.SparseBooleanArray;
60 import android.view.WindowManager;
61 
62 import androidx.annotation.RequiresApi;
63 
64 import com.android.internal.annotations.VisibleForTesting;
65 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
66 import com.android.modules.utils.build.SdkLevel;
67 import com.android.wifi.resources.R;
68 
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 import java.lang.annotation.Retention;
72 import java.lang.annotation.RetentionPolicy;
73 import java.security.InvalidKeyException;
74 import java.security.NoSuchAlgorithmException;
75 import java.security.PublicKey;
76 import java.util.ArrayList;
77 import java.util.HashMap;
78 import java.util.List;
79 import java.util.Map;
80 
81 import javax.annotation.Nullable;
82 import javax.crypto.BadPaddingException;
83 import javax.crypto.Cipher;
84 import javax.crypto.IllegalBlockSizeException;
85 import javax.crypto.NoSuchPaddingException;
86 
87 /**
88  * This class provide APIs to get carrier info from telephony service.
89  * TODO(b/132188983): Refactor into TelephonyFacade which owns all instances of
90  *  TelephonyManager/SubscriptionManager in Wifi
91  */
92 public class WifiCarrierInfoManager {
93     public static final String TAG = "WifiCarrierInfoManager";
94     public static final String DEFAULT_EAP_PREFIX = "\0";
95 
96     public static final int CARRIER_INVALID_TYPE = -1;
97     public static final int CARRIER_MNO_TYPE = 0; // Mobile Network Operator
98     public static final int CARRIER_MVNO_TYPE = 1; // Mobile Virtual Network Operator
99     public static final String ANONYMOUS_IDENTITY = "anonymous";
100     public static final String THREE_GPP_NAI_REALM_FORMAT = "wlan.mnc%s.mcc%s.3gppnetwork.org";
101     /** Intent when user tapped action button to allow the app. */
102     @VisibleForTesting
103     public static final String NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION =
104             "com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER";
105     /** Intent when user tapped action button to disallow the app. */
106     @VisibleForTesting
107     public static final String NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION =
108             "com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER";
109     /** Intent when user dismissed the notification. */
110     @VisibleForTesting
111     public static final String NOTIFICATION_USER_DISMISSED_INTENT_ACTION =
112             "com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED";
113     /** Intent when user clicked on the notification. */
114     @VisibleForTesting
115     public static final String NOTIFICATION_USER_CLICKED_INTENT_ACTION =
116             "com.android.server.wifi.action.CarrierNetwork.USER_CLICKED";
117     @VisibleForTesting
118     public static final String EXTRA_CARRIER_NAME =
119             "com.android.server.wifi.extra.CarrierNetwork.CARRIER_NAME";
120     @VisibleForTesting
121     public static final String EXTRA_CARRIER_ID =
122             "com.android.server.wifi.extra.CarrierNetwork.CARRIER_ID";
123 
124     // IMSI encryption method: RSA-OAEP with SHA-256 hash function
125     private static final String IMSI_CIPHER_TRANSFORMATION =
126             "RSA/ECB/OAEPwithSHA-256andMGF1Padding";
127 
128     private static final HashMap<Integer, String> EAP_METHOD_PREFIX = new HashMap<>();
129     static {
EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.AKA, "0")130         EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.AKA, "0");
EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.SIM, "1")131         EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.SIM, "1");
EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.AKA_PRIME, "6")132         EAP_METHOD_PREFIX.put(WifiEnterpriseConfig.Eap.AKA_PRIME, "6");
133     }
134 
135     public static final int ACTION_USER_ALLOWED_CARRIER = 1;
136     public static final int ACTION_USER_DISALLOWED_CARRIER = 2;
137     public static final int ACTION_USER_DISMISS = 3;
138 
139     @IntDef(prefix = { "ACTION_USER_" }, value = {
140             ACTION_USER_ALLOWED_CARRIER,
141             ACTION_USER_DISALLOWED_CARRIER,
142             ACTION_USER_DISMISS
143     })
144     @Retention(RetentionPolicy.SOURCE)
145     public @interface UserActionCode { }
146 
147     /**
148      * 3GPP TS 11.11  2G_authentication command/response
149      *                Input: [RAND]
150      *                Output: [SRES][Cipher Key Kc]
151      */
152     private static final int START_SRES_POS = 0; // MUST be 0
153     private static final int SRES_LEN = 4;
154     private static final int START_KC_POS = START_SRES_POS + SRES_LEN;
155     private static final int KC_LEN = 8;
156 
157     private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier");
158     /**
159      * Expiration timeout for user notification in milliseconds. (15 min)
160      */
161     private static final long NOTIFICATION_EXPIRY_MILLS = 15 * 60 * 1000;
162     /**
163      * Notification update delay in milliseconds. (10 min)
164      */
165     private static final long NOTIFICATION_UPDATE_DELAY_MILLS = 10 * 60 * 1000;
166 
167     private final WifiContext mContext;
168     private final Handler mHandler;
169     private final WifiInjector mWifiInjector;
170     private final Resources mResources;
171     private final TelephonyManager mTelephonyManager;
172     private final SubscriptionManager mSubscriptionManager;
173     private final WifiNotificationManager mNotificationManager;
174     private final WifiMetrics mWifiMetrics;
175     private final Clock mClock;
176     /**
177      * Cached Map of <subscription ID, CarrierConfig PersistableBundle> since retrieving the
178      * PersistableBundle from CarrierConfigManager is somewhat expensive as it has hundreds of
179      * fields. This cache is cleared when the CarrierConfig changes to ensure data freshness.
180      */
181     private final SparseArray<PersistableBundle> mCachedCarrierConfigPerSubId = new SparseArray<>();
182 
183     /**
184      * Intent filter for processing notification actions.
185      */
186     private final IntentFilter mIntentFilter;
187     private final FrameworkFacade mFrameworkFacade;
188 
189     private boolean mVerboseLogEnabled = false;
190     private SparseBooleanArray mImsiEncryptionInfoAvailable = new SparseBooleanArray();
191     private final Map<Integer, Boolean> mImsiPrivacyProtectionExemptionMap = new HashMap<>();
192     private final Map<Integer, Boolean> mMergedCarrierNetworkOffloadMap = new HashMap<>();
193     private final Map<Integer, Boolean> mUnmergedCarrierNetworkOffloadMap = new HashMap<>();
194     private final List<OnUserApproveCarrierListener> mOnUserApproveCarrierListeners =
195             new ArrayList<>();
196     private final SparseBooleanArray mUserDataEnabled = new SparseBooleanArray();
197     private final List<UserDataEnabledChangedListener>  mUserDataEnabledListenerList =
198             new ArrayList<>();
199     private final List<OnCarrierOffloadDisabledListener> mOnCarrierOffloadDisabledListeners =
200             new ArrayList<>();
201     private final SparseArray<SimInfo> mSubIdToSimInfoSparseArray = new SparseArray<>();
202 
203     private List<SubscriptionInfo> mActiveSubInfos = null;
204 
205     private boolean mHasNewUserDataToSerialize = false;
206     private boolean mHasNewSharedDataToSerialize = false;
207     private boolean mUserDataLoaded = false;
208     private boolean mIsLastUserApprovalUiDialog = false;
209     private CarrierConfigManager mCarrierConfigManager;
210     /**
211      * The {@link Clock#getElapsedSinceBootMillis()} must be at least this value for us
212      * to update/show the notification.
213      */
214     private long mNotificationUpdateTime = 0L;
215 
216     private static class SimInfo {
217         public final String imsi;
218         public final String mccMnc;
219         public final int carrierIdFromSimMccMnc;
220         public final int simCarrierId;
221 
SimInfo(String imsi, String mccMnc, int carrierIdFromSimMccMnc, int simCarrierId)222         SimInfo(String imsi, String mccMnc, int carrierIdFromSimMccMnc, int simCarrierId) {
223             this.imsi = imsi;
224             this.mccMnc = mccMnc;
225             this.carrierIdFromSimMccMnc = carrierIdFromSimMccMnc;
226             this.simCarrierId = simCarrierId;
227         }
228 
229         /**
230          * Get the carrier type of current SIM.
231          */
getCarrierType()232         public int getCarrierType() {
233             if (carrierIdFromSimMccMnc == simCarrierId) {
234                 return CARRIER_MNO_TYPE;
235             }
236             return CARRIER_MVNO_TYPE;
237         }
238         @Override
toString()239         public String toString() {
240             StringBuilder sb = new StringBuilder("SimInfo[ ")
241                     .append("IMSI=").append(imsi)
242                     .append(", MCCMNC=").append(mccMnc)
243                     .append(", carrierIdFromSimMccMnc=").append(carrierIdFromSimMccMnc)
244                     .append(", simCarrierId=").append(simCarrierId)
245                     .append(" ]");
246             return sb.toString();
247         }
248     }
249 
250     /**
251      * Implement of {@link TelephonyCallback.DataEnabledListener}
252      */
253     @VisibleForTesting
254     @RequiresApi(Build.VERSION_CODES.S)
255     public final class UserDataEnabledChangedListener extends TelephonyCallback implements
256             TelephonyCallback.DataEnabledListener {
257         private final int mSubscriptionId;
258 
UserDataEnabledChangedListener(int subscriptionId)259         public UserDataEnabledChangedListener(int subscriptionId) {
260             mSubscriptionId = subscriptionId;
261         }
262 
263         @Override
onDataEnabledChanged(boolean enabled, int reason)264         public void onDataEnabledChanged(boolean enabled, int reason) {
265             if (reason == DATA_ENABLED_REASON_USER) {
266                 Log.d(TAG, "Mobile data change by user to "
267                         + (enabled ? "enabled" : "disabled") + " for subId: " + mSubscriptionId);
268                 mUserDataEnabled.put(mSubscriptionId, enabled);
269                 if (!enabled) {
270                     for (OnCarrierOffloadDisabledListener listener :
271                             mOnCarrierOffloadDisabledListeners) {
272                         listener.onCarrierOffloadDisabled(mSubscriptionId, true);
273                     }
274                 }
275             }
276         }
277 
278         /**
279          * Unregister the listener from TelephonyManager,
280          */
unregisterListener()281         public void unregisterListener() {
282             mTelephonyManager.createForSubscriptionId(mSubscriptionId)
283                     .unregisterTelephonyCallback(this);
284 
285         }
286     }
287 
288     /**
289      * Interface for other modules to listen to the user approve IMSI protection exemption.
290      */
291     public interface OnUserApproveCarrierListener {
292 
293         /**
294          * Invoke when user approve the IMSI protection exemption.
295          */
onUserAllowed(int carrierId)296         void onUserAllowed(int carrierId);
297     }
298 
299     /**
300      * Interface for other modules to listen to the carrier network offload disabled.
301      */
302     public interface OnCarrierOffloadDisabledListener {
303 
304         /**
305          * Invoke when carrier offload on target subscriptionId is disabled.
306          */
onCarrierOffloadDisabled(int subscriptionId, boolean merged)307         void onCarrierOffloadDisabled(int subscriptionId, boolean merged);
308     }
309 
310     /**
311      * Module to interact with the wifi config store.
312      */
313     private class WifiCarrierInfoStoreManagerDataSource implements
314             WifiCarrierInfoStoreManagerData.DataSource {
315 
316         @Override
toSerializeMergedCarrierNetworkOffloadMap()317         public Map<Integer, Boolean> toSerializeMergedCarrierNetworkOffloadMap() {
318             return mMergedCarrierNetworkOffloadMap;
319         }
320 
321         @Override
toSerializeUnmergedCarrierNetworkOffloadMap()322         public Map<Integer, Boolean> toSerializeUnmergedCarrierNetworkOffloadMap() {
323             return mUnmergedCarrierNetworkOffloadMap;
324         }
325 
326         @Override
serializeComplete()327         public void serializeComplete() {
328             mHasNewSharedDataToSerialize = false;
329         }
330 
331 
332         @Override
fromMergedCarrierNetworkOffloadMapDeserialized( Map<Integer, Boolean> carrierOffloadMap)333         public void fromMergedCarrierNetworkOffloadMapDeserialized(
334                 Map<Integer, Boolean> carrierOffloadMap) {
335             mMergedCarrierNetworkOffloadMap.clear();
336             mMergedCarrierNetworkOffloadMap.putAll(carrierOffloadMap);
337         }
338 
339         @Override
fromUnmergedCarrierNetworkOffloadMapDeserialized( Map<Integer, Boolean> subscriptionOffloadMap)340         public void fromUnmergedCarrierNetworkOffloadMapDeserialized(
341                 Map<Integer, Boolean> subscriptionOffloadMap) {
342             mUnmergedCarrierNetworkOffloadMap.clear();
343             mUnmergedCarrierNetworkOffloadMap.putAll(subscriptionOffloadMap);
344         }
345 
346         @Override
reset()347         public void reset() {
348             mMergedCarrierNetworkOffloadMap.clear();
349             mUnmergedCarrierNetworkOffloadMap.clear();
350         }
351 
352         @Override
hasNewDataToSerialize()353         public boolean hasNewDataToSerialize() {
354             return mHasNewSharedDataToSerialize;
355         }
356     }
357 
358     /**
359      * Module to interact with the wifi config store.
360      */
361     private class ImsiProtectionExemptionDataSource implements
362             ImsiPrivacyProtectionExemptionStoreData.DataSource {
363         @Override
toSerialize()364         public Map<Integer, Boolean> toSerialize() {
365             // Clear the flag after writing to disk.
366             mHasNewUserDataToSerialize = false;
367             return mImsiPrivacyProtectionExemptionMap;
368         }
369 
370         @Override
fromDeserialized(Map<Integer, Boolean> imsiProtectionExemptionMap)371         public void fromDeserialized(Map<Integer, Boolean> imsiProtectionExemptionMap) {
372             mImsiPrivacyProtectionExemptionMap.clear();
373             mImsiPrivacyProtectionExemptionMap.putAll(imsiProtectionExemptionMap);
374             mUserDataLoaded = true;
375         }
376 
377         @Override
reset()378         public void reset() {
379             mUserDataLoaded = false;
380             mImsiPrivacyProtectionExemptionMap.clear();
381         }
382 
383         @Override
hasNewDataToSerialize()384         public boolean hasNewDataToSerialize() {
385             return mHasNewUserDataToSerialize;
386         }
387     }
388 
389     private final BroadcastReceiver mBroadcastReceiver =
390             new BroadcastReceiver() {
391                 @Override
392                 public void onReceive(Context context, Intent intent) {
393                     String carrierName = intent.getStringExtra(EXTRA_CARRIER_NAME);
394                     int carrierId = intent.getIntExtra(EXTRA_CARRIER_ID, -1);
395                     if (carrierName == null || carrierId == -1) {
396                         Log.e(TAG, "No carrier name or carrier id found in intent");
397                         return;
398                     }
399 
400                     switch (intent.getAction()) {
401                         case NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION:
402                             handleUserAllowCarrierExemptionAction(carrierName, carrierId);
403                             break;
404                         case NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION:
405                             handleUserDisallowCarrierExemptionAction(carrierName, carrierId);
406                             break;
407                         case NOTIFICATION_USER_CLICKED_INTENT_ACTION:
408                             sendImsiPrivacyConfirmationDialog(carrierName, carrierId);
409                             // Collapse the notification bar
410                             mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
411                             break;
412                         case NOTIFICATION_USER_DISMISSED_INTENT_ACTION:
413                             handleUserDismissAction();
414                             return; // no need to cancel a dismissed notification, return.
415                         default:
416                             Log.e(TAG, "Unknown action " + intent.getAction());
417                             return;
418                     }
419                     // Clear notification once the user interacts with it.
420                     mNotificationManager.cancel(SystemMessage.NOTE_CARRIER_SUGGESTION_AVAILABLE);
421                 }
422             };
handleUserDismissAction()423     private void handleUserDismissAction() {
424         Log.i(TAG, "User dismissed the notification");
425         mNotificationUpdateTime = 0;
426         mWifiMetrics.addUserApprovalCarrierUiReaction(ACTION_USER_DISMISS,
427                 mIsLastUserApprovalUiDialog);
428     }
429 
handleUserAllowCarrierExemptionAction(String carrierName, int carrierId)430     private void handleUserAllowCarrierExemptionAction(String carrierName, int carrierId) {
431         Log.i(TAG, "User clicked to allow carrier:" + carrierName);
432         setHasUserApprovedImsiPrivacyExemptionForCarrier(true, carrierId);
433         mNotificationUpdateTime = 0;
434         mWifiMetrics.addUserApprovalCarrierUiReaction(ACTION_USER_ALLOWED_CARRIER,
435                 mIsLastUserApprovalUiDialog);
436 
437     }
438 
handleUserDisallowCarrierExemptionAction(String carrierName, int carrierId)439     private void handleUserDisallowCarrierExemptionAction(String carrierName, int carrierId) {
440         Log.i(TAG, "User clicked to disallow carrier:" + carrierName);
441         setHasUserApprovedImsiPrivacyExemptionForCarrier(false, carrierId);
442         mNotificationUpdateTime = 0;
443         mWifiMetrics.addUserApprovalCarrierUiReaction(
444                 ACTION_USER_DISALLOWED_CARRIER, mIsLastUserApprovalUiDialog);
445     }
446 
447     private class SubscriptionChangeListener extends
448             SubscriptionManager.OnSubscriptionsChangedListener {
449         @Override
onSubscriptionsChanged()450         public void onSubscriptionsChanged() {
451             mActiveSubInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
452             mSubIdToSimInfoSparseArray.clear();
453             if (mVerboseLogEnabled) {
454                 Log.v(TAG, "active subscription changes: " + mActiveSubInfos);
455             }
456         }
457     }
458 
459     /**
460      * Gets the instance of WifiCarrierInfoManager.
461      * @param telephonyManager Instance of {@link TelephonyManager}
462      * @param subscriptionManager Instance of {@link SubscriptionManager}
463      * @param wifiInjector Instance of {@link WifiInjector}
464      * @return The instance of WifiCarrierInfoManager
465      */
WifiCarrierInfoManager(@onNull TelephonyManager telephonyManager, @NonNull SubscriptionManager subscriptionManager, @NonNull WifiInjector wifiInjector, @NonNull FrameworkFacade frameworkFacade, @NonNull WifiContext context, @NonNull WifiConfigStore configStore, @NonNull Handler handler, @NonNull WifiMetrics wifiMetrics, @NonNull Clock clock)466     public WifiCarrierInfoManager(@NonNull TelephonyManager telephonyManager,
467             @NonNull SubscriptionManager subscriptionManager,
468             @NonNull WifiInjector wifiInjector,
469             @NonNull FrameworkFacade frameworkFacade,
470             @NonNull WifiContext context,
471             @NonNull WifiConfigStore configStore,
472             @NonNull Handler handler,
473             @NonNull WifiMetrics wifiMetrics,
474             @NonNull Clock clock) {
475         mTelephonyManager = telephonyManager;
476         mContext = context;
477         mResources = mContext.getResources();
478         mWifiInjector = wifiInjector;
479         mHandler = handler;
480         mSubscriptionManager = subscriptionManager;
481         mFrameworkFacade = frameworkFacade;
482         mWifiMetrics = wifiMetrics;
483         mNotificationManager = mWifiInjector.getWifiNotificationManager();
484         mClock = clock;
485         // Register broadcast receiver for UI interactions.
486         mIntentFilter = new IntentFilter();
487         mIntentFilter.addAction(NOTIFICATION_USER_DISMISSED_INTENT_ACTION);
488         mIntentFilter.addAction(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION);
489         mIntentFilter.addAction(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION);
490         mIntentFilter.addAction(NOTIFICATION_USER_CLICKED_INTENT_ACTION);
491 
492         mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, NETWORK_SETTINGS, handler);
493         configStore.registerStoreData(wifiInjector.makeWifiCarrierInfoStoreManagerData(
494                 new WifiCarrierInfoStoreManagerDataSource()));
495         configStore.registerStoreData(wifiInjector.makeImsiPrivacyProtectionExemptionStoreData(
496                 new ImsiProtectionExemptionDataSource()));
497 
498         mSubscriptionManager.addOnSubscriptionsChangedListener(new HandlerExecutor(mHandler),
499                 new SubscriptionChangeListener());
500         onCarrierConfigChanged(context);
501 
502         // Monitor for carrier config changes.
503         IntentFilter filter = new IntentFilter();
504         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
505         context.registerReceiver(new BroadcastReceiver() {
506             @Override
507             public void onReceive(Context context, Intent intent) {
508                 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
509                         .equals(intent.getAction())) {
510                     mHandler.post(() -> onCarrierConfigChanged(context));
511                 }
512             }
513         }, filter);
514 
515         frameworkFacade.registerContentObserver(context, CONTENT_URI, false,
516                 new ContentObserver(handler) {
517                     @Override
518                     public void onChange(boolean selfChange) {
519                         mHandler.post(() -> onCarrierConfigChanged(context));
520                     }
521                 });
522     }
523 
524     /**
525      * Enable/disable verbose logging.
526      */
enableVerboseLogging(int verbose)527     public void enableVerboseLogging(int verbose) {
528         mVerboseLogEnabled = verbose > 0;
529     }
530 
onUnlockedUserSwitching(int currentUserId)531     void onUnlockedUserSwitching(int currentUserId) {
532         // Retrieve list of broadcast receivers for this broadcast & send them directed
533         // broadcasts to wake them up (if they're in background).
534         final List<PackageInfo> provisioningPackageInfos =
535                 mContext.getPackageManager().getPackagesHoldingPermissions(
536                         new String[] {android.Manifest.permission.NETWORK_CARRIER_PROVISIONING},
537                         PackageManager.MATCH_UNINSTALLED_PACKAGES);
538 
539         vlogd("switched to current unlocked user. notify apps with"
540                 + " NETWORK_CARRIER_PROVISIONING permission for user - " + currentUserId);
541 
542         for (PackageInfo packageInfo : provisioningPackageInfos) {
543             Intent intentToSend = new Intent(WifiManager.ACTION_REFRESH_USER_PROVISIONING);
544             intentToSend.setPackage(packageInfo.packageName);
545             mContext.sendBroadcastAsUser(intentToSend, UserHandle.CURRENT,
546                     android.Manifest.permission.NETWORK_CARRIER_PROVISIONING);
547         }
548     }
549 
getCarrierConfigForSubId(int subId)550     private PersistableBundle getCarrierConfigForSubId(int subId) {
551         if (mCachedCarrierConfigPerSubId.contains(subId)) {
552             return mCachedCarrierConfigPerSubId.get(subId);
553         }
554         TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
555         if (specifiedTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
556             return null;
557         }
558         if (mCarrierConfigManager == null) {
559             mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
560         }
561         if (mCarrierConfigManager == null) {
562             return null;
563         }
564         PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
565         if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
566             return null;
567         }
568         mCachedCarrierConfigPerSubId.put(subId, carrierConfig);
569         return carrierConfig;
570     }
571 
572     /**
573      * Checks whether MAC randomization should be disabled for the provided WifiConfiguration,
574      * based on an exception list in the CarrierConfigManager per subId.
575      * @param ssid the SSID of a WifiConfiguration, surrounded by double quotes.
576      * @param carrierId the ID associated with the network operator for this network suggestion.
577      * @param subId the best match subscriptionId for this network suggestion.
578      */
shouldDisableMacRandomization(String ssid, int carrierId, int subId)579     public boolean shouldDisableMacRandomization(String ssid, int carrierId, int subId) {
580         if (!SdkLevel.isAtLeastS()) {
581             return false;
582         }
583         if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
584             // only carrier networks are allowed to disable MAC randomization through this path.
585             return false;
586         }
587         PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
588         if (carrierConfig == null) {
589             return false;
590         }
591         String sanitizedSsid = WifiInfo.sanitizeSsid(ssid);
592         String[] macRandDisabledSsids = carrierConfig.getStringArray(CarrierConfigManager.Wifi
593                 .KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED);
594         if (macRandDisabledSsids == null) {
595             return false;
596         }
597         for (String curSsid : macRandDisabledSsids) {
598             if (sanitizedSsid.equals(curSsid)) {
599                 return true;
600             }
601         }
602         return false;
603     }
604 
605     /**
606      * Checks whether merged carrier WiFi networks are permitted for the carrier based on a flag
607      * in the CarrierConfigManager.
608      *
609      * @param subId the best match subscriptionId for this network suggestion.
610      */
areMergedCarrierWifiNetworksAllowed(int subId)611     public boolean areMergedCarrierWifiNetworksAllowed(int subId) {
612         if (!SdkLevel.isAtLeastS()) {
613             return false;
614         }
615         PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
616         if (carrierConfig == null) {
617             return false;
618         }
619 
620         return carrierConfig.getBoolean(
621                 CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false);
622     }
623 
624     /**
625      * Updates the IMSI encryption information and clears cached CarrierConfig data.
626      */
onCarrierConfigChanged(Context context)627     private void onCarrierConfigChanged(Context context) {
628         SparseArray<PersistableBundle> cachedCarrierConfigPerSubIdOld =
629                 mCachedCarrierConfigPerSubId.clone();
630         mCachedCarrierConfigPerSubId.clear();
631         mImsiEncryptionInfoAvailable.clear();
632         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
633             return;
634         }
635         for (SubscriptionInfo subInfo : mActiveSubInfos) {
636             int subId = subInfo.getSubscriptionId();
637             PersistableBundle bundle = getCarrierConfigForSubId(subId);
638             if (bundle == null) {
639                 Log.e(TAG, "Carrier config is missing for: " + subId);
640             } else {
641                 try {
642                     if (requiresImsiEncryption(subId)
643                             && mTelephonyManager.createForSubscriptionId(subId)
644                             .getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN)
645                             != null) {
646                         vlogd("IMSI encryption info is available for " + subId);
647                         mImsiEncryptionInfoAvailable.put(subId, true);
648                     }
649                 } catch (IllegalArgumentException e) {
650                     vlogd("IMSI encryption info is not available.");
651                 }
652             }
653             PersistableBundle bundleOld = cachedCarrierConfigPerSubIdOld.get(subId);
654             if (bundleOld != null && bundleOld.getBoolean(CarrierConfigManager
655                     .KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL)
656                     && !areMergedCarrierWifiNetworksAllowed(subId)) {
657                 vlogd("Allow carrier merged change from true to false");
658                 for (OnCarrierOffloadDisabledListener listener :
659                         mOnCarrierOffloadDisabledListeners) {
660                     listener.onCarrierOffloadDisabled(subId, true);
661                 }
662             }
663 
664         }
665     }
666 
667     /**
668      * Check if the IMSI encryption is required for the SIM card.
669      * Note: should only be called when {@link #isSimReady(int)} is true, or the result may not be
670      * correct.
671      *
672      * @param subId The subscription ID of SIM card.
673      * @return true if the IMSI encryption is required, otherwise false.
674      */
requiresImsiEncryption(int subId)675     public boolean requiresImsiEncryption(int subId) {
676         PersistableBundle bundle = getCarrierConfigForSubId(subId);
677         if (bundle == null) {
678             Log.wtf(TAG, "requiresImsiEncryption is called when SIM is not ready!");
679             return false;
680         }
681         return (bundle.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT)
682                 & TelephonyManager.KEY_TYPE_WLAN) != 0;
683     }
684 
685     /**
686      * Check if the IMSI encryption is downloaded(available) for the SIM card.
687      *
688      * @param subId The subscription ID of SIM card.
689      * @return true if the IMSI encryption is available, otherwise false.
690      */
isImsiEncryptionInfoAvailable(int subId)691     public boolean isImsiEncryptionInfoAvailable(int subId) {
692         return mImsiEncryptionInfoAvailable.get(subId);
693     }
694 
695     /**
696      * Gets the SubscriptionId of SIM card which is from the carrier specified in config.
697      *
698      * @param config the instance of {@link WifiConfiguration}
699      * @return the best match SubscriptionId
700      */
getBestMatchSubscriptionId(@onNull WifiConfiguration config)701     public int getBestMatchSubscriptionId(@NonNull WifiConfiguration config) {
702         if (config.subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
703             return config.subscriptionId;
704         }
705         if (config.isPasspoint()) {
706             return getMatchingSubId(config.carrierId);
707         } else {
708             return getBestMatchSubscriptionIdForEnterprise(config);
709         }
710     }
711 
712     /**
713      * Gets the SubscriptionId of SIM card for given carrier Id
714      *
715      * @param carrierId carrier id for target carrier
716      * @return the matched SubscriptionId
717      */
getMatchingSubId(int carrierId)718     public int getMatchingSubId(int carrierId) {
719         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
720             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
721         }
722 
723         int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
724         int matchSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
725         for (SubscriptionInfo subInfo : mActiveSubInfos) {
726             if (subInfo.getCarrierId() == carrierId) {
727                 matchSubId = subInfo.getSubscriptionId();
728                 if (matchSubId == dataSubId) {
729                     // Priority of Data sub is higher than non data sub.
730                     break;
731                 }
732             }
733         }
734         vlogd("matching subId is " + matchSubId);
735         return matchSubId;
736     }
737 
getBestMatchSubscriptionIdForEnterprise(WifiConfiguration config)738     private int getBestMatchSubscriptionIdForEnterprise(WifiConfiguration config) {
739         if (config.carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
740             return getMatchingSubId(config.carrierId);
741         }
742         // Legacy WifiConfiguration without carrier ID
743         if (config.enterpriseConfig == null
744                  || !config.enterpriseConfig.isAuthenticationSimBased()) {
745             Log.w(TAG, "The legacy config is not using EAP-SIM.");
746             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
747         }
748         int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
749         if (isSimReady(dataSubId)) {
750             vlogd("carrierId is not assigned, using the default data sub.");
751             return dataSubId;
752         }
753         vlogd("data sim is not present.");
754         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
755     }
756 
757     /**
758      * Check if the specified SIM card is ready for Wi-Fi connection on the device.
759      *
760      * @param subId subscription ID of SIM card in the device.
761      * @return true if the SIM is active and all info are available, otherwise false.
762      */
isSimReady(int subId)763     public boolean isSimReady(int subId) {
764         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
765             return false;
766         }
767         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
768             return false;
769         }
770         if (getSimInfo(subId) == null || getCarrierConfigForSubId(subId) == null) {
771             return false;
772         }
773         return mActiveSubInfos.stream().anyMatch(info -> info.getSubscriptionId() == subId);
774     }
775 
776     /**
777      * Get the identity for the current SIM or null if the SIM is not available
778      *
779      * @param config WifiConfiguration that indicates what sort of authentication is necessary
780      * @return Pair<identify, encrypted identity> or null if the SIM is not available
781      * or config is invalid
782      */
getSimIdentity(WifiConfiguration config)783     public Pair<String, String> getSimIdentity(WifiConfiguration config) {
784         int subId = getBestMatchSubscriptionId(config);
785         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
786             return null;
787         }
788 
789         SimInfo simInfo = getSimInfo(subId);
790         if (simInfo == null) {
791             return null;
792         }
793 
794         String identity = buildIdentity(getSimMethodForConfig(config), simInfo.imsi,
795                 simInfo.mccMnc, false);
796         if (identity == null) {
797             Log.e(TAG, "Failed to build the identity");
798             return null;
799         }
800 
801         if (!requiresImsiEncryption(subId)) {
802             return Pair.create(identity, "");
803         }
804         TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
805         ImsiEncryptionInfo imsiEncryptionInfo;
806         try {
807             imsiEncryptionInfo = specifiedTm.getCarrierInfoForImsiEncryption(
808                     TelephonyManager.KEY_TYPE_WLAN);
809         } catch (RuntimeException e) {
810             Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage());
811             return null;
812         }
813         if (imsiEncryptionInfo == null) {
814             // Does not support encrypted identity.
815             return Pair.create(identity, "");
816         }
817 
818         String encryptedIdentity = buildEncryptedIdentity(identity,
819                     imsiEncryptionInfo);
820 
821         // In case of failure for encryption, abort current EAP authentication.
822         if (encryptedIdentity == null) {
823             Log.e(TAG, "failed to encrypt the identity");
824             return null;
825         }
826         return Pair.create(identity, encryptedIdentity);
827     }
828 
829     /**
830      * Gets Anonymous identity for current active SIM.
831      *
832      * @param config the instance of WifiConfiguration.
833      * @return anonymous identity@realm which is based on current MCC/MNC, {@code null} if SIM is
834      * not ready or absent.
835      */
getAnonymousIdentityWith3GppRealm(@onNull WifiConfiguration config)836     public String getAnonymousIdentityWith3GppRealm(@NonNull WifiConfiguration config) {
837         int subId = getBestMatchSubscriptionId(config);
838         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
839             return null;
840         }
841         SimInfo simInfo = getSimInfo(subId);
842         if (simInfo == null) {
843             return null;
844         }
845         Pair<String, String> mccMncPair = extractMccMnc(simInfo.imsi, simInfo.mccMnc);
846         if (mccMncPair == null) {
847             return null;
848         }
849 
850         String realm = String.format(THREE_GPP_NAI_REALM_FORMAT, mccMncPair.second,
851                 mccMncPair.first);
852         StringBuilder sb = new StringBuilder();
853         if (isEapMethodPrefixEnabled(subId)) {
854             // Set the EAP method as a prefix
855             String eapMethod = EAP_METHOD_PREFIX.get(config.enterpriseConfig.getEapMethod());
856             if (!TextUtils.isEmpty(eapMethod)) {
857                 sb.append(eapMethod);
858             }
859         }
860         return sb.append(ANONYMOUS_IDENTITY).append("@").append(realm).toString();
861     }
862 
863     /**
864      * Encrypt the given data with the given public key.  The encrypted data will be returned as
865      * a Base64 encoded string.
866      *
867      * @param key The public key to use for encryption
868      * @param data The data need to be encrypted
869      * @param encodingFlag base64 encoding flag
870      * @return Base64 encoded string, or null if encryption failed
871      */
872     @VisibleForTesting
encryptDataUsingPublicKey(PublicKey key, byte[] data, int encodingFlag)873     public static String encryptDataUsingPublicKey(PublicKey key, byte[] data, int encodingFlag) {
874         try {
875             Cipher cipher = Cipher.getInstance(IMSI_CIPHER_TRANSFORMATION);
876             cipher.init(Cipher.ENCRYPT_MODE, key);
877             byte[] encryptedBytes = cipher.doFinal(data);
878 
879             return Base64.encodeToString(encryptedBytes, 0, encryptedBytes.length, encodingFlag);
880         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
881                 | IllegalBlockSizeException | BadPaddingException e) {
882             Log.e(TAG, "Encryption failed: " + e.getMessage());
883             return null;
884         }
885     }
886 
887     /**
888      * Create the encrypted identity.
889      *
890      * Prefix value:
891      * "0" - EAP-AKA Identity
892      * "1" - EAP-SIM Identity
893      * "6" - EAP-AKA' Identity
894      * Encrypted identity format: prefix|IMSI@<NAIRealm>
895      * @param identity           permanent identity with format based on section 4.1.1.6 of RFC 4187
896      *                           and 4.2.1.6 of RFC 4186.
897      * @param imsiEncryptionInfo The IMSI encryption info retrieved from the SIM
898      * @return "\0" + encryptedIdentity + "{, Key Identifier AVP}"
899      */
buildEncryptedIdentity(String identity, ImsiEncryptionInfo imsiEncryptionInfo)900     private static String buildEncryptedIdentity(String identity,
901             ImsiEncryptionInfo imsiEncryptionInfo) {
902         if (imsiEncryptionInfo == null) {
903             Log.e(TAG, "imsiEncryptionInfo is not valid");
904             return null;
905         }
906         if (identity == null) {
907             Log.e(TAG, "identity is not valid");
908             return null;
909         }
910 
911         // Build and return the encrypted identity.
912         String encryptedIdentity = encryptDataUsingPublicKey(
913                 imsiEncryptionInfo.getPublicKey(), identity.getBytes(), Base64.NO_WRAP);
914         if (encryptedIdentity == null) {
915             Log.e(TAG, "Failed to encrypt IMSI");
916             return null;
917         }
918         encryptedIdentity = DEFAULT_EAP_PREFIX + encryptedIdentity;
919         if (imsiEncryptionInfo.getKeyIdentifier() != null) {
920             // Include key identifier AVP (Attribute Value Pair).
921             encryptedIdentity = encryptedIdentity + "," + imsiEncryptionInfo.getKeyIdentifier();
922         }
923         return encryptedIdentity;
924     }
925 
926     /**
927      * Create an identity used for SIM-based EAP authentication. The identity will be based on
928      * the info retrieved from the SIM card, such as IMSI and IMSI encryption info. The IMSI
929      * contained in the identity will be encrypted if IMSI encryption info is provided.
930      *
931      * See  rfc4186 & rfc4187 & rfc5448:
932      *
933      * Identity format:
934      * Prefix | [IMSI || Encrypted IMSI] | @realm | {, Key Identifier AVP}
935      * where "|" denotes concatenation, "||" denotes exclusive value, "{}"
936      * denotes optional value, and realm is the 3GPP network domain name derived from the given
937      * MCC/MNC according to the 3GGP spec(TS23.003).
938      *
939      * Prefix value:
940      * "\0" - Encrypted Identity
941      * "0" - EAP-AKA Identity
942      * "1" - EAP-SIM Identity
943      * "6" - EAP-AKA' Identity
944      *
945      * Encrypted IMSI:
946      * Base64{RSA_Public_Key_Encryption{eapPrefix | IMSI}}
947      * where "|" denotes concatenation,
948      *
949      * @param eapMethod EAP authentication method: EAP-SIM, EAP-AKA, EAP-AKA'
950      * @param imsi The IMSI retrieved from the SIM
951      * @param mccMnc The MCC MNC identifier retrieved from the SIM
952      * @param isEncrypted Whether the imsi is encrypted or not.
953      * @return the eap identity, built using either the encrypted or un-encrypted IMSI.
954      */
buildIdentity(int eapMethod, String imsi, String mccMnc, boolean isEncrypted)955     private String buildIdentity(int eapMethod, String imsi, String mccMnc,
956                                         boolean isEncrypted) {
957         if (imsi == null || imsi.isEmpty()) {
958             Log.e(TAG, "No IMSI or IMSI is null");
959             return null;
960         }
961 
962         String prefix = isEncrypted ? DEFAULT_EAP_PREFIX : EAP_METHOD_PREFIX.get(eapMethod);
963         if (prefix == null) {
964             return null;
965         }
966 
967         Pair<String, String> mccMncPair = extractMccMnc(imsi, mccMnc);
968 
969         String naiRealm = String.format(THREE_GPP_NAI_REALM_FORMAT, mccMncPair.second,
970                 mccMncPair.first);
971         return prefix + imsi + "@" + naiRealm;
972     }
973 
974     /**
975      * Return the associated SIM method for the configuration.
976      *
977      * @param config WifiConfiguration corresponding to the network.
978      * @return the outer EAP method associated with this SIM configuration.
979      */
getSimMethodForConfig(WifiConfiguration config)980     private static int getSimMethodForConfig(WifiConfiguration config) {
981         if (config == null || config.enterpriseConfig == null
982                 || !config.enterpriseConfig.isAuthenticationSimBased()) {
983             return WifiEnterpriseConfig.Eap.NONE;
984         }
985         int eapMethod = config.enterpriseConfig.getEapMethod();
986         if (eapMethod == WifiEnterpriseConfig.Eap.PEAP) {
987             // Translate known inner eap methods into an equivalent outer eap method.
988             switch (config.enterpriseConfig.getPhase2Method()) {
989                 case WifiEnterpriseConfig.Phase2.SIM:
990                     eapMethod = WifiEnterpriseConfig.Eap.SIM;
991                     break;
992                 case WifiEnterpriseConfig.Phase2.AKA:
993                     eapMethod = WifiEnterpriseConfig.Eap.AKA;
994                     break;
995                 case WifiEnterpriseConfig.Phase2.AKA_PRIME:
996                     eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME;
997                     break;
998             }
999         }
1000 
1001         return eapMethod;
1002     }
1003 
1004     /**
1005      * Returns true if {@code identity} contains an anonymous@realm identity, false otherwise.
1006      */
isAnonymousAtRealmIdentity(String identity)1007     public static boolean isAnonymousAtRealmIdentity(String identity) {
1008         if (TextUtils.isEmpty(identity)) return false;
1009         final String anonymousId = WifiCarrierInfoManager.ANONYMOUS_IDENTITY + "@";
1010         return identity.startsWith(anonymousId)
1011                 || identity.substring(1).startsWith(anonymousId);
1012     }
1013 
1014     // TODO replace some of this code with Byte.parseByte
parseHex(char ch)1015     private static int parseHex(char ch) {
1016         if ('0' <= ch && ch <= '9') {
1017             return ch - '0';
1018         } else if ('a' <= ch && ch <= 'f') {
1019             return ch - 'a' + 10;
1020         } else if ('A' <= ch && ch <= 'F') {
1021             return ch - 'A' + 10;
1022         } else {
1023             throw new NumberFormatException("" + ch + " is not a valid hex digit");
1024         }
1025     }
1026 
parseHex(String hex)1027     private static byte[] parseHex(String hex) {
1028         /* This only works for good input; don't throw bad data at it */
1029         if (hex == null) {
1030             return new byte[0];
1031         }
1032 
1033         if (hex.length() % 2 != 0) {
1034             throw new NumberFormatException(hex + " is not a valid hex string");
1035         }
1036 
1037         byte[] result = new byte[(hex.length()) / 2 + 1];
1038         result[0] = (byte) ((hex.length()) / 2);
1039         for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
1040             int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i + 1));
1041             byte b = (byte) (val & 0xFF);
1042             result[j] = b;
1043         }
1044 
1045         return result;
1046     }
1047 
parseHexWithoutLength(String hex)1048     private static byte[] parseHexWithoutLength(String hex) {
1049         byte[] tmpRes = parseHex(hex);
1050         if (tmpRes.length == 0) {
1051             return tmpRes;
1052         }
1053 
1054         byte[] result = new byte[tmpRes.length - 1];
1055         System.arraycopy(tmpRes, 1, result, 0, tmpRes.length - 1);
1056 
1057         return result;
1058     }
1059 
makeHex(byte[] bytes)1060     private static String makeHex(byte[] bytes) {
1061         StringBuilder sb = new StringBuilder();
1062         for (byte b : bytes) {
1063             sb.append(String.format("%02x", b));
1064         }
1065         return sb.toString();
1066     }
1067 
makeHex(byte[] bytes, int from, int len)1068     private static String makeHex(byte[] bytes, int from, int len) {
1069         StringBuilder sb = new StringBuilder();
1070         for (int i = 0; i < len; i++) {
1071             sb.append(String.format("%02x", bytes[from + i]));
1072         }
1073         return sb.toString();
1074     }
1075 
concatHex(byte[] array1, byte[] array2)1076     private static byte[] concatHex(byte[] array1, byte[] array2) {
1077 
1078         int len = array1.length + array2.length;
1079 
1080         byte[] result = new byte[len];
1081 
1082         int index = 0;
1083         if (array1.length != 0) {
1084             for (byte b : array1) {
1085                 result[index] = b;
1086                 index++;
1087             }
1088         }
1089 
1090         if (array2.length != 0) {
1091             for (byte b : array2) {
1092                 result[index] = b;
1093                 index++;
1094             }
1095         }
1096 
1097         return result;
1098     }
1099 
1100     /**
1101      * Calculate SRES and KC as 3G authentication.
1102      *
1103      * Standard       Cellular_auth     Type Command
1104      *
1105      * 3GPP TS 31.102 3G_authentication [Length][RAND][Length][AUTN]
1106      *                         [Length][RES][Length][CK][Length][IK] and more
1107      *
1108      * @param requestData RAND data from server.
1109      * @param config The instance of WifiConfiguration.
1110      * @return the response data processed by SIM. If all request data is malformed, then returns
1111      * empty string. If request data is invalid, then returns null.
1112      */
getGsmSimAuthResponse(String[] requestData, WifiConfiguration config)1113     public String getGsmSimAuthResponse(String[] requestData, WifiConfiguration config) {
1114         return getGsmAuthResponseWithLength(requestData, config, TelephonyManager.APPTYPE_USIM);
1115     }
1116 
1117     /**
1118      * Calculate SRES and KC as 2G authentication.
1119      *
1120      * Standard       Cellular_auth     Type Command
1121      *
1122      * 3GPP TS 31.102 2G_authentication [Length][RAND]
1123      *                         [Length][SRES][Length][Cipher Key Kc]
1124      *
1125      * @param requestData RAND data from server.
1126      * @param config The instance of WifiConfiguration.
1127      * @return the response data processed by SIM. If all request data is malformed, then returns
1128      * empty string. If request data is invalid, then returns null.
1129      */
getGsmSimpleSimAuthResponse(String[] requestData, WifiConfiguration config)1130     public String getGsmSimpleSimAuthResponse(String[] requestData,
1131             WifiConfiguration config) {
1132         return getGsmAuthResponseWithLength(requestData, config, TelephonyManager.APPTYPE_SIM);
1133     }
1134 
getGsmAuthResponseWithLength(String[] requestData, WifiConfiguration config, int appType)1135     private String getGsmAuthResponseWithLength(String[] requestData,
1136             WifiConfiguration config, int appType) {
1137         int subId = getBestMatchSubscriptionId(config);
1138         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
1139             return null;
1140         }
1141 
1142         StringBuilder sb = new StringBuilder();
1143         for (String challenge : requestData) {
1144             if (challenge == null || challenge.isEmpty()) {
1145                 continue;
1146             }
1147             Log.d(TAG, "RAND = " + challenge);
1148 
1149             byte[] rand = null;
1150             try {
1151                 rand = parseHex(challenge);
1152             } catch (NumberFormatException e) {
1153                 Log.e(TAG, "malformed challenge");
1154                 continue;
1155             }
1156 
1157             String base64Challenge = Base64.encodeToString(rand, Base64.NO_WRAP);
1158             TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
1159             String tmResponse = specifiedTm.getIccAuthentication(
1160                     appType, TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
1161             Log.v(TAG, "Raw Response - " + tmResponse);
1162 
1163             if (tmResponse == null || tmResponse.length() <= 4) {
1164                 Log.e(TAG, "bad response - " + tmResponse);
1165                 return null;
1166             }
1167 
1168             byte[] result = Base64.decode(tmResponse, Base64.DEFAULT);
1169             Log.v(TAG, "Hex Response -" + makeHex(result));
1170             int sresLen = result[0];
1171             if (sresLen < 0 || sresLen >= result.length) {
1172                 Log.e(TAG, "malformed response - " + tmResponse);
1173                 return null;
1174             }
1175             String sres = makeHex(result, 1, sresLen);
1176             int kcOffset = 1 + sresLen;
1177             if (kcOffset >= result.length) {
1178                 Log.e(TAG, "malformed response - " + tmResponse);
1179                 return null;
1180             }
1181             int kcLen = result[kcOffset];
1182             if (kcLen < 0 || kcOffset + kcLen > result.length) {
1183                 Log.e(TAG, "malformed response - " + tmResponse);
1184                 return null;
1185             }
1186             String kc = makeHex(result, 1 + kcOffset, kcLen);
1187             sb.append(":" + kc + ":" + sres);
1188             Log.v(TAG, "kc:" + kc + " sres:" + sres);
1189         }
1190 
1191         return sb.toString();
1192     }
1193 
1194     /**
1195      * Calculate SRES and KC as 2G authentication.
1196      *
1197      * Standard       Cellular_auth     Type Command
1198      *
1199      * 3GPP TS 11.11  2G_authentication [RAND]
1200      *                         [SRES][Cipher Key Kc]
1201      *
1202      * @param requestData RAND data from server.
1203      * @param config the instance of WifiConfiguration.
1204      * @return the response data processed by SIM. If all request data is malformed, then returns
1205      * empty string. If request data is invalid, then returns null.
1206      */
getGsmSimpleSimNoLengthAuthResponse(String[] requestData, @NonNull WifiConfiguration config)1207     public String getGsmSimpleSimNoLengthAuthResponse(String[] requestData,
1208             @NonNull WifiConfiguration config) {
1209 
1210         int subId = getBestMatchSubscriptionId(config);
1211         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
1212             return null;
1213         }
1214 
1215         StringBuilder sb = new StringBuilder();
1216         for (String challenge : requestData) {
1217             if (challenge == null || challenge.isEmpty()) {
1218                 continue;
1219             }
1220             Log.d(TAG, "RAND = " + challenge);
1221 
1222             byte[] rand = null;
1223             try {
1224                 rand = parseHexWithoutLength(challenge);
1225             } catch (NumberFormatException e) {
1226                 Log.e(TAG, "malformed challenge");
1227                 continue;
1228             }
1229 
1230             String base64Challenge = Base64.encodeToString(rand, Base64.NO_WRAP);
1231             TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
1232             String tmResponse = specifiedTm.getIccAuthentication(TelephonyManager.APPTYPE_SIM,
1233                     TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
1234             Log.v(TAG, "Raw Response - " + tmResponse);
1235 
1236             if (tmResponse == null || tmResponse.length() <= 4) {
1237                 Log.e(TAG, "bad response - " + tmResponse);
1238                 return null;
1239             }
1240 
1241             byte[] result = Base64.decode(tmResponse, Base64.DEFAULT);
1242             if (SRES_LEN + KC_LEN != result.length) {
1243                 Log.e(TAG, "malformed response - " + tmResponse);
1244                 return null;
1245             }
1246             Log.v(TAG, "Hex Response -" + makeHex(result));
1247             String sres = makeHex(result, START_SRES_POS, SRES_LEN);
1248             String kc = makeHex(result, START_KC_POS, KC_LEN);
1249             sb.append(":" + kc + ":" + sres);
1250             Log.v(TAG, "kc:" + kc + " sres:" + sres);
1251         }
1252 
1253         return sb.toString();
1254     }
1255 
1256     /**
1257      * Data supplied when making a SIM Auth Request
1258      */
1259     public static class SimAuthRequestData {
SimAuthRequestData()1260         public SimAuthRequestData() {}
SimAuthRequestData(int networkId, int protocol, String ssid, String[] data)1261         public SimAuthRequestData(int networkId, int protocol, String ssid, String[] data) {
1262             this.networkId = networkId;
1263             this.protocol = protocol;
1264             this.ssid = ssid;
1265             this.data = data;
1266         }
1267 
1268         public int networkId;
1269         public int protocol;
1270         public String ssid;
1271         // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
1272         // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
1273         public String[] data;
1274     }
1275 
1276     /**
1277      * The response to a SIM Auth request if successful
1278      */
1279     public static class SimAuthResponseData {
SimAuthResponseData(String type, String response)1280         public SimAuthResponseData(String type, String response) {
1281             this.type = type;
1282             this.response = response;
1283         }
1284 
1285         public String type;
1286         public String response;
1287     }
1288 
1289     /**
1290      * Get the response data for 3G authentication.
1291      *
1292      * @param requestData authentication request data from server.
1293      * @param config the instance of WifiConfiguration.
1294      * @return the response data processed by SIM. If request data is invalid, then returns null.
1295      */
get3GAuthResponse(SimAuthRequestData requestData, WifiConfiguration config)1296     public SimAuthResponseData get3GAuthResponse(SimAuthRequestData requestData,
1297             WifiConfiguration config) {
1298         StringBuilder sb = new StringBuilder();
1299         byte[] rand = null;
1300         byte[] authn = null;
1301         String resType = WifiNative.SIM_AUTH_RESP_TYPE_UMTS_AUTH;
1302 
1303         if (requestData.data.length == 2) {
1304             try {
1305                 rand = parseHex(requestData.data[0]);
1306                 authn = parseHex(requestData.data[1]);
1307             } catch (NumberFormatException e) {
1308                 Log.e(TAG, "malformed challenge");
1309             }
1310         } else {
1311             Log.e(TAG, "malformed challenge");
1312         }
1313 
1314         String tmResponse = "";
1315         if (rand != null && authn != null) {
1316             String base64Challenge = Base64.encodeToString(concatHex(rand, authn), Base64.NO_WRAP);
1317             int subId = getBestMatchSubscriptionId(config);
1318             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
1319                 return null;
1320             }
1321             tmResponse = mTelephonyManager
1322                     .createForSubscriptionId(subId)
1323                     .getIccAuthentication(TelephonyManager.APPTYPE_USIM,
1324                             TelephonyManager.AUTHTYPE_EAP_AKA, base64Challenge);
1325             Log.v(TAG, "Raw Response - " + tmResponse);
1326         }
1327 
1328         boolean goodReponse = false;
1329         if (tmResponse != null && tmResponse.length() > 4) {
1330             byte[] result = Base64.decode(tmResponse, Base64.DEFAULT);
1331             Log.e(TAG, "Hex Response - " + makeHex(result));
1332             byte tag = result[0];
1333             if (tag == (byte) 0xdb) {
1334                 Log.v(TAG, "successful 3G authentication ");
1335                 int resLen = result[1];
1336                 String res = makeHex(result, 2, resLen);
1337                 int ckLen = result[resLen + 2];
1338                 String ck = makeHex(result, resLen + 3, ckLen);
1339                 int ikLen = result[resLen + ckLen + 3];
1340                 String ik = makeHex(result, resLen + ckLen + 4, ikLen);
1341                 sb.append(":" + ik + ":" + ck + ":" + res);
1342                 Log.v(TAG, "ik:" + ik + "ck:" + ck + " res:" + res);
1343                 goodReponse = true;
1344             } else if (tag == (byte) 0xdc) {
1345                 Log.e(TAG, "synchronisation failure");
1346                 int autsLen = result[1];
1347                 String auts = makeHex(result, 2, autsLen);
1348                 resType = WifiNative.SIM_AUTH_RESP_TYPE_UMTS_AUTS;
1349                 sb.append(":" + auts);
1350                 Log.v(TAG, "auts:" + auts);
1351                 goodReponse = true;
1352             } else {
1353                 Log.e(TAG, "bad response - unknown tag = " + tag);
1354             }
1355         } else {
1356             Log.e(TAG, "bad response - " + tmResponse);
1357         }
1358 
1359         if (goodReponse) {
1360             String response = sb.toString();
1361             Log.v(TAG, "Supplicant Response -" + response);
1362             return new SimAuthResponseData(resType, response);
1363         } else {
1364             return null;
1365         }
1366     }
1367 
1368     /**
1369      * Decorates a pseudonym with the NAI realm, in case it wasn't provided by the server
1370      *
1371      * @param config The instance of WifiConfiguration
1372      * @param pseudonym The pseudonym (temporary identity) provided by the server
1373      * @return pseudonym@realm which is based on current MCC/MNC, {@code null} if SIM is
1374      * not ready or absent.
1375      */
decoratePseudonymWith3GppRealm(@onNull WifiConfiguration config, String pseudonym)1376     public String decoratePseudonymWith3GppRealm(@NonNull WifiConfiguration config,
1377             String pseudonym) {
1378         if (TextUtils.isEmpty(pseudonym)) {
1379             return null;
1380         }
1381         if (pseudonym.contains("@")) {
1382             // Pseudonym is already decorated
1383             return pseudonym;
1384         }
1385         int subId = getBestMatchSubscriptionId(config);
1386 
1387         SimInfo simInfo = getSimInfo(subId);
1388         if (simInfo == null) {
1389             return null;
1390         }
1391         Pair<String, String> mccMncPair = extractMccMnc(simInfo.imsi, simInfo.mccMnc);
1392         if (mccMncPair == null) {
1393             return null;
1394         }
1395 
1396         String realm = String.format(THREE_GPP_NAI_REALM_FORMAT, mccMncPair.second,
1397                 mccMncPair.first);
1398         return String.format("%s@%s", pseudonym, realm);
1399     }
1400 
1401     /**
1402      * Reset the downloaded IMSI encryption key.
1403      * @param config Instance of WifiConfiguration
1404      */
resetCarrierKeysForImsiEncryption(@onNull WifiConfiguration config)1405     public void resetCarrierKeysForImsiEncryption(@NonNull WifiConfiguration config) {
1406         int subId = getBestMatchSubscriptionId(config);
1407         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
1408             return;
1409         }
1410         TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
1411         specifiedTm.resetCarrierKeysForImsiEncryption();
1412     }
1413 
1414     /**
1415      * Updates the carrier ID for passpoint configuration with SIM credential.
1416      *
1417      * @param config The instance of PasspointConfiguration.
1418      * @return true if the carrier ID is updated, false otherwise
1419      */
tryUpdateCarrierIdForPasspoint(PasspointConfiguration config)1420     public boolean tryUpdateCarrierIdForPasspoint(PasspointConfiguration config) {
1421         if (config.getCarrierId() != TelephonyManager.UNKNOWN_CARRIER_ID) {
1422             return false;
1423         }
1424 
1425         Credential.SimCredential simCredential = config.getCredential().getSimCredential();
1426         if (simCredential == null) {
1427             // carrier ID is not required.
1428             return false;
1429         }
1430 
1431         IMSIParameter imsiParameter = IMSIParameter.build(simCredential.getImsi());
1432         // If the IMSI is not full, the carrier ID can not be matched for sure, so it should
1433         // be ignored.
1434         if (imsiParameter == null || !imsiParameter.isFullImsi()) {
1435             vlogd("IMSI is not available or not full");
1436             return false;
1437         }
1438         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
1439             return false;
1440         }
1441         // Find the active matching SIM card with the full IMSI from passpoint profile.
1442         for (SubscriptionInfo subInfo : mActiveSubInfos) {
1443             SimInfo simInfo = getSimInfo(subInfo.getSubscriptionId());
1444             if (simInfo == null) {
1445                 continue;
1446             }
1447             if (imsiParameter.matchesImsi(simInfo.imsi)) {
1448                 config.setCarrierId(subInfo.getCarrierId());
1449                 return true;
1450             }
1451         }
1452 
1453         return false;
1454     }
1455 
1456     /**
1457      * Get the IMSI and carrier ID of the SIM card which is matched with the given subscription ID.
1458      *
1459      * @param subId The subscription ID see {@link SubscriptionInfo#getSubscriptionId()}
1460      * @return null if there is no matching SIM card, otherwise the IMSI and carrier ID of the
1461      * matching SIM card
1462      */
getMatchingImsiBySubId(int subId)1463     public @Nullable String getMatchingImsiBySubId(int subId) {
1464         if (!isSimReady(subId)) {
1465             return null;
1466         }
1467         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1468             if (requiresImsiEncryption(subId) && !isImsiEncryptionInfoAvailable(subId)) {
1469                 vlogd("required IMSI encryption information is not available.");
1470                 return null;
1471             }
1472             SimInfo simInfo = getSimInfo(subId);
1473             if (simInfo != null) {
1474                 return simInfo.imsi;
1475             }
1476         }
1477         vlogd("no active SIM card to match the carrier ID.");
1478         return null;
1479     }
1480 
1481     /**
1482      * Get the IMSI and carrier ID of the SIM card which is matched with the given IMSI
1483      * (only prefix of IMSI - mccmnc*) from passpoint profile.
1484      *
1485      * @param imsiPrefix The IMSI parameter from passpoint profile.
1486      * @return null if there is no matching SIM card, otherwise the IMSI and carrier ID of the
1487      * matching SIM card
1488      */
getMatchingImsiCarrierId( String imsiPrefix)1489     public @Nullable Pair<String, Integer> getMatchingImsiCarrierId(
1490             String imsiPrefix) {
1491         IMSIParameter imsiParameter = IMSIParameter.build(imsiPrefix);
1492         if (imsiParameter == null) {
1493             return null;
1494         }
1495         if (mActiveSubInfos == null) {
1496             return null;
1497         }
1498         int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
1499         //Pair<IMSI, carrier ID> the IMSI and carrier ID of matched SIM card
1500         Pair<String, Integer> matchedPair = null;
1501         // matchedDataPair check if the data SIM is matched.
1502         Pair<String, Integer> matchedDataPair = null;
1503         // matchedMnoPair check if any matched SIM card is MNO.
1504         Pair<String, Integer> matchedMnoPair = null;
1505 
1506         // Find the active matched SIM card with the priority order of Data MNO SIM,
1507         // Nondata MNO SIM, Data MVNO SIM, Nondata MVNO SIM.
1508         for (SubscriptionInfo subInfo : mActiveSubInfos) {
1509             int subId = subInfo.getSubscriptionId();
1510             if (requiresImsiEncryption(subId) && !isImsiEncryptionInfoAvailable(subId)) {
1511                 vlogd("required IMSI encryption information is not available.");
1512                 continue;
1513             }
1514             SimInfo simInfo = getSimInfo(subId);
1515             if (simInfo == null) {
1516                 continue;
1517             }
1518             if (simInfo.mccMnc != null && imsiParameter.matchesMccMnc(simInfo.mccMnc)) {
1519                 if (TextUtils.isEmpty(simInfo.imsi)) {
1520                     continue;
1521                 }
1522                 matchedPair = new Pair<>(simInfo.imsi, subInfo.getCarrierId());
1523                 if (subId == dataSubId) {
1524                     matchedDataPair = matchedPair;
1525                     if (simInfo.getCarrierType() == CARRIER_MNO_TYPE) {
1526                         vlogd("MNO data is matched via IMSI.");
1527                         return matchedDataPair;
1528                     }
1529                 }
1530                 if (simInfo.getCarrierType() == CARRIER_MNO_TYPE) {
1531                     matchedMnoPair = matchedPair;
1532                 }
1533             }
1534         }
1535 
1536         if (matchedMnoPair != null) {
1537             vlogd("MNO sub is matched via IMSI.");
1538             return matchedMnoPair;
1539         }
1540 
1541         if (matchedDataPair != null) {
1542             vlogd("MVNO data sub is matched via IMSI.");
1543             return matchedDataPair;
1544         }
1545 
1546         return matchedPair;
1547     }
1548 
vlogd(String msg)1549     private void vlogd(String msg) {
1550         if (!mVerboseLogEnabled) {
1551             return;
1552         }
1553 
1554         Log.d(TAG, msg);
1555     }
1556 
1557     /** Dump state. */
dump(FileDescriptor fd, PrintWriter pw, String[] args)1558     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1559         pw.println(TAG + ": ");
1560         pw.println("mImsiEncryptionInfoAvailable=" + mImsiEncryptionInfoAvailable);
1561         pw.println("mImsiPrivacyProtectionExemptionMap=" + mImsiPrivacyProtectionExemptionMap);
1562         pw.println("mMergedCarrierNetworkOffloadMap=" + mMergedCarrierNetworkOffloadMap);
1563         pw.println("mSubIdToSimInfoSparseArray=" + mSubIdToSimInfoSparseArray);
1564         pw.println("mActiveSubInfos=" + mActiveSubInfos);
1565         pw.println("mCachedCarrierConfigPerSubId=" + mCachedCarrierConfigPerSubId);
1566     }
1567 
1568     /**
1569      * Get the carrier ID {@link TelephonyManager#getSimCarrierId()} of the carrier which give
1570      * target package carrier privileges.
1571      *
1572      * @param packageName target package to check if grant privileges by any carrier.
1573      * @return Carrier ID who give privilege to this package. If package isn't granted privilege
1574      *         by any available carrier, will return UNKNOWN_CARRIER_ID.
1575      */
getCarrierIdForPackageWithCarrierPrivileges(String packageName)1576     public int getCarrierIdForPackageWithCarrierPrivileges(String packageName) {
1577         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
1578             if (mVerboseLogEnabled) Log.v(TAG, "No subs for carrier privilege check");
1579             return TelephonyManager.UNKNOWN_CARRIER_ID;
1580         }
1581         for (SubscriptionInfo info : mActiveSubInfos) {
1582             TelephonyManager specifiedTm =
1583                     mTelephonyManager.createForSubscriptionId(info.getSubscriptionId());
1584             if (specifiedTm.checkCarrierPrivilegesForPackage(packageName)
1585                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
1586                 return info.getCarrierId();
1587             }
1588         }
1589         return TelephonyManager.UNKNOWN_CARRIER_ID;
1590     }
1591 
1592     /**
1593      * Get the carrier name for target subscription id.
1594      * @param subId Subscription id
1595      * @return String of carrier name.
1596      */
getCarrierNameForSubId(int subId)1597     public String getCarrierNameForSubId(int subId) {
1598         TelephonyManager specifiedTm =
1599                 mTelephonyManager.createForSubscriptionId(subId);
1600 
1601         CharSequence name = specifiedTm.getSimCarrierIdName();
1602         if (name == null) {
1603             return null;
1604         }
1605         return name.toString();
1606     }
1607 
1608     /**
1609      * Check if a config is carrier network and from the non default data SIM.
1610      * @return True if it is carrier network and from non default data SIM,otherwise return false.
1611      */
isCarrierNetworkFromNonDefaultDataSim(WifiConfiguration config)1612     public boolean isCarrierNetworkFromNonDefaultDataSim(WifiConfiguration config) {
1613         if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
1614             return false;
1615         }
1616         int subId = getBestMatchSubscriptionId(config);
1617         return subId != SubscriptionManager.getDefaultDataSubscriptionId();
1618     }
1619 
1620     /**
1621      * Get the carrier Id of the default data sim.
1622      */
getDefaultDataSimCarrierId()1623     public int getDefaultDataSimCarrierId() {
1624         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
1625         SimInfo simInfo = getSimInfo(subId);
1626         if (simInfo == null) {
1627             return TelephonyManager.UNKNOWN_CARRIER_ID;
1628         }
1629         return simInfo.simCarrierId;
1630     }
1631 
1632     /**
1633      * Add a listener to monitor user approval IMSI protection exemption.
1634      */
addImsiExemptionUserApprovalListener( OnUserApproveCarrierListener listener)1635     public void addImsiExemptionUserApprovalListener(
1636             OnUserApproveCarrierListener listener) {
1637         mOnUserApproveCarrierListeners.add(listener);
1638     }
1639 
1640     /**
1641      * Add a listener to monitor carrier offload disabled.
1642      */
addOnCarrierOffloadDisabledListener( OnCarrierOffloadDisabledListener listener)1643     public void addOnCarrierOffloadDisabledListener(
1644             OnCarrierOffloadDisabledListener listener) {
1645         mOnCarrierOffloadDisabledListeners.add(listener);
1646     }
1647 
1648     /**
1649      * remove a {@link OnCarrierOffloadDisabledListener}.
1650      */
removeOnCarrierOffloadDisabledListener( OnCarrierOffloadDisabledListener listener)1651     public void removeOnCarrierOffloadDisabledListener(
1652             OnCarrierOffloadDisabledListener listener) {
1653         mOnCarrierOffloadDisabledListeners.remove(listener);
1654     }
1655 
1656     /**
1657      * Clear the Imsi Privacy Exemption user approval info the target carrier.
1658      */
clearImsiPrivacyExemptionForCarrier(int carrierId)1659     public void clearImsiPrivacyExemptionForCarrier(int carrierId) {
1660         mImsiPrivacyProtectionExemptionMap.remove(carrierId);
1661         saveToStore();
1662     }
1663 
1664     /**
1665      * Check if carrier have user approved exemption for IMSI protection
1666      */
hasUserApprovedImsiPrivacyExemptionForCarrier(int carrierId)1667     public boolean hasUserApprovedImsiPrivacyExemptionForCarrier(int carrierId) {
1668         return  mImsiPrivacyProtectionExemptionMap.getOrDefault(carrierId, false);
1669     }
1670 
1671     /**
1672      * Enable or disable exemption on IMSI protection.
1673      */
setHasUserApprovedImsiPrivacyExemptionForCarrier(boolean approved, int carrierId)1674     public void setHasUserApprovedImsiPrivacyExemptionForCarrier(boolean approved, int carrierId) {
1675         if (mVerboseLogEnabled) {
1676             Log.v(TAG, "Setting Imsi privacy exemption for carrier " + carrierId
1677                     + (approved ? " approved" : " not approved"));
1678         }
1679         mImsiPrivacyProtectionExemptionMap.put(carrierId, approved);
1680         // If user approved the exemption restore to initial auto join configure.
1681         if (approved) {
1682             for (OnUserApproveCarrierListener listener : mOnUserApproveCarrierListeners) {
1683                 listener.onUserAllowed(carrierId);
1684             }
1685         }
1686         saveToStore();
1687     }
1688 
sendImsiPrivacyNotification(int carrierId)1689     private void sendImsiPrivacyNotification(int carrierId) {
1690         String carrierName = getCarrierNameForSubId(getMatchingSubId(carrierId));
1691         if (carrierName == null) {
1692             // If carrier name could not be retrieved, do not send notification.
1693             return;
1694         }
1695         Notification.Action userAllowAppNotificationAction =
1696                 new Notification.Action.Builder(null,
1697                         mResources.getText(R.string
1698                                 .wifi_suggestion_action_allow_imsi_privacy_exemption_carrier),
1699                         getPrivateBroadcast(NOTIFICATION_USER_ALLOWED_CARRIER_INTENT_ACTION,
1700                                 Pair.create(EXTRA_CARRIER_NAME, carrierName),
1701                                 Pair.create(EXTRA_CARRIER_ID, carrierId)))
1702                         .build();
1703         Notification.Action userDisallowAppNotificationAction =
1704                 new Notification.Action.Builder(null,
1705                         mResources.getText(R.string
1706                                 .wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier),
1707                         getPrivateBroadcast(NOTIFICATION_USER_DISALLOWED_CARRIER_INTENT_ACTION,
1708                                 Pair.create(EXTRA_CARRIER_NAME, carrierName),
1709                                 Pair.create(EXTRA_CARRIER_ID, carrierId)))
1710                         .build();
1711 
1712         Notification notification = mFrameworkFacade.makeNotificationBuilder(
1713                 mContext, WifiService.NOTIFICATION_NETWORK_STATUS)
1714                 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
1715                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
1716                 .setTicker(mResources.getString(
1717                         R.string.wifi_suggestion_imsi_privacy_title, carrierName))
1718                 .setContentTitle(mResources.getString(
1719                         R.string.wifi_suggestion_imsi_privacy_title, carrierName))
1720                 .setStyle(new Notification.BigTextStyle()
1721                         .bigText(mResources.getString(
1722                                 R.string.wifi_suggestion_imsi_privacy_content)))
1723                 .setContentIntent(getPrivateBroadcast(NOTIFICATION_USER_CLICKED_INTENT_ACTION,
1724                         Pair.create(EXTRA_CARRIER_NAME, carrierName),
1725                         Pair.create(EXTRA_CARRIER_ID, carrierId)))
1726                 .setDeleteIntent(getPrivateBroadcast(NOTIFICATION_USER_DISMISSED_INTENT_ACTION,
1727                         Pair.create(EXTRA_CARRIER_NAME, carrierName),
1728                         Pair.create(EXTRA_CARRIER_ID, carrierId)))
1729                 .setShowWhen(false)
1730                 .setLocalOnly(true)
1731                 .setColor(mResources.getColor(android.R.color.system_notification_accent_color,
1732                         mContext.getTheme()))
1733                 .addAction(userDisallowAppNotificationAction)
1734                 .addAction(userAllowAppNotificationAction)
1735                 .setTimeoutAfter(NOTIFICATION_EXPIRY_MILLS)
1736                 .build();
1737 
1738         // Post the notification.
1739         mNotificationManager.notify(SystemMessage.NOTE_CARRIER_SUGGESTION_AVAILABLE, notification);
1740         mNotificationUpdateTime = mClock.getElapsedSinceBootMillis()
1741                 + NOTIFICATION_UPDATE_DELAY_MILLS;
1742         mIsLastUserApprovalUiDialog = false;
1743     }
1744 
sendImsiPrivacyConfirmationDialog(@onNull String carrierName, int carrierId)1745     private void sendImsiPrivacyConfirmationDialog(@NonNull String carrierName, int carrierId) {
1746         mWifiMetrics.addUserApprovalCarrierUiReaction(ACTION_USER_ALLOWED_CARRIER,
1747                 mIsLastUserApprovalUiDialog);
1748         AlertDialog dialog = mFrameworkFacade.makeAlertDialogBuilder(mContext)
1749                 .setTitle(mResources.getString(
1750                         R.string.wifi_suggestion_imsi_privacy_exemption_confirmation_title))
1751                 .setMessage(mResources.getString(
1752                         R.string.wifi_suggestion_imsi_privacy_exemption_confirmation_content,
1753                         carrierName))
1754                 .setPositiveButton(mResources.getText(
1755                         R.string.wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation),
1756                         (d, which) -> mHandler.post(
1757                                 () -> handleUserAllowCarrierExemptionAction(
1758                                         carrierName, carrierId)))
1759                 .setNegativeButton(mResources.getText(
1760                         R.string.wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation),
1761                         (d, which) -> mHandler.post(
1762                                 () -> handleUserDisallowCarrierExemptionAction(
1763                                         carrierName, carrierId)))
1764                 .setOnDismissListener(
1765                         (d) -> mHandler.post(this::handleUserDismissAction))
1766                 .setOnCancelListener(
1767                         (d) -> mHandler.post(this::handleUserDismissAction))
1768                 .create();
1769         dialog.setCanceledOnTouchOutside(false);
1770         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1771         dialog.getWindow().addSystemFlags(
1772                 WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
1773         dialog.show();
1774         mIsLastUserApprovalUiDialog = true;
1775     }
1776 
1777     /**
1778      * Send notification for exemption of IMSI protection if user never made choice before.
1779      */
sendImsiProtectionExemptionNotificationIfRequired(int carrierId)1780     public void sendImsiProtectionExemptionNotificationIfRequired(int carrierId) {
1781         int subId = getMatchingSubId(carrierId);
1782         // If user data isn't loaded, don't send notification.
1783         if (!mUserDataLoaded) {
1784             return;
1785         }
1786         PersistableBundle bundle = getCarrierConfigForSubId(subId);
1787         if (bundle == null) {
1788             return;
1789         }
1790         if (requiresImsiEncryption(subId)) {
1791             return;
1792         }
1793         if (mImsiPrivacyProtectionExemptionMap.containsKey(carrierId)) {
1794             return;
1795         }
1796         if (mNotificationUpdateTime > mClock.getElapsedSinceBootMillis()) {
1797             return; // Active notification is still available, do not update.
1798         }
1799         Log.i(TAG, "Sending IMSI protection notification for " + carrierId);
1800         sendImsiPrivacyNotification(carrierId);
1801     }
1802 
1803     /**
1804      * Check if the target subscription has a matched carrier Id.
1805      * @param subId Subscription Id for which this carrier network is valid.
1806      * @param carrierId Carrier Id for this carrier network.
1807      * @return true if matches, false otherwise.
1808      */
isSubIdMatchingCarrierId(int subId, int carrierId)1809     public boolean isSubIdMatchingCarrierId(int subId, int carrierId) {
1810         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1811             // If Subscription Id is not set, consider it matches. Best matching one will be used to
1812             // connect.
1813             return true;
1814         }
1815         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
1816             return false;
1817         }
1818         for (SubscriptionInfo info : mActiveSubInfos) {
1819             if (info.getSubscriptionId() == subId) {
1820                 return info.getCarrierId() == carrierId;
1821             }
1822         }
1823         return false;
1824     }
1825 
getPrivateBroadcast(@onNull String action, @NonNull Pair<String, String> extra1, @NonNull Pair<String, Integer> extra2)1826     private PendingIntent getPrivateBroadcast(@NonNull String action,
1827             @NonNull Pair<String, String> extra1, @NonNull Pair<String, Integer> extra2) {
1828         Intent intent = new Intent(action)
1829                 .setPackage(mContext.getServiceWifiPackageName())
1830                 .putExtra(extra1.first, extra1.second)
1831                 .putExtra(extra2.first, extra2.second);
1832         return mFrameworkFacade.getBroadcast(mContext, 0, intent,
1833                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
1834     }
1835 
1836     /**
1837      * Set carrier network offload enabled/disabled.
1838      * @param subscriptionId Subscription Id to change carrier offload
1839      * @param merged True for carrier merged network, false otherwise.
1840      * @param enabled True for enabled carrier offload, false otherwise.
1841      */
setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, boolean enabled)1842     public void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged,
1843             boolean enabled) {
1844         if (merged) {
1845             mMergedCarrierNetworkOffloadMap.put(subscriptionId, enabled);
1846         } else {
1847             mUnmergedCarrierNetworkOffloadMap.put(subscriptionId, enabled);
1848         }
1849         if (!enabled) {
1850             for (OnCarrierOffloadDisabledListener listener : mOnCarrierOffloadDisabledListeners) {
1851                 listener.onCarrierOffloadDisabled(subscriptionId, merged);
1852             }
1853         }
1854         saveToStore();
1855     }
1856 
1857     /**
1858      * Check if carrier network offload is enabled/disabled.
1859      * @param subId Subscription Id to check offload state.
1860      * @param merged True for carrier merged network, false otherwise.
1861      * @return True to indicate carrier offload is enabled, false otherwise.
1862      */
isCarrierNetworkOffloadEnabled(int subId, boolean merged)1863     public boolean isCarrierNetworkOffloadEnabled(int subId, boolean merged) {
1864         if (merged) {
1865             return mMergedCarrierNetworkOffloadMap.getOrDefault(subId, true)
1866                     && isMobileDataEnabled(subId);
1867         } else {
1868             return mUnmergedCarrierNetworkOffloadMap.getOrDefault(subId, true);
1869         }
1870     }
1871 
isMobileDataEnabled(int subId)1872     private boolean isMobileDataEnabled(int subId) {
1873         if (!SdkLevel.isAtLeastS()) {
1874             // As the carrier offload enabled API and the merged carrier API (which is controlled by
1875             // this toggle) were added in S. Don't check the mobile data toggle when Sdk is less
1876             // than S.
1877             return true;
1878         }
1879         if (mUserDataEnabled.indexOfKey(subId) >= 0) {
1880             return mUserDataEnabled.get(subId);
1881         }
1882         TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
1883         boolean enabled = specifiedTm.isDataEnabled();
1884         mUserDataEnabled.put(subId, enabled);
1885         UserDataEnabledChangedListener listener = new UserDataEnabledChangedListener(subId);
1886         specifiedTm.registerTelephonyCallback(new HandlerExecutor(mHandler), listener);
1887         mUserDataEnabledListenerList.add(listener);
1888 
1889         return enabled;
1890     }
1891 
saveToStore()1892     private void saveToStore() {
1893         // Set the flag to let WifiConfigStore that we have new data to write.
1894         mHasNewUserDataToSerialize = true;
1895         mHasNewSharedDataToSerialize = true;
1896         if (!mWifiInjector.getWifiConfigManager().saveToStore(true)) {
1897             Log.w(TAG, "Failed to save to store");
1898         }
1899     }
1900 
1901     /**
1902      * Helper method for user factory reset network setting.
1903      */
clear()1904     public void clear() {
1905         mImsiPrivacyProtectionExemptionMap.clear();
1906         mMergedCarrierNetworkOffloadMap.clear();
1907         mUnmergedCarrierNetworkOffloadMap.clear();
1908         mUserDataEnabled.clear();
1909         if (SdkLevel.isAtLeastS()) {
1910             for (UserDataEnabledChangedListener listener : mUserDataEnabledListenerList) {
1911                 listener.unregisterListener();
1912             }
1913             mUserDataEnabledListenerList.clear();
1914         }
1915         resetNotification();
1916         saveToStore();
1917     }
1918 
resetNotification()1919     public void resetNotification() {
1920         mNotificationManager.cancel(SystemMessage.NOTE_CARRIER_SUGGESTION_AVAILABLE);
1921         mNotificationUpdateTime = 0;
1922     }
1923 
getSimInfo(int subId)1924     private SimInfo getSimInfo(int subId) {
1925         SimInfo simInfo = mSubIdToSimInfoSparseArray.get(subId);
1926         // If mccmnc is not available, try to get it again.
1927         if (simInfo != null && simInfo.mccMnc != null && !simInfo.mccMnc.isEmpty()) {
1928             return simInfo;
1929         }
1930         TelephonyManager specifiedTm = mTelephonyManager.createForSubscriptionId(subId);
1931         if (specifiedTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) {
1932             return null;
1933         }
1934         String imsi = specifiedTm.getSubscriberId();
1935         String mccMnc = specifiedTm.getSimOperator();
1936         if (imsi == null || imsi.isEmpty()) {
1937             Log.wtf(TAG, "Get invalid imsi when SIM is ready!");
1938             return null;
1939         }
1940         int CarrierIdFromSimMccMnc = specifiedTm.getCarrierIdFromSimMccMnc();
1941         int SimCarrierId = specifiedTm.getSimCarrierId();
1942         simInfo = new SimInfo(imsi, mccMnc, CarrierIdFromSimMccMnc, SimCarrierId);
1943         mSubIdToSimInfoSparseArray.put(subId, simInfo);
1944         return simInfo;
1945     }
1946 
1947     /**
1948      * Try to extract mcc and mnc from IMSI and MCCMNC
1949      * @return a pair of string represent <mcc, mnc>. Pair.first is mcc, pair.second is mnc.
1950      */
extractMccMnc(String imsi, String mccMnc)1951     private Pair<String, String> extractMccMnc(String imsi, String mccMnc) {
1952         String mcc;
1953         String mnc;
1954         if (mccMnc != null && !mccMnc.isEmpty() && mccMnc.length() >= 5) {
1955             mcc = mccMnc.substring(0, 3);
1956             mnc = mccMnc.substring(3);
1957             if (mnc.length() == 2) {
1958                 mnc = "0" + mnc;
1959             }
1960         } else if (imsi != null && !imsi.isEmpty() && imsi.length() >= 6) {
1961             // extract mcc & mnc from IMSI, assume mnc size is 3
1962             mcc = imsi.substring(0, 3);
1963             mnc = imsi.substring(3, 6);
1964             vlogd("Guessing from IMSI, MCC=" + mcc + "; MNC=" + mnc);
1965         } else {
1966             return null;
1967         }
1968         return Pair.create(mcc, mnc);
1969     }
1970 
isEapMethodPrefixEnabled(int subId)1971     private boolean isEapMethodPrefixEnabled(int subId) {
1972         PersistableBundle bundle = getCarrierConfigForSubId(subId);
1973         if (bundle == null) {
1974             return false;
1975         }
1976         return bundle.getBoolean(CarrierConfigManager.ENABLE_EAP_METHOD_PREFIX_BOOL);
1977     }
1978 }
1979