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