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