blob: b9d1f539083d3817bf6656c6f9eea699c5769026 [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
chen xubaf9fe52019-07-02 17:28:24 -070021import android.annotation.Nullable;
Tyler Gunn99ae2692020-12-09 11:35:21 -080022import android.app.BroadcastOptions;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070023import android.app.Notification;
24import android.app.NotificationManager;
25import android.app.PendingIntent;
26import android.app.StatusBarManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070027import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070028import android.content.Context;
29import android.content.Intent;
30import android.content.SharedPreferences;
Jordan Liu9ddde022019-08-05 13:49:08 -070031import android.content.pm.PackageManager;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080032import android.content.pm.ResolveInfo;
Nancy Chenb4a92702014-12-04 15:57:29 -080033import android.content.res.Resources;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.net.Uri;
irisykyang25202462018-11-13 14:49:54 +080035import android.os.Handler;
36import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070037import android.os.PersistableBundle;
irisykyang25202462018-11-13 14:49:54 +080038import android.os.SystemClock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070039import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070040import android.os.UserHandle;
41import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import android.provider.ContactsContract.PhoneLookup;
Jeff Davidson6d9bf522017-11-03 14:51:13 -070044import android.provider.Settings;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070045import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080046import android.telecom.PhoneAccountHandle;
47import android.telecom.TelecomManager;
Jonathan Basseri3649bdb2015-04-30 22:39:11 -070048import android.telephony.CarrierConfigManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import android.telephony.PhoneNumberUtils;
50import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080051import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080052import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080053import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070054import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080055import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070056import android.util.Log;
irisykyang25202462018-11-13 14:49:54 +080057import android.util.SparseArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058import android.widget.Toast;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070059
rambowang7d6f4142022-08-13 20:45:20 -050060import com.android.internal.annotations.VisibleForTesting;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070061import com.android.internal.telephony.Phone;
Jayachandran C2ef9a482017-05-12 22:07:47 -070062import com.android.internal.telephony.PhoneFactory;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070063import com.android.internal.telephony.TelephonyCapabilities;
fionaxu8b7620d2017-05-01 16:22:17 -070064import com.android.internal.telephony.util.NotificationChannelController;
Andrew Leebf07f762015-04-07 19:05:50 -070065import com.android.phone.settings.VoicemailSettingsActivity;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070066
Meng Wang1a19e442019-10-11 10:09:00 -070067import java.util.ArrayList;
chen xubaf9fe52019-07-02 17:28:24 -070068import java.util.HashSet;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080069import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070070import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080071import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070072
Santos Cordon7d4ddf62013-07-10 11:58:08 -070073/**
74 * NotificationManager-related utility code for the Phone app.
75 *
76 * This is a singleton object which acts as the interface to the
77 * framework's NotificationManager, and is used to display status bar
78 * icons and control other status bar-related behavior.
79 *
80 * @see PhoneGlobals.notificationMgr
81 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070082public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080083 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070084 private static final boolean DBG =
85 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
86 // Do not check in with VDBG = true, since that may write PII to the system log.
87 private static final boolean VDBG = false;
88
Ta-wei Yenb29425b2016-09-21 17:28:14 -070089 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
90 "mwi_should_check_vvm_configuration_state_";
91
Santos Cordon7d4ddf62013-07-10 11:58:08 -070092 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070093 static final int MMI_NOTIFICATION = 1;
94 static final int NETWORK_SELECTION_NOTIFICATION = 2;
95 static final int VOICEMAIL_NOTIFICATION = 3;
96 static final int CALL_FORWARD_NOTIFICATION = 4;
Jordan Liuc353b162019-07-23 15:54:41 -070097 static final int DATA_ROAMING_NOTIFICATION = 5;
Santos Cordonf68db2e2014-07-02 14:40:44 -070098 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
chen xubaf9fe52019-07-02 17:28:24 -070099 static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100
irisykyang25202462018-11-13 14:49:54 +0800101 // Event for network selection notification.
102 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
103
104 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
105 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
106
107 private static final int STATE_UNKNOWN_SERVICE = -1;
108
chen xubaf9fe52019-07-02 17:28:24 -0700109 private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
110
Tyler Gunn99ae2692020-12-09 11:35:21 -0800111 /**
112 * Grant recipients of new voicemail broadcasts a 10sec allowlist so they can start a background
113 * service to do VVM processing.
114 */
115 private final long VOICEMAIL_ALLOW_LIST_DURATION_MILLIS = 10000L;
116
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700117 /** The singleton NotificationMgr instance. */
118 private static NotificationMgr sInstance;
119
120 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700121
122 private Context mContext;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700123 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -0700124 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800126 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800127 private TelecomManager mTelecomManager;
128 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700129
irisykyang25202462018-11-13 14:49:54 +0800130 // used to track the notification of selected network unavailable, per subscription id.
131 private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700132
chen xubaf9fe52019-07-02 17:28:24 -0700133 // used to track the notification of limited sim function under dual sim, per subscription id.
134 private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
135
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800136 // used to track whether the message waiting indicator is visible, per subscription id.
137 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
138
irisykyang25202462018-11-13 14:49:54 +0800139 // those flags are used to track whether to show network selection notification or not.
140 private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
141 private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
142 private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
143 // maps each subId to selected network operator name.
144 private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
145
146 private final Handler mHandler = new Handler() {
147 @Override
148 public void handleMessage(Message msg) {
149 switch (msg.what) {
150 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
151 int subId = (int) msg.obj;
152 TelephonyManager telephonyManager =
153 mTelephonyManager.createForSubscriptionId(subId);
154 if (telephonyManager.getServiceState() != null) {
155 shouldShowNotification(telephonyManager.getServiceState().getState(),
156 subId);
157 }
158 break;
159 }
160 }
161 };
162
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700163 /**
164 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700165 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700166 */
rambowang7d6f4142022-08-13 20:45:20 -0500167 @VisibleForTesting
168 /* package */ NotificationMgr(PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700169 mApp = app;
170 mContext = app;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700171 mStatusBarManager =
172 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700173 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800174 mSubscriptionManager = SubscriptionManager.from(mContext);
Tyler Gunn5ddfdc92019-10-31 13:08:23 -0700175 mTelecomManager = app.getSystemService(TelecomManager.class);
Andrew Leed5165b02014-12-05 15:53:58 -0800176 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700177 }
178
179 /**
180 * Initialize the singleton NotificationMgr instance.
181 *
182 * This is only done once, at startup, from PhoneApp.onCreate().
183 * From then on, the NotificationMgr instance is available via the
184 * PhoneApp's public "notificationMgr" field, which is why there's no
185 * getInstance() method here.
186 */
187 /* package */ static NotificationMgr init(PhoneGlobals app) {
188 synchronized (NotificationMgr.class) {
189 if (sInstance == null) {
190 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700191 } else {
192 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
193 }
194 return sInstance;
195 }
196 }
197
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700198 /** The projection to use when querying the phones table */
199 static final String[] PHONES_PROJECTION = new String[] {
200 PhoneLookup.NUMBER,
201 PhoneLookup.DISPLAY_NAME,
202 PhoneLookup._ID
203 };
204
205 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800206 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
207 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
208 * settings screen. The voicemail notification sound is suppressed.
209 *
210 * @param subId The subscription Id.
211 */
212 /* package */ void refreshMwi(int subId) {
213 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
214 // reference the single subid stored in the mMwiVisible map.
Ta-wei Yena1390d42017-12-04 15:11:33 -0800215 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800216 if (mMwiVisible.keySet().size() == 1) {
217 Set<Integer> keySet = mMwiVisible.keySet();
218 Iterator<Integer> keyIt = keySet.iterator();
219 if (!keyIt.hasNext()) {
220 return;
221 }
222 subId = keyIt.next();
223 }
224 }
225 if (mMwiVisible.containsKey(subId)) {
226 boolean mwiVisible = mMwiVisible.get(subId);
227 if (mwiVisible) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900228 mApp.notifier.updatePhoneStateListeners(true);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800229 }
230 }
231 }
232
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700233 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
234 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
235 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
236 + subId);
237 return;
238 }
239
240 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
241 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
242 .apply();
243 }
244
245 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
246 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
247 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
248 return true;
249 }
250 return PreferenceManager
251 .getDefaultSharedPreferences(mContext)
252 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
253 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800254 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700255 * Updates the message waiting indicator (voicemail) notification.
256 *
257 * @param visible true if there are messages waiting
258 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800259 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700260 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800261 }
262
263 /**
264 * Updates the message waiting indicator (voicemail) notification.
265 *
266 * @param subId the subId to update.
267 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700268 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
269 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800270 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700271 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800272 if (!PhoneGlobals.sVoiceCapable) {
273 // Do not show the message waiting indicator on devices which are not voice capable.
274 // These events *should* be blocked at the telephony layer for such devices.
275 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
276 return;
277 }
278
Nancy Chen2cf7f292015-05-15 11:00:10 -0700279 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800280 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800281 mMwiVisible.put(subId, visible);
282
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700283 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800284 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800285 Log.w(LOG_TAG, "Found null phone for: " + subId);
286 return;
287 }
288
289 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
290 if (subInfo == null) {
291 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800292 return;
293 }
294
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700295 int resId = android.R.drawable.stat_notify_voicemail;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900296 if (mTelephonyManager.getPhoneCount() > 1) {
297 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
298 : R.drawable.stat_notify_voicemail_sub2;
299 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700300
301 // This Notification can get a lot fancier once we have more
302 // information about the current voicemail messages.
303 // (For example, the current voicemail system can't tell
304 // us the caller-id or timestamp of a message, or tell us the
305 // message count.)
306
307 // But for now, the UI is ultra-simple: if the MWI indication
308 // is supposed to be visible, just show a single generic
309 // notification.
310
311 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800312 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700313 if (DBG) log("- got vm number: '" + vmNumber + "'");
314
Andrew Leea82b8202014-11-21 16:18:28 -0800315 // The voicemail number may be null because:
316 // (1) This phone has no voicemail number.
317 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
318 // happen when the device first boots if we get a MWI notification when we
319 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800320 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
321 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700322 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800323 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700324 }
325
Bryce Lee5dc90842015-08-11 07:57:14 -0700326 Integer vmCount = null;
327
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800328 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700329 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700330 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
331 notificationTitle = String.format(titleFormat, vmCount);
332 }
333
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800334 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
335 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
336
337 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700338 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700339 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
340
341 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800342 notificationText = mContext.getString(
343 R.string.notification_voicemail_no_vm_number);
344
345 // If the voicemail number if unknown, instead of calling voicemail, take the user
346 // to the voicemail settings.
347 notificationText = mContext.getString(
348 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700349 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800350 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700351 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700352 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800353 if (mTelephonyManager.getPhoneCount() > 1) {
354 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800355 } else {
356 notificationText = String.format(
357 mContext.getString(R.string.notification_voicemail_text_format),
358 PhoneNumberUtils.formatNumber(vmNumber));
359 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800360 intent = new Intent(
361 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700362 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800363 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700364 }
365
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800366 PendingIntent pendingIntent =
Shuo Qian9a41a172020-04-24 19:19:31 -0700367 PendingIntent.getActivity(mContext, subId /* requestCode */, intent,
368 PendingIntent.FLAG_IMMUTABLE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700369
Nancy Chenb4a92702014-12-04 15:57:29 -0800370 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700371 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700372 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700373 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700374 builder.setSmallIcon(resId)
375 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800376 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700377 .setContentTitle(notificationTitle)
378 .setContentText(notificationText)
379 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800380 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700381 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700382 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Jordan Liu575c0d02019-07-09 16:29:48 -0700383 .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
Ta-wei Yen282a9702017-05-30 17:32:29 -0700384 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700385
Andrew Lee99d0ac22014-10-10 13:18:04 -0700386 final Notification notification = builder.build();
Meng Wang1a19e442019-10-11 10:09:00 -0700387 List<UserHandle> users = getUsersExcludeDying();
388 for (UserHandle userHandle : users) {
Amit Mahajan2f489b72019-10-08 11:21:52 -0700389 if (!hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700390 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700391 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800392 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700393 pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700394 notifyAsUser(
Bryce Lee5dc90842015-08-11 07:57:14 -0700395 Integer.toString(subId) /* tag */,
396 VOICEMAIL_NOTIFICATION,
397 notification,
398 userHandle);
399 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700400 }
401 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700402 } else {
Meng Wang1a19e442019-10-11 10:09:00 -0700403 List<UserHandle> users = getUsersExcludeDying();
404 for (UserHandle userHandle : users) {
Amit Mahajan2f489b72019-10-08 11:21:52 -0700405 if (!hasUserRestriction(
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800406 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700407 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800408 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700409 false, userHandle, isRefresh)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700410 cancelAsUser(
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800411 Integer.toString(subId) /* tag */,
412 VOICEMAIL_NOTIFICATION,
413 userHandle);
414 }
415 }
Bryce Lee5dc90842015-08-11 07:57:14 -0700416 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700417 }
418 }
419
Meng Wang1a19e442019-10-11 10:09:00 -0700420 private List<UserHandle> getUsersExcludeDying() {
421 long[] serialNumbersOfUsers =
422 mUserManager.getSerialNumbersOfUsers(/* excludeDying= */ true);
423 List<UserHandle> users = new ArrayList<>(serialNumbersOfUsers.length);
424 for (long serialNumber : serialNumbersOfUsers) {
425 users.add(mUserManager.getUserForSerialNumber(serialNumber));
426 }
427 return users;
428 }
429
Amit Mahajan2f489b72019-10-08 11:21:52 -0700430 private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
431 final List<UserManager.EnforcingUser> sources = mUserManager
432 .getUserRestrictionSources(restrictionKey, userHandle);
433 return (sources != null && !sources.isEmpty());
434 }
435
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700436 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800437 * Sends a broadcast with the voicemail notification information to the default dialer. This
438 * method is also used to indicate to the default dialer when to clear the
439 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700440 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800441 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700442 * @param count The number of pending voicemail messages to indicate on the notification. A
443 * Value of 0 is passed here to indicate that the notification should be cleared.
444 * @param number The voicemail phone number if specified.
445 * @param pendingIntent The intent that should be passed as the action to be taken.
446 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
447 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800448 * @param userHandle The user to receive the notification. Each user can have their own default
449 * dialer.
450 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700451 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800452 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
453 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700454 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800455
456 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
457 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700458 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700459 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800460 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
461 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yenafca2d62017-07-18 17:20:59 -0700462 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700463 if (count != null) {
464 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
465 }
466
467 // Additional information about the voicemail notification beyond the count is only
468 // present when the count not specified or greater than 0. The value of 0 represents
469 // clearing the notification, which does not require additional information.
470 if (count == null || count > 0) {
471 if (!TextUtils.isEmpty(number)) {
472 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
473 }
474
475 if (pendingIntent != null) {
476 intent.putExtra(isSettingsIntent
477 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
478 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
479 pendingIntent);
480 }
481 }
Tyler Gunn99ae2692020-12-09 11:35:21 -0800482
483 BroadcastOptions bopts = BroadcastOptions.makeBasic();
484 bopts.setTemporaryAppWhitelistDuration(VOICEMAIL_ALLOW_LIST_DURATION_MILLIS);
485 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE, bopts.toBundle());
Bryce Lee5dc90842015-08-11 07:57:14 -0700486 return true;
487 }
488
489 return false;
490 }
491
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800492 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
Tyler Gunn5ddfdc92019-10-31 13:08:23 -0700493 String dialerPackage = mContext.getSystemService(TelecomManager.class)
Tyler Gunn83adc052020-01-28 09:12:21 -0800494 .getDefaultDialerPackage(userHandle);
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800495 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
496 .setPackage(dialerPackage);
497 }
498
499 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
500 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
501 if (intent == null) {
502 return false;
503 }
504
505 List<ResolveInfo> receivers = mContext.getPackageManager()
506 .queryBroadcastReceivers(intent, 0);
507 return receivers.size() > 0;
508 }
509
Bryce Lee5dc90842015-08-11 07:57:14 -0700510 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700511 * Updates the message call forwarding indicator notification.
512 *
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530513 * @param visible true if call forwarding enabled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700514 */
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530515
516 /* package */ void updateCfi(int subId, boolean visible) {
517 updateCfi(subId, visible, false /* isRefresh */);
518 }
519
520 /**
521 * Updates the message call forwarding indicator notification.
522 *
523 * @param visible true if call forwarding enabled
524 */
525 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
Tyler Gunna584e2c2017-09-19 11:40:12 -0700526 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700527 if (visible) {
528 // If Unconditional Call Forwarding (forward all calls) for VOICE
529 // is enabled, just show a notification. We'll default to expanded
530 // view for now, so the there is less confusion about the icon. If
531 // it is deemed too weird to have CF indications as expanded views,
532 // then we'll flip the flag back.
533
534 // TODO: We may want to take a look to see if the notification can
535 // display the target to forward calls to. This will require some
536 // effort though, since there are multiple layers of messages that
537 // will need to propagate that information.
538
Andrew Leed5165b02014-12-05 15:53:58 -0800539 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
540 if (subInfo == null) {
541 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
542 return;
543 }
544
545 String notificationTitle;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900546 int resId = R.drawable.stat_sys_phone_call_forward;
Andrew Leed5165b02014-12-05 15:53:58 -0800547 if (mTelephonyManager.getPhoneCount() > 1) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900548 int slotId = SubscriptionManager.getSlotIndex(subId);
549 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
550 : R.drawable.stat_sys_phone_call_forward_sub2;
Sarah Chin2c5f23d2020-11-02 21:52:11 -0800551 if (subInfo.getDisplayName() != null) {
552 notificationTitle = subInfo.getDisplayName().toString();
553 } else {
554 notificationTitle = mContext.getString(R.string.labelCF);
555 }
Andrew Leed5165b02014-12-05 15:53:58 -0800556 } else {
557 notificationTitle = mContext.getString(R.string.labelCF);
558 }
559
fionaxu8b7620d2017-05-01 16:22:17 -0700560 Notification.Builder builder = new Notification.Builder(mContext)
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900561 .setSmallIcon(resId)
Andrew Leed5165b02014-12-05 15:53:58 -0800562 .setColor(subInfo.getIconTint())
563 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700564 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
565 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700566 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700567 .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530568 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700569
Tyler Gunn5bc21712021-04-30 15:07:59 -0700570 Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800571 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800572 SubscriptionInfoHelper.addExtrasToIntent(
573 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
fionaxu96ceebd2017-08-24 12:12:32 -0700574 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
Shuo Qian9a41a172020-04-24 19:19:31 -0700575 intent, PendingIntent.FLAG_IMMUTABLE));
Jordan Liu9ddde022019-08-05 13:49:08 -0700576 notifyAsUser(
fionaxu96ceebd2017-08-24 12:12:32 -0700577 Integer.toString(subId) /* tag */,
578 CALL_FORWARD_NOTIFICATION,
579 builder.build(),
580 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700581 } else {
Meng Wang21487ab2019-11-21 10:44:42 -0800582 List<UserHandle> users = getUsersExcludeDying();
583 for (UserHandle user : users) {
584 if (mUserManager.isManagedProfile(user.getIdentifier())) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900585 continue;
586 }
Jordan Liu9ddde022019-08-05 13:49:08 -0700587 cancelAsUser(
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900588 Integer.toString(subId) /* tag */,
589 CALL_FORWARD_NOTIFICATION,
Meng Wang21487ab2019-11-21 10:44:42 -0800590 user);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900591 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700592 }
593 }
594
595 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700596 * Shows either:
597 * 1) the "Data roaming is on" notification, which
598 * appears when you're roaming and you have the "data roaming" feature turned on for the
599 * given {@code subId}.
600 * or
601 * 2) the "data disconnected due to roaming" notification, which
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700602 * appears when you lose data connectivity because you're roaming and
Pengquan Meng8783d932017-10-16 14:57:40 -0700603 * you have the "data roaming" feature turned off for the given {@code subId}.
Jordan Liuc353b162019-07-23 15:54:41 -0700604 * @param subId which subscription it's notifying about.
605 * @param roamingOn whether currently roaming is on or off. If true, we show notification
606 * 1) above; else we show notification 2).
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700607 */
Jordan Liuc353b162019-07-23 15:54:41 -0700608 /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
609 if (DBG) {
610 log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
611 + " on subId " + subId);
612 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700613
614 // "Mobile network settings" screen / dialog
Nazanin Bakhshi682c77d2019-05-02 17:22:27 -0700615 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
Jeff Davidson6d9bf522017-11-03 14:51:13 -0700616 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Shuo Qian9a41a172020-04-24 19:19:31 -0700617 PendingIntent contentIntent = PendingIntent.getActivity(
618 mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700619
Jordan Liuc353b162019-07-23 15:54:41 -0700620 CharSequence contentTitle = mContext.getText(roamingOn
621 ? R.string.roaming_on_notification_title
622 : R.string.roaming_notification_title);
623 CharSequence contentText = mContext.getText(roamingOn
624 ? R.string.roaming_enabled_message
625 : R.string.roaming_reenable_message);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700626
fionaxu8b7620d2017-05-01 16:22:17 -0700627 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700628 .setSmallIcon(android.R.drawable.stat_sys_warning)
Jordan Liuc353b162019-07-23 15:54:41 -0700629 .setContentTitle(contentTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700630 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700631 .setContentText(contentText)
Jordan Liu575c0d02019-07-09 16:29:48 -0700632 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
fionaxu96ceebd2017-08-24 12:12:32 -0700633 .setContentIntent(contentIntent);
634 final Notification notif =
635 new Notification.BigTextStyle(builder).bigText(contentText).build();
Jordan Liu9ddde022019-08-05 13:49:08 -0700636 notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
637 }
638
639 private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
640 try {
641 Context contextForUser =
642 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
643 NotificationManager notificationManager =
644 (NotificationManager) contextForUser.getSystemService(
645 Context.NOTIFICATION_SERVICE);
646 notificationManager.notify(tag, id, notification);
647 } catch (PackageManager.NameNotFoundException e) {
648 Log.e(LOG_TAG, "unable to notify for user " + user);
649 e.printStackTrace();
650 }
651 }
652
653 private void cancelAsUser(String tag, int id, UserHandle user) {
654 try {
655 Context contextForUser =
656 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
657 NotificationManager notificationManager =
658 (NotificationManager) contextForUser.getSystemService(
659 Context.NOTIFICATION_SERVICE);
660 notificationManager.cancel(tag, id);
661 } catch (PackageManager.NameNotFoundException e) {
662 Log.e(LOG_TAG, "unable to cancel for user " + user);
663 e.printStackTrace();
664 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700665 }
666
667 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700668 * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700669 */
Jordan Liuc353b162019-07-23 15:54:41 -0700670 /* package */ void hideDataRoamingNotification() {
671 if (DBG) log("hideDataRoamingNotification()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700672 cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700673 }
674
675 /**
chen xubaf9fe52019-07-02 17:28:24 -0700676 * Shows the "Limited SIM functionality" warning notification, which appears when using a
677 * special carrier under dual sim. limited function applies for DSDS in general when two SIM
678 * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
679 */
680 public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
681 if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
682 + " subId: " + subId);
683 if (mLimitedSimFunctionNotify.contains(subId)) {
684 // handle the case that user swipe the notification but condition triggers
685 // frequently which cause the same notification consistently displayed.
686 if (DBG) log("showLimitedSimFunctionWarningNotification, "
687 + "not display again if already displayed");
688 return;
689 }
690 // Navigate to "Network Selection Settings" which list all subscriptions.
691 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
Shuo Qian9a41a172020-04-24 19:19:31 -0700692 new Intent(ACTION_MOBILE_NETWORK_LIST), PendingIntent.FLAG_IMMUTABLE);
Chen Xue1bbfd22019-09-18 17:17:31 -0700693 // Display phone number from the other sub
694 String line1Num = null;
695 SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
696 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
697 List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
698 for (SubscriptionInfo sub : subList) {
699 if (sub.getSubscriptionId() != subId) {
700 line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
701 }
702 }
chen xubaf9fe52019-07-02 17:28:24 -0700703 final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
704 String.format(mContext.getText(
Chen Xu251ce8f2019-09-13 20:24:22 -0700705 R.string.limited_sim_function_notification_message).toString(), carrierName) :
chen xubaf9fe52019-07-02 17:28:24 -0700706 String.format(mContext.getText(
707 R.string.limited_sim_function_with_phone_num_notification_message).toString(),
Chen Xu251ce8f2019-09-13 20:24:22 -0700708 carrierName, line1Num);
chen xubaf9fe52019-07-02 17:28:24 -0700709 final Notification.Builder builder = new Notification.Builder(mContext)
710 .setSmallIcon(R.drawable.ic_sim_card)
711 .setContentTitle(mContext.getText(
712 R.string.limited_sim_function_notification_title))
713 .setContentText(contentText)
714 .setOnlyAlertOnce(true)
715 .setOngoing(true)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700716 .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
chen xubaf9fe52019-07-02 17:28:24 -0700717 .setContentIntent(contentIntent);
718 final Notification notification = new Notification.BigTextStyle(builder).bigText(
719 contentText).build();
720
Jordan Liu9ddde022019-08-05 13:49:08 -0700721 notifyAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700722 LIMITED_SIM_FUNCTION_NOTIFICATION,
723 notification, UserHandle.ALL);
724 mLimitedSimFunctionNotify.add(subId);
725 }
726
727 /**
728 * Dismiss the "Limited SIM functionality" warning notification for the given subId.
729 */
730 public void dismissLimitedSimFunctionWarningNotification(int subId) {
731 if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
732 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
733 // dismiss all notifications
734 for (int id : mLimitedSimFunctionNotify) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700735 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700736 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
737 }
738 mLimitedSimFunctionNotify.clear();
739 } else if (mLimitedSimFunctionNotify.contains(subId)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700740 cancelAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700741 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
742 mLimitedSimFunctionNotify.remove(subId);
743 }
744 }
745
746 /**
747 * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
748 */
749 public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
750 if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
751 // dismiss notification for inactive subscriptions.
752 // handle the corner case that SIM change by SIM refresh doesn't clear the notification
753 // from the old SIM if both old & new SIM configured to display the notification.
754 mLimitedSimFunctionNotify.removeIf(id -> {
755 if (!mSubscriptionManager.isActiveSubId(id)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700756 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700757 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
758 return true;
759 }
760 return false;
761 });
762 }
763
764 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700765 * Display the network selection "no service" notification
766 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700767 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700768 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700769 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700770 if (DBG) log("showNetworkSelection(" + operator + ")...");
771
Youming Ye0509b532018-09-14 16:21:17 -0700772 if (!TextUtils.isEmpty(operator)) {
773 operator = String.format(" (%s)", operator);
774 }
fionaxu8b7620d2017-05-01 16:22:17 -0700775 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700776 .setSmallIcon(android.R.drawable.stat_sys_warning)
777 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
778 .setContentText(
779 mContext.getString(R.string.notification_network_selection_text, operator))
780 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700781 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700782 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700783
784 // create the target network operators settings intent
785 Intent intent = new Intent(Intent.ACTION_MAIN);
786 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
787 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700788 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700789 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700790 mContext.getString(R.string.mobile_network_settings_package),
791 mContext.getString(R.string.mobile_network_settings_class)));
Pengquan Meng984ba422019-09-05 18:00:06 -0700792 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Shuo Qian9a41a172020-04-24 19:19:31 -0700793 builder.setContentIntent(
794 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE));
Jordan Liu9ddde022019-08-05 13:49:08 -0700795 notifyAsUser(
irisykyang25202462018-11-13 14:49:54 +0800796 Integer.toString(subId) /* tag */,
fionaxu96ceebd2017-08-24 12:12:32 -0700797 SELECTED_OPERATOR_FAIL_NOTIFICATION,
798 builder.build(),
799 UserHandle.ALL);
irisykyang25202462018-11-13 14:49:54 +0800800 mSelectedUnavailableNotify.put(subId, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700801 }
802
803 /**
804 * Turn off the network selection "no service" notification
805 */
irisykyang25202462018-11-13 14:49:54 +0800806 private void cancelNetworkSelection(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700807 if (DBG) log("cancelNetworkSelection()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700808 cancelAsUser(
irisykyang25202462018-11-13 14:49:54 +0800809 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
810 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700811 }
812
813 /**
814 * Update notification about no service of user selected operator
815 *
816 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700817 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700818 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700819 void updateNetworkSelection(int serviceState, int subId) {
820 int phoneId = SubscriptionManager.getPhoneId(subId);
821 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
822 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
823 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800824 if (SubscriptionManager.isValidSubscriptionId(subId)) {
fionaxu996a1c32018-04-13 15:00:37 -0700825 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
826 String selectedNetworkOperatorName =
827 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
828 // get the shared preference of network_selection.
829 // empty is auto mode, otherwise it is the operator alpha name
830 // in case there is no operator name, check the operator numeric
831 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
832 selectedNetworkOperatorName =
833 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
834 }
835 boolean isManualSelection;
fionaxud6aac662018-03-14 16:44:29 -0700836 // if restoring manual selection is controlled by framework, then get network
837 // selection from shared preference, otherwise get from real network indicators.
Daniel Brightebb4eb72020-02-18 15:16:33 -0800838 boolean restoreSelection = !mContext.getResources().getBoolean(
839 com.android.internal.R.bool.skip_restoring_network_selection);
fionaxud6aac662018-03-14 16:44:29 -0700840 if (restoreSelection) {
fionaxud6aac662018-03-14 16:44:29 -0700841 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
842 } else {
fionaxud6aac662018-03-14 16:44:29 -0700843 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
Amit Mahajana60be872015-01-15 16:05:08 -0800844 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700845
fionaxud6aac662018-03-14 16:44:29 -0700846 if (DBG) {
847 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
848 + (isManualSelection ? selectedNetworkOperatorName : ""));
849 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700850
irisykyang25202462018-11-13 14:49:54 +0800851 if (isManualSelection) {
852 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
853 shouldShowNotification(serviceState, subId);
Amit Mahajana60be872015-01-15 16:05:08 -0800854 } else {
irisykyang25202462018-11-13 14:49:54 +0800855 dismissNetworkSelectionNotification(subId);
856 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700857 }
858 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800859 if (DBG) log("updateNetworkSelection()..." + "state = " +
860 serviceState + " not updating network due to invalid subId " + subId);
irisykyang25202462018-11-13 14:49:54 +0800861 dismissNetworkSelectionNotificationForInactiveSubId();
862 }
863 }
864 }
865
866 private void dismissNetworkSelectionNotification(int subId) {
867 if (mSelectedUnavailableNotify.get(subId, false)) {
868 cancelNetworkSelection(subId);
869 mSelectedUnavailableNotify.remove(subId);
870 }
871 }
872
873 private void dismissNetworkSelectionNotificationForInactiveSubId() {
874 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
875 int subId = mSelectedUnavailableNotify.keyAt(i);
876 if (!mSubscriptionManager.isActiveSubId(subId)) {
877 dismissNetworkSelectionNotification(subId);
878 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700879 }
880 }
881 }
882
883 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
884 if (mToast != null) {
885 mToast.cancel();
886 }
887
888 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
889 mToast.show();
890 }
891
892 private void log(String msg) {
893 Log.d(LOG_TAG, msg);
894 }
Tyler Gunna584e2c2017-09-19 11:40:12 -0700895
896 private void logi(String msg) {
897 Log.i(LOG_TAG, msg);
898 }
irisykyang25202462018-11-13 14:49:54 +0800899
900 /**
901 * In case network selection notification shows up repeatedly under
902 * unstable network condition. The logic is to check whether or not
903 * the service state keeps in no service condition for at least
904 * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
905 * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
906 * To avoid the notification showing up for the momentary state.
907 */
908 private void shouldShowNotification(int serviceState, int subId) {
909 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
910 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
911 != ServiceState.STATE_OUT_OF_SERVICE) {
912 mOOSTimestamp.put(subId, getTimeStamp());
913 }
914 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
915 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
916 || mPendingEventCounter.get(subId, 0)
917 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
918 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
919 clearUpNetworkSelectionNotificationParam(subId);
920 } else {
921 startPendingNetworkSelectionNotification(subId);
922 }
923 } else {
924 dismissNetworkSelectionNotification(subId);
925 }
926 mPreviousServiceState.put(subId, serviceState);
927 if (DBG) {
928 log("shouldShowNotification()..." + " subId = " + subId
929 + " serviceState = " + serviceState
930 + " mOOSTimestamp = " + mOOSTimestamp
931 + " mPendingEventCounter = " + mPendingEventCounter);
932 }
933 }
934
935 private void startPendingNetworkSelectionNotification(int subId) {
936 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
937 if (DBG) {
938 log("startPendingNetworkSelectionNotification: subId = " + subId);
939 }
940 mHandler.sendMessageDelayed(
941 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
942 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
943 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
944 }
945 }
946
947 private void clearUpNetworkSelectionNotificationParam(int subId) {
948 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
949 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
950 }
951 mPreviousServiceState.remove(subId);
952 mOOSTimestamp.remove(subId);
953 mPendingEventCounter.remove(subId);
954 mSelectedNetworkOperatorName.remove(subId);
955 }
956
957 private static long getTimeStamp() {
958 return SystemClock.elapsedRealtime();
959 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700960}