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