Merge "Update Provisioning API name and doc per API review"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 488326c..a6c1ec5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -100,6 +100,7 @@
 
     <protected-broadcast android:name= "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED" />
     <protected-broadcast android:name= "android.intent.action.ACTION_MANAGED_ROAMING_IND" />
+    <protected-broadcast android:name= "android.intent.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE" />
 
     <!-- Allows granting runtime permissions to telephony related components. -->
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9ab728f..05ed622 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -159,7 +159,6 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.RIL;
-import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
@@ -236,10 +235,8 @@
     private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
     private static final int CMD_RESET_MODEM_CONFIG = 19;
     private static final int EVENT_RESET_MODEM_CONFIG_DONE = 20;
-    private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
-    private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
-    private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
-    private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
+    private static final int CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK = 21;
+    private static final int EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE = 22;
     private static final int CMD_SEND_ENVELOPE = 25;
     private static final int EVENT_SEND_ENVELOPE_DONE = 26;
     private static final int CMD_INVOKE_OEM_RIL_REQUEST_RAW = 27;
@@ -322,6 +319,8 @@
     private static final int EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE = 104;
     private static final int CMD_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST = 105;
     private static final int EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE = 106;
+    private static final int CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON = 107;
+    private static final int EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE = 108;
     private static final int CMD_PREPARE_UNATTENDED_REBOOT = 109;
 
     // Parameters of select command.
@@ -873,13 +872,14 @@
                     break;
                 }
 
-                case CMD_GET_PREFERRED_NETWORK_TYPE:
+                case CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK:
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
-                    getPhoneFromRequest(request).getPreferredNetworkType(onCompleted);
+                    onCompleted = obtainMessage(EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE,
+                            request);
+                    getPhoneFromRequest(request).getAllowedNetworkTypesBitmask(onCompleted);
                     break;
 
-                case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
+                case EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     if (ar.exception == null && ar.result != null) {
@@ -889,26 +889,31 @@
                         // for the calling thread to unblock
                         request.result = new int[]{-1};
                         if (ar.result == null) {
-                            loge("getPreferredNetworkType: Empty response");
+                            loge("getAllowedNetworkTypesBitmask: Empty response");
                         } else if (ar.exception instanceof CommandException) {
-                            loge("getPreferredNetworkType: CommandException: " +
-                                    ar.exception);
+                            loge("getAllowedNetworkTypesBitmask: CommandException: "
+                                    + ar.exception);
                         } else {
-                            loge("getPreferredNetworkType: Unknown exception");
+                            loge("getAllowedNetworkTypesBitmask: Unknown exception");
                         }
                     }
                     notifyRequester(request);
                     break;
 
-                case CMD_SET_PREFERRED_NETWORK_TYPE:
+                case CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON:
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
-                    int networkType = (Integer) request.argument;
-                    getPhoneFromRequest(request).setPreferredNetworkType(networkType, onCompleted);
+                    onCompleted = obtainMessage(EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE,
+                            request);
+                    Pair<Integer, Long> reasonWithNetworkTypes =
+                            (Pair<Integer, Long>) request.argument;
+                    getPhoneFromRequest(request).setAllowedNetworkTypes(
+                            reasonWithNetworkTypes.first,
+                            reasonWithNetworkTypes.second,
+                            onCompleted);
                     break;
 
-                case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
-                    handleNullReturnEvent(msg, "setPreferredNetworkType");
+                case EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE:
+                    handleNullReturnEvent(msg, "setAllowedNetworkTypesForReason");
                     break;
 
                 case CMD_INVOKE_OEM_RIL_REQUEST_RAW:
@@ -6010,123 +6015,29 @@
     }
 
     /**
-     * Get the calculated preferred network type.
-     * Used for debugging incorrect network type.
+     * Get the allowed network types bitmask.
      *
-     * @return the preferred network type, defined in RILConstants.java.
+     * @return the allowed network types bitmask, defined in RILConstants.java.
      */
     @Override
