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