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