-    public int getCalculatedPreferredNetworkType(String callingPackage, String callingFeatureId) {
-        final Phone defaultPhone = getDefaultPhone();
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, defaultPhone.getSubId(),
-                callingPackage, callingFeatureId, "getCalculatedPreferredNetworkType")) {
-            return RILConstants.PREFERRED_NETWORK_MODE;
-        }
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // FIXME: need to get SubId from somewhere.
-            return PhoneFactory.calculatePreferredNetworkType(defaultPhone.getContext(), 0);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Get the preferred network type.
-     * Used for device configuration by some CDMA operators.
-     *
-     * @return the preferred network type, defined in RILConstants.java.
-     */
-    @Override
-    public int getPreferredNetworkType(int subId) {
+    public int getAllowedNetworkTypesBitmask(int subId) {
         TelephonyPermissions
                 .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "getPreferredNetworkType");
+                        mApp, subId, "getAllowedNetworkTypesBitmask");
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (DBG) log("getPreferredNetworkType");
-            int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null, subId);
-            int networkType = (result != null ? result[0] : -1);
-            if (DBG) log("getPreferredNetworkType: " + networkType);
-            return networkType;
+            if (DBG) log("getAllowedNetworkTypesBitmask");
+            int[] result = (int[]) sendRequest(CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK, null, subId);
+            int networkTypesBitmask = (result != null ? result[0] : -1);
+            if (DBG) log("getAllowedNetworkTypesBitmask: " + networkTypesBitmask);
+            return networkTypesBitmask;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     /**
-     * Set the preferred network type.
-     *
-     * @param networkType the preferred network type, defined in RILConstants.java.
-     * @return true on success; false on any failure.
-     */
-    @Override
-    public boolean setPreferredNetworkType(int subId, int networkType) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                mApp, subId, "setPreferredNetworkType");
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            Boolean success = (Boolean) sendRequest(
-                    CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);
-
-            if (success) {
-                Settings.Global.putInt(mApp.getContentResolver(),
-                        Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);
-            }
-            if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
-            return success;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Get the allowed network types that store in the telephony provider.
-     *
-     * @param subId the id of the subscription.
-     * @return allowedNetworkTypes the allowed network types.
-     */
-    @Override
-    public long getAllowedNetworkTypes(int subId) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                    mApp, subId, "getAllowedNetworkTypes");
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return SubscriptionManager.getLongSubscriptionProperty(
-                    subId, SubscriptionManager.ALLOWED_NETWORK_TYPES, -1, mApp);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Set the allowed network types.
-     *
-     * @param subId the id of the subscription.
-     * @param allowedNetworkTypes the allowed network types.
-     * @return true on success; false on any failure.
-     */
-    @Override
-    public boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                mApp, subId, "setAllowedNetworkTypes");
-
-        SubscriptionManager.setSubscriptionProperty(subId,
-                SubscriptionManager.ALLOWED_NETWORK_TYPES,
-                String.valueOf(allowedNetworkTypes));
-
-        int preferredNetworkMode = Settings.Global.getInt(mApp.getContentResolver(),
-                Settings.Global.PREFERRED_NETWORK_MODE + subId,
-                RILConstants.PREFERRED_NETWORK_MODE);
-        return setPreferredNetworkType(subId, preferredNetworkMode);
-    }
-
-    /**
      * Get the allowed network types for certain reason.
      *
      * @param subId the id of the subscription.
@@ -6222,27 +6133,6 @@
     }
 
     /**
-     * Get the effective allowed network types on the device.
-     * This API will return an intersection of allowed network types for all reasons,
-     * including the configuration done through setAllowedNetworkTypes
-     *
-     * @param subId the id of the subscription.
-     * @return the allowed network types
-     */
-    @Override
-    public long getEffectiveAllowedNetworkTypes(int subId) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "getEffectiveAllowedNetworkTypes");
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return getPhoneFromSubId(subId).getEffectiveAllowedNetworkTypes();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
      * Set the allowed network types of the device and
      * provide the reason triggering the allowed network change.
      *
@@ -6253,16 +6143,27 @@
      */
     @Override
     public boolean setAllowedNetworkTypesForReason(int subId,
-            @TelephonyManager.AllowedNetworkTypesReason int reason, long allowedNetworkTypes) {
+            @TelephonyManager.AllowedNetworkTypesReason int reason,
+            @TelephonyManager.NetworkTypeBitMask long allowedNetworkTypes) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setAllowedNetworkTypesForReason");
+        if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
+            Rlog.e(LOG_TAG, "Invalid allowed network type reason: " + reason);
+            return false;
+        }
+
+        if (DBG) {
+            log("setAllowedNetworkTypesForReason: " + reason
+                    + " value: " + allowedNetworkTypes);
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            getPhoneFromSubId(subId).setAllowedNetworkTypes(reason, allowedNetworkTypes);
-            int preferredNetworkMode = Settings.Global.getInt(mApp.getContentResolver(),
-                    Settings.Global.PREFERRED_NETWORK_MODE + subId,
-                    RILConstants.PREFERRED_NETWORK_MODE);
-            return setPreferredNetworkType(subId, preferredNetworkMode);
+            Boolean success = (Boolean) sendRequest(
+                    CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON,
+                    new Pair<Integer, Long>(reason, allowedNetworkTypes), subId);
+
+            if (DBG) log("setAllowedNetworkTypesForReason: " + (success ? "ok" : "fail"));
+            return success;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -7012,15 +6913,6 @@
     }
 
     @Override
-    public void setRadioCapability(RadioAccessFamily[] rafs) {
-        try {
-            ProxyController.getInstance().setRadioCapability(rafs);
-        } catch (RuntimeException e) {
-            Log.w(LOG_TAG, "setRadioCapability: Runtime Exception");
-        }
-    }
-
-    @Override
     public int getRadioAccessFamily(int phoneId, String callingPackage) {
         Phone phone = PhoneFactory.getPhone(phoneId);
         try {
@@ -7339,7 +7231,15 @@
                 setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_USER,
                         getDefaultDataEnabled());
                 setNetworkSelectionModeAutomatic(subId);
-                setPreferredNetworkType(subId, getDefaultNetworkType(subId));
+                setAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
+                setAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
+                setAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
+                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
                 setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId));
                 CarrierInfoManager.deleteAllCarrierKeysForImsiEncryption(mApp);
             }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 8fc2286..b7a413c 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -17,7 +17,6 @@
 package com.android.phone.settings;
 
 import static android.net.ConnectivityManager.NetworkCallback;
-import static android.provider.Settings.Global.PREFERRED_NETWORK_MODE;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -40,7 +39,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityCdma;
@@ -61,6 +59,7 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
+import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
@@ -200,9 +199,6 @@
     }
 
     private static final int EVENT_CFI_CHANGED = 302;
-
-    private static final int EVENT_QUERY_PREFERRED_TYPE_DONE = 1000;
-    private static final int EVENT_SET_PREFERRED_TYPE_DONE = 1001;
     private static final int EVENT_QUERY_SMSC_DONE = 1005;
     private static final int EVENT_UPDATE_SMSC_DONE = 1006;
     private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 1007;
