Merge changes Id458f4e0,Id51aee37

* changes:
  mainline:Replace serviceState.getDataRegState and getVoiceRegState
  [Mainline] move ServiceStateProvider to TeleService
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c36b1a..6906089 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -257,6 +257,14 @@
          audio stream which the remote party will be able to hear. -->
     <bool name="config_support_telephony_audio_device">false</bool>
 
+    <string-array translatable="false" name="config_volte_provision_error_on_publish_response">
+        <item>403 not authorized for presence</item>
+    </string-array>
+
+    <string-array translatable="false" name="config_rcs_provision_error_on_publish_response">
+        <item>404 not found</item>
+    </string-array>
+
     <!-- The country list that shortcut view can be enabled. -->
     <string-array name="config_countries_to_enable_shortcut_view" translatable="false">
     </string-array>
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index ff38754..46b1930 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -29,6 +29,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -53,9 +54,9 @@
     public static boolean isVoipSupported(Context context) {
         return SipManager.isVoipSupported(context) &&
                 context.getResources().getBoolean(
-                        com.android.internal.R.bool.config_built_in_sip_phone) &&
-                context.getResources().getBoolean(
-                        com.android.internal.R.bool.config_voice_capable);
+                        com.android.internal.R.bool.config_built_in_sip_phone)
+                && ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .isVoiceCapable();
     }
 
     static PendingIntent createIncomingCallPendingIntent(
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 1a28afd..9d9010a 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -37,6 +37,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.services.telephony.rcs.TelephonyRcsService;
 
 import java.util.List;
 
@@ -50,6 +51,7 @@
     private static ImsRcsController sInstance;
 
     private PhoneGlobals mApp;
+    private TelephonyRcsService mRcsService;
 
     /**
      * Initialize the singleton ImsRcsController instance.
@@ -339,4 +341,8 @@
         }
         return rcsFeatureManager;
     }
+
+    void setRcsService(TelephonyRcsService rcsService) {
+        mRcsService = rcsService;
+    }
 }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 9ecb2c5..a694e91 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -71,6 +71,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
+import com.android.services.telephony.rcs.TelephonyRcsService;
 import com.android.services.telephony.sip.SipAccountRegistry;
 import com.android.services.telephony.sip.SipUtil;
 
@@ -149,6 +150,7 @@
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
     ImsResolver mImsResolver;
+    TelephonyRcsService mTelephonyRcsService;
     public PhoneInterfaceManager phoneMgr;
     public ImsRcsController imsRcsController;
     CarrierConfigLoader configLoader;
@@ -303,8 +305,8 @@
         // Cache the "voice capable" flag.
         // This flag currently comes from a resource (which is
         // overrideable on a per-product basis):
-        sVoiceCapable =
-                getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
+        sVoiceCapable = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
+                .isVoiceCapable();
         // ...but this might eventually become a PackageManager "system
         // feature" instead, in which case we'd do something like:
         // sVoiceCapable =
@@ -372,6 +374,11 @@
 
             imsRcsController = ImsRcsController.init(this);
 
+            if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+                mTelephonyRcsService = new TelephonyRcsService(this);
+                imsRcsController.setRcsService(mTelephonyRcsService);
+            }
+
             configLoader = CarrierConfigLoader.init(this);
 
             // Create the CallNotifier singleton, which handles
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 1a93823..2f0add1 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3430,11 +3430,10 @@
         enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (isImsAvailableOnDevice()) {
-                throw new ImsException("IMS not available on device.",
-                        ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
             }
-
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
             ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                     .addProvisioningCallbackForSubscription(callback, subId);
diff --git a/src/com/android/services/telephony/rcs/PresenceHelper.java b/src/com/android/services/telephony/rcs/PresenceHelper.java
new file mode 100644
index 0000000..5f7e35f
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/PresenceHelper.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.RcsFeatureConnection;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsRcsStatusListener;
+import com.android.phone.R;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresenceSubscriber;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+class PresenceHelper {
+
+    private static final String LOG_TAG = "PresenceHelper";
+
+    private final Context mContext;
+    private final List<Phone> mPhones;
+
+    private final SparseArray<PresencePublication> mPresencePublications = new SparseArray<>();
+    private final SparseArray<PresenceSubscriber> mPresenceSubscribers = new SparseArray<>();
+
+    PresenceHelper(Context context) {
+        mContext = context;
+
+        // Get phones
+        Phone[] phoneAry = PhoneFactory.getPhones();
+        mPhones = (phoneAry != null) ? Arrays.asList(phoneAry) : new ArrayList<>();
+
+        initRcsPresencesInstance();
+        registerRcsConnectionStatus();
+
+        Log.i(LOG_TAG, "initialized: phone size=" + mPhones.size());
+    }
+
+    private void initRcsPresencesInstance() {
+        String[] volteError = mContext.getResources().getStringArray(
+                R.array.config_volte_provision_error_on_publish_response);
+        String[] rcsError = mContext.getResources().getStringArray(
+                R.array.config_rcs_provision_error_on_publish_response);
+
+        mPhones.forEach((phone) -> {
+            RcsFeatureConnection rcsConnection = getRcsFeatureConnection(phone);
+            // Initialize PresencePublication
+            mPresencePublications.put(
+                    phone.getPhoneId(),
+                    new PresencePublication(rcsConnection, mContext, volteError, rcsError));
+            // Initialize PresenceSubscriber
+            mPresenceSubscribers.put(
+                    phone.getPhoneId(),
+                    new PresenceSubscriber(rcsConnection, mContext, volteError, rcsError));
+        });
+    }
+
+    private @Nullable RcsFeatureConnection getRcsFeatureConnection(Phone phone) {
+        ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+        if (imsPhone != null) {
+            RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
+            if (rcsFeatureManager != null) {
+                return rcsFeatureManager.getRcsFeatureConnection();
+            }
+        }
+        return null;
+    }
+
+    /*
+     * RcsFeatureManager in ImsPhone is not null only when RCS is connected. Register a callback to
+     * receive the RCS connection status.
+     */
+    private void registerRcsConnectionStatus() {
+        mPhones.forEach((phone) -> {
+            ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+            if (imsPhone != null) {
+                imsPhone.setRcsStatusListener(mStatusListener);
+            }
+        });
+    }
+
+    /**
+     * The IMS RCS status listener to listen the status changed
+     */
+    private ImsRcsStatusListener mStatusListener = new ImsRcsStatusListener() {
+        @Override
+        public void onRcsConnected(int phoneId, RcsFeatureManager rcsFeatureManager) {
+            int subId = getSubscriptionId(phoneId);
+            if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                Log.e(LOG_TAG, "onRcsConnected: invalid subId, phoneId=" + phoneId);
+                return;
+            }
+
+            Log.i(LOG_TAG, "onRcsConnected: phoneId=" + phoneId + ", subId=" + subId);
+            RcsFeatureConnection connection = rcsFeatureManager.getRcsFeatureConnection();
+            PresencePublication presencePublication = getPresencePublication(phoneId);
+            if (presencePublication != null) {
+                Log.i(LOG_TAG, "Update PresencePublisher because RCS is connected");
+                presencePublication.updatePresencePublisher(subId, connection);
+            }
+            PresenceSubscriber presenceSubscriber = getPresenceSubscriber(phoneId);
+            if (presenceSubscriber != null) {
+                Log.i(LOG_TAG, "Update PresenceSubscriber because RCS is connected");
+                presenceSubscriber.updatePresenceSubscriber(subId, connection);
+            }
+        }
+
+        @Override
+        public void onRcsDisconnected(int phoneId) {
+            int subId = getSubscriptionId(phoneId);
+            Log.i(LOG_TAG, "onRcsDisconnected: phoneId=" + phoneId + ", subId=" + subId);
+            PresencePublication publication = getPresencePublication(phoneId);
+            if (publication != null) {
+                Log.i(LOG_TAG, "Remove PresencePublisher because RCS is disconnected");
+                publication.removePresencePublisher(subId);
+            }
+
+            PresenceSubscriber subscriber = getPresenceSubscriber(phoneId);
+            if (subscriber != null) {
+                Log.i(LOG_TAG, "Remove PresencePublisher because RCS is disconnected");
+                subscriber.removePresenceSubscriber(subId);
+            }
+        }
+    };
+
+    private int getSubscriptionId(int phoneId) {
+        Optional<Phone> phone = mPhones.stream()
+                .filter(p -> p.getPhoneId() == phoneId).findFirst();
+        if (phone.isPresent()) {
+            return phone.get().getSubId();
+        }
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    public @Nullable PresencePublication getPresencePublication(int phoneId) {
+        return mPresencePublications.get(phoneId);
+    }
+
+    public @Nullable PresenceSubscriber getPresenceSubscriber(int phoneId) {
+        return mPresenceSubscribers.get(phoneId);
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
new file mode 100644
index 0000000..74765d6
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresenceSubscriber;
+
+/**
+ * Telephony RCS Service integrates PresencePublication and PresenceSubscriber into the service.
+ */
+public class TelephonyRcsService {
+
+    private static final String LOG_TAG = "TelephonyRcsService";
+
+    private final Context mContext;
+
+    // A helper class to manage the RCS Presences instances.
+    private final PresenceHelper mPresenceHelper;
+
+    public TelephonyRcsService(Context context) {
+        Log.i(LOG_TAG, "initialize");
+        mContext = context;
+        mPresenceHelper = new PresenceHelper(mContext);
+    }
+
+    private PresencePublication getPresencePublication(int phoneId) {
+        return mPresenceHelper.getPresencePublication(phoneId);
+    }
+
+    private PresenceSubscriber getPresenceSubscriber(int phoneId) {
+        return mPresenceHelper.getPresenceSubscriber(phoneId);
+    }
+}