blob: e9fd26df41a9f2f01b88779a42b07d9f4fd96231 [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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.app.StatusBarManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070026import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070027import android.content.Context;
28import android.content.Intent;
29import android.content.SharedPreferences;
Jordan Liu9ddde022019-08-05 13:49:08 -070030import android.content.pm.PackageManager;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080031import android.content.pm.ResolveInfo;
Andrew Lee99d0ac22014-10-10 13:18:04 -070032import android.content.pm.UserInfo;
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;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080045import android.telecom.DefaultDialerManager;
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;
51import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080052import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080053import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080054import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070055import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080056import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057import android.util.Log;
irisykyang25202462018-11-13 14:49:54 +080058import android.util.SparseArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070059import android.widget.Toast;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070060
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
chen xubaf9fe52019-07-02 17:28:24 -070067import java.util.HashSet;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080068import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070069import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080070import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070071
Santos Cordon7d4ddf62013-07-10 11:58:08 -070072/**
73 * NotificationManager-related utility code for the Phone app.
74 *
75 * This is a singleton object which acts as the interface to the
76 * framework's NotificationManager, and is used to display status bar
77 * icons and control other status bar-related behavior.
78 *
79 * @see PhoneGlobals.notificationMgr
80 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070081public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080082 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070083 private static final boolean DBG =
84 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
85 // Do not check in with VDBG = true, since that may write PII to the system log.
86 private static final boolean VDBG = false;
87
Ta-wei Yenb29425b2016-09-21 17:28:14 -070088 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
89 "mwi_should_check_vvm_configuration_state_";
90
Santos Cordon7d4ddf62013-07-10 11:58:08 -070091 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070092 static final int MMI_NOTIFICATION = 1;
93 static final int NETWORK_SELECTION_NOTIFICATION = 2;
94 static final int VOICEMAIL_NOTIFICATION = 3;
95 static final int CALL_FORWARD_NOTIFICATION = 4;
Jordan Liuc353b162019-07-23 15:54:41 -070096 static final int DATA_ROAMING_NOTIFICATION = 5;
Santos Cordonf68db2e2014-07-02 14:40:44 -070097 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
chen xubaf9fe52019-07-02 17:28:24 -070098 static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070099
irisykyang25202462018-11-13 14:49:54 +0800100 // Event for network selection notification.
101 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
102
103 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
104 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
105
106 private static final int STATE_UNKNOWN_SERVICE = -1;
107
chen xubaf9fe52019-07-02 17:28:24 -0700108 private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
109
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700110 /** The singleton NotificationMgr instance. */
111 private static NotificationMgr sInstance;
112
113 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700114
115 private Context mContext;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700116 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -0700117 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700118 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800119 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800120 private TelecomManager mTelecomManager;
121 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700122
irisykyang25202462018-11-13 14:49:54 +0800123 // used to track the notification of selected network unavailable, per subscription id.
124 private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125
chen xubaf9fe52019-07-02 17:28:24 -0700126 // used to track the notification of limited sim function under dual sim, per subscription id.
127 private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
128
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800129 // used to track whether the message waiting indicator is visible, per subscription id.
130 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
131
irisykyang25202462018-11-13 14:49:54 +0800132 // those flags are used to track whether to show network selection notification or not.
133 private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
134 private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
135 private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
136 // maps each subId to selected network operator name.
137 private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
138
139 private final Handler mHandler = new Handler() {
140 @Override
141 public void handleMessage(Message msg) {
142 switch (msg.what) {
143 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
144 int subId = (int) msg.obj;
145 TelephonyManager telephonyManager =
146 mTelephonyManager.createForSubscriptionId(subId);
147 if (telephonyManager.getServiceState() != null) {
148 shouldShowNotification(telephonyManager.getServiceState().getState(),
149 subId);
150 }
151 break;
152 }
153 }
154 };
155
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700156 /**
157 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700158 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700159 */
160 private NotificationMgr(PhoneGlobals app) {
161 mApp = app;
162 mContext = app;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700163 mStatusBarManager =
164 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700165 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800166 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800167 mTelecomManager = TelecomManager.from(mContext);
168 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700169 }
170
171 /**
172 * Initialize the singleton NotificationMgr instance.
173 *
174 * This is only done once, at startup, from PhoneApp.onCreate().
175 * From then on, the NotificationMgr instance is available via the
176 * PhoneApp's public "notificationMgr" field, which is why there's no
177 * getInstance() method here.
178 */
179 /* package */ static NotificationMgr init(PhoneGlobals app) {
180 synchronized (NotificationMgr.class) {
181 if (sInstance == null) {
182 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700183 } else {
184 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
185 }
186 return sInstance;
187 }
188 }
189
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700190 /** The projection to use when querying the phones table */
191 static final String[] PHONES_PROJECTION = new String[] {
192 PhoneLookup.NUMBER,
193 PhoneLookup.DISPLAY_NAME,
194 PhoneLookup._ID
195 };
196
197 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800198 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
199 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
200 * settings screen. The voicemail notification sound is suppressed.
201 *
202 * @param subId The subscription Id.
203 */
204 /* package */ void refreshMwi(int subId) {
205 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
206 // reference the single subid stored in the mMwiVisible map.
Ta-wei Yena1390d42017-12-04 15:11:33 -0800207 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800208 if (mMwiVisible.keySet().size() == 1) {
209 Set<Integer> keySet = mMwiVisible.keySet();
210 Iterator<Integer> keyIt = keySet.iterator();
211 if (!keyIt.hasNext()) {
212 return;
213 }
214 subId = keyIt.next();
215 }
216 }
217 if (mMwiVisible.containsKey(subId)) {
218 boolean mwiVisible = mMwiVisible.get(subId);
219 if (mwiVisible) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900220 mApp.notifier.updatePhoneStateListeners(true);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800221 }
222 }
223 }
224
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700225 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
226 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
227 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
228 + subId);
229 return;
230 }
231
232 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
233 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
234 .apply();
235 }
236
237 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
238 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
239 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
240 return true;
241 }
242 return PreferenceManager
243 .getDefaultSharedPreferences(mContext)
244 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
245 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800246 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700247 * Updates the message waiting indicator (voicemail) notification.
248 *
249 * @param visible true if there are messages waiting
250 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800251 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700252 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800253 }
254
255 /**
256 * Updates the message waiting indicator (voicemail) notification.
257 *
258 * @param subId the subId to update.
259 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700260 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
261 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800262 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700263 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800264 if (!PhoneGlobals.sVoiceCapable) {
265 // Do not show the message waiting indicator on devices which are not voice capable.
266 // These events *should* be blocked at the telephony layer for such devices.
267 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
268 return;
269 }
270
Nancy Chen2cf7f292015-05-15 11:00:10 -0700271 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800272 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800273 mMwiVisible.put(subId, visible);
274
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700275 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800276 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800277 Log.w(LOG_TAG, "Found null phone for: " + subId);
278 return;
279 }
280
281 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
282 if (subInfo == null) {
283 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800284 return;
285 }
286
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700287 int resId = android.R.drawable.stat_notify_voicemail;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900288 if (mTelephonyManager.getPhoneCount() > 1) {
289 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
290 : R.drawable.stat_notify_voicemail_sub2;
291 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700292
293 // This Notification can get a lot fancier once we have more
294 // information about the current voicemail messages.
295 // (For example, the current voicemail system can't tell
296 // us the caller-id or timestamp of a message, or tell us the
297 // message count.)
298
299 // But for now, the UI is ultra-simple: if the MWI indication
300 // is supposed to be visible, just show a single generic
301 // notification.
302
303 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800304 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700305 if (DBG) log("- got vm number: '" + vmNumber + "'");
306
Andrew Leea82b8202014-11-21 16:18:28 -0800307 // The voicemail number may be null because:
308 // (1) This phone has no voicemail number.
309 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
310 // happen when the device first boots if we get a MWI notification when we
311 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800312 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
313 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800315 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700316 }
317
Bryce Lee5dc90842015-08-11 07:57:14 -0700318 Integer vmCount = null;
319
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800320 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700321 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700322 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
323 notificationTitle = String.format(titleFormat, vmCount);
324 }
325
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800326 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
327 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
328
329 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700330 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700331 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
332
333 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800334 notificationText = mContext.getString(
335 R.string.notification_voicemail_no_vm_number);
336
337 // If the voicemail number if unknown, instead of calling voicemail, take the user
338 // to the voicemail settings.
339 notificationText = mContext.getString(
340 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700341 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800342 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700343 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700344 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800345 if (mTelephonyManager.getPhoneCount() > 1) {
346 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800347 } else {
348 notificationText = String.format(
349 mContext.getString(R.string.notification_voicemail_text_format),
350 PhoneNumberUtils.formatNumber(vmNumber));
351 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800352 intent = new Intent(
353 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700354 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800355 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700356 }
357
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800358 PendingIntent pendingIntent =
359 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700360
Nancy Chenb4a92702014-12-04 15:57:29 -0800361 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700362 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700363 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700364 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700365 builder.setSmallIcon(resId)
366 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800367 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700368 .setContentTitle(notificationTitle)
369 .setContentText(notificationText)
370 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800371 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700372 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700373 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Jordan Liu575c0d02019-07-09 16:29:48 -0700374 .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
Ta-wei Yen282a9702017-05-30 17:32:29 -0700375 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700376
Andrew Lee99d0ac22014-10-10 13:18:04 -0700377 final Notification notification = builder.build();
378 List<UserInfo> users = mUserManager.getUsers(true);
Jordan Liua55aaaa2019-08-28 15:33:05 -0700379 for (UserInfo user : users) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700380 final UserHandle userHandle = user.getUserHandle();
Amit Mahajan2f489b72019-10-08 11:21:52 -0700381 if (!hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700382 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700383 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800384 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700385 pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700386 notifyAsUser(
Bryce Lee5dc90842015-08-11 07:57:14 -0700387 Integer.toString(subId) /* tag */,
388 VOICEMAIL_NOTIFICATION,
389 notification,
390 userHandle);
391 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700392 }
393 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700394 } else {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800395 List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
Jordan Liua55aaaa2019-08-28 15:33:05 -0700396 for (UserInfo user : users) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800397 final UserHandle userHandle = user.getUserHandle();
Amit Mahajan2f489b72019-10-08 11:21:52 -0700398 if (!hasUserRestriction(
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800399 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700400 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800401 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700402 false, userHandle, isRefresh)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700403 cancelAsUser(
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800404 Integer.toString(subId) /* tag */,
405 VOICEMAIL_NOTIFICATION,
406 userHandle);
407 }
408 }
Bryce Lee5dc90842015-08-11 07:57:14 -0700409 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700410 }
411 }
412
Amit Mahajan2f489b72019-10-08 11:21:52 -0700413 private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
414 final List<UserManager.EnforcingUser> sources = mUserManager
415 .getUserRestrictionSources(restrictionKey, userHandle);
416 return (sources != null && !sources.isEmpty());
417 }
418
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700419 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800420 * Sends a broadcast with the voicemail notification information to the default dialer. This
421 * method is also used to indicate to the default dialer when to clear the
422 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700423 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800424 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700425 * @param count The number of pending voicemail messages to indicate on the notification. A
426 * Value of 0 is passed here to indicate that the notification should be cleared.
427 * @param number The voicemail phone number if specified.
428 * @param pendingIntent The intent that should be passed as the action to be taken.
429 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
430 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800431 * @param userHandle The user to receive the notification. Each user can have their own default
432 * dialer.
433 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700434 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800435 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
436 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700437 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800438
439 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
440 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700441 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700442 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800443 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
444 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yenafca2d62017-07-18 17:20:59 -0700445 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700446 if (count != null) {
447 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
448 }
449
450 // Additional information about the voicemail notification beyond the count is only
451 // present when the count not specified or greater than 0. The value of 0 represents
452 // clearing the notification, which does not require additional information.
453 if (count == null || count > 0) {
454 if (!TextUtils.isEmpty(number)) {
455 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
456 }
457
458 if (pendingIntent != null) {
459 intent.putExtra(isSettingsIntent
460 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
461 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
462 pendingIntent);
463 }
464 }
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800465 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700466 return true;
467 }
468
469 return false;
470 }
471
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800472 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
473 String dialerPackage = DefaultDialerManager
474 .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
475 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
476 .setPackage(dialerPackage);
477 }
478
479 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
480 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
481 if (intent == null) {
482 return false;
483 }
484
485 List<ResolveInfo> receivers = mContext.getPackageManager()
486 .queryBroadcastReceivers(intent, 0);
487 return receivers.size() > 0;
488 }
489
Bryce Lee5dc90842015-08-11 07:57:14 -0700490 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700491 * Updates the message call forwarding indicator notification.
492 *
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530493 * @param visible true if call forwarding enabled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700494 */
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530495
496 /* package */ void updateCfi(int subId, boolean visible) {
497 updateCfi(subId, visible, false /* isRefresh */);
498 }
499
500 /**
501 * Updates the message call forwarding indicator notification.
502 *
503 * @param visible true if call forwarding enabled
504 */
505 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
Tyler Gunna584e2c2017-09-19 11:40:12 -0700506 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700507 if (visible) {
508 // If Unconditional Call Forwarding (forward all calls) for VOICE
509 // is enabled, just show a notification. We'll default to expanded
510 // view for now, so the there is less confusion about the icon. If
511 // it is deemed too weird to have CF indications as expanded views,
512 // then we'll flip the flag back.
513
514 // TODO: We may want to take a look to see if the notification can
515 // display the target to forward calls to. This will require some
516 // effort though, since there are multiple layers of messages that
517 // will need to propagate that information.
518
Andrew Leed5165b02014-12-05 15:53:58 -0800519 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
520 if (subInfo == null) {
521 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
522 return;
523 }
524
525 String notificationTitle;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900526 int resId = R.drawable.stat_sys_phone_call_forward;
Andrew Leed5165b02014-12-05 15:53:58 -0800527 if (mTelephonyManager.getPhoneCount() > 1) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900528 int slotId = SubscriptionManager.getSlotIndex(subId);
529 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
530 : R.drawable.stat_sys_phone_call_forward_sub2;
Andrew Leed5165b02014-12-05 15:53:58 -0800531 notificationTitle = subInfo.getDisplayName().toString();
532 } else {
533 notificationTitle = mContext.getString(R.string.labelCF);
534 }
535
fionaxu8b7620d2017-05-01 16:22:17 -0700536 Notification.Builder builder = new Notification.Builder(mContext)
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900537 .setSmallIcon(resId)
Andrew Leed5165b02014-12-05 15:53:58 -0800538 .setColor(subInfo.getIconTint())
539 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700540 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
541 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700542 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700543 .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530544 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700545
Andrew Lee99d0ac22014-10-10 13:18:04 -0700546 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800547 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700548 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800549 SubscriptionInfoHelper.addExtrasToIntent(
550 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
fionaxu96ceebd2017-08-24 12:12:32 -0700551 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
552 intent, 0));
Jordan Liu9ddde022019-08-05 13:49:08 -0700553 notifyAsUser(
fionaxu96ceebd2017-08-24 12:12:32 -0700554 Integer.toString(subId) /* tag */,
555 CALL_FORWARD_NOTIFICATION,
556 builder.build(),
557 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700558 } else {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900559 List<UserInfo> users = mUserManager.getUsers(true);
560 for (UserInfo user : users) {
Jordan Liua55aaaa2019-08-28 15:33:05 -0700561 if (mUserManager.isManagedProfile(user.getUserHandle().getIdentifier())) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900562 continue;
563 }
564 UserHandle userHandle = user.getUserHandle();
Jordan Liu9ddde022019-08-05 13:49:08 -0700565 cancelAsUser(
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900566 Integer.toString(subId) /* tag */,
567 CALL_FORWARD_NOTIFICATION,
568 userHandle);
569 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700570 }
571 }
572
573 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700574 * Shows either:
575 * 1) the "Data roaming is on" notification, which
576 * appears when you're roaming and you have the "data roaming" feature turned on for the
577 * given {@code subId}.
578 * or
579 * 2) the "data disconnected due to roaming" notification, which
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700580 * appears when you lose data connectivity because you're roaming and
Pengquan Meng8783d932017-10-16 14:57:40 -0700581 * you have the "data roaming" feature turned off for the given {@code subId}.
Jordan Liuc353b162019-07-23 15:54:41 -0700582 * @param subId which subscription it's notifying about.
583 * @param roamingOn whether currently roaming is on or off. If true, we show notification
584 * 1) above; else we show notification 2).
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700585 */
Jordan Liuc353b162019-07-23 15:54:41 -0700586 /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
587 if (DBG) {
588 log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
589 + " on subId " + subId);
590 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700591
592 // "Mobile network settings" screen / dialog
Nazanin Bakhshi682c77d2019-05-02 17:22:27 -0700593 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
Jeff Davidson6d9bf522017-11-03 14:51:13 -0700594 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Pengquan Meng8783d932017-10-16 14:57:40 -0700595 PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700596
Jordan Liuc353b162019-07-23 15:54:41 -0700597 CharSequence contentTitle = mContext.getText(roamingOn
598 ? R.string.roaming_on_notification_title
599 : R.string.roaming_notification_title);
600 CharSequence contentText = mContext.getText(roamingOn
601 ? R.string.roaming_enabled_message
602 : R.string.roaming_reenable_message);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700603
fionaxu8b7620d2017-05-01 16:22:17 -0700604 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700605 .setSmallIcon(android.R.drawable.stat_sys_warning)
Jordan Liuc353b162019-07-23 15:54:41 -0700606 .setContentTitle(contentTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700607 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700608 .setContentText(contentText)
Jordan Liu575c0d02019-07-09 16:29:48 -0700609 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
fionaxu96ceebd2017-08-24 12:12:32 -0700610 .setContentIntent(contentIntent);
611 final Notification notif =
612 new Notification.BigTextStyle(builder).bigText(contentText).build();
Jordan Liu9ddde022019-08-05 13:49:08 -0700613 notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
614 }
615
616 private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
617 try {
618 Context contextForUser =
619 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
620 NotificationManager notificationManager =
621 (NotificationManager) contextForUser.getSystemService(
622 Context.NOTIFICATION_SERVICE);
623 notificationManager.notify(tag, id, notification);
624 } catch (PackageManager.NameNotFoundException e) {
625 Log.e(LOG_TAG, "unable to notify for user " + user);
626 e.printStackTrace();
627 }
628 }
629
630 private void cancelAsUser(String tag, int id, UserHandle user) {
631 try {
632 Context contextForUser =
633 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
634 NotificationManager notificationManager =
635 (NotificationManager) contextForUser.getSystemService(
636 Context.NOTIFICATION_SERVICE);
637 notificationManager.cancel(tag, id);
638 } catch (PackageManager.NameNotFoundException e) {
639 Log.e(LOG_TAG, "unable to cancel for user " + user);
640 e.printStackTrace();
641 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700642 }
643
644 /**
Jordan Liuc353b162019-07-23 15:54:41 -0700645 * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700646 */
Jordan Liuc353b162019-07-23 15:54:41 -0700647 /* package */ void hideDataRoamingNotification() {
648 if (DBG) log("hideDataRoamingNotification()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700649 cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700650 }
651
652 /**
chen xubaf9fe52019-07-02 17:28:24 -0700653 * Shows the "Limited SIM functionality" warning notification, which appears when using a
654 * special carrier under dual sim. limited function applies for DSDS in general when two SIM
655 * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
656 */
657 public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
658 if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
659 + " subId: " + subId);
660 if (mLimitedSimFunctionNotify.contains(subId)) {
661 // handle the case that user swipe the notification but condition triggers
662 // frequently which cause the same notification consistently displayed.
663 if (DBG) log("showLimitedSimFunctionWarningNotification, "
664 + "not display again if already displayed");
665 return;
666 }
667 // Navigate to "Network Selection Settings" which list all subscriptions.
668 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
669 new Intent(ACTION_MOBILE_NETWORK_LIST), 0);
Chen Xue1bbfd22019-09-18 17:17:31 -0700670 // Display phone number from the other sub
671 String line1Num = null;
672 SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
673 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
674 List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
675 for (SubscriptionInfo sub : subList) {
676 if (sub.getSubscriptionId() != subId) {
677 line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
678 }
679 }
chen xubaf9fe52019-07-02 17:28:24 -0700680 final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
681 String.format(mContext.getText(
Chen Xu251ce8f2019-09-13 20:24:22 -0700682 R.string.limited_sim_function_notification_message).toString(), carrierName) :
chen xubaf9fe52019-07-02 17:28:24 -0700683 String.format(mContext.getText(
684 R.string.limited_sim_function_with_phone_num_notification_message).toString(),
Chen Xu251ce8f2019-09-13 20:24:22 -0700685 carrierName, line1Num);
chen xubaf9fe52019-07-02 17:28:24 -0700686 final Notification.Builder builder = new Notification.Builder(mContext)
687 .setSmallIcon(R.drawable.ic_sim_card)
688 .setContentTitle(mContext.getText(
689 R.string.limited_sim_function_notification_title))
690 .setContentText(contentText)
691 .setOnlyAlertOnce(true)
692 .setOngoing(true)
Jordan Liua55aaaa2019-08-28 15:33:05 -0700693 .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
chen xubaf9fe52019-07-02 17:28:24 -0700694 .setContentIntent(contentIntent);
695 final Notification notification = new Notification.BigTextStyle(builder).bigText(
696 contentText).build();
697
Jordan Liu9ddde022019-08-05 13:49:08 -0700698 notifyAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700699 LIMITED_SIM_FUNCTION_NOTIFICATION,
700 notification, UserHandle.ALL);
701 mLimitedSimFunctionNotify.add(subId);
702 }
703
704 /**
705 * Dismiss the "Limited SIM functionality" warning notification for the given subId.
706 */
707 public void dismissLimitedSimFunctionWarningNotification(int subId) {
708 if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
709 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
710 // dismiss all notifications
711 for (int id : mLimitedSimFunctionNotify) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700712 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700713 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
714 }
715 mLimitedSimFunctionNotify.clear();
716 } else if (mLimitedSimFunctionNotify.contains(subId)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700717 cancelAsUser(Integer.toString(subId),
chen xubaf9fe52019-07-02 17:28:24 -0700718 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
719 mLimitedSimFunctionNotify.remove(subId);
720 }
721 }
722
723 /**
724 * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
725 */
726 public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
727 if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
728 // dismiss notification for inactive subscriptions.
729 // handle the corner case that SIM change by SIM refresh doesn't clear the notification
730 // from the old SIM if both old & new SIM configured to display the notification.
731 mLimitedSimFunctionNotify.removeIf(id -> {
732 if (!mSubscriptionManager.isActiveSubId(id)) {
Jordan Liu9ddde022019-08-05 13:49:08 -0700733 cancelAsUser(Integer.toString(id),
chen xubaf9fe52019-07-02 17:28:24 -0700734 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
735 return true;
736 }
737 return false;
738 });
739 }
740
741 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700742 * Display the network selection "no service" notification
743 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700744 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700745 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700746 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700747 if (DBG) log("showNetworkSelection(" + operator + ")...");
748
Youming Ye0509b532018-09-14 16:21:17 -0700749 if (!TextUtils.isEmpty(operator)) {
750 operator = String.format(" (%s)", operator);
751 }
fionaxu8b7620d2017-05-01 16:22:17 -0700752 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700753 .setSmallIcon(android.R.drawable.stat_sys_warning)
754 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
755 .setContentText(
756 mContext.getString(R.string.notification_network_selection_text, operator))
757 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700758 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700759 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700760
761 // create the target network operators settings intent
762 Intent intent = new Intent(Intent.ACTION_MAIN);
763 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
764 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700765 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700766 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700767 mContext.getString(R.string.mobile_network_settings_package),
768 mContext.getString(R.string.mobile_network_settings_class)));
Pengquan Meng984ba422019-09-05 18:00:06 -0700769 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
fionaxu96ceebd2017-08-24 12:12:32 -0700770 builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
Jordan Liu9ddde022019-08-05 13:49:08 -0700771 notifyAsUser(
irisykyang25202462018-11-13 14:49:54 +0800772 Integer.toString(subId) /* tag */,
fionaxu96ceebd2017-08-24 12:12:32 -0700773 SELECTED_OPERATOR_FAIL_NOTIFICATION,
774 builder.build(),
775 UserHandle.ALL);
irisykyang25202462018-11-13 14:49:54 +0800776 mSelectedUnavailableNotify.put(subId, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700777 }
778
779 /**
780 * Turn off the network selection "no service" notification
781 */
irisykyang25202462018-11-13 14:49:54 +0800782 private void cancelNetworkSelection(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700783 if (DBG) log("cancelNetworkSelection()...");
Jordan Liu9ddde022019-08-05 13:49:08 -0700784 cancelAsUser(
irisykyang25202462018-11-13 14:49:54 +0800785 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
786 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700787 }
788
789 /**
790 * Update notification about no service of user selected operator
791 *
792 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700793 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700794 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700795 void updateNetworkSelection(int serviceState, int subId) {
796 int phoneId = SubscriptionManager.getPhoneId(subId);
797 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
798 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
799 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800800 if (SubscriptionManager.isValidSubscriptionId(subId)) {
fionaxu996a1c32018-04-13 15:00:37 -0700801 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
802 String selectedNetworkOperatorName =
803 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
804 // get the shared preference of network_selection.
805 // empty is auto mode, otherwise it is the operator alpha name
806 // in case there is no operator name, check the operator numeric
807 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
808 selectedNetworkOperatorName =
809 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
810 }
811 boolean isManualSelection;
fionaxud6aac662018-03-14 16:44:29 -0700812 // if restoring manual selection is controlled by framework, then get network
813 // selection from shared preference, otherwise get from real network indicators.
814 boolean restoreSelection = !mContext.getResources().getBoolean(
815 com.android.internal.R.bool.skip_restoring_network_selection);
fionaxud6aac662018-03-14 16:44:29 -0700816 if (restoreSelection) {
fionaxud6aac662018-03-14 16:44:29 -0700817 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
818 } else {
fionaxud6aac662018-03-14 16:44:29 -0700819 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
Amit Mahajana60be872015-01-15 16:05:08 -0800820 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700821
fionaxud6aac662018-03-14 16:44:29 -0700822 if (DBG) {
823 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
824 + (isManualSelection ? selectedNetworkOperatorName : ""));
825 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700826
irisykyang25202462018-11-13 14:49:54 +0800827 if (isManualSelection) {
828 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
829 shouldShowNotification(serviceState, subId);
Amit Mahajana60be872015-01-15 16:05:08 -0800830 } else {
irisykyang25202462018-11-13 14:49:54 +0800831 dismissNetworkSelectionNotification(subId);
832 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700833 }
834 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800835 if (DBG) log("updateNetworkSelection()..." + "state = " +
836 serviceState + " not updating network due to invalid subId " + subId);
irisykyang25202462018-11-13 14:49:54 +0800837 dismissNetworkSelectionNotificationForInactiveSubId();
838 }
839 }
840 }
841
842 private void dismissNetworkSelectionNotification(int subId) {
843 if (mSelectedUnavailableNotify.get(subId, false)) {
844 cancelNetworkSelection(subId);
845 mSelectedUnavailableNotify.remove(subId);
846 }
847 }
848
849 private void dismissNetworkSelectionNotificationForInactiveSubId() {
850 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
851 int subId = mSelectedUnavailableNotify.keyAt(i);
852 if (!mSubscriptionManager.isActiveSubId(subId)) {
853 dismissNetworkSelectionNotification(subId);
854 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700855 }
856 }
857 }
858
859 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
860 if (mToast != null) {
861 mToast.cancel();
862 }
863
864 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
865 mToast.show();
866 }
867
868 private void log(String msg) {
869 Log.d(LOG_TAG, msg);
870 }
Tyler Gunna584e2c2017-09-19 11:40:12 -0700871
872 private void logi(String msg) {
873 Log.i(LOG_TAG, msg);
874 }
irisykyang25202462018-11-13 14:49:54 +0800875
876 /**
877 * In case network selection notification shows up repeatedly under
878 * unstable network condition. The logic is to check whether or not
879 * the service state keeps in no service condition for at least
880 * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
881 * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
882 * To avoid the notification showing up for the momentary state.
883 */
884 private void shouldShowNotification(int serviceState, int subId) {
885 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
886 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
887 != ServiceState.STATE_OUT_OF_SERVICE) {
888 mOOSTimestamp.put(subId, getTimeStamp());
889 }
890 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
891 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
892 || mPendingEventCounter.get(subId, 0)
893 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
894 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
895 clearUpNetworkSelectionNotificationParam(subId);
896 } else {
897 startPendingNetworkSelectionNotification(subId);
898 }
899 } else {
900 dismissNetworkSelectionNotification(subId);
901 }
902 mPreviousServiceState.put(subId, serviceState);
903 if (DBG) {
904 log("shouldShowNotification()..." + " subId = " + subId
905 + " serviceState = " + serviceState
906 + " mOOSTimestamp = " + mOOSTimestamp
907 + " mPendingEventCounter = " + mPendingEventCounter);
908 }
909 }
910
911 private void startPendingNetworkSelectionNotification(int subId) {
912 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
913 if (DBG) {
914 log("startPendingNetworkSelectionNotification: subId = " + subId);
915 }
916 mHandler.sendMessageDelayed(
917 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
918 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
919 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
920 }
921 }
922
923 private void clearUpNetworkSelectionNotificationParam(int subId) {
924 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
925 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
926 }
927 mPreviousServiceState.remove(subId);
928 mOOSTimestamp.remove(subId);
929 mPendingEventCounter.remove(subId);
930 mSelectedNetworkOperatorName.remove(subId);
931 }
932
933 private static long getTimeStamp() {
934 return SystemClock.elapsedRealtime();
935 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700936}