@@ -376,8 +372,7 @@
 
     private void updatePreferredNetworkType(int type) {
         if (type >= PREFERRED_NETWORK_LABELS.length || type < 0) {
-            log("EVENT_QUERY_PREFERRED_TYPE_DONE: unknown "
-                    + "type=" + type);
+            log("Network type: unknown type value=" + type);
             type = PREFERRED_NETWORK_LABELS.length - 1; //set to Unknown
         }
         mPreferredNetworkTypeResult = type;
@@ -405,21 +400,6 @@
         public void handleMessage(Message msg) {
             AsyncResult ar;
             switch (msg.what) {
-                case EVENT_QUERY_PREFERRED_TYPE_DONE:
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception == null && ar.result != null) {
-                        updatePreferredNetworkType(((int []) ar.result)[0]);
-                    } else {
-                        //In case of an exception, we will set this to unknown
-                        updatePreferredNetworkType(PREFERRED_NETWORK_LABELS.length - 1);
-                    }
-                    break;
-                case EVENT_SET_PREFERRED_TYPE_DONE:
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        log("Set preferred network type failed.");
-                    }
-                    break;
                 case EVENT_QUERY_SMSC_DONE:
                     ar = (AsyncResult) msg.obj;
                     if (ar.exception != null) {
@@ -606,9 +586,11 @@
         mPreferredNetworkTypeResult = PREFERRED_NETWORK_LABELS.length - 1; //Unknown
         mSelectedPhoneIndex = 0; //phone 0
 
-        //FIXME: Replace with TelephonyManager call
-        mPhone.getPreferredNetworkType(
-                mHandler.obtainMessage(EVENT_QUERY_PREFERRED_TYPE_DONE));
+        new Thread(() -> {
+            int networkType = (int) mTelephonyManager.getPreferredNetworkTypeBitmask();
+            updatePreferredNetworkType(
+                    RadioAccessFamily.getNetworkTypeFromRaf(networkType));
+        }).start();
 
         restoreFromBundle(icicle);
     }
@@ -1659,21 +1641,11 @@
             if (mPreferredNetworkTypeResult != pos && pos >= 0
                     && pos <= PREFERRED_NETWORK_LABELS.length - 2) {
                 mPreferredNetworkTypeResult = pos;
-
-                // TODO: Possibly migrate this to TelephonyManager.setPreferredNetworkType()
-                // which today still has some issues (mostly that the "set" is conditional
-                // on a successful modem call, which is not what we want). Instead we always
-                // want this setting to be set, so that if the radio hiccups and this setting
-                // is for some reason unsuccessful, future calls to the radio will reflect
-                // the users's preference which is set here.
-                final int subId = mPhone.getSubId();
-                if (SubscriptionManager.isUsableSubIdValue(subId)) {
-                    Settings.Global.putInt(mPhone.getContext().getContentResolver(),
-                            PREFERRED_NETWORK_MODE + subId, mPreferredNetworkTypeResult);
-                }
-                log("Calling setPreferredNetworkType(" + mPreferredNetworkTypeResult + ")");
-                Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE);
-                mPhone.setPreferredNetworkType(mPreferredNetworkTypeResult, msg);
+                new Thread(() -> {
+                    mTelephonyManager.setAllowedNetworkTypesForReason(
+                            TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+                            RadioAccessFamily.getRafFromNetworkType(mPreferredNetworkTypeResult));
+                }).start();
             }
         }
 
diff --git a/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
index 5dbf6b0..df80e54 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
@@ -4,11 +4,19 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
+    <TextView
+        android:id="@+id/session_tips"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="15dp"
+        android:textStyle="bold" />
+
     <LinearLayout
         android:id="@id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="horizontal">
+        android:orientation="horizontal"
+        android:layout_below="@+id/session_tips">
 
         <TextView
             android:layout_width="wrap_content"
@@ -59,4 +67,4 @@
             android:text="@string/send" />
     </RelativeLayout>
 
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml b/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml
index 44f6d3c..eb4d1fa 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml
@@ -4,6 +4,13 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
+    <TextView
+        android:id="@+id/tips"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="15dp"
+        android:textStyle="bold" />
+
     <Button
         android:id="@+id/start_chat_btn"
         android:layout_width="match_parent"
@@ -19,4 +26,4 @@
         android:layout_height="match_parent"
         android:layout_alignParentBottom="true" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
index 305c88e..5cf2da2 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
@@ -44,18 +44,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
             android:text="@string/request_capability"
             android:textAllCaps="false" />
 
-        <TextView
-            android:id="@+id/capability_result"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/result"
-            android:textSize="15dp"
-            android:textStyle="bold" />
-
         <Button
             android:id="@+id/availability_btn"
             android:layout_width="match_parent"
@@ -66,11 +57,12 @@
             android:textAllCaps="false" />
 
         <TextView
-            android:id="@+id/availability_result"
+            android:id="@+id/capability_result"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/result"
+            android:scrollbars="vertical"
             android:textSize="15dp"
             android:textStyle="bold" />
     </LinearLayout>
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
index 3528add..a193b46 100644
--- a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -14,16 +14,18 @@
         multiple ones.</string>
     <string name="number">Number: </string>
     <string name="request_capability">requestCapability</string>
