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