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