-    <string name="request_availability">requestNetworkAvailability</string>
+    <string name="request_availability">requestNetworkAvailability (1st number)</string>
     <string name="gba_bootstrap">bootstrapAuthenticationRequest</string>
     <string name="start_chat">Start Chat</string>
     <string name="to">To:</string>
     <string name="chat_message">Chat Message</string>
     <string name="send">Send</string>
     <string name="ok">OK</string>
-    <string name="session_succeeded">Session init succeeded</string>
-    <string name="session_failed">Session init failed</string>
-    <string name="session_not_ready">Session not ready</string>
+    <string name="session_initiating">Initializing chat session..</string>
+    <string name="session_timeout">Session initialization timeout</string>
+    <string name="session_succeeded">Session initialization succeeded</string>
+    <string name="session_failed">Session initialization failed</string>
+    <string name="session_broken_or_not_ready">Session broken or not ready</string>
     <string name="organization">Organization:</string>
     <string name="uicc_type">UICC Type:</string>
     <string name="protocol">Protocol:</string>
@@ -39,8 +41,12 @@
     <string name="chatbot_session">Chatbot Session</string>
     <string name="chatbot_standalone">Chatbot Standalone</string>
     <string name="chatbot_version">Chatbot Version</string>
-    <string name="provisioning_done">Provisioning Done</string>
-    <string name="registration_done">Registration Done</string>
+    <string name="start_provisioning">Start Provisioning....</string>
+    <string name="provisioning_timeout">Provisioning timeout.</string>
+    <string name="provisioning_done">Provisioning done, Start registering...</string>
+    <string name="registration_timeout">Registration timeout</string>
+    <string name="registration_done">Registration done. Enjoy chat!</string>
+    <string name="registration_failed">Registration failed</string>
     <string name="version_info">Version: %s</string>
 
     <string-array name="organization">
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
index 0531209..bb1283a 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -52,17 +52,19 @@
     public static final String TELURI_PREFIX = "tel:";
     private static final String TAG = "TestRcsApp.ChatActivity";
     private static final int INIT_LIST = 1;
-    private static final int SHOW_TOAST = 2;
+    private static final int SHOW_STATUS = 2;
     private static final float TEXT_SIZE = 20.0f;
     private static final int MARGIN_SIZE = 20;
+    private static final long TIMEOUT_IN_MS = 10000L;
     private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(3);
     private boolean mSessionInitResult = false;
     private Button mSend;
     private String mDestNumber;
-    private TextView mDestNumberView;
+    private TextView mDestNumberView, mTips;
     private EditText mNewMessage;
     private ChatObserver mChatObserver;
     private Handler mHandler;
+    private int mSubId;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -80,9 +82,8 @@
                     case INIT_LIST:
                         initChatMessageLayout((Cursor) msg.obj);
                         break;
-                    case SHOW_TOAST:
-                        Toast.makeText(ChatActivity.this, msg.obj.toString(),
-                                Toast.LENGTH_SHORT).show();
+                    case SHOW_STATUS:
+                        mTips.setText(msg.obj.toString());
                         break;
                     default:
                         Log.d(TAG, "unknown msg:" + msg.what);
@@ -92,6 +93,7 @@
             }
         };
         mDestNumberView = findViewById(R.id.destNum);
+        mTips = findViewById(R.id.session_tips);
         initDestNumber();
         mChatObserver = new ChatObserver(mHandler);
     }
@@ -115,9 +117,9 @@
         mNewMessage = findViewById(R.id.new_msg);
         mSend = findViewById(R.id.chat_btn);
 
-        int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            Log.e(TAG, "invalid subId:" + subId);
+        mSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            Log.e(TAG, "invalid subId:" + mSubId);
             return;
         }
         try {
@@ -127,42 +129,53 @@
                 mDestNumber = formattedNumber;
             }
             mDestNumberView.setText(mDestNumber);
