Merge "Integrate in cl/356241895 into testRcsApp"
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java
index 5c2e995..2723940 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java
@@ -35,14 +35,14 @@
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.List;
 
 import javax.sip.message.Message;
 import javax.sip.message.Request;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @RunWith(AndroidJUnit4.class)
 public class SimpleChatSessionTest {
     private static final String LOCAL_URI = "tel:+1234567890";
@@ -126,6 +126,16 @@
                 public String getPlaniHeader() {
                     return "IEEE-802.11;i-wlan-node-id=PLANI01EB5B0";
                 }
+
+                @Override
+                public String getUserAgentHeader() {
+                    return "Test-Client";
+                }
+
+                @Override
+                public int getMaxPayloadSizeOnUdp() {
+                    return 0;
+                }
             };
     private final SimpleRcsClientContext context =
             new SimpleRcsClientContext(
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java
index b621257..6bb8eec 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java
@@ -16,8 +16,9 @@
 
 package com.android.libraries.rcs.simpleclient.protocol.cpim;
 
-import java.time.LocalDate;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Random;
 
 /** Collections of utility functions for CPIM */
@@ -28,6 +29,7 @@
     private CpimUtils() {
     }
 
+    @SuppressWarnings("AndroidJdkLibsChecker")
     public static SimpleCpimMessage createForText(String text) {
         return SimpleCpimMessage.newBuilder()
                 .addNamespace("imdn", "urn:ietf:params:imdn")
@@ -35,7 +37,8 @@
                 .addHeader("imdn.Disposition-Notification", "positive-delivery, display")
                 .addHeader("To", ANONYMOUS_URI)
                 .addHeader("From", ANONYMOUS_URI)
-                .addHeader("DateTime", LocalDate.now(ZoneId.systemDefault()).toString())
+                .addHeader("DateTime", ZonedDateTime.now(ZoneId.systemDefault()).format(
+                        DateTimeFormatter.ISO_INSTANT))
                 .setContentType("text/plain")
                 .setContent(text)
                 .build();
@@ -43,6 +46,6 @@
 
     private static String generateImdnMessageId() {
         Random random = new Random();
-        return "Test_" + random.nextLong();
+        return "Test_" + random.nextInt(1000000);
     }
 }
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java
index ad5f1f5..e2efafe 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java
@@ -68,8 +68,9 @@
 
         builder.append(CRLF);
         builder.append("Content-Type").append(COLSP).append(contentType());
-        builder.append("Content-Length").append(COLSP).append(Utf8.encodedLength(content()));
         builder.append(CRLF);
+        builder.append("Content-Length").append(COLSP).append(Utf8.encodedLength(content()));
+        builder.append(CRLF).append(CRLF);
         builder.append(content());
 
         return builder.toString();
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java
index ad1b98e..ba424c6 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java
@@ -44,8 +44,12 @@
     public static final String HEADER_TO_PATH = "To-Path";
     public static final String HEADER_FROM_PATH = "From-Path";
     public static final String HEADER_FAILURE_REPORT = "Failure-Report";
+    public static final String HEADER_SUCCESS_REPORT = "Success-Report";
+    public static final String REPORT_VALUE_YES = "yes";
+    public static final String REPORT_VALUE_NO = "no";
+
     public static final int RESPONSE_CODE_OK = 200;
 
     private MsrpConstants() {
     }
-}
\ No newline at end of file
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java
index 2f95bef..7605fb5 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java
@@ -22,7 +22,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.libraries.rcs.simpleclient.protocol.sdp.SdpUtils;
+import androidx.annotation.Nullable;
+
 import com.android.libraries.rcs.simpleclient.protocol.sdp.SimpleSdpMessage;
 
 import com.google.common.base.Ascii;
@@ -259,7 +260,7 @@
 
         request.setCallId(invite.getCallId());
 
-        Via via = (Via) request.getTopmostVia().clone();
+        Via via = (Via) invite.getTopmostVia().clone();
         via.removeParameter("branch");
         request.addHeader(via);
         request.addHeader(
@@ -280,12 +281,13 @@
      * @param code          The status code of the response.
      */
     public static SIPResponse buildInviteResponse(
-            SipSessionConfiguration configuration, SIPRequest invite, int code)
+            SipSessionConfiguration configuration,
+            SIPRequest invite,
+            int code,
+            @Nullable SimpleSdpMessage sdp)
             throws ParseException {
         SIPResponse response = invite.createResponse(code);
         if (code == Response.OK) {
-            SimpleSdpMessage sdp = SdpUtils.createSdpForMsrp(configuration.getLocalIpAddress(),
-                    false);
             response.setMessageContent(SDP_CONTENT_TYPE, SDP_CONTENT_SUB_TYPE, sdp.encode());
         }
 
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java
index 01a1061..b204de6 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java
@@ -21,6 +21,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.libraries.rcs.simpleclient.SimpleRcsClientContext;
 import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpManager;
 import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
@@ -45,8 +47,6 @@
 import javax.sip.message.Request;
 import javax.sip.message.Response;
 
-import androidx.annotation.Nullable;
-
 /**
  * Minimal CPM chat session service that provides the interface creating a {@link SimpleChatSession}
  * instance using {@link SipDelegateConnection}.
@@ -162,7 +162,8 @@
                         SipUtils.buildInviteResponse(
                                 mContext.getSipSession().getSessionConfiguration(),
                                 request,
-                                Response.METHOD_NOT_ALLOWED);
+                                Response.METHOD_NOT_ALLOWED,
+                                null);
                 sendSipResponse(response, /* session= */ null)
                         .addListener(() -> {
                         }, MoreExecutors.directExecutor());
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
index 29c5176..6813c91 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
@@ -110,6 +110,8 @@
 
         // Build a new CPIM message and send it out through the MSRP session.
         SimpleCpimMessage cpim = CpimUtils.createForText(msg);
