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