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