• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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