Merge "Integrate Call Forwarding / Waiting function in Telephony"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 72a0cef..ed86560 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -60,7 +60,6 @@
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
     <protected-broadcast android:name= "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
-    <protected-broadcast android:name= "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG" />
     <protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
     <protected-broadcast android:name= "com.android.intent.isim_refresh" />
     <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
diff --git a/OWNERS b/OWNERS
index 849347f..3059d4d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,15 +1,15 @@
 amitmahajan@google.com
 breadley@google.com
 fionaxu@google.com
-hallliu@google.com
 jackyu@google.com
+hallliu@google.com
 rgreenwalt@google.com
 tgunn@google.com
-refuhoo@google.com
-mpq@google.com
 jminjie@google.com
 shuoq@google.com
-paulye@google.com
+refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
 dbright@google.com
+xiaotonj@google.com
+
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 0052cd0..3d40c86 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -39,6 +39,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.service.carrier.CarrierIdentifier;
 import android.service.carrier.CarrierService;
@@ -634,7 +635,7 @@
         }
         intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
         log("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
-        mContext.sendBroadcast(intent);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
         mHasSentConfigChange[phoneId] = true;
     }
 
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 119c71b..c6bac02 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -40,7 +40,6 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.provider.Settings;
-import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
@@ -490,7 +489,7 @@
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
 
-            placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
+            placeCall(phoneNumber, TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT,
                     mShortcutViewConfig.getPhoneInfo());
         } else {
             Log.d(LOG_TAG, "emergency number is empty");
@@ -734,7 +733,7 @@
                 return;
             }
 
-            placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
+            placeCall(mLastNumber, TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD,
                     phoneToMakeCall);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
@@ -1172,9 +1171,9 @@
 
     private String callSourceToString(int callSource) {
         switch (callSource) {
-            case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD:
+            case TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD:
                 return "DialPad";
-            case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT:
+            case TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT:
                 return "Shortcut";
             default:
                 return "Unknown-" + callSource;
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index ffac202..1246574 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -20,6 +20,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.RegistrationManager;
@@ -249,23 +250,35 @@
     public void requestCapabilities(int subId, List<Uri> contactNumbers,
             IRcsUceControllerCallback c) {
         enforceReadPrivilegedPermission("requestCapabilities");
+        if (mRcsService == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS is not available on device.");
+        }
+        mRcsService.requestCapabilities(getImsPhone(subId).getPhoneId(), contactNumbers, c);
     }
 
     @Override
     public int getUcePublishState(int subId) {
         enforceReadPrivilegedPermission("getUcePublishState");
-        return -1;
+        if (mRcsService == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS is not available on device.");
+        }
+        return mRcsService.getUcePublishState(getImsPhone(subId).getPhoneId());
     }
 
     @Override
     public boolean isUceSettingEnabled(int subId) {
         enforceReadPrivilegedPermission("isUceSettingEnabled");
-        return false;
+        return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+                SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
     }
 
     @Override
     public void setUceSettingEnabled(int subId, boolean isEnabled) {
         enforceModifyPermission();
+        SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
+                (isEnabled ? "1" : "0"));
     }
 
     /**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 2928554..32fca63 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -152,6 +152,7 @@
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
 import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.dataconnection.ApnSettingUtils;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
@@ -311,8 +312,6 @@
 
     /** User Activity */
     private AtomicBoolean mNotifyUserActivity;
-    private static final String ACTION_USER_ACTIVITY_NOTIFICATION =
-            "android.intent.action.USER_ACTIVITY_NOTIFICATION";
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
 
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
@@ -1456,7 +1455,7 @@
 
                 case MSG_NOTIFY_USER_ACTIVITY:
                     removeMessages(MSG_NOTIFY_USER_ACTIVITY);
-                    Intent intent = new Intent(ACTION_USER_ACTIVITY_NOTIFICATION);
+                    Intent intent = new Intent(TelephonyIntents.ACTION_USER_ACTIVITY_NOTIFICATION);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     getDefaultPhone().getContext().sendBroadcastAsUser(
                             intent, UserHandle.ALL, permission.USER_ACTIVITY);
@@ -1631,6 +1630,7 @@
                 PreferenceManager.getDefaultSharedPreferences(mApp);
         mNetworkScanRequestTracker = new NetworkScanRequestTracker();
         mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
+        mNotifyUserActivity = new AtomicBoolean(false);
 
         publish();
     }
