blob: 3b9f996867fe2607773e260e4c044bd4a90f0b83 [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;
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;
irisykyangb7263fb2018-09-18 16:20:54 +080034import android.os.Handler;
35import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070036import android.os.PersistableBundle;
irisykyangb7263fb2018-09-18 16:20: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;
irisykyangb7263fb2018-09-18 16:20: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
chen xubaf9fe52019-07-02 17:28:24 -070066import java.util.HashSet;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080067import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070068import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080069import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070070
Santos Cordon7d4ddf62013-07-10 11:58:08 -070071/**
72 * NotificationManager-related utility code for the Phone app.
73 *
74 * This is a singleton object which acts as the interface to the
75 * framework's NotificationManager, and is used to display status bar
76 * icons and control other status bar-related behavior.
77 *
78 * @see PhoneGlobals.notificationMgr
79 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070080public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080081 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070082 private static final boolean DBG =
83 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
84 // Do not check in with VDBG = true, since that may write PII to the system log.
85 private static final boolean VDBG = false;
86
Ta-wei Yenb29425b2016-09-21 17:28:14 -070087 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
88 "mwi_should_check_vvm_configuration_state_";
89
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070091 static final int MMI_NOTIFICATION = 1;
92 static final int NETWORK_SELECTION_NOTIFICATION = 2;
93 static final int VOICEMAIL_NOTIFICATION = 3;
94 static final int CALL_FORWARD_NOTIFICATION = 4;
95 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
96 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
chen xubaf9fe52019-07-02 17:28:24 -070097 static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098
irisykyangb7263fb2018-09-18 16:20:54 +080099 // Event for network selection notification.
100 private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
101
102 private static final long NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS = 10000L;
103 private static final int NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES = 10;
104
105 private static final int STATE_UNKNOWN_SERVICE = -1;
106
chen xubaf9fe52019-07-02 17:28:24 -0700107 private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
108
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700109 /** The singleton NotificationMgr instance. */
110 private static NotificationMgr sInstance;
111
112 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700113
114 private Context mContext;
115 private NotificationManager mNotificationManager;
116 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
irisykyangb7263fb2018-09-18 16:20: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
irisykyangb7263fb2018-09-18 16:20: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;
163 mNotificationManager =
164 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
165 mStatusBarManager =
166 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700167 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800168 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800169 mTelecomManager = TelecomManager.from(mContext);
170 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700171 }
172
173 /**
174 * Initialize the singleton NotificationMgr instance.
175 *
176 * This is only done once, at startup, from PhoneApp.onCreate().
177 * From then on, the NotificationMgr instance is available via the
178 * PhoneApp's public "notificationMgr" field, which is why there's no
179 * getInstance() method here.
180 */
181 /* package */ static NotificationMgr init(PhoneGlobals app) {
182 synchronized (NotificationMgr.class) {
183 if (sInstance == null) {
184 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700185 } else {
186 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
187 }
188 return sInstance;
189 }
190 }
191
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700192 /** The projection to use when querying the phones table */
193 static final String[] PHONES_PROJECTION = new String[] {
194 PhoneLookup.NUMBER,
195 PhoneLookup.DISPLAY_NAME,
196 PhoneLookup._ID
197 };
198
199 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800200 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
201 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
202 * settings screen. The voicemail notification sound is suppressed.
203 *
204 * @param subId The subscription Id.
205 */
206 /* package */ void refreshMwi(int subId) {
207 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
208 // reference the single subid stored in the mMwiVisible map.
Ta-wei Yena1390d42017-12-04 15:11:33 -0800209 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800210 if (mMwiVisible.keySet().size() == 1) {
211 Set<Integer> keySet = mMwiVisible.keySet();
212 Iterator<Integer> keyIt = keySet.iterator();
213 if (!keyIt.hasNext()) {
214 return;
215 }
216 subId = keyIt.next();
217 }
218 }
219 if (mMwiVisible.containsKey(subId)) {
220 boolean mwiVisible = mMwiVisible.get(subId);
221 if (mwiVisible) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900222 mApp.notifier.updatePhoneStateListeners(true);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800223 }
224 }
225 }
226
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700227 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
228 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
229 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
230 + subId);
231 return;
232 }
233
234 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
235 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
236 .apply();
237 }
238
239 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
240 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
241 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
242 return true;
243 }
244 return PreferenceManager
245 .getDefaultSharedPreferences(mContext)
246 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
247 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800248 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700249 * Updates the message waiting indicator (voicemail) notification.
250 *
251 * @param visible true if there are messages waiting
252 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800253 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700254 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800255 }
256
257 /**
258 * Updates the message waiting indicator (voicemail) notification.
259 *
260 * @param subId the subId to update.
261 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700262 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
263 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800264 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700265 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800266 if (!PhoneGlobals.sVoiceCapable) {
267 // Do not show the message waiting indicator on devices which are not voice capable.
268 // These events *should* be blocked at the telephony layer for such devices.
269 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
270 return;
271 }
272
Nancy Chen2cf7f292015-05-15 11:00:10 -0700273 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800274 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800275 mMwiVisible.put(subId, visible);
276
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700277 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800278 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800279 Log.w(LOG_TAG, "Found null phone for: " + subId);
280 return;
281 }
282
283 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
284 if (subInfo == null) {
285 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800286 return;
287 }
288
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700289 int resId = android.R.drawable.stat_notify_voicemail;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900290 if (mTelephonyManager.getPhoneCount() > 1) {
291 resId = (phone.getPhoneId() == 0) ? R.drawable.stat_notify_voicemail_sub1
292 : R.drawable.stat_notify_voicemail_sub2;
293 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700294
295 // This Notification can get a lot fancier once we have more
296 // information about the current voicemail messages.
297 // (For example, the current voicemail system can't tell
298 // us the caller-id or timestamp of a message, or tell us the
299 // message count.)
300
301 // But for now, the UI is ultra-simple: if the MWI indication
302 // is supposed to be visible, just show a single generic
303 // notification.
304
305 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800306 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700307 if (DBG) log("- got vm number: '" + vmNumber + "'");
308
Andrew Leea82b8202014-11-21 16:18:28 -0800309 // The voicemail number may be null because:
310 // (1) This phone has no voicemail number.
311 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
312 // happen when the device first boots if we get a MWI notification when we
313 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800314 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
315 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700316 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800317 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700318 }
319
Bryce Lee5dc90842015-08-11 07:57:14 -0700320 Integer vmCount = null;
321
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800322 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700323 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700324 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
325 notificationTitle = String.format(titleFormat, vmCount);
326 }
327
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800328 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
329 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
330
331 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700332 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700333 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
334
335 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800336 notificationText = mContext.getString(
337 R.string.notification_voicemail_no_vm_number);
338
339 // If the voicemail number if unknown, instead of calling voicemail, take the user
340 // to the voicemail settings.
341 notificationText = mContext.getString(
342 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700343 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800344 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700345 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700346 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800347 if (mTelephonyManager.getPhoneCount() > 1) {
348 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800349 } else {
350 notificationText = String.format(
351 mContext.getString(R.string.notification_voicemail_text_format),
352 PhoneNumberUtils.formatNumber(vmNumber));
353 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800354 intent = new Intent(
355 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700356 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800357 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700358 }
359
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800360 PendingIntent pendingIntent =
361 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700362
Nancy Chenb4a92702014-12-04 15:57:29 -0800363 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700364 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700365 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700366 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700367 builder.setSmallIcon(resId)
368 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800369 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700370 .setContentTitle(notificationTitle)
371 .setContentText(notificationText)
372 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800373 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700374 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700375 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Ta-wei Yen282a9702017-05-30 17:32:29 -0700376 .setChannel(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
377 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700378
Andrew Lee99d0ac22014-10-10 13:18:04 -0700379 final Notification notification = builder.build();
380 List<UserInfo> users = mUserManager.getUsers(true);
381 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700382 final UserInfo user = users.get(i);
383 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700384 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700385 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700386 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800387 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700388 pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700389 mNotificationManager.notifyAsUser(
390 Integer.toString(subId) /* tag */,
391 VOICEMAIL_NOTIFICATION,
392 notification,
393 userHandle);
394 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700395 }
396 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700397 } else {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800398 List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
399 for (int i = 0; i < users.size(); i++) {
400 final UserInfo user = users.get(i);
401 final UserHandle userHandle = user.getUserHandle();
402 if (!mUserManager.hasUserRestriction(
403 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
404 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800405 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700406 false, userHandle, isRefresh)) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800407 mNotificationManager.cancelAsUser(
408 Integer.toString(subId) /* tag */,
409 VOICEMAIL_NOTIFICATION,
410 userHandle);
411 }
412 }
Bryce Lee5dc90842015-08-11 07:57:14 -0700413 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700414 }
415 }
416
417 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800418 * Sends a broadcast with the voicemail notification information to the default dialer. This
419 * method is also used to indicate to the default dialer when to clear the
420 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700421 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800422 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700423 * @param count The number of pending voicemail messages to indicate on the notification. A
424 * Value of 0 is passed here to indicate that the notification should be cleared.
425 * @param number The voicemail phone number if specified.
426 * @param pendingIntent The intent that should be passed as the action to be taken.
427 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
428 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800429 * @param userHandle The user to receive the notification. Each user can have their own default
430 * dialer.
431 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700432 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800433 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
434 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700435 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800436
437 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
438 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700439 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700440 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800441 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
442 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yenafca2d62017-07-18 17:20:59 -0700443 intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700444 if (count != null) {
445 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
446 }
447
448 // Additional information about the voicemail notification beyond the count is only
449 // present when the count not specified or greater than 0. The value of 0 represents
450 // clearing the notification, which does not require additional information.
451 if (count == null || count > 0) {
452 if (!TextUtils.isEmpty(number)) {
453 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
454 }
455
456 if (pendingIntent != null) {
457 intent.putExtra(isSettingsIntent
458 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
459 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
460 pendingIntent);
461 }
462 }
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800463 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700464 return true;
465 }
466
467 return false;
468 }
469
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800470 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
471 String dialerPackage = DefaultDialerManager
472 .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
473 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
474 .setPackage(dialerPackage);
475 }
476
477 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
478 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
479 if (intent == null) {
480 return false;
481 }
482
483 List<ResolveInfo> receivers = mContext.getPackageManager()
484 .queryBroadcastReceivers(intent, 0);
485 return receivers.size() > 0;
486 }
487
Bryce Lee5dc90842015-08-11 07:57:14 -0700488 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700489 * Updates the message call forwarding indicator notification.
490 *
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530491 * @param visible true if call forwarding enabled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700492 */
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530493
494 /* package */ void updateCfi(int subId, boolean visible) {
495 updateCfi(subId, visible, false /* isRefresh */);
496 }
497
498 /**
499 * Updates the message call forwarding indicator notification.
500 *
501 * @param visible true if call forwarding enabled
502 */
503 /* package */ void updateCfi(int subId, boolean visible, boolean isRefresh) {
Tyler Gunna584e2c2017-09-19 11:40:12 -0700504 logi("updateCfi: subId= " + subId + ", visible=" + (visible ? "Y" : "N"));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700505 if (visible) {
506 // If Unconditional Call Forwarding (forward all calls) for VOICE
507 // is enabled, just show a notification. We'll default to expanded
508 // view for now, so the there is less confusion about the icon. If
509 // it is deemed too weird to have CF indications as expanded views,
510 // then we'll flip the flag back.
511
512 // TODO: We may want to take a look to see if the notification can
513 // display the target to forward calls to. This will require some
514 // effort though, since there are multiple layers of messages that
515 // will need to propagate that information.
516
Andrew Leed5165b02014-12-05 15:53:58 -0800517 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
518 if (subInfo == null) {
519 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
520 return;
521 }
522
523 String notificationTitle;
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900524 int resId = R.drawable.stat_sys_phone_call_forward;
Andrew Leed5165b02014-12-05 15:53:58 -0800525 if (mTelephonyManager.getPhoneCount() > 1) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900526 int slotId = SubscriptionManager.getSlotIndex(subId);
527 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
528 : R.drawable.stat_sys_phone_call_forward_sub2;
Andrew Leed5165b02014-12-05 15:53:58 -0800529 notificationTitle = subInfo.getDisplayName().toString();
530 } else {
531 notificationTitle = mContext.getString(R.string.labelCF);
532 }
533
fionaxu8b7620d2017-05-01 16:22:17 -0700534 Notification.Builder builder = new Notification.Builder(mContext)
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900535 .setSmallIcon(resId)
Andrew Leed5165b02014-12-05 15:53:58 -0800536 .setColor(subInfo.getIconTint())
537 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700538 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
539 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700540 .setOngoing(true)
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530541 .setChannel(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
542 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700543
Andrew Lee99d0ac22014-10-10 13:18:04 -0700544 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800545 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700546 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800547 SubscriptionInfoHelper.addExtrasToIntent(
548 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
fionaxu96ceebd2017-08-24 12:12:32 -0700549 builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
550 intent, 0));
551 mNotificationManager.notifyAsUser(
552 Integer.toString(subId) /* tag */,
553 CALL_FORWARD_NOTIFICATION,
554 builder.build(),
555 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700556 } else {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900557 List<UserInfo> users = mUserManager.getUsers(true);
558 for (UserInfo user : users) {
559 if (user.isManagedProfile()) {
560 continue;
561 }
562 UserHandle userHandle = user.getUserHandle();
563 mNotificationManager.cancelAsUser(
564 Integer.toString(subId) /* tag */,
565 CALL_FORWARD_NOTIFICATION,
566 userHandle);
567 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700568 }
569 }
570
571 /**
572 * Shows the "data disconnected due to roaming" notification, which
573 * appears when you lose data connectivity because you're roaming and
Pengquan Meng8783d932017-10-16 14:57:40 -0700574 * you have the "data roaming" feature turned off for the given {@code subId}.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700575 */
Pengquan Meng8783d932017-10-16 14:57:40 -0700576 /* package */ void showDataDisconnectedRoaming(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700577 if (DBG) log("showDataDisconnectedRoaming()...");
578
579 // "Mobile network settings" screen / dialog
Nazanin Bakhshifd4f9e52019-05-02 17:22:27 -0700580 Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
Jeff Davidson6d9bf522017-11-03 14:51:13 -0700581 intent.putExtra(Settings.EXTRA_SUB_ID, subId);
Pengquan Meng8783d932017-10-16 14:57:40 -0700582 PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700583
584 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
585
fionaxu8b7620d2017-05-01 16:22:17 -0700586 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700587 .setSmallIcon(android.R.drawable.stat_sys_warning)
fionaxuba6bdef2018-03-13 14:18:34 -0700588 .setContentTitle(mContext.getText(R.string.roaming_notification_title))
Andrew Lee99d0ac22014-10-10 13:18:04 -0700589 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700590 .setContentText(contentText)
fionaxu96ceebd2017-08-24 12:12:32 -0700591 .setChannel(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
592 .setContentIntent(contentIntent);
593 final Notification notif =
594 new Notification.BigTextStyle(builder).bigText(contentText).build();
595 mNotificationManager.notifyAsUser(
596 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700597 }
598
599 /**
600 * Turns off the "data disconnected due to roaming" notification.
601 */
602 /* package */ void hideDataDisconnectedRoaming() {
603 if (DBG) log("hideDataDisconnectedRoaming()...");
604 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
605 }
606
607 /**
chen xubaf9fe52019-07-02 17:28:24 -0700608 * Shows the "Limited SIM functionality" warning notification, which appears when using a
609 * special carrier under dual sim. limited function applies for DSDS in general when two SIM
610 * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
611 */
612 public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
613 if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
614 + " subId: " + subId);
615 if (mLimitedSimFunctionNotify.contains(subId)) {
616 // handle the case that user swipe the notification but condition triggers
617 // frequently which cause the same notification consistently displayed.
618 if (DBG) log("showLimitedSimFunctionWarningNotification, "
619 + "not display again if already displayed");
620 return;
621 }
622 // Navigate to "Network Selection Settings" which list all subscriptions.
623 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
624 new Intent(ACTION_MOBILE_NETWORK_LIST), 0);
625 String line1Num = mTelephonyManager.getLine1Number(subId);
626
627 final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
628 String.format(mContext.getText(
629 R.string.limited_sim_function_notification_message).toString(),
630 carrierName, line1Num) :
631 String.format(mContext.getText(
632 R.string.limited_sim_function_with_phone_num_notification_message).toString(),
633 carrierName);
634 final Notification.Builder builder = new Notification.Builder(mContext)
635 .setSmallIcon(R.drawable.ic_sim_card)
636 .setContentTitle(mContext.getText(
637 R.string.limited_sim_function_notification_title))
638 .setContentText(contentText)
639 .setOnlyAlertOnce(true)
640 .setOngoing(true)
641 .setChannel(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
642 .setContentIntent(contentIntent);
643 final Notification notification = new Notification.BigTextStyle(builder).bigText(
644 contentText).build();
645
646 mNotificationManager.notifyAsUser(Integer.toString(subId),
647 LIMITED_SIM_FUNCTION_NOTIFICATION,
648 notification, UserHandle.ALL);
649 mLimitedSimFunctionNotify.add(subId);
650 }
651
652 /**
653 * Dismiss the "Limited SIM functionality" warning notification for the given subId.
654 */
655 public void dismissLimitedSimFunctionWarningNotification(int subId) {
656 if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
657 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
658 // dismiss all notifications
659 for (int id : mLimitedSimFunctionNotify) {
660 mNotificationManager.cancelAsUser(Integer.toString(id),
661 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
662 }
663 mLimitedSimFunctionNotify.clear();
664 } else if (mLimitedSimFunctionNotify.contains(subId)) {
665 mNotificationManager.cancelAsUser(Integer.toString(subId),
666 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
667 mLimitedSimFunctionNotify.remove(subId);
668 }
669 }
670
671 /**
672 * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
673 */
674 public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
675 if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
676 // dismiss notification for inactive subscriptions.
677 // handle the corner case that SIM change by SIM refresh doesn't clear the notification
678 // from the old SIM if both old & new SIM configured to display the notification.
679 mLimitedSimFunctionNotify.removeIf(id -> {
680 if (!mSubscriptionManager.isActiveSubId(id)) {
681 mNotificationManager.cancelAsUser(Integer.toString(id),
682 LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
683 return true;
684 }
685 return false;
686 });
687 }
688
689 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700690 * Display the network selection "no service" notification
691 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700692 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700693 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700694 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700695 if (DBG) log("showNetworkSelection(" + operator + ")...");
696
Youming Ye0509b532018-09-14 16:21:17 -0700697 if (!TextUtils.isEmpty(operator)) {
698 operator = String.format(" (%s)", operator);
699 }
fionaxu8b7620d2017-05-01 16:22:17 -0700700 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700701 .setSmallIcon(android.R.drawable.stat_sys_warning)
702 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
703 .setContentText(
704 mContext.getString(R.string.notification_network_selection_text, operator))
705 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700706 .setOngoing(true)
fionaxu8b7620d2017-05-01 16:22:17 -0700707 .setChannel(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700708
709 // create the target network operators settings intent
710 Intent intent = new Intent(Intent.ACTION_MAIN);
711 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
712 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700713 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700714 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700715 mContext.getString(R.string.mobile_network_settings_package),
716 mContext.getString(R.string.mobile_network_settings_class)));
Jayachandran C2ef9a482017-05-12 22:07:47 -0700717 intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, subId);
fionaxu96ceebd2017-08-24 12:12:32 -0700718 builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
719 mNotificationManager.notifyAsUser(
irisykyangb7263fb2018-09-18 16:20:54 +0800720 Integer.toString(subId) /* tag */,
fionaxu96ceebd2017-08-24 12:12:32 -0700721 SELECTED_OPERATOR_FAIL_NOTIFICATION,
722 builder.build(),
723 UserHandle.ALL);
irisykyangb7263fb2018-09-18 16:20:54 +0800724 mSelectedUnavailableNotify.put(subId, true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700725 }
726
727 /**
728 * Turn off the network selection "no service" notification
729 */
irisykyangb7263fb2018-09-18 16:20:54 +0800730 private void cancelNetworkSelection(int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700731 if (DBG) log("cancelNetworkSelection()...");
Andrew Lee99d0ac22014-10-10 13:18:04 -0700732 mNotificationManager.cancelAsUser(
irisykyangb7263fb2018-09-18 16:20:54 +0800733 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
734 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700735 }
736
737 /**
738 * Update notification about no service of user selected operator
739 *
740 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700741 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700742 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700743 void updateNetworkSelection(int serviceState, int subId) {
744 int phoneId = SubscriptionManager.getPhoneId(subId);
745 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
746 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
747 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800748 if (SubscriptionManager.isValidSubscriptionId(subId)) {
fionaxu996a1c32018-04-13 15:00:37 -0700749 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
750 String selectedNetworkOperatorName =
751 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
752 // get the shared preference of network_selection.
753 // empty is auto mode, otherwise it is the operator alpha name
754 // in case there is no operator name, check the operator numeric
755 if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
756 selectedNetworkOperatorName =
757 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
758 }
759 boolean isManualSelection;
fionaxud6aac662018-03-14 16:44:29 -0700760 // if restoring manual selection is controlled by framework, then get network
761 // selection from shared preference, otherwise get from real network indicators.
762 boolean restoreSelection = !mContext.getResources().getBoolean(
763 com.android.internal.R.bool.skip_restoring_network_selection);
fionaxud6aac662018-03-14 16:44:29 -0700764 if (restoreSelection) {
fionaxud6aac662018-03-14 16:44:29 -0700765 isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
766 } else {
fionaxud6aac662018-03-14 16:44:29 -0700767 isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
Amit Mahajana60be872015-01-15 16:05:08 -0800768 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700769
fionaxud6aac662018-03-14 16:44:29 -0700770 if (DBG) {
771 log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
772 + (isManualSelection ? selectedNetworkOperatorName : ""));
773 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700774
irisykyangb7263fb2018-09-18 16:20:54 +0800775 if (isManualSelection) {
776 mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
777 shouldShowNotification(serviceState, subId);
Amit Mahajana60be872015-01-15 16:05:08 -0800778 } else {
irisykyangb7263fb2018-09-18 16:20:54 +0800779 dismissNetworkSelectionNotification(subId);
780 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700781 }
782 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800783 if (DBG) log("updateNetworkSelection()..." + "state = " +
784 serviceState + " not updating network due to invalid subId " + subId);
irisykyangb7263fb2018-09-18 16:20:54 +0800785 dismissNetworkSelectionNotificationForInactiveSubId();
786 }
787 }
788 }
789
790 private void dismissNetworkSelectionNotification(int subId) {
791 if (mSelectedUnavailableNotify.get(subId, false)) {
792 cancelNetworkSelection(subId);
793 mSelectedUnavailableNotify.remove(subId);
794 }
795 }
796
797 private void dismissNetworkSelectionNotificationForInactiveSubId() {
798 for (int i = 0; i < mSelectedUnavailableNotify.size(); i++) {
799 int subId = mSelectedUnavailableNotify.keyAt(i);
800 if (!mSubscriptionManager.isActiveSubId(subId)) {
801 dismissNetworkSelectionNotification(subId);
802 clearUpNetworkSelectionNotificationParam(subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700803 }
804 }
805 }
806
807 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
808 if (mToast != null) {
809 mToast.cancel();
810 }
811
812 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
813 mToast.show();
814 }
815
816 private void log(String msg) {
817 Log.d(LOG_TAG, msg);
818 }
Tyler Gunna584e2c2017-09-19 11:40:12 -0700819
820 private void logi(String msg) {
821 Log.i(LOG_TAG, msg);
822 }
irisykyangb7263fb2018-09-18 16:20:54 +0800823
824 /**
825 * In case network selection notification shows up repeatedly under
826 * unstable network condition. The logic is to check whether or not
827 * the service state keeps in no service condition for at least
828 * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
829 * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
830 * To avoid the notification showing up for the momentary state.
831 */
832 private void shouldShowNotification(int serviceState, int subId) {
833 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
834 if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
835 != ServiceState.STATE_OUT_OF_SERVICE) {
836 mOOSTimestamp.put(subId, getTimeStamp());
837 }
838 if ((getTimeStamp() - mOOSTimestamp.get(subId, 0L)
839 >= NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS)
840 || mPendingEventCounter.get(subId, 0)
841 > NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES) {
842 showNetworkSelection(mSelectedNetworkOperatorName.get(subId), subId);
843 clearUpNetworkSelectionNotificationParam(subId);
844 } else {
845 startPendingNetworkSelectionNotification(subId);
846 }
847 } else {
848 dismissNetworkSelectionNotification(subId);
849 }
850 mPreviousServiceState.put(subId, serviceState);
851 if (DBG) {
852 log("shouldShowNotification()..." + " subId = " + subId
853 + " serviceState = " + serviceState
854 + " mOOSTimestamp = " + mOOSTimestamp
855 + " mPendingEventCounter = " + mPendingEventCounter);
856 }
857 }
858
859 private void startPendingNetworkSelectionNotification(int subId) {
860 if (!mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
861 if (DBG) {
862 log("startPendingNetworkSelectionNotification: subId = " + subId);
863 }
864 mHandler.sendMessageDelayed(
865 mHandler.obtainMessage(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId),
866 NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS);
867 mPendingEventCounter.put(subId, mPendingEventCounter.get(subId, 0) + 1);
868 }
869 }
870
871 private void clearUpNetworkSelectionNotificationParam(int subId) {
872 if (mHandler.hasMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId)) {
873 mHandler.removeMessages(EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION, subId);
874 }
875 mPreviousServiceState.remove(subId);
876 mOOSTimestamp.remove(subId);
877 mPendingEventCounter.remove(subId);
878 mSelectedNetworkOperatorName.remove(subId);
879 }
880
881 private static long getTimeStamp() {
882 return SystemClock.elapsedRealtime();
883 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700884}