[RCS] Implement ImsService API in RcsService

Test: live test
Bug: 139261235
Change-Id: If497367f0f1accce58f502d608f9507654a92fe3
diff --git a/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java b/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
index 644d567..c198bb3 100644
--- a/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
+++ b/rcs/rcsmanager/src/java/com/android/ims/RcsManager.java
@@ -239,6 +239,7 @@
     private int mSubId;
     private IRcsService mRcsService = null;
     private RcsServiceDeathRecipient mDeathRecipient = new RcsServiceDeathRecipient();
+    private boolean mPresenceEnabledByFramework = true;
 
     // Interface for presence
     // TODO: Could add other RCS service such RcsChat, RcsFt later.
@@ -290,8 +291,9 @@
             Rlog.e(TAG, "isRcsServiceAvailable RcsException", e);
         }
 
-        if (DBG) Rlog.d(TAG, "isRcsServiceAvailable ret =" + ret);
-        return ret;
+        if (DBG) Rlog.d(TAG, "isRcsServiceAvailable ret =" + ret + " mPresenceEnabledByFramework ="
+                + mPresenceEnabledByFramework);
+        return ret && mPresenceEnabledByFramework;
     }
 
     /**
@@ -381,4 +383,8 @@
             }
         }
     }
+
+    public void setPresenceEnabledByFramework(boolean presenceEnabledByFramework) {
+        mPresenceEnabledByFramework = presenceEnabledByFramework;
+    }
 }
diff --git a/rcs/rcsservice/Android.bp b/rcs/rcsservice/Android.bp
index 24153a5..547ae5e 100644
--- a/rcs/rcsservice/Android.bp
+++ b/rcs/rcsservice/Android.bp
@@ -38,6 +38,9 @@
         "src/com/android/service/ims/RcsUtils.java",
         "src/com/android/service/ims/Task.java",
         "src/com/android/service/ims/TaskManager.java",
+        "src/com/android/service/ims/RcsFeatureImpl.java",
+        "src/com/android/service/ims/RcsPresenceExchangeImpl.java",
+        "src/com/android/service/ims/ImsServiceImpl.java",
         // Move the following to the app once the dependencies have been decoupled.
         "src/com/android/service/ims/RcsStackAdaptor.java"
     ],
diff --git a/rcs/rcsservice/AndroidManifest.xml b/rcs/rcsservice/AndroidManifest.xml
index 9b9de40..0f31c7b 100644
--- a/rcs/rcsservice/AndroidManifest.xml
+++ b/rcs/rcsservice/AndroidManifest.xml
@@ -56,12 +56,22 @@
     <uses-permission android:name="com.android.rcs.eab.permission.READ_WRITE_EAB"/>
 
     <application android:name="RcsServiceApp" android:persistent="true"
-        android:process="com.android.ims.rcsservice">
+        android:process="com.android.ims.rcsservice" android:directBootAware="true">
         <service android:name="com.android.service.ims.RcsService"
             android:exported="true"
             android:enabled="true"
             android:permission="com.android.ims.permission.PRESENCE_ACCESS">
         </service>
+        <service
+            android:name="com.android.service.ims.ImsServiceImpl"
+            android:persistent="true"
+            android:permission="android.permission.BIND_IMS_SERVICE" >
+            <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+            <intent-filter>
+                <action android:name="android.telephony.ims.ImsService" />
+            </intent-filter>
+        </service>
+
 
         <receiver android:name="com.android.service.ims.DeviceShutdown"
             androidprv:systemUserOnly="true">
diff --git a/rcs/rcsservice/src/com/android/service/ims/ImsServiceImpl.java b/rcs/rcsservice/src/com/android/service/ims/ImsServiceImpl.java
new file mode 100644
index 0000000..8f4948c
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/ImsServiceImpl.java
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 copyright holder 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+public class ImsServiceImpl extends ImsService {
+    public static final String TAG = "ImsServiceImpl";
+    private static final int INVALID_SLOT_ID = -1;
+    private static final int UNINITIALIZED_VALUE = -1;
+    private int mNumPhonesCache = UNINITIALIZED_VALUE;
+    private RcsFeature mRcsFeature[];
+    protected final Object mLock = new Object();
+
+    private int getNumSlots() {
+        if (mNumPhonesCache == UNINITIALIZED_VALUE) {
+            mNumPhonesCache = ((TelephonyManager) getSystemService(
+                    Context.TELEPHONY_SERVICE)).getPhoneCount();
+        }
+        return mNumPhonesCache;
+    }
+
+    private void setup() {
+        final int numSlots = getNumSlots();
+        mRcsFeature = new RcsFeatureImpl[numSlots];
+        for (int i = 0; i < numSlots; i++) {
+            mRcsFeature[i] = new RcsFeatureImpl(this, i);
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "ImsService created!");
+        setup();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (android.telephony.ims.ImsService.SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.d(TAG, "Returning mImsServiceController for ImsService binding");
+            return mImsServiceController;
+        }
+        Log.e(TAG, "Invalid Intent action in onBind: " + intent.getAction());
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "ImsService destroyed!");
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified slot.
+     *
+     * @param slotId The slot ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+     * is not supported.
+     */
+    @Override
+    public RcsFeature createRcsFeature(int slotId) {
+        Log.d(TAG, "createRcsFeature :: slotId=" + slotId + " numSlots=" + mNumPhonesCache);
+        if (slotId > INVALID_SLOT_ID && slotId < getNumSlots()) {
+            return mRcsFeature[slotId];
+        }
+        Log.e(TAG, "createRcsFeature :: Invalid slotId " + slotId);
+        return null;
+    }
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsFeatureImpl.java b/rcs/rcsservice/src/com/android/service/ims/RcsFeatureImpl.java
new file mode 100644
index 0000000..53c44ad
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsFeatureImpl.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 copyright holder 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.telephony.ims.stub.RcsSipOptionsImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsManager;
+import com.android.internal.telephony.TelephonyIntents;
+
+import java.util.List;
+
+public class RcsFeatureImpl extends RcsFeature {
+
+    private static final String TAG = "RcsFeatureImpl";
+
+    private static final String RCS_PACKAGE = "com.android.service.ims";
+    private static final String RCS_CLASS = "com.android.service.ims.RcsService";
+    private Context mContext;
+    private Handler mFeatureCallbackHandler;
+    private HandlerThread mFeatureHandlerThread;
+    private RcsImsCapabilities mRcsImsCapabilities;
+    private int mPhoneId;
+    private int mSubId;
+    private int mDefaultVoiceSubId;
+    private SubscriptionManager mSubscriptionManager;
+    private SubscriptionChangedListener mSubscriptionListener;
+    private RcsPresenceExchangeImplBase mRcsPresenceExchangeBase;
+    private RcsStackAdaptor mRcsStackAdaptor;
+
+    // Used to synchronize mSubId and mDefaultVoiceSubId
+    private Object mToken = new Object();
+
+
+    public RcsFeatureImpl(Context context, int phoneId) {
+        mContext = context;
+        mPhoneId = phoneId;
+        mFeatureHandlerThread = new HandlerThread(this + "FeatureHandlerThread");
+        mFeatureHandlerThread.start();
+        mFeatureCallbackHandler = new Handler(mFeatureHandlerThread.getLooper());
+        mDefaultVoiceSubId = SubscriptionManager.getDefaultSubscriptionId();
+        mRcsPresenceExchangeBase = new RcsPresenceExchangeImpl(mContext);
+        mRcsStackAdaptor = RcsStackAdaptor.getInstance(mContext);
+        mRcsImsCapabilities = new RcsImsCapabilities(RcsImsCapabilities.CAPABILITY_TYPE_NONE);
+        mSubscriptionManager = (SubscriptionManager) mContext.getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mSubId = getSubId();
+        mSubscriptionListener = new SubscriptionChangedListener();
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
+
+        registerVoiceSubscriptionChange();
+        registerRcsStateReceiver();
+        initFeatureState();
+        logd("RcsFeatureImpl constructor mSubId:" + mSubId + ", "
+                + "mDefaultVoiceSubId:" + mDefaultVoiceSubId);
+
+    }
+
+    private int getSubId() {
+        int subId[] = mSubscriptionManager.getSubscriptionIds(mPhoneId);
+        return subId != null ? subId[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    private void registerVoiceSubscriptionChange() {
+        IntentFilter intentFilter = new IntentFilter
+                (TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+        mContext.registerReceiver(mVoiceSubscriptionReceiver, intentFilter);
+    }
+
+    private void initFeatureState() {
+        // In DSDS environment, RcsFeatureImpl is only available for default voice subId.
+        if (isDefaultVoiceSubId()) {
+            logd("set STATE_READY");
+            setFeatureState(ImsFeature.STATE_READY);
+        } else {
+            logd("set STATE_UNAVAILABLE");
+            setFeatureState(ImsFeature.STATE_UNAVAILABLE);
+        }
+    }
+
+    private boolean isDefaultVoiceSubId() {
+        synchronized (mToken) {
+            return SubscriptionManager.isValidSubscriptionId(mSubId) &&
+                    SubscriptionManager.isValidSubscriptionId(mDefaultVoiceSubId) &&
+                    mSubscriptionManager.isActiveSubscriptionId(mSubId) &&
+                    mSubscriptionManager.isActiveSubscriptionId(mDefaultVoiceSubId) &&
+                    mSubId == mDefaultVoiceSubId;
+        }
+    }
+
+    private void startRcsService() {
+        ComponentName comp = new ComponentName(RCS_PACKAGE, RCS_CLASS);
+        ComponentName service = mContext.startService(new Intent().setComponent(comp));
+        if (service == null) {
+            Log.e(TAG, "Could Not Start Service " + comp.toString());
+        } else {
+            Log.d(TAG, comp.toString() + " started Successfully");
+        }
+    }
+
+    @Override
+    public void onFeatureReady() {
+        logd("onFeatureReady");
+        mRcsPresenceExchangeBase.initialize(this);
+        mRcsStackAdaptor.setRcsPresenceExchangeImplBase(mRcsPresenceExchangeBase);
+        mRcsStackAdaptor.init();
+        startRcsService();
+    }
+
+    @Override
+    public void onFeatureRemoved() {
+        logd("onFeatureRemoved");
+        mContext.unregisterReceiver(mRcsStateReceiver);
+        mContext.unregisterReceiver(mVoiceSubscriptionReceiver);
+        mRcsStackAdaptor.setRcsPresenceExchangeImplBase(null);
+    }
+
+    private void registerRcsStateReceiver() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_AVAILABLE);
+        intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE);
+        intentFilter.addAction(RcsManager.ACTION_RCS_SERVICE_DIED);
+        mContext.registerReceiver(mRcsStateReceiver, intentFilter);
+    }
+
+    /**
+     * Retrieve the implementation of UCE presence for this {@link RcsFeature}.
+     * Will only be requested by the framework if presence exchang is configured as capable during
+     * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
+     * exchange if it is supported by the device.
+     * @hide
+     */
+    @Override
+    public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+        return mRcsPresenceExchangeBase;
+    }
+
+    /**
+     * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}.
+     * <p>
+     * Will only be requested by the framework if capability exchange via SIP OPTIONS is
+     * configured as capable during a
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
+     * it is supported by the device.
+     * @hide
+     */
+    @Override
+    public RcsSipOptionsImplBase getOptionsExchangeImpl() {
+        return null;
+    }
+
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        logd("changeEnabledCapabilities");
+        if (request == null || c == null) {
+            loge("changeEnabledCapabilities :: Invalid argument(s).");
+            return;
+        }
+
+        List<CapabilityPair> capsToEnable = request.getCapabilitiesToEnable();
+        List<CapabilityPair> capsToDisable = request.getCapabilitiesToDisable();
+        if (capsToEnable.isEmpty() && capsToDisable.isEmpty()) {
+            loge("changeEnabledCapabilities :: No CapabilityPair objects to process!");
+            return;
+        }
+
+        for (CapabilityPair cp : capsToEnable) {
+            if (cp.getCapability() == RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE) {
+                // SIP OPTION is not supported
+                callBackError(cp, c);
+            } else if (cp.getCapability() == RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE) {
+                mRcsImsCapabilities.addCapabilities(
+                        RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+                enableRcsPresence();
+            }
+        }
+        for (CapabilityPair cp : capsToDisable) {
+            if (cp.getCapability() == RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE) {
+                // SIP OPTION is not supported
+                callBackError(cp, c);
+            } else if (cp.getCapability() == RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE) {
+                mRcsImsCapabilities.removeCapabilities(RcsImsCapabilities
+                        .CAPABILITY_TYPE_PRESENCE_UCE);
+                disableRcsPresence();
+            }
+        }
+    }
+
+    private void callBackError(CapabilityPair cp, CapabilityCallbackProxy c) {
+        final Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                c.onChangeCapabilityConfigurationError(cp.getCapability(), cp.getRadioTech(),
+                        ImsFeature.CAPABILITY_ERROR_GENERIC);
+            }
+        };
+        if (mFeatureCallbackHandler != null) {
+            mFeatureCallbackHandler.post(r);
+        }
+    }
+
+    private void enableRcsPresence() {
+        RcsManager rcsManager = getRcsManager();
+        if (rcsManager != null) {
+            rcsManager.setPresenceEnabledByFramework(true);
+        }
+        if (rcsManager != null && rcsManager.isRcsServiceAvailable()) {
+            Intent intent = new Intent(RcsManager.ACTION_RCS_SERVICE_AVAILABLE);
+            mContext.sendBroadcast(intent,
+                    "com.android.ims.rcs.permission.STATUS_CHANGED");
+        }
+    }
+
+    private void disableRcsPresence() {
+        RcsManager rcsManager = getRcsManager();
+        if (rcsManager != null) {
+            rcsManager.setPresenceEnabledByFramework(false);
+        }
+        Intent intent = new Intent(RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE);
+        mContext.sendBroadcast(intent,
+                "com.android.ims.rcs.permission.STATUS_CHANGED");
+    }
+
+    private RcsManager getRcsManager() {
+        if (isDefaultVoiceSubId()) {
+            return RcsManager.getInstance(mContext, mSubId);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean queryCapabilityConfiguration(
+            @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        logi("queryCapabilityConfiguration :: capability=" + capability
+                + " radioTech=" + radioTech);
+        return mRcsImsCapabilities != null && mRcsImsCapabilities.isCapable(capability);
+    }
+
+    private final BroadcastReceiver mRcsStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            RcsFeatureImpl.this.logi("onReceive(), intent: " + intent + ", context: " + context);
+            String action = intent.getAction();
+            RcsManager rcsManager = getRcsManager();
+            if (RcsManager.ACTION_RCS_SERVICE_AVAILABLE.equals(action)) {
+                // Check if both enableRcsPresence() and StackListener's serviceAvailable() are
+                // executed.
+                if (rcsManager != null && rcsManager.isRcsServiceAvailable()) {
+                    notifyCapabilitiesStatusChanged(new RcsImsCapabilities(RcsImsCapabilities
+                            .CAPABILITY_TYPE_PRESENCE_UCE));
+                }
+            } else if (RcsManager.ACTION_RCS_SERVICE_UNAVAILABLE.equals(action) ||
+                    RcsManager.ACTION_RCS_SERVICE_DIED.equals(action)) {
+                // Triggered from disabledRcsPresence() or StackListener's serviceUnAvailable().
+                if (rcsManager != null) {
+                    notifyCapabilitiesStatusChanged(new RcsImsCapabilities(RcsImsCapabilities
+                            .CAPABILITY_TYPE_NONE));
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mVoiceSubscriptionReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED.equals(action)) {
+                int newVoiceSubId = SubscriptionManager.getDefaultSubscriptionId();
+                synchronized (mToken) {
+                    if (newVoiceSubId != mDefaultVoiceSubId) {
+                        mDefaultVoiceSubId = newVoiceSubId;
+                        mSubId = getSubId();
+                        logd("mVoiceSubscriptionReceiver mSubId:" + mSubId + " "
+                                + "mDefaultVoiceSubId:" + mDefaultVoiceSubId);
+                        processFeatureChange();
+                    }
+                }
+            }
+        }
+    };
+
+    class SubscriptionChangedListener extends SubscriptionManager.OnSubscriptionsChangedListener {
+        @Override
+        public void onSubscriptionsChanged() {
+            int newSubId = getSubId();
+            synchronized (mToken) {
+                if (newSubId != mSubId) {
+                    mSubId = newSubId;
+                    mDefaultVoiceSubId = SubscriptionManager.getDefaultSubscriptionId();
+                    logd("onSubscriptionsChanged mSubId:" + mSubId + " mDefaultVoiceSubId:" +
+                            mDefaultVoiceSubId);
+                    processFeatureChange();
+                }
+            }
+        }
+    }
+
+    private void processFeatureChange() {
+        if (isDefaultVoiceSubId()) {
+            logd("set STATE_READY");
+            setFeatureState(ImsFeature.STATE_READY);
+        } else {
+            logd("set STATE_UNAVAILABLE");
+            setFeatureState(ImsFeature.STATE_UNAVAILABLE);
+        }
+    }
+
+    private void logd(String s) {
+        Log.d(TAG, "[" + mPhoneId + "][" + mSubId + "] " + s);
+    }
+
+    private void logi(String s) {
+        Log.i(TAG, "[" + mPhoneId + "][" + mSubId + "] " + s);
+    }
+
+    private void loge(String s) {
+        Log.e(TAG, "[" + mPhoneId + "][" + mSubId + "] " + s);
+    }
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsPresenceExchangeImpl.java b/rcs/rcsservice/src/com/android/service/ims/RcsPresenceExchangeImpl.java
new file mode 100644
index 0000000..3a995f4
--- /dev/null
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsPresenceExchangeImpl.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 copyright holder 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsManager;
+import com.android.ims.RcsPresenceInfo;
+import com.android.service.ims.RcsStackAdaptor;
+import com.android.service.ims.TaskManager;
+
+import java.util.List;
+
+public class RcsPresenceExchangeImpl extends RcsPresenceExchangeImplBase {
+
+    private Context mContext;
+    private static final String TAG = "RcsPresenceExchangeImpl";
+
+    public RcsPresenceExchangeImpl(Context context) {
+        mContext = context;
+    }
+
+
+    /**
+     * The user capabilities of one or multiple contacts have been requested by the framework.
+     * <p>
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded.  If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+     * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
+     * information for the contacts specified.
+     *
+     * @param uris           A {@link List} of the {@link Uri}s that the framework is requesting
+     *                       the UCE
+     *                       capabilities for.
+     * @param operationToken The token associated with this operation. Updates to this request using
+     *                       {@link #onCommandUpdate(int, int)},
+     *                       {@link #onNetworkResponse(int, String, int)}, and
+     *                       {@link #onCapabilityRequestResponse(List, int)}  must use the same
+     *                       operation token
+     *                       in response.
+     */
+    public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
+        Log.d(TAG, "requestCapabilities operationToken:" + operationToken);
+        String[] contacts = new String[uris.size()];
+        for (int i = 0; i < uris.size(); i++) {
+            contacts[i] = uris.get(i).toString();
+        }
+        RcsStackAdaptor.getInstance(null).requestCapability(contacts, operationToken);
+    }
+
+    /**
+     * The capabilities of this device have been updated and should be published to the network.
+     * <p>
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded. If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}.
+     *
+     * @param capabilities   The capabilities for this device.
+     * @param operationToken The token associated with this operation. Any subsequent
+     *                       {@link #onCommandUpdate(int, int)} or
+     *                       {@link #onNetworkResponse(int, String, int)}
+     *                       calls regarding this update must use the same token.
+     */
+    public void updateCapabilities(@NonNull RcsContactUceCapability capabilities,
+            int operationToken) {
+        Log.d(TAG, "updateCapabilities operationToken:" + operationToken);
+        boolean isVolte = capabilities.isCapable(RcsContactUceCapability
+                .CAPABILITY_IP_VOICE_CALL);
+        boolean isVt = capabilities.isCapable(
+                RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL);
+        int volteState = isVolte ? RcsPresenceInfo.ServiceState.ONLINE
+                : RcsPresenceInfo.ServiceState.OFFLINE;
+        int vtState = isVt ? RcsPresenceInfo.ServiceState.ONLINE
+                : RcsPresenceInfo.ServiceState.OFFLINE;
+        RcsPresenceInfo presenceInfo = new RcsPresenceInfo(capabilities.getContactUri().toString(),
+                RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN,
+                volteState,
+                null,
+                System.currentTimeMillis(),
+                vtState,
+                null,
+                System.currentTimeMillis()
+        );
+        RcsStackAdaptor.getInstance(null).requestPublication(presenceInfo, null, operationToken);
+    }
+
+}
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsService.java b/rcs/rcsservice/src/com/android/service/ims/RcsService.java
index 953db2f..5e83d51 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsService.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsService.java
@@ -80,7 +80,7 @@
 
         logger.debug("RcsService onCreate");
 
