blob: bf611a66a26d094c49b3a4faafc570f6044009af [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 Yen5bb19562016-11-16 11:05:37 -080019import 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;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080029import android.content.pm.ResolveInfo;
Andrew Lee99d0ac22014-10-10 13:18:04 -070030import android.content.pm.UserInfo;
Nancy Chenb4a92702014-12-04 15:57:29 -080031import android.content.res.Resources;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070032import android.net.Uri;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070033import android.os.PersistableBundle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070035import android.os.UserHandle;
36import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070037import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.provider.ContactsContract.PhoneLookup;
Ta-wei Yen5bb19562016-11-16 11:05:37 -080039import android.telecom.DefaultDialerManager;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070040import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080041import android.telecom.PhoneAccountHandle;
42import android.telecom.TelecomManager;
Jonathan Basseri3649bdb2015-04-30 22:39:11 -070043import android.telephony.CarrierConfigManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044import android.telephony.PhoneNumberUtils;
45import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080046import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080047import android.telephony.SubscriptionManager;
Ta-wei Yenfb4f0502016-05-27 12:15:43 -070048import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
Andrew Leed5165b02014-12-05 15:53:58 -080049import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080051import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052import android.util.Log;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import android.widget.Toast;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070054
Santos Cordon7d4ddf62013-07-10 11:58:08 -070055import com.android.internal.telephony.Phone;
Jayachandran C2ef9a482017-05-12 22:07:47 -070056import com.android.internal.telephony.PhoneFactory;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057import com.android.internal.telephony.TelephonyCapabilities;
fionaxu8b7620d2017-05-01 16:22:17 -070058import com.android.internal.telephony.util.NotificationChannelController;
Andrew Leebf07f762015-04-07 19:05:50 -070059import com.android.phone.settings.VoicemailSettingsActivity;
Ta-wei Yenb29425b2016-09-21 17:28:14 -070060
Tyler Gunn9c1071f2014-12-09 10:07:54 -080061import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070062import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080063import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070064
Santos Cordon7d4ddf62013-07-10 11:58:08 -070065/**
66 * NotificationManager-related utility code for the Phone app.
67 *
68 * This is a singleton object which acts as the interface to the
69 * framework's NotificationManager, and is used to display status bar
70 * icons and control other status bar-related behavior.
71 *
72 * @see PhoneGlobals.notificationMgr
73 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070074public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080075 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070076 private static final boolean DBG =
77 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
78 // Do not check in with VDBG = true, since that may write PII to the system log.
79 private static final boolean VDBG = false;
80
Ta-wei Yenb29425b2016-09-21 17:28:14 -070081 private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
82 "mwi_should_check_vvm_configuration_state_";
83
Ta-wei Yen282a9702017-05-30 17:32:29 -070084 /**
85 * Boolean value representing whether the {@link
86 * TelephonyManager#ACTION_SHOW_VOICEMAIL_NOTIFICATION} is new or a refresh of an existing
87 * notification.
88 *
89 * TODO(b/62202833): make public
90 */
91 private static final String EXTRA_IS_REFRESH = "is_refresh";
92
Santos Cordon7d4ddf62013-07-10 11:58:08 -070093 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070094 static final int MMI_NOTIFICATION = 1;
95 static final int NETWORK_SELECTION_NOTIFICATION = 2;
96 static final int VOICEMAIL_NOTIFICATION = 3;
97 static final int CALL_FORWARD_NOTIFICATION = 4;
98 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
99 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100
101 /** The singleton NotificationMgr instance. */
102 private static NotificationMgr sInstance;
103
104 private PhoneGlobals mApp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700105
106 private Context mContext;
107 private NotificationManager mNotificationManager;
108 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -0700109 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700110 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800111 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800112 private TelecomManager mTelecomManager;
113 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700114
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700115 // used to track the notification of selected network unavailable
116 private boolean mSelectedUnavailableNotify = false;
117
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800118 // used to track whether the message waiting indicator is visible, per subscription id.
119 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
120
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700121 /**
122 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700123 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700124 */
125 private NotificationMgr(PhoneGlobals app) {
126 mApp = app;
127 mContext = app;
128 mNotificationManager =
129 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
130 mStatusBarManager =
131 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700132 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800133 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800134 mTelecomManager = TelecomManager.from(mContext);
135 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700136
Ta-wei Yenfb4f0502016-05-27 12:15:43 -0700137 mSubscriptionManager.addOnSubscriptionsChangedListener(
138 new OnSubscriptionsChangedListener() {
139 @Override
140 public void onSubscriptionsChanged() {
141 updateActivePhonesMwi();
142 }
143 });
144 }
145
146 public void updateActivePhonesMwi() {
147 List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
148
149 if (subInfos == null) {
150 return;
151 }
152
153 for (int i = 0; i < subInfos.size(); i++) {
154 int subId = subInfos.get(i).getSubscriptionId();
155 refreshMwi(subId);
156 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700157 }
158
159 /**
160 * Initialize the singleton NotificationMgr instance.
161 *
162 * This is only done once, at startup, from PhoneApp.onCreate().
163 * From then on, the NotificationMgr instance is available via the
164 * PhoneApp's public "notificationMgr" field, which is why there's no
165 * getInstance() method here.
166 */
167 /* package */ static NotificationMgr init(PhoneGlobals app) {
168 synchronized (NotificationMgr.class) {
169 if (sInstance == null) {
170 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700171 } else {
172 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
173 }
174 return sInstance;
175 }
176 }
177
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700178 /** The projection to use when querying the phones table */
179 static final String[] PHONES_PROJECTION = new String[] {
180 PhoneLookup.NUMBER,
181 PhoneLookup.DISPLAY_NAME,
182 PhoneLookup._ID
183 };
184
185 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800186 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
187 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
188 * settings screen. The voicemail notification sound is suppressed.
189 *
190 * @param subId The subscription Id.
191 */
192 /* package */ void refreshMwi(int subId) {
193 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
194 // reference the single subid stored in the mMwiVisible map.
195 if (subId == SubscriptionInfoHelper.NO_SUB_ID) {
196 if (mMwiVisible.keySet().size() == 1) {
197 Set<Integer> keySet = mMwiVisible.keySet();
198 Iterator<Integer> keyIt = keySet.iterator();
199 if (!keyIt.hasNext()) {
200 return;
201 }
202 subId = keyIt.next();
203 }
204 }
205 if (mMwiVisible.containsKey(subId)) {
206 boolean mwiVisible = mMwiVisible.get(subId);
207 if (mwiVisible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700208 updateMwi(subId, mwiVisible, true /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800209 }
210 }
211 }
212
Ta-wei Yenb29425b2016-09-21 17:28:14 -0700213 public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
214 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
215 Log.e(LOG_TAG, "setShouldCheckVisualVoicemailConfigurationForMwi: invalid subId"
216 + subId);
217 return;
218 }
219
220 PreferenceManager.getDefaultSharedPreferences(mContext).edit()
221 .putBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, enabled)
222 .apply();
223 }
224
225 private boolean shouldCheckVisualVoicemailConfigurationForMwi(int subId) {
226 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
227 Log.e(LOG_TAG, "shouldCheckVisualVoicemailConfigurationForMwi: invalid subId" + subId);
228 return true;
229 }
230 return PreferenceManager
231 .getDefaultSharedPreferences(mContext)
232 .getBoolean(MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX + subId, true);
233 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800234 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700235 * Updates the message waiting indicator (voicemail) notification.
236 *
237 * @param visible true if there are messages waiting
238 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800239 /* package */ void updateMwi(int subId, boolean visible) {
Ta-wei Yen282a9702017-05-30 17:32:29 -0700240 updateMwi(subId, visible, false /* isRefresh */);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800241 }
242
243 /**
244 * Updates the message waiting indicator (voicemail) notification.
245 *
246 * @param subId the subId to update.
247 * @param visible true if there are messages waiting
Ta-wei Yen282a9702017-05-30 17:32:29 -0700248 * @param isRefresh {@code true} if the notification is a refresh and the user should not be
249 * notified again.
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800250 */
Ta-wei Yen282a9702017-05-30 17:32:29 -0700251 void updateMwi(int subId, boolean visible, boolean isRefresh) {
Andrew Leea82b8202014-11-21 16:18:28 -0800252 if (!PhoneGlobals.sVoiceCapable) {
253 // Do not show the message waiting indicator on devices which are not voice capable.
254 // These events *should* be blocked at the telephony layer for such devices.
255 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
256 return;
257 }
258
Nancy Chen2cf7f292015-05-15 11:00:10 -0700259 Phone phone = PhoneGlobals.getPhone(subId);
Yorke Lee67a62a22014-12-15 18:46:17 -0800260 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800261 mMwiVisible.put(subId, visible);
262
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700263 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800264 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800265 Log.w(LOG_TAG, "Found null phone for: " + subId);
266 return;
267 }
268
269 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
270 if (subInfo == null) {
271 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800272 return;
273 }
274
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700275 int resId = android.R.drawable.stat_notify_voicemail;
276
277 // This Notification can get a lot fancier once we have more
278 // information about the current voicemail messages.
279 // (For example, the current voicemail system can't tell
280 // us the caller-id or timestamp of a message, or tell us the
281 // message count.)
282
283 // But for now, the UI is ultra-simple: if the MWI indication
284 // is supposed to be visible, just show a single generic
285 // notification.
286
287 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800288 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700289 if (DBG) log("- got vm number: '" + vmNumber + "'");
290
Andrew Leea82b8202014-11-21 16:18:28 -0800291 // The voicemail number may be null because:
292 // (1) This phone has no voicemail number.
293 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
294 // happen when the device first boots if we get a MWI notification when we
295 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800296 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
297 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700298 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800299 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700300 }
301
Bryce Lee5dc90842015-08-11 07:57:14 -0700302 Integer vmCount = null;
303
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800304 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700305 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700306 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
307 notificationTitle = String.format(titleFormat, vmCount);
308 }
309
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800310 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
311 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
312
313 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700315 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
316
317 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800318 notificationText = mContext.getString(
319 R.string.notification_voicemail_no_vm_number);
320
321 // If the voicemail number if unknown, instead of calling voicemail, take the user
322 // to the voicemail settings.
323 notificationText = mContext.getString(
324 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700325 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800326 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700327 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700328 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800329 if (mTelephonyManager.getPhoneCount() > 1) {
330 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800331 } else {
332 notificationText = String.format(
333 mContext.getString(R.string.notification_voicemail_text_format),
334 PhoneNumberUtils.formatNumber(vmNumber));
335 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800336 intent = new Intent(
337 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700338 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800339 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700340 }
341
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800342 PendingIntent pendingIntent =
343 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700344
Nancy Chenb4a92702014-12-04 15:57:29 -0800345 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700346 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Ta-wei Yen9b37a872016-05-27 12:16:58 -0700347 subId);
fionaxu8b7620d2017-05-01 16:22:17 -0700348 Notification.Builder builder = new Notification.Builder(mContext);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700349 builder.setSmallIcon(resId)
350 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800351 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700352 .setContentTitle(notificationTitle)
353 .setContentText(notificationText)
354 .setContentIntent(pendingIntent)
Nancy Chenb4a92702014-12-04 15:57:29 -0800355 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700356 .setOngoing(carrierConfig.getBoolean(
fionaxu75b66a72017-04-19 19:01:56 -0700357 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL))
Ta-wei Yen282a9702017-05-30 17:32:29 -0700358 .setChannel(NotificationChannelController.CHANNEL_ID_VOICE_MAIL)
359 .setOnlyAlertOnce(isRefresh);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700360
Andrew Lee99d0ac22014-10-10 13:18:04 -0700361 final Notification notification = builder.build();
362 List<UserInfo> users = mUserManager.getUsers(true);
363 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700364 final UserInfo user = users.get(i);
365 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700366 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700367 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700368 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800369 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700370 pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700371 mNotificationManager.notifyAsUser(
372 Integer.toString(subId) /* tag */,
373 VOICEMAIL_NOTIFICATION,
374 notification,
375 userHandle);
376 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700377 }
378 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700379 } else {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800380 List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
381 for (int i = 0; i < users.size(); i++) {
382 final UserInfo user = users.get(i);
383 final UserHandle userHandle = user.getUserHandle();
384 if (!mUserManager.hasUserRestriction(
385 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
386 && !user.isManagedProfile()) {
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800387 if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700388 false, userHandle, isRefresh)) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800389 mNotificationManager.cancelAsUser(
390 Integer.toString(subId) /* tag */,
391 VOICEMAIL_NOTIFICATION,
392 userHandle);
393 }
394 }
Bryce Lee5dc90842015-08-11 07:57:14 -0700395 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700396 }
397 }
398
399 /**
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800400 * Sends a broadcast with the voicemail notification information to the default dialer. This
401 * method is also used to indicate to the default dialer when to clear the
402 * notification. A pending intent can be passed to the default dialer to indicate an action to
Bryce Lee5dc90842015-08-11 07:57:14 -0700403 * be taken as it would by a notification produced in this class.
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800404 * @param phone The phone the notification is sent from
Bryce Lee5dc90842015-08-11 07:57:14 -0700405 * @param count The number of pending voicemail messages to indicate on the notification. A
406 * Value of 0 is passed here to indicate that the notification should be cleared.
407 * @param number The voicemail phone number if specified.
408 * @param pendingIntent The intent that should be passed as the action to be taken.
409 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
410 * otherwise, {@code false} to indicate the intent launches voicemail.
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800411 * @param userHandle The user to receive the notification. Each user can have their own default
412 * dialer.
413 * @return {@code true} if the default was notified of the notification.
Bryce Lee5dc90842015-08-11 07:57:14 -0700414 */
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800415 private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
416 String number, PendingIntent pendingIntent, boolean isSettingsIntent,
Ta-wei Yen282a9702017-05-30 17:32:29 -0700417 UserHandle userHandle, boolean isRefresh) {
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800418
419 if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
420 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
Bryce Lee5dc90842015-08-11 07:57:14 -0700421 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Bryce Lee5dc90842015-08-11 07:57:14 -0700422 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
Ta-wei Yena71a38b2017-02-24 18:19:27 -0800423 intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
424 PhoneUtils.makePstnPhoneAccountHandle(phone));
Ta-wei Yen282a9702017-05-30 17:32:29 -0700425 intent.putExtra(EXTRA_IS_REFRESH, isRefresh);
Bryce Lee5dc90842015-08-11 07:57:14 -0700426 if (count != null) {
427 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
428 }
429
430 // Additional information about the voicemail notification beyond the count is only
431 // present when the count not specified or greater than 0. The value of 0 represents
432 // clearing the notification, which does not require additional information.
433 if (count == null || count > 0) {
434 if (!TextUtils.isEmpty(number)) {
435 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
436 }
437
438 if (pendingIntent != null) {
439 intent.putExtra(isSettingsIntent
440 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
441 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
442 pendingIntent);
443 }
444 }
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800445 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700446 return true;
447 }
448
449 return false;
450 }
451
Ta-wei Yen5bb19562016-11-16 11:05:37 -0800452 private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
453 String dialerPackage = DefaultDialerManager
454 .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
455 return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
456 .setPackage(dialerPackage);
457 }
458
459 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
460 Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
461 if (intent == null) {
462 return false;
463 }
464
465 List<ResolveInfo> receivers = mContext.getPackageManager()
466 .queryBroadcastReceivers(intent, 0);
467 return receivers.size() > 0;
468 }
469
Bryce Lee5dc90842015-08-11 07:57:14 -0700470 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700471 * Updates the message call forwarding indicator notification.
472 *
473 * @param visible true if there are messages waiting
474 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800475 /* package */ void updateCfi(int subId, boolean visible) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700476 if (DBG) log("updateCfi(): " + visible);
477 if (visible) {
478 // If Unconditional Call Forwarding (forward all calls) for VOICE
479 // is enabled, just show a notification. We'll default to expanded
480 // view for now, so the there is less confusion about the icon. If
481 // it is deemed too weird to have CF indications as expanded views,
482 // then we'll flip the flag back.
483
484 // TODO: We may want to take a look to see if the notification can
485 // display the target to forward calls to. This will require some
486 // effort though, since there are multiple layers of messages that
487 // will need to propagate that information.
488
Andrew Leed5165b02014-12-05 15:53:58 -0800489 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
490 if (subInfo == null) {
491 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
492 return;
493 }
494
495 String notificationTitle;
496 if (mTelephonyManager.getPhoneCount() > 1) {
497 notificationTitle = subInfo.getDisplayName().toString();
498 } else {
499 notificationTitle = mContext.getString(R.string.labelCF);
500 }
501
fionaxu8b7620d2017-05-01 16:22:17 -0700502 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700503 .setSmallIcon(R.drawable.stat_sys_phone_call_forward)
Andrew Leed5165b02014-12-05 15:53:58 -0800504 .setColor(subInfo.getIconTint())
505 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700506 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
507 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700508 .setOngoing(true)
fionaxu8b7620d2017-05-01 16:22:17 -0700509 .setChannel(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700510
Andrew Lee99d0ac22014-10-10 13:18:04 -0700511 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800512 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700513 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800514 SubscriptionInfoHelper.addExtrasToIntent(
515 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
516 PendingIntent contentIntent =
517 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700518
519 List<UserInfo> users = mUserManager.getUsers(true);
520 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800521 final UserInfo user = users.get(i);
522 if (user.isManagedProfile()) {
523 continue;
524 }
525 UserHandle userHandle = user.getUserHandle();
Xiaohui Chen3105e9a2015-10-21 12:27:17 -0700526 builder.setContentIntent(user.isAdmin() ? contentIntent : null);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800527 mNotificationManager.notifyAsUser(
528 Integer.toString(subId) /* tag */,
529 CALL_FORWARD_NOTIFICATION,
530 builder.build(),
531 userHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700532 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700533 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700534 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800535 Integer.toString(subId) /* tag */,
536 CALL_FORWARD_NOTIFICATION,
537 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700538 }
539 }
540
541 /**
542 * Shows the "data disconnected due to roaming" notification, which
543 * appears when you lose data connectivity because you're roaming and
544 * you have the "data roaming" feature turned off.
545 */
546 /* package */ void showDataDisconnectedRoaming() {
547 if (DBG) log("showDataDisconnectedRoaming()...");
548
549 // "Mobile network settings" screen / dialog
550 Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700551 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700552
553 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
554
fionaxu8b7620d2017-05-01 16:22:17 -0700555 final Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700556 .setSmallIcon(android.R.drawable.stat_sys_warning)
557 .setContentTitle(mContext.getText(R.string.roaming))
558 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
fionaxu75b66a72017-04-19 19:01:56 -0700559 .setContentText(contentText)
fionaxu8b7620d2017-05-01 16:22:17 -0700560 .setChannel(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700561
Andrew Lee99d0ac22014-10-10 13:18:04 -0700562 List<UserInfo> users = mUserManager.getUsers(true);
563 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800564 final UserInfo user = users.get(i);
565 if (user.isManagedProfile()) {
566 continue;
567 }
568 UserHandle userHandle = user.getUserHandle();
Xiaohui Chen3105e9a2015-10-21 12:27:17 -0700569 builder.setContentIntent(user.isAdmin() ? contentIntent : null);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700570 final Notification notif =
571 new Notification.BigTextStyle(builder).bigText(contentText).build();
572 mNotificationManager.notifyAsUser(
573 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle);
574 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700575 }
576
577 /**
578 * Turns off the "data disconnected due to roaming" notification.
579 */
580 /* package */ void hideDataDisconnectedRoaming() {
581 if (DBG) log("hideDataDisconnectedRoaming()...");
582 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
583 }
584
585 /**
586 * Display the network selection "no service" notification
587 * @param operator is the numeric operator number
Jayachandran C2ef9a482017-05-12 22:07:47 -0700588 * @param subId is the subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700589 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700590 private void showNetworkSelection(String operator, int subId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700591 if (DBG) log("showNetworkSelection(" + operator + ")...");
592
fionaxu8b7620d2017-05-01 16:22:17 -0700593 Notification.Builder builder = new Notification.Builder(mContext)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700594 .setSmallIcon(android.R.drawable.stat_sys_warning)
595 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
596 .setContentText(
597 mContext.getString(R.string.notification_network_selection_text, operator))
598 .setShowWhen(false)
fionaxu75b66a72017-04-19 19:01:56 -0700599 .setOngoing(true)
fionaxu8b7620d2017-05-01 16:22:17 -0700600 .setChannel(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700601
602 // create the target network operators settings intent
603 Intent intent = new Intent(Intent.ACTION_MAIN);
604 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
605 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700606 // Use MobileNetworkSettings to handle the selection intent
Wei Liube964582015-08-21 11:57:00 -0700607 intent.setComponent(new ComponentName(
Malcolm Chen34d4fa52017-06-05 19:02:16 -0700608 mContext.getString(R.string.mobile_network_settings_package),
609 mContext.getString(R.string.mobile_network_settings_class)));
Jayachandran C2ef9a482017-05-12 22:07:47 -0700610 intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, subId);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700611 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700612
Andrew Lee99d0ac22014-10-10 13:18:04 -0700613 List<UserInfo> users = mUserManager.getUsers(true);
614 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800615 final UserInfo user = users.get(i);
616 if (user.isManagedProfile()) {
617 continue;
618 }
619 UserHandle userHandle = user.getUserHandle();
Xiaohui Chen3105e9a2015-10-21 12:27:17 -0700620 builder.setContentIntent(user.isAdmin() ? contentIntent : null);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700621 mNotificationManager.notifyAsUser(
622 null /* tag */,
623 SELECTED_OPERATOR_FAIL_NOTIFICATION,
624 builder.build(),
625 userHandle);
626 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700627 }
628
629 /**
630 * Turn off the network selection "no service" notification
631 */
632 private void cancelNetworkSelection() {
633 if (DBG) log("cancelNetworkSelection()...");
Andrew Lee99d0ac22014-10-10 13:18:04 -0700634 mNotificationManager.cancelAsUser(
635 null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700636 }
637
638 /**
639 * Update notification about no service of user selected operator
640 *
641 * @param serviceState Phone service state
Jayachandran C2ef9a482017-05-12 22:07:47 -0700642 * @param subId The subscription ID
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700643 */
Jayachandran C2ef9a482017-05-12 22:07:47 -0700644 void updateNetworkSelection(int serviceState, int subId) {
645 int phoneId = SubscriptionManager.getPhoneId(subId);
646 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
647 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
648 if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800649 if (SubscriptionManager.isValidSubscriptionId(subId)) {
650 // get the shared preference of network_selection.
651 // empty is auto mode, otherwise it is the operator alpha name
652 // in case there is no operator name, check the operator numeric
653 SharedPreferences sp =
654 PreferenceManager.getDefaultSharedPreferences(mContext);
655 String networkSelection =
Amit Mahajanc5201f42015-11-24 15:38:12 -0800656 sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
Amit Mahajana60be872015-01-15 16:05:08 -0800657 if (TextUtils.isEmpty(networkSelection)) {
658 networkSelection =
Amit Mahajanc5201f42015-11-24 15:38:12 -0800659 sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
Amit Mahajana60be872015-01-15 16:05:08 -0800660 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700661
Amit Mahajana60be872015-01-15 16:05:08 -0800662 if (DBG) log("updateNetworkSelection()..." + "state = " +
663 serviceState + " new network " + networkSelection);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700664
Amit Mahajana60be872015-01-15 16:05:08 -0800665 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
666 && !TextUtils.isEmpty(networkSelection)) {
Jayachandran C2ef9a482017-05-12 22:07:47 -0700667 showNetworkSelection(networkSelection, subId);
Hall Liu2b846c72016-02-09 18:21:24 -0800668 mSelectedUnavailableNotify = true;
Amit Mahajana60be872015-01-15 16:05:08 -0800669 } else {
670 if (mSelectedUnavailableNotify) {
671 cancelNetworkSelection();
672 mSelectedUnavailableNotify = false;
673 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700674 }
675 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800676 if (DBG) log("updateNetworkSelection()..." + "state = " +
677 serviceState + " not updating network due to invalid subId " + subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700678 }
679 }
680 }
681
682 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
683 if (mToast != null) {
684 mToast.cancel();
685 }
686
687 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
688 mToast.show();
689 }
690
691 private void log(String msg) {
692 Log.d(LOG_TAG, msg);
693 }
694}