@@ -2250,6 +2250,7 @@
                                 .setCallingPid(Binder.getCallingPid())
                                 .setCallingUid(Binder.getCallingUid())
                                 .setMethod("getCellLocation")
+                                .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE)
                                 .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
                                 .build());
         switch (locationResult) {
@@ -4916,6 +4917,27 @@
     }
 
     /**
+     * Toggle IMS disable and enable for the framework to reset it. See {@link #enableIms(int)} and
+     * {@link #disableIms(int)}.
+     * @param slotIndex device slot.
+     */
+    public void resetIms(int slotIndex) {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (mImsResolver == null) {
+                // may happen if the does not support IMS.
+                return;
+            }
+            mImsResolver.disableIms(slotIndex);
+            mImsResolver.enableIms(slotIndex);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
      * status updates, if not already enabled.
      */
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index c27a82b..57caede 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.euicc.EuiccConnector;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.HashSet;
 import java.util.List;
@@ -176,7 +177,7 @@
                             Log.e(TAG, "Failed to revoke LUI app permissions.");
                         }
                     });
-            waitUntilReady(latch);
+            TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
         } catch (RuntimeException e) {
             Log.e(TAG, "Failed to grant permissions to active LUI app.", e);
         }
@@ -197,7 +198,7 @@
                             Log.e(TAG, "Failed to revoke LUI app permissions.");
                         }
                     });
-            waitUntilReady(latch);
+            TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
         } catch (RuntimeException e) {
             Log.e(TAG, "Failed to revoke LUI app permissions.");
             throw e;
@@ -215,11 +216,4 @@
         }
         return packageNames;
     }
-
-    private void waitUntilReady(CountDownLatch latch) {
-        try {
-            latch.await(CHANGE_PERMISSION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-        }
-    }
 }
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9e32f00..d46c715 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -857,7 +857,7 @@
             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
                 // Do not play any tones if disconnected because of a successful merge.
             default:
-                return ToneGenerator.TONE_UNKNOWN;
+                return -1;
         }
     }
 }
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index dd000ce..f5f5c66 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -540,7 +540,7 @@
         if (mConferenceHost == null) {
             return;
         }
-        mConferenceHost.performReject();
+        mConferenceHost.performReject(android.telecom.Call.REJECT_REASON_DECLINED);
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 919d3b2..adfae1b 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -890,9 +890,8 @@
             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
                 Log.i(this, "User changed, re-registering phone accounts.");
 
-                int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                UserHandle currentUserHandle = UserHandle.of(userHandleId);
-                mIsPrimaryUser = currentUserHandle.isSystem();
+                UserHandle currentUser = intent.getParcelableExtra(Intent.EXTRA_USER);
+                mIsPrimaryUser = currentUser == null ? true : currentUser.isSystem();
 
                 // Any time the user changes, re-register the accounts.
                 tearDownAccounts();
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f2b2244..2f5e2a6 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -113,6 +113,7 @@
     private static final int MSG_SET_CALL_RADIO_TECH = 18;
     private static final int MSG_ON_CONNECTION_EVENT = 19;
     private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
+    private static final int MSG_REJECT = 21;
 
     private List<Uri> mParticipants;
     private boolean mIsAdhocConferenceCall;
@@ -273,6 +274,10 @@
                     int cause = (int) msg.obj;
                     hangup(cause);
                     break;