-            ChatManager.getInstance(getApplicationContext(), subId).initChatSession(
+            mTips.setText(ChatActivity.this.getResources().getString(R.string.session_initiating));
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS,
+                    ChatActivity.this.getResources().getString(R.string.session_timeout)),
+                    TIMEOUT_IN_MS);
+            ChatManager.getInstance(getApplicationContext(), mSubId).initChatSession(
                     TELURI_PREFIX + mDestNumber, new SessionStateCallback() {
                         @Override
                         public void onSuccess() {
                             Log.i(TAG, "session init succeeded");
-                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_TOAST,
-                                    ChatActivity.this.getResources().getString(
-                                            R.string.session_succeeded)));
+                            String success = ChatActivity.this.getResources().getString(
+                                    R.string.session_succeeded);
+                            if (mHandler.hasMessages(SHOW_STATUS)) {
+                                mHandler.removeMessages(SHOW_STATUS);
+                            }
+                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, success));
                             mSessionInitResult = true;
                         }
 
                         @Override
                         public void onFailure() {
                             Log.i(TAG, "session init failed");
-                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_TOAST,
-                                    ChatActivity.this.getResources().getString(
-                                            R.string.session_failed)));
+                            String failure = ChatActivity.this.getResources().getString(
+                                    R.string.session_failed);
+                            if (mHandler.hasMessages(SHOW_STATUS)) {
+                                mHandler.removeMessages(SHOW_STATUS);
+                            }
+                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, failure));
                             mSessionInitResult = false;
                         }
                     });
 
             mSend.setOnClickListener(view -> {
-                if (!mSessionInitResult) {
+                if (!ChatManager.getInstance(getApplicationContext(), mSubId).isRegistered()
+                        || !mSessionInitResult) {
                     Toast.makeText(ChatActivity.this,
-                            getResources().getString(R.string.session_not_ready),
+                            getResources().getString(R.string.session_broken_or_not_ready),
                             Toast.LENGTH_SHORT).show();
-                    Log.i(TAG, "session not ready");
+                    Log.i(TAG, "session broken or not ready");
                     return;
                 }
                 mFixedThreadPool.execute(() -> {
                     if (TextUtils.isEmpty(mDestNumber)) {
                         Log.i(TAG, "Destination number is empty");
                     } else {
-                        ChatManager.getInstance(getApplicationContext(), subId).addNewMessage(
+                        ChatManager.getInstance(getApplicationContext(), mSubId).addNewMessage(
                                 mNewMessage.getText().toString(), ChatManager.SELF, mDestNumber);
-                        ChatManager.getInstance(getApplicationContext(), subId).sendMessage(
+                        ChatManager.getInstance(getApplicationContext(), mSubId).sendMessage(
                                 TELURI_PREFIX + mDestNumber, mNewMessage.getText().toString());
                     }
                 });
@@ -246,16 +259,8 @@
     protected void onDestroy() {
         super.onDestroy();
         Log.i(TAG, "onDestroy");
-    }
-
-    private void dispose() {
-        int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            Log.e(TAG, "invalid subId:" + subId);
-            return;
-        }
-        ChatManager chatManager = ChatManager.getInstance(this, subId);
-        chatManager.deregister();
+        ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(
+                TELURI_PREFIX + mDestNumber);
     }
 
     @Override
@@ -277,5 +282,4 @@
             queryChatData();
         }
     }
-
 }
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java
index 70715f0..b641606 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java
@@ -32,7 +32,6 @@
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
@@ -51,13 +50,17 @@
 
     private static final String TAG = "TestRcsApp.ContactListActivity";
     private static final int RENDER_LISTVIEW = 1;
-    private static final int SHOW_TOAST = 2;
+    private static final int SHOW_STATUS = 2;
+    private static final long TIMEOUT_IN_MS = 10000L;
     private final ExecutorService mSingleThread = Executors.newSingleThreadExecutor();
+    private TextView mTips;
     private Button mStartChatButton;
     private Handler mHandler;
     private SummaryObserver mSummaryObserver;
     private ArrayAdapter mAdapter;
     private ListView mListview;
+    private State mState;
+    private ArrayList<ContactAttributes> mContactList;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -68,22 +71,25 @@
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         getSupportActionBar().setDisplayShowHomeEnabled(true);
 
+        mContactList = new ArrayList<>();
+        mTips = findViewById(R.id.tips);
+        mListview = findViewById(R.id.listview);
         mStartChatButton = findViewById(R.id.start_chat_btn);
         mStartChatButton.setOnClickListener(view -> {
             Intent intent = new Intent(ContactListActivity.this, PhoneNumberActivity.class);
             ContactListActivity.this.startActivity(intent);
         });
+        setButtonClickable(false);
 
         mHandler = new Handler() {
             public void handleMessage(Message message) {
                 Log.i(TAG, "handleMessage:" + message.what);
                 switch (message.what) {
                     case RENDER_LISTVIEW:
-                        renderListView((ArrayList<ContactAttributes>) message.obj);
+                        renderListView();
                         break;
-                    case SHOW_TOAST:
-                        Toast.makeText(ContactListActivity.this, message.obj.toString(),
-                                Toast.LENGTH_SHORT).show();
+                    case SHOW_STATUS:
+                        mTips.setText(message.obj.toString());
                         break;
                     default:
                         Log.i(TAG, "unknown msg:" + message.what);
@@ -91,6 +97,7 @@
             }
         };
         initListView();
+        initSipDelegate();
         mSummaryObserver = new SummaryObserver(mHandler);
     }
 
@@ -98,7 +105,6 @@
     protected void onStart() {
         super.onStart();
         Log.i(TAG, "onStart");
-        initSipDelegate();
         querySummaryData();
         getContentResolver().registerContentObserver(ChatProvider.SUMMARY_URI, false,
                 mSummaryObserver);
@@ -130,7 +136,6 @@
 
     private void initListView() {
         Log.i(TAG, "initListView");
-        mListview = findViewById(R.id.listview);
 
         mAdapter = new ArrayAdapter<ContactAttributes>(this,
                 android.R.layout.simple_list_item_2,
@@ -159,28 +164,34 @@
                             ChatProvider.SummaryColumns.LATEST_MESSAGE,
                             ChatProvider.SummaryColumns.IS_READ}, null, null, null);
 
-            ArrayList<ContactAttributes> contactList = new ArrayList<>();
+            mContactList.clear();
             while (cursor.moveToNext()) {
                 String phoneNumber = getPhoneNumber(cursor);
                 String latestMessage = getLatestMessage(cursor);
                 boolean isRead = getIsRead(cursor);
-                contactList.add(new ContactAttributes(phoneNumber, latestMessage, isRead));
+                mContactList.add(new ContactAttributes(phoneNumber, latestMessage, isRead));
             }
-            mHandler.sendMessage(mHandler.obtainMessage(RENDER_LISTVIEW, contactList));
+            mHandler.sendMessage(mHandler.obtainMessage(RENDER_LISTVIEW));
             cursor.close();
         });
     }
 
-    private void renderListView(ArrayList<ContactAttributes> contactList) {
+    private void renderListView() {
         mAdapter.clear();
-        mAdapter.addAll(contactList);
-        mListview.setOnItemClickListener((parent, view, position, id) -> {
-            Intent intent = new Intent(ContactListActivity.this, ChatActivity.class);
-            intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER,
-                    contactList.get(position).phoneNumber);
-            ContactListActivity.this.startActivity(intent);
-        });
+        mAdapter.addAll(mContactList);
+    }
 
