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