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