+    private void setListViewClickable(boolean clickable) {
+        if (clickable) {
+            mListview.setOnItemClickListener((parent, view, position, id) -> {
+                Intent intent = new Intent(ContactListActivity.this, ChatActivity.class);
+                intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER,
+                        mContactList.get(position).phoneNumber);
+                ContactListActivity.this.startActivity(intent);
+            });
+        } else {
+            mListview.setOnItemClickListener(null);
+        }
     }
 
     private void initSipDelegate() {
@@ -193,21 +204,56 @@
         ChatManager chatManager = ChatManager.getInstance(this, subId);
         chatManager.setRcsStateChangedCallback((oldState, newState) -> {
             //Show toast when provisioning or registration is done.
-            if (newState == State.REGISTERING) {
-                mHandler.sendMessage(mHandler.obtainMessage(SHOW_TOAST,
-                        ContactListActivity.this.getResources().getString(
-                                R.string.provisioning_done)));
-            } else if (newState == State.REGISTERED) {
-                mHandler.sendMessage(mHandler.obtainMessage(SHOW_TOAST,
-                        ContactListActivity.this.getResources().getString(
-                                R.string.registration_done)));
-            }
+            mState = newState;
+            String tips = "";
+            String timeoutTips = "";
+            switch (newState) {
+                case PROVISIONING:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.start_provisioning);
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    timeoutTips = ContactListActivity.this.getResources().getString(
+                            R.string.provisioning_timeout);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS, timeoutTips),
+                            TIMEOUT_IN_MS);
+                    break;
+                case REGISTERING:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.provisioning_done);
+                    if (mHandler.hasMessages(SHOW_STATUS)) {
+                        mHandler.removeMessages(SHOW_STATUS);
+                    }
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    timeoutTips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_timeout);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS, timeoutTips),
+                            TIMEOUT_IN_MS);
+                    break;
+                case REGISTERED:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_done);
+                    if (mHandler.hasMessages(SHOW_STATUS)) {
+                        mHandler.removeMessages(SHOW_STATUS);
+                    }
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    setButtonClickable(true);
+                    setListViewClickable(true);
+                    break;
+                case NOT_REGISTERED:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_failed);
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    setButtonClickable(false);
+                    setListViewClickable(false);
+                    break;
 
+                default:
+                    Log.i(TAG, "unknown state:" + newState);
+            }
         });
         chatManager.register();
     }
 
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         if (item.getItemId() == android.R.id.home) {
@@ -230,6 +276,16 @@
         return 1 == cursor.getInt(cursor.getColumnIndex(ChatProvider.SummaryColumns.IS_READ));
     }
 
+    private void setButtonClickable(boolean clickable) {
+        if (clickable) {
+            mStartChatButton.setAlpha(1);
+            mStartChatButton.setClickable(true);
+        } else {
+            mStartChatButton.setAlpha(.5f);
+            mStartChatButton.setClickable(false);
+        }
+    }
+
     class ContactAttributes {
         public String phoneNumber;
         public String message;
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
index 10f588c..bbecbc2 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
@@ -19,6 +19,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsRcsManager;
@@ -26,6 +27,7 @@
 import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RcsUceAdapter;
 import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
 import android.view.MenuItem;
 import android.widget.Button;
@@ -47,7 +49,6 @@
     private Button mCapabilityButton;
     private Button mAvailabilityButton;
     private TextView mCapabilityResult;
-    private TextView mAvailabilityResult;
     private EditText mNumbers;
     private int mDefaultSmsSubId;
     private ImsRcsManager mImsRcsManager;
@@ -63,15 +64,25 @@
         initLayout();
     }
 
-    private void initLayout() {
+    @Override
+    protected void onStart() {
+        super.onStart();
         mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        Log.i(TAG, "defaultSmsSubId:" + mDefaultSmsSubId);
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            mImsRcsManager = getImsRcsManager(mDefaultSmsSubId);
+            if (mImsRcsManager != null) {
+                initLayout();
+            }
+        }
+    }
 
