Merge "Remove dependencies on library (2/2)"
diff --git a/rcs/presencepolling/src/com/android/service/ims/presence/CapabilityPolling.java b/rcs/presencepolling/src/com/android/service/ims/presence/CapabilityPolling.java
index db09fc4..6dfa651 100644
--- a/rcs/presencepolling/src/com/android/service/ims/presence/CapabilityPolling.java
+++ b/rcs/presencepolling/src/com/android/service/ims/presence/CapabilityPolling.java
@@ -56,7 +56,6 @@
 
 import com.android.ims.RcsException;
 import com.android.ims.RcsManager;
-import com.android.ims.RcsManager.ResultCode;
 import com.android.ims.RcsPresence;
 import com.android.ims.RcsPresence.PublishState;
 import com.android.ims.RcsPresenceInfo;
diff --git a/rcs/presencepolling/src/com/android/service/ims/presence/PersistService.java b/rcs/presencepolling/src/com/android/service/ims/presence/PersistService.java
index bbd9cee..e606b56 100644
--- a/rcs/presencepolling/src/com/android/service/ims/presence/PersistService.java
+++ b/rcs/presencepolling/src/com/android/service/ims/presence/PersistService.java
@@ -43,7 +43,6 @@
 import android.text.TextUtils;
 
 import com.android.ims.internal.Logger;
-import com.android.ims.RcsManager.ResultCode;
 import com.android.ims.RcsPresence;
 import com.android.ims.RcsPresenceInfo;
 
diff --git a/rcs/presencepolling/src/com/android/service/ims/presence/PollingAction.java b/rcs/presencepolling/src/com/android/service/ims/presence/PollingAction.java
index fc297ae..ea38ed9 100644
--- a/rcs/presencepolling/src/com/android/service/ims/presence/PollingAction.java
+++ b/rcs/presencepolling/src/com/android/service/ims/presence/PollingAction.java
@@ -36,7 +36,7 @@
 import com.android.ims.RcsManager;
 import com.android.ims.RcsPresence;
 import com.android.ims.RcsException;
-import com.android.ims.RcsManager.ResultCode;
+import com.android.ims.ResultCode;
 import com.android.ims.internal.Logger;
 
 import java.util.ArrayList;
diff --git a/rcs/rcsmanager/Android.bp b/rcs/rcsmanager/Android.bp
index 663f42a..8a12412 100644
--- a/rcs/rcsmanager/Android.bp
+++ b/rcs/rcsmanager/Android.bp
@@ -33,6 +33,17 @@
         "src/java/com/android/ims/internal/IRcsService.aidl",
         "src/java/com/android/ims/internal/IRcsPresence.aidl",
         "src/java/com/android/ims/IRcsPresenceListener.aidl",
+        ":rcsmanager-utils"
+    ]
+}
+
+filegroup {
+    name: "rcsmanager-utils",
+    srcs: [
+        "src/java/com/android/ims/internal/ContactNumberUtils.java",
+        "src/java/com/android/ims/internal/Logger.java",
+        "src/java/com/android/ims/RcsPresenceInfo.java",
+        "src/java/com/android/ims/ResultCode.java",
     ],
-    libs: ["ims-common"],
+    path: "src/java",
 }
diff --git a/rcs/rcsmanager/LICENSE b/rcs/rcsmanager/LICENSE
index 2e7609b..bfb2ad6 100644
--- a/rcs/rcsmanager/LICENSE
+++ b/rcs/rcsmanager/LICENSE
@@ -1,3 +1,31 @@
+Copyright (c) 2019, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    - Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    - Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    - Neither the name of The Android Open Source Project nor the names of its contributors may
+      be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+----------------------------------------------------------------------------------------------------
+
 Copyright (c) 2015, Motorola Mobility LLC
 All rights reserved.
 
