Handle MSIM behavior for VM notifications and CFI.
Implement correct behavior for voicemail notifications, also known
as message waiting indicator (MWI) and call forwarding (CFI).
- Don't instantiate CallNotifier with a Phone object.
+ Change updateMwi and updateCfi to take the subscription id as a
parameter so the notification can be shown on a per-SIM basis.
+ Change phone state listener to be subscription-specific.
+ Register/unregister phone state listeners on subscription changes.
+ Update Cfi according to subscription-specific changes.
+ Use subscription ids as a tag on the voicemail/call forwarding
notifications so they can be shown/canceled with greater precision.
Tested:
+ Voicemail ringtone on Sprout for different SIMs.
+ Voicemail vibration on Sprout for different SIMs.
+ Call forwarding notifications on Sprout for different SIMs.
+ Remove/place SIM on Shamu to see notifications disappear/reappear.
TBD:
+ Voicemail notifications just dial the voicemail schema right now.
Need to ascertain whether we can dial a specific number, and what
happens if the number is the same for different SIMs.
Bug: 18232725
Change-Id: Ie15c3d640e8da217fa8778b2d9d904d76bf0c586
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 01a2449..6f9c9dd 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -49,10 +49,18 @@
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
/**
* Phone app module that listens for phone state changes and various other
* events from the telephony layer, and triggers any resulting UI behavior
@@ -85,6 +93,8 @@
// object used to synchronize access to mCallerInfoQueryState
private Object mCallerInfoQueryStateGuard = new Object();
+ private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
+ new ArrayMap<Integer, CallNotifierPhoneStateListener>();
private PhoneGlobals mApplication;
private CallManager mCM;
@@ -103,20 +113,22 @@
// Cached AudioManager
private AudioManager mAudioManager;
-
private final BluetoothManager mBluetoothManager;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
/**
* Initialize the singleton CallNotifier instance.
* This is only done once, at startup, from PhoneApp.onCreate().
*/
- /* package */ static CallNotifier init(PhoneGlobals app, Phone phone,
- CallLogger callLogger, CallStateMonitor callStateMonitor,
+ /* package */ static CallNotifier init(
+ PhoneGlobals app,
+ CallLogger callLogger,
+ CallStateMonitor callStateMonitor,
BluetoothManager bluetoothManager) {
synchronized (CallNotifier.class) {
if (sInstance == null) {
- sInstance = new CallNotifier(app, phone, callLogger, callStateMonitor,
- bluetoothManager);
+ sInstance = new CallNotifier(app, callLogger, callStateMonitor, bluetoothManager);
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
@@ -125,29 +137,38 @@
}
/** Private constructor; @see init() */
- private CallNotifier(PhoneGlobals app, Phone phone, CallLogger callLogger,
- CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager) {
+ private CallNotifier(
+ PhoneGlobals app,
+ CallLogger callLogger,
+ CallStateMonitor callStateMonitor,
+ BluetoothManager bluetoothManager) {
mApplication = app;
mCM = app.mCM;
mCallLogger = callLogger;
mBluetoothManager = bluetoothManager;
mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
+ mTelephonyManager =
+ (TelephonyManager) mApplication.getSystemService(Context.TELEPHONY_SERVICE);
+ mSubscriptionManager = (SubscriptionManager) mApplication.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
callStateMonitor.addListener(this);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
adapter.getProfileProxy(mApplication.getApplicationContext(),
- mBluetoothProfileServiceListener,
- BluetoothProfile.HEADSET);
+ mBluetoothProfileServiceListener,
+ BluetoothProfile.HEADSET);
}
- TelephonyManager telephonyManager = (TelephonyManager) app.getSystemService(
- Context.TELEPHONY_SERVICE);
- telephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
+ mSubscriptionManager.registerOnSubscriptionsChangedListener(
+ new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ updatePhoneStateListeners();
+ }
+ });
}
private void createSignalInfoToneGenerator() {
@@ -239,20 +260,6 @@
}
}
- PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onMessageWaitingIndicatorChanged(boolean visible) {
- if (VDBG) log("onMessageWaitingIndicatorChanged(): " + visible);
- mApplication.notificationMgr.updateMwi(visible);
- }
-
- @Override
- public void onCallForwardingIndicatorChanged(boolean visible) {
- if (VDBG) log("onCallForwardingIndicatorChanged(): " + visible);
- mApplication.notificationMgr.updateCfi(visible);
- }
- };
-
/**
* Handles a "new ringing connection" event from the telephony layer.
*/
@@ -913,6 +920,58 @@
SHOW_MESSAGE_NOTIFICATION_TIME);
}
+ public void updatePhoneStateListeners() {
+ List<SubscriptionInfo> subInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
+
+ // Unregister phone listeners for inactive subscriptions.
+ Iterator<Integer> itr = mPhoneStateListeners.keySet().iterator();
+ while (itr.hasNext()) {
+ int subId = itr.next();
+ if (subInfos == null || !containsSubId(subInfos, subId)) {
+ // Hide the outstanding notifications.
+ mApplication.notificationMgr.updateMwi(subId, false);
+ mApplication.notificationMgr.updateCfi(subId, false);
+
+ // Listening to LISTEN_NONE removes the listener.
+ mTelephonyManager.listen(
+ mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
+ itr.remove();
+ }
+ }
+
+ if (subInfos == null) {
+ return;
+ }
+
+ // Register new phone listeners for active subscriptions.
+ for (int i = 0; i < subInfos.size(); i++) {
+ int subId = subInfos.get(i).getSubscriptionId();
+ if (!mPhoneStateListeners.containsKey(subId)) {
+ CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
+ mTelephonyManager.listen(listener,
+ PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
+ mPhoneStateListeners.put(subId, listener);
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
+ */
+ private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
+ if (subInfos == null) {
+ return false;
+ }
+
+ for (int i = 0; i < subInfos.size(); i++) {
+ if (subInfos.get(i).getSubscriptionId() == subId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Helper class to play SignalInfo tones using the ToneGenerator.
*
@@ -1014,14 +1073,32 @@
}
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
- new BluetoothProfile.ServiceListener() {
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- mBluetoothHeadset = (BluetoothHeadset) proxy;
- if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
+ }
+
+ public void onServiceDisconnected(int profile) {
+ mBluetoothHeadset = null;
+ }
+ };
+
+ private class CallNotifierPhoneStateListener extends PhoneStateListener {
+ public CallNotifierPhoneStateListener(int subId) {
+ super(subId);
}
- public void onServiceDisconnected(int profile) {
- mBluetoothHeadset = null;
+ @Override
+ public void onMessageWaitingIndicatorChanged(boolean visible) {
+ if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
+ mApplication.notificationMgr.updateMwi(this.mSubId, visible);
+ }
+
+ @Override
+ public void onCallForwardingIndicatorChanged(boolean visible) {
+ if (VDBG) log("onCallForwardingIndicatorChanged(): " + this.mSubId + " " + visible);
+ mApplication.notificationMgr.updateCfi(this.mSubId, visible);
}
};
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 6e372c2..90b6595 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -35,8 +35,8 @@
import android.telecom.PhoneAccount;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
@@ -83,6 +83,7 @@
private StatusBarManager mStatusBarManager;
private UserManager mUserManager;
private Toast mToast;
+ private SubscriptionManager mSubscriptionManager;
public StatusBarHelper statusBarHelper;
@@ -103,15 +104,7 @@
mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
mPhone = app.phone; // TODO: better style to use mCM.getDefaultPhone() everywhere instead
statusBarHelper = new StatusBarHelper();
-
- SubscriptionManager.from(mContext).registerOnSubscriptionsChangedListener(
- new OnSubscriptionsChangedListener() {
- @Override
- public void onSubscriptionsChanged() {
- // Update the message waiting indicator if the SIM is changed.
- updateMwi(mPhone.getMessageWaitingIndicator());
- }
- });
+ mSubscriptionManager = SubscriptionManager.from(mContext);
}
/**
@@ -240,8 +233,8 @@
*
* @param visible true if there are messages waiting
*/
- /* package */ void updateMwi(boolean visible) {
- if (DBG) log("updateMwi(): " + visible);
+ /* package */ void updateMwi(int subId, boolean visible) {
+ if (DBG) log("updateMwi(): subId " + subId + " update to " + visible);
if (!PhoneGlobals.sVoiceCapable) {
// Do not show the message waiting indicator on devices which are not voice capable.
@@ -251,6 +244,12 @@
}
if (visible) {
+ Phone phone = PhoneGlobals.getPhone(subId);
+ if (phone == null) {
+ Log.w(LOG_TAG, "Null phone returned for " + subId);
+ return;
+ }
+
int resId = android.R.drawable.stat_notify_voicemail;
// This Notification can get a lot fancier once we have more
@@ -264,7 +263,7 @@
// notification.
String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
- String vmNumber = mPhone.getVoiceMailNumber();
+ String vmNumber = phone.getVoiceMailNumber();
if (DBG) log("- got vm number: '" + vmNumber + "'");
// The voicemail number may be null because:
@@ -272,15 +271,14 @@
// (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
// happen when the device first boots if we get a MWI notification when we
// register on the network before the SIM has loaded. In this case, the
- // SubscriptionListener this class registers on the SubscriptionManager will
- // call this method again once the SIM is loaded.
- if ((vmNumber == null) && !mPhone.getIccRecordsLoaded()) {
+ // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
+ if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
return;
}
- if (TelephonyCapabilities.supportsVoiceMessageCount(mPhone)) {
- int vmCount = mPhone.getVoiceMessageCount();
+ if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
+ int vmCount = phone.getVoiceMessageCount();
String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
notificationTitle = String.format(titleFormat, vmCount);
}
@@ -297,8 +295,9 @@
Intent intent = new Intent(Intent.ACTION_CALL,
Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null));
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
- Uri ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
+ Uri ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(phone);
Notification.Builder builder = new Notification.Builder(mContext);
builder.setSmallIcon(resId)
@@ -310,7 +309,7 @@
.setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
.setOngoing(true);
- if (VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone)) {
+ if (VoicemailNotificationSettingsUtil.isVibrationEnabled(phone)) {
builder.setDefaults(Notification.DEFAULT_VIBRATE);
}
@@ -323,12 +322,17 @@
UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
&& !user.isManagedProfile()) {
mNotificationManager.notifyAsUser(
- null /* tag */, VOICEMAIL_NOTIFICATION, notification, userHandle);
+ Integer.toString(subId) /* tag */,
+ VOICEMAIL_NOTIFICATION,
+ notification,
+ userHandle);
}
}
} else {
mNotificationManager.cancelAsUser(
- null /* tag */, VOICEMAIL_NOTIFICATION, UserHandle.ALL);
+ Integer.toString(subId) /* tag */,
+ VOICEMAIL_NOTIFICATION,
+ UserHandle.ALL);
}
}
@@ -337,7 +341,7 @@
*
* @param visible true if there are messages waiting
*/
- /* package */ void updateCfi(boolean visible) {
+ /* package */ void updateCfi(int subId, boolean visible) {
if (DBG) log("updateCfi(): " + visible);
if (visible) {
// If Unconditional Call Forwarding (forward all calls) for VOICE
@@ -359,9 +363,12 @@
.setOngoing(true);
Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
- PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ SubscriptionInfoHelper.addExtrasToIntent(
+ intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
+ PendingIntent contentIntent =
+ PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
List<UserInfo> users = mUserManager.getUsers(true);
for (int i = 0; i < users.size(); i++) {
@@ -371,12 +378,17 @@
}
UserHandle userHandle = user.getUserHandle();
builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
- mNotificationManager.notifyAsUser(
- null /* tag */, CALL_FORWARD_NOTIFICATION, builder.build(), userHandle);
+ mNotificationManager.notifyAsUser(
+ Integer.toString(subId) /* tag */,
+ CALL_FORWARD_NOTIFICATION,
+ builder.build(),
+ userHandle);
}
} else {
mNotificationManager.cancelAsUser(
- null /* tag */, CALL_FORWARD_NOTIFICATION, UserHandle.ALL);
+ Integer.toString(subId) /* tag */,
+ CALL_FORWARD_NOTIFICATION,
+ UserHandle.ALL);
}
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index fe40b17..03827ce 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -48,6 +48,7 @@
import android.preference.PreferenceManager;
import android.provider.Settings.System;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -402,8 +403,7 @@
// asynchronous events from the telephony layer (like
// launching the incoming-call UI when an incoming call comes
// in.)
- notifier = CallNotifier.init(this, phone, callLogger, callStateMonitor,
- bluetoothManager);
+ notifier = CallNotifier.init(this, callLogger, callStateMonitor, bluetoothManager);
// register for ICC status
IccCard sim = phone.getIccCard();
@@ -492,17 +492,8 @@
return getInstance().phone;
}
- /**
- * Returns a list of the currently active phones for the Telephony package.
- */
- public static List<Phone> getPhones() {
- int[] subIds = SubscriptionController.getInstance().getActiveSubIdList();
- List<Phone> phones = new ArrayList<Phone>(subIds.length);
-
- for (int i = 0; i < subIds.length; i++) {
- phones.add(PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subIds[i])));
- }
- return phones;
+ public static Phone getPhone(int subId) {
+ return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
}
/* package */ BluetoothManager getBluetoothManager() {
diff --git a/src/com/android/phone/SubscriptionInfoHelper.java b/src/com/android/phone/SubscriptionInfoHelper.java
index 1255452..347d00e 100644
--- a/src/com/android/phone/SubscriptionInfoHelper.java
+++ b/src/com/android/phone/SubscriptionInfoHelper.java
@@ -76,6 +76,10 @@
}
public static void addExtrasToIntent(Intent intent, SubscriptionInfo subscription) {
+ if (subscription == null) {
+ return;
+ }
+
intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subscription.getSubscriptionId());
intent.putExtra(
SubscriptionInfoHelper.SUB_LABEL_EXTRA, subscription.getDisplayName().toString());
diff --git a/src/com/android/phone/settings/VoicemailRingtonePreference.java b/src/com/android/phone/settings/VoicemailRingtonePreference.java
index fa3cc70..3dbb99f 100644
--- a/src/com/android/phone/settings/VoicemailRingtonePreference.java
+++ b/src/com/android/phone/settings/VoicemailRingtonePreference.java
@@ -39,6 +39,10 @@
}
}
};
+ }
+
+ public void init(Phone phone) {
+ mPhone = phone;
final Preference preference = this;
final String preferenceKey =
@@ -58,10 +62,6 @@
updateRingtoneName();
}
- public void init(Phone phone) {
- mPhone = phone;
- }
-
@Override
protected Uri onRestoreRingtone() {
return VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);