+    private void initLayout() {
         mCapabilityButton = findViewById(R.id.capability_btn);
         mAvailabilityButton = findViewById(R.id.availability_btn);
         mCapabilityResult = findViewById(R.id.capability_result);
-        mAvailabilityResult = findViewById(R.id.capability_result);
+        mCapabilityResult.setMovementMethod(new ScrollingMovementMethod());
 
-        mImsRcsManager = getImsRcsManager(mDefaultSmsSubId);
         mCapabilityButton.setOnClickListener(view -> {
             List<Uri> contactList = getContectList();
             if (contactList.size() == 0) {
@@ -84,13 +95,13 @@
                         new RcsUceAdapter.CapabilitiesCallback() {
                             public void onCapabilitiesReceived(
                                     List<RcsContactUceCapability> contactCapabilities) {
-                                Log.i(TAG, "onCapabilitiesReceived()");
                                 StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
                                 for (RcsContactUceCapability c : contactCapabilities) {
                                     b.append(getReadableCapability(c));
                                     b.append("\n");
                                 }
                                 mCapabilityResult.append(b.toString() + "\n");
+                                Log.i(TAG, b.toString());
                             }
 
                             public void onComplete() {
@@ -100,10 +111,11 @@
                             }
 
                             public void onError(int errorCode, long retryAfterMilliseconds) {
-                                Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
-                                        + retryAfterMilliseconds);
-                                mCapabilityResult.append("error - errorCode:" + errorCode
-                                        + " retryAfterMs:" + retryAfterMilliseconds);
+                                String result =
+                                        "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                                + retryAfterMilliseconds + "\n";
+                                Log.i(TAG, result);
+                                mCapabilityResult.append(result);
                             }
                         });
             } catch (ImsException e) {
@@ -117,36 +129,37 @@
                 Log.i(TAG, "empty contact list");
                 return;
             }
-            mAvailabilityResult.setText("pending...\n");
+            mCapabilityResult.setText("pending...\n");
             try {
                 mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),
                         getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {
                             public void onCapabilitiesReceived(
                                     List<RcsContactUceCapability> contactCapabilities) {
-                                Log.i(TAG, "onCapabilitiesReceived()");
                                 StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
                                 for (RcsContactUceCapability c : contactCapabilities) {
                                     b.append(getReadableCapability(c));
                                     b.append("\n");
                                 }
-                                mAvailabilityResult.append(b.toString() + "\n");
+                                mCapabilityResult.append(b.toString() + "\n");
+                                Log.i(TAG, b.toString());
                             }
 
                             public void onComplete() {
                                 Log.i(TAG, "onComplete()");
-                                mAvailabilityResult.append("complete");
+                                mCapabilityResult.append("complete");
 
                             }
 
                             public void onError(int errorCode, long retryAfterMilliseconds) {
-                                Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
-                                        + retryAfterMilliseconds);
-                                mAvailabilityResult.append("error - errorCode:" + errorCode
-                                        + " retryAfterMs:" + retryAfterMilliseconds);
+                                String result =
+                                        "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                                + retryAfterMilliseconds + "\n";
+                                Log.i(TAG, result);
+                                mCapabilityResult.append(result);
                             }
                         });
             } catch (ImsException e) {
-                mAvailabilityResult.setText("ImsException:" + e);
+                mCapabilityResult.setText("ImsException:" + e);
             }
         });
     }
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
index 0447d1a..4489349 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
@@ -79,8 +79,8 @@
         mSimpleRcsClient = SimpleRcsClient.newBuilder()
                 .registrationController(mRegistrationController)
                 .provisioningController(mProvisioningController)