diff --git a/rcs/rcsmanager/src/java/com/android/ims/RcsException.java b/rcs/rcsmanager/src/java/com/android/ims/RcsException.java
index 0d5f36e..754876e 100644
--- a/rcs/rcsmanager/src/java/com/android/ims/RcsException.java
+++ b/rcs/rcsmanager/src/java/com/android/ims/RcsException.java
@@ -36,7 +36,7 @@
 public class RcsException extends Exception {
 
     /**
-     * Refer to {@link RcsManager.ResultCode}
+     * Refer to {@link ResultCode}
      */
     private int mCode;
 
@@ -56,7 +56,7 @@
     /**
      * Gets the detailed exception code when RcsException is throwed
      *
-     * @return the exception code in {@link RcsManager.ResultCode}
+     * @return the exception code in {@link ResultCode}
      */
     public int getCode() {
         return mCode;
diff --git a/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java b/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
index 644d567..4c29e8b 100644
--- a/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
+++ b/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
@@ -31,7 +31,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Rlog;
@@ -94,140 +93,7 @@
     public static final String ACTION_RCS_SERVICE_DIED =
             "com.android.ims.ACTION_RCS_SERVICE_DIED";
 
-    public static class ResultCode {
-        /**
-         * The code is used when the request is success.
-         */
-        public static final int SUCCESS =0;
-
-        /**
-         * Return this code if the service doesn't be enabled on the phone.
-         * As per the requirement the feature can be enabled/disabled by DM.
-         */
-        public static final int ERROR_SERVICE_NOT_ENABLED = -1;
-
-        /**
-         * Return this code if the service didn't publish yet.
-         */
-        public static final int ERROR_SERVICE_NOT_PUBLISHED = -2;
-
-        /**
-         * The service is not available, for example it is 1x only
-         */
-        public static final int ERROR_SERVICE_NOT_AVAILABLE = -3;
-
-        /**
-         *  SUBSCRIBE Error base
-         */
-        public static final int SUBSCRIBER_ERROR_CODE_START = ERROR_SERVICE_NOT_AVAILABLE;
-
-        /**
-         * Temporary error and need retry later.
-         * such as:
-         * 503 Service Unavailable
-         * Device shall retry with exponential back-off
-         *
-         * 408 Request Timeout
-         * Device shall retry with exponential back-off
-         *
-         * 423 Interval Too Short. Requested expiry interval too short and server rejects it
-         * Device shall re-attempt subscription after changing the expiration interval in
-         * the Expires header field to be equal to or greater than the expiration interval
-         * within the Min-Expires header field of the 423 response
-         */
-        public static final int SUBSCRIBE_TEMPORARY_ERROR = SUBSCRIBER_ERROR_CODE_START - 1;
-
-        /**
-         * receives 403 (reason="User Not Registered").
-         * Re-Register to IMS then retry the single resource subscription if capability polling.
-         * availability fetch: no retry.
-         */
-         public static final int SUBSCRIBE_NOT_REGISTERED = SUBSCRIBER_ERROR_CODE_START - 2;
-
-        /**
-         * Responding for 403 - not authorized (Requestor)
-         * No retry.
-         */
-        public static final int SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE =
-                SUBSCRIBER_ERROR_CODE_START - 3;
-
-        /**
-         * Responding for "403 Forbidden" or "403"
-         * Handle it as same as 404 Not found.
-         * No retry.
-         */
-        public static final int SUBSCRIBE_FORBIDDEN = SUBSCRIBER_ERROR_CODE_START - 4;
-
-        /**
-         * Responding for 404 (target number)
-         * No retry.
-         */
-        public static final int SUBSCRIBE_NOT_FOUND = SUBSCRIBER_ERROR_CODE_START - 5;
-
-        /**
-         *  Responding for 413 - Too Large. Top app need shrink the size
-         *  of request contact list and resend the request
-         */
-        public static final int SUBSCRIBE_TOO_LARGE = SUBSCRIBER_ERROR_CODE_START - 6;
-
-        /**
-         * All subscribe errors not covered by specific errors
-         * Other 4xx/5xx/6xx
-         *
-         * Device shall not retry
-         */
-        public static final int SUBSCRIBE_GENIRIC_FAILURE = SUBSCRIBER_ERROR_CODE_START - 7;
-
-        /**
-         * Invalid parameter - The caller should check the parameter.
-         */
-        public static final int SUBSCRIBE_INVALID_PARAM = SUBSCRIBER_ERROR_CODE_START - 8;
-
-        /**
-         * Fetch error - The RCS statck failed to fetch the presence information.
-         */
-        public static final int SUBSCRIBE_FETCH_ERROR = SUBSCRIBER_ERROR_CODE_START - 9;
-
-        /**
-         * Request timeout - The RCS statck returns timeout error.
-         */
-        public static final int SUBSCRIBE_REQUEST_TIMEOUT = SUBSCRIBER_ERROR_CODE_START - 10;
-
-        /**
-         * Insufficient memory - The RCS statck returns the insufficient memory error.
-         */
-        public static final int SUBSCRIBE_INSUFFICIENT_MEMORY = SUBSCRIBER_ERROR_CODE_START - 11;
-
-        /**
-         * Lost network error - The RCS statck returns the lost network error.
-         */
-        public static final int SUBSCRIBE_LOST_NETWORK = SUBSCRIBER_ERROR_CODE_START - 12;
-
-        /**
-         * Not supported error - The RCS statck returns the not supported error.
-         */
-        public static final int SUBSCRIBE_NOT_SUPPORTED = SUBSCRIBER_ERROR_CODE_START - 13;
-
-        /**
-         * Generic error - RCS Presence stack returns generic error
-         */
-        public static final int SUBSCRIBE_GENERIC = SUBSCRIBER_ERROR_CODE_START - 14;
-
-        /**
-         * There is a request for the same number in queue.
-         */
-        public static final int SUBSCRIBE_ALREADY_IN_QUEUE = SUBSCRIBER_ERROR_CODE_START - 16;
-
-        /**
-         * Request too frequently.
-         */
-        public static final int SUBSCRIBE_TOO_FREQUENTLY = SUBSCRIBER_ERROR_CODE_START - 17;
-
-        /**
-         *  The last Subscriber error code
-         */
-        public static final int SUBSCRIBER_ERROR_CODE_END = SUBSCRIBER_ERROR_CODE_START - 17;
-    };
+    ;
 
     private static final String TAG = "RcsManager";
     private static final boolean DBG = true;
diff --git a/rcs/rcsmanager/src/java/com/android/ims/RcsPresence.java b/rcs/rcsmanager/src/java/com/android/ims/RcsPresence.java
index c52e7e9..ba7a7d5 100644
--- a/rcs/rcsmanager/src/java/com/android/ims/RcsPresence.java
+++ b/rcs/rcsmanager/src/java/com/android/ims/RcsPresence.java
@@ -29,13 +29,10 @@
 package com.android.ims;
 
 import java.util.List;
-import java.util.ArrayList;
-import android.content.Intent;
 import android.os.RemoteException;
 import android.util.Log;
 
 import com.android.ims.internal.IRcsPresence;
-import com.android.ims.RcsManager.ResultCode;
 
 /**
  *
@@ -192,7 +189,7 @@
      * @return the request ID if it is >0. Or it is RcsManager.ResultCode for error.
      *
      * @see IRcsPresenceListener
-     * @see RcsManager.ResultCode
+     * @see ResultCode
      */
     public int requestCapability(List<String> contactsNumber,
             IRcsPresenceListener listener) throws RcsException {
@@ -237,7 +234,7 @@
      * @return the request ID if it is >0. Or it is RcsManager.ResultCode for error.
      *
      * @see IRcsPresenceListener
-     * @see RcsManager.ResultCode
+     * @see ResultCode
      * @see RcsPresence.ACTION_PRESENCE_CHANGED
      */
     public int requestAvailability(String contactNumber, IRcsPresenceListener listener)
@@ -264,7 +261,7 @@
      * But for this funcation it will always send the request to network.
      *
      * @see IRcsPresenceListener
-     * @see RcsManager.ResultCode
+     * @see ResultCode
      * @see RcsPresence.ACTION_PRESENCE_CHANGED
      * @see ResultCode.SUBSCRIBE_TOO_FREQUENTLY
      */
diff --git a/rcs/rcsmanager/src/java/com/android/ims/ResultCode.java b/rcs/rcsmanager/src/java/com/android/ims/ResultCode.java
new file mode 100644
index 0000000..2119d5c
--- /dev/null
+++ b/rcs/rcsmanager/src/java/com/android/ims/ResultCode.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     - Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     - Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     - Neither the name of The Android Open Source Project nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+package com.android.ims;
+
+public class ResultCode {
+    /**
+     * The code is used when the request is success.
+     */
+    public static final int SUCCESS =0;
+
+    /**
+     * Return this code if the service doesn't be enabled on the phone.
+     * As per the requirement the feature can be enabled/disabled by DM.
+     */
+    public static final int ERROR_SERVICE_NOT_ENABLED = -1;
+
+    /**
+     * Return this code if the service didn't publish yet.
+     */
+    public static final int ERROR_SERVICE_NOT_PUBLISHED = -2;
+
+    /**
+     * The service is not available, for example it is 1x only
+     */
+    public static final int ERROR_SERVICE_NOT_AVAILABLE = -3;
+
+    /**
+     *  SUBSCRIBE Error base
+     */
+    public static final int SUBSCRIBER_ERROR_CODE_START = ERROR_SERVICE_NOT_AVAILABLE;
+
+    /**
+     * Temporary error and need retry later.
+     * such as:
+     * 503 Service Unavailable
+     * Device shall retry with exponential back-off
+     *
+     * 408 Request Timeout
+     * Device shall retry with exponential back-off
+     *
+     * 423 Interval Too Short. Requested expiry interval too short and server rejects it
+     * Device shall re-attempt subscription after changing the expiration interval in
+     * the Expires header field to be equal to or greater than the expiration interval
+     * within the Min-Expires header field of the 423 response
+     */
+    public static final int SUBSCRIBE_TEMPORARY_ERROR = SUBSCRIBER_ERROR_CODE_START - 1;
+
+    /**
+     * receives 403 (reason="User Not Registered").
+     * Re-Register to IMS then retry the single resource subscription if capability polling.
+     * availability fetch: no retry.
+     */
+     public static final int SUBSCRIBE_NOT_REGISTERED = SUBSCRIBER_ERROR_CODE_START - 2;
+
+    /**
+     * Responding for 403 - not authorized (Requestor)
+     * No retry.
+     */
+    public static final int SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE =
+            SUBSCRIBER_ERROR_CODE_START - 3;
+
+    /**
+     * Responding for "403 Forbidden" or "403"
+     * Handle it as same as 404 Not found.
+     * No retry.
+     */
+    public static final int SUBSCRIBE_FORBIDDEN = SUBSCRIBER_ERROR_CODE_START - 4;
+
+    /**
+     * Responding for 404 (target number)
+     * No retry.
+     */
+    public static final int SUBSCRIBE_NOT_FOUND = SUBSCRIBER_ERROR_CODE_START - 5;
+
+    /**
+     *  Responding for 413 - Too Large. Top app need shrink the size
+     *  of request contact list and resend the request
+     */
+    public static final int SUBSCRIBE_TOO_LARGE = SUBSCRIBER_ERROR_CODE_START - 6;
+
+    /**
+     * All subscribe errors not covered by specific errors
+     * Other 4xx/5xx/6xx
+     *
+     * Device shall not retry
+     */
+    public static final int SUBSCRIBE_GENIRIC_FAILURE = SUBSCRIBER_ERROR_CODE_START - 7;
+
+    /**
+     * Invalid parameter - The caller should check the parameter.
+     */
+    public static final int SUBSCRIBE_INVALID_PARAM = SUBSCRIBER_ERROR_CODE_START - 8;
+
+    /**
+     * Fetch error - The RCS statck failed to fetch the presence information.
+     */
+    public static final int SUBSCRIBE_FETCH_ERROR = SUBSCRIBER_ERROR_CODE_START - 9;
+
+    /**
+     * Request timeout - The RCS statck returns timeout error.
+     */
+    public static final int SUBSCRIBE_REQUEST_TIMEOUT = SUBSCRIBER_ERROR_CODE_START - 10;
+
+    /**
+     * Insufficient memory - The RCS statck returns the insufficient memory error.
+     */
+    public static final int SUBSCRIBE_INSUFFICIENT_MEMORY = SUBSCRIBER_ERROR_CODE_START - 11;
+
+    /**
+     * Lost network error - The RCS statck returns the lost network error.
+     */
+    public static final int SUBSCRIBE_LOST_NETWORK = SUBSCRIBER_ERROR_CODE_START - 12;
+
+    /**
+     * Not supported error - The RCS statck returns the not supported error.
+     */
+    public static final int SUBSCRIBE_NOT_SUPPORTED = SUBSCRIBER_ERROR_CODE_START - 13;
+
+    /**
+     * Generic error - RCS Presence stack returns generic error
+     */
+    public static final int SUBSCRIBE_GENERIC = SUBSCRIBER_ERROR_CODE_START - 14;
+
+    /**
+     * There is a request for the same number in queue.
+     */
+    public static final int SUBSCRIBE_ALREADY_IN_QUEUE = SUBSCRIBER_ERROR_CODE_START - 16;
+
+    /**
+     * Request too frequently.
+     */
+    public static final int SUBSCRIBE_TOO_FREQUENTLY = SUBSCRIBER_ERROR_CODE_START - 17;
+
+    /**
+     *  The last Subscriber error code
+     */
+    public static final int SUBSCRIBER_ERROR_CODE_END = SUBSCRIBER_ERROR_CODE_START - 17;
+
+    /**
+     * All publish errors not covered by specific errors
+     */
+    public static final int PUBLISH_GENERIC_FAILURE =  ResultCode.SUBSCRIBER_ERROR_CODE_END - 1;
+
+    /**
+     * Responding for 403 - not authorized
+     */
+    public static final int PUBLISH_NOT_AUTHORIZED_FOR_PRESENCE
+            = ResultCode.SUBSCRIBER_ERROR_CODE_END - 2;
+
+    /**
+     * Responding for 404 error code. The subscriber is not provisioned.
+     * The Client should not send any EAB traffic after get this error.
+     */
+    public static final int PUBLISH_NOT_PROVISIONED = ResultCode.SUBSCRIBER_ERROR_CODE_END - 3;
+}
diff --git a/rcs/rcsservice/Android.bp b/rcs/rcsservice/Android.bp
index ba191e8..bb23348 100644
--- a/rcs/rcsservice/Android.bp
+++ b/rcs/rcsservice/Android.bp
@@ -35,17 +35,14 @@
     srcs: [
         "src/com/android/service/ims/presence/*.java",
         "src/com/android/service/ims/RcsSettingUtils.java",
-        "src/com/android/service/ims/RcsUtils.java",
         "src/com/android/service/ims/Task.java",
         "src/com/android/service/ims/TaskManager.java",
-        // Move the following to the app once the dependencies have been decoupled.
-        "src/com/android/service/ims/RcsStackAdaptor.java"
+        ":rcsmanager-utils"
     ],
     libs: [
         "telephony-common",
         "ims-common",
     ],
-    static_libs: ["com.android.ims.rcsmanager"]
 }
 
 android_app {
@@ -54,13 +51,19 @@
     platform_apis: true,
     // Only compile source java files in this apk.
     srcs: [
+        "src/com/android/service/ims/AlarmBroadcastReceiver.java",
         "src/com/android/service/ims/DeviceShutdown.java",
         "src/com/android/service/ims/LauncherUtils.java",
+        "src/com/android/service/ims/PresenceInfoParser.java",
         "src/com/android/service/ims/RcsService.java",
-        "src/com/android/service/ims/RcsServiceApp.java"
+        "src/com/android/service/ims/RcsServiceApp.java",
+        "src/com/android/service/ims/RcsStackAdaptor.java",
+        "src/com/android/service/ims/RcsUtils.java",
+        "src/com/android/service/ims/StackListener.java"
     ],
     static_libs: [
-        "ucepresencelib"
+        "ucepresencelib",
+        "com.android.ims.rcsmanager"
     ],
     certificate: "platform",
 }
diff --git a/rcs/rcsservice/AndroidManifest.xml b/rcs/rcsservice/AndroidManifest.xml
index 9b9de40..77921c3 100644
--- a/rcs/rcsservice/AndroidManifest.xml
+++ b/rcs/rcsservice/AndroidManifest.xml
@@ -70,13 +70,14 @@
             </intent-filter>
         </receiver>
 
-        <!-- Receives the following explicit intents:
-            - com.android.service.ims.presence.retry
-            - com.android.service.ims.presence.task.timeout
-            - com.android.service.ims.presence.retry.publish -->
-        <receiver android:name="com.android.service.ims.presence.AlarmBroadcastReceiver"
+        <receiver android:name=".AlarmBroadcastReceiver"
             android:permission="com.android.ims.permission.PRESENCE_ACCESS"
             androidprv:systemUserOnly="true">
+            <intent-filter>
+                <action android:name="com.android.service.ims.presence.retry"/>
+                <action android:name="com.android.service.ims.presence.task.timeout"/>
+                <action android:name="com.android.service.ims.presence.retry.publish"/>
+            </intent-filter>
         </receiver>
     </application>
 </manifest>
diff --git a/rcs/rcsservice/LICENSE b/rcs/rcsservice/LICENSE
index 2e7609b..871b32a 100644
--- a/rcs/rcsservice/LICENSE
+++ b/rcs/rcsservice/LICENSE
@@ -1,3 +1,31 @@
+Copyright (c) 2019, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    - Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+    - Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    - Neither the name of The Android Open Source Project nor the names of its contributors may
+      be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+----------------------------------------------------------------------------------------------------
+
 Copyright (c) 2015, Motorola Mobility LLC
 All rights reserved.
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/AlarmBroadcastReceiver.java b/rcs/rcsservice/src/com/android/service/ims/AlarmBroadcastReceiver.java
similarity index 79%
rename from rcs/rcsservice/src/com/android/service/ims/presence/AlarmBroadcastReceiver.java
rename to rcs/rcsservice/src/com/android/service/ims/AlarmBroadcastReceiver.java
index f191186..e06b4a4 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/AlarmBroadcastReceiver.java
+++ b/rcs/rcsservice/src/com/android/service/ims/AlarmBroadcastReceiver.java
@@ -26,25 +26,19 @@
  * DAMAGE.
  */
 
-package com.android.service.ims.presence;
+package com.android.service.ims;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
 import com.android.ims.internal.Logger;
-import com.android.service.ims.RcsStackAdaptor;
-import com.android.service.ims.TaskManager;
+import com.android.service.ims.presence.PresenceCapabilityTask;
+import com.android.service.ims.presence.PresencePublication;
 
 public class AlarmBroadcastReceiver extends BroadcastReceiver{
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
-    public static final String ACTION_RETRY_ALARM = "com.android.service.ims.presence.retry";
-    private static final String ACTION_TASK_TIMEOUT_ALARM =
-            PresenceCapabilityTask.ACTION_TASK_TIMEOUT_ALARM;
-    private static final String ACTION_RETRY_PUBLISH_ALARM =
-            PresencePublication.ACTION_RETRY_PUBLISH_ALARM;
-
     @Override
     public void onReceive(Context context, Intent intent) {
         String action = intent.getAction();
@@ -58,15 +52,13 @@
             return;
         }
 
-        if(ACTION_RETRY_ALARM.equals(action)) {
+        if(RcsStackAdaptor.ACTION_RETRY_ALARM.equals(action)) {
             int times = intent.getIntExtra("times", -1);
             rcsStackAdaptor.startInitThread(times);
-        }else if(ACTION_TASK_TIMEOUT_ALARM.equals(action)){
+        }else if(PresenceCapabilityTask.ACTION_TASK_TIMEOUT_ALARM.equals(action)){
             int taskId = intent.getIntExtra("taskId", -1);
             TaskManager.getDefault().onTimeout(taskId);
-        } else if(ACTION_RETRY_PUBLISH_ALARM.equals(action)) {
-            // default retry is for 888
-            int sipCode = intent.getIntExtra("sipCode", 888);
+        } else if(PresencePublication.ACTION_RETRY_PUBLISH_ALARM.equals(action)) {
             PresencePublication publication = PresencePublication.getPresencePublication();
             if(publication != null) {
                 publication.retryPublish();
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceInfoParser.java b/rcs/rcsservice/src/com/android/service/ims/PresenceInfoParser.java
similarity index 84%
rename from rcs/rcsservice/src/com/android/service/ims/presence/PresenceInfoParser.java
rename to rcs/rcsservice/src/com/android/service/ims/PresenceInfoParser.java
index 1b3d795..5edb9a1 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceInfoParser.java
+++ b/rcs/rcsservice/src/com/android/service/ims/PresenceInfoParser.java
@@ -26,7 +26,10 @@
  * DAMAGE.
  */
 
-package com.android.service.ims.presence;
+package com.android.service.ims;
+
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
 
 import java.lang.String;
 import java.util.ArrayList;
@@ -39,6 +42,7 @@
 import com.android.ims.RcsPresenceInfo;
 import com.android.ims.RcsPresenceInfo.ServiceType;
 import com.android.ims.RcsPresenceInfo.ServiceState;
+import com.android.service.ims.presence.PresenceUtils;
 
 public class PresenceInfoParser{
     /*
@@ -166,8 +170,8 @@
                                 (ServiceState.ONLINE == presenceInfoTmp.getServiceState(
                                 ServiceType.VT_CALL)))?ServiceState.ONLINE:
                                     presenceInfoTmp.getServiceState(ServiceType.VT_CALL),
-                        presenceInfoTmp.getServiceContact(ServiceType.VOLTE_CALL),
-                        presenceInfoTmp.getTimeStamp(ServiceType.VOLTE_CALL)));
+                        presenceInfoTmp.getServiceContact(ServiceType.VT_CALL),
+                        presenceInfoTmp.getTimeStamp(ServiceType.VT_CALL)));
                 return;
             }
         }
@@ -268,4 +272,39 @@
 
         return number;
     }
+
+    public static RcsContactUceCapability getUceCapability(RcsPresenceInfo info) {
+        RcsContactUceCapability.Builder result = new RcsContactUceCapability.Builder(
+                PresenceUtils.convertContactNumber(info.getContactNumber()));
+        if (ServiceState.ONLINE == info.getServiceState(ServiceType.VOLTE_CALL)) {
+            result.add(RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL,
+                    PresenceUtils.convertContactNumber(
+                            info.getServiceContact(ServiceType.VOLTE_CALL)));
+        }
+        if (ServiceState.ONLINE == info.getServiceState(ServiceType.VT_CALL)) {
+            result.add(RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL,
+                    PresenceUtils.convertContactNumber(
+                            info.getServiceContact(ServiceType.VT_CALL)));
+        }
+        return result.build();
+    }
+
+    public static RcsPresenceInfo getRcsPresenceInfo(RcsContactUceCapability capability) {
+        int volteCapable = capability.isCapable(RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL) ?
+                ServiceState.ONLINE : ServiceState.OFFLINE;
+        int vtCapable = capability.isCapable(RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL) ?
+                ServiceState.ONLINE : ServiceState.OFFLINE;
+        return new RcsPresenceInfo(capability.getContactUri().getSchemeSpecificPart(),
+                // Not sure what the difference is, just track voice capable.
+                (volteCapable == ServiceState.ONLINE) ? RcsPresenceInfo.VolteStatus.VOLTE_ENABLED :
+                        RcsPresenceInfo.VolteStatus.VOLTE_DISABLED, volteCapable,
+                PresenceUtils.getNumber(capability.getServiceUri(
+                        RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL)),
+                // We always use system current time instead of time from server
+                System.currentTimeMillis(), vtCapable,
+                PresenceUtils.getNumber(capability.getServiceUri(
+                        RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL)),
+                // We always use system current time instead of time from server
+                System.currentTimeMillis());
+    }
 }
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsService.java b/rcs/rcsservice/src/com/android/service/ims/RcsService.java
index 78708da..f80e2e5 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsService.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsService.java
@@ -29,6 +29,7 @@
 package com.android.service.ims;
 
 import android.app.Service;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
@@ -44,20 +45,26 @@
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.feature.MmTelFeature;
 
 import com.android.ims.IRcsPresenceListener;
-import com.android.ims.RcsManager.ResultCode;
+import com.android.ims.RcsPresenceInfo;
+import com.android.ims.ResultCode;
+import com.android.ims.RcsPresence;
 import com.android.ims.internal.IRcsPresence;
 import com.android.ims.internal.IRcsService;
 import com.android.ims.internal.Logger;
-import com.android.internal.telephony.IccCardConstants;
 import com.android.service.ims.R;
+import com.android.service.ims.presence.ContactCapabilityResponse;
+import com.android.service.ims.presence.PresenceBase;
 import com.android.service.ims.presence.PresencePublication;
 import com.android.service.ims.presence.PresenceSubscriber;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class RcsService extends Service {
 
@@ -83,6 +90,78 @@
         }
     };
 
+    private class CapabilityResultListener implements ContactCapabilityResponse {
+
+        private final IRcsPresenceListener mListener;
+
+        public CapabilityResultListener(IRcsPresenceListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onSuccess(int reqId) {
+            try {
+                mListener.onSuccess(reqId);
+            } catch (RemoteException e) {
+                logger.warn("CapabilityResultListener: onSuccess exception = " + e.getMessage());
+            }
+        }
+
+        @Override
+        public void onError(int reqId, int resultCode) {
+            try {
+                mListener.onError(reqId, resultCode);
+            } catch (RemoteException e) {
+                logger.warn("CapabilityResultListener: onError exception = " + e.getMessage());
+            }
+        }
+
+        @Override
+        public void onFinish(int reqId) {
+            try {
+                mListener.onFinish(reqId);
+            } catch (RemoteException e) {
+                logger.warn("CapabilityResultListener: onFinish exception = " + e.getMessage());
+            }
+        }
+
+        @Override
+        public void onTimeout(int reqId) {
+            try {
+                mListener.onTimeout(reqId);
+            } catch (RemoteException e) {
+                logger.warn("CapabilityResultListener: onTimeout exception = " + e.getMessage());
+            }
+        }
+
+        @Override
+        public void onCapabilitiesUpdated(List<RcsContactUceCapability> contactCapabilities,
+                boolean updateLastTimestamp) {
+            ArrayList<RcsPresenceInfo> presenceInfoList = contactCapabilities.stream().map(
+                    PresenceInfoParser::getRcsPresenceInfo).collect(
+                    Collectors.toCollection(ArrayList::new));
+
+            logger.debug("capabilities updated:");
+            for (RcsPresenceInfo info : presenceInfoList) {
+                logger.debug("capabilities updated: info -" + info);
+            }
+            // For some reason it uses an intent to send this info back instead of just using the
+            // active binder...
+            Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
+            intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
+                    presenceInfoList);
+            intent.putExtra("updateLastTimestamp", updateLastTimestamp);
+            launchPersistService(intent);
+        }
+    }
+
+    private void launchPersistService(Intent intent) {
+        ComponentName component = new ComponentName("com.android.service.ims.presence",
+                "com.android.service.ims.presence.PersistService");
+        intent.setComponent(component);
+        startService(intent);
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -266,7 +345,8 @@
                 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
             }
 
-            return mSubscriber.requestCapability(contactsNumber, listener);
+            return mSubscriber.requestCapability(contactsNumber,
+                    new CapabilityResultListener(listener));
          }
 
         /**
@@ -287,7 +367,8 @@
             }
 
             // check availability cache (in RAM).
-            return mSubscriber.requestAvailability(contactNumber, listener, false);
+            return mSubscriber.requestAvailability(contactNumber,
+                    new CapabilityResultListener(listener), false);
         }
 
         /**
@@ -309,11 +390,12 @@
             }
 
             // check availability cache (in RAM).
-            return mSubscriber.requestAvailability(contactNumber, listener, true);
+            return mSubscriber.requestAvailability(contactNumber,
+                    new CapabilityResultListener(listener), true);
         }
 
         public int getPublishState() throws RemoteException {
-            return mPublication.getPublishState();
+            return publisherPublishStateToPublishState(mPublication.getPublishState());
         }
     };
 
@@ -448,5 +530,24 @@
             mPublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
         }
     };
+
+    private static int publisherPublishStateToPublishState(int publisherPublishState) {
+        switch(publisherPublishState) {
+            case PresenceBase.PUBLISH_STATE_200_OK:
+                return RcsPresence.PublishState.PUBLISH_STATE_200_OK;
+            case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
+                return RcsPresence.PublishState.PUBLISH_STATE_NOT_PUBLISHED;
+            case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
+                return RcsPresence.PublishState.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
+                return RcsPresence.PublishState.PUBLISH_STATE_RCS_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
+                return RcsPresence.PublishState.PUBLISH_STATE_REQUEST_TIMEOUT;
+            case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
+                return RcsPresence.PublishState.PUBLISH_STATE_OTHER_ERROR;
+
+        }
+        return PresenceBase.PUBLISH_STATE_OTHER_ERROR;
+    }
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsSettingUtils.java b/rcs/rcsservice/src/com/android/service/ims/RcsSettingUtils.java
index 40fb709..f5f893e 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsSettingUtils.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsSettingUtils.java
@@ -31,82 +31,131 @@
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsException;
-import com.android.ims.ImsManager;
 import com.android.ims.internal.Logger;
 
-public class RcsSettingUtils{
-    /*
-     * The logger
-     */
+import java.util.List;
+
+public class RcsSettingUtils {
     static private Logger logger = Logger.getLogger("RcsSettingUtils");
 
-    public RcsSettingUtils() {
-    }
-
-    public static boolean isFeatureProvisioned(Context context,
-            int featureId, boolean defaultValue) {
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        // Don't need provision.
-        if (configManager != null) {
-            PersistableBundle config = configManager.getConfig();
-            if (config != null && !config.getBoolean(
-                    CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) {
-                return true;
-            }
-        }
-
-        boolean provisioned = defaultValue;
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    provisioned = imsConfig.getProvisionedValue(featureId)
-                            == ImsConfig.FeatureValueConstants.ON;
-                }
-            } catch (ImsException ex) {
-            }
-        }
-
-        logger.debug("featureId=" + featureId + " provisioned=" + provisioned);
-        return provisioned;
-    }
+    // Values taken from ImsConfig - Should define in @SystemApi as well.
+    /**
+     * SIP T1 timer value in milliseconds. See RFC 3261 for definition.
+     * Value is in Integer format.
+     */
+    private static final int SIP_T1_TIMER = 7;
+    /**
+     * Whether or not capability discovery is provisioned.
+     */
+    private static final int CAPABILITY_DISCOVERY_ENABLED = 17;
+    /**
+     * period of time the availability information of a contact is cached on device.
+     * Value is in Integer format.
+     */
+    private static final int AVAILABILITY_CACHE_EXPIRATION = 19;
+    /**
+     * Minimum time between two published messages from the device.
+     * Value is in Integer format.
+     */
+    private static final int SOURCE_THROTTLE_PUBLISH = 21;
+    /**
+     * The Maximum number of MDNs contained in one Request Contained List.
+     * Value is in Integer format.
+     */
+    private static final int MAX_NUMENTRIES_IN_RCL = 22;
+    /**
+     * Expiration timer for subscription of a Request Contained List, used in capability
+     * polling.
+     * Value is in Integer format.
+     */
+    private static final int CAPAB_POLL_LIST_SUB_EXP = 23;
+    /**
+     * Provisioning status for Enhanced Address Book (EAB)
+     * Value is in Integer format.
+     */
+    private static final int EAB_SETTING_ENABLED = 25;
+    /**
+     * Whether or not mobile data is enabled currently.
+     */
+    private static final int MOBILE_DATA_ENABLED = 29;
 
     public static boolean isVowifiProvisioned(Context context) {
-        return isFeatureProvisioned(context,
-                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED, false);
+        try {
+            boolean isProvisioned;
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            isProvisioned = manager.getProvisioningStatusForCapability(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+            logger.debug("isVowifiProvisioned=" + isProvisioned);
+            return isProvisioned;
+        } catch (Exception e) {
+            logger.debug("isVowifiProvisioned, exception = " + e.getMessage());
+            return false;
+        }
     }
 
     public static boolean isLvcProvisioned(Context context) {
-        return isFeatureProvisioned(context,
-                ImsConfig.ConfigConstants.LVC_SETTING_ENABLED, false);
+        try {
+            boolean isProvisioned;
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            isProvisioned = manager.getProvisioningStatusForCapability(
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+            logger.debug("isLvcProvisioned=" + isProvisioned);
+            return isProvisioned;
+        } catch (Exception e) {
+            logger.debug("isLvcProvisioned, exception = " + e.getMessage());
+            return false;
+        }
     }
 
     public static boolean isEabProvisioned(Context context) {
-        return isFeatureProvisioned(context,
-                ImsConfig.ConfigConstants.EAB_SETTING_ENABLED, false);
+        boolean isProvisioned = false;
+        int subId = getDefaultSubscriptionId(context);
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            logger.debug("isEabProvisioned: no valid subscriptions!");
+            return false;
+        }
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager != null) {
+            PersistableBundle config = configManager.getConfigForSubId(subId);
+            if (config != null && !config.getBoolean(
+                    CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) {
+                // If we don't need provisioning, just return true.
+                return true;
+            }
+        }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
+            isProvisioned = manager.getProvisioningIntValue(EAB_SETTING_ENABLED)
+                    == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+        } catch (Exception e) {
+            logger.debug("isEabProvisioned: exception=" + e.getMessage());
+        }
+        logger.debug("isEabProvisioned=" + isProvisioned);
+        return isProvisioned;
     }
 
     public static int getSIPT1Timer(Context context) {
         int sipT1Timer = 0;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    sipT1Timer = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.SIP_T1_TIMER);
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            sipT1Timer = manager.getProvisioningIntValue(SIP_T1_TIMER);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("getSIPT1Timer: exception=" + e.getMessage());
         }
-
-        logger.debug("sipT1Timer=" + sipT1Timer);
+        logger.debug("getSIPT1Timer=" + sipT1Timer);
         return sipT1Timer;
     }
 
@@ -115,20 +164,15 @@
      */
     public static boolean getCapabilityDiscoveryEnabled(Context context) {
         boolean capabilityDiscoveryEnabled = false;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    capabilityDiscoveryEnabled = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.CAPABILITY_DISCOVERY_ENABLED)
-                            == ImsConfig.FeatureValueConstants.ON;
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            capabilityDiscoveryEnabled = manager.getProvisioningIntValue(CAPABILITY_DISCOVERY_ENABLED)
+                    == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("capabilityDiscoveryEnabled: exception=" + e.getMessage());
         }
-
         logger.debug("capabilityDiscoveryEnabled=" + capabilityDiscoveryEnabled);
         return capabilityDiscoveryEnabled;
     }
@@ -138,20 +182,15 @@
      */
     public static int getMaxNumbersInRCL(Context context) {
         int maxNumbersInRCL = 100;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    maxNumbersInRCL = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.MAX_NUMENTRIES_IN_RCL);
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            maxNumbersInRCL = manager.getProvisioningIntValue(MAX_NUMENTRIES_IN_RCL);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("getMaxNumbersInRCL: exception=" + e.getMessage());
         }
-
-        logger.debug("maxNumbersInRCL=" + maxNumbersInRCL);
+        logger.debug("getMaxNumbersInRCL=" + maxNumbersInRCL);
         return maxNumbersInRCL;
     }
 
