Merge "Improves service state changed notification"
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index e69a593..842563d 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -211,6 +211,7 @@
     protected static final int EVENT_PHONE_TYPE_SWITCHED               = 50;
     protected static final int EVENT_RADIO_POWER_FROM_CARRIER          = 51;
     protected static final int EVENT_SIM_NOT_INSERTED                  = 52;
+    protected static final int EVENT_IMS_SERVICE_STATE_CHANGED         = 53;
 
     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -1346,6 +1347,15 @@
                 updateSpnDisplay();
                 break;
 
+            case EVENT_IMS_SERVICE_STATE_CHANGED:
+                if (DBG) log("EVENT_IMS_SERVICE_STATE_CHANGED");
+                // IMS state will only affect the merged service state if the service state of
+                // GsmCdma phone is not STATE_IN_SERVICE.
+                if (mSS.getState() != ServiceState.STATE_IN_SERVICE) {
+                    mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+                }
+                break;
+
             //CDMA
             case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
                 handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
@@ -2536,6 +2546,11 @@
         }
     }
 
+    /** Called when the service state of ImsPhone is changed. */
+    public void onImsServiceStateChanged() {
+        sendMessage(obtainMessage(EVENT_IMS_SERVICE_STATE_CHANGED));
+    }
+
     public void setImsRegistrationState(boolean registered) {
         log("ImsRegistrationState - registered : " + registered);
 
@@ -2798,6 +2813,8 @@
             mRejectCode = mNewRejectCode;
         }
 
+        ServiceState oldMergedSS = mPhone.getServiceState();
+
         // swap mSS and mNewSS to put new state in mSS
         ServiceState tss = mSS;
         mSS = mNewSS;
@@ -2909,7 +2926,10 @@
             setRoamingType(mSS);
             log("Broadcasting ServiceState : " + mSS);
             // notify using PhoneStateListener and the legacy intent ACTION_SERVICE_STATE_CHANGED
-            mPhone.notifyServiceStateChanged(mSS);
+            // notify service state changed only if the merged service state is changed.
+            if (!oldMergedSS.equals(mPhone.getServiceState())) {
+                mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+            }
 
             // insert into ServiceStateProvider. This will trigger apps to wake through JobScheduler
             mPhone.getContext().getContentResolver()
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 03843a7..d27cae7 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -258,9 +258,21 @@
         return mSS;
     }
 
-    /* package */ void setServiceState(int state) {
-        mSS.setVoiceRegState(state);
+    @VisibleForTesting
+    public void setServiceState(int state) {
+        boolean isVoiceRegStateChanged = false;
+
+        synchronized (this) {
+            isVoiceRegStateChanged = mSS.getVoiceRegState() != state;
+            mSS.setVoiceRegState(state);
+        }
         updateDataServiceState();
+
+        if (isVoiceRegStateChanged) {
+            if (mDefaultPhone.getServiceStateTracker() != null) {
+                mDefaultPhone.getServiceStateTracker().onImsServiceStateChanged();
+            }
+        }
     }
 
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 15897d4..5c2b427 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -332,6 +332,31 @@
     }
 
     @Test
+    public void testOnImsServiceStateChanged() {
+        // The service state of GsmCdmaPhone is STATE_OUT_OF_SERVICE, and IMS is unregistered.
+        ServiceState ss = new ServiceState();
+        ss.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+        sst.mSS = ss;
+
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_IMS_SERVICE_STATE_CHANGED));
+        waitForMs(200);
+
+        // The listener will be notified that the service state was changed.
+        verify(mPhone).notifyServiceStateChanged(any(ServiceState.class));
+
+        // The service state of GsmCdmaPhone is STATE_IN_SERVICE, and IMS is registered.
+        ss = new ServiceState();
+        ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+        sst.mSS = ss;
+
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_IMS_SERVICE_STATE_CHANGED));
+        waitForMs(200);
+
+        // Nothing happened because the IMS service state was not affected the merged service state.
+        verify(mPhone, times(1)).notifyServiceStateChanged(any(ServiceState.class));
+    }
+
+    @Test
     @MediumTest
     public void testSignalStrength() {
         SignalStrength ss = new SignalStrength(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 3b357d7..f2e69cc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -30,6 +30,8 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -47,6 +49,7 @@
 import android.os.SystemProperties;
 import android.support.test.filters.FlakyTest;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsCallProfile;
@@ -485,6 +488,24 @@
     }
 
     @Test
+    public void testShouldSendNotificationWhenServiceStateIsChanged() {
+        mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+        reset(mSST);
+
+        mImsPhoneUT.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+        verify(mSST).onImsServiceStateChanged();
+    }
+
+    @Test
+    public void testShouldNotSendNotificationWhenServiceStateIsNotChanged() {
+        mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+        reset(mSST);
+
+        mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+        verify(mSST, never()).onImsServiceStateChanged();
+    }
+
+    @Test
     @SmallTest
     public void testCellBarring() throws Exception {
         Message msg = mTestHandler.obtainMessage();