Support VoWifi for rcs.
Gao Bing [Tue, 22 Mar 2016 00:30:53 +0000 (19:30 -0500)]
Need handle it different since the capability could be changed along
with the wifi signal strength.

Bug: 23222011
Change-Id: I68d412798f6a3c9d9b087508ff12405ac3de4b30

rcs/rcsservice/src/com/android/service/ims/RcsService.java
rcs/rcsservice/src/com/android/service/ims/RcsSettingUtils.java
rcs/rcsservice/src/com/android/service/ims/presence/AlarmBroadcastReceiver.java
rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
src/java/com/android/ims/ImsConfig.java

index 5933f87..2fadcac 100644 (file)
@@ -48,6 +48,7 @@ import android.net.ConnectivityManager;
 import com.android.ims.ImsConfig.FeatureValueConstants;
 import com.android.ims.ImsManager;
 import com.android.ims.ImsConfig;
+import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsConnectionStateListener;
 import com.android.ims.ImsServiceClass;
 import com.android.ims.ImsException;
@@ -81,6 +82,8 @@ public class RcsService extends Service{
     private PresenceSubscriber mSubscriber = null;
     private ImsManager mImsManager = null;
 
+    private BroadcastReceiver mReceiver = null;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -122,8 +125,56 @@ public class RcsService extends Service{
                 SubscriptionManager.from(this).getDefaultDataPhoneId());
 
         registerImsConnectionStateListener();
+
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                logger.print("onReceive intent=" + intent);
+                if(ImsManager.ACTION_IMS_SERVICE_UP.equalsIgnoreCase(
+                        intent.getAction())){
+                    handleImsServiceUp();
+                } else if(ImsManager.ACTION_IMS_SERVICE_DOWN.equalsIgnoreCase(
+                        intent.getAction())){
+                    handleImsServiceDown();
+                }
+            }
+        };
+
+        IntentFilter statusFilter = new IntentFilter();
+        statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+        statusFilter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+        registerReceiver(mReceiver, statusFilter);
+    }
+
+    public void handleImsServiceUp() {
+        if(mImsManager == null) {
+            mImsManager = ImsManager.getInstance(this,
+                    SubscriptionManager.getDefaultVoiceSubscriptionId());
+        }
+
+        // Don't check mServiceId since it wasn't reset to INVALID_SERVICE_ID when
+        // got ACTION_IMS_SERVICE_DOWN
+        // This is phone crash case. Reset mServiceId to INVALID_SERVICE_ID
+        mServiceId = INVALID_SERVICE_ID;
+        if(mPublication != null) {
+            mPublication.handleImsServiceUp();
+        }
+
+        registerImsConnectionStateListener();
+    }
+
+    public void handleImsServiceDown() {
+        // Don't close since it could close the wrong one when phone crashed and restarted.
+        //if((mImsManager != null) && (mServiceId != INVALID_SERVICE_ID)) {
+        //    mImsManager.close(mServiceId);
+        //    mServiceId = INVALID_SERVICE_ID;
+        //}
+        if(mPublication != null) {
+            mPublication.handleImsServiceDown();
+        }
     }
 
+
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         logger.debug("RcsService onStartCommand");
@@ -138,6 +189,11 @@ public class RcsService extends Service{
     public void onDestroy() {
         getContentResolver().unregisterContentObserver(mObserver);
         getContentResolver().unregisterContentObserver(mVtSettingObserver);
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+
         mRcsStackAdaptor.finish();
         mPublication.finish();
         mPublication = null;
@@ -367,6 +423,27 @@ public class RcsService extends Service{
                 if(mRcsStackAdaptor != null) {
                     mRcsStackAdaptor.checkSubService();
                 }
+
+                if(mPublication != null) {
+                    mPublication.onImsConnected();
+                }
+            }
+
+            @Override
+            public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
+                logger.debug("onImsDisconnected");
+                if(mPublication != null) {
+                    mPublication.onImsDisconnected();
+                }
+            }
+
+            @Override
+            public void onFeatureCapabilityChanged(final int serviceClass,
+                    final int[] enabledFeatures, final int[] disabledFeatures) {
+                logger.debug("onFeatureCapabilityChanged");
+                if(mPublication != null) {
+                    mPublication.onFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
+                }
             }
         };
 }
index 24f4823..eb415cf 100644 (file)
@@ -49,61 +49,44 @@ public class RcsSettingUtils{
     public RcsSettingUtils() {
     }
 
-    public static boolean isVolteProvisioned(Context context) {
-        boolean volteProvisioned = false;
+    public static boolean isFeatureProvisioned(Context context,
+            int featureId, boolean defaultValue) {
+        // Don't need provision.
+        if (!context.getResources().getBoolean(
+                com.android.internal.R.bool.config_carrier_volte_provisioned)) {
+            return true;
+        }
+
+        boolean provisioned = defaultValue;
         ImsManager imsManager = ImsManager.getInstance(context, 0);
         if (imsManager != null) {
             try {
                 ImsConfig imsConfig = imsManager.getConfigInterface();
                 if (imsConfig != null) {
-                    volteProvisioned = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.VLT_SETTING_ENABLED)
+                    provisioned = imsConfig.getProvisionedValue(featureId)
                             == ImsConfig.FeatureValueConstants.ON;
                 }
             } catch (ImsException ex) {
             }
         }
 
-        logger.debug("volteProvisioned=" + volteProvisioned);
-        return volteProvisioned;
+        logger.debug("featureId=" + featureId + " provisioned=" + provisioned);
+        return provisioned;
     }
 
-    public static boolean isLvcProvisioned(Context context) {
-        boolean lvcProvisioned = false;
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    lvcProvisioned = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.LVC_SETTING_ENABLED)
-                            == ImsConfig.FeatureValueConstants.ON;
-                }
-            } catch (ImsException ex) {
-            }
-        }
+    public static boolean isVowifiProvisioned(Context context) {
+        return isFeatureProvisioned(context,
+                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED, false);
+    }
 
