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