blob: 5a5b444afd62f35a09ae9abfd9571e16d842a8c7 [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
Santos Cordon7d4ddf62013-07-10 11:58:08 -070021import android.app.Notification;
22import android.app.NotificationManager;
23import android.app.PendingIntent;
24import android.app.StatusBarManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070025import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070026import android.content.Context;
27import android.content.Intent;
28import android.content.SharedPreferences;
Jordan Liu69fc7182019-08-05 13:49:08 -070029import android.content.pm.PackageManager;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080030import android.content.pm.ResolveInfo;
Andrew Lee99d0ac22014-10-10 13:18:04 -070031import android.content.pm.UserInfo;
Nancy Chenb4a92702014-12-04 15:57:29 -080032import android.content.res.Resources;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070033import android.net.Uri;
irisykyang25202462018-11-13 14:49:54 +080034import android.os.Handler;
35import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070036import android.os.PersistableBundle;
irisykyang25202462018-11-13 14:49:54 +080037import android.os.SystemClock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070039import android.os.UserHandle;
40import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070041import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042import android.provider.ContactsContract.PhoneLookup;
Jeff Davidson6d9bf522017-11-03 14:51:13 -070043import android.provider.Settings;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080044import android.telecom.DefaultDialerManager;
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
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060import com.android.internal.telephony.Phone;
Jayachandran C2ef9a482017-05-12 22:07:47 -070061import com.android.internal.telephony.PhoneFactory;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070062import com.android.internal.telephony.TelephonyCapabilities;
fionaxu8b7620d2017-05-01 16:22:17 -070063import com.android.internal.telephony.util.NotificationChannelController;
Andrew Leebf07f762015-04-07 19:05:50 -070064import com.android.phone.settings.VoicemailSettingsActivity;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070065
Tyler Gunn9c1071f2014-12-09 10:07:54 -080066import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070067import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080068import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070069
Santos Cordon7d4ddf62013-07-10 11:58:08 -070070/**
71 * NotificationManager-related utility code for the Phone app.
72 *
73 * This is a singleton object which acts as the interface to the
74 * framework's NotificationManager, and is used to display status bar
75 * icons and control other status bar-related behavior.
76 *
77 * @see PhoneGlobals.notificationMgr
78 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070079public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080080 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070081 private static final boolean DBG =
82 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
83 // Do not check in with VDBG = true, since that may write PII to the system log.
84 private static final boolean VDBG = false;
85
Ta-wei Yenb29425b2016-09-21 17:28:14 -070086 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
87 "mwi_should_check_vvm_configuration_state_";
88
Santos Cordon7d4ddf62013-07-10 11:58:08 -070089 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070090 static final int MMI_NOTIFICATION = 1;
91 static final int NETWORK_SELECTION_NOTIFICATION = 2;
92 static final int VOICEMAIL_NOTIFICATION = 3;
93 static final int CALL_FORWARD_NOTIFICATION = 4;
Jordan Liuff2ccd72019-07-23 15:54:41 -070094 static final int DATA_ROAMING_NOTIFICATION = 5;
Santos Cordonf68db2e2014-07-02 14:40:44 -070095 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070096
irisykyang25202462018-11-13 14:49:54 +080097 // Event for network selection notification.
98 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
99
100 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
101 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
102
103 private static final int STATE_UNKNOWN_SERVICE = -1;
104
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700105 /** The singleton NotificationMgr instance. */
106 private static NotificationMgr sInstance;
107
108 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700109
110 private Context mContext;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700111 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -0700112 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700113 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800114 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800115 private TelecomManager mTelecomManager;
116 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700117
irisykyang25202462018-11-13 14:49:54 +0800118 // used to track the notification of selected network unavailable, per subscription id.
119 private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700120
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800121 // used to track whether the message waiting indicator is visible, per subscription id.
122 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
123
irisykyang25202462018-11-13 14:49:54 +0800124 // those flags are used to track whether to show network selection notification or not.
125 private SparseArray<Integer> mPreviousServiceState = new SparseArray<>();
126 private SparseArray<Long> mOOSTimestamp = new SparseArray<>();
127 private SparseArray<Integer> mPendingEventCounter = new SparseArray<>();
128 // maps each subId to selected network operator name.
129 private SparseArray<String> mSelectedNetworkOperatorName = new SparseArray<>();
130
131 private final Handler mHandler = new Handler() {
132 @Override
133 public void handleMessage(Message msg) {
134 switch (msg.what) {
135 case EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION:
136 int subId = (int) msg.obj;
137 TelephonyManager telephonyManager =
138 mTelephonyManager.createForSubscriptionId(subId);
139 if (telephonyManager.getServiceState() != null) {
140 shouldShowNotification(telephonyManager.getServiceState().getState(),
141 subId);
142 }
143 break;
144 }
145 }
146 };
147
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700148 /**
149 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700150 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700151 */
152 private NotificationMgr(PhoneGlobals app) {
153 mApp = app;
154 mContext = app;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700155 mStatusBarManager =
156 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700157 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800158 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800159 mTelecomManager = TelecomManager.from(mContext);
160 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700161 }
162
163 /**
164 * Initialize the singleton NotificationMgr instance.
165 *
166 * This is only done once, at startup, from PhoneApp.onCreate().
167 * From then on, the NotificationMgr instance is available via the
168 * PhoneApp's public "notificationMgr" field, which is why there's no
169 * getInstance() method here.
170 */
171 /* package */ static NotificationMgr init(PhoneGlobals app) {
172 synchronized (NotificationMgr.class) {
173 if (sInstance == null) {
174 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700175 } else {
176 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
177 }
178 return sInstance;
179 }
180 }
181
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700182 /** The projection to use when querying the phones table */
183 static final String[] PHONES_PROJECTION = new String[] {
184 PhoneLookup.NUMBER,
185 PhoneLookup.DISPLAY_NAME,
186 PhoneLookup._ID
187 };
188
189 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800190 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
191 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
192 * settings screen. The voicemail notification sound is suppressed.
193 *
194 * @param subId The subscription Id.
195 */
196 /* package */ void refreshMwi(int subId) {
197 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
198 // reference the single subid stored in the mMwiVisible map.
Ta-wei Yena1390d42017-12-04 15:11:33 -0800199 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800200 if (mMwiVisible.keySet().size() == 1) {
201 Set<Integer> keySet = mMwiVisible.keySet();
202 Iterator<Integer> keyIt = keySet.iterator();
203 if (!keyIt.hasNext()) {
204 return;
205 }
206 subId = keyIt.next();
207 }
208 }
209 if (mMwiVisible.containsKey(subId)) {
210 boolean mwiVisible = mMwiVisible.get(subId);
211 if (mwiVisible) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900212 mApp.notifier.updatePhoneStateListeners(true);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800213 }
214 }
215 }
216
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700217 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
218 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
219 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
220 + subId);
221 return;
222 }
223
224 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
225 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
226 .apply();
227 }
228
229 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
230 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
231 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
232 return true;
233 }
234 return PreferenceManager
235 .getDefaultSharedPreferences(mContext)
236 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
237 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800238 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700239 * Updates the message waiting indicator (voicemail) notification.
240 *
241 * @param visible true if there are messages waiting
242 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800243 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700244 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800245 }
246
247 /**
248 * Updates the message waiting indicator (voicemail) notification.
249 *
250 * @param subId the subId to update.
251 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700252 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
253 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800254 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700255 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800256 if (!PhoneGlobals.sVoiceCapable) {
257 // Do not show the message waiting indicator on devices which are not voice capable.
258 // These events *should* be blocked at the telephony layer for such devices.
259 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
260 return;
261 }
262
Nancy Chen2cf7f292015-05-15 11:00:10 -0700263 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800264 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800265 mMwiVisible.put(subId, visible);
266
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700267 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800268 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800269 Log.w(LOG_TAG, "Found null phone for: " + subId);
270 return;
271 }
272
273 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
274 if (subInfo == null) {
275 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800276 return;
277 }
278
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700279 int resId = android.R.drawable.stat_notify_voicemail;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900280 if (mTelephonyManager.getPhoneCount() > 1) {
281 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
282 : R.drawable.stat_notify_voicemail_sub2;
283 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700284
285 // This Notification can get a lot fancier once we have more
286 // information about the current voicemail messages.
287 // (For example, the current voicemail system can't tell
288 // us the caller-id or timestamp of a message, or tell us the
289 // message count.)
290
291 // But for now, the UI is ultra-simple: if the MWI indication
292 // is supposed to be visible, just show a single generic
293 // notification.
294
295 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800296 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700297 if (DBG) log("- got vm number: '" + vmNumber + "'");
298
Andrew Leea82b8202014-11-21 16:18:28 -0800299 // The voicemail number may be null because:
300 // (1) This phone has no voicemail number.
301 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
302 // happen when the device first boots if we get a MWI notification when we
303 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800304 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
305 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700306 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800307 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700308 }
309
Bryce Lee5dc90842015-08-11 07:57:14 -0700310 Integer vmCount = null;
311
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800312 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700313 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
315 notificationTitle = String.format(titleFormat, vmCount);
316 }
317
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800318 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
319 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
320
321 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700322 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700323 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
324
325 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800326 notificationText = mContext.getString(
327 R.string.notification_voicemail_no_vm_number);
328
329 // If the voicemail number if unknown, instead of calling voicemail, take the user
330 // to the voicemail settings.
331 notificationText = mContext.getString(
332 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700333 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800334 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700335 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700336 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800337 if (mTelephonyManager.getPhoneCount() > 1) {
338 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800339 } else {
340 notificationText = String.format(
341 mContext.getString(R.string.notification_voicemail_text_format),
342 PhoneNumberUtils.formatNumber(vmNumber));
343 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800344 intent = new Intent(
345 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700346 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800347 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700348 }
349
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800350 PendingIntent pendingIntent =
351 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700352
Nancy Chenb4a92702014-12-04 15:57:29 -0800353 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700354 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700355 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700356 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700357 builder.setSmallIcon(resId)
358 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800359 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700360 .setContentTitle(notificationTitle)
361 .setContentText(notificationText)
362 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800363 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700364 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700365 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Jordan Liu575c0d02019-07-09 16:29:48 -0700366 .setChannelId(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
Ta-wei Yen282a9702017-05-30 17:32:29 -0700367 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700368
Andrew Lee99d0ac22014-10-10 13:18:04 -0700369 final Notification notification = builder.build();
370 List<UserInfo> users = mUserManager.getUsers(true);
371 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700372 final UserInfo user = users.get(i);
373 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700374 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700375 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700376 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800377 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700378 pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
Jordan Liu69fc7182019-08-05 13:49:08 -0700379 notifyAsUser(
Bryce Lee5dc90842015-08-11 07:57:14 -0700380 Integer.toString(subId) /* tag */,
381 VOICEMAIL_NOTIFICATION,
382 notification,
383 userHandle);
384 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700385 }
386 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700387 } else {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800388 List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
389 for (int i = 0; i < users.size(); i++) {
390 final UserInfo user = users.get(i);
391 final UserHandle userHandle = user.getUserHandle();
392 if (!mUserManager.hasUserRestriction(
393 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
394 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800395 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700396 false, userHandle, isRefresh)) {
Jordan Liu69fc7182019-08-05 13:49:08 -0700397 cancelAsUser(
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800398 Integer.toString(subId) /* tag */,
399 VOICEMAIL_NOTIFICATION,
400 userHandle);
401 }
402 }
Bryce Lee5dc90842015-08-11 07:57:14 -0700403 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700404 }
405 }
406
407 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800408 * Sends a broadcast with the voicemail notification information to the default dialer. This
409 * method is also used to indicate to the default dialer when to clear the
410 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700411 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800412 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700413 * @param count The number of pending voicemail messages to indicate on the notification. A
414 * Value of 0 is passed here to indicate that the notification should be cleared.
415 * @param number The voicemail phone number if specified.
416 * @param pendingIntent The intent that should be passed as the action to be taken.
417 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
418 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800419 * @param userHandle The user to receive the notification. Each user can have their own default
420 * dialer.
421 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700422 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800423 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
424 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700425 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800426
427 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
428 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700429 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700430 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800431 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
432 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yenafca2d62017-07-18 17:20:59 -0700433 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700434 if (count != null) {
435 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
436 }
437
438 // Additional information about the voicemail notification beyond the count is only
439 // present when the count not specified or greater than 0. The value of 0 represents
440 // clearing the notification, which does not require additional information.
441 if (count == null || count > 0) {
442 if (!TextUtils.isEmpty(number)) {
443 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
444 }
445
446 if (pendingIntent != null) {
447 intent.putExtra(isSettingsIntent
448 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
449 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
450 pendingIntent);
451 }
452 }
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800453 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700454 return true;
455 }
456
457 return false;
458 }
459
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800460 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
461 String dialerPackage = DefaultDialerManager
462 .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
463 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
464 .setPackage(dialerPackage);
465 }
466
467 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
468 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
469 if (intent == null) {
470 return false;
471 }
472
473 List<ResolveInfo> receivers = mContext.getPackageManager()
474 .queryBroadcastReceivers(intent, 0);
475 return receivers.size() > 0;
476 }
477
Bryce Lee5dc90842015-08-11 07:57:14 -0700478 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700479 * Updates the message call forwarding indicator notification.
480 *
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530481 * @param visible true if call forwarding enabled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700482 */
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530483
484 /* package */ void updateCfi(int subId, boolean visible) {
485 updateCfi(subId, visible, false /* isRefresh */);
486 }
487
488 /**
489 * Updates the message call forwarding indicator notification.
490 *
491 * @param visible true if call forwarding enabled
492 */
493 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
Tyler Gunna584e2c2017-09-19 11:40:12 -0700494 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700495 if (visible) {
496 // If Unconditional Call Forwarding (forward all calls) for VOICE
497 // is enabled, just show a notification. We'll default to expanded
498 // view for now, so the there is less confusion about the icon. If
499 // it is deemed too weird to have CF indications as expanded views,
500 // then we'll flip the flag back.
501
502 // TODO: We may want to take a look to see if the notification can
503 // display the target to forward calls to. This will require some
504 // effort though, since there are multiple layers of messages that
505 // will need to propagate that information.
506
Andrew Leed5165b02014-12-05 15:53:58 -0800507 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
508 if (subInfo == null) {
509 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
510 return;
511 }
512
513 String notificationTitle;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900514 int resId = R.drawable.stat_sys_phone_call_forward;
Andrew Leed5165b02014-12-05 15:53:58 -0800515 if (mTelephonyManager.getPhoneCount() > 1) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900516 int slotId = SubscriptionManager.getSlotIndex(subId);
517 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
518 : R.drawable.stat_sys_phone_call_forward_sub2;
Andrew Leed5165b02014-12-05 15:53:58 -0800519 notificationTitle = subInfo.getDisplayName().toString();
520 } else {
521 notificationTitle = mContext.getString(R.string.labelCF);
522 }
523
fionaxu8b7620d2017-05-01 16:22:17 -0700524 Notification.Builder builder = new Notification.Builder(mContext)
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900525 .setSmallIcon(resId)
Andrew Leed5165b02014-12-05 15:53:58 -0800526 .setColor(subInfo.getIconTint())
527 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700528 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
529 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700530 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700531 .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530532 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700533
Andrew Lee99d0ac22014-10-10 13:18:04 -0700534 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800535 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700536 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800537 SubscriptionInfoHelper.addExtrasToIntent(
538 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
fionaxu96ceebd2017-08-24 12:12:32 -0700539 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
540 intent, 0));
Jordan Liu69fc7182019-08-05 13:49:08 -0700541 notifyAsUser(
fionaxu96ceebd2017-08-24 12:12:32 -0700542 Integer.toString(subId) /* tag */,
543 CALL_FORWARD_NOTIFICATION,
544 builder.build(),
545 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700546 } else {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900547 List<UserInfo> users = mUserManager.getUsers(true);
548 for (UserInfo user : users) {
549 if (user.isManagedProfile()) {
550 continue;
551 }
552 UserHandle userHandle = user.getUserHandle();
Jordan Liu69fc7182019-08-05 13:49:08 -0700553 cancelAsUser(
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900554 Integer.toString(subId) /* tag */,
555 CALL_FORWARD_NOTIFICATION,
556 userHandle);
557 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700558 }
559 }
560
561 /**
Jordan Liuff2ccd72019-07-23 15:54:41 -0700562 * Shows either:
563 * 1) the "Data roaming is on" notification, which
564 * appears when you're roaming and you have the "data roaming" feature turned on for the
565 * given {@code subId}.
566 * or
567 * 2) the "data disconnected due to roaming" notification, which
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700568 * appears when you lose data connectivity because you're roaming and
Pengquan Meng8783d932017-10-16 14:57:40 -0700569 * you have the "data roaming" feature turned off for the given {@code subId}.
Jordan Liuff2ccd72019-07-23 15:54:41 -0700570 * @param subId which subscription it's notifying about.
571 * @param roamingOn whether currently roaming is on or off. If true, we show notification
572 * 1) above; else we show notification 2).
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700573 */
Jordan Liuff2ccd72019-07-23 15:54:41 -0700574 /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
575 if (DBG) {
576 log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
577 + " on subId " + subId);
578 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700579
580 // "Mobile network settings" screen / dialog
Nazanin Bakhshi682c77d2019-05-02 17:22:27 -0700581 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
Jeff Davidson6d9bf522017-11-03 14:51:13 -0700582 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Pengquan Meng8783d932017-10-16 14:57:40 -0700583 PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700584
Jordan Liuff2ccd72019-07-23 15:54:41 -0700585 CharSequence contentTitle = mContext.getText(roamingOn
586 ? R.string.roaming_on_notification_title
587 : R.string.roaming_notification_title);
588 CharSequence contentText = mContext.getText(roamingOn
589 ? R.string.roaming_enabled_message
590 : R.string.roaming_reenable_message);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700591
fionaxu8b7620d2017-05-01 16:22:17 -0700592 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700593 .setSmallIcon(android.R.drawable.stat_sys_warning)
Jordan Liuff2ccd72019-07-23 15:54:41 -0700594 .setContentTitle(contentTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700595 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700596 .setContentText(contentText)
Jordan Liu575c0d02019-07-09 16:29:48 -0700597 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
fionaxu96ceebd2017-08-24 12:12:32 -0700598 .setContentIntent(contentIntent);
599 final Notification notif =
600 new Notification.BigTextStyle(builder).bigText(contentText).build();
Jordan Liu69fc7182019-08-05 13:49:08 -0700601 notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
602 }
603
604 private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
605 try {
606 Context contextForUser =
607 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
608 NotificationManager notificationManager =
609 (NotificationManager) contextForUser.getSystemService(
610 Context.NOTIFICATION_SERVICE);
611 notificationManager.notify(tag, id, notification);
612 } catch (PackageManager.NameNotFoundException e) {
613 Log.e(LOG_TAG, "unable to notify for user " + user);
614 e.printStackTrace();
615 }
616 }
617
618 private void cancelAsUser(String tag, int id, UserHandle user) {
619 try {
620 Context contextForUser =
621 mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
622 NotificationManager notificationManager =
623 (NotificationManager) contextForUser.getSystemService(
624 Context.NOTIFICATION_SERVICE);
625 notificationManager.cancel(tag, id);
626 } catch (PackageManager.NameNotFoundException e) {
627 Log.e(LOG_TAG, "unable to cancel for user " + user);
628 e.printStackTrace();
629 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700630 }
631
632 /**
Jordan Liuff2ccd72019-07-23 15:54:41 -0700633 * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700634 */
Jordan Liuff2ccd72019-07-23 15:54:41 -0700635 /* package */ void hideDataRoamingNotification() {
636 if (DBG) log("hideDataRoamingNotification()...");
Jordan Liu69fc7182019-08-05 13:49:08 -0700637 cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700638 }
639
640 /**
641 * Display the network selection "no service" notification
642 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700643 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700644 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700645 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700646 if (DBG) log("showNetworkSelection(" + operator + ")...");
647
Youming Ye0509b532018-09-14 16:21:17 -0700648 if (!TextUtils.isEmpty(operator)) {
649 operator = String.format(" (%s)", operator);
650 }
fionaxu8b7620d2017-05-01 16:22:17 -0700651 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700652 .setSmallIcon(android.R.drawable.stat_sys_warning)
653 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
654 .setContentText(
655 mContext.getString(R.string.notification_network_selection_text, operator))
656 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700657 .setOngoing(true)
Jordan Liu575c0d02019-07-09 16:29:48 -0700658 .setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700659
660 // create the target network operators settings intent
661 Intent intent = new Intent(Intent.ACTION_MAIN);
662 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
663 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700664 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700665 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700666 mContext.getString(R.string.mobile_network_settings_package),
667 mContext.getString(R.string.mobile_network_settings_class)));
Jayachandran C2ef9a482017-05-12 22:07:47 -0700668 intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, subId);
fionaxu96ceebd2017-08-24 12:12:32 -0700669 builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
Jordan Liu69fc7182019-08-05 13:49:08 -0700670 notifyAsUser(
irisykyang25202462018-11-13 14:49:54 +0800671 Integer.toString(subId) /* tag */,
fionaxu96ceebd2017-08-24 12:12:32 -0700672 SELECTED_OPERATOR_FAIL_NOTIFICATION,
673 builder.build(),
674 UserHandle.ALL);
irisykyang25202462018-11-13 14:49:54 +0800675 mSelectedUnavailableNotify.put(subId, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700676 }
677
678 /**
679 * Turn off the network selection "no service" notification
680 */
irisykyang25202462018-11-13 14:49:54 +0800681 private void cancelNetworkSelection(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700682 if (DBG) log("cancelNetworkSelection()...");
Jordan Liu69fc7182019-08-05 13:49:08 -0700683 cancelAsUser(
irisykyang25202462018-11-13 14:49:54 +0800684 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
685 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700686 }
687
688 /**
689 * Update notification about no service of user selected operator
690 *
691 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700692 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700693 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700694 void updateNetworkSelection(int serviceState, int subId) {
695 int phoneId = SubscriptionManager.getPhoneId(subId);
696 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
697 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
698 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800699 if (SubscriptionManager.isValidSubscriptionId(subId)) {
fionaxu996a1c32018-04-13 15:00:37 -0700700 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
701 String selectedNetworkOperatorName =
702 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
703 // get the shared preference of network_selection.
704 // empty is auto mode, otherwise it is the operator alpha name
705 // in case there is no operator name, check the operator numeric
706 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
707 selectedNetworkOperatorName =
708 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
709 }
710 boolean isManualSelection;
fionaxud6aac662018-03-14 16:44:29 -0700711 // if restoring manual selection is controlled by framework, then get network
712 // selection from shared preference, otherwise get from real network indicators.
713 boolean restoreSelection = !mContext.getResources().getBoolean(
714 com.android.internal.R.bool.skip_restoring_network_selection);
fionaxud6aac662018-03-14 16:44:29 -0700715 if (restoreSelection) {
fionaxud6aac662018-03-14 16:44:29 -0700716 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
717 } else {
fionaxud6aac662018-03-14 16:44:29 -0700718 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
Amit Mahajana60be872015-01-15 16:05:08 -0800719 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700720
fionaxud6aac662018-03-14 16:44:29 -0700721 if (DBG) {
722 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
723 + (isManualSelection ? selectedNetworkOperatorName : ""));
724 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700725
irisykyang25202462018-11-13 14:49:54 +0800726 if (isManualSelection) {
727 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
728 shouldShowNotification(serviceState, subId);
Amit Mahajana60be872015-01-15 16:05:08 -0800729 } else {
irisykyang25202462018-11-13 14:49:54 +0800730 dismissNetworkSelectionNotification(subId);
731 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700732 }
733 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800734 if (DBG) log("updateNetworkSelection()..." + "state = " +
735 serviceState + " not updating network due to invalid subId " + subId);
irisykyang25202462018-11-13 14:49:54 +0800736 dismissNetworkSelectionNotificationForInactiveSubId();
737 }
738 }
739 }
740
741 private void dismissNetworkSelectionNotification(int subId) {
742 if (mSelectedUnavailableNotify.get(subId, false)) {
743 cancelNetworkSelection(subId);
744 mSelectedUnavailableNotify.remove(subId);
745 }
746 }
747
748 private void dismissNetworkSelectionNotificationForInactiveSubId() {
749 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
750 int subId = mSelectedUnavailableNotify.keyAt(i);
751 if (!mSubscriptionManager.isActiveSubId(subId)) {
752 dismissNetworkSelectionNotification(subId);
753 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700754 }
755 }
756 }
757
758 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
759 if (mToast != null) {
760 mToast.cancel();
761 }
762
763 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
764 mToast.show();
765 }
766
767 private void log(String msg) {
768 Log.d(LOG_TAG, msg);
769 }
Tyler Gunna584e2c2017-09-19 11:40:12 -0700770
771 private void logi(String msg) {
772 Log.i(LOG_TAG, msg);
773 }
irisykyang25202462018-11-13 14:49:54 +0800774
775 /**
776 * In case network selection notification shows up repeatedly under
777 * unstable network condition. The logic is to check whether or not
778 * the service state keeps in no service condition for at least
779 * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
780 * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
781 * To avoid the notification showing up for the momentary state.
782 */
783 private void shouldShowNotification(int serviceState, int subId) {
784 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
785 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
786 != ServiceState.STATE_OUT_OF_SERVICE) {
787 mOOSTimestamp.put(subId, getTimeStamp());
788 }
789 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
790 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
791 || mPendingEventCounter.get(subId, 0)
792 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
793 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
794 clearUpNetworkSelectionNotificationParam(subId);
795 } else {
796 startPendingNetworkSelectionNotification(subId);
797 }
798 } else {
799 dismissNetworkSelectionNotification(subId);
800 }
801 mPreviousServiceState.put(subId, serviceState);
802 if (DBG) {
803 log("shouldShowNotification()..." + " subId = " + subId
804 + " serviceState = " + serviceState
805 + " mOOSTimestamp = " + mOOSTimestamp
806 + " mPendingEventCounter = " + mPendingEventCounter);
807 }
808 }
809
810 private void startPendingNetworkSelectionNotification(int subId) {
811 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
812 if (DBG) {
813 log("startPendingNetworkSelectionNotification: subId = " + subId);
814 }
815 mHandler.sendMessageDelayed(
816 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
817 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
818 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
819 }
820 }
821
822 private void clearUpNetworkSelectionNotificationParam(int subId) {
823 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
824 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
825 }
826 mPreviousServiceState.remove(subId);
827 mOOSTimestamp.remove(subId);
828 mPendingEventCounter.remove(subId);
829 mSelectedNetworkOperatorName.remove(subId);
830 }
831
832 private static long getTimeStamp() {
833 return SystemClock.elapsedRealtime();
834 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700835}