blob: 3c7b321c71b264f71463a8d319aa97777fbc10d4 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
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
17package com.android.phone;
18
Ta-wei Yenafca2d62017-07-18 17:20:59 -070019import static android.Manifest.permission.READ_PHONE_STATE;
20
Rambo Wangc30938c2022-08-10 22:54:23 +000021import android.annotation.NonNull;
chen xubaf9fe52019-07-02 17:28:24 -070022import android.annotation.Nullable;
Tyler Gunn99ae2692020-12-09 11:35:21 -080023import android.app.BroadcastOptions;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070024import android.app.Notification;
25import android.app.NotificationManager;
26import android.app.PendingIntent;
27import android.app.StatusBarManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070028import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070029import android.content.Context;
30import android.content.Intent;
31import android.content.SharedPreferences;
Jordan Liu9ddde022019-08-05 13:49:08 -070032import android.content.pm.PackageManager;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080033import android.content.pm.ResolveInfo;
Nancy Chenb4a92702014-12-04 15:57:29 -080034import android.content.res.Resources;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070035import android.net.Uri;
irisykyang25202462018-11-13 14:49:54 +080036import android.os.Handler;
37import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070038import android.os.PersistableBundle;
irisykyang25202462018-11-13 14:49:54 +080039import android.os.SystemClock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070041import android.os.UserHandle;
42import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044import android.provider.ContactsContract.PhoneLookup;
Jeff Davidson6d9bf522017-11-03 14:51:13 -070045import android.provider.Settings;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070046import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080047import android.telecom.PhoneAccountHandle;
48import android.telecom.TelecomManager;
Jonathan Basseri3649bdb2015-04-30 22:39:11 -070049import android.telephony.CarrierConfigManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import android.telephony.PhoneNumberUtils;
Rambo Wangc30938c2022-08-10 22:54:23 +000051import android.telephony.RadioAccessFamily;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080053import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080054import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080055import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070056import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080057import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058import android.util.Log;
irisykyang25202462018-11-13 14:49:54 +080059import android.util.SparseArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060import android.widget.Toast;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070061
rambowang7d6f4142022-08-13 20:45:20 -050062import com.android.internal.annotations.VisibleForTesting;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070063import com.android.internal.telephony.Phone;
Jayachandran C2ef9a482017-05-12 22:07:47 -070064import com.android.internal.telephony.PhoneFactory;
Rambo Wangc30938c2022-08-10 22:54:23 +000065import com.android.internal.telephony.RILConstants;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070066import com.android.internal.telephony.TelephonyCapabilities;
sangyund5f37ab2023-11-13 22:52:28 +090067import com.android.internal.telephony.flags.FeatureFlags;
68import com.android.internal.telephony.flags.FeatureFlagsImpl;
fionaxu8b7620d2017-05-01 16:22:17 -070069import com.android.internal.telephony.util.NotificationChannelController;
Andrew Leebf07f762015-04-07 19:05:50 -070070import com.android.phone.settings.VoicemailSettingsActivity;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070071
Meng Wang1a19e442019-10-11 10:09:00 -070072import java.util.ArrayList;
chen xubaf9fe52019-07-02 17:28:24 -070073import java.util.HashSet;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080074import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070075import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080076import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070077
Santos Cordon7d4ddf62013-07-10 11:58:08 -070078/**
79 * NotificationManager-related utility code for the Phone app.
80 *
81 * This is a singleton object which acts as the interface to the
82 * framework's NotificationManager, and is used to display status bar
83 * icons and control other status bar-related behavior.
84 *
85 * @see PhoneGlobals.notificationMgr
86 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070087public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080088 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070089 private static final boolean DBG =
90 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
91 // Do not check in with VDBG = true, since that may write PII to the system log.
92 private static final boolean VDBG = false;
93
Ta-wei Yenb29425b2016-09-21 17:28:14 -070094 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
95 "mwi_should_check_vvm_configuration_state_";
96
Santos Cordon7d4ddf62013-07-10 11:58:08 -070097 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070098 static final int MMI_NOTIFICATION = 1;
99 static final int NETWORK_SELECTION_NOTIFICATION = 2;
100 static final int VOICEMAIL_NOTIFICATION = 3;
101 static final int CALL_FORWARD_NOTIFICATION = 4;
Jordan Liuc353b162019-07-23 15:54:41 -0700102 static final int DATA_ROAMING_NOTIFICATION = 5;
Santos Cordonf68db2e2014-07-02 14:40:44 -0700103 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
chen xubaf9fe52019-07-02 17:28:24 -0700104 static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700105
irisykyang25202462018-11-13 14:49:54 +0800106 // Event for network selection notification.
107 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
108
109 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
110 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
111
112 private static final int STATE_UNKNOWN_SERVICE = -1;
113
chen xubaf9fe52019-07-02 17:28:24 -0700114 private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
115
Tyler Gunn99ae2692020-12-09 11:35:21 -0800116 /**
117 * Grant recipients of new voicemail broadcasts a 10sec allowlist so they can start a background
118 * service to do VVM processing.
119 */
120 private final long VOICEMAIL_ALLOW_LIST_DURATION_MILLIS = 10000L;
121
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700122 /** The singleton NotificationMgr instance. */
123 private static NotificationMgr sInstance;
124
125 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700126
127 private Context mContext;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700128 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -0700129 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700130 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800131 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800132 private TelecomManager mTelecomManager;
133 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700134
irisykyang25202462018-11-13 14:49:54 +0800135 // used to track the notification of selected network unavailable, per subscription id.
136 private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700137
chen xubaf9fe52019-07-02 17:28:24 -0700138 // used to track the notification of limited sim function under dual sim, per subscription id.
139 private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
140
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800141 // used to track whether the message waiting indicator is visible, per subscription id.
142 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
143
irisykyang25202462018-11-13 14:49:54 +0800144 // those flags are used to track whether to show network selection notification or not.
145 private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
146 private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
147 private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
148 // maps each subId to selected network operator name.
149 private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
150
sangyund5f37ab2023-11-13 22:52:28 +0900151 // feature flags
152 private final FeatureFlags mFeatureFlags;
153
irisykyang25202462018-11-13 14:49:54 +0800154 private final Handler mHandler = new Handler() {
155 @Override
156 public void handleMessage(Message msg) {
157 switch (msg.what) {
158 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
159 int subId = (int) msg.obj;
160 TelephonyManager telephonyManager =
161 mTelephonyManager.createForSubscriptionId(subId);
162 if (telephonyManager.getServiceState() != null) {
163 shouldShowNotification(telephonyManager.getServiceState().getState(),
164 subId);
165 }
166 break;
167 }
168 }
169 };
170
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700171 /**
172 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700173 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700174 */
rambowang7d6f4142022-08-13 20:45:20 -0500175 @VisibleForTesting
176 /* package */ NotificationMgr(PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700177 mApp = app;
178 mContext = app;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700179 mStatusBarManager =
180 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700181 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800182 mSubscriptionManager = SubscriptionManager.from(mContext);
Tyler Gunn5ddfdc92019-10-31 13:08:23 -0700183 mTelecomManager = app.getSystemService(TelecomManager.class);
Andrew Leed5165b02014-12-05 15:53:58 -0800184 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
sangyund5f37ab2023-11-13 22:52:28 +0900185 mFeatureFlags = new FeatureFlagsImpl();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700186 }
187
188 /**
189 * Initialize the singleton NotificationMgr instance.
190 *
191 * This is only done once, at startup, from PhoneApp.onCreate().
192 * From then on, the NotificationMgr instance is available via the
193 * PhoneApp's public "notificationMgr" field, which is why there's no
194 * getInstance() method here.
195 */
196 /* package */ static NotificationMgr init(PhoneGlobals app) {
197 synchronized (NotificationMgr.class) {
198 if (sInstance == null) {
199 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700200 } else {
201 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
202 }
203 return sInstance;
204 }
205 }
206
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700207 /** The projection to use when querying the phones table */
208 static final String[] PHONES_PROJECTION = new String[] {
209 PhoneLookup.NUMBER,
210 PhoneLookup.DISPLAY_NAME,
211 PhoneLookup._ID
212 };
213
214 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800215 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
216 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
217 * settings screen. The voicemail notification sound is suppressed.
218 *
219 * @param subId The subscription Id.
220 */
221 /* package */ void refreshMwi(int subId) {
222 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
223 // reference the single subid stored in the mMwiVisible map.
Ta-wei Yena1390d42017-12-04 15:11:33 -0800224 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800225 if (mMwiVisible.keySet().size() == 1) {
226 Set<Integer> keySet = mMwiVisible.keySet();
227 Iterator<Integer> keyIt = keySet.iterator();
228 if (!keyIt.hasNext()) {
229 return;
230 }
231 subId = keyIt.next();
232 }
233 }
234 if (mMwiVisible.containsKey(subId)) {
235 boolean mwiVisible = mMwiVisible.get(subId);
236 if (mwiVisible) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900237 mApp.notifier.updatePhoneStateListeners(true);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800238 }
239 }
240 }
241
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700242 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
243 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
244 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
245 + subId);
246 return;
247 }
248
249 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
250 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
251 .apply();
252 }
253
254 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
255 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
256 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
257 return true;
258 }
259 return PreferenceManager
260 .getDefaultSharedPreferences(mContext)
261 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
262 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800263 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700264 * Updates the message waiting indicator (voicemail) notification.
265 *
266 * @param visible true if there are messages waiting
267 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800268 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700269 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800270 }
271
272 /**
273 * Updates the message waiting indicator (voicemail) notification.
274 *
275 * @param subId the subId to update.
276 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700277 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
278 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800279 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700280 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800281 if (!PhoneGlobals.sVoiceCapable) {
282 // Do not show the message waiting indicator on devices which are not voice capable.
283 // These events *should* be blocked at the telephony layer for such devices.
284 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
285 return;
286 }
287
Nancy Chen2cf7f292015-05-15 11:00:10 -0700288 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800289 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800290 mMwiVisible.put(subId, visible);
291
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700292 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800293 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800294 Log.w(LOG_TAG, "Found null phone for: " + subId);
295 return;
296 }
297
298 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
299 if (subInfo == null) {
300 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800301 return;
302 }
303
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700304 int resId = android.R.drawable.stat_notify_voicemail;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900305 if (mTelephonyManager.getPhoneCount() > 1) {
306 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
307 : R.drawable.stat_notify_voicemail_sub2;
308 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700309
310 // This Notification can get a lot fancier once we have more
311 // information about the current voicemail messages.
312 // (For example, the current voicemail system can't tell
313 // us the caller-id or timestamp of a message, or tell us the
314 // message count.)
315
316 // But for now, the UI is ultra-simple: if the MWI indication
317 // is supposed to be visible, just show a single generic
318 // notification.
319
320 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800321 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700322 if (DBG) log("- got vm number: '" + vmNumber + "'");
323
Andrew Leea82b8202014-11-21 16:18:28 -0800324 // The voicemail number may be null because:
325 // (1) This phone has no voicemail number.
326 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
327 // happen when the device first boots if we get a MWI notification when we
328 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800329 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
330 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700331 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800332 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700333 }
334
Bryce Lee5dc90842015-08-11 07:57:14 -0700335 Integer vmCount = null;
336
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800337 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700338 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700339 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
340 notificationTitle = String.format(titleFormat, vmCount);
341 }
342
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800343 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
344 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
345
346 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700347 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700348 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
349
350 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800351 notificationText = mContext.getString(
352 R.string.notification_voicemail_no_vm_number);
353
354 // If the voicemail number if unknown, instead of calling voicemail, take the user
355 // to the voicemail settings.
356 notificationText = mContext.getString(
357 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700358 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800359 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700360 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700361 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800362 if (mTelephonyManager.getPhoneCount() > 1) {
363 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800364 } else {
365 notificationText = String.format(
366 mContext.getString(R.string.notification_voicemail_text_format),
367 PhoneNumberUtils.formatNumber(vmNumber));
368 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800369 intent = new Intent(
370 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700371 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800372 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700373 }
Oli Thompsondf9e1392023-03-13 15:48:59 +0000374 PendingIntent pendingIntent;
375 UserHandle subAssociatedUserHandle =
376 mSubscriptionManager.getSubscriptionUserHandle(subId);
377 if (subAssociatedUserHandle == null) {
378 pendingIntent = PendingIntent.getActivity(mContext, subId /* requestCode */, intent,
379 PendingIntent.FLAG_IMMUTABLE);
380 } else {
381 pendingIntent = PendingIntent.getActivityAsUser(mContext, subId /* requestCode */,
382 intent, PendingIntent.FLAG_IMMUTABLE, null, subAssociatedUserHandle);
383 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700384
Nancy Chenb4a92702014-12-04 15:57:29 -0800385 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700386 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700387 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700388 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700389 builder.setSmallIcon(resId)
390 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800391 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700392 .setContentTitle(notificationTitle)
393 .setContentText(notificationText)
394 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800395 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700396 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700397 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Jordan Liu575c0d02019-07-09 16:29:48 -0700398 .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
Ta-wei Yen282a9702017-05-30 17:32:29 -0700399 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700400
Andrew Lee99d0ac22014-10-10 13:18:04 -0700401 final Notification notification = builder.build();
Meng Wang1a19e442019-10-11 10:09:00 -0700402 List<UserHandle> users = getUsersExcludeDying();
403 for (UserHandle userHandle : users) {
Oli Thompsondf9e1392023-03-13 15:48:59 +0000404 boolean isManagedUser = mUserManager.isManagedProfile(userHandle.getIdentifier());
405 if (!hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
406 && (userHandle.equals(subAssociatedUserHandle)
407 || (subAssociatedUserHandle == null && !isManagedUser))
408 && !maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount,
409 vmNumber, pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
410 notifyAsUser(
411 Integer.toString(subId) /* tag */,
412 VOICEMAIL_NOTIFICATION,
413 notification,
414 userHandle);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700415 }
416 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700417 } else {
Oli Thompson78ad84e2023-08-01 21:42:23 +0000418 UserHandle subAssociatedUserHandle =
419 mSubscriptionManager.getSubscriptionUserHandle(subId);
420 List<UserHandle> users = getUsersExcludeDying();
421 for (UserHandle userHandle : users) {
422 boolean isManagedUser = mUserManager.isManagedProfile(userHandle.getIdentifier());
423 if (!hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
424 && (userHandle.equals(subAssociatedUserHandle)
425 || (subAssociatedUserHandle == null && !isManagedUser))
426 && !maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
427 false, userHandle, isRefresh)) {
428 cancelAsUser(
429 Integer.toString(subId) /* tag */,
430 VOICEMAIL_NOTIFICATION,
431 userHandle);
432 }
433 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700434 }
435 }
436
Meng Wang1a19e442019-10-11 10:09:00 -0700437 private List<UserHandle> getUsersExcludeDying() {
438 long[] serialNumbersOfUsers =
439 mUserManager.getSerialNumbersOfUsers(/* excludeDying= */ true);
440 List<UserHandle> users = new ArrayList<>(serialNumbersOfUsers.length);
441 for (long serialNumber : serialNumbersOfUsers) {
442 users.add(mUserManager.getUserForSerialNumber(serialNumber));
443 }
444 return users;
445 }
446
Amit Mahajan2f489b72019-10-08 11:21:52 -0700447 private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
448 final List<UserManager.EnforcingUser> sources = mUserManager
449 .getUserRestrictionSources(restrictionKey, userHandle);
450 return (sources != null && !sources.isEmpty());
451 }
452
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700453 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800454 * Sends a broadcast with the voicemail notification information to the default dialer. This
455 * method is also used to indicate to the default dialer when to clear the
456 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700457 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800458 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700459 * @param count The number of pending voicemail messages to indicate on the notification. A
460 * Value of 0 is passed here to indicate that the notification should be cleared.
461 * @param number The voicemail phone number if specified.
462 * @param pendingIntent The intent that should be passed as the action to be taken.
463 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
464 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800465 * @param userHandle The user to receive the notification. Each user can have their own default
466 * dialer.
467 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700468 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800469 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
470 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700471 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800472
473 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
474 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700475 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700476 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800477 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
478 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yenafca2d62017-07-18 17:20:59 -0700479 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700480 if (count != null) {
481 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
482 }
483
484 // Additional information about the voicemail notification beyond the count is only
485 // present when the count not specified or greater than 0. The value of 0 represents
486 // clearing the notification, which does not require additional information.
487 if (count == null || count > 0) {
488 if (!TextUtils.isEmpty(number)) {
489 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
490 }
491
492 if (pendingIntent != null) {
493 intent.putExtra(isSettingsIntent
494 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
495 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
496 pendingIntent);
497 }
498 }
Tyler Gunn99ae2692020-12-09 11:35:21 -0800499
500 BroadcastOptions bopts = BroadcastOptions.makeBasic();
501 bopts.setTemporaryAppWhitelistDuration(VOICEMAIL_ALLOW_LIST_DURATION_MILLIS);
502 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE, bopts.toBundle());
Bryce Lee5dc90842015-08-11 07:57:14 -0700503 return true;
504 }
505
506 return false;
507 }
508
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800509 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
Tyler Gunn5ddfdc92019-10-31 13:08:23 -0700510 String dialerPackage = mContext.getSystemService(TelecomManager.class)
Tyler Gunn83adc052020-01-28 09:12:21 -0800511 .getDefaultDialerPackage(userHandle);
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800512 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
513 .setPackage(dialerPackage);
514 }
515
516 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
517 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
518 if (intent == null) {
519 return false;
520 }
521
Jack Yube3fa442024-09-16 14:39:45 -0700522 List<ResolveInfo> receivers;
523 if (mFeatureFlags.hsumPackageManager()) {
524 receivers = mContext.createContextAsUser(userHandle, 0)
525 .getPackageManager().queryBroadcastReceivers(intent, 0);
526 } else {
527 receivers = mContext.getPackageManager()
528 .queryBroadcastReceivers(intent, 0);
529 }
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800530 return receivers.size() > 0;
531 }
532
Bryce Lee5dc90842015-08-11 07:57:14 -0700533 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700534 * Updates the message call forwarding indicator notification.
535 *
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530536 * @param visible true if call forwarding enabled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700537 */
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530538
539 /* package */ void updateCfi(int subId, boolean visible) {
540 updateCfi(subId, visible, false /* isRefresh */);
541 }
542
543 /**
544 * Updates the message call forwarding indicator notification.
545 *
546 * @param visible true if call forwarding enabled
547 */
548 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
Tyler Gunna584e2c2017-09-19 11:40:12 -0700549 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700550 if (visible) {
551 // If Unconditional Call Forwarding (forward all calls) for VOICE
552 // is enabled, just show a notification. We'll default to expanded
553 // view for now, so the there is less confusion about the icon. If
554 // it is deemed too weird to have CF indications as expanded views,
555 // then we'll flip the flag back.
556
557 // TODO: We may want to take a look to see if the notification can
558 // display the target to forward calls to. This will require some
559 // effort though, since there are multiple layers of messages that
560 // will need to propagate that information.
561
Andrew Leed5165b02014-12-05 15:53:58 -0800562 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
563 if (subInfo == null) {
564 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
565 return;
566 }
567
568 String notificationTitle;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900569 int resId = R.drawable.stat_sys_phone_call_forward;
Andrew Leed5165b02014-12-05 15:53:58 -0800570 if (mTelephonyManager.getPhoneCount() > 1) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900571 int slotId = SubscriptionManager.getSlotIndex(subId);
572 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
573 : R.drawable.stat_sys_phone_call_forward_sub2;
Sarah Chin2c5f23d2020-11-02 21:52:11 -0800574 if (subInfo.getDisplayName() != null) {
575 notificationTitle = subInfo.getDisplayName().toString();
576 } else {
577 notificationTitle = mContext.getString(R.string.labelCF);
578 }
Andrew Leed5165b02014-12-05 15:53:58 -0800579 } else {
580 notificationTitle = mContext.getString(R.string.labelCF);
581 }
582
fionaxu8b7620d2017-05-01 16:22:17 -0700583 Notification.Builder builder = new Notification.Builder(mContext)
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900584 .setSmallIcon(resId)
Andrew Leed5165b02014-12-05 15:53:58 -0800585 .setColor(subInfo.getIconTint())
586 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700587 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
588 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700589 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700590 .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530591 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700592
Tyler Gunn5bc21712021-04-30 15:07:59 -0700593 Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800594 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800595 SubscriptionInfoHelper.addExtrasToIntent(
596 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
fionaxu96ceebd2017-08-24 12:12:32 -0700597 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
Shuo Qian9a41a172020-04-24 19:19:31 -0700598 intent, PendingIntent.FLAG_IMMUTABLE));
Jordan Liu9ddde022019-08-05 13:49:08 -0700599 notifyAsUser(
fionaxu96ceebd2017-08-24 12:12:32 -0700600 Integer.toString(subId) /* tag */,
601 CALL_FORWARD_NOTIFICATION,
602 builder.build(),
603 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700604 } else {
Meng Wang21487ab2019-11-21 10:44:42 -0800605 List<UserHandle> users = getUsersExcludeDying();
606 for (UserHandle user : users) {
607 if (mUserManager.isManagedProfile(user.getIdentifier())) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900608 continue;
609 }
Jordan Liu9ddde022019-08-05 13:49:08 -0700610 cancelAsUser(
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900611 Integer.toString(subId) /* tag */,
612 CALL_FORWARD_NOTIFICATION,
Meng Wang21487ab2019-11-21 10:44:42 -0800613 user);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900614 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700615 }
616 }
617
618 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700619 * Shows either:
620 * 1) the "Data roaming is on" notification, which
621 * appears when you're roaming and you have the "data roaming" feature turned on for the
622 * given {@code subId}.
623 * or
624 * 2) the "data disconnected due to roaming" notification, which
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700625 * appears when you lose data connectivity because you're roaming and
Pengquan Meng8783d932017-10-16 14:57:40 -0700626 * you have the "data roaming" feature turned off for the given {@code subId}.
Jordan Liuc353b162019-07-23 15:54:41 -0700627 * @param subId which subscription it's notifying about.
628 * @param roamingOn whether currently roaming is on or off. If true, we show notification
629 * 1) above; else we show notification 2).
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700630 */
Jordan Liuc353b162019-07-23 15:54:41 -0700631 /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
632 if (DBG) {
633 log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
634 + " on subId " + subId);
635 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700636
637 // "Mobile network settings" screen / dialog
Nazanin Bakhshi682c77d2019-05-02 17:22:27 -0700638 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
Jeff Davidson6d9bf522017-11-03 14:51:13 -0700639 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Shuo Qian9a41a172020-04-24 19:19:31 -0700640 PendingIntent contentIntent = PendingIntent.getActivity(
641 mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700642
Jordan Liuc353b162019-07-23 15:54:41 -0700643 CharSequence contentTitle = mContext.getText(roamingOn
644 ? R.string.roaming_on_notification_title
645 : R.string.roaming_notification_title);
646 CharSequence contentText = mContext.getText(roamingOn
647 ? R.string.roaming_enabled_message
648 : R.string.roaming_reenable_message);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700649
fionaxu8b7620d2017-05-01 16:22:17 -0700650 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700651 .setSmallIcon(android.R.drawable.stat_sys_warning)
Jordan Liuc353b162019-07-23 15:54:41 -0700652 .setContentTitle(contentTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700653 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700654 .setContentText(contentText)
Jordan Liu575c0d02019-07-09 16:29:48 -0700655 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
fionaxu96ceebd2017-08-24 12:12:32 -0700656 .setContentIntent(contentIntent);
657 final Notification notif =
658 new Notification.BigTextStyle(builder).bigText(contentText).build();
Jordan Liu9ddde022019-08-05 13:49:08 -0700659 notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
660 }
661
662 private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
663 try {
664 Context contextForUser =
665 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
666 NotificationManager notificationManager =
667 (NotificationManager) contextForUser.getSystemService(
668 Context.NOTIFICATION_SERVICE);
669 notificationManager.notify(tag, id, notification);
670 } catch (PackageManager.NameNotFoundException e) {
671 Log.e(LOG_TAG, "unable to notify for user " + user);
672 e.printStackTrace();
673 }
674 }
675
676 private void cancelAsUser(String tag, int id, UserHandle user) {
677 try {
678 Context contextForUser =
679 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
680 NotificationManager notificationManager =
681 (NotificationManager) contextForUser.getSystemService(
682 Context.NOTIFICATION_SERVICE);
683 notificationManager.cancel(tag, id);
684 } catch (PackageManager.NameNotFoundException e) {
685 Log.e(LOG_TAG, "unable to cancel for user " + user);
686 e.printStackTrace();
687 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700688 }
689
690 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700691 * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700692 */
Jordan Liuc353b162019-07-23 15:54:41 -0700693 /* package */ void hideDataRoamingNotification() {
694 if (DBG) log("hideDataRoamingNotification()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700695 cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700696 }
697
698 /**
chen xubaf9fe52019-07-02 17:28:24 -0700699 * Shows the "Limited SIM functionality" warning notification, which appears when using a
700 * special carrier under dual sim. limited function applies for DSDS in general when two SIM
701 * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
702 */
703 public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
704 if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
705 + " subId: " + subId);
706 if (mLimitedSimFunctionNotify.contains(subId)) {
707 // handle the case that user swipe the notification but condition triggers
708 // frequently which cause the same notification consistently displayed.
709 if (DBG) log("showLimitedSimFunctionWarningNotification, "
710 + "not display again if already displayed");
711 return;
712 }
713 // Navigate to "Network Selection Settings" which list all subscriptions.
714 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
Shuo Qian9a41a172020-04-24 19:19:31 -0700715 new Intent(ACTION_MOBILE_NETWORK_LIST), PendingIntent.FLAG_IMMUTABLE);
Chen Xue1bbfd22019-09-18 17:17:31 -0700716 // Display phone number from the other sub
717 String line1Num = null;
718 SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
719 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
720 List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
721 for (SubscriptionInfo sub : subList) {
722 if (sub.getSubscriptionId() != subId) {
723 line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
724 }
725 }
chen xubaf9fe52019-07-02 17:28:24 -0700726 final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
727 String.format(mContext.getText(
Chen Xu251ce8f2019-09-13 20:24:22 -0700728 R.string.limited_sim_function_notification_message).toString(), carrierName) :
chen xubaf9fe52019-07-02 17:28:24 -0700729 String.format(mContext.getText(
730 R.string.limited_sim_function_with_phone_num_notification_message).toString(),
Chen Xu251ce8f2019-09-13 20:24:22 -0700731 carrierName, line1Num);
chen xubaf9fe52019-07-02 17:28:24 -0700732 final Notification.Builder builder = new Notification.Builder(mContext)
733 .setSmallIcon(R.drawable.ic_sim_card)
734 .setContentTitle(mContext.getText(
735 R.string.limited_sim_function_notification_title))
736 .setContentText(contentText)
737 .setOnlyAlertOnce(true)
738 .setOngoing(true)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700739 .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
chen xubaf9fe52019-07-02 17:28:24 -0700740 .setContentIntent(contentIntent);
741 final Notification notification = new Notification.BigTextStyle(builder).bigText(
742 contentText).build();
743
Jordan Liu9ddde022019-08-05 13:49:08 -0700744 notifyAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700745 LIMITED_SIM_FUNCTION_NOTIFICATION,
746 notification, UserHandle.ALL);
747 mLimitedSimFunctionNotify.add(subId);
748 }
749
750 /**
751 * Dismiss the "Limited SIM functionality" warning notification for the given subId.
752 */
753 public void dismissLimitedSimFunctionWarningNotification(int subId) {
754 if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
755 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
756 // dismiss all notifications
757 for (int id : mLimitedSimFunctionNotify) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700758 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700759 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
760 }
761 mLimitedSimFunctionNotify.clear();
762 } else if (mLimitedSimFunctionNotify.contains(subId)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700763 cancelAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700764 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
765 mLimitedSimFunctionNotify.remove(subId);
766 }
767 }
768
769 /**
770 * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
771 */
772 public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
773 if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
774 // dismiss notification for inactive subscriptions.
775 // handle the corner case that SIM change by SIM refresh doesn't clear the notification
776 // from the old SIM if both old & new SIM configured to display the notification.
777 mLimitedSimFunctionNotify.removeIf(id -> {
778 if (!mSubscriptionManager.isActiveSubId(id)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700779 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700780 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
781 return true;
782 }
783 return false;
784 });
785 }
786
787 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700788 * Display the network selection "no service" notification
789 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700790 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700791 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700792 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700793 if (DBG) log("showNetworkSelection(" + operator + ")...");
794
Youming Ye0509b532018-09-14 16:21:17 -0700795 if (!TextUtils.isEmpty(operator)) {
796 operator = String.format(" (%s)", operator);
797 }
fionaxu8b7620d2017-05-01 16:22:17 -0700798 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700799 .setSmallIcon(android.R.drawable.stat_sys_warning)
800 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
801 .setContentText(
802 mContext.getString(R.string.notification_network_selection_text, operator))
803 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700804 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700805 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700806
807 // create the target network operators settings intent
808 Intent intent = new Intent(Intent.ACTION_MAIN);
809 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
810 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700811 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700812 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700813 mContext.getString(R.string.mobile_network_settings_package),
814 mContext.getString(R.string.mobile_network_settings_class)));
Pengquan Meng984ba422019-09-05 18:00:06 -0700815 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Shuo Qian9a41a172020-04-24 19:19:31 -0700816 builder.setContentIntent(
817 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE));
Jordan Liu9ddde022019-08-05 13:49:08 -0700818 notifyAsUser(
irisykyang25202462018-11-13 14:49:54 +0800819 Integer.toString(subId) /* tag */,
fionaxu96ceebd2017-08-24 12:12:32 -0700820 SELECTED_OPERATOR_FAIL_NOTIFICATION,
821 builder.build(),
822 UserHandle.ALL);
irisykyang25202462018-11-13 14:49:54 +0800823 mSelectedUnavailableNotify.put(subId, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700824 }
825
826 /**
827 * Turn off the network selection "no service" notification
828 */
irisykyang25202462018-11-13 14:49:54 +0800829 private void cancelNetworkSelection(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700830 if (DBG) log("cancelNetworkSelection()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700831 cancelAsUser(
irisykyang25202462018-11-13 14:49:54 +0800832 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
833 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700834 }
835
836 /**
837 * Update notification about no service of user selected operator
838 *
839 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700840 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700841 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700842 void updateNetworkSelection(int serviceState, int subId) {
sangyund5f37ab2023-11-13 22:52:28 +0900843 if (!mFeatureFlags.dismissNetworkSelectionNotificationOnSimDisable()) {
844 updateNetworkSelectionForFeatureDisabled(serviceState, subId);
845 return;
846 }
847
848 // for dismissNetworkSelectionNotificationOnSimDisable feature enabled.
Jayachandran C2ef9a482017-05-12 22:07:47 -0700849 int phoneId = SubscriptionManager.getPhoneId(subId);
850 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
851 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
852 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
sangyun73353172023-08-09 04:12:34 +0900853 if (SubscriptionManager.isValidSubscriptionId(subId)
854 && mSubscriptionManager.isActiveSubId(subId)) {
fionaxu996a1c32018-04-13 15:00:37 -0700855 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
856 String selectedNetworkOperatorName =
857 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
858 // get the shared preference of network_selection.
859 // empty is auto mode, otherwise it is the operator alpha name
860 // in case there is no operator name, check the operator numeric
861 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
862 selectedNetworkOperatorName =
863 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
864 }
865 boolean isManualSelection;
fionaxud6aac662018-03-14 16:44:29 -0700866 // if restoring manual selection is controlled by framework, then get network
867 // selection from shared preference, otherwise get from real network indicators.
Daniel Brightebb4eb72020-02-18 15:16:33 -0800868 boolean restoreSelection = !mContext.getResources().getBoolean(
869 com.android.internal.R.bool.skip_restoring_network_selection);
fionaxud6aac662018-03-14 16:44:29 -0700870 if (restoreSelection) {
fionaxud6aac662018-03-14 16:44:29 -0700871 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
872 } else {
fionaxud6aac662018-03-14 16:44:29 -0700873 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
Amit Mahajana60be872015-01-15 16:05:08 -0800874 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700875
fionaxud6aac662018-03-14 16:44:29 -0700876 if (DBG) {
877 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
878 + (isManualSelection ? selectedNetworkOperatorName : ""));
879 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700880
irisykyang25202462018-11-13 14:49:54 +0800881 if (isManualSelection) {
882 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
883 shouldShowNotification(serviceState, subId);
Amit Mahajana60be872015-01-15 16:05:08 -0800884 } else {
irisykyang25202462018-11-13 14:49:54 +0800885 dismissNetworkSelectionNotification(subId);
886 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700887 }
888 } else {
sangyund5f37ab2023-11-13 22:52:28 +0900889 if (DBG) {
890 log("updateNetworkSelection()... state = " + serviceState
891 + " not updating network due to invalid subId " + subId);
892 }
893 dismissNetworkSelectionNotificationForInactiveSubId();
894 }
895 }
896 }
897
898 /**
899 * Update notification about no service of user selected operator.
900 * For dismissNetworkSelectionNotificationOnSimDisable feature disabled.
901 *
902 * @param serviceState Phone service state
903 * @param subId The subscription ID
904 */
905 private void updateNetworkSelectionForFeatureDisabled(int serviceState, int subId) {
906 int phoneId = SubscriptionManager.getPhoneId(subId);
907 Phone phone = SubscriptionManager.isValidPhoneId(phoneId)
908 ? PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
909 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
910 if (SubscriptionManager.isValidSubscriptionId(subId)) {
911 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
912 String selectedNetworkOperatorName =
913 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
914 // get the shared preference of network_selection.
915 // empty is auto mode, otherwise it is the operator alpha name
916 // in case there is no operator name, check the operator numeric
917 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
918 selectedNetworkOperatorName =
919 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
920 }
921 boolean isManualSelection;
922 // if restoring manual selection is controlled by framework, then get network
923 // selection from shared preference, otherwise get from real network indicators.
924 boolean restoreSelection = !mContext.getResources().getBoolean(
925 com.android.internal.R.bool.skip_restoring_network_selection);
926 if (restoreSelection) {
927 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
928 } else {
929 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
930 }
931
932 if (DBG) {
933 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
934 + (isManualSelection ? selectedNetworkOperatorName : ""));
935 }
936
937 if (isManualSelection) {
938 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
939 shouldShowNotification(serviceState, subId);
940 } else {
941 dismissNetworkSelectionNotification(subId);
942 clearUpNetworkSelectionNotificationParam(subId);
943 }
944 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800945 if (DBG) log("updateNetworkSelection()..." + "state = " +
946 serviceState + " not updating network due to invalid subId " + subId);
irisykyang25202462018-11-13 14:49:54 +0800947 dismissNetworkSelectionNotificationForInactiveSubId();
948 }
949 }
950 }
951
952 private void dismissNetworkSelectionNotification(int subId) {
953 if (mSelectedUnavailableNotify.get(subId, false)) {
954 cancelNetworkSelection(subId);
955 mSelectedUnavailableNotify.remove(subId);
956 }
957 }
958
Sarah Chin010884c2023-06-14 15:55:08 -0700959 /**
960 * Dismiss the network selection "no service" notification for all inactive subscriptions.
961 */
962 public void dismissNetworkSelectionNotificationForInactiveSubId() {
irisykyang25202462018-11-13 14:49:54 +0800963 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
964 int subId = mSelectedUnavailableNotify.keyAt(i);
965 if (!mSubscriptionManager.isActiveSubId(subId)) {
966 dismissNetworkSelectionNotification(subId);
967 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700968 }
969 }
970 }
971
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700972 private void log(String msg) {
973 Log.d(LOG_TAG, msg);
974 }
Tyler Gunna584e2c2017-09-19 11:40:12 -0700975
976 private void logi(String msg) {
977 Log.i(LOG_TAG, msg);
978 }
irisykyang25202462018-11-13 14:49:54 +0800979
irisykyang25202462018-11-13 14:49:54 +0800980 private void shouldShowNotification(int serviceState, int subId) {
Rambo Wangc30938c2022-08-10 22:54:23 +0000981 // "Network selection unavailable" notification should only show when network selection is
982 // visible to the end user. Some CC items e.g. KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL
983 // can be overridden to hide the network selection to the end user. In this case, the
984 // notification is not shown to avoid confusion to the end user.
985 if (!shouldDisplayNetworkSelectOptions(subId)) {
986 logi("Carrier configs refuse to show network selection not available notification");
987 return;
988 }
989
990 // In case network selection notification shows up repeatedly under
991 // unstable network condition. The logic is to check whether or not
992 // the service state keeps in no service condition for at least
993 // {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
994 // And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
995 // To avoid the notification showing up for the momentary state.
irisykyang25202462018-11-13 14:49:54 +0800996 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
997 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
998 != ServiceState.STATE_OUT_OF_SERVICE) {
999 mOOSTimestamp.put(subId, getTimeStamp());
1000 }
1001 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
1002 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
1003 || mPendingEventCounter.get(subId, 0)
1004 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
1005 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
1006 clearUpNetworkSelectionNotificationParam(subId);
1007 } else {
1008 startPendingNetworkSelectionNotification(subId);
1009 }
1010 } else {
1011 dismissNetworkSelectionNotification(subId);
1012 }
1013 mPreviousServiceState.put(subId, serviceState);
1014 if (DBG) {
1015 log("shouldShowNotification()..." + " subId = " + subId
1016 + " serviceState = " + serviceState
1017 + " mOOSTimestamp = " + mOOSTimestamp
1018 + " mPendingEventCounter = " + mPendingEventCounter);
1019 }
1020 }
1021
Rambo Wangc30938c2022-08-10 22:54:23 +00001022 // TODO(b/243010310): merge methods below with Settings#MobileNetworkUtils and optimize them.
1023 // The methods below are copied from com.android.settings.network.telephony.MobileNetworkUtils
1024 // to make sure the network selection unavailable notification should not show when Network
1025 // Selection menu is not present in Settings app.
1026 private boolean shouldDisplayNetworkSelectOptions(int subId) {
1027 final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
1028 final CarrierConfigManager carrierConfigManager = mContext.getSystemService(
1029 CarrierConfigManager.class);
1030 final PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
1031
1032 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1033 || carrierConfig == null
1034 || !carrierConfig.getBoolean(
1035 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
1036 || carrierConfig.getBoolean(
1037 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
1038 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
1039 && !telephonyManager.isManualNetworkSelectionAllowed())) {
1040 return false;
1041 }
1042
1043 if (isWorldMode(carrierConfig)) {
1044 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
1045 (int) telephonyManager.getAllowedNetworkTypesForReason(
1046 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
1047 if (networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO) {
1048 return false;
1049 }
1050 if (shouldSpeciallyUpdateGsmCdma(telephonyManager, carrierConfig)) {
1051 return false;
1052 }
1053 if (networkMode == RILConstants.NETWORK_MODE_LTE_GSM_WCDMA) {
1054 return true;
1055 }
1056 }
1057
1058 return isGsmBasicOptions(telephonyManager, carrierConfig);
1059 }
1060
1061 private static boolean isWorldMode(@NonNull PersistableBundle carrierConfig) {
1062 return carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
1063 }
1064
1065 private static boolean shouldSpeciallyUpdateGsmCdma(@NonNull TelephonyManager telephonyManager,
1066 @NonNull PersistableBundle carrierConfig) {
1067 if (!isWorldMode(carrierConfig)) {
1068 return false;
1069 }
1070
1071 final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
1072 (int) telephonyManager.getAllowedNetworkTypesForReason(
1073 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
1074 if (networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM
1075 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
1076 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA
1077 || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA
1078 || networkMode
1079 == RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
1080 || networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
1081 if (!isTdscdmaSupported(telephonyManager, carrierConfig)) {
1082 return true;
1083 }
1084 }
1085
1086 return false;
1087 }
1088
1089 private static boolean isTdscdmaSupported(@NonNull TelephonyManager telephonyManager,
1090 @NonNull PersistableBundle carrierConfig) {
1091 if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
1092 return true;
1093 }
1094 final String[] numericArray = carrierConfig.getStringArray(
1095 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
1096 if (numericArray == null) {
1097 return false;
1098 }
1099 final ServiceState serviceState = telephonyManager.getServiceState();
1100 final String operatorNumeric =
1101 (serviceState != null) ? serviceState.getOperatorNumeric() : null;
1102 if (operatorNumeric == null) {
1103 return false;
1104 }
1105 for (String numeric : numericArray) {
1106 if (operatorNumeric.equals(numeric)) {
1107 return true;
1108 }
1109 }
1110 return false;
1111 }
1112
1113 private static boolean isGsmBasicOptions(@NonNull TelephonyManager telephonyManager,
1114 @NonNull PersistableBundle carrierConfig) {
1115 if (!carrierConfig.getBoolean(
1116 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
1117 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
1118 return true;
1119 }
1120
1121 if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
1122 return true;
1123 }
1124
1125 return false;
1126 }
1127 // END of TODO:(b/243010310): merge methods above with Settings#MobileNetworkUtils and optimize.
1128
irisykyang25202462018-11-13 14:49:54 +08001129 private void startPendingNetworkSelectionNotification(int subId) {
1130 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
1131 if (DBG) {
1132 log("startPendingNetworkSelectionNotification: subId = " + subId);
1133 }
1134 mHandler.sendMessageDelayed(
1135 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
1136 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
1137 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
1138 }
1139 }
1140
1141 private void clearUpNetworkSelectionNotificationParam(int subId) {
1142 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
1143 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
1144 }
1145 mPreviousServiceState.remove(subId);
1146 mOOSTimestamp.remove(subId);
1147 mPendingEventCounter.remove(subId);
1148 mSelectedNetworkOperatorName.remove(subId);
1149 }
1150
rambowang19b7b572023-01-25 16:07:08 -06001151 @VisibleForTesting
1152 public long getTimeStamp() {
irisykyang25202462018-11-13 14:49:54 +08001153 return SystemClock.elapsedRealtime();
1154 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001155}