-        mRcsStackAdaptor = RcsStackAdaptor.getInstance(this);
+        mRcsStackAdaptor = RcsStackAdaptor.getInstance(null);
 
         mPublication = new PresencePublication(mRcsStackAdaptor, this,
                 getResources().getStringArray(
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsServiceApp.java b/rcs/rcsservice/src/com/android/service/ims/RcsServiceApp.java
index c1f683d..c48f1dc 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsServiceApp.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsServiceApp.java
@@ -39,8 +39,6 @@
     public void onCreate() {
         super.onCreate();
         logger.debug("in onCreate() of RcsServiceApp");
-
-        LauncherUtils.launchRcsService(this);
     }
 }
 
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java b/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
index 902f6c7..2f2c2d3 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsStackAdaptor.java
@@ -56,6 +56,7 @@
 import com.android.ims.ImsManager;
 import com.android.ims.ImsException;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 
 import com.android.ims.IRcsPresenceListener;
 import com.android.ims.RcsPresence;
@@ -160,11 +161,11 @@
     // The singleton.
     private static RcsStackAdaptor sInstance = null;
 
+    private RcsPresenceExchangeImplBase mRcsPresenceExchangeImplBase;
+
     // Constructor
     private RcsStackAdaptor(Context context) {
         mContext = context;
-
-        init();
     }
 
     public static synchronized RcsStackAdaptor getInstance(Context context) {
@@ -342,7 +343,8 @@
         return  ret;
     }
 
-    public int requestPublication(RcsPresenceInfo presenceInfo, IRcsPresenceListener listener) {
+    public int requestPublication(RcsPresenceInfo presenceInfo, IRcsPresenceListener listener,
+            int taskId) {
         logger.debug("requestPublication ...");
 
          // Don't use checkStackAndPublish()
@@ -395,7 +397,6 @@
             return PresencePublication.PUBLISH_GENIRIC_FAILURE;
         }
 
-        int taskId = TaskManager.getDefault().addPublishTask(myNumber, listener);
         try{
             PresCapInfo pMyCapInfo = new PresCapInfo();
             // Fill cap info
@@ -614,7 +615,7 @@
         thread.start();
     }
 
-    private void init() {
+    public void init() {
         createListeningThread();
         logger.debug("after createListeningThread");
 
@@ -729,6 +730,15 @@
         clearImsUceService();
     }
 
+    public RcsPresenceExchangeImplBase getRcsPresenceExchangeImplBase() {
+        return mRcsPresenceExchangeImplBase;
+    }
+
+    public void setRcsPresenceExchangeImplBase(
+            RcsPresenceExchangeImplBase rcsPresenceExchangeImplBase) {
+        mRcsPresenceExchangeImplBase = rcsPresenceExchangeImplBase;
+    }
+
     protected void finalize() throws Throwable {
         super.finalize();
         finish();
diff --git a/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java b/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
index bbfacd2..623fa74 100644
--- a/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
+++ b/rcs/rcsservice/src/com/android/service/ims/RcsUtils.java
@@ -28,10 +28,19 @@
 
 package com.android.service.ims;
 
+import static com.android.ims.RcsPresenceInfo.ServiceType.VOLTE_CALL;
+import static com.android.ims.RcsPresenceInfo.ServiceType.VT_CALL;
+
 import java.lang.String;
+import java.util.ArrayList;
+
+import android.net.Uri;
 import android.telephony.TelephonyManager;
 import android.content.Context;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.RcsCapabilityExchange;
 
+import com.android.ims.RcsPresenceInfo;
 import com.android.ims.internal.uce.common.StatusCode;
 
 import com.android.ims.RcsManager.ResultCode;
@@ -90,6 +99,75 @@
         return ResultCode.SUBSCRIBE_GENERIC;
     }
 
+    static public int statusCodeToCommandCode(int sipStatusCode) {
+        if (sipStatusCode == StatusCode.UCE_SUCCESS ||
+                sipStatusCode == StatusCode.UCE_SUCCESS_ASYC_UPDATE) {
+            return RcsCapabilityExchange.COMMAND_CODE_SUCCESS;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_INVALID_PARAM) {
+            return RcsCapabilityExchange.COMMAND_CODE_INVALID_PARAM;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_FETCH_ERROR) {
+            return RcsCapabilityExchange.COMMAND_CODE_FETCH_ERROR;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_REQUEST_TIMEOUT) {
+            return RcsCapabilityExchange.COMMAND_CODE_REQUEST_TIMEOUT;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_INSUFFICIENT_MEMORY) {
+            return RcsCapabilityExchange.COMMAND_CODE_INSUFFICIENT_MEMORY;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_LOST_NET) {
+            return RcsCapabilityExchange.COMMAND_CODE_LOST_NETWORK_CONNECTION;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_NOT_SUPPORTED) {
+            return RcsCapabilityExchange.COMMAND_CODE_NOT_SUPPORTED;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_NOT_FOUND) {
+            return RcsCapabilityExchange.COMMAND_CODE_NOT_FOUND;
+        }
+
+        if (sipStatusCode == StatusCode.UCE_FAILURE ||
+                sipStatusCode == StatusCode.UCE_INVALID_SERVICE_HANDLE ||
+                sipStatusCode == StatusCode.UCE_INVALID_LISTENER_HANDLE) {
+            return RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE;
+        }
+
+        return RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE;
+    }
+
+    static public ArrayList<RcsContactUceCapability> transformToRcsContactUceCapability
+            (ArrayList<RcsPresenceInfo> presenceInfoList) {
+        ArrayList<RcsContactUceCapability> uceCapabilityArrayList = new ArrayList<>(
+                presenceInfoList.size());
+        for (RcsPresenceInfo info : presenceInfoList) {
+            RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(Uri
+                    .parse(info.getContactNumber()));
+            if (info.getServiceState(VOLTE_CALL) == RcsPresenceInfo.ServiceState.ONLINE) {
+                builder.add(RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL);
+            }
+            if (info.getServiceState(VT_CALL) == RcsPresenceInfo.ServiceState.ONLINE) {
+                builder.add(RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL);
+            }
+            uceCapabilityArrayList.add(builder.build());
+        }
+        return uceCapabilityArrayList;
+    }
+
+    static public ArrayList<Uri> transformToUri(String[] contacts) {
+        ArrayList<Uri> list = new ArrayList<>();
+        for(String contact : contacts) {
+            list.add(Uri.parse(contact));
+        }
+        return list;
+    }
+
     static public String toContactString(String[] contacts) {
         if(contacts == null) {
             return null;
diff --git a/rcs/rcsservice/src/com/android/service/ims/TaskManager.java b/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
index 74e11a6..7d11ea3 100644
--- a/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
+++ b/rcs/rcsservice/src/com/android/service/ims/TaskManager.java
@@ -66,6 +66,7 @@
 
     private int mTaskId = 0;
 
+    public final static int INVALID_ID = -1;
     public final static int TASK_TYPE_GET_CAPABILITY   = 1;
     public final static int TASK_TYPE_GET_AVAILABILITY = 2;
     public final static int TASK_TYPE_PUBLISH          = 3;
@@ -190,16 +191,16 @@
         return null;
     }
 
-    public void onTerminated(String contact){ // for single number capability polling
+    public int onTerminated(String contact){ // for single number capability polling
         if(contact == null){
-            return;
+            return INVALID_ID;
         }
 
         synchronized (mSyncObj){
             Set<String> keys= mTaskMap.keySet();
             if(keys == null){
                 logger.debug("onTerminated keys is null");
-                return;
+                return INVALID_ID;
             }
 
             for(String key:keys){
@@ -225,19 +226,21 @@
                                 TASK_MANAGER_ON_TERMINATED,
                                 messageData);
                         sMsgHandler.sendMessage(notifyMessage);
+                        return task.mTaskId;
                     }
                 }
             }
         }
+        return INVALID_ID;
     }
 
-    public void onTerminated(int requestId, String reason){
+    public int onTerminated(int requestId, String reason){
         logger.debug("onTerminated requestId=" + requestId + " reason=" + reason);
 
         Task task = getTaskByRequestId(requestId);
         if(task == null){
             logger.debug("onTerminated Can't find request " + requestId);
-            return;
+            return INVALID_ID;
         }
 
         synchronized (mSyncObj){
@@ -249,8 +252,10 @@
                 Message notifyMessage = sMsgHandler.obtainMessage(TASK_MANAGER_ON_TERMINATED,
                         messageData);
                 sMsgHandler.sendMessage(notifyMessage);
+                return task.mTaskId;
             }
         }
+        return INVALID_ID;
     }
 
     public void onTimeout(int taskId){
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 e7607ee..4e7edb6 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresencePublication.java
@@ -28,53 +28,45 @@
 
 package com.android.service.ims.presence;
 
-import java.util.List;
-import java.util.Arrays;
-
+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.provider.Settings;
-import android.os.SystemProperties;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.telephony.TelephonyManager;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
-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.net.Uri;
 import android.os.Handler;
-
-
-import com.android.ims.ImsManager;
-import com.android.ims.ImsConnectionStateListener;
-import com.android.ims.ImsServiceClass;
-import com.android.ims.ImsException;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.telephony.SubscriptionManager;
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsConfig.FeatureConstants;
-import com.android.ims.ImsConfig.FeatureValueConstants;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 
-import com.android.service.ims.RcsSettingUtils;
-import com.android.ims.RcsPresenceInfo;
-import com.android.ims.IRcsPresenceListener;
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.ims.ImsServiceClass;
 import com.android.ims.RcsManager.ResultCode;
 import com.android.ims.RcsPresence.PublishState;
-
+import com.android.ims.RcsPresenceInfo;
 import com.android.ims.internal.Logger;
-import com.android.service.ims.TaskManager;
-import com.android.service.ims.Task;
-
+import com.android.ims.internal.uce.presence.PresCmdStatus;
 import com.android.ims.internal.uce.presence.PresPublishTriggerType;
 import com.android.ims.internal.uce.presence.PresSipResponse;
-import com.android.ims.internal.uce.common.StatusCode;
-import com.android.ims.internal.uce.presence.PresCmdStatus;
-
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.service.ims.RcsPresenceExchangeImpl;
+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.Arrays;
 
 public class PresencePublication extends PresenceBase {
     private Logger logger = Logger.getLogger(this.getClass().getName());
@@ -932,7 +924,24 @@
             mPublishingRequest.setTimestamp(System.currentTimeMillis());
         }
 
-        int ret = mRcsStackAdaptor.requestPublication(presenceInfo, null);
+        RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(
+                null).getRcsPresenceExchangeImplBase();
+        int ret = ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+        if (rcsPresenceExchange != null) {
+            RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(Uri
+                    .parse("tel:" + teleMgr.getLine1Number()));
+            int taskId = TaskManager.getDefault().addPublishTask(teleMgr.getLine1Number(), null);
+            if (publishRequest.getVolteCapable()) {
+                builder.add(RcsContactUceCapability.CAPABILITY_IP_VOICE_CALL);
+            }
+            if (publishRequest.getVtCapable()) {
+                builder.add(RcsContactUceCapability.CAPABILITY_IP_VIDEO_CALL);
+            }
+            rcsPresenceExchange.updateCapabilities(builder.build(), taskId);
+        } else {
+            logger.debug("rcsPresenceExchange = null for publish request");
+        }
+
         if(ret == ResultCode.ERROR_SERVICE_NOT_AVAILABLE){
             mHasCachedTrigger = true;
         }else{
@@ -943,6 +952,19 @@
 
     public void handleCmdStatus(PresCmdStatus cmdStatus) {
         super.handleCmdStatus(cmdStatus);
+        int commandCode = RcsUtils.statusCodeToCommandCode(cmdStatus.getStatus().getStatusCode());
+        RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(null)
+                .getRcsPresenceExchangeImplBase();
+        logger.debug("publish cmd:" + cmdStatus);
+        if (rcsPresenceExchange != null) {
+            try {
+                rcsPresenceExchange.onCommandUpdate(commandCode, cmdStatus.getUserData());
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        } else {
+            logger.debug("rcsPresenceExchange = null for sip response");
+        }
     }
 
     private PendingIntent mRetryAlarmIntent = null;
@@ -1077,6 +1099,24 @@
         }
 
         handleCallback(task, getPublishState(), false);
+        notifyFrameworkForPublishResponse(task);
+    }
+
+    private void notifyFrameworkForPublishResponse(Task task) {
+        RcsPresenceExchangeImpl rcsPresenceExchange =
+                (RcsPresenceExchangeImpl) RcsStackAdaptor.getInstance(null)
+                        .getRcsPresenceExchangeImplBase();
+        if (rcsPresenceExchange != null) {
+            try {
+                logger.debug("onNetworkResponse task:"+task);
+                rcsPresenceExchange.onNetworkResponse(task.mSipResponseCode, task.mSipReasonPhrase,
+                        task.mTaskId);
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        } else {
+            logger.debug("rcsPresenceExchange = null for sip response");
+        }
     }
 
     private static boolean isTtyEnabled(int mode) {
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 daa2636..fd2a701 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/PresenceSubscriber.java
@@ -30,30 +30,19 @@
 
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.Semaphore;
-import android.content.ContentValues;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.RcsCapabilityExchange;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 import android.text.TextUtils;
 
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import com.android.internal.telephony.TelephonyIntents;
-import android.os.HandlerThread;
-import android.os.RemoteException;
 import android.telephony.TelephonyManager;
-import android.database.Cursor;
 
 import java.lang.String;
-import android.content.Context;
-import android.util.Log;
 
 import com.android.ims.internal.uce.presence.PresSipResponse;
-import com.android.ims.internal.uce.common.StatusCode;
-import com.android.ims.internal.uce.common.StatusCode;
 import com.android.ims.internal.uce.presence.PresSubscriptionState;
 import com.android.ims.internal.uce.presence.PresCmdStatus;
 import com.android.ims.internal.uce.presence.PresResInfo;
@@ -68,6 +57,7 @@
 
 import com.android.ims.internal.Logger;
 import com.android.ims.internal.ContactNumberUtils;
+import com.android.service.ims.RcsPresenceExchangeImpl;
 import com.android.service.ims.TaskManager;
 import com.android.service.ims.Task;
 import com.android.service.ims.RcsStackAdaptor;
@@ -160,6 +150,15 @@
         for(int i=0; i<formatedContacts.length; i++){
             formatedContacts[i] = numberToTelString(formatedNumbers[i]);
         }
+
+        RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(
+                null).getRcsPresenceExchangeImplBase();
+        if (rcsPresenceExchange == null) {
+            ret = ResultCode.SUBSCRIBE_GENIRIC_FAILURE;
+            logger.error("rcsPresenceExchange = null");
+            return ret;
+        }
+
         // In ms
         long timeout = RcsSettingUtils.getCapabPollListSubExp(mContext) * 1000;
         timeout += RcsSettingUtils.getSIPT1Timer(mContext);
@@ -176,12 +175,8 @@
                 listener, timeout);
         logger.print("taskId=" + taskId);
 
-        ret = mRcsStackAdaptor.requestCapability(formatedContacts, taskId);
-        if(ret < ResultCode.SUCCESS){
-            logger.error("requestCapability ret=" + ret + " remove taskId=" + taskId);
-            TaskManager.getDefault().removeTask(taskId);
-        }
-
+        rcsPresenceExchange.requestCapabilities(RcsUtils.transformToUri(formatedContacts),
+                taskId);
         ret = taskId;
 
         return  ret;
@@ -444,6 +439,24 @@
         }
 
         handleCallback(task, errorCode, false);
+        notifyFrameworkForSubscribeResponse(task);
+    }
+
+    private void notifyFrameworkForSubscribeResponse(Task task) {
+        RcsPresenceExchangeImpl rcsPresenceExchange =
+                (RcsPresenceExchangeImpl) RcsStackAdaptor.getInstance(null)
+                        .getRcsPresenceExchangeImplBase();
+        if (rcsPresenceExchange != null) {
+            try {
+                logger.debug("onNetworkResponse taskid=" + task.mTaskId);
+                rcsPresenceExchange.onNetworkResponse(task.mSipResponseCode, task.mSipReasonPhrase,
+                        task.mTaskId);
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        } else {
+            logger.debug("rcsPresenceExchange = null for sip response");
+        }
     }
 
     private void launchPersistService(Intent intent) {
@@ -481,7 +494,7 @@
         rcsPresenceInfoList.add(rcsPresenceInfo);
 
         // For single contact number we got 1 NOTIFY only. So regard it as terminated.
-        TaskManager.getDefault().onTerminated(rcsPresenceInfo.getContactNumber());
+        int taskId = TaskManager.getDefault().onTerminated(rcsPresenceInfo.getContactNumber());
 
         PresenceAvailabilityTask availabilityTask = TaskManager.getDefault().
                 getAvailabilityTaskByContact(rcsPresenceInfo.getContactNumber());
@@ -495,6 +508,7 @@
                 rcsPresenceInfoList);
         intent.putExtra("updateLastTimestamp", true);
         launchPersistService(intent);
+        notifyPresence(rcsPresenceInfoList, taskId);
     }
 
     public void updatePresences(PresRlmiInfo pRlmiInfo, PresResInfo[] pRcsPresenceInfo) {
@@ -529,8 +543,9 @@
             }
         }
 
+        int taskId = TaskManager.INVALID_ID;
         if(isTerminated){
-            TaskManager.getDefault().onTerminated(pRlmiInfo.getRequestId(),
+            taskId = TaskManager.getDefault().onTerminated(pRlmiInfo.getRequestId(),
                     pRlmiInfo.getSubscriptionTerminatedReason());
         }
 
@@ -543,6 +558,24 @@
                     rcsPresenceInfoList);
             intent.putExtra("updateLastTimestamp", true);
             launchPersistService(intent);
+            notifyPresence(rcsPresenceInfoList, taskId);
+        }
+    }
+
+    private void notifyPresence(ArrayList rcsPresenceInfoList, int taskId) {
+        RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(null)
+                .getRcsPresenceExchangeImplBase();
+        if (rcsPresenceExchange != null) {
+            try {
+                ArrayList<RcsContactUceCapability> list = RcsUtils
+                        .transformToRcsContactUceCapability(rcsPresenceInfoList);
+                logger.debug("onCapabilityRequestResponse taskid="+taskId);
+                rcsPresenceExchange.onCapabilityRequestResponse(list, taskId);
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        } else {
+            logger.debug("rcsPresenceExchange = null for sip response");
         }
     }
 
@@ -552,7 +585,6 @@
             return;
         }
 
