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