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