+        Log.i(TAG, "Encoded CPIM:" + cpim.encode());
+
         byte[] content = cpim.encode().getBytes(UTF_8);
         MsrpChunk msrpChunk =
                 MsrpChunk.newBuilder()
@@ -119,12 +121,18 @@
                         .continuation(Continuation.COMPLETE)
                         .addHeader(MsrpConstants.HEADER_TO_PATH, mRemoteSdp.getPath().get())
                         .addHeader(MsrpConstants.HEADER_FROM_PATH, mLocalSdp.getPath().get())
+                        .addHeader(MsrpConstants.HEADER_FAILURE_REPORT,
+                                MsrpConstants.REPORT_VALUE_YES)
+                        .addHeader(MsrpConstants.HEADER_SUCCESS_REPORT,
+                                MsrpConstants.REPORT_VALUE_NO)
                         .addHeader(
                                 MsrpConstants.HEADER_BYTE_RANGE,
                                 String.format("1-%d/%d", content.length, content.length))
                         .addHeader(MsrpConstants.HEADER_MESSAGE_ID, MsrpUtils.generateRandomId())
                         .addHeader(MsrpConstants.HEADER_CONTENT_TYPE, CPIM_CONTENT_TYPE)
                         .build();
+
+        Log.i(TAG, "Send a MSRP chunk: " + msrpChunk);
         Futures.addCallback(
                 session.send(msrpChunk),
                 new FutureCallback<MsrpChunk>() {
@@ -214,11 +222,13 @@
 
         updateRemoteUri(mInviteRequest);
 
+        SipSessionConfiguration configuration = mContext.getSipSession().getSessionConfiguration();
+        SimpleSdpMessage sdp = SdpUtils.createSdpForMsrp(configuration.getLocalIpAddress(), false);
+
         // Automatically reply back to the invite by building a pre-canned response.
         try {
-            SIPResponse response =
-                    SipUtils.buildInviteResponse(
-                            mContext.getSipSession().getSessionConfiguration(), invite, statusCode);
+            SIPResponse response = SipUtils.buildInviteResponse(configuration, invite, statusCode,
+                    sdp);
             return Futures.transform(
                     mService.sendSipResponse(response, this), result -> null,
                     MoreExecutors.directExecutor());
@@ -340,41 +350,53 @@
             return;
         }
 
+        SimpleSdpMessage sdp;
         try {
-            SimpleSdpMessage sdp =
-                    SimpleSdpMessage.parse(new ByteArrayInputStream(response.getRawContent()));
-            startMsrpSession(sdp);
+            sdp = SimpleSdpMessage.parse(new ByteArrayInputStream(response.getRawContent()));
         } catch (ParseException | IOException e) {
             notifyFailure("Invalid SDP in INVITE", CODE_ERROR_UNSPECIFIED);
+            return;
         }
 
-        if (mInviteRequest != null) {
-            SIPRequest ack = mInviteRequest.createAckRequest((To) response.getToHeader());
-            Futures.addCallback(
-                    mService.sendSipRequest(ack, this),
-                    new FutureCallback<Boolean>() {
-                        @Override
-                        public void onSuccess(Boolean result) {
-                            if (result) {
-                                mStartFuture.set(null);
-                                mStartFuture = null;
-                            } else {
-                                notifyFailure("Failed to send ACK", CODE_ERROR_UNSPECIFIED);
-                            }
-                        }
+        if (mInviteRequest == null) {
+            notifyFailure("No INVITE request sent out", CODE_ERROR_UNSPECIFIED);
+            return;
+        }
 
-                        @Override
-                        public void onFailure(Throwable t) {
+        SIPRequest ack = mInviteRequest.createAckRequest((To) response.getToHeader());
+        Futures.addCallback(
+                mService.sendSipRequest(ack, this),
+                new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean result) {
+                        if (result) {
+                            startMsrpSession(sdp);
+                            mRemoteSdp = sdp;
+                        } else {
                             notifyFailure("Failed to send ACK", CODE_ERROR_UNSPECIFIED);
                         }
-                    },
-                    MoreExecutors.directExecutor());
-        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        notifyFailure("Failed to send ACK", CODE_ERROR_UNSPECIFIED);
+                    }
+                },
+                MoreExecutors.directExecutor());
     }
 
     private void notifyFailure(String message, @ErrorCode int code) {
-        mStartFuture.setException(new ChatServiceException(message, code));
-        mStartFuture = null;
+        if (mStartFuture != null) {
+            mStartFuture.setException(new ChatServiceException(message, code));
+            mStartFuture = null;
+        }
+    }
+
+    private void notifySuccess() {
+        if (mStartFuture != null) {
+            mStartFuture.set(null);
+            mStartFuture = null;
+        }
     }
 
     private void startMsrpSession(SimpleSdpMessage remoteSdp) {
@@ -388,16 +410,17 @@
                         @Override
                         public void onSuccess(MsrpSession result) {
                             mMsrpSession = result;
+                            notifySuccess();
                         }
 
                         @Override
                         public void onFailure(Throwable t) {
                             Log.e(TAG, "Failed to create msrp session", t);
+                            notifyFailure("Failed to establish msrp session",
+                                    CODE_ERROR_UNSPECIFIED);
                             terminate()
                                     .addListener(
-                                            () -> {
-                                                Log.d(TAG, "Session terminated");
-                                            },
+                                            () -> Log.d(TAG, "Session terminated"),
                                             MoreExecutors.directExecutor());
                         }
                     },