• 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.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.BroadcastOptions;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.StatusBarManager;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.SharedPreferences;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.content.res.Resources;
35 import android.net.Uri;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.PersistableBundle;
39 import android.os.SystemClock;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.preference.PreferenceManager;
44 import android.provider.ContactsContract.PhoneLookup;
45 import android.provider.Settings;
46 import android.telecom.PhoneAccount;
47 import android.telecom.PhoneAccountHandle;
48 import android.telecom.TelecomManager;
49 import android.telephony.CarrierConfigManager;
50 import android.telephony.PhoneNumberUtils;
51 import android.telephony.RadioAccessFamily;
52 import android.telephony.ServiceState;
53 import android.telephony.SubscriptionInfo;
54 import android.telephony.SubscriptionManager;
55 import android.telephony.TelephonyManager;
56 import android.text.TextUtils;
57 import android.util.ArrayMap;
58 import android.util.Log;
59 import android.util.SparseArray;
60 import android.widget.Toast;
61 
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.telephony.Phone;
64 import com.android.internal.telephony.PhoneFactory;
65 import com.android.internal.telephony.RILConstants;
66 import com.android.internal.telephony.TelephonyCapabilities;
67 import com.android.internal.telephony.flags.FeatureFlags;
68 import com.android.internal.telephony.flags.FeatureFlagsImpl;
69 import com.android.internal.telephony.util.NotificationChannelController;
70 import com.android.phone.settings.VoicemailSettingsActivity;
71 
72 import java.util.ArrayList;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Objects;
77 import java.util.Set;
78 
79 /**
80  * NotificationManager-related utility code for the Phone app.
81  *
82  * This is a singleton object which acts as the interface to the
83  * framework's NotificationManager, and is used to display status bar
84  * icons and control other status bar-related behavior.
85  *
86  * @see PhoneGlobals.notificationMgr
87  */
88 public class NotificationMgr {
89     private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
90     private static final boolean DBG =
91             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
92     // Do not check in with VDBG = true, since that may write PII to the system log.
93     private static final boolean VDBG = false;
94 
95     private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
96             "mwi_should_check_vvm_configuration_state_";
97 
98     // notification types
99     static final int MMI_NOTIFICATION = 1;
100     static final int NETWORK_SELECTION_NOTIFICATION = 2;
101     static final int VOICEMAIL_NOTIFICATION = 3;
102     static final int CALL_FORWARD_NOTIFICATION = 4;
103     static final int DATA_ROAMING_NOTIFICATION = 5;
104     static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
105     static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
106 
107     // Event for network selection notification.
108     private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
109 
110     private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
111     private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
112 
113     private static final int STATE_UNKNOWN_SERVICE = -1;
114 
115     private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
116 
117     /**
118      * Grant recipients of new voicemail broadcasts a 10sec allowlist so they can start a background
119      * service to do VVM processing.
120      */
121     private final long VOICEMAIL_ALLOW_LIST_DURATION_MILLIS = 10000L;
122 
123     /** The singleton NotificationMgr instance. */
124     private static NotificationMgr sInstance;
125 
126     private PhoneGlobals mApp;
127 
128     private Context mContext;
129     private StatusBarManager mStatusBarManager;
130     private UserManager mUserManager;
131     private Toast mToast;
132     private SubscriptionManager mSubscriptionManager;
133     private TelecomManager mTelecomManager;
134     private TelephonyManager mTelephonyManager;
135 
136     // used to track the notification of selected network unavailable, per subscription id.
137     private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
138 
139     // used to track the notification of limited sim function under dual sim, per subscription id.
140     private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
141 
142     // used to track whether the message waiting indicator is visible, per subscription id.
143     private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
144     // used to track the last broadcast sent to the dialer about the MWI, per sub id.
145     private ArrayMap<Integer, Integer> mLastMwiCountSent = new ArrayMap<Integer, Integer>();
146 
147     // those flags are used to track whether to show network selection notification or not.
148     private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
149     private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
150     private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
151     // maps each subId to selected network operator name.
152     private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
153 
154     // feature flags
155     private final FeatureFlags mFeatureFlags;
156 
157     private final Handler mHandler = new Handler() {
158         @Override
159         public void handleMessage(Message msg) {
160             switch (msg.what) {
161                 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
162                     int subId = (int) msg.obj;
163                     TelephonyManager telephonyManager =
164                             mTelephonyManager.createForSubscriptionId(subId);
165                     if (telephonyManager.getServiceState() != null) {
166                         shouldShowNotification(telephonyManager.getServiceState().getState(),
167                                 subId);
168                     }
169                     break;
170             }
171         }
172     };
173 
174     /**
175      * Private constructor (this is a singleton).
176      * @see #init(PhoneGlobals)
177      */
178     @VisibleForTesting
NotificationMgr(PhoneGlobals app)179     /* package */ NotificationMgr(PhoneGlobals app) {
180         mApp = app;
181         mContext = app;
182         mStatusBarManager =
183                 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
184         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
185         mSubscriptionManager = SubscriptionManager.from(mContext);
186         mTelecomManager = app.getSystemService(TelecomManager.class);
187         mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
188         mFeatureFlags = new FeatureFlagsImpl();
189     }
190 
191     /**
192      * Initialize the singleton NotificationMgr instance.
193      *
194      * This is only done once, at startup, from PhoneApp.onCreate().
195      * From then on, the NotificationMgr instance is available via the
196      * PhoneApp's public "notificationMgr" field, which is why there's no
197      * getInstance() method here.
198      */
init(PhoneGlobals app)199     /* package */ static NotificationMgr init(PhoneGlobals app) {
200         synchronized (NotificationMgr.class) {
201             if (sInstance == null) {
202                 sInstance = new NotificationMgr(app);
203             } else {
204                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
205             }
206             return sInstance;
207         }
208     }
209 
210     /** The projection to use when querying the phones table */
211     static final String[] PHONES_PROJECTION = new String[] {
212         PhoneLookup.NUMBER,
213         PhoneLookup.DISPLAY_NAME,
214         PhoneLookup._ID
215     };
216 
217     /**
218      * Re-creates the message waiting indicator (voicemail) notification if it is showing.  Used to
219      * refresh the voicemail intent on the indicator when the user changes it via the voicemail
220      * settings screen.  The voicemail notification sound is suppressed.
221      *
222      * @param subId The subscription Id.
223      */
refreshMwi(int subId)224     /* package */ void refreshMwi(int subId) {
225         // In a single-sim device, subId can be -1 which means "no sub id".  In this case we will
226         // reference the single subid stored in the mMwiVisible map.
227         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
228             if (mMwiVisible.keySet().size() == 1) {
229                 Set<Integer> keySet = mMwiVisible.keySet();
230                 Iterator<Integer> keyIt = keySet.iterator();
231                 if (!keyIt.hasNext()) {
232                     return;
233                 }
234                 subId = keyIt.next();
235             }
236         }
237         if (mMwiVisible.containsKey(subId)) {
238             boolean mwiVisible = mMwiVisible.get(subId);
239             if (mwiVisible) {
240                 mApp.notifier.updatePhoneStateListeners(true);
241             }
242         }
243     }
244 
setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled)245     public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
246         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
247             Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
248                     + subId);
249             return;
250         }
251 
252         PreferenceManager.getDefaultSharedPreferences(mContext).edit()
253                 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
254                 .apply();
255     }
256 
shouldCheckVisualVoicemailConfigurationForMwi(int subId)257     private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
258         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
259             Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
260             return true;
261         }
262         return PreferenceManager
263                 .getDefaultSharedPreferences(mContext)
264                 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
265     }
266     /**
267      * Updates the message waiting indicator (voicemail) notification.
268      *
269      * @param visible true if there are messages waiting
270      */
updateMwi(int subId, boolean visible)271     /* package */ void updateMwi(int subId, boolean visible) {
272         updateMwi(subId, visible, false /* isRefresh */);
273     }
274 
275     /**
276      * Updates the message waiting indicator (voicemail) notification.
277      * See also {@link CallNotifier#updatePhoneStateListeners(boolean)} for more background.
278      * Okay, lets get started; time for a code adventure.
279      * This method (unfortunately) serves double-duty for updating the message waiting indicator
280      * when either: the "subscription info" changes (could be due to the carrier name loading, the
281      * current phase of the moon, etc) -- this is considered a "refresh"; or due to a direct signal
282      * from the network that indeed the voicemail indicator has changed state.  Note that although
283      * {@link android.telephony.ims.feature.MmTelFeature.Listener#onVoiceMessageCountUpdate(int)}
284      * gives us an actual COUNT of the messages, that information is unfortunately lost in the
285      * current design -- that's a design issue left for another day.  Note; it is ALSO possible to
286      * get updates through SMS via {@code GsmInboundSmsHandler#updateMessageWaitingIndicator(int)},
287      * and that seems to generally not include a count.
288      * <p>
289      * There are two ways the message waiting indicator can be notified to the user; either directly
290      * here by posting a notification, or via the default dialer.
291      * <p>
292      * The {@code isRefresh} == false case is pretty intuitive.  The network told us something
293      * changed, so we should notify the user.
294      * <p>
295      * The {@code isRefresh} == true case is unfortunately not very intuitive.  While the device is
296      * booting up, we'll actually get a callback from the ImsService telling us the state of the
297      * MWI indication, BUT at that point in time it's possible the sim records won't have loaded so
298      * code below will return early -- the voicemail notification is supposed to include the user's
299      * voicemail number, so we have to defer posting the notification or notifying the dialer until
300      * later on.  That's where the refreshes are handy; once the voicemail number is known, we'll
301      * get a refresh and have an opportunity to notify the user at this point.
302      * @param subId the subId to update where {@code isRefresh} is false,
303      *              or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if {@code isRefresh} is
304      *              true.
305      * @param visible {@code true} if there are messages waiting, or {@code false} if there are not.
306      * @param isRefresh {@code true} if this is a refresh triggered by a subscription change,
307      *                              {@code false} if this is an update based on actual network
308      *                              signalling.
309      */
updateMwi(int subId, boolean visible, boolean isRefresh)310     void updateMwi(int subId, boolean visible, boolean isRefresh) {
311         if (!PhoneGlobals.sVoiceCapable) {
312             // Do not show the message waiting indicator on devices which are not voice capable.
313             // These events *should* be blocked at the telephony layer for such devices.
314             Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
315             return;
316         }
317 
318         Phone phone = PhoneGlobals.getPhone(subId);
319         // --
320         // Note: This is all done just so that we can have a better log of what is going on here.
321         // The state changes are unfortunately not so intuitive.
322         boolean wasPrevStateKnown = mMwiVisible.containsKey(subId);
323         boolean wasVisible = wasPrevStateKnown && mMwiVisible.get(subId);
324         mMwiVisible.put(subId, visible);
325         boolean mwiStateChanged = !wasPrevStateKnown || wasVisible != visible;
326         // --
327 
328         Log.i(LOG_TAG, "updateMwi(): subId:" + subId + " isRefresh:" + isRefresh + " state:"
329                 + (wasPrevStateKnown ? (wasVisible ? "Y" : "N") : "unset") + "->"
330                 + (visible ? "Y" : "N")
331                 + " (changed:" + (mwiStateChanged ? "Y" : "N") + ")");
332 
333         if (visible) {
334             if (phone == null) {
335                 Log.w(LOG_TAG, "Found null phone for: " + subId);
336                 return;
337             }
338 
339             SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
340             if (subInfo == null) {
341                 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
342                 return;
343             }
344 
345             int resId = android.R.drawable.stat_notify_voicemail;
346             if (mTelephonyManager.getPhoneCount() > 1) {
347                 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
348                         : R.drawable.stat_notify_voicemail_sub2;
349             }
350 
351             // This Notification can get a lot fancier once we have more
352             // information about the current voicemail messages.
353             // (For example, the current voicemail system can't tell
354             // us the caller-id or timestamp of a message, or tell us the
355             // message count.)
356 
357             // But for now, the UI is ultra-simple: if the MWI indication
358             // is supposed to be visible, just show a single generic
359             // notification.
360 
361             String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
362             String vmNumber = phone.getVoiceMailNumber();
363             if (DBG) log("- got vm number: '" + vmNumber + "'");
364 
365             // The voicemail number may be null because:
366             //   (1) This phone has no voicemail number.
367             //   (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
368             //       happen when the device first boots if we get a MWI notification when we
369             //       register on the network before the SIM has loaded. In this case, the
370             //       SubscriptionListener in CallNotifier will update this once the SIM is loaded.
371             if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
372                 Log.i(LOG_TAG, "updateMwi - Null vm number: SIM records not loaded (yet)...");
373                 return;
374             }
375 
376             // Pay attention here; vmCount is an Integer, not an int.  This is because:
377             // vmCount == null - means there are voicemail messages waiting.
378             // vmCount == 0 - means there are no voicemail messages waiting.
379             // vmCount > 0 - means there are a specific number of voicemail messages waiting.
380             // Awesome.
381             Integer vmCount = null;
382 
383             // TODO: This should be revisited; in the IMS case, the network tells us a count, so
384             // it is strange to stash it and then retrieve it here instead of just passing it.
385             if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
386                 vmCount = phone.getVoiceMessageCount();
387                 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
388                 notificationTitle = String.format(titleFormat, vmCount);
389             }
390 
391             // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
392             PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
393 
394             Intent intent;
395             String notificationText;
396             boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
397 
398             if (isSettingsIntent) {
399                 // If the voicemail number if unknown, instead of calling voicemail, take the user
400                 // to the voicemail settings.
401                 notificationText = mContext.getString(
402                         R.string.notification_voicemail_no_vm_number);
403                 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
404                 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
405                 intent.setClass(mContext, VoicemailSettingsActivity.class);
406             } else {
407                 if (mTelephonyManager.getPhoneCount() > 1) {
408                     notificationText = subInfo.getDisplayName().toString();
409                 } else {
410                     notificationText = String.format(
411                             mContext.getString(R.string.notification_voicemail_text_format),
412                             PhoneNumberUtils.formatNumber(vmNumber));
413                 }
414                 intent = new Intent(
415                         Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
416                                 null));
417                 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
418             }
419             PendingIntent pendingIntent;
420             UserHandle subAssociatedUserHandle =
421                     mSubscriptionManager.getSubscriptionUserHandle(subId);
422             if (subAssociatedUserHandle == null) {
423                 pendingIntent = PendingIntent.getActivity(mContext, subId /* requestCode */, intent,
424                         PendingIntent.FLAG_IMMUTABLE);
425             } else {
426                 pendingIntent = PendingIntent.getActivityAsUser(mContext, subId /* requestCode */,
427                         intent, PendingIntent.FLAG_IMMUTABLE, null, subAssociatedUserHandle);
428             }
429 
430             Resources res = mContext.getResources();
431             PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
432                     subId);
433             Notification.Builder builder = new Notification.Builder(mContext);
434             builder.setSmallIcon(resId)
435                     .setWhen(System.currentTimeMillis())
436                     .setColor(subInfo.getIconTint())
437                     .setContentTitle(notificationTitle)
438                     .setContentText(notificationText)
439                     .setContentIntent(pendingIntent)
440                     .setColor(res.getColor(R.color.dialer_theme_color))
441                     .setOngoing(carrierConfig.getBoolean(
442                             CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
443                     .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
444                     .setOnlyAlertOnce(isRefresh);
445 
446             final Notification notification = builder.build();
447             List<UserHandle> users = getUsersExcludeDying();
448             for (UserHandle userHandle : users) {
449                 boolean isProfile = mUserManager.isProfile(userHandle.getIdentifier());
450                 if (!hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
451                         && (userHandle.equals(subAssociatedUserHandle)
452                             || (subAssociatedUserHandle == null && !isProfile))
453                         && !maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount,
454                         vmNumber, pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
455                     Log.i(LOG_TAG, "updateMwi: notify userHandle=" + userHandle);
456                     notifyAsUser(
457                             Integer.toString(subId) /* tag */,
458                             VOICEMAIL_NOTIFICATION,
459                             notification,
460                             userHandle);
461                 }
462             }
463         } else {
464             UserHandle subAssociatedUserHandle =
465                     mSubscriptionManager.getSubscriptionUserHandle(subId);
466             List<UserHandle> users = getUsersExcludeDying();
467             for (UserHandle userHandle : users) {
468                 boolean isProfile = mUserManager.isProfile(userHandle.getIdentifier());
469                 if (!hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
470                         && (userHandle.equals(subAssociatedUserHandle)
471                             || (subAssociatedUserHandle == null && !isProfile))
472                         && !maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
473                         false, userHandle, isRefresh)) {
474                     Log.i(LOG_TAG, "notifyMwi: cancel userHandle=" + userHandle);
475                     cancelAsUser(
476                             Integer.toString(subId) /* tag */,
477                             VOICEMAIL_NOTIFICATION,
478                             userHandle);
479                 }
480             }
481         }
482     }
483 
getUsersExcludeDying()484     private List<UserHandle> getUsersExcludeDying() {
485         long[] serialNumbersOfUsers =
486                 mUserManager.getSerialNumbersOfUsers(/* excludeDying= */ true);
487         List<UserHandle> users = new ArrayList<>(serialNumbersOfUsers.length);
488         for (long serialNumber : serialNumbersOfUsers) {
489             UserHandle userHandle = mUserManager.getUserForSerialNumber(serialNumber);
490             if (userHandle != null) {
491                 users.add(userHandle);
492             }
493         }
494         return users;
495     }
496 
hasUserRestriction(String restrictionKey, UserHandle userHandle)497     private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
498         final List<UserManager.EnforcingUser> sources = mUserManager
499                 .getUserRestrictionSources(restrictionKey, userHandle);
500         return (sources != null && !sources.isEmpty());
501     }
502 
503     /**
504      * Sends a broadcast with the voicemail notification information to the default dialer. This
505      * method is also used to indicate to the default dialer when to clear the
506      * notification. A pending intent can be passed to the default dialer to indicate an action to
507      * be taken as it would by a notification produced in this class.
508      * @param phone The phone the notification is sent from
509      * @param count The number of pending voicemail messages to indicate on the notification. A
510      *              Value of 0 is passed here to indicate that the notification should be cleared.
511      * @param number The voicemail phone number if specified.
512      * @param pendingIntent The intent that should be passed as the action to be taken.
513      * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
514      *                         otherwise, {@code false} to indicate the intent launches voicemail.
515      * @param userHandle The user to receive the notification. Each user can have their own default
516      *                   dialer.
517      * @return {@code true} if the default was notified of the notification.
518      */
maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count, String number, PendingIntent pendingIntent, boolean isSettingsIntent, UserHandle userHandle, boolean isRefresh)519     private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
520             String number, PendingIntent pendingIntent, boolean isSettingsIntent,
521             UserHandle userHandle, boolean isRefresh) {
522 
523         if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
524             int subId = phone.getSubId();
525             // We want to determine if the count of voicemails that we notified to the dialer app
526             // has changed or not.  mLastMwiCountSent will initially contain no entry for a subId
527             // meaning no count was ever sent to dialer.  The previous count is an Integer (not int)
528             // because the caller of maybeSendVoicemailNotificationUsingDefaultDialer will either
529             // send an instance of Integer with an actual number or "null" if the count isn't known.
530             // See the docs on updateMwi to get more flavor on this lovely logic.
531             // The end result here is we want to know if the "count" we last sent to the dialer for
532             // a sub has changed or not; this will play into whether we want to actually send the
533             // broadcast or not.
534             boolean wasCountSentYet = mLastMwiCountSent.containsKey(subId);
535             Integer previousCount = wasCountSentYet ? mLastMwiCountSent.get(subId) : null;
536             boolean didCountChange = !wasCountSentYet || !Objects.equals(previousCount, count);
537             mLastMwiCountSent.put(subId, count);
538 
539             Log.i(LOG_TAG,
540                     "maybeSendVoicemailNotificationUsingDefaultDialer: count: " + (wasCountSentYet
541                             ? previousCount : "undef") + "->" + count + " (changed="
542                             + didCountChange + ")");
543 
544             Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
545 
546             /**
547              * isRefresh == true means that we're rebroadcasting because of an
548              * onSubscriptionsChanged callback -- that happens a LOT at boot up.  isRefresh == false
549              * happens when TelephonyCallback#onMessageWaitingIndicatorChanged is triggered in
550              * CallNotifier.  It's important to note that that count may either be an actual number,
551              * or "we don't know" because the modem doesn't know the actual count.  Hence anytime
552              * TelephonyCallback#onMessageWaitingIndicatorChanged occurs, we have to sent the
553              * broadcast even if the count didn't actually change.
554              */
555             if (!didCountChange && isRefresh) {
556                 Log.i(LOG_TAG, "maybeSendVoicemailNotificationUsingDefaultDialer: skip bcast to:"
557                         + intent.getPackage() + ", user:" + userHandle);
558                 // It's "technically" being sent through the dialer, but we just skipped that so
559                 // still return true so we don't post a notification.
560                 return true;
561             }
562 
563             intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
564             intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
565             intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
566                     PhoneUtils.makePstnPhoneAccountHandle(phone));
567             intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
568             if (count != null) {
569                 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
570             }
571 
572             // Additional information about the voicemail notification beyond the count is only
573             // present when the count not specified or greater than 0. The value of 0 represents
574             // clearing the notification, which does not require additional information.
575             if (count == null || count > 0) {
576                 if (!TextUtils.isEmpty(number)) {
577                     intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
578                 }
579 
580                 if (pendingIntent != null) {
581                     intent.putExtra(isSettingsIntent
582                             ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
583                             : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
584                             pendingIntent);
585                 }
586             }
587 
588             BroadcastOptions bopts = BroadcastOptions.makeBasic();
589             bopts.setTemporaryAppWhitelistDuration(VOICEMAIL_ALLOW_LIST_DURATION_MILLIS);
590 
591             Log.i(LOG_TAG, "maybeSendVoicemailNotificationUsingDefaultDialer: send via Dialer:"
592                     + intent.getPackage() + ", user:" + userHandle);
593             mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE,
594                     bopts.toBundle());
595             return true;
596         }
597         Log.i(LOG_TAG, "maybeSendVoicemailNotificationUsingDefaultDialer: not using dialer ; user:"
598                 + userHandle);
599 
600         return false;
601     }
602 
getShowVoicemailIntentForDefaultDialer(UserHandle userHandle)603     private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
604         String dialerPackage = mContext.getSystemService(TelecomManager.class)
605                 .getDefaultDialerPackage(userHandle);
606         return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
607                 .setPackage(dialerPackage);
608     }
609 
shouldManageNotificationThroughDefaultDialer(UserHandle userHandle)610     private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
611         Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
612         if (intent == null) {
613             return false;
614         }
615 
616         List<ResolveInfo> receivers;
617         if (mFeatureFlags.hsumPackageManager()) {
618             receivers = mContext.createContextAsUser(userHandle, 0)
619                     .getPackageManager().queryBroadcastReceivers(intent, 0);
620         } else {
621             receivers = mContext.getPackageManager()
622                     .queryBroadcastReceivers(intent, 0);
623         }
624         return receivers.size() > 0;
625     }
626 
627     /**
628      * Updates the message call forwarding indicator notification.
629      *
630      * @param visible true if call forwarding enabled
631      */
632 
updateCfi(int subId, boolean visible)633      /* package */ void updateCfi(int subId, boolean visible) {
634         updateCfi(subId, visible, false /* isRefresh */);
635     }
636 
637     /**
638      * Updates the message call forwarding indicator notification.
639      *
640      * @param visible true if call forwarding enabled
641      */
updateCfi(int subId, boolean visible, boolean isRefresh)642     /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
643         logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
644         if (visible) {
645             // If Unconditional Call Forwarding (forward all calls) for VOICE
646             // is enabled, just show a notification.  We'll default to expanded
647             // view for now, so the there is less confusion about the icon.  If
648             // it is deemed too weird to have CF indications as expanded views,
649             // then we'll flip the flag back.
650 
651             // TODO: We may want to take a look to see if the notification can
652             // display the target to forward calls to.  This will require some
653             // effort though, since there are multiple layers of messages that
654             // will need to propagate that information.
655 
656             SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
657             if (subInfo == null) {
658                 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
659                 return;
660             }
661 
662             String notificationTitle;
663             int resId = R.drawable.stat_sys_phone_call_forward;
664             if (mTelephonyManager.getPhoneCount() > 1) {
665                 int slotId = SubscriptionManager.getSlotIndex(subId);
666                 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
667                         : R.drawable.stat_sys_phone_call_forward_sub2;
668                 if (subInfo.getDisplayName() != null) {
669                     notificationTitle = subInfo.getDisplayName().toString();
670                 } else {
671                     notificationTitle = mContext.getString(R.string.labelCF);
672                 }
673             } else {
674                 notificationTitle = mContext.getString(R.string.labelCF);
675             }
676 
677             Notification.Builder builder = new Notification.Builder(mContext)
678                     .setSmallIcon(resId)
679                     .setColor(subInfo.getIconTint())
680                     .setContentTitle(notificationTitle)
681                     .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
682                     .setShowWhen(false)
683                     .setOngoing(true)
684                     .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
685                     .setOnlyAlertOnce(isRefresh);
686 
687             Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
688             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
689             SubscriptionInfoHelper.addExtrasToIntent(
690                     intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
691             builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
692                     intent, PendingIntent.FLAG_IMMUTABLE));
693             notifyAsUser(
694                     Integer.toString(subId) /* tag */,
695                     CALL_FORWARD_NOTIFICATION,
696                     builder.build(),
697                     UserHandle.ALL);
698         } else {
699             List<UserHandle> users = getUsersExcludeDying();
700             for (UserHandle user : users) {
701                 if (mUserManager.isManagedProfile(user.getIdentifier())) {
702                     continue;
703                 }
704                 cancelAsUser(
705                         Integer.toString(subId) /* tag */,
706                         CALL_FORWARD_NOTIFICATION,
707                         user);
708             }
709         }
710     }
711 
712     /**
713      * Shows either:
714      * 1) the "Data roaming is on" notification, which
715      * appears when you're roaming and you have the "data roaming" feature turned on for the
716      * given {@code subId}.
717      * or
718      * 2) the "data disconnected due to roaming" notification, which
719      * appears when you lose data connectivity because you're roaming and
720      * you have the "data roaming" feature turned off for the given {@code subId}.
721      * @param subId which subscription it's notifying about.
722      * @param roamingOn whether currently roaming is on or off. If true, we show notification
723      *                  1) above; else we show notification 2).
724      */
showDataRoamingNotification(int subId, boolean roamingOn)725     /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
726         if (DBG) {
727             log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
728                     + " on subId " + subId);
729         }
730 
731         // "Mobile network settings" screen / dialog
732         Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
733         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
734         PendingIntent contentIntent = PendingIntent.getActivity(
735                 mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE);
736 
737         CharSequence contentTitle = mContext.getText(roamingOn
738                 ? R.string.roaming_on_notification_title
739                 : R.string.roaming_notification_title);
740         CharSequence contentText = mContext.getText(roamingOn
741                 ? R.string.roaming_enabled_message
742                 : R.string.roaming_reenable_message);
743 
744         final Notification.Builder builder = new Notification.Builder(mContext)
745                 .setSmallIcon(android.R.drawable.stat_sys_warning)
746                 .setContentTitle(contentTitle)
747                 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
748                 .setContentText(contentText)
749                 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
750                 .setContentIntent(contentIntent);
751         final Notification notif =
752                 new Notification.BigTextStyle(builder).bigText(contentText).build();
753         notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
754     }
755 
notifyAsUser(String tag, int id, Notification notification, UserHandle user)756     private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
757         try {
758             Context contextForUser =
759                     mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
760             NotificationManager notificationManager =
761                     (NotificationManager) contextForUser.getSystemService(
762                             Context.NOTIFICATION_SERVICE);
763             notificationManager.notify(tag, id, notification);
764         } catch (PackageManager.NameNotFoundException e) {
765             Log.e(LOG_TAG, "unable to notify for user " + user);
766             e.printStackTrace();
767         }
768     }
769 
cancelAsUser(String tag, int id, UserHandle user)770     private void cancelAsUser(String tag, int id, UserHandle user) {
771         try {
772             Context contextForUser =
773                     mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
774             NotificationManager notificationManager =
775                     (NotificationManager) contextForUser.getSystemService(
776                             Context.NOTIFICATION_SERVICE);
777             notificationManager.cancel(tag, id);
778         } catch (PackageManager.NameNotFoundException e) {
779             Log.e(LOG_TAG, "unable to cancel for user " + user);
780             e.printStackTrace();
781         }
782     }
783 
784     /**
785      * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
786      */
hideDataRoamingNotification()787     /* package */ void hideDataRoamingNotification() {
788         if (DBG) log("hideDataRoamingNotification()...");
789         cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
790     }
791 
792     /**
793      * Shows the "Limited SIM functionality" warning notification, which appears when using a
794      * special carrier under dual sim. limited function applies for DSDS in general when two SIM
795      * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
796      */
showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName)797     public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
798         if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
799                 + " subId: " + subId);
800         if (mLimitedSimFunctionNotify.contains(subId)) {
801             // handle the case that user swipe the notification but condition triggers
802             // frequently which cause the same notification consistently displayed.
803             if (DBG) log("showLimitedSimFunctionWarningNotification, "
804                     + "not display again if already displayed");
805             return;
806         }
807         // Navigate to "Network Selection Settings" which list all subscriptions.
808         PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
809                 new Intent(ACTION_MOBILE_NETWORK_LIST), PendingIntent.FLAG_IMMUTABLE);
810         // Display phone number from the other sub
811         String line1Num = null;
812         SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
813             Context.TELEPHONY_SUBSCRIPTION_SERVICE);
814         List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
815         for (SubscriptionInfo sub : subList) {
816             if (sub.getSubscriptionId() != subId) {
817                 line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
818             }
819         }
820         final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
821             String.format(mContext.getText(
822                 R.string.limited_sim_function_notification_message).toString(), carrierName) :
823             String.format(mContext.getText(
824                 R.string.limited_sim_function_with_phone_num_notification_message).toString(),
825                 carrierName, line1Num);
826         final Notification.Builder builder = new Notification.Builder(mContext)
827                 .setSmallIcon(R.drawable.ic_sim_card)
828                 .setContentTitle(mContext.getText(
829                         R.string.limited_sim_function_notification_title))
830                 .setContentText(contentText)
831                 .setOnlyAlertOnce(true)
832                 .setOngoing(true)
833                 .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
834                 .setContentIntent(contentIntent);
835         final Notification notification = new Notification.BigTextStyle(builder).bigText(
836                 contentText).build();
837 
838         notifyAsUser(Integer.toString(subId),
839                 LIMITED_SIM_FUNCTION_NOTIFICATION,
840                 notification, UserHandle.ALL);
841         mLimitedSimFunctionNotify.add(subId);
842     }
843 
844     /**
845      * Dismiss the "Limited SIM functionality" warning notification for the given subId.
846      */
dismissLimitedSimFunctionWarningNotification(int subId)847     public void dismissLimitedSimFunctionWarningNotification(int subId) {
848         if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
849         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
850             // dismiss all notifications
851             for (int id : mLimitedSimFunctionNotify) {
852                 cancelAsUser(Integer.toString(id),
853                         LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
854             }
855             mLimitedSimFunctionNotify.clear();
856         } else if (mLimitedSimFunctionNotify.contains(subId)) {
857             cancelAsUser(Integer.toString(subId),
858                     LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
859             mLimitedSimFunctionNotify.remove(subId);
860         }
861     }
862 
863     /**
864      * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
865      */
dismissLimitedSimFunctionWarningNotificationForInactiveSubs()866     public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
867         if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
868         // dismiss notification for inactive subscriptions.
869         // handle the corner case that SIM change by SIM refresh doesn't clear the notification
870         // from the old SIM if both old & new SIM configured to display the notification.
871         mLimitedSimFunctionNotify.removeIf(id -> {
872             if (!mSubscriptionManager.isActiveSubId(id)) {
873                 cancelAsUser(Integer.toString(id),
874                         LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
875                 return true;
876             }
877             return false;
878         });
879     }
880 
881     /**
882      * Display the network selection "no service" notification
883      * @param operator is the numeric operator number
884      * @param subId is the subscription ID
885      */
showNetworkSelection(String operator, int subId)886     private void showNetworkSelection(String operator, int subId) {
887         if (DBG) log("showNetworkSelection(" + operator + ")...");
888 
889         if (!TextUtils.isEmpty(operator)) {
890             operator = String.format(" (%s)", operator);
891         }
892         Notification.Builder builder = new Notification.Builder(mContext)
893                 .setSmallIcon(android.R.drawable.stat_sys_warning)
894                 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
895                 .setContentText(
896                         mContext.getString(R.string.notification_network_selection_text, operator))
897                 .setShowWhen(false)
898                 .setOngoing(true)
899                 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
900 
901         // create the target network operators settings intent
902         Intent intent = new Intent(Intent.ACTION_MAIN);
903         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
904                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
905         // Use MobileNetworkSettings to handle the selection intent
906         intent.setComponent(new ComponentName(
907                 mContext.getString(R.string.mobile_network_settings_package),
908                 mContext.getString(R.string.mobile_network_settings_class)));
909         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
910         builder.setContentIntent(
911                 PendingIntent.getActivity(mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE));
912         notifyAsUser(
913                 Integer.toString(subId) /* tag */,
914                 SELECTED_OPERATOR_FAIL_NOTIFICATION,
915                 builder.build(),
916                 UserHandle.ALL);
917         mSelectedUnavailableNotify.put(subId, true);
918     }
919 
920     /**
921      * Turn off the network selection "no service" notification
922      */
cancelNetworkSelection(int subId)923     private void cancelNetworkSelection(int subId) {
924         if (DBG) log("cancelNetworkSelection()...");
925         cancelAsUser(
926                 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
927                 UserHandle.ALL);
928     }
929 
930     /**
931      * Update notification about no service of user selected operator
932      *
933      * @param serviceState Phone service state
934      * @param subId The subscription ID
935      */
updateNetworkSelection(int serviceState, int subId)936     void updateNetworkSelection(int serviceState, int subId) {
937         // for dismissNetworkSelectionNotificationOnSimDisable feature enabled.
938         int phoneId = SubscriptionManager.getPhoneId(subId);
939         Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
940                 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
941         if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
942             if (SubscriptionManager.isValidSubscriptionId(subId)
943                     && mSubscriptionManager.isActiveSubId(subId)) {
944                 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
945                 String selectedNetworkOperatorName =
946                         sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
947                 // get the shared preference of network_selection.
948                 // empty is auto mode, otherwise it is the operator alpha name
949                 // in case there is no operator name, check the operator numeric
950                 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
951                     selectedNetworkOperatorName =
952                             sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
953                 }
954                 boolean isManualSelection;
955                 // if restoring manual selection is controlled by framework, then get network
956                 // selection from shared preference, otherwise get from real network indicators.
957                 boolean restoreSelection = !mContext.getResources().getBoolean(
958                         com.android.internal.R.bool.skip_restoring_network_selection);
959                 if (restoreSelection) {
960                     isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
961                 } else {
962                     isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
963                 }
964 
965                 if (DBG) {
966                     log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
967                             + (isManualSelection ? selectedNetworkOperatorName : ""));
968                 }
969 
970                 if (isManualSelection
971                         && isSubscriptionVisibleToUser(
972                               mSubscriptionManager.getActiveSubscriptionInfo(subId))) {
973                     mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
974                     shouldShowNotification(serviceState, subId);
975                 } else {
976                     dismissNetworkSelectionNotification(subId);
977                     clearUpNetworkSelectionNotificationParam(subId);
978                 }
979             } else {
980                 if (DBG) {
981                     log("updateNetworkSelection()... state = " + serviceState
982                             + " not updating network due to invalid subId " + subId);
983                 }
984                 dismissNetworkSelectionNotificationForInactiveSubId();
985             }
986         }
987     }
988 
989     /**
990      * Update notification about no service of user selected operator.
991      * For dismissNetworkSelectionNotificationOnSimDisable feature disabled.
992      *
993      * @param serviceState Phone service state
994      * @param subId The subscription ID
995      */
updateNetworkSelectionForFeatureDisabled(int serviceState, int subId)996     private void updateNetworkSelectionForFeatureDisabled(int serviceState, int subId) {
997         int phoneId = SubscriptionManager.getPhoneId(subId);
998         Phone phone = SubscriptionManager.isValidPhoneId(phoneId)
999                 ? PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
1000         if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
1001             if (SubscriptionManager.isValidSubscriptionId(subId)) {
1002                 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1003                 String selectedNetworkOperatorName =
1004                         sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
1005                 // get the shared preference of network_selection.
1006                 // empty is auto mode, otherwise it is the operator alpha name
1007                 // in case there is no operator name, check the operator numeric
1008                 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
1009                     selectedNetworkOperatorName =
1010                             sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
1011                 }
1012                 boolean isManualSelection;
1013                 // if restoring manual selection is controlled by framework, then get network
1014                 // selection from shared preference, otherwise get from real network indicators.
1015                 boolean restoreSelection = !mContext.getResources().getBoolean(
1016                         com.android.internal.R.bool.skip_restoring_network_selection);
1017                 if (restoreSelection) {
1018                     isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
1019                 } else {
1020                     isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
1021                 }
1022 
1023                 if (DBG) {
1024                     log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
1025                             + (isManualSelection ? selectedNetworkOperatorName : ""));
1026                 }
1027 
1028                 if (isManualSelection
1029                         && isSubscriptionVisibleToUser(
1030                               mSubscriptionManager.getActiveSubscriptionInfo(subId))) {
1031                     mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
1032                     shouldShowNotification(serviceState, subId);
1033                 } else {
1034                     dismissNetworkSelectionNotification(subId);
1035                     clearUpNetworkSelectionNotificationParam(subId);
1036                 }
1037             } else {
1038                 if (DBG) log("updateNetworkSelection()..." + "state = " +
1039                         serviceState + " not updating network due to invalid subId " + subId);
1040                 dismissNetworkSelectionNotificationForInactiveSubId();
1041             }
1042         }
1043     }
1044 
1045     // TODO(b/261916533) This should be handled by SubscriptionManager#isSubscriptionVisible(),
1046     // but that method doesn't support system callers, so here we are.
isSubscriptionVisibleToUser(SubscriptionInfo subInfo)1047     private boolean isSubscriptionVisibleToUser(SubscriptionInfo subInfo) {
1048         return subInfo != null && (!subInfo.isOpportunistic() || subInfo.getGroupUuid() == null);
1049     }
1050 
dismissNetworkSelectionNotification(int subId)1051     private void dismissNetworkSelectionNotification(int subId) {
1052         if (mSelectedUnavailableNotify.get(subId, false)) {
1053             cancelNetworkSelection(subId);
1054             mSelectedUnavailableNotify.remove(subId);
1055         }
1056     }
1057 
1058     /**
1059      * Dismiss the network selection "no service" notification for all inactive subscriptions.
1060      */
dismissNetworkSelectionNotificationForInactiveSubId()1061     public void dismissNetworkSelectionNotificationForInactiveSubId() {
1062         for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
1063             int subId = mSelectedUnavailableNotify.keyAt(i);
1064             if (!mSubscriptionManager.isActiveSubId(subId)) {
1065                 dismissNetworkSelectionNotification(subId);
1066                 clearUpNetworkSelectionNotificationParam(subId);
1067             }
1068         }
1069     }
1070 
log(String msg)1071     private void log(String msg) {
1072         Log.d(LOG_TAG, msg);
1073     }
1074 
logi(String msg)1075     private void logi(String msg) {
1076         Log.i(LOG_TAG, msg);
1077     }
1078 
shouldShowNotification(int serviceState, int subId)1079     private void shouldShowNotification(int serviceState, int subId) {
1080         // "Network selection unavailable" notification should only show when network selection is
1081         // visible to the end user. Some CC items e.g. KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL
1082         // can be overridden to hide the network selection to the end user. In this case, the
1083         // notification is not shown to avoid confusion to the end user.
1084         if (!shouldDisplayNetworkSelectOptions(subId)) {
1085             logi("Carrier configs refuse to show network selection not available notification");
1086             return;
1087         }
1088 
1089         // In case network selection notification shows up repeatedly under
1090         // unstable network condition. The logic is to check whether or not
1091         // the service state keeps in no service condition for at least
1092         // {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
1093         // And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
1094         // To avoid the notification showing up for the momentary state.
1095         if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
1096             if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
1097                     != ServiceState.STATE_OUT_OF_SERVICE) {
1098                 mOOSTimestamp.put(subId, getTimeStamp());
1099             }
1100             if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
1101                     >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
1102                     || mPendingEventCounter.get(subId, 0)
1103                     > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
1104                 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
1105                 clearUpNetworkSelectionNotificationParam(subId);
1106             } else {
1107                 startPendingNetworkSelectionNotification(subId);
1108             }
1109         } else {
1110             dismissNetworkSelectionNotification(subId);
1111         }
1112         mPreviousServiceState.put(subId, serviceState);
1113         if (DBG) {
1114             log("shouldShowNotification()..." + " subId = " + subId
1115                     + " serviceState = " + serviceState
1116                     + " mOOSTimestamp = " + mOOSTimestamp
1117                     + " mPendingEventCounter = " + mPendingEventCounter);
1118         }
1119     }
1120 
1121     // TODO(b/243010310): merge methods below with Settings#MobileNetworkUtils and optimize them.
1122     // The methods below are copied from com.android.settings.network.telephony.MobileNetworkUtils
1123     // to make sure the network selection unavailable notification should not show when Network
1124     // Selection menu is not present in Settings app.
shouldDisplayNetworkSelectOptions(int subId)1125     private boolean shouldDisplayNetworkSelectOptions(int subId) {
1126         final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
1127         final CarrierConfigManager carrierConfigManager = mContext.getSystemService(
1128                 CarrierConfigManager.class);
1129         final PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
1130 
1131         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1132                 || carrierConfig == null
1133                 || !carrierConfig.getBoolean(
1134                 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
1135                 || carrierConfig.getBoolean(
1136                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
1137                 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
1138                 && !telephonyManager.isManualNetworkSelectionAllowed())) {
1139             return false;
1140         }
1141 
1142         if (isWorldMode(carrierConfig)) {
1143             final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
1144                     (int) telephonyManager.getAllowedNetworkTypesForReason(
1145                             TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
1146             if (networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO) {
1147                 return false;
1148             }
1149             if (shouldSpeciallyUpdateGsmCdma(telephonyManager, carrierConfig)) {
1150                 return false;
1151             }
1152             if (networkMode == RILConstants.NETWORK_MODE_LTE_GSM_WCDMA) {
1153                 return true;
1154             }
1155         }
1156 
1157         return isGsmBasicOptions(telephonyManager, carrierConfig);
1158     }
1159 
isWorldMode(@onNull PersistableBundle carrierConfig)1160     private static boolean isWorldMode(@NonNull PersistableBundle carrierConfig) {
1161         return carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
1162     }
1163 
shouldSpeciallyUpdateGsmCdma(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1164     private static boolean shouldSpeciallyUpdateGsmCdma(@NonNull TelephonyManager telephonyManager,
1165             @NonNull PersistableBundle carrierConfig) {
1166         if (!isWorldMode(carrierConfig)) {
1167             return false;
1168         }
1169 
1170         final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
1171                 (int) telephonyManager.getAllowedNetworkTypesForReason(
1172                         TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
1173         if (networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM
1174                 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
1175                 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA
1176                 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA
1177                 || networkMode
1178                 == RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
1179                 || networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
1180             if (!isTdscdmaSupported(telephonyManager, carrierConfig)) {
1181                 return true;
1182             }
1183         }
1184 
1185         return false;
1186     }
1187 
isTdscdmaSupported(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1188     private static boolean isTdscdmaSupported(@NonNull TelephonyManager telephonyManager,
1189             @NonNull PersistableBundle carrierConfig) {
1190         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
1191             return true;
1192         }
1193         final String[] numericArray = carrierConfig.getStringArray(
1194                 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
1195         if (numericArray == null) {
1196             return false;
1197         }
1198         final ServiceState serviceState = telephonyManager.getServiceState();
1199         final String operatorNumeric =
1200                 (serviceState != null) ? serviceState.getOperatorNumeric() : null;
1201         if (operatorNumeric == null) {
1202             return false;
1203         }
1204         for (String numeric : numericArray) {
1205             if (operatorNumeric.equals(numeric)) {
1206                 return true;
1207             }
1208         }
1209         return false;
1210     }
1211 
isGsmBasicOptions(@onNull TelephonyManager telephonyManager, @NonNull PersistableBundle carrierConfig)1212     private static boolean isGsmBasicOptions(@NonNull TelephonyManager telephonyManager,
1213             @NonNull PersistableBundle carrierConfig) {
1214         if (!carrierConfig.getBoolean(
1215                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
1216                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
1217             return true;
1218         }
1219 
1220         if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
1221             return true;
1222         }
1223 
1224         return false;
1225     }
1226     // END of TODO:(b/243010310): merge methods above with Settings#MobileNetworkUtils and optimize.
1227 
startPendingNetworkSelectionNotification(int subId)1228     private void startPendingNetworkSelectionNotification(int subId) {
1229         if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
1230             if (DBG) {
1231                 log("startPendingNetworkSelectionNotification: subId = " + subId);
1232             }
1233             mHandler.sendMessageDelayed(
1234                     mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
1235                     NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
1236             mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
1237         }
1238     }
1239 
clearUpNetworkSelectionNotificationParam(int subId)1240     private void clearUpNetworkSelectionNotificationParam(int subId) {
1241         if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
1242             mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
1243         }
1244         mPreviousServiceState.remove(subId);
1245         mOOSTimestamp.remove(subId);
1246         mPendingEventCounter.remove(subId);
1247         mSelectedNetworkOperatorName.remove(subId);
1248     }
1249 
1250     @VisibleForTesting
getTimeStamp()1251     public long getTimeStamp() {
1252         return SystemClock.elapsedRealtime();
1253     }
1254 }
1255