@@ -160,98 +199,100 @@
      */
     public static int getCapabPollListSubExp(Context context) {
         int capabPollListSubExp = 30;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    capabPollListSubExp = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.CAPAB_POLL_LIST_SUB_EXP);
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            capabPollListSubExp = manager.getProvisioningIntValue(CAPAB_POLL_LIST_SUB_EXP);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("getCapabPollListSubExp: exception=" + e.getMessage());
         }
-
-        logger.debug("capabPollListSubExp=" + capabPollListSubExp);
+        logger.debug("getCapabPollListSubExp=" + capabPollListSubExp);
         return capabPollListSubExp;
     }
 
     /**
-     * Peiod of time the availability information of a contact is cached on device.
+     * Period of time the availability information of a contact is cached on device.
      */
     public static int getAvailabilityCacheExpiration(Context context) {
         int availabilityCacheExpiration = 30;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    availabilityCacheExpiration = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.AVAILABILITY_CACHE_EXPIRATION);
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            availabilityCacheExpiration = manager.getProvisioningIntValue(
+                    AVAILABILITY_CACHE_EXPIRATION);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("getAvailabilityCacheExpiration: exception=" + e.getMessage());
         }
-
-        logger.debug("availabilityCacheExpiration=" + availabilityCacheExpiration);
+        logger.debug("getAvailabilityCacheExpiration=" + availabilityCacheExpiration);
         return availabilityCacheExpiration;
     }
 
     public static boolean isMobileDataEnabled(Context context) {
         boolean mobileDataEnabled = false;
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    mobileDataEnabled = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.MOBILE_DATA_ENABLED)
-                            == ImsConfig.FeatureValueConstants.ON;
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            mobileDataEnabled = manager.getProvisioningIntValue(MOBILE_DATA_ENABLED)
+                    == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("isMobileDataEnabled: exception=" + e.getMessage());
         }
