• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.Nullable;
20 import android.net.Uri;
21 import android.telephony.ims.RcsContactPresenceTuple;
22 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
23 import android.telephony.ims.RcsContactUceCapability;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 
28 import com.android.ims.rcs.uce.presence.pidfparser.capabilities.Audio;
29 import com.android.ims.rcs.uce.presence.pidfparser.capabilities.CapsConstant;
30 import com.android.ims.rcs.uce.presence.pidfparser.capabilities.Duplex;
31 import com.android.ims.rcs.uce.presence.pidfparser.capabilities.ServiceCaps;
32 import com.android.ims.rcs.uce.presence.pidfparser.capabilities.Video;
33 import com.android.ims.rcs.uce.presence.pidfparser.omapres.OmaPresConstant;
34 import com.android.ims.rcs.uce.presence.pidfparser.pidf.Basic;
35 import com.android.ims.rcs.uce.presence.pidfparser.pidf.PidfConstant;
36 import com.android.ims.rcs.uce.presence.pidfparser.pidf.Presence;
37 import com.android.ims.rcs.uce.presence.pidfparser.pidf.Tuple;
38 import com.android.ims.rcs.uce.presence.pidfparser.RcsContactUceCapabilityWrapper;
39 import com.android.ims.rcs.uce.util.UceUtils;
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.io.IOException;
43 import java.io.Reader;
44 import java.io.StringReader;
45 import java.io.StringWriter;
46 import java.time.Instant;
47 import java.util.List;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50 
51 import org.xmlpull.v1.XmlPullParser;
52 import org.xmlpull.v1.XmlPullParserException;
53 import org.xmlpull.v1.XmlPullParserFactory;
54 import org.xmlpull.v1.XmlSerializer;
55 
56 /**
57  * Convert between the class RcsContactUceCapability and the pidf format.
58  */
59 public class PidfParser {
60 
61     private static final String LOG_TAG = UceUtils.getLogPrefix() + "PidfParser";
62 
63     private static final Pattern PIDF_PATTERN = Pattern.compile("\t|\r|\n");
64 
65     /**
66      * Testing interface used to get the timestamp.
67      */
68     @VisibleForTesting
69     public interface TimestampProxy {
getTimestamp()70         Instant getTimestamp();
71     }
72 
73     // The timestamp proxy to create the local timestamp.
74     private static final TimestampProxy sLocalTimestampProxy = () -> Instant.now();
75 
76     // Override timestamp proxy for testing only.
77     private static TimestampProxy sOverrideTimestampProxy;
78 
79     @VisibleForTesting
setTimestampProxy(TimestampProxy proxy)80     public static void setTimestampProxy(TimestampProxy proxy) {
81         sOverrideTimestampProxy = proxy;
82     }
83 
getTimestampProxy()84     private static TimestampProxy getTimestampProxy() {
85         return (sOverrideTimestampProxy != null) ? sOverrideTimestampProxy : sLocalTimestampProxy;
86     }
87 
88     /**
89      * Convert the RcsContactUceCapability to the string of pidf.
90      */
convertToPidf(RcsContactUceCapability capabilities)91     public static String convertToPidf(RcsContactUceCapability capabilities) {
92         StringWriter pidfWriter = new StringWriter();
93         try {
94             // Init the instance of the XmlSerializer.
95             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
96             XmlSerializer serializer = factory.newSerializer();
97 
98             // setup output and namespace
99             serializer.setOutput(pidfWriter);
100             serializer.setPrefix("", PidfConstant.NAMESPACE);
101             serializer.setPrefix("op", OmaPresConstant.NAMESPACE);
102             serializer.setPrefix("caps", CapsConstant.NAMESPACE);
103 
104             // Get the Presence element
105             Presence presence = PidfParserUtils.getPresence(capabilities);
106 
107             // Start serializing.
108             serializer.startDocument(PidfParserConstant.ENCODING_UTF_8, true);
109             presence.serialize(serializer);
110             serializer.endDocument();
111             serializer.flush();
112 
113         } catch (XmlPullParserException parserEx) {
114             parserEx.printStackTrace();
115             return null;
116         } catch (IOException ioException) {
117             ioException.printStackTrace();
118             return null;
119         }
120         return pidfWriter.toString();
121     }
122 
123     /**
124      * Get the RcsContactUceCapabilityWrapper from the given PIDF xml format.
125      */
getRcsContactUceCapabilityWrapper( String pidf)126     public static @Nullable RcsContactUceCapabilityWrapper getRcsContactUceCapabilityWrapper(
127             String pidf) {
128         if (TextUtils.isEmpty(pidf)) {
129             Log.w(LOG_TAG, "getRcsContactUceCapabilityWrapper: The given pidf is empty");
130             return null;
131         }
132 
133         // Filter the newline characters
134         Matcher matcher = PIDF_PATTERN.matcher(pidf);
135         String formattedPidf = matcher.replaceAll("");
136         if (TextUtils.isEmpty(formattedPidf)) {
137             Log.w(LOG_TAG, "getRcsContactUceCapabilityWrapper: The formatted pidf is empty");
138             return null;
139         }
140 
141         Reader reader = null;
142         try {
143             // Init the instance of the parser
144             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
145             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
146             reader = new StringReader(formattedPidf);
147             parser.setInput(reader);
148 
149             // Start parsing
150             Presence presence = parsePidf(parser);
151 
152             // Convert from the Presence to the RcsContactUceCapabilityWrapper
153             return convertToRcsContactUceCapability(presence);
154 
155         } catch (XmlPullParserException | IOException e) {
156             e.printStackTrace();
157         } finally {
158             if (reader != null) {
159                 try {
160                     reader.close();
161                 } catch (IOException e) {
162                     e.printStackTrace();
163                 }
164             }
165         }
166         return null;
167     }
168 
parsePidf(XmlPullParser parser)169     private static Presence parsePidf(XmlPullParser parser) throws IOException,
170             XmlPullParserException {
171         Presence presence = null;
172         int nextType = parser.next();
173         boolean findPresenceTag = false;
174         do {
175             // Find the Presence start tag
176             if (nextType == XmlPullParser.START_TAG
177                     && Presence.ELEMENT_NAME.equals(parser.getName())) {
178                 findPresenceTag = true;
179                 presence = new Presence();
180                 presence.parse(parser);
181                 break;
182             }
183             nextType = parser.next();
184         } while(nextType != XmlPullParser.END_DOCUMENT);
185 
186         if (!findPresenceTag) {
187             Log.w(LOG_TAG, "parsePidf: The presence start tag not found.");
188         }
189 
190         return presence;
191     }
192 
193     /*
194      * Convert the given Presence to the RcsContactUceCapabilityWrapper
195      */
convertToRcsContactUceCapability( Presence presence)196     private static RcsContactUceCapabilityWrapper convertToRcsContactUceCapability(
197             Presence presence) {
198         if (presence == null) {
199             Log.w(LOG_TAG, "convertToRcsContactUceCapability: The presence is null");
200             return null;
201         }
202         if (TextUtils.isEmpty(presence.getEntity())) {
203             Log.w(LOG_TAG, "convertToRcsContactUceCapability: The entity is empty");
204             return null;
205         }
206 
207         RcsContactUceCapabilityWrapper uceCapabilityWrapper = new RcsContactUceCapabilityWrapper(
208                 Uri.parse(presence.getEntity()), RcsContactUceCapability.SOURCE_TYPE_NETWORK,
209                 RcsContactUceCapability.REQUEST_RESULT_FOUND);
210 
211         // Add all the capability tuples of this contact
212         presence.getTupleList().forEach(tuple -> {
213             // The tuple that fails parsing is invalid data, so discard it.
214             if (!tuple.getMalformed()) {
215                 RcsContactPresenceTuple capabilityTuple = getRcsContactPresenceTuple(tuple);
216                 if (capabilityTuple != null) {
217                     uceCapabilityWrapper.addCapabilityTuple(capabilityTuple);
218                 }
219             } else {
220                 uceCapabilityWrapper.setMalformedContents();
221             }
222         });
223         uceCapabilityWrapper.setEntityUri(Uri.parse(presence.getEntity()));
224         return uceCapabilityWrapper;
225     }
226 
227     /*
228      * Get the RcsContactPresenceTuple from the giving tuple element.
229      */
getRcsContactPresenceTuple(Tuple tuple)230     private static RcsContactPresenceTuple getRcsContactPresenceTuple(Tuple tuple) {
231         if (tuple == null) {
232             return null;
233         }
234 
235         String status = RcsContactPresenceTuple.TUPLE_BASIC_STATUS_CLOSED;
236         if (Basic.OPEN.equals(PidfParserUtils.getTupleStatus(tuple))) {
237             status = RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN;
238         }
239 
240         String serviceId = PidfParserUtils.getTupleServiceId(tuple);
241         String serviceVersion = PidfParserUtils.getTupleServiceVersion(tuple);
242         String serviceDescription = PidfParserUtils.getTupleServiceDescription(tuple);
243 
244         RcsContactPresenceTuple.Builder builder = new RcsContactPresenceTuple.Builder(status,
245                 serviceId, serviceVersion);
246 
247         // Set contact uri
248         String contact = PidfParserUtils.getTupleContact(tuple);
249         if (!TextUtils.isEmpty(contact)) {
250             builder.setContactUri(Uri.parse(contact));
251         }
252 
253         // Use local time instead to prevent we receive the incorrect timestamp from the network.
254         builder.setTime(getTimestampProxy().getTimestamp());
255 
256         // Set service description
257         if (!TextUtils.isEmpty(serviceDescription)) {
258             builder.setServiceDescription(serviceDescription);
259         }
260 
261         // Set service capabilities
262         ServiceCaps serviceCaps = tuple.getServiceCaps();
263         if (serviceCaps != null) {
264             List<ElementBase> serviceCapsList = serviceCaps.getElements();
265             if (serviceCapsList != null && !serviceCapsList.isEmpty()) {
266                 boolean isAudioSupported = false;
267                 boolean isVideoSupported = false;
268                 List<String> supportedTypes = null;
269                 List<String> notSupportedTypes = null;
270 
271                 for (ElementBase element : serviceCapsList) {
272                     if (element instanceof Audio) {
273                         isAudioSupported = ((Audio) element).isAudioSupported();
274                     } else if (element instanceof Video) {
275                         isVideoSupported = ((Video) element).isVideoSupported();
276                     } else if (element instanceof Duplex) {
277                         supportedTypes = ((Duplex) element).getSupportedTypes();
278                         notSupportedTypes = ((Duplex) element).getNotSupportedTypes();
279                     }
280                 }
281 
282                 ServiceCapabilities.Builder capabilitiesBuilder
283                         = new ServiceCapabilities.Builder(isAudioSupported, isVideoSupported);
284 
285                 if (supportedTypes != null && !supportedTypes.isEmpty()) {
286                     for (String supportedType : supportedTypes) {
287                         capabilitiesBuilder.addSupportedDuplexMode(supportedType);
288                     }
289                 }
290 
291                 if (notSupportedTypes != null && !notSupportedTypes.isEmpty()) {
292                     for (String notSupportedType : notSupportedTypes) {
293                         capabilitiesBuilder.addUnsupportedDuplexMode(notSupportedType);
294                     }
295                 }
296                 builder.setServiceCapabilities(capabilitiesBuilder.build());
297             }
298         }
299         return builder.build();
300     }
301 }
302