+                case MSG_REJECT:
+                    int rejectReason = (int) msg.obj;
+                    reject(rejectReason);
+                    break;
 
                 case MSG_SET_CALL_RADIO_TECH:
                     int vrat = (int) msg.obj;
@@ -926,13 +931,18 @@
 
     @Override
     public void onReject() {
-        performReject();
+        performReject(android.telecom.Call.REJECT_REASON_DECLINED);
     }
 
-    public void performReject() {
+    @Override
+    public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+        performReject(rejectReason);
+    }
+
+    public void performReject(int rejectReason) {
         Log.v(this, "performReject");
         if (isValidRingingCall()) {
-            mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
+            mHandler.obtainMessage(MSG_REJECT, rejectReason)
                     .sendToTarget();
         }
         super.onReject();
@@ -1678,6 +1688,46 @@
         }
     }
 
+    protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
+        if (mOriginalConnection != null) {
+            mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
+            try {
+                // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
+                // connection.hangup(). Without this change, the party originating the call
+                // will not get sent to voicemail if the user opts to reject the call.
+                if (isValidRingingCall()) {
+                    Call call = getCall();
+                    if (call != null) {
+                        call.hangup(rejectReason);
+                    } else {
+                        Log.w(this, "Attempting to hangup a connection without backing call.");
+                    }
+                } else {
+                    // We still prefer to call connection.hangup() for non-ringing calls
+                    // in order to support hanging-up specific calls within a conference call.
+                    // If we invoked call.hangup() while in a conference, we would end up
+                    // hanging up the entire conference call instead of the specific connection.
+                    mOriginalConnection.hangup();
+                }
+            } catch (CallStateException e) {
+                Log.e(this, e, "Call to Connection.hangup failed with exception");
+            }
+        } else {
+            if (getState() == STATE_DISCONNECTED) {
+                Log.i(this, "hangup called on an already disconnected call!");
+                close();
+            } else {
+                // There are a few cases where mOriginalConnection has not been set yet. For
+                // example, when the radio has to be turned on to make an emergency call,
+                // mOriginalConnection could not be set for many seconds.
+                setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                        android.telephony.DisconnectCause.LOCAL,
+                        "Local Disconnect before connection established."));
+                close();
+            }
+        }
+    }
+
     com.android.internal.telephony.Connection getOriginalConnection() {
         return mOriginalConnection;
     }
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 74765d6..0e8f8de 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -17,11 +17,25 @@
 package com.android.services.telephony.rcs;
 
 import android.content.Context;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.util.Log;
 
+import com.android.ims.ResultCode;
+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.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
 /**
  * Telephony RCS Service integrates PresencePublication and PresenceSubscriber into the service.
  */
@@ -34,10 +48,104 @@
     // A helper class to manage the RCS Presences instances.
     private final PresenceHelper mPresenceHelper;
 
