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