-
         Task taskTmp = TaskManager.getDefault().getTask(pCmdStatus.getUserData());
         int resultCode = RcsUtils.statusCodeToResultCode(pCmdStatus.getStatus().getStatusCode());
         logger.print("handleCmdStatus resultCode=" + resultCode);
@@ -565,12 +597,27 @@
 
             // handle error as the same as temporary network error
             // set availability to false, keep old capability
-            if(resultCode != ResultCode.SUCCESS && task.mContacts != null){
+            if(resultCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS && task.mContacts !=
+                    null){
                 updateAvailabilityToUnknown(task);
             }
         }
 
         handleCallback(task, resultCode, true);
+
+        RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(null)
+                .getRcsPresenceExchangeImplBase();
+        if (rcsPresenceExchange != null) {
+            try {
+                logger.print("onCommandUpdate id:"+pCmdStatus.getUserData());
+                int commandCode = RcsUtils.statusCodeToCommandCode(pCmdStatus.getStatus().getStatusCode());
+                rcsPresenceExchange.onCommandUpdate(commandCode, pCmdStatus.getUserData());
+            } catch (Exception e) {
+                logger.error(e.getMessage());
+            }
+        } else {
+            logger.debug("rcsPresenceExchange = null for sip response");
+        }
     }
 
     private void updateAvailabilityToUnknown(Task inTask){
diff --git a/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java b/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java
index 9ca82fc..9f15e23 100644
--- a/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java
+++ b/rcs/rcsservice/src/com/android/service/ims/presence/StackListener.java
@@ -28,19 +28,13 @@
 
 package com.android.service.ims.presence;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import android.content.ServiceConnection;
-import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.text.TextUtils;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 import android.util.Log;
 import android.os.Parcel;
 
@@ -53,17 +47,11 @@
 import com.android.ims.internal.uce.presence.PresSipResponse;
 import com.android.ims.internal.uce.presence.PresTupleInfo;
 import com.android.ims.internal.uce.common.StatusCode;
-import com.android.ims.internal.uce.common.StatusCode;
 
 import com.android.ims.RcsManager;
-import com.android.ims.RcsManager.ResultCode;
-import com.android.ims.RcsPresence;
-import com.android.ims.RcsPresenceInfo;
-import com.android.ims.IRcsPresenceListener;
 
 import com.android.ims.internal.Logger;
-import com.android.service.ims.TaskManager;
-import com.android.service.ims.Task;
+import com.android.service.ims.RcsPresenceExchangeImpl;
 import com.android.service.ims.RcsStackAdaptor;
 
 public class StackListener extends Handler{
@@ -391,7 +379,18 @@
                          publishTrigger.getPublishTrigeerType());
             }
             logger.debug("ListenerHandler : PublishTriggering");