-
         logger.debug("mobileDataEnabled=" + mobileDataEnabled);
         return mobileDataEnabled;
     }
 
     public static void setMobileDataEnabled(Context context, boolean mobileDataEnabled) {
         logger.debug("mobileDataEnabled=" + mobileDataEnabled);
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    imsConfig.setProvisionedValue(
-                            ImsConfig.ConfigConstants.MOBILE_DATA_ENABLED, mobileDataEnabled?
-                            ImsConfig.FeatureValueConstants.ON:ImsConfig.FeatureValueConstants.OFF);
-                }
-            } catch (ImsException ex) {
-                logger.debug("ImsException", ex);
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            manager.setProvisioningIntValue(MOBILE_DATA_ENABLED, mobileDataEnabled ?
+                    ProvisioningManager.PROVISIONING_VALUE_ENABLED :
+                    ProvisioningManager.PROVISIONING_VALUE_DISABLED);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("mobileDataEnabled: exception=" + e.getMessage());
         }
     }
 
     public static int getPublishThrottle(Context context) {
+        // Default
         int publishThrottle = 60000;
-
-        ImsManager imsManager = ImsManager.getInstance(context, 0);
-        if (imsManager != null) {
-            try {
-                ImsConfig imsConfig = imsManager.getConfigInterface();
-                if (imsConfig != null) {
-                    publishThrottle = imsConfig.getProvisionedValue(
-                            ImsConfig.ConfigConstants.SOURCE_THROTTLE_PUBLISH);
-                }
-            } catch (ImsException ex) {
-            }
+        try {
+            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(
+                    getDefaultSubscriptionId(context));
+            publishThrottle = manager.getProvisioningIntValue(SOURCE_THROTTLE_PUBLISH);
+        } catch (Exception e) {
+            // If there is no active subscriptions, this will throw an exception.
+            logger.debug("publishThrottle: exception=" + e.getMessage());
         }
-
         logger.debug("publishThrottle=" + publishThrottle);
         return publishThrottle;
     }
+
+    private static int getDefaultSubscriptionId(Context context) {
+        SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
+        List<SubscriptionInfo> infos = sm.getActiveSubscriptionInfoList();
+        if (infos == null || infos.isEmpty()) {
+            // There are no active subscriptions right now.
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+        // This code does not support MSIM unfortunately, so only provide presence on the default
+        // subscription that the user chose.
+        int defaultSub = SubscriptionManager.getDefaultSubscriptionId();
+        // If the user has no default set, just pick the first as backup.
+        if (defaultSub == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            for (SubscriptionInfo info : infos) {
+                if (!info.isOpportunistic()) {
+                    defaultSub = info.getSubscriptionId();
+                    break;
+                }
+            }
+        }
+        return defaultSub;
+    }
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java b/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
index eb2edd7..4f1df37 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
@@ -44,12 +44,11 @@
 import android.os.SystemProperties;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.text.TextUtils;
 
-import com.android.ims.IRcsPresenceListener;
-import com.android.ims.RcsManager.ResultCode;
+import com.android.ims.ResultCode;
 import com.android.ims.RcsPresence;
-import com.android.ims.RcsPresence.PublishState;
-import com.android.ims.RcsPresenceInfo;
 import com.android.ims.internal.ContactNumberUtils;
 import com.android.ims.internal.Logger;
 import com.android.ims.internal.uce.common.CapInfo;
@@ -59,18 +58,23 @@
 import com.android.ims.internal.uce.presence.PresCapInfo;
 import com.android.ims.internal.uce.uceservice.IUceService;
 import com.android.ims.internal.uce.uceservice.ImsUceManager;
-import com.android.service.ims.presence.AlarmBroadcastReceiver;
-import com.android.service.ims.presence.PresenceInfoParser;
-import com.android.service.ims.presence.PresencePublication;
-import com.android.service.ims.presence.StackListener;
+import com.android.service.ims.presence.PresenceBase;
+import com.android.service.ims.presence.PresencePublisher;
+import com.android.service.ims.presence.SubscribePublisher;
 
-public class RcsStackAdaptor{
+public class RcsStackAdaptor implements PresencePublisher, SubscribePublisher {
     private static final boolean DEBUG = true;
 
     private static final String PERSIST_SERVICE_NAME =
             "com.android.service.ims.presence.PersistService";
     private static final String PERSIST_SERVICE_PACKAGE = "com.android.service.ims.presence";
 
+    /**
+     * PendingIntent action used to retry getting the UCE service. Need an associated
+     * BroadcastReceiver.
+     */
+    public static final String ACTION_RETRY_ALARM = "com.android.service.ims.presence.retry";
+
     // The logger
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
@@ -83,7 +87,7 @@
 
     // provision status can be set by both subscribe and pubilish
     // for unprovisioned for 403 or 404
-    private volatile int mPublishingState = PublishState.PUBLISH_STATE_NOT_PUBLISHED;
+    private volatile int mPublishingState = PresenceBase.PUBLISH_STATE_NOT_PUBLISHED;
 
     // It is initializing the stack presence service.
     private volatile boolean mIsIniting = false;
@@ -186,7 +190,8 @@
         return mListenerHandler;
     }
 
-    public int checkStackAndPublish(){
+    @Override
+    public int getStackStatusForCapabilityRequest() {
         if (!RcsSettingUtils.getCapabilityDiscoveryEnabled(mContext)) {
             logger.error("getCapabilityDiscoveryEnabled = false");
             return ResultCode.ERROR_SERVICE_NOT_ENABLED;
@@ -199,16 +204,15 @@
         }
 
         if (!isPublished()) {
-            logger.error(
-                    "checkStackAndPublish ERROR_SERVICE_NOT_PUBLISHED");
+            logger.error("checkStackAndPublish ERROR_SERVICE_NOT_PUBLISHED");
             return ResultCode.ERROR_SERVICE_NOT_PUBLISHED;
         }
 
         return ResultCode.SUCCESS;
     }
 
-    private boolean isPublished(){
-        if(getPublishState() != PublishState.PUBLISH_STATE_200_OK){
+    private boolean isPublished() {
+        if (getPublisherState() != PresenceBase.PUBLISH_STATE_200_OK) {
             logger.error("Didnt' publish properly");
             return false;
         }
@@ -216,33 +220,30 @@
         return true;
     }
 
-    public void setPublishState(int publishState) {
+    @Override
+    public void updatePublisherState(@PresenceBase.PresencePublishState int publishState) {
         synchronized (mSyncObj) {
             logger.print("mPublishingState=" + mPublishingState + " publishState=" + publishState);
-            if (mPublishingState != publishState) {
-                // save it for recovery when PresenceService crash.
-                SystemProperties.set("rcs.publish.status",
-                        String.valueOf(publishState));
-
-                Intent publishIntent = new Intent(RcsPresence.ACTION_PUBLISH_STATE_CHANGED);
-                publishIntent.putExtra(RcsPresence.EXTRA_PUBLISH_STATE, publishState);
-                // Start PersistService and broadcast to other receivers that are listening
-                // dynamically.
-                mContext.sendStickyBroadcast(publishIntent);
-                launchPersistService(publishIntent);
-            }
-
             mPublishingState = publishState;
         }
+        // save it for recovery when PresenceService crash.
+        SystemProperties.set("rcs.publish.status", String.valueOf(publishState));
+        Intent publishIntent = new Intent(RcsPresence.ACTION_PUBLISH_STATE_CHANGED);
+        publishIntent.putExtra(RcsPresence.EXTRA_PUBLISH_STATE, publishState);
+        // Start PersistService and broadcast to other receivers that are listening
+        // dynamically.
+        mContext.sendStickyBroadcast(publishIntent);
+        launchPersistService(publishIntent);
     }
 
-    public int getPublishState(){
+    @Override
+    public @PresenceBase.PresencePublishState int getPublisherState() {
         synchronized (mSyncObj) {
             return mPublishingState;
         }
     }
 
-    public int checkStackStatus(){
+    private int checkStackStatus() {
         synchronized (mSyncObj) {
             if (!RcsSettingUtils.isEabProvisioned(mContext)) {
                 logger.error("Didn't get EAB provisioned by DM");
@@ -275,19 +276,20 @@
         return ResultCode.SUCCESS;
     }
 
-    public int requestCapability(String[] formatedContacts, int taskId){
-        logger.print("requestCapability formatedContacts=" + formatedContacts);
+    @Override
+    public int requestCapability(String[] formattedContacts, int taskId) {
+        logger.print("requestCapability formattedContacts=" + formattedContacts);
 
         int ret = ResultCode.SUCCESS;
         try {
             synchronized (mSyncObj) {
                 StatusCode retCode;
-                if (formatedContacts.length == 1) {
+                if (formattedContacts.length == 1) {
                     retCode = mStackPresService.getContactCap(
-                            mStackPresenceServiceHandle, formatedContacts[0], taskId);
+                            mStackPresenceServiceHandle, formattedContacts[0], taskId);
                 } else {
                     retCode = mStackPresService.getContactListCap(
-                            mStackPresenceServiceHandle, formatedContacts, taskId);
+                            mStackPresenceServiceHandle, formattedContacts, taskId);
                 }
 
                 logger.print("GetContactListCap retCode=" + retCode);
@@ -304,14 +306,15 @@
         return  ret;
     }
 
-    public int requestAvailability(String formatedContact, int taskId){
+    @Override
+    public int requestAvailability(String formattedContact, int taskId) {
         logger.debug("requestAvailability ...");
 
         int ret = ResultCode.SUCCESS;
         try{
             synchronized (mSyncObj) {
                 StatusCode retCode = mStackPresService.getContactCap(
-                        mStackPresenceServiceHandle, formatedContact, taskId);
+                        mStackPresenceServiceHandle, formattedContact, taskId);
                 logger.print("getContactCap retCode=" + retCode);
 
                 ret = RcsUtils.statusCodeToResultCode(retCode.getStatusCode());
@@ -325,7 +328,8 @@
         return  ret;
     }
 
-    public int requestPublication(RcsPresenceInfo presenceInfo, IRcsPresenceListener listener) {
+    @Override
+    public int requestPublication(RcsContactUceCapability capabilities) {
         logger.debug("requestPublication ...");
 
          // Don't use checkStackAndPublish()
@@ -338,15 +342,15 @@
 
         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
             Context.TELEPHONY_SERVICE);
-        if(teleMgr == null){
+        if (teleMgr == null) {
             logger.error("teleMgr = null");
-            return PresencePublication.PUBLISH_GENIRIC_FAILURE;
+            return ResultCode.PUBLISH_GENERIC_FAILURE;
         }
 
         String myNumUri = null;
         String myDomain = teleMgr.getIsimDomain();
         logger.debug("myDomain=" + myDomain);
-        if(myDomain != null && myDomain.length() !=0){
+        if (myDomain != null && !TextUtils.isEmpty(myDomain)) {
             String[] impu = teleMgr.getIsimImpu();
 
             if(impu !=null){
@@ -363,34 +367,32 @@
 
         String myNumber = PresenceInfoParser.getPhoneFromUri(myNumUri);
 
-        if(myNumber == null){
+        if (myNumber == null) {
             myNumber = ContactNumberUtils.getDefault().format(teleMgr.getLine1Number());
-            if(myDomain != null && myDomain.length() !=0){
+            if (myDomain != null && !TextUtils.isEmpty(myDomain)) {
                 myNumUri = "sip:" + myNumber + "@" + myDomain;
-            }else{
+            } else {
                 myNumUri = "tel:" + myNumber;
             }
         }
 
         logger.print("myNumUri=" + myNumUri + " myNumber=" + myNumber);
-        if(myNumUri == null || myNumber == null){
+        if (myNumUri == null || myNumber == null) {
             logger.error("Didn't find number or impu.");
-            return PresencePublication.PUBLISH_GENIRIC_FAILURE;
+            return ResultCode.PUBLISH_GENERIC_FAILURE;
         }
 
-        int taskId = TaskManager.getDefault().addPublishTask(myNumber, listener);
-        try{
+        int taskId = TaskManager.getDefault().addPublishTask(myNumber);
+        try {
             PresCapInfo pMyCapInfo = new PresCapInfo();
             // Fill cap info
             pMyCapInfo.setContactUri(myNumUri);
 
             CapInfo capInfo = new CapInfo();
-            capInfo.setIpVoiceSupported(presenceInfo.getServiceState(
-                    RcsPresenceInfo.ServiceType.VOLTE_CALL)
-                    == RcsPresenceInfo.ServiceState.ONLINE);
-            capInfo.setIpVideoSupported(presenceInfo.getServiceState(
-                    RcsPresenceInfo.ServiceType.VT_CALL)
-                    == RcsPresenceInfo.ServiceState.ONLINE);
+            capInfo.setIpVoiceSupported(capabilities.isCapable(
+                    RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL));
+            capInfo.setIpVideoSupported(capabilities.isCapable(
+                    RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL));
             capInfo.setCdViaPresenceSupported(true);
 
             capInfo.setFtSupported(false); // TODO: support FT
@@ -413,12 +415,12 @@
             }
 
             logger.debug("requestPublication ret=" + ret);
-            if(ret != ResultCode.SUCCESS){
+            if (ret != ResultCode.SUCCESS) {
                 logger.error("requestPublication remove taskId=" + taskId);
                 TaskManager.getDefault().removeTask(taskId);
                 return ret;
             }
-        }catch(RemoteException e){
+        } catch (RemoteException e) {
             e.printStackTrace();
             logger.error("Exception when call mStackPresService.getContactCap");
             logger.error("requestPublication remove taskId=" + taskId);
@@ -608,6 +610,12 @@
         initImsUceService();
 
         setInPowerDown(false);
+        try {
+            // Restore the previous value in case the process has crashed.
+            updatePublisherState(Integer.parseInt(SystemProperties.get("rcs.publish.status", "1")));
+        } catch (NumberFormatException e) {
+            // do nothing and use the default.
+        }
         logger.debug("init finished");
     }
 
@@ -626,9 +634,9 @@
 
             mIsIniting = true;
 
-            Intent intent = new Intent(AlarmBroadcastReceiver.ACTION_RETRY_ALARM);
+            Intent intent = new Intent(ACTION_RETRY_ALARM);
             intent.putExtra("times", times);
-            intent.setClass(mContext, AlarmBroadcastReceiver.class);
+            intent.setPackage(mContext.getPackageName());
             mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                     PendingIntent.FLAG_UPDATE_CURRENT);
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java b/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
index 22e156e..0e05e7f 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
@@ -28,18 +28,10 @@
 
 package com.android.service.ims;
 
-import com.android.ims.RcsManager.ResultCode;
-import com.android.ims.internal.Logger;
+import com.android.ims.ResultCode;
 import com.android.ims.internal.uce.common.StatusCode;
 
-public class RcsUtils{
-    /*
-     * The logger
-     */
-    static private Logger logger = Logger.getLogger("RcsUtils");
-
-    public RcsUtils() {
-    }
+public class RcsUtils {
 
     static public int statusCodeToResultCode(int sipStatusCode){
         if(sipStatusCode == StatusCode.UCE_SUCCESS ||
@@ -83,21 +75,5 @@
 
         return ResultCode.SUBSCRIBE_GENERIC;
     }
-
-    static public String toContactString(String[] contacts) {
-        if(contacts == null) {
-            return null;
-        }
-
-         String result = "";
-        for(int i=0; i<contacts.length; i++) {
-            result += contacts[i];
-            if(i != contacts.length -1) {
-                result += ";";
-            }
-        }
-
-        return result;
-    }
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java b/rcs/rcsservice/src/com/android/service/ims/StackListener.java
similarity index 78%
rename from rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java
rename to rcs/rcsservice/src/com/android/service/ims/StackListener.java
index ae1d415..7edf080 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java
+++ b/rcs/rcsservice/src/com/android/service/ims/StackListener.java
@@ -26,7 +26,7 @@
  * DAMAGE.
  */
 
-package com.android.service.ims.presence;
+package com.android.service.ims;
 
 import android.content.Context;
 import android.content.Intent;
@@ -35,9 +35,12 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.ims.RcsManager;
+import com.android.ims.RcsPresenceInfo;
 import com.android.ims.internal.Logger;
 import com.android.ims.internal.uce.common.StatusCode;
 import com.android.ims.internal.uce.presence.IPresenceListener;
@@ -47,8 +50,12 @@
 import com.android.ims.internal.uce.presence.PresResInfo;
 import com.android.ims.internal.uce.presence.PresRlmiInfo;
 import com.android.ims.internal.uce.presence.PresSipResponse;
+import com.android.ims.internal.uce.presence.PresSubscriptionState;
 import com.android.ims.internal.uce.presence.PresTupleInfo;
-import com.android.service.ims.RcsStackAdaptor;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresenceSubscriber;
+
+import java.util.ArrayList;
 
 public class StackListener extends Handler{
     /*
@@ -82,7 +89,7 @@
         mContext = context;
     }
 
-    public void setPresencePublication(PresencePublication presencePublication){
+    public void setPresencePublication(PresencePublication presencePublication) {
         mPresencePublication = presencePublication;
     }
 
@@ -111,7 +118,8 @@
                     return;
                 }
 
-                mPresencePublication.invokePublish(val);
+                mPresencePublication.onStackPublishRequested(
+                        convertToStackPublishTriggerType(val.getPublishTrigeerType()));
                 break;
             }
 
@@ -119,13 +127,16 @@
             case PRESENCE_IMS_UNSOL_PUBLISH_CMDSTATUS:
             {
                 PresCmdStatus pCmdStatus = (PresCmdStatus) msg.obj;
-                if(mPresencePublication == null || pCmdStatus == null){
+                if(mPresencePublication == null || pCmdStatus == null) {
                     logger.error("mPresencePublication=" + mPresencePublication +
                             " pCmdStatus=" + pCmdStatus);
                     return;
                 }
 
-                mPresencePublication.handleCmdStatus(pCmdStatus);
+                int commandResult = RcsUtils.statusCodeToResultCode(
+                        pCmdStatus.getStatus().getStatusCode());
+                mPresencePublication.onCommandStatusUpdated(pCmdStatus.getUserData(),
+                        pCmdStatus.getRequestId(), commandResult);
             }
                 break;
 
@@ -138,8 +149,10 @@
                             " pCmdStatus=" + pCmdStatus);
                     return;
                 }
-
-                mPresenceSubscriber.handleCmdStatus(pCmdStatus);
+                int commandResult = RcsUtils.statusCodeToResultCode(
+                        pCmdStatus.getStatus().getStatusCode());
+                mPresenceSubscriber.onCommandStatusUpdated(pCmdStatus.getUserData(),
+                        pCmdStatus.getRequestId(), commandResult);
                 break;
             }
 
@@ -147,13 +160,14 @@
             case PRESENCE_IMS_UNSOL_PUBLISH_SIPRESPONSE:
             {
                 PresSipResponse pSipResponse =  (PresSipResponse) msg.obj;
-                if(mPresencePublication == null || pSipResponse == null){
+                if(mPresencePublication == null || pSipResponse == null) {
                     logger.error("mPresencePublication=" + mPresencePublication +
                             "pSipResponse=" +pSipResponse);
                     return;
                 }
 
-                mPresencePublication.handleSipResponse(pSipResponse);
+                mPresencePublication.onSipResponse(pSipResponse.getRequestId(),
+                        pSipResponse.getSipResponseCode(), pSipResponse.getReasonPhrase());
                 break;
             }
 
@@ -167,7 +181,8 @@
                     return;
                 }
 
-                mPresenceSubscriber.handleSipResponse(pSipResponse);
+                mPresenceSubscriber.onSipResponse(pSipResponse.getRequestId(),
+                        pSipResponse.getSipResponseCode(), pSipResponse.getReasonPhrase());
                 break;
             }
 
@@ -180,9 +195,16 @@
                             " notifyData=" + notifyData);
                     return;
                 }
-
-                mPresenceSubscriber.updatePresence(notifyData.getUri(),
-                        notifyData.getTupleInfo());
+                RcsPresenceInfo rcsPresenceInfo = PresenceInfoParser.getPresenceInfoFromTuple(
+                        notifyData.getUri(), notifyData.getTupleInfo());
+                if(rcsPresenceInfo == null || TextUtils.isEmpty(
+                        rcsPresenceInfo.getContactNumber())){
+                    logger.error("rcsPresenceInfo is null or " +
+                            "TextUtils.isEmpty(rcsPresenceInfo.getContactNumber()");
+                    return;
+                }
+                mPresenceSubscriber.updatePresence(
+                        PresenceInfoParser.getUceCapability(rcsPresenceInfo));
                 break;
             }
 
@@ -196,8 +218,40 @@
                     return;
                 }
 
-                mPresenceSubscriber.updatePresences(notifyListData.getRlmiInfo(),
-                        notifyListData.getResInfo());
+                RcsPresenceInfo[] rcsPresenceInfos = PresenceInfoParser.
+                        getPresenceInfosFromPresenceRes(notifyListData.getRlmiInfo(),
+                                notifyListData.getResInfo());
+                if(rcsPresenceInfos == null){
+                    logger.error("updatePresences: rcsPresenceInfos == null");
+                    return;
+                }
+
+                PresRlmiInfo info = notifyListData.getRlmiInfo();
+                boolean isTerminated = false;
+                if (info.getPresSubscriptionState() != null) {
+                    if (info.getPresSubscriptionState().getPresSubscriptionStateValue() ==
+                            PresSubscriptionState.UCE_PRES_SUBSCRIPTION_STATE_TERMINATED) {
+                        isTerminated = true;
+                    }
+                }
+
+                ArrayList<RcsContactUceCapability> capabilities = new ArrayList<>();
+
+                for (int i=0; i < rcsPresenceInfos.length; i++) {
+                    if(rcsPresenceInfos[i] != null && TextUtils.isEmpty(
+                            rcsPresenceInfos[i].getContactNumber())){
+                        continue;
+                    }
+                    RcsContactUceCapability capability = PresenceInfoParser.getUceCapability(
+                            rcsPresenceInfos[i]);
+                    if(capability != null && (capability.getContactUri() != null)){
+                        logger.debug("capability=" + capability);
+                        capabilities.add(capability);
+                    }
+                }
+
+                mPresenceSubscriber.updatePresences(info.getRequestId(), capabilities, isTerminated,
+                        info.getSubscriptionTerminatedReason());
                 break;
             }
 
@@ -353,11 +407,9 @@
                 }
 
                 // Handle the cached trigger which got from stack
-                if(mPresencePublication != null && mPresencePublication.getHasCachedTrigger()){
+                if(mPresencePublication != null) {
                     logger.debug("publish for cached trigger");
-
-                    mPresencePublication.invokePublish(
-                            PresencePublication.PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
+                    mPresencePublication.onStackAvailable();
                 }
 
                 Intent intent = new Intent(RcsManager.ACTION_RCS_SERVICE_AVAILABLE);
@@ -518,5 +570,30 @@
             logger.debug("unpublishMessageSent()");
         }
     };
+
+    @PresencePublication.StackPublishTriggerType
+    private static int convertToStackPublishTriggerType(int presPublishTriggerType) {
+        switch (presPublishTriggerType) {
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN;
+            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN:
+                return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN;
+        }
+        return PresencePublication.UCE_PRES_PUBLISH_TRIGGER_UNKNOWN;
+    }
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/Task.java b/rcs/rcsservice/src/com/android/service/ims/Task.java
index eb36a5f..61e85b4 100644
--- a/rcs/rcsservice/src/com/android/service/ims/Task.java
+++ b/rcs/rcsservice/src/com/android/service/ims/Task.java
@@ -28,8 +28,8 @@
 
 package com.android.service.ims;
 
-import com.android.ims.IRcsPresenceListener;
 import com.android.ims.internal.Logger;
+import com.android.service.ims.presence.ContactCapabilityResponse;
 
 /**
  * Task
@@ -51,21 +51,6 @@
     // QRCS_PRES_CMD_SETNEWFEATURETAG
     public int mCmdId;
 
-    // filled after IQPresListener_CMDStatus
-    // QRCS_STATUSCODE, possible values are:
-    // QRCS_SUCCESS
-    // QRCS_FAILURE
-    // QRCS_SUCCESS_ASYC_UPDATE
-    // QRCS_INVALID_SERVICE_HANDLE
-    // QRCS_INVALID_LISTENER_HANDLE
-    // QRCS_INVALID_PARAM
-    // QRCS_FETCH_ERROR
-    // QRCS_REQUEST_TIMEOUT
-    // QRCS_INSUFFICIENT_MEMORY
-    // QRCS_LOST_NET
-    // QRCS_NOT_SUPPORTED
-    // QRCS_NOT_FOUND
-    // Note: have converted it to ResultCode.
     public int mCmdStatus;
 
     //filled after IQPresListener_CMDStatus
@@ -77,10 +62,9 @@
     // filled after IQPresListener_SipResponseReceived
     public String mSipReasonPhrase;
 
-    // filled before send the request
-    public IRcsPresenceListener mListener;
+    public ContactCapabilityResponse mListener;
 
-    public Task(int taskId, int cmdId, IRcsPresenceListener listener){
+    public Task(int taskId, int cmdId, ContactCapabilityResponse listener) {
         mTaskId = taskId;
         mCmdId = cmdId;
         mListener = listener;
diff --git a/rcs/rcsservice/src/com/android/service/ims/TaskManager.java b/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
index a643861..076a244 100644
--- a/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
+++ b/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
@@ -35,8 +35,8 @@
 import android.os.Message;
 import android.telephony.PhoneNumberUtils;
 
-import com.android.ims.IRcsPresenceListener;
 import com.android.ims.internal.Logger;
+import com.android.service.ims.presence.ContactCapabilityResponse;
 import com.android.service.ims.presence.PresenceAvailabilityTask;
 import com.android.service.ims.presence.PresenceCapabilityTask;
 import com.android.service.ims.presence.PresenceTask;
@@ -109,7 +109,7 @@
     }
 
     public int addCapabilityTask(Context context, String[] contacts,
-            IRcsPresenceListener listener, long timeout){
+            ContactCapabilityResponse listener, long timeout){
         int taskId = TaskManager.getDefault().generateTaskId();
         synchronized (mSyncObj){
             Task task = new PresenceCapabilityTask(context, taskId, TASK_TYPE_GET_CAPABILITY,
@@ -120,7 +120,7 @@
         return taskId;
     }
 
-    public int addAvailabilityTask(String contact, IRcsPresenceListener listener){
+    public int addAvailabilityTask(String contact, ContactCapabilityResponse listener){
         int taskId = TaskManager.getDefault().generateTaskId();
         synchronized (mSyncObj){
             String[] contacts = new String[1];
@@ -133,12 +133,12 @@
         return taskId;
     }
 
-    public int addPublishTask(String contact, IRcsPresenceListener listener){
+    public int addPublishTask(String contact){
         int taskId = TaskManager.getDefault().generateTaskId();
         synchronized (mSyncObj){
             String[] contacts = new String[1];
             contacts[0] = contact;
-            Task task = new PresenceTask(taskId, TASK_TYPE_PUBLISH, listener, contacts);
+            Task task = new PresenceTask(taskId, TASK_TYPE_PUBLISH, null /*listener*/, contacts);
             putTaskInternal(taskId, task);
         }
 
@@ -162,6 +162,32 @@
         }
     }
 
+    public Task getTaskForSingleContactQuery(String contact) {
+        synchronized (mSyncObj){
+            Set<String> keys= mTaskMap.keySet();
+            if(keys == null){
+                logger.debug("getTaskByContact keys=null");
+                return null;
+            }
+
+            for(String key:keys){
+                Task task = mTaskMap.get(key);
+                if(task == null){
+                    continue;
+                }
+
+                if (task instanceof PresenceTask) {
+                    PresenceTask presenceTask = (PresenceTask) task;
+                    if(presenceTask.mContacts.length == 1 &&
+                            PhoneNumberUtils.compare(contact, presenceTask.mContacts[0])){
+                        return task;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     public Task getTaskByRequestId(int sipRequestId){
         synchronized (mSyncObj){
             Set<String> keys= mTaskMap.keySet();
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/ContactCapabilityResponse.java b/rcs/rcsservice/src/com/android/service/ims/presence/ContactCapabilityResponse.java
new file mode 100644
index 0000000..5eb3154
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/ContactCapabilityResponse.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     - Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     - Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     - Neither the name of The Android Open Source Project nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+package com.android.service.ims.presence;
+
+import android.telephony.ims.RcsContactUceCapability;
+
+import java.util.List;
+
+public interface ContactCapabilityResponse {
+
+    /**
+     * Called when a capability request returns a "200 OK" (means capable) or "404 xxxx" for SIP
+     * request of single contact number (means not capable).
+     *
+     * @param reqId the request ID which is returned by requestCapability or
+     * requestAvailability
+     */
+    void onSuccess(int reqId);
+
+    /**
+     * Called when a local error is generated a SIP error is generated from the network.
+     *
+     * @param reqId the request ID which is returned by requestCapability or
+     * requestAvailability
+     * @param resultCode the result code which is defined in RcsManager.ResultCode.
+     */
+    void onError(int reqId, int resultCode);
+
+    /**
+     * Called when the request returns a "terminated notify" indication from the network.
+     * The presence service will not receive any more notifications for the request after this
+     * indication is received.
+     *
+     * @param reqId the request ID which is returned by requestCapability or
+     * requestAvailability
+     */
+    void onFinish(int reqId);
+
+    /**
+     * Called when there is a timeout waiting for the "terminated notify" indication from the
+     * network.
+     *
+     * @param reqId the request ID which is returned by requestCapability or
+     * requestAvailability.
+     */
+    void onTimeout(int reqId);
+
+    /**
+     * Called when there is an update to the capabilities from the network. On error, the
+     * capabilities will also be updates as not capable.
+     */
+    void onCapabilitiesUpdated(List<RcsContactUceCapability> contactCapabilities,
+            boolean updateLastTimestamp);
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceAvailabilityTask.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceAvailabilityTask.java
index a008589..08a5b32 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceAvailabilityTask.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceAvailabilityTask.java
@@ -28,7 +28,6 @@
 
 package com.android.service.ims.presence;
 
-import com.android.ims.IRcsPresenceListener;
 import com.android.ims.internal.Logger;
 
 /**
@@ -45,7 +44,7 @@
     // Time when get the notify. Used to check the 60s expires.
     private long mNotifyTimeStamp = 0;
 
-    public PresenceAvailabilityTask(int taskId, int cmdId, IRcsPresenceListener listener,
+    public PresenceAvailabilityTask(int taskId, int cmdId, ContactCapabilityResponse listener,
             String[] contacts){
         super(taskId, cmdId, listener, contacts);
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceBase.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceBase.java
index abe6fd7..9ffcba1 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceBase.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceBase.java
@@ -28,61 +28,110 @@
 
 package com.android.service.ims.presence;
 
+import android.annotation.IntDef;
 import android.content.Context;
-import android.os.RemoteException;
 import android.content.Intent;
 
-import com.android.ims.internal.uce.presence.PresCmdStatus;
-import com.android.ims.internal.uce.presence.PresSipResponse;
-import com.android.ims.RcsManager.ResultCode;
-import com.android.ims.RcsPresence.PublishState;
+import com.android.ims.ResultCode;
 import com.android.ims.internal.Logger;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.service.ims.Task;
-import com.android.service.ims.RcsUtils;
 import com.android.service.ims.TaskManager;
 
-public abstract class PresenceBase{
-    /*
-     * The logger
-     */
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class PresenceBase {
     static private Logger logger = Logger.getLogger("PresenceBase");
 
-    protected Context mContext = null;
+    protected Context mContext;
 
-    public PresenceBase() {
+    /**
+     * The phone is PUBLISH_STATE_200_OK when
+     * the response of the last publish is "200 OK"
+     */
+    public static final int PUBLISH_STATE_200_OK = 0;
+
+    /**
+     * The phone didn't publish after power on.
+     * the phone didn't get any publish response yet.
+     */
+    public static final int PUBLISH_STATE_NOT_PUBLISHED = 1;
+
+    /**
+     * The phone is PUBLISH_STATE_VOLTE_PROVISION_ERROR when the response is one of items
+     * in config_volte_provision_error_on_publish_response for PUBLISH or
+     * in config_volte_provision_error_on_subscribe_response for SUBSCRIBE.
+     */
+    public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 2;
+
+    /**
+     * The phone is PUBLISH_STATE_RCS_PROVISION_ERROR when the response is one of items
+     * in config_rcs_provision_error_on_publish_response for PUBLISH or
+     * in config_rcs_provision_error_on_subscribe_response for SUBSCRIBE.Publ
+     */
+    public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 3;
+
+    /**
+     * The phone is PUBLISH_STATE_REQUEST_TIMEOUT when
+     * The response of the last publish is "408 Request Timeout".
+     */
+    public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 4;
+
+    /**
+     * The phone is PUBLISH_STATE_OTHER_ERROR when
+     * the response of the last publish is other temp error. Such as
+     * 503 Service Unavailable
+     * Device shall retry with exponential back-off
+     *
+     * 423 Interval Too Short. Requested expiry interval too short and server rejects it
+     * Device shall re-attempt subscription after changing the expiration interval in
+     * the Expires header field to be equal to or greater than the expiration interval
+     * within the Min-Expires header field of the 423 response.
+     */
+    public static final int PUBLISH_STATE_OTHER_ERROR = 5;
+
+    @IntDef(value = {
+            PUBLISH_STATE_200_OK,
+            PUBLISH_STATE_NOT_PUBLISHED,
+            PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+            PUBLISH_STATE_RCS_PROVISION_ERROR,
+            PUBLISH_STATE_REQUEST_TIMEOUT,
+            PUBLISH_STATE_OTHER_ERROR
+    }, prefix="PUBLISH_STATE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PresencePublishState {}
+
+    public PresenceBase(Context context) {
+        mContext = context;
     }
 
-    protected void handleCallback(Task task, int resultCode, boolean forCmdStatus){
-        if(task == null){
+    protected void handleCallback(Task task, int resultCode, boolean forCmdStatus) {
+        if (task == null) {
             logger.debug("task == null");
             return;
         }
 
-        if(task.mListener != null){
-            try{
-                if(resultCode >= ResultCode.SUCCESS){
-                    if(!forCmdStatus){
-                        task.mListener.onSuccess(task.mTaskId);
-                    }
-                }else{
-                    task.mListener.onError(task.mTaskId, resultCode);
+        if (task.mListener != null) {
+            if(resultCode >= ResultCode.SUCCESS){
+                if(!forCmdStatus){
+                    task.mListener.onSuccess(task.mTaskId);
                 }
-            }catch(RemoteException e){
-                logger.debug("Failed to send the status to client.");
+            }else{
+                task.mListener.onError(task.mTaskId, resultCode);
             }
         }
 
         // remove task when error
         // remove task when SIP response success.
         // For list capability polling we will waiting for the terminated notify or timeout.
-        if(resultCode != ResultCode.SUCCESS){
+        if (resultCode != ResultCode.SUCCESS) {
             if(task instanceof PresencePublishTask){
                 PresencePublishTask publishTask = (PresencePublishTask) task;
                 logger.debug("handleCallback for publishTask=" + publishTask);
-                if(resultCode == PublishState.PUBLISH_STATE_VOLTE_PROVISION_ERROR) {
+                if(resultCode == PUBLISH_STATE_VOLTE_PROVISION_ERROR) {
                     // retry 3 times for "403 Not Authorized for Presence".
-                    if(publishTask.getRetryCount() >= 3) {
+                    if (publishTask.getRetryCount() >= 3) {
                         //remove capability after try 3 times by PresencePolling
                         logger.debug("handleCallback remove task=" + task);
                         TaskManager.getDefault().removeTask(task.mTaskId);
@@ -121,16 +170,10 @@
         }
     }
 
-    public void handleCmdStatus(PresCmdStatus pCmdStatus){
-        if(pCmdStatus == null){
-            logger.error("handleCallbackForCmdStatus pCmdStatus=null");
-            return;
-        }
-
-        Task task = TaskManager.getDefault().getTask(pCmdStatus.getUserData());
-        int resultCode = RcsUtils.statusCodeToResultCode(pCmdStatus.getStatus().getStatusCode());
-        if(task != null){
-            task.mSipRequestId = pCmdStatus.getRequestId();
+    public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) {
+        Task task = TaskManager.getDefault().getTask(taskId);
+        if (task != null){
+            task.mSipRequestId = requestId;
             task.mCmdStatus = resultCode;
             TaskManager.getDefault().putTask(task.mTaskId, task);
         }
@@ -167,7 +210,5 @@
         }
         return false;
     }
-
-    abstract public void handleSipResponse(PresSipResponse pSipResponse);
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceCapabilityTask.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceCapabilityTask.java
index ff69915..e94e51a 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceCapabilityTask.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceCapabilityTask.java
@@ -32,11 +32,9 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.os.RemoteException;
 import android.os.SystemClock;
 
 import com.android.ims.internal.Logger;
-import com.android.ims.IRcsPresenceListener;
 import com.android.service.ims.TaskManager;
 
 /**
@@ -48,6 +46,9 @@
      */
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
+    /**
+     * PendingIntent Action that will be scheduled via AlarmManager as a task timeout.
+     */
     public static final String ACTION_TASK_TIMEOUT_ALARM =
             "com.android.service.ims.presence.task.timeout";
 
@@ -70,7 +71,7 @@
     private long mTimeout;
 
     public PresenceCapabilityTask(Context context, int taskId, int cmdId,
-            IRcsPresenceListener listener, String[] contacts,
+            ContactCapabilityResponse listener, String[] contacts,
             long timeout){
         super(taskId, cmdId, listener, contacts);
         mContext = context;
@@ -105,7 +106,7 @@
         }
 
         Intent intent = new Intent(ACTION_TASK_TIMEOUT_ALARM);
-        intent.setClass(mContext, AlarmBroadcastReceiver.class);
+        intent.setPackage(mContext.getPackageName());
         intent.putExtra("taskId", mTaskId);
         PendingIntent mAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                 PendingIntent.FLAG_ONE_SHOT);
@@ -133,14 +134,9 @@
 
     public void onTimeout(){
         logger.debug("onTimeout, taskId=" + mTaskId);
-        try{
-            if(mListener != null){
-                mListener.onTimeout(mTaskId);
-            }
-        }catch (RemoteException e){
-            logger.error("RemoteException", e);
+        if(mListener != null){
+            mListener.onTimeout(mTaskId);
         }
-
         TaskManager.getDefault().removeTask(mTaskId);
     }
 
@@ -159,12 +155,8 @@
         }
 
         cancelTimer();
-        try{
-            if(mListener != null){
-                mListener.onFinish(mTaskId);
-            }
-        }catch (RemoteException e){
-            logger.error("RemoteException", e);
+        if(mListener != null){
+            mListener.onFinish(mTaskId);
         }
 
         TaskManager.getDefault().removeTask(mTaskId);
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
index f827622..62c5091 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
@@ -28,46 +28,40 @@
 
 package com.android.service.ims.presence;
 
-import java.util.Arrays;
-
+import android.annotation.IntDef;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import com.android.internal.telephony.Phone;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
 import android.provider.Settings;
-import android.os.SystemProperties;
-import android.content.BroadcastReceiver;
+import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-
-import android.telecom.TelecomManager;
-import android.content.IntentFilter;
-import android.app.PendingIntent;
-import android.app.AlarmManager;
-import android.os.SystemClock;
-import android.os.Message;
-import android.os.Handler;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.feature.MmTelFeature;
 
-
-import com.android.ims.ImsManager;
-import com.android.ims.ImsServiceClass;
 import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.ims.ResultCode;
 import com.android.ims.internal.Logger;
-import com.android.ims.internal.uce.presence.PresPublishTriggerType;
-import com.android.ims.internal.uce.presence.PresSipResponse;
-import com.android.ims.internal.uce.presence.PresCmdStatus;
-import com.android.ims.RcsPresenceInfo;
-import com.android.ims.IRcsPresenceListener;
-import com.android.ims.RcsManager.ResultCode;
-import com.android.ims.RcsPresence.PublishState;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.service.ims.RcsSettingUtils;
-import com.android.service.ims.RcsStackAdaptor;
 import com.android.service.ims.Task;
 import com.android.service.ims.TaskManager;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class PresencePublication extends PresenceBase {
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
@@ -88,37 +82,11 @@
     volatile PublishRequest mPublishedRequest = null;
 
     /*
-     * Message to notify for a publish
+     * Message sent to mMsgHandler when there is a new request to Publish capabilities.
      */
-    public static final int MESSAGE_RCS_PUBLISH_REQUEST = 1;
+    private static final int MESSAGE_RCS_PUBLISH_REQUEST = 1;
 
-    /**
-     *  Publisher Error base
-     */
-    public static final int PUBLISH_ERROR_CODE_START = ResultCode.SUBSCRIBER_ERROR_CODE_END;
-
-    /**
-     * All publish errors not covered by specific errors
-     */
-    public static final int PUBLISH_GENIRIC_FAILURE = PUBLISH_ERROR_CODE_START - 1;
-
-    /**
-     * Responding for 403 - not authorized
-     */
-    public static final int PUBLISH_NOT_AUTHORIZED_FOR_PRESENCE = PUBLISH_ERROR_CODE_START - 2;
-
-    /**
-     * Responding for 404 error code. The subscriber is not provisioned.
-     * The Client should not send any EAB traffic after get this error.
-     */
-    public static final int PUBLISH_NOT_PROVISIONED = PUBLISH_ERROR_CODE_START - 3;
-
-    /**
-     * Responding for 200 - OK
-     */
-    public static final int PUBLISH_SUCESS = ResultCode.SUCCESS;
-
-    private RcsStackAdaptor mRcsStackAdaptor = null;
+    private PresencePublisher mPresencePublisher;
     private PresenceSubscriber mSubscriber = null;
     static private PresencePublication sPresencePublication = null;
     private BroadcastReceiver mReceiver = null;
@@ -135,7 +103,43 @@
     private final String[] mConfigVolteProvisionErrorOnPublishResponse;
     private final String[] mConfigRcsProvisionErrorOnPublishResponse;
 
-    public class PublishType{
+    /** ETag expired. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED = 0;
+    /** Move to LTE with VoPS disabled. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1;
+    /** Move to LTE with VoPS enabled. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2;
+    /** Move to eHRPD. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD = 3;
+    /** Move to HSPA+. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS = 4;
+    /** Move to 3G. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G = 5;
+    /** Move to 2G. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G = 6;
+    /** Move to WLAN */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN = 7;
+    /** Move to IWLAN */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN = 8;
+    /** Trigger is unknown. */
+    public static final int UCE_PRES_PUBLISH_TRIGGER_UNKNOWN = 9;
+
+    @IntDef(value = {
+            UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_WLAN,
+            UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN,
+            UCE_PRES_PUBLISH_TRIGGER_UNKNOWN
+    }, prefix="UCE_PRES_PUBLISH_TRIGGER_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StackPublishTriggerType {}
+
+    public class PublishType {
         public static final int PRES_PUBLISH_TRIGGER_DATA_CHANGED = 0;
         // the lower layer should send trigger when enable volte
         // the lower layer should unpublish when disable volte
@@ -147,17 +151,12 @@
         public static final int PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED = 5;
     };
 
-    /**
-     * @param rcsStackAdaptor
-     * @param context
-     */
-    public PresencePublication(RcsStackAdaptor rcsStackAdaptor, Context context,
+    public PresencePublication(PresencePublisher presencePublisher, Context context,
             String[] configVolteProvisionErrorOnPublishResponse,
             String[] configRcsProvisionErrorOnPublishResponse) {
-        super();
+        super(context);
         logger.debug("PresencePublication constrcuct");
-        this.mRcsStackAdaptor = rcsStackAdaptor;
-        this.mContext = context;
+        this.mPresencePublisher = presencePublisher;
         mConfigVolteProvisionErrorOnPublishResponse = configVolteProvisionErrorOnPublishResponse;
         mConfigRcsProvisionErrorOnPublishResponse = configRcsProvisionErrorOnPublishResponse;
 
@@ -176,12 +175,6 @@
                 Phone.TTY_MODE_OFF);
         logger.debug("The current TTY mode is: " + mPreferredTtyMode);
 
-        if(mRcsStackAdaptor != null){
-            mRcsStackAdaptor.setPublishState(
-                    SystemProperties.getInt("rcs.publish.status",
-                    PublishState.PUBLISH_STATE_NOT_PUBLISHED));
-        }
-
         mReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -214,7 +207,7 @@
                                          // treate hot SIM hot swap as power on.
                                          mDonotRetryUntilPowerCycle = false;
                                          if(mHasCachedTrigger) {
-                                             invokePublish(PresencePublication.PublishType.
+                                             requestLocalPublish(PublishType.
                                                      PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
                                          }
                                          break;
@@ -235,16 +228,14 @@
                         // only reset when the SIM gets absent.
                         reset();
                         mSimLoaded = false;
-                        setPublishState(
-                                PublishState.PUBLISH_STATE_NOT_PUBLISHED);
+                        setPublishState(PUBLISH_STATE_NOT_PUBLISHED);
                     }
                 }else if(Intent.ACTION_AIRPLANE_MODE_CHANGED.equalsIgnoreCase(intent.getAction())){
                     boolean airplaneMode = intent.getBooleanExtra("state", false);
                     if(airplaneMode){
                         logger.print("Airplane mode, set to PUBLISH_STATE_NOT_PUBLISHED");
                         reset();
-                        setPublishState(
-                                PublishState.PUBLISH_STATE_NOT_PUBLISHED);
+                        setPublishState(PUBLISH_STATE_NOT_PUBLISHED);
                     }
                 }else if(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED.equalsIgnoreCase(
                         intent.getAction())){
@@ -261,7 +252,7 @@
                     if (mIsTtyEnabled != isTtyEnabled) {
                         logger.print("ttyEnabled status changed from " + mIsTtyEnabled
                                 + " to " + isTtyEnabled);
-                        invokePublish(PresencePublication.PublishType.
+                        requestLocalPublish(PublishType.
                                 PRES_PUBLISH_TRIGGER_TTY_ENABLE_STATUS);
                     }
                 } else if(ImsConfig.ACTION_IMS_FEATURE_CHANGED.equalsIgnoreCase(
@@ -421,7 +412,7 @@
             logger.debug("provisioned, set mDonotRetryUntilPowerCycle to false");
             mDonotRetryUntilPowerCycle = false;
             if(mHasCachedTrigger) {
-                invokePublish(PresencePublication.PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
+                requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
             }
         }
     }
@@ -445,8 +436,7 @@
             mDataEnabled = value;
             RcsSettingUtils.setMobileDataEnabled(mContext, mDataEnabled);
 
-            invokePublish(
-                    PresencePublication.PublishType.PRES_PUBLISH_TRIGGER_DATA_CHANGED);
+            requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_DATA_CHANGED);
         }
     }
 
@@ -455,11 +445,16 @@
 
         if(mVtEnabled != enabled) {
             mVtEnabled = enabled;
-            invokePublish(PresencePublication.PublishType.
-                    PRES_PUBLISH_TRIGGER_VTCALL_CHANGED);
+            requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_VTCALL_CHANGED);
         }
     }
 
+    @Override
+    public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) {
+        logger.info("onCommandStatusUpdated: resultCode= " + resultCode);
+        super.onCommandStatusUpdated(taskId, requestId, resultCode);
+    }
+
     /**
      * @return return true if it had been PUBLISHED.
      */
@@ -471,36 +466,30 @@
                 (System.currentTimeMillis() - mPublishingRequest.getTimestamp()
                 <= publishThreshold);
 
-        return (getPublishState() == PublishState.PUBLISH_STATE_200_OK || publishing);
+        return (getPublishState() == PUBLISH_STATE_200_OK || publishing);
     }
 
     /**
-     * @return the Publish State
+     * @return The result of the last Publish request.
      */
-    public int getPublishState() {
-        if(mRcsStackAdaptor == null){
-            return PublishState.PUBLISH_STATE_NOT_PUBLISHED;
+    public @PresenceBase.PresencePublishState int getPublishState() {
+        if (mPresencePublisher != null) {
+            return mPresencePublisher.getPublisherState();
         }
-
-        return mRcsStackAdaptor.getPublishState();
+        return PUBLISH_STATE_NOT_PUBLISHED;
     }
 
     /**
-     * @param mPublishState the publishState to set
+     * @param publishState The result of the last publish request.
      */
     public void setPublishState(int publishState) {
-        if(mRcsStackAdaptor != null){
-            mRcsStackAdaptor.setPublishState(publishState);
+        if (mPresencePublisher != null) {
+            mPresencePublisher.updatePublisherState(publishState);
         }
     }
 
-    public boolean getHasCachedTrigger(){
-        return mHasCachedTrigger;
-    }
-
-    // Tiggered by framework.
-    public int invokePublish(int trigger){
-        int ret;
+    // Trigger a local publish based off of state changes in the framework.
+    private void requestLocalPublish(int trigger) {
 
         // if the value is true then it will call stack to send the PUBLISH
         // though the previous publish had the same capability
@@ -553,26 +542,26 @@
         if(mGotTriggerFromStack == false){
             // Waiting for RCS stack get ready.
             logger.print("Didn't get trigger from stack yet, discard framework trigger.");
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+            return;
         }
 
         if (mDonotRetryUntilPowerCycle) {
             logger.print("Don't publish until next power cycle");
-            return ResultCode.SUCCESS;
+            return;
         }
 
         if(!mSimLoaded){
             //need to read some information from SIM to publish
             logger.print("invokePublish cache the trigger since the SIM is not ready");
             mHasCachedTrigger = true;
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+            return;
         }
 
         //the provision status didn't be read from modem yet
         if(!RcsSettingUtils.isEabProvisioned(mContext)) {
             logger.print("invokePublish cache the trigger, not provision yet");
             mHasCachedTrigger = true;
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+            return;
         }
 
         PublishRequest publishRequest = new PublishRequest(
@@ -580,25 +569,20 @@
 
         requestPublication(publishRequest);
 
-        return ResultCode.SUCCESS;
+        return;
     }
 
-    public int invokePublish(PresPublishTriggerType val) {
-        int ret;
-
+    public void onStackPublishRequested(@StackPublishTriggerType int publishTriggerType) {
         mGotTriggerFromStack = true;
 
-        // Always send the PUBLISH when we got the trigger from stack.
-        // This can avoid any issue when switch the phone between IWLAN and LTE.
-        boolean bForceToNetwork = true;
-        switch (val.getPublishTrigeerType())
+        switch (publishTriggerType)
         {
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED:
+            case UCE_PRES_PUBLISH_TRIGGER_ETAG_EXPIRED:
             {
                 logger.print("PUBLISH_TRIGGER_ETAG_EXPIRED");
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED");
                 mMovedToLTE = true;
@@ -609,7 +593,7 @@
                 mImsRegistered = true;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED");
                 mMovedToLTE = true;
@@ -620,7 +604,7 @@
                 mImsRegistered = true;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN:
             {
                 logger.print("QRCS_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN");
                 mMovedToLTE = false;
@@ -631,7 +615,7 @@
                 mImsRegistered = true;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_EHRPD:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_EHRPD");
                 mMovedToLTE = false;
@@ -642,7 +626,7 @@
                 mImsRegistered = true;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_HSPAPLUS");
                 mMovedToLTE = false;
@@ -650,7 +634,7 @@
                 mMovedToIWLAN = false;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_2G:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_2G");
                 mMovedToLTE = false;
@@ -658,7 +642,7 @@
                 mMovedToIWLAN = false;
                 break;
             }
-            case PresPublishTriggerType.UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G:
+            case UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_3G:
             {
                 logger.print("PUBLISH_TRIGGER_MOVE_TO_3G");
                 mMovedToLTE = false;
@@ -672,29 +656,38 @@
 
         if (mDonotRetryUntilPowerCycle) {
             logger.print("Don't publish until next power cycle");
-            return ResultCode.SUCCESS;
+            return;
         }
 
         if(!mSimLoaded){
             //need to read some information from SIM to publish
             logger.print("invokePublish cache the trigger since the SIM is not ready");
             mHasCachedTrigger = true;
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+            return;
         }
 
         //the provision status didn't be read from modem yet
         if(!RcsSettingUtils.isEabProvisioned(mContext)) {
             logger.print("invokePublish cache the trigger, not provision yet");
             mHasCachedTrigger = true;
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+            return;
         }
 
+        // Always send the PUBLISH when we got the trigger from stack.
+        // This can avoid any issue when switch the phone between IWLAN and LTE.
         PublishRequest publishRequest = new PublishRequest(
-                bForceToNetwork, System.currentTimeMillis());
+                true /*forceToNetwork*/, System.currentTimeMillis());
 
         requestPublication(publishRequest);
+    }
 
-        return ResultCode.SUCCESS;
+    /**
+     * Trigger a publish when the stack becomes available and we have a cached trigger waiting.
+     */
+    public void onStackAvailable() {
+        if (mHasCachedTrigger) {
+            requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_CACHED_TRIGGER);
+        }
     }
 
     public class PublishRequest {
@@ -835,8 +828,8 @@
             return;
         }
 
-        if (mRcsStackAdaptor == null) {
-            logger.error("mRcsStackAdaptor == null");
+        if (mPresencePublisher == null) {
+            logger.error("mPresencePublisher == null");
             return;
         }
 
@@ -861,7 +854,7 @@
                 (publishRequest.hasSamePublishContent(mPublishingRequest) ||
                         (mPublishingRequest == null) &&
                         publishRequest.hasSamePublishContent(mPublishedRequest)) &&
-                (getPublishState() != PublishState.PUBLISH_STATE_NOT_PUBLISHED)) {
+                (getPublishState() != PUBLISH_STATE_NOT_PUBLISHED)) {
             logger.print("Don't need publish since the capability didn't change publishRequest " +
                     publishRequest + " getPublishState()=" + getPublishState());
             return;
@@ -897,7 +890,7 @@
         // we need send PUBLISH once even the volte is off when power on the phone.
         // That will tell other phone that it has no volte/vt capability.
         if(!getImsManager().isEnhanced4gLteModeSettingEnabledByUser() &&
-                getPublishState() != PublishState.PUBLISH_STATE_NOT_PUBLISHED) {
+                getPublishState() != PUBLISH_STATE_NOT_PUBLISHED) {
              // volte was not enabled.
              // or it is turnning off volte. lower layer should unpublish
              reset();
@@ -906,37 +899,35 @@
 
         TelephonyManager teleMgr = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
-        if(teleMgr ==null) {
-             logger.debug("teleMgr=null");
+        if (teleMgr == null) {
+             logger.error("TelephonyManager not available.");
              return;
-         }
+        }
+        Uri contactUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, teleMgr.getLine1Number(), null);
 
-        RcsPresenceInfo presenceInfo = new RcsPresenceInfo(teleMgr.getLine1Number(),
-                RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN,
-                publishRequest.getVolteCapable()?RcsPresenceInfo.ServiceState.ONLINE:
-                        RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis(),
-                publishRequest.getVtCapable()?RcsPresenceInfo.ServiceState.ONLINE:
-                        RcsPresenceInfo.ServiceState.OFFLINE, null,
-                        System.currentTimeMillis());
+        RcsContactUceCapability.Builder presenceInfoBuilder =
+                new RcsContactUceCapability.Builder(contactUri);
+        if (publishRequest.getVolteCapable()) {
+            presenceInfoBuilder.add(RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL);
+        }
+        if (publishRequest.getVtCapable()) {
+            presenceInfoBuilder.add(RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL);
+        }
 
         synchronized(mSyncObj) {
             mPublishingRequest = publishRequest;
             mPublishingRequest.setTimestamp(System.currentTimeMillis());
         }
 
-        int ret = mRcsStackAdaptor.requestPublication(presenceInfo, null);
-        if(ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE){
+        int ret = mPresencePublisher.requestPublication(presenceInfoBuilder.build());
+        if (ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE) {
             mHasCachedTrigger = true;
-        }else{
+        } else {
             //reset the cached trigger
             mHasCachedTrigger = false;
         }
     }
 
-    public void handleCmdStatus(PresCmdStatus cmdStatus) {
-        super.handleCmdStatus(cmdStatus);
-    }
-
     private PendingIntent mRetryAlarmIntent = null;
     public static final String ACTION_RETRY_PUBLISH_ALARM =
             "com.android.service.ims.presence.retry.publish";
@@ -959,7 +950,7 @@
         mCancelRetry = false;
 
         Intent intent = new Intent(ACTION_RETRY_PUBLISH_ALARM);
-        intent.setClass(mContext, AlarmBroadcastReceiver.class);
+        intent.setPackage(mContext.getPackageName());
         mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
 
@@ -981,28 +972,23 @@
             return;
         }
 
-        invokePublish(PublishType.PRES_PUBLISH_TRIGGER_RETRY);
+        requestLocalPublish(PublishType.PRES_PUBLISH_TRIGGER_RETRY);
     }
 
-    public void handleSipResponse(PresSipResponse pSipResponse) {
-        logger.print( "Publish response code = " + pSipResponse.getSipResponseCode()
-                +"Publish response reason phrase = " + pSipResponse.getReasonPhrase());
+    public void onSipResponse(int requestId, int responseCode, String reasonPhrase) {
+        logger.print( "Publish response code = " + responseCode
+                +"Publish response reason phrase = " + reasonPhrase);
 
         synchronized(mSyncObj) {
             mPublishedRequest = mPublishingRequest;
             mPublishingRequest = null;
         }
-        if(pSipResponse == null){
-            logger.debug("handlePublishResponse pSipResponse = null");
-            return;
-        }
-        int sipCode = pSipResponse.getSipResponseCode();
 
-        if(isInConfigList(sipCode, pSipResponse.getReasonPhrase(),
+        if (isInConfigList(responseCode, reasonPhrase,
                 mConfigVolteProvisionErrorOnPublishResponse)) {
-            logger.print("volte provision error. sipCode=" + sipCode + " phrase=" +
-                    pSipResponse.getReasonPhrase());
-            setPublishState(PublishState.PUBLISH_STATE_VOLTE_PROVISION_ERROR);
+            logger.print("volte provision error. sipCode=" + responseCode + " phrase=" +
+                    reasonPhrase);
+            setPublishState(PUBLISH_STATE_VOLTE_PROVISION_ERROR);
             mDonotRetryUntilPowerCycle = true;
 
             notifyDm();
@@ -1010,49 +996,47 @@
             return;
         }
 
-        if(isInConfigList(sipCode, pSipResponse.getReasonPhrase(),
-                mConfigRcsProvisionErrorOnPublishResponse)) {
-            logger.print("rcs provision error.sipCode=" + sipCode + " phrase=" +
-                    pSipResponse.getReasonPhrase());
-            setPublishState(PublishState.PUBLISH_STATE_RCS_PROVISION_ERROR);
+        if (isInConfigList(responseCode, reasonPhrase, mConfigRcsProvisionErrorOnPublishResponse)) {
+            logger.print("rcs provision error.sipCode=" + responseCode + " phrase=" + reasonPhrase);
+            setPublishState(PUBLISH_STATE_RCS_PROVISION_ERROR);
             mDonotRetryUntilPowerCycle = true;
 
             return;
         }
 
-        switch (sipCode) {
+        switch (responseCode) {
             case 999:
                 logger.debug("Publish ignored - No capability change");
                 break;
             case 200:
-                setPublishState(PublishState.PUBLISH_STATE_200_OK);
+                setPublishState(PUBLISH_STATE_200_OK);
                 if(mSubscriber != null) {
                     mSubscriber.retryToGetAvailability();
                 }
                 break;
 
             case 408:
-                setPublishState(PublishState.PUBLISH_STATE_REQUEST_TIMEOUT);
+                setPublishState(PUBLISH_STATE_REQUEST_TIMEOUT);
                 break;
 
             default: // Generic Failure
-                if ((sipCode < 100) || (sipCode > 699)) {
-                    logger.debug("Ignore internal response code, sipCode=" + sipCode);
+                if ((responseCode < 100) || (responseCode > 699)) {
+                    logger.debug("Ignore internal response code, sipCode=" + responseCode);
                     // internal error
                     //  0- PERMANENT ERROR: UI should not retry immediately
                     //  888- TEMP ERROR:  UI Can retry immediately
                     //  999- NO CHANGE: No Publish needs to be sent
-                    if(sipCode == 888) {
+                    if(responseCode == 888) {
                         // retry per 2 minutes
                         scheduleRetryPublish(120000);
                     } else {
-                        logger.debug("Ignore internal response code, sipCode=" + sipCode);
+                        logger.debug("Ignore internal response code, sipCode=" + responseCode);
                     }
                 } else {
                     logger.debug( "Generic Failure");
-                    setPublishState(PublishState.PUBLISH_STATE_OTHER_ERROR);
+                    setPublishState(PUBLISH_STATE_OTHER_ERROR);
 
-                    if ((sipCode>=400) && (sipCode <= 699)) {
+                    if ((responseCode>=400) && (responseCode <= 699)) {
                         // 4xx/5xx/6xx, No retry, no impact on subsequent publish
                         logger.debug( "No Retry in OEM");
                     }
@@ -1061,11 +1045,10 @@
         }
 
         // Suppose the request ID had been set when IQPresListener_CMDStatus
-        Task task = TaskManager.getDefault().getTaskByRequestId(
-                pSipResponse.getRequestId());
+        Task task = TaskManager.getDefault().getTaskByRequestId(requestId);
         if(task != null){
-            task.mSipResponseCode = pSipResponse.getSipResponseCode();
-            task.mSipReasonPhrase = pSipResponse.getReasonPhrase();
+            task.mSipResponseCode = responseCode;
+            task.mSipReasonPhrase = reasonPhrase;
         }
 
         handleCallback(task, getPublishState(), false);
@@ -1154,7 +1137,7 @@
 
                 if(isOnIWLAN()) {
                     // will check duplicated PUBLISH in requestPublication by invokePublish
-                    invokePublish(PresencePublication.PublishType.
+                    requestLocalPublish(PublishType.
                             PRES_PUBLISH_TRIGGER_FEATURE_AVAILABILITY_CHANGED);
                 }
             } else {
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublishTask.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublishTask.java
index 73ae4ca..24d1ada 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublishTask.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublishTask.java
@@ -28,23 +28,16 @@
 
 package com.android.service.ims.presence;
 
-import com.android.ims.internal.Logger;
-import com.android.ims.IRcsPresenceListener;
-
 /**
  * PresencePublishTask
  */
 public class PresencePublishTask extends PresenceTask{
-    /*
-     * The logger
-     */
-    private Logger logger = Logger.getLogger(this.getClass().getName());
 
     private long mCreateTimestamp = 0;
 
     private int mRetryCount = 0;
 
-    public PresencePublishTask(int taskId, int cmdId, IRcsPresenceListener listener,
+    public PresencePublishTask(int taskId, int cmdId, ContactCapabilityResponse listener,
             String[] contacts){
         super(taskId, cmdId, listener, contacts);
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublisher.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublisher.java
new file mode 100644
index 0000000..4b6eb89
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublisher.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     - Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     - Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     - Neither the name of The Android Open Source Project nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+package com.android.service.ims.presence;
+
+import android.telephony.ims.RcsContactUceCapability;
+
+public interface PresencePublisher {
+
+    /**
+     * Notify the stack of the last publish request result.
+     */
+    @PresenceBase.PresencePublishState int getPublisherState();
+
+    /**
+     * Request that the specified capabilities are published to the network.
+     * @param capabilities The capabilities to publish.
+     * @return the result of requesting that the capabilities are published.
+     */
+    int requestPublication(RcsContactUceCapability capabilities);
+
+    /**
+     * Notify the stack of the last publish or subscribe request result.
+     */
+    void updatePublisherState(@PresenceBase.PresencePublishState int publishState);
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java
index ccd4cae..9a7e6f9 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java
@@ -28,43 +28,24 @@
 
 package com.android.service.ims.presence;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.RcsContactUceCapability;
 import android.text.TextUtils;
 
-import com.android.ims.IRcsPresenceListener;
-import com.android.ims.RcsManager.ResultCode;
-import com.android.ims.RcsPresence;
-import com.android.ims.RcsPresence.PublishState;
-import com.android.ims.RcsPresenceInfo;
+import com.android.ims.ResultCode;
 import com.android.ims.internal.ContactNumberUtils;
 import com.android.ims.internal.Logger;
-import com.android.ims.internal.uce.presence.PresCmdStatus;
-import com.android.ims.internal.uce.presence.PresResInfo;
-import com.android.ims.internal.uce.presence.PresRlmiInfo;
-import com.android.ims.internal.uce.presence.PresSipResponse;
-import com.android.ims.internal.uce.presence.PresSubscriptionState;
-import com.android.ims.internal.uce.presence.PresTupleInfo;
 import com.android.service.ims.RcsSettingUtils;
-import com.android.service.ims.RcsStackAdaptor;
-import com.android.service.ims.RcsUtils;
 import com.android.service.ims.Task;
 import com.android.service.ims.TaskManager;
-
 import java.util.ArrayList;
 import java.util.List;
 
-public class PresenceSubscriber extends PresenceBase{
-    /*
-     * The logger
-     */
+public class PresenceSubscriber extends PresenceBase {
     private Logger logger = Logger.getLogger(this.getClass().getName());
 
-    private RcsStackAdaptor mRcsStackAdaptor = null;
-
-    private static PresenceSubscriber sSubsriber = null;
+    private SubscribePublisher mSubscriber;
 
     private String mAvailabilityRetryNumber = null;
 
@@ -74,11 +55,11 @@
     /*
      * Constructor
      */
-    public PresenceSubscriber(RcsStackAdaptor rcsStackAdaptor, Context context,
+    public PresenceSubscriber(SubscribePublisher subscriber, Context context,
             String[] configVolteProvisionErrorOnSubscribeResponse,
             String[] configRcsProvisionErrorOnSubscribeResponse){
-        mRcsStackAdaptor = rcsStackAdaptor;
-        mContext = context;
+        super(context);
+        mSubscriber = subscriber;
         mConfigVolteProvisionErrorOnSubscribeResponse
                 = configVolteProvisionErrorOnSubscribeResponse;
         mConfigRcsProvisionErrorOnSubscribeResponse = configRcsProvisionErrorOnSubscribeResponse;
@@ -111,10 +92,10 @@
     }
 
     public int requestCapability(List<String> contactsNumber,
-            IRcsPresenceListener listener){
+            ContactCapabilityResponse listener) {
 
-        int ret = mRcsStackAdaptor.checkStackAndPublish();
-        if(ret < ResultCode.SUCCESS){
+        int ret = mSubscriber.getStackStatusForCapabilityRequest();
+        if (ret < ResultCode.SUCCESS) {
             logger.error("requestCapability ret=" + ret);
             return ret;
         }
@@ -132,10 +113,10 @@
         }
 
         String[] formatedNumbers = ContactNumberUtils.getDefault().format(contactsNumber);
-        ret = ContactNumberUtils.getDefault().validate(formatedNumbers);
-        if(ret != ContactNumberUtils.NUMBER_VALID){
-            logger.error("requestCapability ret=" + ret);
-            return ret;
+        int formatResult = ContactNumberUtils.getDefault().validate(formatedNumbers);
+        if (formatResult != ContactNumberUtils.NUMBER_VALID) {
+            logger.error("requestCapability formatResult=" + formatResult);
+            return ResultCode.SUBSCRIBE_INVALID_PARAM;
         }
 
         String[] formatedContacts = new String[formatedNumbers.length];
@@ -153,13 +134,13 @@
         timeout += 3000;
 
         logger.print("add to task manager, formatedNumbers=" +
-                RcsUtils.toContactString(formatedNumbers));
+                PresenceUtils.toContactString(formatedNumbers));
         int taskId = TaskManager.getDefault().addCapabilityTask(mContext, formatedNumbers,
                 listener, timeout);
         logger.print("taskId=" + taskId);
 
-        ret = mRcsStackAdaptor.requestCapability(formatedContacts, taskId);
-        if(ret < ResultCode.SUCCESS){
+        ret = mSubscriber.requestCapability(formatedContacts, taskId);
+        if(ret < ResultCode.SUCCESS) {
             logger.error("requestCapability ret=" + ret + " remove taskId=" + taskId);
             TaskManager.getDefault().removeTask(taskId);
         }
@@ -169,8 +150,8 @@
         return  ret;
     }
 
-    public int requestAvailability(String contactNumber, IRcsPresenceListener listener,
-            boolean forceToNetwork){
+    public int requestAvailability(String contactNumber, ContactCapabilityResponse listener,
+            boolean forceToNetwork) {
 
         String formatedContact = ContactNumberUtils.getDefault().format(contactNumber);
         int ret = ContactNumberUtils.getDefault().validate(formatedContact);
@@ -212,8 +193,8 @@
             return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
         }
 
-        ret = mRcsStackAdaptor.checkStackAndPublish();
-        if(ret < ResultCode.SUCCESS){
+        ret = mSubscriber.getStackStatusForCapabilityRequest();
+        if (ret < ResultCode.SUCCESS) {
             logger.error("requestAvailability=" + ret);
             return ret;
         }
@@ -226,19 +207,18 @@
 
         logger.print("addAvailabilityTask formatedContact=" + formatedContact);
 
-        ret = mRcsStackAdaptor.requestAvailability(formatedContact, taskId);
-        if(ret < ResultCode.SUCCESS){
+        ret = mSubscriber.requestAvailability(formatedContact, taskId);
+        if (ret < ResultCode.SUCCESS) {
             logger.error("requestAvailability ret=" + ret + " remove taskId=" + taskId);
             TaskManager.getDefault().removeTask(taskId);
         }
 
         ret = taskId;
 
-        return  ret;
+        return ret;
     }
 
-    private int translateResponse403(PresSipResponse pSipResponse){
-        String reasonPhrase = pSipResponse.getReasonPhrase();
+    private int translateResponse403(String reasonPhrase){
         if(reasonPhrase == null){
             // No retry. The PS provisioning has not occurred correctly. UX Decision to show errror.
             return ResultCode.SUBSCRIBE_GENIRIC_FAILURE;
@@ -260,26 +240,24 @@
         return ResultCode.SUBSCRIBE_FORBIDDEN;
     }
 
-    private int translateResponseCode(PresSipResponse pSipResponse){
+    private int translateResponseCode(int responseCode, String reasonPhrase) {
         // pSipResponse should not be null.
-        logger.debug("translateResponseCode getSipResponseCode=" +
-                pSipResponse.getSipResponseCode());
+        logger.debug("translateResponseCode getSipResponseCode=" +responseCode);
         int ret = ResultCode.SUBSCRIBE_GENIRIC_FAILURE;
 
-        int sipCode = pSipResponse.getSipResponseCode();
-        if(sipCode < 100 || sipCode > 699){
-            logger.debug("internal error code sipCode=" + sipCode);
+        if(responseCode < 100 || responseCode > 699){
+            logger.debug("internal error code sipCode=" + responseCode);
             ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; //it is internal issue. ignore it.
             return ret;
         }
 
-        switch(sipCode){
+        switch(responseCode){
             case 200:
                 ret = ResultCode.SUCCESS;
                 break;
 
             case 403:
-                ret = translateResponse403(pSipResponse);
+                ret = translateResponse403(reasonPhrase);
                 break;
 
             case 404:
@@ -340,32 +318,26 @@
         return ret;
     }
 
-    public void handleSipResponse(PresSipResponse pSipResponse){
-        if(pSipResponse == null){
-            logger.debug("handleSipResponse pSipResponse = null");
-            return;
-        }
-
-        int sipCode = pSipResponse.getSipResponseCode();
-        String phrase = pSipResponse.getReasonPhrase();
-        if(isInConfigList(sipCode, phrase, mConfigVolteProvisionErrorOnSubscribeResponse)) {
-            logger.print("volte provision sipCode=" + sipCode + " phrase=" + phrase);
-            mRcsStackAdaptor.setPublishState(PublishState.PUBLISH_STATE_VOLTE_PROVISION_ERROR);
+    public void onSipResponse(int requestId, int responseCode, String reasonPhrase) {
+        if(isInConfigList(responseCode, reasonPhrase,
+                mConfigVolteProvisionErrorOnSubscribeResponse)) {
+            logger.print("volte provision sipCode=" + responseCode + " phrase=" + reasonPhrase);
+            mSubscriber.updatePublisherState(PUBLISH_STATE_VOLTE_PROVISION_ERROR);
 
             notifyDm();
-        } else if(isInConfigList(sipCode, phrase, mConfigRcsProvisionErrorOnSubscribeResponse)) {
-            logger.print("rcs provision sipCode=" + sipCode + " phrase=" + phrase);
-            mRcsStackAdaptor.setPublishState(PublishState.PUBLISH_STATE_RCS_PROVISION_ERROR);
+        } else if(isInConfigList(responseCode, reasonPhrase,
+                mConfigRcsProvisionErrorOnSubscribeResponse)) {
+            logger.print("rcs proRcsPresence.vision sipCode=" + responseCode + " phrase=" + reasonPhrase);
+            mSubscriber.updatePublisherState(PUBLISH_STATE_RCS_PROVISION_ERROR);
         }
 
-        int errorCode = translateResponseCode(pSipResponse);
+        int errorCode = translateResponseCode(responseCode, reasonPhrase);
         logger.print("handleSipResponse errorCode=" + errorCode);
 
         if(errorCode == ResultCode.SUBSCRIBE_NOT_REGISTERED){
             logger.debug("setPublishState to unknown" +
                    " for subscribe error 403 not registered");
-            mRcsStackAdaptor.setPublishState(
-                   PublishState.PUBLISH_STATE_OTHER_ERROR);
+            mSubscriber.updatePublisherState(PUBLISH_STATE_OTHER_ERROR);
         }
 
         if(errorCode == ResultCode.SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE) {
@@ -377,12 +349,11 @@
         }
 
         // Suppose the request ID had been set when IQPresListener_CMDStatus
-        Task task = TaskManager.getDefault().getTaskByRequestId(
-                pSipResponse.getRequestId());
+        Task task = TaskManager.getDefault().getTaskByRequestId(requestId);
         logger.debug("handleSipResponse task=" + task);
         if(task != null){
-            task.mSipResponseCode = sipCode;
-            task.mSipReasonPhrase = phrase;
+            task.mSipResponseCode = responseCode;
+            task.mSipReasonPhrase = reasonPhrase;
             TaskManager.getDefault().putTask(task.mTaskId, task);
         }
 
@@ -400,27 +371,19 @@
         if(errorCode == ResultCode.SUBSCRIBE_NOT_FOUND &&
                 task != null && ((PresenceTask)task).mContacts != null) {
             String[] contacts = ((PresenceTask)task).mContacts;
-            ArrayList<RcsPresenceInfo> presenceInfoList = new ArrayList<RcsPresenceInfo>();
+            ArrayList<RcsContactUceCapability> contactCapabilities = new ArrayList<>();
 
             for(int i=0; i<contacts.length; i++){
                 if(TextUtils.isEmpty(contacts[i])){
                     continue;
                 }
-
-                RcsPresenceInfo presenceInfo = new RcsPresenceInfo(contacts[i],
-                        RcsPresenceInfo.VolteStatus.VOLTE_DISABLED,
-                        RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis(),
-                        RcsPresenceInfo.ServiceState.OFFLINE, null, System.currentTimeMillis());
-                presenceInfoList.add(presenceInfo);
+                logger.debug("onSipResponse: contact= " + contacts[i] + ", not found.");
+                // Build contacts with no capabilities.
+                contactCapabilities.add(new RcsContactUceCapability.Builder(
+                        PresenceUtils.convertContactNumber(contacts[i])).build());
             }
+            handleCapabilityUpdate(task, contactCapabilities, true);
 
-            // Notify presence information changed.
-            logger.debug("notify presence changed for 404 error");
-            Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
-            intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
-                    presenceInfoList);
-            intent.putExtra("updateLastTimestamp", true);
-            launchPersistService(intent);
         } else if(errorCode == ResultCode.SUBSCRIBE_GENIRIC_FAILURE) {
             updateAvailabilityToUnknown(task);
         }
@@ -428,11 +391,13 @@
         handleCallback(task, errorCode, false);
     }
 
-    private void launchPersistService(Intent intent) {
-        ComponentName component = new ComponentName("com.android.service.ims.presence",
-                "com.android.service.ims.presence.PersistService");
-        intent.setComponent(component);
-        mContext.startService(intent);
+    private void handleCapabilityUpdate(Task task, List<RcsContactUceCapability> capabilities,
+            boolean updateLastTimestamp) {
+        if (task == null || task.mListener == null ) {
+            logger.warn("handleCapabilityUpdate, invalid listener!");
+            return;
+        }
+        task.mListener.onCapabilitiesUpdated(capabilities, updateLastTimestamp);
     }
 
     public void retryToGetAvailability() {
@@ -444,104 +409,52 @@
         mAvailabilityRetryNumber = null;
     }
 
-    public void updatePresence(String pPresentityUri, PresTupleInfo[] pTupleInfo){
+    public void updatePresence(RcsContactUceCapability capabilities) {
         if(mContext == null){
             logger.error("updatePresence mContext == null");
             return;
         }
 
-        RcsPresenceInfo rcsPresenceInfo = PresenceInfoParser.getPresenceInfoFromTuple(
-                pPresentityUri, pTupleInfo);
-        if(rcsPresenceInfo == null || TextUtils.isEmpty(
-                rcsPresenceInfo.getContactNumber())){
-            logger.error("rcsPresenceInfo is null or " +
-                    "TextUtils.isEmpty(rcsPresenceInfo.getContactNumber()");
-            return;
-        }
+        ArrayList<RcsContactUceCapability> presenceInfos = new ArrayList<>();
+        presenceInfos.add(capabilities);
 
-        ArrayList<RcsPresenceInfo> rcsPresenceInfoList = new ArrayList<RcsPresenceInfo>();
-        rcsPresenceInfoList.add(rcsPresenceInfo);
-
+        String contactNumber = capabilities.getContactUri().getSchemeSpecificPart();
         // For single contact number we got 1 NOTIFY only. So regard it as terminated.
-        TaskManager.getDefault().onTerminated(rcsPresenceInfo.getContactNumber());
+        TaskManager.getDefault().onTerminated(contactNumber);
 
         PresenceAvailabilityTask availabilityTask = TaskManager.getDefault().
-                getAvailabilityTaskByContact(rcsPresenceInfo.getContactNumber());
-        if(availabilityTask != null){
+                getAvailabilityTaskByContact(contactNumber);
+        if (availabilityTask != null) {
             availabilityTask.updateNotifyTimestamp();
         }
-
-        // Notify presence information changed.
-        Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
-        intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
-                rcsPresenceInfoList);
-        intent.putExtra("updateLastTimestamp", true);
-        launchPersistService(intent);
+        Task task = TaskManager.getDefault().getTaskForSingleContactQuery(contactNumber);
+        handleCapabilityUpdate(task, presenceInfos, true);
     }
 
-    public void updatePresences(PresRlmiInfo pRlmiInfo, PresResInfo[] pRcsPresenceInfo) {
+    public void updatePresences(int requestId, List<RcsContactUceCapability> contactsCapabilities,
+            boolean isTerminated, String terminatedReason) {
         if(mContext == null){
             logger.error("updatePresences: mContext == null");
             return;
         }
 
-        RcsPresenceInfo[] rcsPresenceInfos = PresenceInfoParser.
-                getPresenceInfosFromPresenceRes(pRlmiInfo, pRcsPresenceInfo);
-        if(rcsPresenceInfos == null){
-            logger.error("updatePresences: rcsPresenceInfos == null");
-            return;
+        if (isTerminated) {
+            TaskManager.getDefault().onTerminated(requestId, terminatedReason);
         }
 
-        ArrayList<RcsPresenceInfo> rcsPresenceInfoList = new ArrayList<RcsPresenceInfo>();
-
-        for (int i=0; i < rcsPresenceInfos.length; i++) {
-            RcsPresenceInfo rcsPresenceInfo = rcsPresenceInfos[i];
-            if(rcsPresenceInfo != null && !TextUtils.isEmpty(rcsPresenceInfo.getContactNumber())){
-                logger.debug("rcsPresenceInfo=" + rcsPresenceInfo);
-
-                rcsPresenceInfoList.add(rcsPresenceInfo);
-            }
-        }
-
-        boolean isTerminated = false;
-        if(pRlmiInfo.getPresSubscriptionState() != null){
-            if(pRlmiInfo.getPresSubscriptionState().getPresSubscriptionStateValue() ==
-                    PresSubscriptionState.UCE_PRES_SUBSCRIPTION_STATE_TERMINATED){
-                isTerminated = true;
-            }
-        }
-
-        if(isTerminated){
-            TaskManager.getDefault().onTerminated(pRlmiInfo.getRequestId(),
-                    pRlmiInfo.getSubscriptionTerminatedReason());
-        }
-
-        if (rcsPresenceInfoList.size() > 0) {
-            // Notify presence changed
-            Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
-            intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
-                    rcsPresenceInfoList);
-            logger.debug("broadcast ACTION_PRESENCE_CHANGED, rcsPresenceInfo=" +
-                    rcsPresenceInfoList);
-            intent.putExtra("updateLastTimestamp", true);
-            launchPersistService(intent);
+        Task task = TaskManager.getDefault().getTaskByRequestId(requestId);
+        if (contactsCapabilities.size() > 0 || task != null) {
+            handleCapabilityUpdate(task, contactsCapabilities, true);
         }
     }
 
-    public void handleCmdStatus(PresCmdStatus pCmdStatus){
-        if(pCmdStatus == null){
-            logger.error("handleCallbackForCmdStatus pCmdStatus=null");
-            return;
-        }
-
-
-        Task taskTmp = TaskManager.getDefault().getTask(pCmdStatus.getUserData());
-        int resultCode = RcsUtils.statusCodeToResultCode(pCmdStatus.getStatus().getStatusCode());
+    public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) {
+        Task taskTmp = TaskManager.getDefault().getTask(taskId);
         logger.print("handleCmdStatus resultCode=" + resultCode);
         PresenceTask task = null;
         if(taskTmp != null && (taskTmp instanceof PresenceTask)){
             task = (PresenceTask)taskTmp;
-            task.mSipRequestId = pCmdStatus.getRequestId();
+            task.mSipRequestId = requestId;
             task.mCmdStatus = resultCode;
             TaskManager.getDefault().putTask(task.mTaskId, task);
 
@@ -579,30 +492,18 @@
             return;
         }
 
-        ArrayList<RcsPresenceInfo> presenceInfoList = new ArrayList<RcsPresenceInfo>();
-        for(int i = 0; i< task.mContacts.length; i++){
-            if(task.mContacts[i] == null || task.mContacts[i].length() == 0){
+        ArrayList<RcsContactUceCapability> presenceInfoList = new ArrayList<>();
+        for (int i = 0; i< task.mContacts.length; i++) {
+            if(TextUtils.isEmpty(task.mContacts[i])){
                 continue;
             }
-
-            RcsPresenceInfo presenceInfo = new RcsPresenceInfo(
-                PresenceInfoParser.getPhoneFromUri(task.mContacts[i]),
-                        RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN,
-                        RcsPresenceInfo.ServiceState.UNKNOWN, null, System.currentTimeMillis(),
-                        RcsPresenceInfo.ServiceState.UNKNOWN, null, System.currentTimeMillis());
-            presenceInfoList.add(presenceInfo);
+            // Add each contacts with no capabilities.
+            presenceInfoList.add(new RcsContactUceCapability.Builder(
+                    PresenceUtils.convertContactNumber(task.mContacts[i])).build());
         }
 
         if(presenceInfoList.size() > 0) {
-             // Notify presence information changed.
-             logger.debug("notify presence changed for cmd error");
-             Intent intent = new Intent(RcsPresence.ACTION_PRESENCE_CHANGED);
-             intent.putParcelableArrayListExtra(RcsPresence.EXTRA_PRESENCE_INFO_LIST,
-                     presenceInfoList);
-
-             // don't update timestamp so it can be subscribed soon.
-             intent.putExtra("updateLastTimestamp", false);
-             launchPersistService(intent);
+             handleCapabilityUpdate(task, presenceInfoList, false);
         }
     }
 }
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceTask.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceTask.java
index ed8afdf..5465e10 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceTask.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceTask.java
@@ -28,34 +28,28 @@
 
 package com.android.service.ims.presence;
 
-import com.android.ims.IRcsPresenceListener;
-import com.android.ims.internal.Logger;
-import com.android.service.ims.RcsUtils;
 import com.android.service.ims.Task;
 
 /**
  * PresenceTask
  */
-public class PresenceTask extends Task{
-    /*
-     * The logger
-     */
-    private Logger logger = Logger.getLogger(this.getClass().getName());
+public class PresenceTask extends Task {
 
     // filled before send the request to stack
     public String[] mContacts;
 
-    public PresenceTask(int taskId, int cmdId, IRcsPresenceListener listener, String[] contacts){
+    public PresenceTask(int taskId, int cmdId, ContactCapabilityResponse listener,
+            String[] contacts){
         super(taskId, cmdId, listener);
 
         mContacts = contacts;
         mListener = listener;
     }
 
-    public String toString(){
+    public String toString() {
         return "PresenceTask: mTaskId=" + mTaskId +
                 " mCmdId=" + mCmdId +
-                " mContacts=" + RcsUtils.toContactString(mContacts) +
+                " mContacts=" + PresenceUtils.toContactString(mContacts) +
                 " mCmdStatus=" + mCmdStatus +
                 " mSipRequestId=" + mSipRequestId +
                 " mSipResponseCode=" + mSipResponseCode +
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceUtils.java b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceUtils.java
new file mode 100644
index 0000000..088f52b
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     - Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     - Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     - Neither the name of The Android Open Source Project nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+package com.android.service.ims.presence;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+public class PresenceUtils {
+
+    public static final String LOG_TAG_PREFIX = "rcs_lib";
+
+    private static final String TEL_SCHEME = "tel:";
+
+    public static String toContactString(String[] contacts) {
+        if(contacts == null) {
+            return null;
+        }
+
+        String result = "";
+        for(int i=0; i<contacts.length; i++) {
+            result += contacts[i];
+            if(i != contacts.length -1) {
+                result += ";";
+            }
+        }
+
+        return result;
+    }
+
+    public static Uri convertContactNumber(String number) {
+        if (TextUtils.isEmpty(number)) return null;
+        Uri possibleNumber = Uri.parse(number);
+        // already in the  correct format
+        if (TEL_SCHEME.equals(possibleNumber.getScheme())) {
+            return possibleNumber;
+        }
+        // need to append tel scheme
+        return Uri.fromParts(TEL_SCHEME, number, null);
+    }
+
+    public static String getNumber(Uri numberUri) {
+        if (numberUri == null) return null;
+        return numberUri.getSchemeSpecificPart();
+    }
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/SubscribePublisher.java b/rcs/rcsservice/src/com/android/service/ims/presence/SubscribePublisher.java
new file mode 100644
index 0000000..b3eb46d
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/SubscribePublisher.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     - Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     - Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     - Neither the name of The Android Open Source Project nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+package com.android.service.ims.presence;
+
+public interface SubscribePublisher {
+
+    /**
+     * Request the Capabilities for the requested contacts associated with a taskId.
+     */
+    int requestCapability(String[] formatedContacts, int taskId);
+
+    /**
+     * Request the Capabilities for the requested contacts associated with a taskId.
+     */
+    int requestAvailability(String formattedContact, int taskId);
+
+    /**
+     * @return the current state of the RCS stack.
+     */
+    int getStackStatusForCapabilityRequest();
+
+    /**==
+     * Notify the stack of the last publish or subscribe request result.
+     */
+    void updatePublisherState(@PresenceBase.PresencePublishState int publishState);
+
+}