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