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