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