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