-
+            RcsPresenceExchangeImplBase rcsPresenceExchange = RcsStackAdaptor.getInstance(null)
+                    .getRcsPresenceExchangeImplBase();
+            if (rcsPresenceExchange != null) {
+                try {
+                    logger.debug("onNotifyUpdateCapabilites");
+                    rcsPresenceExchange.onNotifyUpdateCapabilites();
+                } catch (Exception e) {
+                    logger.error(e.getMessage());
+                }
+            } else {
+                logger.debug("rcsPresenceExchange = null for publishTriggering");
+            }
             Message publishTrigerMsg = StackListener.this.obtainMessage(
                     PRESENCE_IMS_UNSOL_PUBLISH_TRIGGER, publishTrigger);
             StackListener.this.sendMessage(publishTrigerMsg);
@@ -532,6 +531,19 @@
 
         public void unpublishMessageSent() {
             logger.debug("unpublishMessageSent()");
+            RcsPresenceExchangeImpl rcsPresenceExchange =
+                    (RcsPresenceExchangeImpl) RcsStackAdaptor.getInstance(null)
+                            .getRcsPresenceExchangeImplBase();
+            if (rcsPresenceExchange != null) {
+                try {
+                    logger.debug("onUnpublish");
+                    rcsPresenceExchange.onUnpublish();
+                } catch (Exception e) {
+                    logger.error(e.getMessage());
+                }
+            } else {
+                logger.debug("rcsPresenceExchange = null for unpublish sent");
+            }
         }
     };
 }