-        logger.debug("lvcProvisioned=" + lvcProvisioned);
-        return lvcProvisioned;
+    public static boolean isLvcProvisioned(Context context) {
+        return isFeatureProvisioned(context,
+                ImsConfig.ConfigConstants.LVC_SETTING_ENABLED, false);
     }
 
     public static boolean isEabProvisioned(Context context) {
-        boolean eabProvisioned = false;
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    eabProvisioned = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.EAB_SETTING_ENABLED)
-                            == ImsConfig.FeatureValueConstants.ON;
-                }
-            } catch (ImsException ex) {
-            }
-        }
-
-        logger.debug("eabProvisioned=" + eabProvisioned);
-        return eabProvisioned;
+        return isFeatureProvisioned(context,
+                ImsConfig.ConfigConstants.EAB_SETTING_ENABLED, false);
     }
 
     public static int getSIPT1Timer(Context context) {
@@ -249,5 +232,24 @@ public class RcsSettingUtils{
             }
         }
     }
+
+    public static int getPublishThrottle(Context context) {
+        int publishThrottle = 60000;
+
+        ImsManager imsManager = ImsManager.getInstance(context, 0);
+        if (imsManager != null) {
+            try {
+                ImsConfig imsConfig = imsManager.getConfigInterface();
+                if (imsConfig != null) {
+                    publishThrottle = imsConfig.getProvisionedValue(
+                            ImsConfig.ConfigConstants.SOURCE_THROTTLE_PUBLISH);
+                }
+            } catch (ImsException ex) {
+            }
+        }
+
+        logger.debug("publishThrottle=" + publishThrottle);
+        return publishThrottle;
+    }
 }
 
