[RCS] Refactor App

1. Adjust UCE Test UI
2. Terminate chat session as leaving ChatActivity
3. Show status at the top screen instead of Toast
4. Use normal callback instead of Future to track Delegate status

Bug: 181853449
Bug: 181209960
Test: manual
Change-Id: I52a0ef6d12da18f46eec587b058c5fef2f0e0351
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 9d27fbc..45cccb1 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) -> {
@@ -168,6 +168,8 @@
         URI uri = createUri(telUriContact);
         if (mContactSessionMap.containsKey(uri)) {
             callback.onSuccess();
+            Log.i(TAG, "uri exists");
+            return;
         }
         Futures.addCallback(
                 mImsService.startOriginatingChatSession(telUriContact),
@@ -208,10 +210,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);
@@ -220,6 +218,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);
 }