Merge 7b62b1db05bc60f88df81d49e23698abf474162c on remote branch

Change-Id: I72dd96346458e7f9eaba2302da9f0c60f061cfe1
diff --git a/ims/ims-ext-common/src/org/codeaurora/ims/CrsCrbtControllerBase.java b/ims/ims-ext-common/src/org/codeaurora/ims/CrsCrbtControllerBase.java
index ab75505..e1d6708 100644
--- a/ims/ims-ext-common/src/org/codeaurora/ims/CrsCrbtControllerBase.java
+++ b/ims/ims-ext-common/src/org/codeaurora/ims/CrsCrbtControllerBase.java
@@ -32,36 +32,60 @@
 import android.os.RemoteException;
 import org.codeaurora.ims.internal.ICrsCrbtListener;
 import org.codeaurora.ims.internal.ICrsCrbtController;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
 
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
+// Check the permission of the calling process
 public abstract class CrsCrbtControllerBase {
     public final class CrsCrbtBinder extends ICrsCrbtController.Stub {
 
         @Override
         public void setCrsCrbtListener(ICrsCrbtListener listener)
-            throws RemoteException{
-            CrsCrbtControllerBase.this.onSetCrsCrbtListener(listener);
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() -> {
+                try {
+                    CrsCrbtControllerBase.this.onSetCrsCrbtListener(listener);
+                } catch (RemoteException e) {
+                    throw new CompletionException(e);
+                }
+            }, "setCrsCrbtListener", mExecutor,
+            QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
         public void removeCrsCrbtListener(ICrsCrbtListener listener)
-            throws RemoteException{
-            CrsCrbtControllerBase.this.onRemoveCrsCrbtListener(listener);
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    CrsCrbtControllerBase.this.onRemoveCrsCrbtListener(listener),
+                    "removeCrsCrbtListener", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
         public boolean isPreparatorySession(String callId)
-            throws RemoteException {
-            return CrsCrbtControllerBase.this.onIsPreparatorySession(callId);
+                throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    CrsCrbtControllerBase.this.onIsPreparatorySession(callId),
+                    "isPreparatorySession", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE,
+                    mContext);
         }
 
         @Override
         public void sendSipDtmf(String requestCode)
-            throws RemoteException {
-            CrsCrbtControllerBase.this.onSendSipDtmf(requestCode);
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    CrsCrbtControllerBase.this.onSendSipDtmf(requestCode),
+                    "sendSipDtmf", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE,
+                    mContext);
         }
+
     }
 
     private ICrsCrbtController mBinder;
+    private Executor mExecutor;
+    private Context mContext;
 
     public ICrsCrbtController getBinder() {
         if (mBinder == null) {
@@ -70,24 +94,26 @@
         return mBinder;
     }
 
+    public CrsCrbtControllerBase(Executor executor, Context context) {
+        mExecutor = executor;
+        mContext = context;
+    }
+
     protected void onSetCrsCrbtListener(ICrsCrbtListener listener)
         throws RemoteException {
         //no-op
     }
 
-    protected void onRemoveCrsCrbtListener(ICrsCrbtListener listener)
-        throws RemoteException {
+    protected void onRemoveCrsCrbtListener(ICrsCrbtListener listener) {
         //no-op
     }
 
-    protected boolean onIsPreparatorySession(String callId)
-        throws RemoteException {
+    protected boolean onIsPreparatorySession(String callId) {
         //no-op
         return false;
     }
 
-    protected void onSendSipDtmf(String requestCode)
-        throws RemoteException {
+    protected void onSendSipDtmf(String requestCode) {
         //no-op
     }
 }
diff --git a/ims/ims-ext-common/src/org/codeaurora/ims/ImsMultiIdentityControllerBase.java b/ims/ims-ext-common/src/org/codeaurora/ims/ImsMultiIdentityControllerBase.java
index 05cf4e2..0615458 100644
--- a/ims/ims-ext-common/src/org/codeaurora/ims/ImsMultiIdentityControllerBase.java
+++ b/ims/ims-ext-common/src/org/codeaurora/ims/ImsMultiIdentityControllerBase.java
@@ -34,33 +34,55 @@
 import org.codeaurora.ims.MultiIdentityLineInfo;
 import org.codeaurora.ims.internal.IImsMultiIdentityListener;
 import org.codeaurora.ims.internal.IImsMultiIdentityInterface;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
 import java.util.List;
 import java.util.ArrayList;
 
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
 public abstract class ImsMultiIdentityControllerBase {
     public final class MultiIdentityBinder extends IImsMultiIdentityInterface.Stub {
 
         @Override
         public void setMultiIdentityListener(
-                IImsMultiIdentityListener listener) throws RemoteException{
-            ImsMultiIdentityControllerBase.this.
-                setMultiIdentityListener(listener);
+                IImsMultiIdentityListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() -> {
+                    try {
+                        ImsMultiIdentityControllerBase.this.
+                            setMultiIdentityListener(listener);
+                    } catch (RemoteException e) {
+                        throw new CompletionException(e);
+                    }}, "setMultiIdentityListener", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void updateRegistrationStatus(List<MultiIdentityLineInfo> linesInfo) {
-            ImsMultiIdentityControllerBase.this.
-                updateRegistrationStatus(linesInfo);
+        public void updateRegistrationStatus(List<MultiIdentityLineInfo> linesInfo)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    ImsMultiIdentityControllerBase.this.
+                    updateRegistrationStatus(linesInfo),
+                    "updateRegistrationStatus", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void queryVirtualLineInfo(String msisdn) throws RemoteException{
-           ImsMultiIdentityControllerBase.this.
-               queryVirtualLineInfo(msisdn);
+        public void queryVirtualLineInfo(String msisdn) throws RemoteException {
+            if (msisdn == null) {
+                throw new RemoteException("queryVirtualLineInfo :: msisdn is null");
+            }
+            QtiImsExtUtils.executeMethodAsync(() ->
+                        ImsMultiIdentityControllerBase.this.
+                        queryVirtualLineInfo(msisdn),
+                        "queryVirtualLineInfo", mExecutor,
+                        QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
     }
 
     private IImsMultiIdentityInterface mBinder;
+    private Executor mExecutor;
+    private Context mContext;
 
     public IImsMultiIdentityInterface getBinder() {
         if (mBinder == null) {
@@ -69,8 +91,13 @@
         return mBinder;
     }
 
+    public ImsMultiIdentityControllerBase(Executor executor, Context context) {
+        mExecutor = executor;
+        mContext = context;
+    }
+
     protected void setMultiIdentityListener(
-            IImsMultiIdentityListener listener) throws RemoteException{
+            IImsMultiIdentityListener listener) throws RemoteException {
         //no-op
     }
 
@@ -79,7 +106,7 @@
         //no-op
     }
 
-    protected void queryVirtualLineInfo(String msisdn) throws RemoteException{
+    protected void queryVirtualLineInfo(String msisdn) {
         //no-op
     }
 }
diff --git a/ims/ims-ext-common/src/org/codeaurora/ims/ImsScreenShareControllerBase.java b/ims/ims-ext-common/src/org/codeaurora/ims/ImsScreenShareControllerBase.java
index 7109f55..1af3293 100644
--- a/ims/ims-ext-common/src/org/codeaurora/ims/ImsScreenShareControllerBase.java
+++ b/ims/ims-ext-common/src/org/codeaurora/ims/ImsScreenShareControllerBase.java
@@ -32,28 +32,47 @@
 import android.os.RemoteException;
 import org.codeaurora.ims.internal.IImsScreenShareListener;
 import org.codeaurora.ims.internal.IImsScreenShareController;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
 
 public abstract class ImsScreenShareControllerBase {
     public final class ScreenShareBinder extends IImsScreenShareController.Stub {
 
         @Override
         public void setScreenShareListener(
-                IImsScreenShareListener listener) throws RemoteException{
-            ImsScreenShareControllerBase.this.onSetScreenShareListener(listener);
+                IImsScreenShareListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() -> {
+                    try {
+                        ImsScreenShareControllerBase.this.onSetScreenShareListener(listener);
+                    } catch (RemoteException e) {
+                        throw new CompletionException(e);
+                    }},
+                    "setScreenShareListener", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
         public void startScreenShare(int width, int height) throws RemoteException{
-            ImsScreenShareControllerBase.this.onStartScreenShare(width, height);
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    ImsScreenShareControllerBase.this.onStartScreenShare(width, height),
+                    "startScreenShare", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE,
+                    mContext);
         }
 
         @Override
         public void stopScreenShare() throws RemoteException{
-           ImsScreenShareControllerBase.this.onStopScreenShare();
+           QtiImsExtUtils.executeMethodAsync(() ->
+                    ImsScreenShareControllerBase.this.onStopScreenShare(),
+                    "stopScreenShare", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
     }
 
     private IImsScreenShareController mBinder;
+    private Executor mExecutor;
+    private Context mContext;
 
     public IImsScreenShareController getBinder() {
         if (mBinder == null) {
@@ -62,16 +81,21 @@
         return mBinder;
     }
 
+    public ImsScreenShareControllerBase(Executor executor, Context context) {
+        mExecutor = executor;
+        mContext = context;
+    }
+
     protected void onSetScreenShareListener(
             IImsScreenShareListener listener) throws RemoteException{
         //no-op
     }
 
-    protected void onStartScreenShare(int width, int height) throws RemoteException{
+    protected void onStartScreenShare(int width, int height) {
         //no-op
     }
 
-    protected void onStopScreenShare() throws RemoteException{
+    protected void onStopScreenShare() {
         //no-op
     }
 }
diff --git a/ims/ims-ext-common/src/org/codeaurora/ims/QtiImsExtBase.java b/ims/ims-ext-common/src/org/codeaurora/ims/QtiImsExtBase.java
index dee1a72..d1128b1 100644
--- a/ims/ims-ext-common/src/org/codeaurora/ims/QtiImsExtBase.java
+++ b/ims/ims-ext-common/src/org/codeaurora/ims/QtiImsExtBase.java
@@ -31,7 +31,9 @@
  */
 package org.codeaurora.ims;
 
+import android.content.Context;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.telephony.ims.feature.ImsFeature;
 
 import org.codeaurora.ims.internal.ICrsCrbtController;
@@ -39,10 +41,17 @@
 import org.codeaurora.ims.internal.IQtiImsExtListener;
 import org.codeaurora.ims.internal.IImsMultiIdentityInterface;
 import org.codeaurora.ims.internal.IImsScreenShareController;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
 import org.codeaurora.ims.QtiCallConstants;
 import org.codeaurora.ims.VosActionInfo;
+
+import java.util.concurrent.Executor;
+
 /**
  * Base implementation for IQtiImsExt.
+ * Introduce Executor pattern where API(s) will be called on the executor thread.
+ * Except for non-oneway API(s) which call into ImsConfigImpl as setConfig/getConfig
+ * are blocking AIDL calls.
  */
 public abstract class QtiImsExtBase {
 
@@ -54,168 +63,257 @@
         @Override
         public void setCallForwardUncondTimer(int phoneId, int startHour, int startMinute,
                 int endHour, int endMinute, int action, int condition, int serviceClass,
-                String number, IQtiImsExtListener listener) {
-            onSetCallForwardUncondTimer(phoneId, startHour, startMinute, endHour, endMinute, action,
-                condition, serviceClass, number, listener);
+                String number, IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() -> QtiImsExtBase.this.onSetCallForwardUncondTimer(
+                    phoneId, startHour, startMinute, endHour, endMinute, action,
+                    condition, serviceClass, number, listener),
+                    "setCallForwardUncondTimer", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
         public void getCallForwardUncondTimer(int phoneId, int reason, int serviceClass,
-                IQtiImsExtListener listener) {
-            onGetCallForwardUncondTimer(phoneId, reason, serviceClass, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onGetCallForwardUncondTimer(phoneId, reason,
+                    serviceClass, listener), "getCallForwardUncondTimer", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void resumePendingCall(int phoneId, int videoState) {
-            onResumePendingCall(phoneId, videoState);
+        public void resumePendingCall(int phoneId, int videoState) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onResumePendingCall(phoneId, videoState),
+                    "resumePendingCall", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void sendCancelModifyCall(int phoneId, IQtiImsExtListener listener) {
-            onSendCancelModifyCall(phoneId, listener);
+        public void sendCancelModifyCall(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSendCancelModifyCall(phoneId, listener),
+                    "sendCancelModifyCall", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void queryVopsStatus(int phoneId, IQtiImsExtListener listener) {
-            onQueryVopsStatus(phoneId, listener);
+        public void queryVopsStatus(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onQueryVopsStatus(phoneId, listener),
+                    "queryVopsStatus", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void querySsacStatus(int phoneId, IQtiImsExtListener listener) {
-            onQuerySsacStatus(phoneId, listener);
+        public void querySsacStatus(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onQuerySsacStatus(phoneId, listener),
+                    "querySsacStatus", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void registerForParticipantStatusInfo(int phoneId, IQtiImsExtListener listener) {
-            onRegisterForParticipantStatusInfo(phoneId, listener);
+        public void registerForParticipantStatusInfo(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onRegisterForParticipantStatusInfo(phoneId, listener),
+                    "registerForParticipantStatusInfo", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
         public void updateVoltePreference(int phoneId, int preference,
-                IQtiImsExtListener listener) {
-            onUpdateVoltePreference(phoneId, preference, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onUpdateVoltePreference(phoneId, preference, listener),
+                    "updateVoltePreference", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void queryVoltePreference(int phoneId, IQtiImsExtListener listener) {
-            onQueryVoltePreference(phoneId, listener);
+        public void queryVoltePreference(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onQueryVoltePreference(phoneId, listener),
+                    "queryVoltePreference", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void getHandoverConfig(int phoneId, IQtiImsExtListener listener) {
-            onGetHandoverConfig(phoneId, listener);
+        public void getHandoverConfig(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onGetHandoverConfig(phoneId, listener),
+                    "getHandoverConfig", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
         public void setHandoverConfig(int phoneId, int hoConfig,
-                IQtiImsExtListener listener) {
-            onSetHandoverConfig(phoneId, hoConfig, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSetHandoverConfig(phoneId, hoConfig, listener),
+                    "setHandoverConfig", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public void setUssdInfoListener(int phoneId, IQtiImsExtListener listener) {
-            onSetUssdInfoListener(phoneId, listener);
+        public void setUssdInfoListener(int phoneId, IQtiImsExtListener listener)
+                throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSetUssdInfoListener(phoneId, listener),
+                    "setUssdInfoListener", mExecutor, QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public int setRcsAppConfig(int phoneId, int defaultSmsApp) {
-            return onSetRcsAppConfig(phoneId, defaultSmsApp);
+        public int setRcsAppConfig(int phoneId, int defaultSmsApp) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onSetRcsAppConfig(phoneId, defaultSmsApp),
+                    "setRcsAppConfig", getBinderExecutor(), QtiImsExtUtils.MODIFY_PHONE_STATE,
+                    mContext);
         }
 
         @Override
         public void setDataChannelCapabilityListener(int phoneId,
-                IQtiImsExtListener listener) {
-           onSetDataChannelCapabilityListener(phoneId, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSetDataChannelCapabilityListener(
+                    phoneId, listener), "setDataChannelCapabilityListener", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public int getRcsAppConfig(int phoneId) {
-            return onGetRcsAppConfig(phoneId);
-
+        public int getRcsAppConfig(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetRcsAppConfig(phoneId),
+                    "getRcsAppConfig", getBinderExecutor(),
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public int setVvmAppConfig(int phoneId, int defaultVvmApp) {
-            return onSetVvmAppConfig(phoneId, defaultVvmApp);
+        public int setVvmAppConfig(int phoneId, int defaultVvmApp) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onSetVvmAppConfig(phoneId, defaultVvmApp),
+                    "setVvmAppConfig", getBinderExecutor(),
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public int getVvmAppConfig(int phoneId) {
-            return onGetVvmAppConfig(phoneId);
+        public int getVvmAppConfig(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetVvmAppConfig(phoneId),
+                    "getVvmAppConfig", getBinderExecutor(),
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public IImsMultiIdentityInterface getMultiIdentityInterface(int phoneId) {
-            return onGetMultiIdentityInterface(phoneId);
+        public IImsMultiIdentityInterface getMultiIdentityInterface(int phoneId)
+                throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetMultiIdentityInterface(phoneId),
+                    "getMultiIdentityInterface", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public IImsScreenShareController getScreenShareController(int phoneId) {
-            return onGetScreenShareController(phoneId);
+        public IImsScreenShareController getScreenShareController(int phoneId)
+                throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetScreenShareController(phoneId),
+                    "getScreenShareController", mExecutor,
+                    QtiImsExtUtils.MODIFY_PHONE_STATE, mContext);
         }
 
         @Override
-        public int getImsFeatureState(int phoneId) {
-            return onGetImsFeatureState(phoneId);
+        public int getImsFeatureState(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetImsFeatureState(phoneId),
+                    "getImsFeatureState", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void setAnswerExtras(int phoneId, Bundle extras) {
-            onSetAnswerExtras(phoneId, extras);
+        public void setAnswerExtras(int phoneId, Bundle extras) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSetAnswerExtras(phoneId, extras),
+                    "setAnswerExtras", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public boolean isCallComposerEnabled(int phoneId) {
-            return onIsCallComposerEnabled(phoneId);
+        public boolean isCallComposerEnabled(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onIsCallComposerEnabled(phoneId),
+                    "isCallComposerEnabled", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public ICrsCrbtController getCrsCrbtController(int phoneId) {
-            return onGetCrsCrbtController(phoneId);
+        public ICrsCrbtController getCrsCrbtController(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onGetCrsCrbtController(phoneId),
+                    "getCrsCrbtController", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
         public void queryCallForwardStatus(int phoneId, int reason, int serviceClass,
-                boolean expectMore, IQtiImsExtListener listener) {
-            onQueryCallForwardStatus(phoneId, reason, serviceClass, expectMore, listener);
+                boolean expectMore, IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onQueryCallForwardStatus(phoneId, reason,
+                    serviceClass, expectMore, listener),
+                    "queryCallForwardStatus", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
         public void queryCallBarring(int phoneId, int cbType, String password, int serviceClass,
-                boolean expectMore, IQtiImsExtListener listener) {
-            onQueryCallBarringStatus(phoneId, cbType, password, serviceClass, expectMore,
-                    listener);
+                boolean expectMore, IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onQueryCallBarringStatus(phoneId, cbType,
+                    password, serviceClass, expectMore, listener),
+                    "queryCallBarring", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public void exitScbm(int phoneId, IQtiImsExtListener listener) {
-            onExitScbm(phoneId, listener);
+        public void exitScbm(int phoneId, IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onExitScbm(phoneId, listener),
+                    "exitScbm", mExecutor, QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public boolean isExitScbmFeatureSupported(int phoneId) {
-            return onIsExitScbmFeatureSupported(phoneId);
+        public boolean isExitScbmFeatureSupported(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onIsExitScbmFeatureSupported(phoneId),
+                    "isExitScbmFeatureSupported", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
-        public boolean isDataChannelEnabled(int phoneId) {
-            return onIsDataChannelEnabled(phoneId);
+        public boolean isDataChannelEnabled(int phoneId) throws RemoteException {
+            return QtiImsExtUtils.executeMethodAsyncForResult(() ->
+                    QtiImsExtBase.this.onIsDataChannelEnabled(phoneId),
+                    "isDataChannelEnabled", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
         public void sendVosSupportStatus(int phoneId, boolean isVosSupported,
-                IQtiImsExtListener listener) {
-            onSendVosSupportStatus(phoneId, isVosSupported, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() ->
+                    QtiImsExtBase.this.onSendVosSupportStatus(phoneId,
+                    isVosSupported, listener),
+                    "sendVosSupportStatus", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
 
         @Override
         public void sendVosActionInfo(int phoneId, VosActionInfo vosActionInfo,
-                IQtiImsExtListener listener) {
-            onSendVosActionInfo(phoneId, vosActionInfo, listener);
+                IQtiImsExtListener listener) throws RemoteException {
+            QtiImsExtUtils.executeMethodAsync(() -> QtiImsExtBase.this.onSendVosActionInfo(phoneId,
+                    vosActionInfo, listener),
+                    "sendVosActionInfo", mExecutor,
+                    QtiImsExtUtils.READ_PHONE_STATE, mContext);
         }
     };
 
     private QtiImsExtBinder mQtiImsExtBinder;
+    private Executor mExecutor;
+    private Executor mBinderExecutor = Runnable::run;
+    private Context mContext;
 
     public QtiImsExtBinder getBinder() {
         if (mQtiImsExtBinder == null) {
@@ -224,6 +322,15 @@
         return mQtiImsExtBinder;
     }
 
+    public QtiImsExtBase(Executor executor, Context context) {
+        mExecutor = executor;
+        mContext = context;
+    }
+
+    private Executor getBinderExecutor() {
+        return mBinderExecutor;
+    }
+
     protected void onSetCallForwardUncondTimer(int phoneId, int startHour, int startMinute,
             int endHour, int endMinute, int action, int condition, int serviceClass, String number,
             IQtiImsExtListener listener) {
diff --git a/ims/ims-ext-common/src/org/codeaurora/ims/utils/QtiImsExtUtils.java b/ims/ims-ext-common/src/org/codeaurora/ims/utils/QtiImsExtUtils.java
index cf5d2a5..ded9d1b 100644
--- a/ims/ims-ext-common/src/org/codeaurora/ims/utils/QtiImsExtUtils.java
+++ b/ims/ims-ext-common/src/org/codeaurora/ims/utils/QtiImsExtUtils.java
@@ -63,6 +63,7 @@
 
 package org.codeaurora.ims.utils;
 
+import android.Manifest;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -72,6 +73,7 @@
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -92,6 +94,13 @@
 import org.codeaurora.ims.QtiImsException;
 import org.codeaurora.ims.QtiImsExtManager;
 
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
 /**
  * This class contains QtiImsExt specific utiltity functions.
  */
@@ -188,6 +197,10 @@
     /*RTT upgrade not supported */
     public static final int QTI_IMS_RTT_NOT_SUPPORTED = 0;
 
+    // Phone permissions
+    public static final String READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE;
+    public static final String MODIFY_PHONE_STATE = Manifest.permission.MODIFY_PHONE_STATE;
+
     /**
      * Private constructor for QtiImsExtUtils as we don't want to instantiate this class
      */
@@ -860,4 +873,29 @@
         return isCarrierConfigEnabled(phoneId, context,
                 QtiCarrierConfigs.KEY_CARRIER_VIDEO_ONLINE_SERVICE_SUPPORTED);
     }
+
+    public static void executeMethodAsync(Runnable r, String errorLogName, Executor executor,
+            String permission, Context context) throws RemoteException {
+        context.enforceCallingOrSelfPermission(permission, errorLogName);
+        try {
+            CompletableFuture.runAsync(r, executor).join();
+        } catch (CancellationException | CompletionException e) {
+            Log.w(LOG_TAG, "executeMethodAsync for " + errorLogName + " failed with: " +
+                    e.getMessage());
+            throw new RemoteException(e.getMessage());
+        }
+    }
+
+    public static <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName,
+            Executor executor, String permission, Context context) throws RemoteException {
+        context.enforceCallingOrSelfPermission(permission, errorLogName);
+        try {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(r, executor);
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "executeMethodAsyncForResult for " + errorLogName + " failed with: " +
+                    e.getMessage());
+            throw new RemoteException(e.getMessage());
+        }
+    }
 }