index d4422c0..f02f9ce 100644 (file)
@@ -71,7 +71,7 @@ public class AlarmBroadcastReceiver extends BroadcastReceiver{
             int sipCode = intent.getIntExtra("sipCode", 888);
             PresencePublication publication = PresencePublication.getPresencePublication();
             if(publication != null) {
-                publication.retryPublish(sipCode);
+                publication.retryPublish();
             }
         }
         else{
index af7568c..05dcd7a 100644 (file)
@@ -29,6 +29,7 @@
 package com.android.service.ims.presence;
 
 import java.util.List;
+import java.util.Arrays;
 
 import android.content.Context;
 import android.content.Intent;
@@ -45,13 +46,20 @@ import android.content.IntentFilter;
 import android.app.PendingIntent;
 import android.app.AlarmManager;
 import android.os.SystemClock;
+import android.os.Message;
+import android.os.Handler;
+
 
 import com.android.ims.ImsManager;
+import com.android.ims.ImsConnectionStateListener;
+import com.android.ims.ImsServiceClass;
+import com.android.ims.ImsException;
+import android.telephony.SubscriptionManager;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsConfig.FeatureConstants;
 import com.android.ims.ImsConfig.FeatureValueConstants;
-import com.android.service.ims.RcsSettingUtils;
 
+import com.android.service.ims.RcsSettingUtils;
 import com.android.ims.RcsPresenceInfo;
 import com.android.ims.IRcsPresenceListener;
 import com.android.ims.RcsManager.ResultCode;
@@ -73,6 +81,31 @@ import com.android.service.ims.R;
 public class PresencePublication extends PresenceBase {
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
+    private final Object mSyncObj = new Object();
+
+    final static String ACTION_IMS_FEATURE_AVAILABLE =
+            "com.android.service.ims.presence.ims-feature-status-changed";
+    private static final int INVALID_SERVICE_ID = -1;
+
+    boolean mMovedToIWLAN = false;
+    boolean mMovedToLTE = false;
+    boolean mVoPSEnabled = false;
+
+    boolean mIsVolteAvailable = false;
+    boolean mIsVtAvailable = false;
+    boolean mIsVoWifiAvailable = false;
+    boolean mIsViWifiAvailable = false;
+
+    // Queue for the pending PUBLISH request. Just need cache the last one.
+    volatile PublishRequest mPendingRequest = null;
+    volatile PublishRequest mPublishingRequest = null;
+    volatile PublishRequest mPublishedRequest = null;
+
+    /*
+     * Message to notify for a publish
+     */
+    public static final int MESSAGE_RCS_PUBLISH_REQUEST = 1;
+
     /**
      *  Publisher Error base
      */
@@ -104,99 +137,28 @@ public class PresencePublication extends PresenceBase {
     static private PresencePublication sPresencePublication = null;
     private BroadcastReceiver mReceiver = null;
 
-    private PresenceCapability mMyCap = new PresenceCapability();
-
-    private boolean mNetworkTypeLTE;
-    private boolean mNetworkVoPSEnabled = true;
-
     private boolean mHasCachedTrigger = false;
     private boolean mGotTriggerFromStack = false;
     private boolean mDonotRetryUntilPowerCycle = false;
     private boolean mSimLoaded = false;
     private int mPreferredTtyMode = Phone.TTY_MODE_OFF;
 
+    private boolean mImsRegistered = false;
     private boolean mVtEnabled = false;
     private boolean mDataEnabled = false;
 
     public class PublishType{
         public static final int PRES_PUBLISH_TRIGGER_DATA_CHANGED = 0;
+        // the lower layer should send trigger when enable volte
+        // the lower layer should unpublish when disable volte
+        // so only handle VT here.
         public static final int PRES_PUBLISH_TRIGGER_VTCALL_CHANGED = 1;
-        public static final int PRES_PUBLISH_TRIGGER_VOIP_ENABLE_STATUS = 2;
-        public static final int PRES_PUBLISH_TRIGGER_CACHED_TRIGGER = 3;
-        public static final int PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS = 4;
-        public static final int PRES_PUBLISH_TRIGGER_RETRY = 5;
+        public static final int PRES_PUBLISH_TRIGGER_CACHED_TRIGGER = 2;
+        public static final int PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS = 3;
+        public static final int PRES_PUBLISH_TRIGGER_RETRY = 4;
+        public static final int PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED = 5;
     };
 
-    public class PresenceCapability {
-        private boolean mIPVoiceSupported = true;
-        private boolean mIPVideoSupported = true;
-
-        public String myNumUri;
-
-        public boolean isIPVoiceSupported() {
-            if(!RcsSettingUtils.isVolteProvisioned(mContext)) {
-                logger.print("isVolteProvisioned()=false");
-                return false;
-            }
-
-            if (!mNetworkTypeLTE || !mNetworkVoPSEnabled) {
-                logger.print("mNetworkTypeLTE=" + mNetworkTypeLTE +
-                        " mNetworkVoPSEnabled=" + mNetworkVoPSEnabled);
-                return false;
-            }
-
-            if(!ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext)) {
-                logger.print("volte is not enabled.");
-                return false;
-            }
-
-            return mIPVoiceSupported;
-        }
-
-        public void setIPVoiceSupported(boolean mIPVoiceSupported) {
-            this.mIPVoiceSupported = mIPVoiceSupported;
-        }
-
-        public boolean isIPVideoSupported() {
-            boolean videoSupported = false;
-            logger.print("mVtEnabled=" + mVtEnabled + " mDataEnabled=" + mDataEnabled);
-
-            if(!RcsSettingUtils.isLvcProvisioned(mContext)) {
-                logger.print("isLvcProvisioned()=false");
-                return false;
-            }
-
-            if(isTtyOn()){
-                videoSupported = false;
-                logger.print("isTtyOn=true, videoSupported=false");
-                return videoSupported;
-            }
-
-            if (mVtEnabled && mDataEnabled && mNetworkVoPSEnabled) {
-                logger.print( "Video is supported 1 and DATA is ON from NV");
-                videoSupported = true;
-            } else {
-                logger.print( "Video is not supported and DATA is OFF from NV");
-                videoSupported = false;
-            }
-
-            if(!ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext)) {
-                videoSupported = false;
-            }
-
-            if (!mNetworkTypeLTE) {
-                logger.print("networkType is not LTE");
-                videoSupported = false;
-            }
-            return videoSupported;
-        }
-
-        public boolean isTtyOn() {
-            logger.debug("isTtyOn settingsTtyMode=" + mPreferredTtyMode);
-            return isTtyEnabled(mPreferredTtyMode);
-        }
-    }
-
     /**
      * @param rcsStackAdaptor
      * @param context
@@ -225,16 +187,17 @@ public class PresencePublication extends PresenceBase {
         mReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                logger.print("statusReceiver.onReceive intent=" + intent);
+                logger.print("onReceive intent=" + intent);
                 if(TelephonyIntents.ACTION_SIM_STATE_CHANGED.equalsIgnoreCase(intent.getAction())){
                     String stateExtra = intent.getStringExtra(
                             IccCardConstants.INTENT_KEY_ICC_STATE);
                     logger.print("ACTION_SIM_STATE_CHANGED INTENT_KEY_ICC_STATE=" + stateExtra);
+                    final TelephonyManager teleMgr = (TelephonyManager) context.getSystemService(
+                                        Context.TELEPHONY_SERVICE);
                     if(IccCardConstants.INTENT_VALUE_ICC_LOADED.equalsIgnoreCase(stateExtra)) {
                         new Thread(new Runnable() {
                             @Override
                             public void run() {
-                                TelephonyManager teleMgr = TelephonyManager.getDefault();
                                 if(teleMgr == null){
                                     logger.error("teleMgr = null");
                                     return;
@@ -274,6 +237,7 @@ public class PresencePublication extends PresenceBase {
                         logger.print("Pulled out SIM, set to PUBLISH_STATE_NOT_PUBLISHED");
 
                         // only reset when the SIM gets absent.
+                        reset();
                         mSimLoaded = false;
                         setPublishState(
                                 PublishState.PUBLISH_STATE_NOT_PUBLISHED);
@@ -282,6 +246,7 @@ public class PresencePublication extends PresenceBase {
                     boolean airplaneMode = intent.getBooleanExtra("state", false);
                     if(airplaneMode){
                         logger.print("Airplane mode, set to PUBLISH_STATE_NOT_PUBLISHED");
+                        reset();
                         setPublishState(
                                 PublishState.PUBLISH_STATE_NOT_PUBLISHED);
                     }
@@ -320,6 +285,135 @@ public class PresencePublication extends PresenceBase {
         sPresencePublication = this;
     }
 
+    private boolean isIPVoiceSupported(boolean volteAvailable, boolean vtAvailable,
+            boolean voWifiAvailable, boolean viWifiAvailable) {
+        // volte and vowifi can be enabled separately
+        if(!ImsManager.isVolteEnabledByPlatform(mContext) &&
+                !ImsManager.isWfcEnabledByPlatform(mContext)) {
+            logger.print("Disabled by platform, voiceSupported=false");
+            return false;
+        }
+
+        if(!ImsManager.isVolteProvisionedOnDevice(mContext) &&
+                !RcsSettingUtils.isVowifiProvisioned(mContext)) {
+            logger.print("Wasn't provisioned, voiceSupported=false");
+            return false;
+        }
+
+        if(!ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
+                !ImsManager.isWfcEnabledByUser(mContext)){
+            logger.print("User didn't enable volte or wfc, voiceSupported=false");
+            return false;
+        }
+
+        // for IWLAN. All WFC settings and provision should be fine if voWifiAvailable is true.
+        if(isOnIWLAN()) {
+            boolean voiceSupported=volteAvailable || voWifiAvailable;
+            logger.print("on IWLAN, voiceSupported=" + voiceSupported);
+            return voiceSupported;
+        }
+
+        // for none-IWLAN
+        if(!isOnLTE()) {
+            logger.print("isOnLTE=false, voiceSupported=false");
+            return false;
+        }
+
+        if(!mVoPSEnabled) {
+            logger.print("mVoPSEnabled=false, voiceSupported=false");
+            return false;
+        }
+
+        logger.print("voiceSupported=true");
+        return true;
+    }
+
+    private boolean isIPVideoSupported(boolean volteAvailable, boolean vtAvailable,
+            boolean voWifiAvailable, boolean viWifiAvailable) {
+        // if volte or vt was disabled then the viwifi will be disabled as well.
+        if(!ImsManager.isVolteEnabledByPlatform(mContext) ||
+                !ImsManager.isVtEnabledByPlatform(mContext)) {
+            logger.print("Disabled by platform, videoSupported=false");
+            return false;
+        }
+
+        if(!ImsManager.isVolteProvisionedOnDevice(mContext) ||
+                !RcsSettingUtils.isLvcProvisioned(mContext)) {
+            logger.print("Not provisioned. videoSupported=false");
+            return false;
+        }
+
+        if(!ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) ||
+                !mVtEnabled){
+            logger.print("User disabled volte or vt, videoSupported=false");
+            return false;
+        }
+
+        if(isTtyOn()){
+            logger.print("isTtyOn=true, videoSupported=false");
+            return false;
+        }
+
+        // for IWLAN, all WFC settings and provision should be fine if viWifiAvailable is true.
+        if(isOnIWLAN()) {
+            boolean videoSupported = vtAvailable || viWifiAvailable;
+            logger.print("on IWLAN, videoSupported=" + videoSupported);
+            return videoSupported;
+        }
+
+        if(!isDataEnabled()) {
+            logger.print("isDataEnabled()=false, videoSupported=false");
+            return false;
+        }
+
+        if(!isOnLTE()) {
+            logger.print("isOnLTE=false, videoSupported=false");
+            return false;
+        }
+
+        if(!mVoPSEnabled) {
+            logger.print("mVoPSEnabled=false, videoSupported=false");
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean isTtyOn() {
+        logger.debug("isTtyOn settingsTtyMode=" + mPreferredTtyMode);
+        return isTtyEnabled(mPreferredTtyMode);
+    }
+
+    public void onImsConnected() {
+        mImsRegistered = true;
+    }
+
+    public void onImsDisconnected() {
+        logger.debug("reset PUBLISH status for IMS had been disconnected");
+        mImsRegistered = false;
+        reset();
+    }
+
+    private void reset() {
+        mIsVolteAvailable = false;
+        mIsVtAvailable = false;
+        mIsVoWifiAvailable = false;
+        mIsViWifiAvailable = false;
+
+        synchronized(mSyncObj) {
+            mPendingRequest = null; //ignore the previous request
+            mPublishingRequest = null;
+            mPublishedRequest = null;
+        }
+    }
+
+    public void handleImsServiceUp() {
+        reset();
+    }
+
+    public void handleImsServiceDown() {
+    }
+
     private void handleProvisionChanged() {
         if(RcsSettingUtils.isEabProvisioned(mContext)) {
             logger.debug("provisioned, set mDonotRetryUntilPowerCycle to false");
@@ -365,6 +459,20 @@ public class PresencePublication extends PresenceBase {
     }
 
     /**
+     * @return return true if it had been PUBLISHED.
+     */
+    public boolean isPublishedOrPublishing() {
+        long publishThreshold = RcsSettingUtils.getPublishThrottle(mContext);
+
+        boolean publishing = false;
+        publishing = (mPublishingRequest != null) &&
+                (System.currentTimeMillis() - mPublishingRequest.getTimestamp()
+                <= publishThreshold);
+
+        return (getPublishState() == PublishState.PUBLISH_STATE_200_OK || publishing);
+    }
+
+    /**
      * @return the Publish State
      */
     public int getPublishState() {
@@ -392,19 +500,23 @@ public class PresencePublication extends PresenceBase {
     public int invokePublish(int trigger){
         int ret;
 
-        long sleepTime = 0;
+        // if the value is true then it will call stack to send the PUBLISH
+        // though the previous publish had the same capability
+        // for example: for retry PUBLISH.
+        boolean bForceToNetwork = true;
         switch(trigger)
         {
             case PublishType.PRES_PUBLISH_TRIGGER_DATA_CHANGED:
             {
-                sleepTime = 300;
                 logger.print("PRES_PUBLISH_TRIGGER_DATA_CHANGED");
+                bForceToNetwork = false;
                 break;
             }
             case PublishType.PRES_PUBLISH_TRIGGER_VTCALL_CHANGED:
             {
-                sleepTime = 300;
                 logger.print("PRES_PUBLISH_TRIGGER_VTCALL_CHANGED");
+                // Didn't get featureCapabilityChanged sometimes.
+                bForceToNetwork = true;
                 break;
             }
             case PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER:
@@ -415,12 +527,14 @@ public class PresencePublication extends PresenceBase {
             case PublishType.PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS:
             {
                 logger.print("PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS");
+                bForceToNetwork = true;
+
                 break;
             }
-            case PublishType.PRES_PUBLISH_TRIGGER_VOIP_ENABLE_STATUS:
+            case PublishType.PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED:
             {
-                sleepTime = 300;
-                logger.print("PRES_PUBLISH_TRIGGER_VOIP_ENABLE_STATUS");
+                bForceToNetwork = false;
+                logger.print("PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED");
                 break;
             }
             case PublishType.PRES_PUBLISH_TRIGGER_RETRY:
@@ -434,17 +548,8 @@ public class PresencePublication extends PresenceBase {
             }
         }
 
-        try {
-            if(sleepTime > 0) {
-                Thread.sleep(sleepTime);
-            }
-        } catch (InterruptedException e) {
-            logger.debug("failed to sleep");
-        }
-
         if(mGotTriggerFromStack == false){
-            // The value mNetworkTypeLTE, mNetworkVoPSEnabled is not correct
-            // if there is no trigger from stack yet.
+            // Waiting for RCS stack get ready.
             logger.print("Didn't get trigger from stack yet, discard framework trigger.");
             return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
         }
@@ -468,20 +573,22 @@ public class PresencePublication extends PresenceBase {
             return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
         }
 
-        ret = requestPublication();
+        PublishRequest publishRequest = new PublishRequest(
+                bForceToNetwork, System.currentTimeMillis());
 
-        if(ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE){
-            mHasCachedTrigger = true;
-        }else{
-            //reset the cached trigger
-            mHasCachedTrigger = false;
-        }
+        requestPublication(publishRequest);
 
-        return ret;
+        return ResultCode.SUCCESS;
     }
 
     public int invokePublish(PresPublishTriggerType val) {
         int ret;
+
+        mGotTriggerFromStack = true;
+
+        // Always send the PUBLISH when we got the trigger from stack.
+        // This can avoid any issue when switch the phone between IWLAN and LTE.
+        boolean bForceToNetwork = true;
         switch (val.getPublishTrigeerType())
         {
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED:
@@ -492,36 +599,69 @@ public class PresencePublication extends PresenceBase {
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED");
-                mNetworkTypeLTE = true;
-                mNetworkVoPSEnabled = false;
+                mMovedToLTE = true;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = false;
+
+                // onImsConnected could came later than this trigger.
+                mImsRegistered = true;
                 break;
             }
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED");
-                mNetworkTypeLTE = true;
-                mNetworkVoPSEnabled = true;
+                mMovedToLTE = true;
+                mVoPSEnabled = true;
+                mMovedToIWLAN = false;
+
+                // onImsConnected could came later than this trigger.
+                mImsRegistered = true;
+                break;
+            }
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN:
+            {
+                logger.print("QRCS_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN");
+                mMovedToLTE = false;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = true;
+
+                // onImsConnected could came later than this trigger.
+                mImsRegistered = true;
                 break;
             }
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_EHRPD");
-                mNetworkTypeLTE = false;
+                mMovedToLTE = false;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = false;
+
+                // onImsConnected could came later than this trigger.
+                mImsRegistered = true;
                 break;
             }
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS");
+                mMovedToLTE = false;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = false;
                 break;
             }
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_2G");
+                mMovedToLTE = false;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = false;
                 break;
             }
             case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_3G");
+                mMovedToLTE = false;
+                mVoPSEnabled = false;
+                mMovedToIWLAN = false;
                 break;
             }
             default:
@@ -533,7 +673,6 @@ public class PresencePublication extends PresenceBase {
             return ResultCode.SUCCESS;
         }
 
-        mGotTriggerFromStack = true;
         if(!mSimLoaded){
             //need to read some information from SIM to publish
             logger.print("invokePublish cache the trigger since the SIM is not ready");
@@ -548,49 +687,248 @@ public class PresencePublication extends PresenceBase {
             return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
         }
 
-        ret = requestPublication();
+        PublishRequest publishRequest = new PublishRequest(
+                bForceToNetwork, System.currentTimeMillis());
 
-        if(ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE){
-            mHasCachedTrigger = true;
-        }else{
-            //reset the cached trigger
-            mHasCachedTrigger = false;
+        requestPublication(publishRequest);
+
+        return ResultCode.SUCCESS;
+    }
+
+    public class PublishRequest {
+        private boolean mForceToNetwork = true;
+        private long mCurrentTime = 0;
+        private boolean mVolteCapable = false;
+        private boolean mVtCapable = false;
+
+        PublishRequest(boolean bForceToNetwork, long currentTime) {
+            refreshPublishContent();
+            mForceToNetwork = bForceToNetwork;
+            mCurrentTime = currentTime;
+        }
+
+        public void refreshPublishContent() {
+            mVolteCapable = isIPVoiceSupported(mIsVolteAvailable,
+                mIsVtAvailable,
+                mIsVoWifiAvailable,
+                mIsViWifiAvailable);
+            mVtCapable = isIPVideoSupported(mIsVolteAvailable,
+                mIsVtAvailable,
+                mIsVoWifiAvailable,
+                mIsViWifiAvailable);
+        }
+
+        public boolean getForceToNetwork() {
+            return mForceToNetwork;
         }
 
-        return ret;
+        public void setForceToNetwork(boolean bForceToNetwork) {
+            mForceToNetwork = bForceToNetwork;
+        }
+
+        public long getTimestamp() {
+            return mCurrentTime;
+        }
+
+        public void setTimestamp(long currentTime) {
+            mCurrentTime = currentTime;
+        }
+
+        public void setVolteCapable(boolean capable) {
+            mVolteCapable = capable;
+        }
+
+        public void setVtCapable(boolean capable) {
+            mVtCapable = capable;
+        }
+
+        public boolean getVolteCapable() {
+            return mVolteCapable;
+        }
+
+        public boolean getVtCapable() {
+            return mVtCapable;
+        }
+
+        public boolean hasSamePublishContent(PublishRequest request) {
+            if(request == null) {
+                logger.error("request == null");
+                return false;
+            }
+
+            return (mVolteCapable == request.getVolteCapable() &&
+                    mVtCapable == request.getVtCapable());
+        }
+
+        public String toString(){
+            return "mForceToNetwork=" + mForceToNetwork +
+                    " mCurrentTime=" + mCurrentTime +
+                    " mVolteCapable=" + mVolteCapable +
+                    " mVtCapable=" + mVtCapable;
+        }
     }
 
-    private int requestPublication(){
-        int ret = -1;
+    private Handler mMsgHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            logger.debug( "Thread=" + Thread.currentThread().getName() + " received "
+                    + msg);
+            if(msg == null){
+                logger.error("msg=null");
+                return;
+           }
+
+            switch (msg.what) {
+                case MESSAGE_RCS_PUBLISH_REQUEST:
+                    logger.debug("handleMessage  msg=RCS_PUBLISH_REQUEST:");
+
+                    PublishRequest publishRequest = (PublishRequest) msg.obj;
+                    synchronized(mSyncObj) {
+                        mPendingRequest = null;
+                    }
+                    doPublish(publishRequest);
+                break;
 
-        if (mRcsStackAdaptor != null) {
-            logger.debug("requestPublication");
+                default:
+                    logger.debug("handleMessage unknown msg=" + msg.what);
+            }
+        }
+    };
 
-            // "No response" had been handled by stack. So handle retry as per stack request only
-            // The retry should be triggered by publish response
-            // Since we are doing a publish, don't need the retry any more.
-            mCancelRetry = true;
+    private void requestPublication(PublishRequest publishRequest) {
+        // this is used to avoid the temp false feature change when switched between network.
+        if(publishRequest == null) {
+            logger.error("Invalid parameter publishRequest == null");
+            return;
+        }
+
+        long requestThrottle = 2000;
+        long currentTime = System.currentTimeMillis();
+        synchronized(mSyncObj){
+            // There is a PUBLISH will be done soon. Discard it
+            if((mPendingRequest != null) && currentTime - mPendingRequest.getTimestamp()
+                    <= requestThrottle) {
+                logger.print("A publish is pending, update the pending request and discard this one");
+                if(publishRequest.getForceToNetwork() && !mPendingRequest.getForceToNetwork()) {
+                    mPendingRequest.setForceToNetwork(true);
+                }
+                mPendingRequest.setTimestamp(publishRequest.getTimestamp());
+                return;
+            }
+
+            mPendingRequest = publishRequest;
+        }
+
+        Message publishMessage = mMsgHandler.obtainMessage(
+                MESSAGE_RCS_PUBLISH_REQUEST, mPendingRequest);
+        mMsgHandler.sendMessageDelayed(publishMessage, requestThrottle);
+    }
+
+    private void doPublish(PublishRequest publishRequest) {
+
+        if(publishRequest == null) {
+            logger.error("publishRequest == null");
+            return;
+        }
+
+        if (mRcsStackAdaptor == null) {
+            logger.error("mRcsStackAdaptor == null");
+            return;
+        }
+
+        if(!mImsRegistered) {
+            logger.debug("IMS wasn't registered");
+            return;
+        }
+
+        // Since we are doing a publish, don't need the retry any more.
+        if(mPendingRetry) {
+            mPendingRetry = false;
+            mAlarmManager.cancel(mRetryAlarmIntent);
+        }
+
+        // always publish the latest status.
+        synchronized(mSyncObj) {
+            publishRequest.refreshPublishContent();
+        }
+
+        logger.print("publishRequest=" + publishRequest);
+        if(!publishRequest.getForceToNetwork() && isPublishedOrPublishing() &&
+                (publishRequest.hasSamePublishContent(mPublishingRequest) ||
+                        (mPublishingRequest == null) &&
+                        publishRequest.hasSamePublishContent(mPublishedRequest)) &&
+                (getPublishState() != PublishState.PUBLISH_STATE_NOT_PUBLISHED)) {
+            logger.print("Don't need publish since the capability didn't change publishRequest " +
+                    publishRequest + " getPublishState()=" + getPublishState());
+            return;
+        }
+
+        // Don't publish too frequently. Checking the throttle timer.
+        if(isPublishedOrPublishing()) {
             if(mPendingRetry) {
-                mPendingRetry = false;
-                mAlarmManager.cancel(mRetryAlarmIntent);
+                logger.print("Pending a retry");
+                return;
             }
 
-            TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
-                    Context.TELEPHONY_SERVICE);
+            long publishThreshold = RcsSettingUtils.getPublishThrottle(mContext);
+            long passed = publishThreshold;
+            if(mPublishingRequest != null) {
+                passed = System.currentTimeMillis() - mPublishingRequest.getTimestamp();
+            } else if(mPublishedRequest != null) {
+                passed = System.currentTimeMillis() - mPublishedRequest.getTimestamp();
+            }
+            passed = passed>=0?passed:publishThreshold;
+
+            long left = publishThreshold - passed;
+            left = left>120000?120000:left; // Don't throttle more then 2 mintues.
+            if(left > 0) {
+                // A publish is ongoing or published in 1 minute.
+                // Since the new publish has new status. So schedule
+                // the publish after the throttle timer.
+                scheduleRetryPublish(left);
+                return;
+            }
+        }
 
-            RcsPresenceInfo presenceInfo = new RcsPresenceInfo(teleMgr.getLine1Number(),
-                    RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN,
-                    mMyCap.isIPVoiceSupported()?RcsPresenceInfo.ServiceState.ONLINE:
-                            RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis(),
-                    mMyCap.isIPVideoSupported()?RcsPresenceInfo.ServiceState.ONLINE:
-                            RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis());
+        // we need send PUBLISH once even the volte is off when power on the phone.
+        // That will tell other phone that it has no volte/vt capability.
+        if(!ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
+                getPublishState() != PublishState.PUBLISH_STATE_NOT_PUBLISHED) {
+             // volte was not enabled.
+             // or it is turnning off volte. lower layer should unpublish
+             reset();
+             return;
+        }
 
-            ret = mRcsStackAdaptor.requestPublication(presenceInfo, null);
-        } else {
-            logger.error("mRcsStackAdaptor = null");
+        TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        if(teleMgr ==null) {
+             logger.debug("teleMgr=null");
+             return;
+         }
+
+        RcsPresenceInfo presenceInfo = new RcsPresenceInfo(teleMgr.getLine1Number(),
+                RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN,
+                publishRequest.getVolteCapable()?RcsPresenceInfo.ServiceState.ONLINE:
+                        RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis(),
+                publishRequest.getVtCapable()?RcsPresenceInfo.ServiceState.ONLINE:
+                        RcsPresenceInfo.ServiceState.OFFLINE, null,
+                        System.currentTimeMillis());
+
+        synchronized(mSyncObj) {
+            mPublishingRequest = publishRequest;
+            mPublishingRequest.setTimestamp(System.currentTimeMillis());
         }
 
-        return ret;
+        int ret = mRcsStackAdaptor.requestPublication(presenceInfo, null);
+        if(ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE){
+            mHasCachedTrigger = true;
+        }else{
+            //reset the cached trigger
+            mHasCachedTrigger = false;
+        }
     }
 
     public void handleCmdStatus(PresCmdStatus cmdStatus) {
@@ -605,8 +943,9 @@ public class PresencePublication extends PresenceBase {
     boolean mCancelRetry = true;
     boolean mPendingRetry = false;
 
-    private void scheduleRetryPublish(int sipCode) {
-        logger.print("sipCode=" + sipCode + " mPendingRetry=" + mPendingRetry +
+    private void scheduleRetryPublish(long timeSpan) {
+        logger.print("timeSpan=" + timeSpan +
+                " mPendingRetry=" + mPendingRetry +
                 " mCancelRetry=" + mCancelRetry);
 
         // avoid duplicated retry.
@@ -618,7 +957,6 @@ public class PresencePublication extends PresenceBase {
         mCancelRetry = false;
 
         Intent intent = new Intent(ACTION_RETRY_PUBLISH_ALARM);
-        intent.putExtra("sipCode", sipCode);
         intent.setClass(mContext, AlarmBroadcastReceiver.class);
         mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
@@ -627,12 +965,11 @@ public class PresencePublication extends PresenceBase {
             mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         }
 
-        //retry per 2 minutes
         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                SystemClock.elapsedRealtime() + 120000, mRetryAlarmIntent);
+                SystemClock.elapsedRealtime() + timeSpan, mRetryAlarmIntent);
     }
 
-    public void retryPublish(int sipCode) {
+    public void retryPublish() {
         logger.print("mCancelRetry=" + mCancelRetry);
         mPendingRetry = false;
 
@@ -649,6 +986,10 @@ public class PresencePublication extends PresenceBase {
         logger.print( "Publish response code = " + pSipResponse.getSipResponseCode()
                 +"Publish response reason phrase = " + pSipResponse.getReasonPhrase());
 
+        synchronized(mSyncObj) {
+            mPublishedRequest = mPublishingRequest;
+            mPublishingRequest = null;
+        }
         if(pSipResponse == null){
             logger.debug("handlePublishResponse pSipResponse = null");
             return;
@@ -695,8 +1036,13 @@ public class PresencePublication extends PresenceBase {
             default: // Generic Failure
                 if ((sipCode < 100) || (sipCode > 699)) {
                     logger.debug("Ignore internal response code, sipCode=" + sipCode);
+                    // internal error
+                    //  0- PERMANENT ERROR: UI should not retry immediately
+                    //  888- TEMP ERROR:  UI Can retry immediately
+                    //  999- NO CHANGE: No Publish needs to be sent
                     if(sipCode == 888) {
-                        scheduleRetryPublish(sipCode);
+                        // retry per 2 minutes
+                        scheduleRetryPublish(120000);
                     } else {
                         logger.debug("Ignore internal response code, sipCode=" + sipCode);
                     }
@@ -751,4 +1097,131 @@ public class PresencePublication extends PresenceBase {
     protected void finalize() throws Throwable {
         finish();
     }
+
+    private PendingIntent createIncomingCallPendingIntent() {
+        Intent intent = new Intent(ACTION_IMS_FEATURE_AVAILABLE);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        return PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    public void onFeatureCapabilityChanged(final int serviceClass,
+            final int[] enabledFeatures, final int[] disabledFeatures) {
+        logger.debug("onFeatureCapabilityChanged serviceClass="+serviceClass
+                +", enabledFeatures="+Arrays.toString(enabledFeatures)
+                +", disabledFeatures="+Arrays.toString(disabledFeatures));
+
+        Thread thread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                onFeatureCapabilityChangedInternal(serviceClass,
+                        enabledFeatures, disabledFeatures);
+            }
+        }, "onFeatureCapabilityChangedInternal thread");
+
+        thread.start();
+    }
+
+    synchronized private void onFeatureCapabilityChangedInternal(int serviceClass,
+            int[] enabledFeatures, int[] disabledFeatures) {
+        if (serviceClass == ImsServiceClass.MMTEL) {
+            boolean oldIsVolteAvailable = mIsVolteAvailable;
+            boolean oldIsVtAvailable = mIsVtAvailable;
+            boolean oldIsVoWifiAvailable = mIsVoWifiAvailable;
+            boolean oldIsViWifiAvailable = mIsViWifiAvailable;
+
+            if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
+                mIsVolteAvailable = true;
+            } else if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                mIsVolteAvailable = false;
+            } else {
+                logger.print("invalid value for FEATURE_TYPE_VOICE_OVER_LTE");
+            }
+
+            if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI) {
+                mIsVoWifiAvailable = true;
+            } else if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                mIsVoWifiAvailable = false;
+            } else {
+                logger.print("invalid value for FEATURE_TYPE_VOICE_OVER_WIFI");
+            }
+
+            if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
+                mIsVtAvailable = true;
+            } else if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                mIsVtAvailable = false;
+            } else {
+                logger.print("invalid value for FEATURE_TYPE_VIDEO_OVER_LTE");
+            }
+
+            if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI) {
+                mIsViWifiAvailable = true;
+            } else if(enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI] ==
+                    ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                mIsViWifiAvailable = false;
+            } else {
+                logger.print("invalid value for FEATURE_TYPE_VIDEO_OVER_WIFI");
+            }
+
+            logger.print("mIsVolteAvailable=" + mIsVolteAvailable +
+                    " mIsVoWifiAvailable=" + mIsVoWifiAvailable +
+                    " mIsVtAvailable=" + mIsVtAvailable +
+                    " mIsViWifiAvailable=" + mIsViWifiAvailable +
+                    " oldIsVolteAvailable=" + oldIsVolteAvailable +
+                    " oldIsVoWifiAvailable=" + oldIsVoWifiAvailable +
+                    " oldIsVtAvailable=" + oldIsVtAvailable +
+                    " oldIsViWifiAvailable=" + oldIsViWifiAvailable);
+
+            if(oldIsVolteAvailable != mIsVolteAvailable ||
+                    oldIsVtAvailable != mIsVtAvailable ||
+                    oldIsVoWifiAvailable != mIsVoWifiAvailable ||
+                    oldIsViWifiAvailable != mIsViWifiAvailable) {
+                if(mGotTriggerFromStack) {
+                    if((Settings.Global.getInt(mContext.getContentResolver(),
+                            Settings.Global.AIRPLANE_MODE_ON, 0) != 0) && !mIsVoWifiAvailable &&
+                            !mIsViWifiAvailable) {
+                        logger.print("Airplane mode was on and no vowifi and viwifi." +
+                            " Don't need publish. Stack will unpublish");
+                        return;
+                    }
+
+                    if(isOnIWLAN()) {
+                        // will check duplicated PUBLISH in requestPublication by invokePublish
+                        invokePublish(PresencePublication.PublishType.
+                                PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED);
+                    }
+                } else {
+                    mHasCachedTrigger = true;
+                }
+            }
+        }
+    }
+
+    private boolean isOnLTE() {
+        TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+            int networkType = teleMgr.getDataNetworkType();
+            logger.debug("mMovedToLTE=" + mMovedToLTE + " networkType=" + networkType);
+
+            // Had reported LTE by trigger and still have DATA.
+            return mMovedToLTE && (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
+    private boolean isOnIWLAN() {
+        TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+            int networkType = teleMgr.getDataNetworkType();
+            logger.debug("mMovedToIWLAN=" + mMovedToIWLAN + " networkType=" + networkType);
+
+            // Had reported IWLAN by trigger and still have DATA.
+            return mMovedToIWLAN && (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
 }
index 02a9bd7..7a74492 100644 (file)
@@ -275,150 +275,156 @@ public class ImsConfig {
          */
         public static final int VOICE_OVER_WIFI_MODE = 27;
         /**
+         * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+        /**
          * Mobile data enabled.
          * Value is in Integer format. On (1), OFF(0).
          */
-        public static final int MOBILE_DATA_ENABLED = 28;
+        public static final int MOBILE_DATA_ENABLED = 29;
         /**
          * VoLTE user opted in status.
          * Value is in Integer format. Opted-in (1) Opted-out (0).
          */
-        public static final int VOLTE_USER_OPT_IN_STATUS = 29;
+        public static final int VOLTE_USER_OPT_IN_STATUS = 30;
         /**
          * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
          * Value is in String format.
          */
-        public static final int LBO_PCSCF_ADDRESS = 30;
+        public static final int LBO_PCSCF_ADDRESS = 31;
         /**
          * Keep Alive Enabled for SIP.
          * Value is in Integer format. On(1), OFF(0).
          */
-        public static final int KEEP_ALIVE_ENABLED = 31;
+        public static final int KEEP_ALIVE_ENABLED = 32;
         /**
          * Registration retry Base Time value in seconds.
          * Value is in Integer format.
          */
-        public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 32;
+        public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
         /**
          * Registration retry Max Time value in seconds.
          * Value is in Integer format.
          */
-        public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 33;
+        public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
         /**
          * Smallest RTP port for speech codec.
          * Value is in integer format.
          */
-        public static final int SPEECH_START_PORT = 34;
+        public static final int SPEECH_START_PORT = 35;
         /**
          * Largest RTP port for speech code.
          * Value is in Integer format.
          */
-        public static final int SPEECH_END_PORT = 35;
+        public static final int SPEECH_END_PORT = 36;
         /**
          * SIP Timer A's value in milliseconds. Timer A is the INVITE request
          * retransmit interval, for UDP only.
          * Value is in Integer format.
          */
-        public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 36;
+        public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
         /**
          * SIP Timer B's value in milliseconds. Timer B is the wait time for
          * INVITE message to be acknowledged.
          * Value is in Integer format.
          */
-        public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 37;
+        public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
         /**
          * SIP Timer D's value in milliseconds. Timer D is the wait time for
          * response retransmits of the invite client transactions.
          * Value is in Integer format.
          */
-        public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 38;
+        public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
         /**
          * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
          * request retransmit interval, for UDP only.
          * Value is in Integer format.
          */
-        public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 39;
+        public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
         /**
          * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
          * timeout timer.
          * Value is in Integer format.
          */
-        public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 40;
+        public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
         /**
          * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
          * retransmit interval.
          * Value is in Integer format.
          */
-        public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 41;
+        public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
         /**
          * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
          * ACK receipt.
          * Value is in Integer format.
          */
-        public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 42;
+        public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
         /**
          * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
          * ACK retransmits.
          * Value is in Integer format.
          */
-        public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 43;
+        public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
         /**
          * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
          * non-invite request retransmission.
          * Value is in Integer format.
          */
-        public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 44;
+        public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
         /**
          * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
          * non-invite response retransmits.
          * Value is in Integer format.
          */
-        public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 45;
+        public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
         /**
          * AMR WB octet aligned dynamic payload type.
          * Value is in Integer format.
          */
-        public static final int AMR_WB_OCTET_ALIGNED_PT = 46;
+        public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
         /**
          * AMR WB bandwidth efficient payload type.
          * Value is in Integer format.
          */
-        public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 47;
+        public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
         /**
          * AMR octet aligned dynamic payload type.
          * Value is in Integer format.
          */
-        public static final int AMR_OCTET_ALIGNED_PT = 48;
+        public static final int AMR_OCTET_ALIGNED_PT = 49;
         /**
          * AMR bandwidth efficient payload type.
          * Value is in Integer format.
          */
-        public static final int AMR_BANDWIDTH_EFFICIENT_PT = 49;
+        public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
         /**
          * DTMF WB payload type.
          * Value is in Integer format.
          */
-        public static final int DTMF_WB_PT = 50;
+        public static final int DTMF_WB_PT = 51;
         /**
          * DTMF NB payload type.
          * Value is in Integer format.
          */
-        public static final int DTMF_NB_PT = 51;
+        public static final int DTMF_NB_PT = 52;
         /**
          * AMR Default encoding mode.
          * Value is in Integer format.
          */
-        public static final int AMR_DEFAULT_MODE = 52;
+        public static final int AMR_DEFAULT_MODE = 53;
         /**
          * SMS Public Service Identity.
          * Value is in String format.
          */
-        public static final int SMS_PSI = 53;
+        public static final int SMS_PSI = 54;
         /**
          * Video Quality - VideoQualityFeatureValuesConstants.
          * Value is in Integer format.
          */
-        public static final int VIDEO_QUALITY = 54;
+        public static final int VIDEO_QUALITY = 55;
+
 
         // Expand the operator config items as needed here, need to change
         // PROVISIONED_CONFIG_END after that.