Fix race condition which caused extra ImsPhone object to be created.

Extra ImsPhone object was created if IMS service was available when
voice RAT was changing. Later this object was replaced inside GSM or
CDMA phone object, but it proceed receiving updates from IMS service.

Bug: 19986577
Change-Id: I2907569fc0270e58a37725d530da0a60ead4215e
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 59e932b..c9cce3d 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -1889,6 +1889,11 @@
     public Phone getImsPhone();
 
     /**
+     * Start listening for IMS service UP/DOWN events.
+     */
+    public void startMonitoringImsService();
+
+    /**
      * Release the local instance of the ImsPhone and disconnect from
      * the phone.
      * @return the instance of the ImsPhone phone previously owned
diff --git a/src/java/com/android/internal/telephony/PhoneBase.java b/src/java/com/android/internal/telephony/PhoneBase.java
index 0148891..c805110 100644
--- a/src/java/com/android/internal/telephony/PhoneBase.java
+++ b/src/java/com/android/internal/telephony/PhoneBase.java
@@ -86,6 +86,7 @@
 public abstract class PhoneBase extends Handler implements Phone {
     private static final String LOG_TAG = "PhoneBase";
 
+    private boolean mImsIntentReceiverRegistered = false;
     private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -100,12 +101,14 @@
                 }
             }
 
-            if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
-                mImsServiceReady = true;
-                updateImsPhone();
-            } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
-                mImsServiceReady = false;
-                updateImsPhone();
+            synchronized (PhoneProxy.lockForRadioTechnologyChange) {
+                if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
+                    mImsServiceReady = true;
+                    updateImsPhone();
+                } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
+                    mImsServiceReady = false;
+                    updateImsPhone();
+                }
             }
         }
     };
@@ -218,7 +221,6 @@
 
     protected int mPhoneId;
 
-    private final Object mImsLock = new Object();
     private boolean mImsServiceReady = false;
     protected ImsPhone mImsPhone = null;
 
@@ -425,26 +427,36 @@
         mUiccController = UiccController.getInstance();
         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
 
-        // Monitor IMS service - but first poll to see if already up (could miss
-        // intent)
-        ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
-        if (imsManager != null && imsManager.isServiceAvailable()) {
-            mImsServiceReady = true;
-            updateImsPhone();
-        }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
-        filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
-        mContext.registerReceiver(mImsIntentReceiver, filter);
-
         mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         mCi.setOnUnsolOemHookRaw(this, EVENT_UNSOL_OEM_HOOK_RAW, null);
     }
 
     @Override
+    public void startMonitoringImsService() {
+        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+            filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+            mContext.registerReceiver(mImsIntentReceiver, filter);
+            mImsIntentReceiverRegistered = true;
+
+            // Monitor IMS service - but first poll to see if already up (could miss
+            // intent)
+            ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
+            if (imsManager != null && imsManager.isServiceAvailable()) {
+                mImsServiceReady = true;
+                updateImsPhone();
+            }
+        }
+    }
+
+    @Override
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
-            mContext.unregisterReceiver(mImsIntentReceiver);
+            if (mImsIntentReceiverRegistered) {
+                mContext.unregisterReceiver(mImsIntentReceiver);
+                mImsIntentReceiverRegistered = false;
+            }
             mCi.unSetOnCallRing(this);
             // Must cleanup all connectionS and needs to use sendMessage!
             mDcTracker.cleanUpAllConnections(null);
@@ -1904,10 +1916,15 @@
 
     @Override
     public ImsPhone relinquishOwnershipOfImsPhone() {
-        synchronized (mImsLock) {
+        synchronized (PhoneProxy.lockForRadioTechnologyChange) {
             if (mImsPhone == null)
                 return null;
 
+            if (mImsIntentReceiverRegistered) {
+                mContext.unregisterReceiver(mImsIntentReceiver);
+                mImsIntentReceiverRegistered = false;
+            }
+
             ImsPhone imsPhone = mImsPhone;
             mImsPhone = null;
 
@@ -1920,7 +1937,7 @@
 
     @Override
     public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) {
-        synchronized (mImsLock) {
+        synchronized (PhoneProxy.lockForRadioTechnologyChange) {
             if (imsPhone == null)
                 return;
 
@@ -1945,26 +1962,24 @@
     }
 
     protected void updateImsPhone() {
-        synchronized (mImsLock) {
-            Rlog.d(LOG_TAG, "updateImsPhone"
-                    + " mImsServiceReady=" + mImsServiceReady);
+        Rlog.d(LOG_TAG, "updateImsPhone"
+                + " mImsServiceReady=" + mImsServiceReady);
 
-            if (mImsServiceReady && (mImsPhone == null)) {
-                mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
-                CallManager.getInstance().registerPhone(mImsPhone);
-                mImsPhone.registerForSilentRedial(
-                        this, EVENT_INITIATE_SILENT_REDIAL, null);
-            } else if (!mImsServiceReady && (mImsPhone != null)) {
-                CallManager.getInstance().unregisterPhone(mImsPhone);
-                mImsPhone.unregisterForSilentRedial(this);
+        if (mImsServiceReady && (mImsPhone == null)) {
+            mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
+            CallManager.getInstance().registerPhone(mImsPhone);
+            mImsPhone.registerForSilentRedial(
+                    this, EVENT_INITIATE_SILENT_REDIAL, null);
+        } else if (!mImsServiceReady && (mImsPhone != null)) {
+            CallManager.getInstance().unregisterPhone(mImsPhone);
+            mImsPhone.unregisterForSilentRedial(this);
 
-                mImsPhone.dispose();
-                // Potential GC issue if someone keeps a reference to ImsPhone.
-                // However: this change will make sure that such a reference does
-                // not access functions through NULL pointer.
-                //mImsPhone.removeReferences();
-                mImsPhone = null;
-            }
+            mImsPhone.dispose();
+            // Potential GC issue if someone keeps a reference to ImsPhone.
+            // However: this change will make sure that such a reference does
+            // not access functions through NULL pointer.
+            //mImsPhone.removeReferences();
+            mImsPhone = null;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 9f02433..29ce320 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -151,9 +151,11 @@
                     if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                         phone = new GSMPhone(context,
                                 sCommandsInterfaces[i], sPhoneNotifier, i);
+                        phone.startMonitoringImsService();
                     } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                         phone = new CDMALTEPhone(context,
                                 sCommandsInterfaces[i], sPhoneNotifier, i);
+                        phone.startMonitoringImsService();
                     }
                     Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
 
diff --git a/src/java/com/android/internal/telephony/PhoneProxy.java b/src/java/com/android/internal/telephony/PhoneProxy.java
index 70ee3db..d402565 100644
--- a/src/java/com/android/internal/telephony/PhoneProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneProxy.java
@@ -40,7 +40,6 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.cdma.CDMALTEPhone;
-import com.android.internal.telephony.RadioCapability;
 import com.android.internal.telephony.uicc.IccCardProxy;
 import com.android.internal.telephony.uicc.IccFileHandler;
 import com.android.internal.telephony.uicc.IsimRecords;
@@ -50,7 +49,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
-import java.util.Set;
 
 import com.android.internal.telephony.dataconnection.DctController;
 
@@ -301,6 +299,7 @@
             if (imsPhone != null) {
                 mActivePhone.acquireOwnershipOfImsPhone(imsPhone);
             }
+            mActivePhone.startMonitoringImsService();
             mActivePhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
         }
 
@@ -1444,6 +1443,9 @@
     public ImsPhone relinquishOwnershipOfImsPhone() { return null; }
 
     @Override
+    public void startMonitoringImsService() {}
+
+    @Override
     public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) { }
 
     @Override