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.pidf;
18 
19 import android.annotation.NonNull;
20 import android.net.Uri;
21 import android.text.TextUtils;
22 import android.util.Log;
23 
24 import com.android.ims.rcs.uce.presence.pidfparser.ElementBase;
25 import com.android.ims.rcs.uce.util.UceUtils;
26 import com.android.internal.annotations.VisibleForTesting;
27 
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 import org.xmlpull.v1.XmlSerializer;
31 
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 
37 /**
38  * The "present" element is the root element of an "application/pidf+xml" object.
39  */
40 public class Presence extends ElementBase {
41     /**
42      * The presence element consists the following elements:
43      * 1: Any number (including 0) of <tuple> elements
44      * 2: Any number (including 0) of <note> elements
45      * 3: Any number of OPTIONAL extension elements from other namespaces.
46      */
47     private static final String LOG_TAG = UceUtils.getLogPrefix() + "Presence";
48 
49     /** The name of this element */
50     public static final String ELEMENT_NAME = "presence";
51 
52     private static final String ATTRIBUTE_NAME_ENTITY = "entity";
53 
54     // The presence element must have an "entity" attribute.
55     private String mEntity;
56 
57     // The presence element contains any number of <tuple> elements
58     private final List<Tuple> mTupleList = new ArrayList<>();
59 
60     // The presence element contains any number of <note> elements;
61     private final List<Note> mNoteList = new ArrayList<>();
62 
Presence()63     public Presence() {
64     }
65 
Presence(@onNull Uri contact)66     public Presence(@NonNull Uri contact) {
67         initEntity(contact);
68     }
69 
initEntity(Uri contact)70     private void initEntity(Uri contact) {
71         mEntity = contact.toString();
72     }
73 
74     @VisibleForTesting
setEntity(String entity)75     public void setEntity(String entity) {
76         mEntity = entity;
77     }
78 
79     @Override
initNamespace()80     protected String initNamespace() {
81         return PidfConstant.NAMESPACE;
82     }
83 
84     @Override
initElementName()85     protected String initElementName() {
86         return ELEMENT_NAME;
87     }
88 
getEntity()89     public String getEntity() {
90         return mEntity;
91     }
92 
addTuple(@onNull Tuple tuple)93     public void addTuple(@NonNull Tuple tuple) {
94         mTupleList.add(tuple);
95     }
96 
getTupleList()97     public @NonNull List<Tuple> getTupleList() {
98         return Collections.unmodifiableList(mTupleList);
99     }
100 
addNote(@onNull Note note)101     public void addNote(@NonNull Note note) {
102         mNoteList.add(note);
103     }
104 
getNoteList()105     public @NonNull List<Note> getNoteList() {
106         return Collections.unmodifiableList(mNoteList);
107     }
108 
109     @Override
serialize(XmlSerializer serializer)110     public void serialize(XmlSerializer serializer) throws IOException {
111         String namespace = getNamespace();
112         String elementName = getElementName();
113 
114         serializer.startTag(namespace, elementName);
115         // entity attribute
116         serializer.attribute(XmlPullParser.NO_NAMESPACE, ATTRIBUTE_NAME_ENTITY, mEntity);
117 
118         // tuple elements
119         for (Tuple tuple : mTupleList) {
120             tuple.serialize(serializer);
121         }
122 
123         // note elements
124         for (Note note : mNoteList) {
125             note.serialize(serializer);
126         }
127         serializer.endTag(namespace, elementName);
128     }
129 
130     @Override
parse(XmlPullParser parser)131     public void parse(XmlPullParser parser) throws IOException, XmlPullParserException {
132         String namespace = parser.getNamespace();
133         String name = parser.getName();
134 
135         if (!verifyParsingElement(namespace, name)) {
136             throw new XmlPullParserException("Incorrect element: " + namespace + ", " + name);
137         }
138 
139         mEntity = parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, ATTRIBUTE_NAME_ENTITY);
140         if (TextUtils.isEmpty(mEntity)) {
141             throw new XmlPullParserException("Entity uri of presence is empty");
142         }
143 
144         // Move to the next event.
145         int eventType = parser.next();
146 
147         while(!(eventType == XmlPullParser.END_TAG
148                 && getNamespace().equals(parser.getNamespace())
149                 && getElementName().equals(parser.getName()))) {
150 
151             if (eventType == XmlPullParser.START_TAG) {
152                 String tagName = parser.getName();
153 
154                 if (isTupleElement(eventType, tagName)) {
155                     Tuple tuple = new Tuple();
156                     try {
157                         // If one tuple encounters an xml exception, we must parse the other tuple
158                         // and store valid information.
159                         tuple.parse(parser);
160                     } catch (XmlPullParserException e) {
161                         e.printStackTrace();
162                         Log.w(LOG_TAG, "parse: Exception occurred during Tuple parsing.");
163                         tuple.setMalformed(true);
164                     }
165                     mTupleList.add(tuple);
166                 } else if (isNoteElement(eventType, tagName)) {
167                     Note note = new Note();
168                     try {
169                         note.parse(parser);
170                     } catch (XmlPullParserException e) {
171                         e.printStackTrace();
172                         Log.w(LOG_TAG, "parse: Exception occurred during Note parsing.");
173                     }
174                     mNoteList.add(note);
175                 }
176             }
177 
178             eventType = parser.next();
179 
180             // Leave directly if the event type is the end of the document.
181             if (eventType == XmlPullParser.END_DOCUMENT) {
182                 return;
183             }
184         }
185     }
186 
isTupleElement(int eventType, String name)187     private boolean isTupleElement(int eventType, String name) {
188         return (eventType == XmlPullParser.START_TAG && Tuple.ELEMENT_NAME.equals(name)) ?
189                 true : false;
190     }
191 
isNoteElement(int eventType, String name)192     private boolean isNoteElement(int eventType, String name) {
193         return (eventType == XmlPullParser.START_TAG && Note.ELEMENT_NAME.equals(name)) ?
194                 true : false;
195     }
196 }
197