-                .imsService(mImsService)
-                .executor(mFixedThreadPool).build();
+                .imsService(mImsService).build();
+
         mState = State.NEW;
         // register callback for state change
         mSimpleRcsClient.onStateChanged((oldState, newState) -> {
@@ -183,6 +183,8 @@
         URI uri = createUri(telUriContact);
         if (mContactSessionMap.containsKey(uri)) {
             callback.onSuccess();
+            Log.i(TAG, "uri exists");
+            return;
         }
         Futures.addCallback(
                 mImsService.startOriginatingChatSession(telUriContact),
@@ -223,10 +225,6 @@
      * @param message chat message.
      */
     public void sendMessage(String telUriContact, String message) {
-        if (mState != State.REGISTERED) {
-            Log.i(TAG, "Could not send msg due to State = " + mState);
-            return;
-        }
         SimpleChatSession chatSession = mContactSessionMap.get(createUri(telUriContact));
         if (chatSession == null) {
             Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
@@ -235,6 +233,26 @@
         chatSession.sendMessage(message);
     }
 
+    public boolean isRegistered() {
+        return (mState == State.REGISTERED);
+    }
+
+    /**
+     * Terminate the chat session.
+     * @param telUriContact destination tel Uri
+     */
+    public void terminateSession(String telUriContact) {
+        Log.i(TAG, "terminateSession");
+        URI uri = createUri(telUriContact);
+        SimpleChatSession chatSession = mContactSessionMap.get(uri);
+        if (chatSession == null) {
+            Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
+            return;
+        }
+        chatSession.terminate();
+        mContactSessionMap.remove(uri);
+    }
+
     /**
      * Insert chat information into database.
      * @param message chat message.
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java
index c299cc9..0469bc0 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java
@@ -24,14 +24,10 @@
 
 import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
 import com.android.libraries.rcs.simpleclient.provisioning.ProvisioningController;
-import com.android.libraries.rcs.simpleclient.provisioning.StaticConfigProvisioningController;
 import com.android.libraries.rcs.simpleclient.registration.RegistrationController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationStateChangeCallback;
 import com.android.libraries.rcs.simpleclient.service.ImsService;
 
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-
-import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -46,7 +42,6 @@
     private ProvisioningController provisioningController;
     private RegistrationController registrationController;
     private ImsService imsService;
-    private Executor executor;
     private SimpleRcsClientContext context;
     private StateChangedCallback stateChangedCallback;
 
@@ -108,23 +103,31 @@
             return;
         }
 
-        Futures.addCallback(registrationController.register(imsService),
-                new FutureCallback<SipSession>() {
+        registrationController.register(imsService,
+                new RegistrationStateChangeCallback() {
                     @Override
-                    public void onSuccess(SipSession result) {
-                        Log.i(TAG, "onSuccess:" + result);
-                        registered(result);
+                    public void notifyRegStateChanged(ImsService imsService) {
+
                     }
 
                     @Override
-                    public void onFailure(Throwable t) {
-                        Log.i(TAG, "onFailure:" + t);
+                    public void onSuccess(SipSession sipSession) {
+                        Log.i(TAG, "onSuccess");
+                        registered(sipSession);
                     }
-                }, executor);
+
+                    @Override
+                    public void onFailure(String reason) {
+                        Log.i(TAG, "onFailure reason:" + reason);
+                        notRegistered();
+                    }
+                });
     }
 
     private void registered(SipSession session) {
-        enterState(State.REGISTERING, State.REGISTERED);
+        if (state.get().equals(State.REGISTERING) || state.get().equals(State.NOT_REGISTERED)) {
+            enterState(state.get(), State.REGISTERED);
+        }
 
         context = new SimpleRcsClientContext(provisioningController, registrationController,
                 imsService,
@@ -133,6 +136,10 @@
         imsService.start(context);
     }
 
+    private void notRegistered() {
+        enterState(State.REGISTERED, State.NOT_REGISTERED);
+    }
+
     /**
      * Possible client states.
      */
@@ -141,6 +148,7 @@
         PROVISIONING,
         REGISTERING,
         REGISTERED,
+        NOT_REGISTERED,
     }
 
     /**
@@ -151,7 +159,6 @@
         private ProvisioningController provisioningController;
         private RegistrationController registrationController;
         private ImsService imsService;
-        private Executor executor;
 
         public Builder provisioningController(ProvisioningController controller) {
             this.provisioningController = controller;
@@ -168,17 +175,11 @@
             return this;
         }
 
-        public Builder executor(Executor executor) {
-            this.executor = executor;
-            return this;
-        }
-
         public SimpleRcsClient build() {
             SimpleRcsClient client = new SimpleRcsClient();
             client.registrationController = registrationController;
             client.provisioningController = provisioningController;
             client.imsService = imsService;
-            client.executor = executor;
 
             return client;
         }
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java
index 64d93b2..8bbe327 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java
@@ -16,21 +16,18 @@
 
 package com.android.libraries.rcs.simpleclient.registration;
 
-import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
 import com.android.libraries.rcs.simpleclient.service.ImsService;
 
-import com.google.common.util.concurrent.ListenableFuture;
-
 /**
  * Access to registration functionality.
  */
 public interface RegistrationController {
 
     /**
-     * Registers the given ImsService with the backend and returns a SipSession for sending and
-     * receiving SIP messages.
+     * Register the given ImsService with the backend and use the callback to return a SipSession
+     * for sending and receiving SIP messages.
      */
-    ListenableFuture<SipSession> register(ImsService imsService);
+    void register(ImsService imsService, RegistrationStateChangeCallback callback);
 
     void deregister();
 
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
index f1868fc..9ababc3 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
@@ -68,6 +68,7 @@
     private final int subscriptionId;
     private SipDelegateManager sipDelegateManager;
     private RegistrationContext context;
+    private RegistrationStateChangeCallback callback;
 
     public RegistrationControllerImpl(int subscriptionId, Executor executor,
             ImsManager imsManager) {
@@ -77,11 +78,11 @@
     }
 
     @Override
-    public ListenableFuture<SipSession> register(ImsService imsService) {
+    public void register(ImsService imsService, RegistrationStateChangeCallback callback) {
         Log.i(TAG, "register");
+        this.callback = callback;
         context = new RegistrationContext(this, imsService);
         context.register();
-        return context.getFuture();
     }
 
     @Override
@@ -101,7 +102,7 @@
     /**
      * Envelopes the registration data for a single ImsService instance.
      */
-    private static class RegistrationContext implements SipSession, SipSessionConfiguration {
+    private class RegistrationContext implements SipSession, SipSessionConfiguration {
 
         private final RegistrationControllerImpl controller;
         private final ImsService imsService;
@@ -139,12 +140,17 @@
                                 .getRegisteredFeatureTags()
                                 .containsAll(imsService.getFeatureTags())) {
                             // registered;
-                            sessionFuture.set(RegistrationContext.this);
+                            callback.onSuccess(RegistrationContext.this);
+                        } else {
+                            callback.onFailure("feature tag not registered");
                         }
                     }
 
                     @Override
                     public void onDestroyed(int reason) {
+                        Log.d(TAG, "onDestroyed:" + reason);
+                        callback.onFailure("delegate destroyed");
+
                     }
                 };
         private SipSessionListener sipSessionListener;
@@ -167,10 +173,13 @@
 
                     @Override
                     public void onMessageSendFailure(@NonNull String viaTransactionId, int reason) {
+                        Log.i(TAG, "onMessageSendFailure: viaTransactionId:"
+                                + viaTransactionId + ", reason:" + reason);
                     }
 
                     @Override
                     public void onMessageSent(@NonNull String viaTransactionId) {
+                        Log.i(TAG, "onMessageSent: viaTransactionId:" + viaTransactionId);
                     }
 
                 };
@@ -435,7 +444,7 @@
          * for now.
          * @return A SipMessage with the corrected header section.
          */
-        private static SipMessage repairHeaderSection(SipMessage message) {
+        private SipMessage repairHeaderSection(SipMessage message) {
             String headers = message.getHeaderSection();
 
             if (headers.startsWith("ia:")) {
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java
index 4f36ce5..570b313 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java
@@ -16,6 +16,7 @@
 
 package com.android.libraries.rcs.simpleclient.registration;
 
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
 import com.android.libraries.rcs.simpleclient.service.ImsService;
 
 /**
@@ -30,4 +31,10 @@
      * @param imsService the newly registered service.
      */
     void notifyRegStateChanged(ImsService imsService);
+
+    /**callback for successful session creation */
+    void onSuccess(SipSession sipSession);
+
+    /**callback for failed session creation. */
+    void onFailure(String reason);
 }