+    private ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingRequests;
+
     public TelephonyRcsService(Context context) {
         Log.i(LOG_TAG, "initialize");
         mContext = context;
         mPresenceHelper = new PresenceHelper(mContext);
+        mPendingRequests = new ConcurrentHashMap<>();
+    }
+
+    /**
+     * @return the UCE Publish state for the phone ID specified.
+     */
+    public int getUcePublishState(int phoneId) {
+        PresencePublication publisher = getPresencePublication(phoneId);
+        if (publisher == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                    "UCE service is not currently running.");
+        }
+        int publishState = publisher.getPublishState();
+        return toUcePublishState(publishState);
+    }
+
+    /**
+     * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
+     */
+    public void requestCapabilities(int phoneId, List<Uri> contactNumbers,
+            IRcsUceControllerCallback c) {
+        PresenceSubscriber subscriber = getPresenceSubscriber(phoneId);
+        if (subscriber == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                    "UCE service is not currently running.");
+        }
+        List<String> numbers = contactNumbers.stream().map(TelephonyRcsService::getNumberFromUri)
+                .collect(Collectors.toList());
+        int taskId = subscriber.requestCapability(numbers, new ContactCapabilityResponse() {
+            @Override
+            public void onSuccess(int reqId) {
+                Log.i(LOG_TAG, "onSuccess called for reqId:" + reqId);
+            }
+
+            @Override
+            public void onError(int reqId, int resultCode) {
+                IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+                try {
+                    if (c != null) {
+                        c.onError(toUceError(resultCode));
+                    } else {
+                        Log.w(LOG_TAG, "onError called for unknown reqId:" + reqId);
+                    }
+                } catch (RemoteException e) {
+                    Log.i(LOG_TAG, "Calling back to dead service");
+                }
+            }
+
+            @Override
+            public void onFinish(int reqId) {
+                Log.i(LOG_TAG, "onFinish called for reqId:" + reqId);
+            }
+
+            @Override
+            public void onTimeout(int reqId) {
+                IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+                try {
+                    if (c != null) {
+                        c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
+                    } else {
+                        Log.w(LOG_TAG, "onTimeout called for unknown reqId:" + reqId);
+                    }
+                } catch (RemoteException e) {
+                    Log.i(LOG_TAG, "Calling back to dead service");
+                }
+            }
+
+            @Override
+            public void onCapabilitiesUpdated(int reqId,
+                    List<RcsContactUceCapability> contactCapabilities,
+                    boolean updateLastTimestamp) {
+                IRcsUceControllerCallback c = mPendingRequests.remove(reqId);
+                try {
+                    if (c != null) {
+                        c.onCapabilitiesReceived(contactCapabilities);
+                    } else {
+                        Log.w(LOG_TAG, "onCapabilitiesUpdated, unknown reqId:" + reqId);
+                    }
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, "onCapabilitiesUpdated on dead service");
+                }
+            }
+        });
+        if (taskId < 0) {
+            try {
+                c.onError(toUceError(taskId));
+                return;
+            } catch (RemoteException e) {
+                Log.i(LOG_TAG, "Calling back to dead service");
+            }
+        }
+        mPendingRequests.put(taskId, c);
     }
 
     private PresencePublication getPresencePublication(int phoneId) {
@@ -47,4 +155,56 @@
     private PresenceSubscriber getPresenceSubscriber(int phoneId) {
         return mPresenceHelper.getPresenceSubscriber(phoneId);
     }
+
+    private static String getNumberFromUri(Uri uri) {
+        String number = uri.getSchemeSpecificPart();
+        String[] numberParts = number.split("[@;:]");
+
+        if (numberParts.length == 0) {
+            return null;
+        }
+        return numberParts[0];
+    }
+
+    private static int toUcePublishState(int publishState) {
+        switch (publishState) {
+            case PresenceBase.PUBLISH_STATE_200_OK:
+                return RcsUceAdapter.PUBLISH_STATE_200_OK;
+            case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
+                return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
+            case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
+                return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
+                return  RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
+                return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
+            case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
+                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+            default:
+                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+        }
+    }
+
+    private static int toUceError(int resultCode) {
+        switch(resultCode) {
+            case ResultCode.SUBSCRIBE_NOT_REGISTERED:
+                return RcsUceAdapter.ERROR_NOT_REGISTERED;
+            case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
+                return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
+            case ResultCode.SUBSCRIBE_FORBIDDEN:
+                return RcsUceAdapter.ERROR_FORBIDDEN;
+            case ResultCode.SUBSCRIBE_NOT_FOUND:
+                return RcsUceAdapter.ERROR_NOT_FOUND;
+            case ResultCode.SUBSCRIBE_TOO_LARGE:
+                return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
+            case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
+                return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
+            case ResultCode.SUBSCRIBE_LOST_NETWORK:
+                return RcsUceAdapter.ERROR_LOST_NETWORK;
+            case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
+                return  RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
+            default:
+                return RcsUceAdapter.ERROR_GENERIC_FAILURE;
+        }
+    }
 }