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 android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.app.StatusBarManager; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.SharedPreferences; 27 import android.content.pm.UserInfo; 28 import android.content.res.Resources; 29 import android.net.Uri; 30 import android.os.PersistableBundle; 31 import android.os.SystemProperties; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.preference.PreferenceManager; 35 import android.provider.ContactsContract.PhoneLookup; 36 import android.provider.Settings; 37 import android.telecom.PhoneAccount; 38 import android.telecom.PhoneAccountHandle; 39 import android.telecom.TelecomManager; 40 import android.telephony.CarrierConfigManager; 41 import android.telephony.PhoneNumberUtils; 42 import android.telephony.ServiceState; 43 import android.telephony.SubscriptionInfo; 44 import android.telephony.SubscriptionManager; 45 import android.telephony.TelephonyManager; 46 import android.text.TextUtils; 47 import android.util.ArrayMap; 48 import android.util.Log; 49 import android.widget.Toast; 50 51 import com.android.internal.telephony.Phone; 52 import com.android.internal.telephony.PhoneBase; 53 import com.android.internal.telephony.TelephonyCapabilities; 54 import com.android.phone.settings.VoicemailSettingsActivity; 55 import com.android.phone.vvm.omtp.sync.VoicemailStatusQueryHelper; 56 import com.android.phone.settings.VoicemailNotificationSettingsUtil; 57 import com.android.phone.settings.VoicemailProviderSettingsUtil; 58 59 import java.util.Iterator; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.Set; 63 64 /** 65 * NotificationManager-related utility code for the Phone app. 66 * 67 * This is a singleton object which acts as the interface to the 68 * framework's NotificationManager, and is used to display status bar 69 * icons and control other status bar-related behavior. 70 * 71 * @see PhoneGlobals.notificationMgr 72 */ 73 public class NotificationMgr { 74 private static final String LOG_TAG = NotificationMgr.class.getSimpleName(); 75 private static final boolean DBG = 76 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 77 // Do not check in with VDBG = true, since that may write PII to the system log. 78 private static final boolean VDBG = false; 79 80 // notification types 81 static final int MMI_NOTIFICATION = 1; 82 static final int NETWORK_SELECTION_NOTIFICATION = 2; 83 static final int VOICEMAIL_NOTIFICATION = 3; 84 static final int CALL_FORWARD_NOTIFICATION = 4; 85 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5; 86 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6; 87 88 /** The singleton NotificationMgr instance. */ 89 private static NotificationMgr sInstance; 90 91 private PhoneGlobals mApp; 92 private Phone mPhone; 93 94 private Context mContext; 95 private NotificationManager mNotificationManager; 96 private StatusBarManager mStatusBarManager; 97 private UserManager mUserManager; 98 private Toast mToast; 99 private SubscriptionManager mSubscriptionManager; 100 private TelecomManager mTelecomManager; 101 private TelephonyManager mTelephonyManager; 102 103 public StatusBarHelper statusBarHelper; 104 105 // used to track the notification of selected network unavailable 106 private boolean mSelectedUnavailableNotify = false; 107 108 // used to track whether the message waiting indicator is visible, per subscription id. 109 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>(); 110 111 /** 112 * Private constructor (this is a singleton). 113 * @see #init(PhoneGlobals) 114 */ NotificationMgr(PhoneGlobals app)115 private NotificationMgr(PhoneGlobals app) { 116 mApp = app; 117 mContext = app; 118 mNotificationManager = 119 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE); 120 mStatusBarManager = 121 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE); 122 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE); 123 mPhone = app.mCM.getDefaultPhone(); 124 statusBarHelper = new StatusBarHelper(); 125 mSubscriptionManager = SubscriptionManager.from(mContext); 126 mTelecomManager = TelecomManager.from(mContext); 127 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE); 128 } 129 130 /** 131 * Initialize the singleton NotificationMgr instance. 132 * 133 * This is only done once, at startup, from PhoneApp.onCreate(). 134 * From then on, the NotificationMgr instance is available via the 135 * PhoneApp's public "notificationMgr" field, which is why there's no 136 * getInstance() method here. 137 */ init(PhoneGlobals app)138 /* package */ static NotificationMgr init(PhoneGlobals app) { 139 synchronized (NotificationMgr.class) { 140 if (sInstance == null) { 141 sInstance = new NotificationMgr(app); 142 } else { 143 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 144 } 145 return sInstance; 146 } 147 } 148 149 /** 150 * Helper class that's a wrapper around the framework's 151 * StatusBarManager.disable() API. 152 * 153 * This class is used to control features like: 154 * 155 * - Disabling the status bar "notification windowshade" 156 * while the in-call UI is up 157 * 158 * - Disabling notification alerts (audible or vibrating) 159 * while a phone call is active 160 * 161 * - Disabling navigation via the system bar (the "soft buttons" at 162 * the bottom of the screen on devices with no hard buttons) 163 * 164 * We control these features through a single point of control to make 165 * sure that the various StatusBarManager.disable() calls don't 166 * interfere with each other. 167 */ 168 public class StatusBarHelper { 169 // Current desired state of status bar / system bar behavior 170 private boolean mIsNotificationEnabled = true; 171 private boolean mIsExpandedViewEnabled = true; 172 private boolean mIsSystemBarNavigationEnabled = true; 173 StatusBarHelper()174 private StatusBarHelper() { 175 } 176 177 /** 178 * Enables or disables auditory / vibrational alerts. 179 * 180 * (We disable these any time a voice call is active, regardless 181 * of whether or not the in-call UI is visible.) 182 */ enableNotificationAlerts(boolean enable)183 public void enableNotificationAlerts(boolean enable) { 184 if (mIsNotificationEnabled != enable) { 185 mIsNotificationEnabled = enable; 186 updateStatusBar(); 187 } 188 } 189 190 /** 191 * Enables or disables the expanded view of the status bar 192 * (i.e. the ability to pull down the "notification windowshade"). 193 * 194 * (This feature is disabled by the InCallScreen while the in-call 195 * UI is active.) 196 */ enableExpandedView(boolean enable)197 public void enableExpandedView(boolean enable) { 198 if (mIsExpandedViewEnabled != enable) { 199 mIsExpandedViewEnabled = enable; 200 updateStatusBar(); 201 } 202 } 203 204 /** 205 * Enables or disables the navigation via the system bar (the 206 * "soft buttons" at the bottom of the screen) 207 * 208 * (This feature is disabled while an incoming call is ringing, 209 * because it's easy to accidentally touch the system bar while 210 * pulling the phone out of your pocket.) 211 */ enableSystemBarNavigation(boolean enable)212 public void enableSystemBarNavigation(boolean enable) { 213 if (mIsSystemBarNavigationEnabled != enable) { 214 mIsSystemBarNavigationEnabled = enable; 215 updateStatusBar(); 216 } 217 } 218 219 /** 220 * Updates the status bar to reflect the current desired state. 221 */ updateStatusBar()222 private void updateStatusBar() { 223 int state = StatusBarManager.DISABLE_NONE; 224 225 if (!mIsExpandedViewEnabled) { 226 state |= StatusBarManager.DISABLE_EXPAND; 227 } 228 if (!mIsNotificationEnabled) { 229 state |= StatusBarManager.DISABLE_NOTIFICATION_ALERTS; 230 } 231 if (!mIsSystemBarNavigationEnabled) { 232 // Disable *all* possible navigation via the system bar. 233 state |= StatusBarManager.DISABLE_HOME; 234 state |= StatusBarManager.DISABLE_RECENT; 235 state |= StatusBarManager.DISABLE_BACK; 236 state |= StatusBarManager.DISABLE_SEARCH; 237 } 238 239 if (DBG) log("updateStatusBar: state = 0x" + Integer.toHexString(state)); 240 mStatusBarManager.disable(state); 241 } 242 } 243 244 /** The projection to use when querying the phones table */ 245 static final String[] PHONES_PROJECTION = new String[] { 246 PhoneLookup.NUMBER, 247 PhoneLookup.DISPLAY_NAME, 248 PhoneLookup._ID 249 }; 250 251 /** 252 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to 253 * refresh the voicemail intent on the indicator when the user changes it via the voicemail 254 * settings screen. The voicemail notification sound is suppressed. 255 * 256 * @param subId The subscription Id. 257 */ refreshMwi(int subId)258 /* package */ void refreshMwi(int subId) { 259 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will 260 // reference the single subid stored in the mMwiVisible map. 261 if (subId == SubscriptionInfoHelper.NO_SUB_ID) { 262 if (mMwiVisible.keySet().size() == 1) { 263 Set<Integer> keySet = mMwiVisible.keySet(); 264 Iterator<Integer> keyIt = keySet.iterator(); 265 if (!keyIt.hasNext()) { 266 return; 267 } 268 subId = keyIt.next(); 269 } 270 } 271 if (mMwiVisible.containsKey(subId)) { 272 boolean mwiVisible = mMwiVisible.get(subId); 273 if (mwiVisible) { 274 updateMwi(subId, mwiVisible, false /* enableNotificationSound */); 275 } 276 } 277 } 278 279 /** 280 * Updates the message waiting indicator (voicemail) notification. 281 * 282 * @param visible true if there are messages waiting 283 */ updateMwi(int subId, boolean visible)284 /* package */ void updateMwi(int subId, boolean visible) { 285 updateMwi(subId, visible, true /* enableNotificationSound */); 286 } 287 288 /** 289 * Updates the message waiting indicator (voicemail) notification. 290 * 291 * @param subId the subId to update. 292 * @param visible true if there are messages waiting 293 * @param enableNotificationSound {@code true} if the notification sound should be played. 294 */ updateMwi(int subId, boolean visible, boolean enableNotificationSound)295 void updateMwi(int subId, boolean visible, boolean enableNotificationSound) { 296 if (!PhoneGlobals.sVoiceCapable) { 297 // Do not show the message waiting indicator on devices which are not voice capable. 298 // These events *should* be blocked at the telephony layer for such devices. 299 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring..."); 300 return; 301 } 302 303 Phone phone = PhoneGlobals.getPhone(subId); 304 if (visible && phone != null) { 305 VoicemailStatusQueryHelper queryHelper = new VoicemailStatusQueryHelper(mContext); 306 PhoneAccountHandle phoneAccount = PhoneUtils.makePstnPhoneAccountHandle(phone); 307 if (queryHelper.isNotificationsChannelActive(phoneAccount)) { 308 Log.v(LOG_TAG, "Notifications channel active for visual voicemail, hiding mwi."); 309 visible = false; 310 } 311 } 312 313 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible); 314 mMwiVisible.put(subId, visible); 315 316 if (visible) { 317 if (phone == null) { 318 Log.w(LOG_TAG, "Found null phone for: " + subId); 319 return; 320 } 321 322 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 323 if (subInfo == null) { 324 Log.w(LOG_TAG, "Found null subscription info for: " + subId); 325 return; 326 } 327 328 int resId = android.R.drawable.stat_notify_voicemail; 329 330 // This Notification can get a lot fancier once we have more 331 // information about the current voicemail messages. 332 // (For example, the current voicemail system can't tell 333 // us the caller-id or timestamp of a message, or tell us the 334 // message count.) 335 336 // But for now, the UI is ultra-simple: if the MWI indication 337 // is supposed to be visible, just show a single generic 338 // notification. 339 340 String notificationTitle = mContext.getString(R.string.notification_voicemail_title); 341 String vmNumber = phone.getVoiceMailNumber(); 342 if (DBG) log("- got vm number: '" + vmNumber + "'"); 343 344 // The voicemail number may be null because: 345 // (1) This phone has no voicemail number. 346 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may 347 // happen when the device first boots if we get a MWI notification when we 348 // register on the network before the SIM has loaded. In this case, the 349 // SubscriptionListener in CallNotifier will update this once the SIM is loaded. 350 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) { 351 if (DBG) log("- Null vm number: SIM records not loaded (yet)..."); 352 return; 353 } 354 355 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) { 356 int vmCount = phone.getVoiceMessageCount(); 357 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count); 358 notificationTitle = String.format(titleFormat, vmCount); 359 } 360 361 // This pathway only applies to PSTN accounts; only SIMS have subscription ids. 362 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone); 363 364 Intent intent; 365 String notificationText; 366 if (TextUtils.isEmpty(vmNumber)) { 367 notificationText = mContext.getString( 368 R.string.notification_voicemail_no_vm_number); 369 370 // If the voicemail number if unknown, instead of calling voicemail, take the user 371 // to the voicemail settings. 372 notificationText = mContext.getString( 373 R.string.notification_voicemail_no_vm_number); 374 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL); 375 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId); 376 intent.setClass(mContext, VoicemailSettingsActivity.class); 377 } else { 378 if (mTelephonyManager.getPhoneCount() > 1) { 379 notificationText = subInfo.getDisplayName().toString(); 380 } else { 381 notificationText = String.format( 382 mContext.getString(R.string.notification_voicemail_text_format), 383 PhoneNumberUtils.formatNumber(vmNumber)); 384 } 385 intent = new Intent( 386 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", 387 null)); 388 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 389 } 390 391 PendingIntent pendingIntent = 392 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0); 393 Uri ringtoneUri = null; 394 395 if (enableNotificationSound) { 396 ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone); 397 } 398 399 Resources res = mContext.getResources(); 400 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId( 401 mPhone.getSubId()); 402 Notification.Builder builder = new Notification.Builder(mContext); 403 builder.setSmallIcon(resId) 404 .setWhen(System.currentTimeMillis()) 405 .setColor(subInfo.getIconTint()) 406 .setContentTitle(notificationTitle) 407 .setContentText(notificationText) 408 .setContentIntent(pendingIntent) 409 .setSound(ringtoneUri) 410 .setColor(res.getColor(R.color.dialer_theme_color)) 411 .setOngoing(carrierConfig.getBoolean( 412 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL)); 413 414 if (VoicemailNotificationSettingsUtil.isVibrationEnabled(phone)) { 415 builder.setDefaults(Notification.DEFAULT_VIBRATE); 416 } 417 418 final Notification notification = builder.build(); 419 List<UserInfo> users = mUserManager.getUsers(true); 420 for (int i = 0; i < users.size(); i++) { 421 final UserInfo user = users.get(i); 422 final UserHandle userHandle = user.getUserHandle(); 423 if (!mUserManager.hasUserRestriction( 424 UserManager.DISALLOW_OUTGOING_CALLS, userHandle) 425 && !user.isManagedProfile()) { 426 mNotificationManager.notifyAsUser( 427 Integer.toString(subId) /* tag */, 428 VOICEMAIL_NOTIFICATION, 429 notification, 430 userHandle); 431 } 432 } 433 } else { 434 mNotificationManager.cancelAsUser( 435 Integer.toString(subId) /* tag */, 436 VOICEMAIL_NOTIFICATION, 437 UserHandle.ALL); 438 } 439 } 440 441 /** 442 * Updates the message call forwarding indicator notification. 443 * 444 * @param visible true if there are messages waiting 445 */ updateCfi(int subId, boolean visible)446 /* package */ void updateCfi(int subId, boolean visible) { 447 if (DBG) log("updateCfi(): " + visible); 448 if (visible) { 449 // If Unconditional Call Forwarding (forward all calls) for VOICE 450 // is enabled, just show a notification. We'll default to expanded 451 // view for now, so the there is less confusion about the icon. If 452 // it is deemed too weird to have CF indications as expanded views, 453 // then we'll flip the flag back. 454 455 // TODO: We may want to take a look to see if the notification can 456 // display the target to forward calls to. This will require some 457 // effort though, since there are multiple layers of messages that 458 // will need to propagate that information. 459 460 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId); 461 if (subInfo == null) { 462 Log.w(LOG_TAG, "Found null subscription info for: " + subId); 463 return; 464 } 465 466 String notificationTitle; 467 if (mTelephonyManager.getPhoneCount() > 1) { 468 notificationTitle = subInfo.getDisplayName().toString(); 469 } else { 470 notificationTitle = mContext.getString(R.string.labelCF); 471 } 472 473 Notification.Builder builder = new Notification.Builder(mContext) 474 .setSmallIcon(R.drawable.stat_sys_phone_call_forward) 475 .setColor(subInfo.getIconTint()) 476 .setContentTitle(notificationTitle) 477 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator)) 478 .setShowWhen(false) 479 .setOngoing(true); 480 481 Intent intent = new Intent(Intent.ACTION_MAIN); 482 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 483 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting"); 484 SubscriptionInfoHelper.addExtrasToIntent( 485 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId)); 486 PendingIntent contentIntent = 487 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0); 488 489 List<UserInfo> users = mUserManager.getUsers(true); 490 for (int i = 0; i < users.size(); i++) { 491 final UserInfo user = users.get(i); 492 if (user.isManagedProfile()) { 493 continue; 494 } 495 UserHandle userHandle = user.getUserHandle(); 496 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null); 497 mNotificationManager.notifyAsUser( 498 Integer.toString(subId) /* tag */, 499 CALL_FORWARD_NOTIFICATION, 500 builder.build(), 501 userHandle); 502 } 503 } else { 504 mNotificationManager.cancelAsUser( 505 Integer.toString(subId) /* tag */, 506 CALL_FORWARD_NOTIFICATION, 507 UserHandle.ALL); 508 } 509 } 510 511 /** 512 * Shows the "data disconnected due to roaming" notification, which 513 * appears when you lose data connectivity because you're roaming and 514 * you have the "data roaming" feature turned off. 515 */ showDataDisconnectedRoaming()516 /* package */ void showDataDisconnectedRoaming() { 517 if (DBG) log("showDataDisconnectedRoaming()..."); 518 519 // "Mobile network settings" screen / dialog 520 Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class); 521 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); 522 523 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message); 524 525 final Notification.Builder builder = new Notification.Builder(mContext) 526 .setSmallIcon(android.R.drawable.stat_sys_warning) 527 .setContentTitle(mContext.getText(R.string.roaming)) 528 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color)) 529 .setContentText(contentText); 530 531 List<UserInfo> users = mUserManager.getUsers(true); 532 for (int i = 0; i < users.size(); i++) { 533 final UserInfo user = users.get(i); 534 if (user.isManagedProfile()) { 535 continue; 536 } 537 UserHandle userHandle = user.getUserHandle(); 538 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null); 539 final Notification notif = 540 new Notification.BigTextStyle(builder).bigText(contentText).build(); 541 mNotificationManager.notifyAsUser( 542 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle); 543 } 544 } 545 546 /** 547 * Turns off the "data disconnected due to roaming" notification. 548 */ hideDataDisconnectedRoaming()549 /* package */ void hideDataDisconnectedRoaming() { 550 if (DBG) log("hideDataDisconnectedRoaming()..."); 551 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION); 552 } 553 554 /** 555 * Display the network selection "no service" notification 556 * @param operator is the numeric operator number 557 */ showNetworkSelection(String operator)558 private void showNetworkSelection(String operator) { 559 if (DBG) log("showNetworkSelection(" + operator + ")..."); 560 561 Notification.Builder builder = new Notification.Builder(mContext) 562 .setSmallIcon(android.R.drawable.stat_sys_warning) 563 .setContentTitle(mContext.getString(R.string.notification_network_selection_title)) 564 .setContentText( 565 mContext.getString(R.string.notification_network_selection_text, operator)) 566 .setShowWhen(false) 567 .setOngoing(true); 568 569 // create the target network operators settings intent 570 Intent intent = new Intent(Intent.ACTION_MAIN); 571 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 572 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 573 // Use NetworkSetting to handle the selection intent 574 intent.setComponent(new ComponentName("com.android.phone", 575 "com.android.phone.NetworkSetting")); 576 intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, mPhone.getSubId()); 577 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); 578 579 List<UserInfo> users = mUserManager.getUsers(true); 580 for (int i = 0; i < users.size(); i++) { 581 final UserInfo user = users.get(i); 582 if (user.isManagedProfile()) { 583 continue; 584 } 585 UserHandle userHandle = user.getUserHandle(); 586 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null); 587 mNotificationManager.notifyAsUser( 588 null /* tag */, 589 SELECTED_OPERATOR_FAIL_NOTIFICATION, 590 builder.build(), 591 userHandle); 592 } 593 } 594 595 /** 596 * Turn off the network selection "no service" notification 597 */ cancelNetworkSelection()598 private void cancelNetworkSelection() { 599 if (DBG) log("cancelNetworkSelection()..."); 600 mNotificationManager.cancelAsUser( 601 null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL); 602 } 603 604 /** 605 * Update notification about no service of user selected operator 606 * 607 * @param serviceState Phone service state 608 */ updateNetworkSelection(int serviceState)609 void updateNetworkSelection(int serviceState) { 610 if (TelephonyCapabilities.supportsNetworkSelection(mPhone)) { 611 int subId = mPhone.getSubId(); 612 if (SubscriptionManager.isValidSubscriptionId(subId)) { 613 // get the shared preference of network_selection. 614 // empty is auto mode, otherwise it is the operator alpha name 615 // in case there is no operator name, check the operator numeric 616 SharedPreferences sp = 617 PreferenceManager.getDefaultSharedPreferences(mContext); 618 String networkSelection = 619 sp.getString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId, ""); 620 if (TextUtils.isEmpty(networkSelection)) { 621 networkSelection = 622 sp.getString(PhoneBase.NETWORK_SELECTION_KEY + subId, ""); 623 } 624 625 if (DBG) log("updateNetworkSelection()..." + "state = " + 626 serviceState + " new network " + networkSelection); 627 628 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE 629 && !TextUtils.isEmpty(networkSelection)) { 630 if (!mSelectedUnavailableNotify) { 631 showNetworkSelection(networkSelection); 632 mSelectedUnavailableNotify = true; 633 } 634 } else { 635 if (mSelectedUnavailableNotify) { 636 cancelNetworkSelection(); 637 mSelectedUnavailableNotify = false; 638 } 639 } 640 } else { 641 if (DBG) log("updateNetworkSelection()..." + "state = " + 642 serviceState + " not updating network due to invalid subId " + subId); 643 } 644 } 645 } 646 postTransientNotification(int notifyId, CharSequence msg)647 /* package */ void postTransientNotification(int notifyId, CharSequence msg) { 648 if (mToast != null) { 649 mToast.cancel(); 650 } 651 652 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG); 653 mToast.show(); 654 } 655 log(String msg)656 private void log(String msg) { 657 Log.d(LOG_TAG, msg); 658 } 659 } 660