1 /* 2 * Copyright (C) 2006 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.phone; 18 19 import static android.Manifest.permission.READ_PHONE_STATE; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.BroadcastOptions; 24 import android.app.Notification; 25 import android.app.NotificationManager; 26 import android.app.PendingIntent; 27 import android.app.StatusBarManager; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.SharedPreferences; 32 import android.content.pm.PackageManager; 33 import android.content.pm.ResolveInfo; 34 import android.content.res.Resources; 35 import android.net.Uri; 36 import android.os.Handler; 37 import android.os.Message; 38 import android.os.PersistableBundle; 39 import android.os.SystemClock; 40 import android.os.SystemProperties; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.preference.PreferenceManager; 44 import android.provider.ContactsContract.PhoneLookup; 45 import android.provider.Settings; 46 import android.telecom.PhoneAccount; 47 import android.telecom.PhoneAccountHandle; 48 import android.telecom.TelecomManager; 49 import android.telephony.CarrierConfigManager; 50 import android.telephony.PhoneNumberUtils; 51 import android.telephony.RadioAccessFamily; 52 import android.telephony.ServiceState; 53 import android.telephony.SubscriptionInfo; 54 import android.telephony.SubscriptionManager; 55 import android.telephony.TelephonyManager; 56 import android.text.TextUtils; 57 import android.util.ArrayMap; 58 import android.util.Log; 59 import android.util.SparseArray; 60 import android.widget.Toast; 61 62 import com.android.internal.telephony.Phone; 63 import com.android.internal.telephony.PhoneFactory; 64 import com.android.internal.telephony.RILConstants; 65 import com.android.internal.telephony.TelephonyCapabilities; 66 import com.android.internal.telephony.util.NotificationChannelController; 67 import com.android.phone.settings.VoicemailSettingsActivity; 68 69 import java.util.ArrayList; 70 import java.util.HashSet; 71 import java.util.Iterator; 72 import java.util.List; 73 import java.util.Set; 74 75 /** 76 * NotificationManager-related utility code for the Phone app. 77 * 78 * This is a singleton object which acts as the interface to the 79 * framework's NotificationManager, and is used to display status bar 80 * icons and control other status bar-related behavior. 81 * 82 * @see PhoneGlobals.notificationMgr 83 */ 84 public class NotificationMgr { 85 private static final String LOG_TAG = NotificationMgr.class.getSimpleName(); 86 private static final boolean DBG = 87 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 88 // Do not check in with VDBG = true, since that may write PII to the system log. 89 private static final boolean VDBG = false; 90 91 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX = 92 "mwi_should_check_vvm_configuration_state_"; 93 94 // notification types 95 static final int MMI_NOTIFICATION = 1; 96 static final int NETWORK_SELECTION_NOTIFICATION = 2; 97 static final int VOICEMAIL_NOTIFICATION = 3; 98 static final int CALL_FORWARD_NOTIFICATION = 4; 99 static final int DATA_ROAMING_NOTIFICATION = 5; 100 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6; 101 static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7; 102 103 // Event for network selection notification. 104 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1; 105 106 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L; 107 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10; 108 109 private static final int STATE_UNKNOWN_SERVICE = -1; 110 111 private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST"; 112 113 /** 114 * Grant recipients of new voicemail broadcasts a 10sec allowlist so they can start a background 115 * service to do VVM processing. 116 */ 117 private final long VOICEMAIL_ALLOW_LIST_DURATION_MILLIS = 10000L; 118 119 /** The singleton NotificationMgr instance. */ 120 private static NotificationMgr sInstance; 121 122 private PhoneGlobals mApp; 123 124 private Context mContext; 125 private StatusBarManager mStatusBarManager; 126 private UserManager mUserManager; 127 private Toast mToast; 128 private SubscriptionManager mSubscriptionManager; 129 private TelecomManager mTelecomManager; 130 private TelephonyManager mTelephonyManager; 131 132 // used to track the notification of selected network unavailable, per subscription id. 133 private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>(); 134 135 // used to track the notification of limited sim function under dual sim, per subscription id. 136 private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>(); 137 138 // used to track whether the message waiting indicator is visible, per subscription id. 139 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>(); 140 141 // those flags are used to track whether to show network selection notification or not. 142 private SparseArray<Integer> mPreviousServiceState = new SparseArray<>(); 143 private SparseArray<Long> mOOSTimestamp = new SparseArray<>(); 144 private SparseArray<Integer> mPendingEventCounter = new SparseArray<>(); 145 // maps each subId to selected network operator name. 146 private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>(); 147 148 private final Handler mHandler = new Handler() { 149 @Override 150 public void handleMessage(Message msg) { 151 switch (msg.what) { 152 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION: 153 int subId = (int) msg.obj; 154 TelephonyManager telephonyManager = 155 mTelephonyManager.createForSubscriptionId(subId); 156 if (telephonyManager.getServiceState() != null) { 157 shouldShowNotification(telephonyManager.getServiceState().getState(), 158 subId); 159 } 160 break; 161 } 162 } 163 }; 164 165 /** 166 * Private constructor (this is a singleton). 167 * @see #init(PhoneGlobals) 168 */ NotificationMgr(PhoneGlobals app)169 private NotificationMgr(PhoneGlobals app) { 170 mApp = app; 171 mContext = app; 172 mStatusBarManager = 173 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE); 174 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE); 175 mSubscriptionManager = SubscriptionManager.from(mContext); 176 mTelecomManager = app.getSystemService(TelecomManager.class); 177 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE); 178 } 179 180 /** 181 * Initialize the singleton NotificationMgr instance. 182 * 183 * This is only done once, at startup, from PhoneApp.onCreate(). 184 * From then on, the NotificationMgr instance is available via the 185 * PhoneApp's public "notificationMgr" field, which is why there's no 186 * getInstance() method here. 187 */ init(PhoneGlobals app)188 /* package */ static NotificationMgr init(PhoneGlobals app) { 189 synchronized (NotificationMgr.class) { 190 if (sInstance == null) { 191 sInstance = new NotificationMgr(app); 192 } else { 193 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 194 } 195 return sInstance; 196 } 197 } 198 199 /** The projection to use when querying the phones table */ 200 static final String[] PHONES_PROJECTION = new String[] { 201 PhoneLookup.NUMBER, 202 PhoneLookup.DISPLAY_NAME, 203 PhoneLookup._ID 204 }; 205 206 /** 207 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to 208 * refresh the voicemail intent on the indicator when the user changes it via the voicemail 209 * settings screen. The voicemail notification sound is suppressed. 210 * 211 * @param subId The subscription Id. 212 */ refreshMwi(int subId)213 /* package */ void refreshMwi(int subId) { 214 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will 215 // reference the single subid stored in the mMwiVisible map. 216 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 217 if (mMwiVisible.keySet().size() == 1) { 218 Set<Integer> keySet = mMwiVisible.keySet(); 219 Iterator<Integer> keyIt = keySet.iterator(); 220 if (!keyIt.hasNext()) { 221 return; 222 } 223 subId = keyIt.next(); 224 } 225 } 226 if (mMwiVisible.containsKey(subId)) { 227 boolean mwiVisible = mMwiVisible.get(subId); 228 if (mwiVisible) { 229 mApp.notifier.updatePhoneStateListeners(true); 230 } 231 } 232 } 233 setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled)234 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) { 235 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 236 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId" 237 + subId); 238 return; 239 } 240 241 PreferenceManager.getDefaultSharedPreferences(mContext).edit() 242 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled) 243 .apply(); 244 } 245 shouldCheckVisualVoicemailConfigurationForMwi(int subId)246 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) { 247 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 248 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId); 249 return true; 250 } 251 return PreferenceManager 252 .getDefaultSharedPreferences(mContext) 253 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true); 254 } 255 /** 256 * Updates the message waiting indicator (voicemail) notification. 257 * 258 * @param visible true if there are messages waiting 259 */ updateMwi(int subId, boolean visible)260 /* package */ void updateMwi(int subId, boolean visible) { 261 updateMwi(subId, visible, false /* isRefresh */); 262 } 263 264 /** 265 * Updates the message waiting indicator (voicemail) notification. 266 * 267 * @param subId the subId to update. 268 * @param visible true if there are messages waiting 269 * @param isRefresh {@code true} if the notification is a refresh and the user should not be 270 * notified again. 271 */ updateMwi(int subId, boolean visible, boolean isRefresh)272 void updateMwi(int subId, boolean visible, boolean isRefresh) { 273 if (!PhoneGlobals.sVoiceCapable) { 274 // Do not show the message waiting indicator on devices which are not voice capable. 275 // These events *should* be blocked at the telephony layer for such devices. 276 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring..."); 277 return; 278 } 279 280 Phone phone = PhoneGlobals.getPhone(subId); 281 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible); 282 mMwiVisible.put(subId, visible); 283 284 if (visible) { 285 if (phone == null) { 286 Log.w(LOG_TAG, "Found null phone for: " + subId); 287 return; 288 } 289 290 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 291 if (subInfo == null) { 292 Log.w(LOG_TAG, "Found null subscription info for: " + subId); 293 return; 294 } 295 296 int resId = android.R.drawable.stat_notify_voicemail; 297 if (mTelephonyManager.getPhoneCount() > 1) { 298 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1 299 : R.drawable.stat_notify_voicemail_sub2; 300 } 301 302 // This Notification can get a lot fancier once we have more 303 // information about the current voicemail messages. 304 // (For example, the current voicemail system can't tell 305 // us the caller-id or timestamp of a message, or tell us the 306 // message count.) 307 308 // But for now, the UI is ultra-simple: if the MWI indication 309 // is supposed to be visible, just show a single generic 310 // notification. 311 312 String notificationTitle = mContext.getString(R.string.notification_voicemail_title); 313 String vmNumber = phone.getVoiceMailNumber(); 314 if (DBG) log("- got vm number: '" + vmNumber + "'"); 315 316 // The voicemail number may be null because: 317 // (1) This phone has no voicemail number. 318 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may 319 // happen when the device first boots if we get a MWI notification when we 320 // register on the network before the SIM has loaded. In this case, the 321 // SubscriptionListener in CallNotifier will update this once the SIM is loaded. 322 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) { 323 if (DBG) log("- Null vm number: SIM records not loaded (yet)..."); 324 return; 325 } 326 327 Integer vmCount = null; 328 329 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) { 330 vmCount = phone.getVoiceMessageCount(); 331 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count); 332 notificationTitle = String.format(titleFormat, vmCount); 333 } 334 335 // This pathway only applies to PSTN accounts; only SIMS have subscription ids. 336 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone); 337 338 Intent intent; 339 String notificationText; 340 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber); 341 342 if (isSettingsIntent) { 343 notificationText = mContext.getString( 344 R.string.notification_voicemail_no_vm_number); 345 346 // If the voicemail number if unknown, instead of calling voicemail, take the user 347 // to the voicemail settings. 348 notificationText = mContext.getString( 349 R.string.notification_voicemail_no_vm_number); 350 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL); 351 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId); 352 intent.setClass(mContext, VoicemailSettingsActivity.class); 353 } else { 354 if (mTelephonyManager.getPhoneCount() > 1) { 355 notificationText = subInfo.getDisplayName().toString(); 356 } else { 357 notificationText = String.format( 358 mContext.getString(R.string.notification_voicemail_text_format), 359 PhoneNumberUtils.formatNumber(vmNumber)); 360 } 361 intent = new Intent( 362 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", 363 null)); 364 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 365 } 366 367 PendingIntent pendingIntent = 368 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 369 PendingIntent.FLAG_IMMUTABLE); 370 371 Resources res = mContext.getResources(); 372 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId( 373 subId); 374 Notification.Builder builder = new Notification.Builder(mContext); 375 builder.setSmallIcon(resId) 376 .setWhen(System.currentTimeMillis()) 377 .setColor(subInfo.getIconTint()) 378 .setContentTitle(notificationTitle) 379 .setContentText(notificationText) 380 .setContentIntent(pendingIntent) 381 .setColor(res.getColor(R.color.dialer_theme_color)) 382 .setOngoing(carrierConfig.getBoolean( 383 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL)) 384 .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL) 385 .setOnlyAlertOnce(isRefresh); 386 387 final Notification notification = builder.build(); 388 List<UserHandle> users = getUsersExcludeDying(); 389 for (UserHandle userHandle : users) { 390 if (!hasUserRestriction( 391 UserManager.DISALLOW_OUTGOING_CALLS, userHandle) 392 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) { 393 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber, 394 pendingIntent, isSettingsIntent, userHandle, isRefresh)) { 395 notifyAsUser( 396 Integer.toString(subId) /* tag */, 397 VOICEMAIL_NOTIFICATION, 398 notification, 399 userHandle); 400 } 401 } 402 } 403 } else { 404 List<UserHandle> users = getUsersExcludeDying(); 405 for (UserHandle userHandle : users) { 406 if (!hasUserRestriction( 407 UserManager.DISALLOW_OUTGOING_CALLS, userHandle) 408 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) { 409 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null, 410 false, userHandle, isRefresh)) { 411 cancelAsUser( 412 Integer.toString(subId) /* tag */, 413 VOICEMAIL_NOTIFICATION, 414 userHandle); 415 } 416 } 417 } 418 } 419 } 420 getUsersExcludeDying()421 private List<UserHandle> getUsersExcludeDying() { 422 long[] serialNumbersOfUsers = 423 mUserManager.getSerialNumbersOfUsers(/* excludeDying= */ true); 424 List<UserHandle> users = new ArrayList<>(serialNumbersOfUsers.length); 425 for (long serialNumber : serialNumbersOfUsers) { 426 users.add(mUserManager.getUserForSerialNumber(serialNumber)); 427 } 428 return users; 429 } 430 hasUserRestriction(String restrictionKey, UserHandle userHandle)431 private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { 432 final List<UserManager.EnforcingUser> sources = mUserManager 433 .getUserRestrictionSources(restrictionKey, userHandle); 434 return (sources != null && !sources.isEmpty()); 435 } 436 437 /** 438 * Sends a broadcast with the voicemail notification information to the default dialer. This 439 * method is also used to indicate to the default dialer when to clear the 440 * notification. A pending intent can be passed to the default dialer to indicate an action to 441 * be taken as it would by a notification produced in this class. 442 * @param phone The phone the notification is sent from 443 * @param count The number of pending voicemail messages to indicate on the notification. A 444 * Value of 0 is passed here to indicate that the notification should be cleared. 445 * @param number The voicemail phone number if specified. 446 * @param pendingIntent The intent that should be passed as the action to be taken. 447 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings. 448 * otherwise, {@code false} to indicate the intent launches voicemail. 449 * @param userHandle The user to receive the notification. Each user can have their own default 450 * dialer. 451 * @return {@code true} if the default was notified of the notification. 452 */ maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count, String number, PendingIntent pendingIntent, boolean isSettingsIntent, UserHandle userHandle, boolean isRefresh)453 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count, 454 String number, PendingIntent pendingIntent, boolean isSettingsIntent, 455 UserHandle userHandle, boolean isRefresh) { 456 457 if (shouldManageNotificationThroughDefaultDialer(userHandle)) { 458 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle); 459 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 460 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION); 461 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE, 462 PhoneUtils.makePstnPhoneAccountHandle(phone)); 463 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh); 464 if (count != null) { 465 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count); 466 } 467 468 // Additional information about the voicemail notification beyond the count is only 469 // present when the count not specified or greater than 0. The value of 0 represents 470 // clearing the notification, which does not require additional information. 471 if (count == null || count > 0) { 472 if (!TextUtils.isEmpty(number)) { 473 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number); 474 } 475 476 if (pendingIntent != null) { 477 intent.putExtra(isSettingsIntent 478 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT 479 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT, 480 pendingIntent); 481 } 482 } 483 484 BroadcastOptions bopts = BroadcastOptions.makeBasic(); 485 bopts.setTemporaryAppWhitelistDuration(VOICEMAIL_ALLOW_LIST_DURATION_MILLIS); 486 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE, bopts.toBundle()); 487 return true; 488 } 489 490 return false; 491 } 492 getShowVoicemailIntentForDefaultDialer(UserHandle userHandle)493 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) { 494 String dialerPackage = mContext.getSystemService(TelecomManager.class) 495 .getDefaultDialerPackage(userHandle); 496 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION) 497 .setPackage(dialerPackage); 498 } 499 shouldManageNotificationThroughDefaultDialer(UserHandle userHandle)500 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) { 501 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle); 502 if (intent == null) { 503 return false; 504 } 505 506 List<ResolveInfo> receivers = mContext.getPackageManager() 507 .queryBroadcastReceivers(intent, 0); 508 return receivers.size() > 0; 509 } 510 511 /** 512 * Updates the message call forwarding indicator notification. 513 * 514 * @param visible true if call forwarding enabled 515 */ 516 updateCfi(int subId, boolean visible)517 /* package */ void updateCfi(int subId, boolean visible) { 518 updateCfi(subId, visible, false /* isRefresh */); 519 } 520 521 /** 522 * Updates the message call forwarding indicator notification. 523 * 524 * @param visible true if call forwarding enabled 525 */ updateCfi(int subId, boolean visible, boolean isRefresh)526 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) { 527 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N")); 528 if (visible) { 529 // If Unconditional Call Forwarding (forward all calls) for VOICE 530 // is enabled, just show a notification. We'll default to expanded 531 // view for now, so the there is less confusion about the icon. If 532 // it is deemed too weird to have CF indications as expanded views, 533 // then we'll flip the flag back. 534 535 // TODO: We may want to take a look to see if the notification can 536 // display the target to forward calls to. This will require some 537 // effort though, since there are multiple layers of messages that 538 // will need to propagate that information. 539 540 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 541 if (subInfo == null) { 542 Log.w(LOG_TAG, "Found null subscription info for: " + subId); 543 return; 544 } 545 546 String notificationTitle; 547 int resId = R.drawable.stat_sys_phone_call_forward; 548 if (mTelephonyManager.getPhoneCount() > 1) { 549 int slotId = SubscriptionManager.getSlotIndex(subId); 550 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1 551 : R.drawable.stat_sys_phone_call_forward_sub2; 552 if (subInfo.getDisplayName() != null) { 553 notificationTitle = subInfo.getDisplayName().toString(); 554 } else { 555 notificationTitle = mContext.getString(R.string.labelCF); 556 } 557 } else { 558 notificationTitle = mContext.getString(R.string.labelCF); 559 } 560 561 Notification.Builder builder = new Notification.Builder(mContext) 562 .setSmallIcon(resId) 563 .setColor(subInfo.getIconTint()) 564 .setContentTitle(notificationTitle) 565 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator)) 566 .setShowWhen(false) 567 .setOngoing(true) 568 .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD) 569 .setOnlyAlertOnce(isRefresh); 570 571 Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS); 572 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 573 SubscriptionInfoHelper.addExtrasToIntent( 574 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId)); 575 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */, 576 intent, PendingIntent.FLAG_IMMUTABLE)); 577 notifyAsUser( 578 Integer.toString(subId) /* tag */, 579 CALL_FORWARD_NOTIFICATION, 580 builder.build(), 581 UserHandle.ALL); 582 } else { 583 List<UserHandle> users = getUsersExcludeDying(); 584 for (UserHandle user : users) { 585 if (mUserManager.isManagedProfile(user.getIdentifier())) { 586 continue; 587 } 588 cancelAsUser( 589 Integer.toString(subId) /* tag */, 590 CALL_FORWARD_NOTIFICATION, 591 user); 592 } 593 } 594 } 595 596 /** 597 * Shows either: 598 * 1) the "Data roaming is on" notification, which 599 * appears when you're roaming and you have the "data roaming" feature turned on for the 600 * given {@code subId}. 601 * or 602 * 2) the "data disconnected due to roaming" notification, which 603 * appears when you lose data connectivity because you're roaming and 604 * you have the "data roaming" feature turned off for the given {@code subId}. 605 * @param subId which subscription it's notifying about. 606 * @param roamingOn whether currently roaming is on or off. If true, we show notification 607 * 1) above; else we show notification 2). 608 */ showDataRoamingNotification(int subId, boolean roamingOn)609 /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) { 610 if (DBG) { 611 log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off") 612 + " on subId " + subId); 613 } 614 615 // "Mobile network settings" screen / dialog 616 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS); 617 intent.putExtra(Settings.EXTRA_SUB_ID, subId); 618 PendingIntent contentIntent = PendingIntent.getActivity( 619 mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE); 620 621 CharSequence contentTitle = mContext.getText(roamingOn 622 ? R.string.roaming_on_notification_title 623 : R.string.roaming_notification_title); 624 CharSequence contentText = mContext.getText(roamingOn 625 ? R.string.roaming_enabled_message 626 : R.string.roaming_reenable_message); 627 628 final Notification.Builder builder = new Notification.Builder(mContext) 629 .setSmallIcon(android.R.drawable.stat_sys_warning) 630 .setContentTitle(contentTitle) 631 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color)) 632 .setContentText(contentText) 633 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS) 634 .setContentIntent(contentIntent); 635 final Notification notif = 636 new Notification.BigTextStyle(builder).bigText(contentText).build(); 637 notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL); 638 } 639 notifyAsUser(String tag, int id, Notification notification, UserHandle user)640 private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) { 641 try { 642 Context contextForUser = 643 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 644 NotificationManager notificationManager = 645 (NotificationManager) contextForUser.getSystemService( 646 Context.NOTIFICATION_SERVICE); 647 notificationManager.notify(tag, id, notification); 648 } catch (PackageManager.NameNotFoundException e) { 649 Log.e(LOG_TAG, "unable to notify for user " + user); 650 e.printStackTrace(); 651 } 652 } 653 cancelAsUser(String tag, int id, UserHandle user)654 private void cancelAsUser(String tag, int id, UserHandle user) { 655 try { 656 Context contextForUser = 657 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 658 NotificationManager notificationManager = 659 (NotificationManager) contextForUser.getSystemService( 660 Context.NOTIFICATION_SERVICE); 661 notificationManager.cancel(tag, id); 662 } catch (PackageManager.NameNotFoundException e) { 663 Log.e(LOG_TAG, "unable to cancel for user " + user); 664 e.printStackTrace(); 665 } 666 } 667 668 /** 669 * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification. 670 */ hideDataRoamingNotification()671 /* package */ void hideDataRoamingNotification() { 672 if (DBG) log("hideDataRoamingNotification()..."); 673 cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL); 674 } 675 676 /** 677 * Shows the "Limited SIM functionality" warning notification, which appears when using a 678 * special carrier under dual sim. limited function applies for DSDS in general when two SIM 679 * cards share a single radio, thus the voice & data maybe impaired under certain scenarios. 680 */ showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName)681 public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) { 682 if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName 683 + " subId: " + subId); 684 if (mLimitedSimFunctionNotify.contains(subId)) { 685 // handle the case that user swipe the notification but condition triggers 686 // frequently which cause the same notification consistently displayed. 687 if (DBG) log("showLimitedSimFunctionWarningNotification, " 688 + "not display again if already displayed"); 689 return; 690 } 691 // Navigate to "Network Selection Settings" which list all subscriptions. 692 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, 693 new Intent(ACTION_MOBILE_NETWORK_LIST), PendingIntent.FLAG_IMMUTABLE); 694 // Display phone number from the other sub 695 String line1Num = null; 696 SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService( 697 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 698 List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false); 699 for (SubscriptionInfo sub : subList) { 700 if (sub.getSubscriptionId() != subId) { 701 line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId()); 702 } 703 } 704 final CharSequence contentText = TextUtils.isEmpty(line1Num) ? 705 String.format(mContext.getText( 706 R.string.limited_sim_function_notification_message).toString(), carrierName) : 707 String.format(mContext.getText( 708 R.string.limited_sim_function_with_phone_num_notification_message).toString(), 709 carrierName, line1Num); 710 final Notification.Builder builder = new Notification.Builder(mContext) 711 .setSmallIcon(R.drawable.ic_sim_card) 712 .setContentTitle(mContext.getText( 713 R.string.limited_sim_function_notification_title)) 714 .setContentText(contentText) 715 .setOnlyAlertOnce(true) 716 .setOngoing(true) 717 .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY) 718 .setContentIntent(contentIntent); 719 final Notification notification = new Notification.BigTextStyle(builder).bigText( 720 contentText).build(); 721 722 notifyAsUser(Integer.toString(subId), 723 LIMITED_SIM_FUNCTION_NOTIFICATION, 724 notification, UserHandle.ALL); 725 mLimitedSimFunctionNotify.add(subId); 726 } 727 728 /** 729 * Dismiss the "Limited SIM functionality" warning notification for the given subId. 730 */ dismissLimitedSimFunctionWarningNotification(int subId)731 public void dismissLimitedSimFunctionWarningNotification(int subId) { 732 if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId); 733 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 734 // dismiss all notifications 735 for (int id : mLimitedSimFunctionNotify) { 736 cancelAsUser(Integer.toString(id), 737 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL); 738 } 739 mLimitedSimFunctionNotify.clear(); 740 } else if (mLimitedSimFunctionNotify.contains(subId)) { 741 cancelAsUser(Integer.toString(subId), 742 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL); 743 mLimitedSimFunctionNotify.remove(subId); 744 } 745 } 746 747 /** 748 * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions. 749 */ dismissLimitedSimFunctionWarningNotificationForInactiveSubs()750 public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() { 751 if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs"); 752 // dismiss notification for inactive subscriptions. 753 // handle the corner case that SIM change by SIM refresh doesn't clear the notification 754 // from the old SIM if both old & new SIM configured to display the notification. 755 mLimitedSimFunctionNotify.removeIf(id -> { 756 if (!mSubscriptionManager.isActiveSubId(id)) { 757 cancelAsUser(Integer.toString(id), 758 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL); 759 return true; 760 } 761 return false; 762 }); 763 } 764 765 /** 766 * Display the network selection "no service" notification 767 * @param operator is the numeric operator number 768 * @param subId is the subscription ID 769 */ showNetworkSelection(String operator, int subId)770 private void showNetworkSelection(String operator, int subId) { 771 if (DBG) log("showNetworkSelection(" + operator + ")..."); 772 773 if (!TextUtils.isEmpty(operator)) { 774 operator = String.format(" (%s)", operator); 775 } 776 Notification.Builder builder = new Notification.Builder(mContext) 777 .setSmallIcon(android.R.drawable.stat_sys_warning) 778 .setContentTitle(mContext.getString(R.string.notification_network_selection_title)) 779 .setContentText( 780 mContext.getString(R.string.notification_network_selection_text, operator)) 781 .setShowWhen(false) 782 .setOngoing(true) 783 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT); 784 785 // create the target network operators settings intent 786 Intent intent = new Intent(Intent.ACTION_MAIN); 787 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 788 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 789 // Use MobileNetworkSettings to handle the selection intent 790 intent.setComponent(new ComponentName( 791 mContext.getString(R.string.mobile_network_settings_package), 792 mContext.getString(R.string.mobile_network_settings_class))); 793 intent.putExtra(Settings.EXTRA_SUB_ID, subId); 794 builder.setContentIntent( 795 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE)); 796 notifyAsUser( 797 Integer.toString(subId) /* tag */, 798 SELECTED_OPERATOR_FAIL_NOTIFICATION, 799 builder.build(), 800 UserHandle.ALL); 801 mSelectedUnavailableNotify.put(subId, true); 802 } 803 804 /** 805 * Turn off the network selection "no service" notification 806 */ cancelNetworkSelection(int subId)807 private void cancelNetworkSelection(int subId) { 808 if (DBG) log("cancelNetworkSelection()..."); 809 cancelAsUser( 810 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, 811 UserHandle.ALL); 812 } 813 814 /** 815 * Update notification about no service of user selected operator 816 * 817 * @param serviceState Phone service state 818 * @param subId The subscription ID 819 */ updateNetworkSelection(int serviceState, int subId)820 void updateNetworkSelection(int serviceState, int subId) { 821 int phoneId = SubscriptionManager.getPhoneId(subId); 822 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ? 823 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone(); 824 if (TelephonyCapabilities.supportsNetworkSelection(phone)) { 825 if (SubscriptionManager.isValidSubscriptionId(subId)) { 826 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); 827 String selectedNetworkOperatorName = 828 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, ""); 829 // get the shared preference of network_selection. 830 // empty is auto mode, otherwise it is the operator alpha name 831 // in case there is no operator name, check the operator numeric 832 if (TextUtils.isEmpty(selectedNetworkOperatorName)) { 833 selectedNetworkOperatorName = 834 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, ""); 835 } 836 boolean isManualSelection; 837 // if restoring manual selection is controlled by framework, then get network 838 // selection from shared preference, otherwise get from real network indicators. 839 boolean restoreSelection = !mContext.getResources().getBoolean( 840 com.android.internal.R.bool.skip_restoring_network_selection); 841 if (restoreSelection) { 842 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName); 843 } else { 844 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection(); 845 } 846 847 if (DBG) { 848 log("updateNetworkSelection()..." + "state = " + serviceState + " new network " 849 + (isManualSelection ? selectedNetworkOperatorName : "")); 850 } 851 852 if (isManualSelection) { 853 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName); 854 shouldShowNotification(serviceState, subId); 855 } else { 856 dismissNetworkSelectionNotification(subId); 857 clearUpNetworkSelectionNotificationParam(subId); 858 } 859 } else { 860 if (DBG) log("updateNetworkSelection()..." + "state = " + 861 serviceState + " not updating network due to invalid subId " + subId); 862 dismissNetworkSelectionNotificationForInactiveSubId(); 863 } 864 } 865 } 866 dismissNetworkSelectionNotification(int subId)867 private void dismissNetworkSelectionNotification(int subId) { 868 if (mSelectedUnavailableNotify.get(subId, false)) { 869 cancelNetworkSelection(subId); 870 mSelectedUnavailableNotify.remove(subId); 871 } 872 } 873 dismissNetworkSelectionNotificationForInactiveSubId()874 private void dismissNetworkSelectionNotificationForInactiveSubId() { 875 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) { 876 int subId = mSelectedUnavailableNotify.keyAt(i); 877 if (!mSubscriptionManager.isActiveSubId(subId)) { 878 dismissNetworkSelectionNotification(subId); 879 clearUpNetworkSelectionNotificationParam(subId); 880 } 881 } 882 } 883 postTransientNotification(int notifyId, CharSequence msg)884 /* package */ void postTransientNotification(int notifyId, CharSequence msg) { 885 if (mToast != null) { 886 mToast.cancel(); 887 } 888 889 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG); 890 mToast.show(); 891 } 892 log(String msg)893 private void log(String msg) { 894 Log.d(LOG_TAG, msg); 895 } 896 logi(String msg)897 private void logi(String msg) { 898 Log.i(LOG_TAG, msg); 899 } 900 shouldShowNotification(int serviceState, int subId)901 private void shouldShowNotification(int serviceState, int subId) { 902 // "Network selection unavailable" notification should only show when network selection is 903 // visible to the end user. Some CC items e.g. KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL 904 // can be overridden to hide the network selection to the end user. In this case, the 905 // notification is not shown to avoid confusion to the end user. 906 if (!shouldDisplayNetworkSelectOptions(subId)) { 907 logi("Skipping network selection unavailable notification due to carrier policy."); 908 return; 909 } 910 911 // In unstable network condition, the phone may go in and out of service. Add logic here to 912 // debounce the network selection notification. The notification only shows after phone is 913 // out of service, AND fulfills one of the two conditions below: 914 // - Out of service lasts {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS} 915 // - Or has checked {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times 916 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) { 917 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE) 918 != ServiceState.STATE_OUT_OF_SERVICE) { 919 mOOSTimestamp.put(subId, getTimeStamp()); 920 } 921 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L) 922 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS) 923 || mPendingEventCounter.get(subId, 0) 924 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) { 925 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId); 926 clearUpNetworkSelectionNotificationParam(subId); 927 } else { 928 startPendingNetworkSelectionNotification(subId); 929 } 930 } else { 931 dismissNetworkSelectionNotification(subId); 932 } 933 mPreviousServiceState.put(subId, serviceState); 934 if (DBG) { 935 log("shouldShowNotification()..." + " subId = " + subId 936 + " serviceState = " + serviceState 937 + " mOOSTimestamp = " + mOOSTimestamp 938 + " mPendingEventCounter = " + mPendingEventCounter); 939 } 940 } 941 startPendingNetworkSelectionNotification(int subId)942 private void startPendingNetworkSelectionNotification(int subId) { 943 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) { 944 if (DBG) { 945 log("startPendingNetworkSelectionNotification: subId = " + subId); 946 } 947 mHandler.sendMessageDelayed( 948 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId), 949 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS); 950 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1); 951 } 952 } 953 clearUpNetworkSelectionNotificationParam(int subId)954 private void clearUpNetworkSelectionNotificationParam(int subId) { 955 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) { 956 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId); 957 } 958 mPreviousServiceState.remove(subId); 959 mOOSTimestamp.remove(subId); 960 mPendingEventCounter.remove(subId); 961 mSelectedNetworkOperatorName.remove(subId); 962 } 963 getTimeStamp()964 private static long getTimeStamp() { 965 return SystemClock.elapsedRealtime(); 966 } 967 968 // TODO(b/243010310): merge methods below with Settings#MobileNetworkUtils and optimize them. 969 // The methods below are copied from com.android.settings.network.telephony.MobileNetworkUtils 970 // to make sure the network selection unavailable notification should not show when network 971 // selection menu is not visible to the end user in Settings app. shouldDisplayNetworkSelectOptions(int subId)972 private boolean shouldDisplayNetworkSelectOptions(int subId) { 973 final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId); 974 final CarrierConfigManager carrierConfigManager = mContext.getSystemService( 975 CarrierConfigManager.class); 976 final PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId); 977 978 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 979 || carrierConfig == null 980 || !carrierConfig.getBoolean( 981 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL) 982 || carrierConfig.getBoolean( 983 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) 984 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL) 985 && !telephonyManager.isManualNetworkSelectionAllowed())) { 986 return false; 987 } 988 989 if (isWorldMode(carrierConfig)) { 990 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf( 991 (int) telephonyManager.getAllowedNetworkTypesForReason( 992 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 993 if (networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO) { 994 return false; 995 } 996 if (shouldSpeciallyUpdateGsmCdma(telephonyManager, carrierConfig)) { 997 return false; 998 } 999 if (networkMode == RILConstants.NETWORK_MODE_LTE_GSM_WCDMA) { 1000 return true; 1001 } 1002 } 1003 1004 return isGsmBasicOptions(telephonyManager, carrierConfig); 1005 } 1006 isWorldMode(@onNull PersistableBundle carrierConfig)1007 private static boolean isWorldMode(@NonNull PersistableBundle carrierConfig) { 1008 return carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL); 1009 } 1010 shouldSpeciallyUpdateGsmCdma(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1011 private static boolean shouldSpeciallyUpdateGsmCdma(@NonNull TelephonyManager telephonyManager, 1012 @NonNull PersistableBundle carrierConfig) { 1013 if (!isWorldMode(carrierConfig)) { 1014 return false; 1015 } 1016 1017 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf( 1018 (int) telephonyManager.getAllowedNetworkTypesForReason( 1019 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER)); 1020 if (networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM 1021 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA 1022 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA 1023 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA 1024 || networkMode 1025 == RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA 1026 || networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) { 1027 if (!isTdscdmaSupported(telephonyManager, carrierConfig)) { 1028 return true; 1029 } 1030 } 1031 1032 return false; 1033 } 1034 isTdscdmaSupported(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1035 private static boolean isTdscdmaSupported(@NonNull TelephonyManager telephonyManager, 1036 @NonNull PersistableBundle carrierConfig) { 1037 if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) { 1038 return true; 1039 } 1040 final String[] numericArray = carrierConfig.getStringArray( 1041 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY); 1042 if (numericArray == null) { 1043 return false; 1044 } 1045 final ServiceState serviceState = telephonyManager.getServiceState(); 1046 final String operatorNumeric = 1047 (serviceState != null) ? serviceState.getOperatorNumeric() : null; 1048 if (operatorNumeric == null) { 1049 return false; 1050 } 1051 for (String numeric : numericArray) { 1052 if (operatorNumeric.equals(numeric)) { 1053 return true; 1054 } 1055 } 1056 return false; 1057 } 1058 isGsmBasicOptions(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1059 private static boolean isGsmBasicOptions(@NonNull TelephonyManager telephonyManager, 1060 @NonNull PersistableBundle carrierConfig) { 1061 if (!carrierConfig.getBoolean( 1062 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL) 1063 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) { 1064 return true; 1065 } 1066 1067 if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { 1068 return true; 1069 } 1070 1071 return false; 1072 } 1073 // END of TODO:(b/243010310): merge methods above with Settings#MobileNetworkUtils and optimize. 1074 } 1075