Merge "Send Empty packet in MSRP"
diff --git a/res/values/config.xml b/res/values/config.xml
index 7ce141e..08a84f8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -310,4 +310,7 @@
     <!-- Whether or not to support device to device communication using RTP and DTMF communication
          transports. -->
     <bool name="config_use_device_to_device_communication">false</bool>
+
+    <!-- Whether or not to show notifications for when bluetooth connection is bad during a call -->
+    <bool name="enable_bluetooth_call_quality_notification">false</bool>
 </resources>
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 9334078..701a759 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,6 +16,7 @@
 
 package com.android.phone;
 
+import android.app.ActivityManager;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -270,7 +271,11 @@
     @Override
     public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestCapabilities");
+        enforceAccessUserCapabilityExchangePermission("requestCapabilities");
+        enforceReadContactsPermission("requestCapabilities");
+        if (!isCallingProcessInForeground(Binder.getCallingUid())) {
+            throw new SecurityException("The caller is not in the foreground.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -290,7 +295,11 @@
     @Override
     public void requestAvailability(int subId, String callingPackage,
             String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestAvailability");
+        enforceAccessUserCapabilityExchangePermission("requestAvailability");
+        enforceReadContactsPermission("requestAvailability");
+        if (!isCallingProcessInForeground(Binder.getCallingUid())) {
+            throw new SecurityException("The caller is not in the foreground.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -548,6 +557,39 @@
     }
 
     /**
+     * Make sure the caller has the ACCESS_RCS_USER_CAPABILITY_EXCHANGE permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceAccessUserCapabilityExchangePermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, message);
+    }
+
+    /**
+     * Make sure the caller has the READ_CONTACTS permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceReadContactsPermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_CONTACTS, message);
+    }
+
+    /**
+     * Check if the calling process is in the foreground.
+     *
+     * @return true if the caller is in the foreground.
+     */
+    private boolean isCallingProcessInForeground(int uid) {
+        ActivityManager am = mApp.getSystemService(ActivityManager.class);
+        boolean isCallingProcessForeground = am != null
+                && am.getUidImportance(uid)
+                        == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+        return isCallingProcessForeground;
+    }
+
+    /**
      * Retrieve ImsPhone instance.
      *
      * @param subId the subscription ID
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9510cae..111a38c 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -88,7 +88,6 @@
 import android.telephony.PhoneNumberRange;
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.RadioInterfaceCapabilities;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SignalStrengthUpdateRequest;
@@ -9405,12 +9404,12 @@
     @Override
     public boolean isRadioInterfaceCapabilitySupported(
             @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
-        RadioInterfaceCapabilities radioInterfaceCapabilities =
+        Set<String> radioInterfaceCapabilities =
                 mPhoneConfigurationManager.getRadioInterfaceCapabilities();
         if (radioInterfaceCapabilities == null) {
             throw new RuntimeException("radio interface capabilities are not available");
         } else {
-            return radioInterfaceCapabilities.isSupported(capability);
+            return radioInterfaceCapabilities.contains(capability);
         }
     }
 
diff --git a/src/com/android/services/telephony/CallQualityManager.java b/src/com/android/services/telephony/CallQualityManager.java
index 0e32ddc..c8785d6 100644
--- a/src/com/android/services/telephony/CallQualityManager.java
+++ b/src/com/android/services/telephony/CallQualityManager.java
@@ -90,6 +90,11 @@
     }
 
     private void popUpNotification(String title, CharSequence details) {
+        if (!mContext.getResources().getBoolean(
+                R.bool.enable_bluetooth_call_quality_notification)) {
+            Log.d(TAG, "Bluetooth call quality notifications not enabled.");
+            return;
+        }
         int iconId = android.R.drawable.stat_notify_error;
 
         Notification notification = new Notification.Builder(mContext)
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 8f2d6bd..6bd5f32 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -19,8 +19,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.sample.rcsclient"
-    android:versionCode="5"
-    android:versionName="1.0.4">
+    android:versionCode="6"
+    android:versionName="1.0.5">
 
     <uses-sdk
         android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
index 1db4af7..0531209 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -168,8 +168,10 @@
                 });
             });
         } catch (Exception e) {
-            Log.e(TAG, e.getMessage());
+            Log.e(TAG, "Exception: " + e);
             e.printStackTrace();
+            Toast.makeText(this, getResources().getString(R.string.session_failed),
+                    Toast.LENGTH_SHORT).show();
         }
     }
 
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
index ac37110..f1868fc 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
@@ -47,6 +47,7 @@
 import com.google.common.util.concurrent.SettableFuture;
 
 import java.text.ParseException;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -152,6 +153,7 @@
                 new DelegateConnectionMessageCallback() {
                     @Override
                     public void onMessageReceived(@NonNull SipMessage message) {
+                        message = repairHeaderSection(message);
                         SipSessionListener listener = sipSessionListener;
                         if (listener != null) {
                             try {
@@ -386,6 +388,9 @@
             String serviceRoutes =
                     configuration.getString(
                             SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING);
+            if (TextUtils.isEmpty(serviceRoutes)) {
+                return Collections.emptyList();
+            }
             return Splitter.on(',').trimResults().splitToList(serviceRoutes);
         }
 
@@ -424,6 +429,23 @@
             return configuration.getInt(
                     SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, 1500);
         }
+
+        /**
+         * There is a modem issue where "ia:" is returned back instead of "Via:". Fix that locally
+         * for now.
+         * @return A SipMessage with the corrected header section.
+         */
+        private static SipMessage repairHeaderSection(SipMessage message) {
+            String headers = message.getHeaderSection();
+
+            if (headers.startsWith("ia:")) {
+                headers = "V" + headers;
+                Log.i(TAG, "repairHeaderSection: detected malformed via: "
+                        + message.getHeaderSection().substring(0, 10) + "->"
+                        + headers.substring(0, 10));
+            }
+            return new SipMessage(message.getStartLine(), headers, message.getContent());
+        }
     }
 }