1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ims.rcs.uce.presence.pidfparser;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.net.Uri;
27 import android.telephony.ims.RcsContactPresenceTuple;
28 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
29 import android.telephony.ims.RcsContactUceCapability;
30 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.ims.ImsTestBase;
36 
37 import java.time.Instant;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 import org.junit.After;
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.mockito.Mock;
46 
47 @RunWith(AndroidJUnit4.class)
48 public class PidfParserTest extends ImsTestBase {
49 
50     // The timestamp of the PIDF
51     private final Instant mPidfTimestamp = Instant.now().plusMillis(1);
52 
53     @Before
setUp()54     public void setUp() throws Exception {
55         super.setUp();
56         PidfParser.setTimestampProxy(() -> mPidfTimestamp);
57     }
58 
59     @After
tearDown()60     public void tearDown() throws Exception {
61         super.tearDown();
62         PidfParser.setTimestampProxy(null);
63     }
64 
65     @Test
66     @SmallTest
testConvertToPidf()67     public void testConvertToPidf() throws Exception {
68         RcsContactUceCapability capability = getRcsContactUceCapability();
69 
70         String pidfResult = PidfParser.convertToPidf(capability);
71 
72         String contact = "<contact>sip:test</contact>";
73         String audioSupported = "<caps:audio>true</caps:audio>";
74         String videoSupported = "<caps:video>true</caps:video>";
75         String description = "<op:version>1.0</op:version>";
76         assertTrue(pidfResult.contains(contact));
77         assertTrue(pidfResult.contains(audioSupported));
78         assertTrue(pidfResult.contains(videoSupported));
79         assertTrue(pidfResult.contains(description));
80     }
81 
82     @Test
83     @SmallTest
testConvertFromPidfToRcsContactUceCapability()84     public void testConvertFromPidfToRcsContactUceCapability() throws Exception {
85         final String contact = "sip:+11234567890@test";
86         final String serviceId = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
87         final String serviceDescription = "MMTEL feature service";
88         final boolean isAudioSupported = true;
89         final boolean isVideoSupported = false;
90 
91         // Create the first PIDF data
92         String pidfData = getPidfData(contact, serviceId, serviceDescription, isAudioSupported,
93                 isVideoSupported);
94 
95         // Convert to the class RcsContactUceCapability
96         RcsContactUceCapabilityWrapper capabilitiesWrapper =
97                 PidfParser.getRcsContactUceCapabilityWrapper(pidfData);
98         assertNotNull(capabilitiesWrapper);
99         RcsContactUceCapability capabilities = capabilitiesWrapper.toRcsContactUceCapability();
100         assertNotNull(capabilities);
101         assertEquals(Uri.parse(contact), capabilities.getContactUri());
102         assertEquals(Uri.parse(contact), capabilities.getEntityUri());
103         assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capabilities.getSourceType());
104         assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE,
105                 capabilities.getCapabilityMechanism());
106 
107         List<RcsContactPresenceTuple> presenceTupleList = capabilities.getCapabilityTuples();
108         assertNotNull(presenceTupleList);
109         assertEquals(1, presenceTupleList.size());
110 
111         RcsContactPresenceTuple presenceTuple1 = presenceTupleList.get(0);
112         assertEquals(serviceId, presenceTuple1.getServiceId());
113         assertEquals("1.0", presenceTuple1.getServiceVersion());
114         assertEquals(serviceDescription, presenceTuple1.getServiceDescription());
115         assertEquals(Uri.parse(contact), presenceTuple1.getContactUri());
116         assertEquals(mPidfTimestamp.toString(), presenceTuple1.getTime().toString());
117         assertTrue(presenceTuple1.getServiceCapabilities().isAudioCapable());
118         assertFalse(presenceTuple1.getServiceCapabilities().isVideoCapable());
119     }
120 
121     @Test
122     @SmallTest
testConvertFromNewlineIncludedPidfToRcsContactUceCapability()123     public void testConvertFromNewlineIncludedPidfToRcsContactUceCapability() throws Exception {
124         final String contact = "tel:+11234567890";
125 
126         final RcsContactPresenceTuple.Builder tuple1Builder = new RcsContactPresenceTuple.Builder(
127                 "open",
128                 "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp",
129                 "1.0");
130         tuple1Builder.setServiceDescription("DiscoveryPresence")
131                 .setContactUri(Uri.parse(contact));
132 
133         final RcsContactPresenceTuple.Builder tuple2Builder = new RcsContactPresenceTuple.Builder(
134                 "open",
135                 "org.openmobilealliance:StandaloneMsg",
136                 "2.0");
137         tuple2Builder.setServiceDescription("StandaloneMsg")
138                 .setContactUri(Uri.parse(contact));
139 
140         final RcsContactPresenceTuple.Builder tuple3Builder = new RcsContactPresenceTuple.Builder(
141                 "open",
142                 "org.openmobilealliance:ChatSession",
143                 "2.0");
144         tuple3Builder.setServiceDescription("Session Mode Messaging")
145                 .setContactUri(Uri.parse(contact));
146 
147         final RcsContactPresenceTuple.Builder tuple4Builder = new RcsContactPresenceTuple.Builder(
148                 "open",
149                 "org.openmobilealliance:File-Transfer",
150                 "1.0");
151         tuple4Builder.setServiceDescription("File Transfer")
152                 .setContactUri(Uri.parse(contact));
153 
154         final RcsContactPresenceTuple.Builder tuple5Builder = new RcsContactPresenceTuple.Builder(
155                 "open",
156                 "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel",
157                 "1.0");
158         tuple5Builder.setServiceDescription("VoLTE service");
159         ServiceCapabilities.Builder capBuilder = new ServiceCapabilities.Builder(true, true);
160         tuple5Builder.setServiceCapabilities(capBuilder.build())
161                 .setContactUri(Uri.parse(contact));
162 
163         final List<RcsContactPresenceTuple> expectedTupleList = new ArrayList<>(5);
164         expectedTupleList.add(tuple1Builder.build());
165         expectedTupleList.add(tuple2Builder.build());
166         expectedTupleList.add(tuple3Builder.build());
167         expectedTupleList.add(tuple4Builder.build());
168         expectedTupleList.add(tuple5Builder.build());
169 
170         // Create the newline included PIDF data
171         String pidfData = getPidfDataWithNewlineAndWhitespaceCharacters();
172 
173         // Convert to the class RcsContactUceCapability
174         RcsContactUceCapabilityWrapper capabilitiesWrapper =
175                 PidfParser.getRcsContactUceCapabilityWrapper(pidfData);
176         assertNotNull(capabilitiesWrapper);
177         RcsContactUceCapability capabilities = capabilitiesWrapper.toRcsContactUceCapability();
178 
179         assertNotNull(capabilities);
180         assertEquals(Uri.parse(contact), capabilities.getContactUri());
181         assertEquals(Uri.parse(contact), capabilities.getEntityUri());
182         assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capabilities.getSourceType());
183         assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE,
184                 capabilities.getCapabilityMechanism());
185 
186         List<RcsContactPresenceTuple> presenceTupleList = capabilities.getCapabilityTuples();
187         assertNotNull(presenceTupleList);
188         assertEquals(expectedTupleList.size(), presenceTupleList.size());
189 
190         for(RcsContactPresenceTuple tuple : presenceTupleList) {
191             String serviceId = tuple.getServiceId();
192             RcsContactPresenceTuple expectedTuple = findTuple(serviceId, expectedTupleList);
193             if (expectedTuple == null) {
194                 fail("The service ID is invalid");
195             }
196 
197             assertEquals(expectedTuple.getStatus(), tuple.getStatus());
198             assertEquals(expectedTuple.getServiceVersion(), tuple.getServiceVersion());
199             assertEquals(expectedTuple.getServiceDescription(), tuple.getServiceDescription());
200             assertEquals(mPidfTimestamp, tuple.getTime());
201             assertEquals(expectedTuple.getContactUri(), tuple.getContactUri());
202 
203             ServiceCapabilities expectedCap = expectedTuple.getServiceCapabilities();
204             ServiceCapabilities resultCap = tuple.getServiceCapabilities();
205             if (expectedCap != null) {
206                 assertNotNull(resultCap);
207                 assertEquals(expectedCap.isAudioCapable(), resultCap.isAudioCapable());
208                 assertEquals(expectedCap.isVideoCapable(), resultCap.isVideoCapable());
209             } else {
210                 assertNull(resultCap);
211             }
212         }
213     }
214 
findTuple(String serviceId, List<RcsContactPresenceTuple> expectedTupleList)215     private RcsContactPresenceTuple findTuple(String serviceId,
216             List<RcsContactPresenceTuple> expectedTupleList) {
217         if (serviceId == null) {
218             return null;
219         }
220         for (RcsContactPresenceTuple tuple : expectedTupleList) {
221             if (serviceId.equalsIgnoreCase(tuple.getServiceId())) {
222                 return tuple;
223             }
224         }
225         return null;
226     }
227 
228     @Test
229     @SmallTest
testConvertToRcsContactUceCapabilityForMultipleTuples()230     public void testConvertToRcsContactUceCapabilityForMultipleTuples() throws Exception {
231         final String contact = "sip:+11234567890@test";
232         final String serviceId1 = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp";
233         final String serviceDescription1 = "capabilities discovery";
234         final String serviceId2 = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
235         final String serviceDescription2 = "MMTEL feature service";
236         final boolean isAudioSupported = true;
237         final boolean isVideoSupported = false;
238 
239         // Create the PIDF data
240         String pidfData = getPidfDataWithMultiTuples(contact, serviceId1, serviceDescription1,
241                 serviceId2, serviceDescription2, isAudioSupported, isVideoSupported);
242 
243         // Convert to the class RcsContactUceCapability
244         RcsContactUceCapabilityWrapper capabilitiesWrapper =
245                 PidfParser.getRcsContactUceCapabilityWrapper(pidfData);
246         assertNotNull(capabilitiesWrapper);
247         RcsContactUceCapability capabilities = capabilitiesWrapper.toRcsContactUceCapability();
248 
249         assertNotNull(capabilities);
250         assertEquals(Uri.parse(contact), capabilities.getContactUri());
251 
252         List<RcsContactPresenceTuple> presenceTupleList = capabilities.getCapabilityTuples();
253         assertNotNull(presenceTupleList);
254         assertEquals(2, presenceTupleList.size());
255 
256         // Verify the first tuple information
257         RcsContactPresenceTuple presenceTuple1 = presenceTupleList.get(0);
258         assertEquals(serviceId1, presenceTuple1.getServiceId());
259         assertEquals("1.0", presenceTuple1.getServiceVersion());
260         assertEquals(serviceDescription1, presenceTuple1.getServiceDescription());
261         assertEquals(Uri.parse(contact), presenceTuple1.getContactUri());
262         assertEquals(mPidfTimestamp.toString(), presenceTuple1.getTime().toString());
263         assertNull(presenceTuple1.getServiceCapabilities());
264 
265         // Verify the second tuple information
266         RcsContactPresenceTuple presenceTuple2 = presenceTupleList.get(1);
267         assertEquals(serviceId2, presenceTuple2.getServiceId());
268         assertEquals("1.0", presenceTuple2.getServiceVersion());
269         assertTrue(presenceTuple2.getServiceCapabilities().isAudioCapable());
270         assertFalse(presenceTuple2.getServiceCapabilities().isVideoCapable());
271         assertEquals(serviceDescription2, presenceTuple2.getServiceDescription());
272         assertEquals(Uri.parse(contact), presenceTuple2.getContactUri());
273         assertEquals(mPidfTimestamp.toString(), presenceTuple2.getTime().toString());
274         assertNotNull(presenceTuple2.getServiceCapabilities());
275         assertEquals(isAudioSupported, presenceTuple2.getServiceCapabilities().isAudioCapable());
276         assertEquals(isVideoSupported, presenceTuple2.getServiceCapabilities().isVideoCapable());
277     }
278 
279     @Test
280     @SmallTest
testConversionAndRestoration()281     public void testConversionAndRestoration() throws Exception {
282         // Create the capability
283         final RcsContactUceCapability capability = getRcsContactUceCapability();
284 
285         // Convert the capability to the pidf
286         final String pidf = PidfParser.convertToPidf(capability);
287 
288         // Restore to the RcsContactUceCapability from the pidf
289         RcsContactUceCapabilityWrapper capabilitiesWrapper =
290                 PidfParser.getRcsContactUceCapabilityWrapper(pidf);
291         assertNotNull(capabilitiesWrapper);
292         final RcsContactUceCapability restoredCapability =
293                 capabilitiesWrapper.toRcsContactUceCapability();
294 
295         assertEquals(capability.getContactUri(), restoredCapability.getContactUri());
296         assertEquals(capability.getCapabilityMechanism(),
297                 restoredCapability.getCapabilityMechanism());
298         assertEquals(capability.getSourceType(), restoredCapability.getSourceType());
299 
300         // Assert all the tuples are equal
301         List<RcsContactPresenceTuple> originalTuples = capability.getCapabilityTuples();
302         List<RcsContactPresenceTuple> restoredTuples = restoredCapability.getCapabilityTuples();
303 
304         assertNotNull(restoredTuples);
305         assertEquals(originalTuples.size(), restoredTuples.size());
306 
307         for (int i = 0; i < originalTuples.size(); i++) {
308             RcsContactPresenceTuple tuple = originalTuples.get(i);
309             RcsContactPresenceTuple restoredTuple = restoredTuples.get(i);
310 
311             assertEquals(tuple.getContactUri(), restoredTuple.getContactUri());
312             assertEquals(tuple.getStatus(), restoredTuple.getStatus());
313             assertEquals(tuple.getServiceId(), restoredTuple.getServiceId());
314             assertEquals(tuple.getServiceVersion(), restoredTuple.getServiceVersion());
315             assertEquals(tuple.getServiceDescription(), restoredTuple.getServiceDescription());
316 
317             boolean isAudioCapable = false;
318             boolean isVideoCapable = false;
319             boolean isRestoredAudioCapable = false;
320             boolean isRestoredVideoCapable = false;
321 
322             ServiceCapabilities servCaps = tuple.getServiceCapabilities();
323             if (servCaps != null) {
324                 isAudioCapable = servCaps.isAudioCapable();
325                 isVideoCapable = servCaps.isVideoCapable();
326             }
327 
328             ServiceCapabilities restoredServCaps = restoredTuple.getServiceCapabilities();
329             if (restoredServCaps != null) {
330                 isRestoredAudioCapable = restoredServCaps.isAudioCapable();
331                 isRestoredVideoCapable = restoredServCaps.isVideoCapable();
332             }
333 
334             assertEquals(isAudioCapable, isRestoredAudioCapable);
335             assertEquals(isVideoCapable, isRestoredVideoCapable);
336         }
337      }
338 
getPidfData(String contact, String serviceId, String serviceDescription, boolean isAudioSupported, boolean isVideoSupported)339     private String getPidfData(String contact, String serviceId, String serviceDescription,
340             boolean isAudioSupported, boolean isVideoSupported) {
341         StringBuilder pidfBuilder = new StringBuilder();
342         pidfBuilder.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>")
343                 .append("<presence entity=\"" + contact + "\"")
344                 .append(" xmlns=\"urn:ietf:params:xml:ns:pidf\"")
345                 .append(" xmlns:op=\"urn:oma:xml:prs:pidf:oma-pres\"")
346                 .append(" xmlns:op=\"urn:oma:xml:prs:pidf:oma-pres\"")
347                 .append(" xmlns:caps=\"urn:ietf:params:xml:ns:pidf:caps\">")
348                 // tuple data
349                 .append("<tuple id=\"tid0\">")
350                 .append("<status><basic>open</basic></status>")
351                 .append("<op:service-description>")
352                 .append("<op:service-id>").append(serviceId).append("</op:service-id>")
353                 .append("<op:version>1.0</op:version>")
354                 .append("<op:description>").append(serviceDescription).append("</op:description>")
355                 .append("</op:service-description>")
356                 // is audio supported
357                 .append("<caps:servcaps>")
358                 .append("<caps:audio>").append(isAudioSupported).append("</caps:audio>")
359                 // is video supported
360                 .append("<caps:video>").append(isVideoSupported).append("</caps:video>")
361                 .append("</caps:servcaps>")
362                 .append("<contact>").append(contact).append("</contact>")
363                 .append("<timestamp>").append(mPidfTimestamp.toString()).append("</timestamp>")
364                 .append("</tuple></presence>");
365         return pidfBuilder.toString();
366     }
367 
getPidfDataWithNewlineAndWhitespaceCharacters()368     private String getPidfDataWithNewlineAndWhitespaceCharacters() {
369         String pidf = "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" "
370                         + "xmlns:op=\"urn:oma:xml:prs:pidf:oma-pres\" "
371                         + "xmlns:b=\"urn:ietf:params:xml:ns:pidf:caps\" "
372                         + "entity=\"tel:+11234567890\">\n"
373                 // Tuple: Discovery
374                 + "   <tuple id=\"DiscoveryPres\">\n\t"
375                 + "     <status>\n\t"
376                 + "       <basic>open</basic>\n\t"
377                 + "     </status>\n\t"
378                 + "     <op:service-description>\n\t"
379                 + "       <op:service-id>org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp"
380                                 + "</op:service-id>\n\t"
381                 + "       <op:version>1.0</op:version>\n\t"
382                 + "       <op:description>DiscoveryPresence</op:description>\n\t"
383                 + "     </op:service-description>\n\t"
384                 + "     <contact>tel:+11234567890</contact>\n\t"
385                 + "   </tuple>\n\t"
386                 // Tuple: VoLTE
387                 + "   <tuple id=\"VoLTE\">\n"
388                 + "     <status>\n"
389                 + "       <basic>open</basic>\n"
390                 + "     </status>\n"
391                 + "     <b:servcaps>\n"
392                 + "       <b:audio>true</b:audio>\n"
393                 + "       <b:video>true</b:video>\n"
394                 + "       <b:duplex>\n"
395                 + "         <b:supported>\n"
396                 + "           <b:full/>\n"
397                 + "         </b:supported>\n"
398                 + "       </b:duplex>\n"
399                 + "     </b:servcaps>\n"
400                 + "     <op:service-description>\n"
401                 + "       <op:service-id>org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"
402                                 + "</op:service-id>\n"
403                 + "       <op:version>1.0</op:version>\n"
404                 + "       <op:description>VoLTE service</op:description>\n"
405                 + "     </op:service-description>\n"
406                 + "     <contact>tel:+11234567890</contact>\n"
407                 + "   </tuple>\n"
408                 // Tuple: Standalone Message
409                 + "   <tuple id=\"StandaloneMsg\">\n"
410                 + "     <status>\n"
411                 + "       <basic>open</basic>\n"
412                 + "     </status>\n"
413                 + "     <op:service-description>\n"
414                 + "       <op:service-id>org.openmobilealliance:StandaloneMsg</op:service-id>\n"
415                 + "       <op:version>2.0</op:version>\n"
416                 + "       <op:description>StandaloneMsg</op:description>\n"
417                 + "     </op:service-description>\n"
418                 + "     <contact>tel:+11234567890</contact>\n"
419                 + "   </tuple>\n"
420                 // Tuple: Session Mode Message
421                 + "   <tuple id=\"SessModeMessa\">\n"
422                 + "     <status>\n"
423                 + "       <basic>open</basic>\n"
424                 + "     </status>\n"
425                 + "     <op:service-description>\n"
426                 + "       <op:service-id>org.openmobilealliance:ChatSession</op:service-id>\n"
427                 + "       <op:version>2.0</op:version>\n"
428                 + "       <op:description>Session Mode Messaging</op:description>\n"
429                 + "     </op:service-description>\n"
430                 + "     <contact>tel:+11234567890</contact>\n"
431                 + "   </tuple>\n"
432                 // Tuple: File Transfer
433                 + "   <tuple id=\"FileTransfer\">\n"
434                 + "     <status>\n"
435                 + "       <basic>open</basic>\n"
436                 + "     </status>\n"
437                 + "     <op:service-description>\n"
438                 + "       <op:service-id>org.openmobilealliance:File-Transfer</op:service-id>\n"
439                 + "       <op:version>1.0</op:version>\n"
440                 + "       <op:description>File Transfer</op:description>\n"
441                 + "     </op:service-description>\n"
442                 + "     <contact>tel:+11234567890</contact>\n"
443                 + "   </tuple>\n"
444                 + " </presence>";
445 
446         return pidf;
447     }
448 
getPidfDataWithMultiTuples(String contact, String serviceId1, String serviceDescription1, String serviceId2, String serviceDescription2, boolean audioSupported, boolean videoSupported)449     private String getPidfDataWithMultiTuples(String contact, String serviceId1,
450             String serviceDescription1, String serviceId2, String serviceDescription2,
451             boolean audioSupported, boolean videoSupported) {
452         return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
453                 + "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\""
454                 + " xmlns:op=\"urn:oma:xml:prs:pidf:oma-pres\""
455                 + " xmlns:caps=\"urn:ietf:params:xml:ns:pidf:caps\""
456                 + " entity=\"" + contact + "\">"
457                 // tuple 1
458                 + "<tuple id=\"a0\">"
459                 + "<status><basic>open</basic></status>"
460                 + "<op:service-description>"
461                 + "<op:service-id>" + serviceId1 + "</op:service-id>"
462                 + "<op:version>1.0</op:version>"
463                 + "<op:description>" + serviceDescription1 + "</op:description>"
464                 + "</op:service-description>"
465                 + "<contact>" + contact + "</contact>"
466                 + "<timestamp>" + mPidfTimestamp.toString() + "</timestamp>"
467                 + "</tuple>"
468                 // tuple 2
469                 + "<tuple id=\"a1\">"
470                 + "<status><basic>open</basic></status>"
471                 + "<op:service-description>"
472                 + "<op:service-id>" + serviceId2 + "</op:service-id>"
473                 + "<op:version>1.0</op:version>"
474                 + "<op:description>" + serviceDescription2 + "</op:description>"
475                 + "</op:service-description>"
476                 + "<caps:servcaps>"
477                 + "<caps:audio>" + audioSupported + "</caps:audio>"
478                 + "<caps:duplex>"
479                 + "<caps:supported><caps:full></caps:full></caps:supported>"
480                 + "</caps:duplex>"
481                 + "<caps:video>" + videoSupported + "</caps:video>"
482                 + "</caps:servcaps>"
483                 + "<contact>" + contact + "</contact>"
484                 + "<timestamp>" + mPidfTimestamp.toString() + "</timestamp>"
485                 + "</tuple>"
486                 + "</presence>";
487     }
488 
getRcsContactUceCapability()489     private RcsContactUceCapability getRcsContactUceCapability() {
490         final Uri contact = Uri.fromParts("sip", "test", null);
491         final boolean isAudioCapable = true;
492         final boolean isVideoCapable = true;
493         final String duplexMode = ServiceCapabilities.DUPLEX_MODE_FULL;
494         final String basicStatus = RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN;
495         final String version = "1.0";
496         final String description = "description test";
497 
498         // init the capabilities
499         ServiceCapabilities.Builder servCapsBuilder =
500                 new ServiceCapabilities.Builder(isAudioCapable, isVideoCapable);
501         servCapsBuilder.addSupportedDuplexMode(duplexMode);
502 
503         // init the presence tuple
504         RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
505                 basicStatus, RcsContactPresenceTuple.SERVICE_ID_MMTEL, version);
506         tupleBuilder.setContactUri(contact)
507                 .setServiceDescription(description)
508                 .setTime(mPidfTimestamp)
509                 .setServiceCapabilities(servCapsBuilder.build());
510 
511         PresenceBuilder presenceBuilder = new PresenceBuilder(contact,
512                 RcsContactUceCapability.SOURCE_TYPE_NETWORK,
513                 RcsContactUceCapability.REQUEST_RESULT_FOUND);
514         presenceBuilder.addCapabilityTuple(tupleBuilder.build());
515 
516         return presenceBuilder.build();
517     }
518 }
519