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);
         }
     };