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