• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015, Motorola Mobility LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     - Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     - Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     - Neither the name of Motorola Mobility nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26  * DAMAGE.
27  */
28 
29 package com.android.service.ims;
30 
31 import android.net.Uri;
32 import android.telephony.ims.RcsContactPresenceTuple;
33 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
34 import android.telephony.ims.RcsContactUceCapability;
35 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
36 
37 import java.lang.String;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 
41 import com.android.ims.internal.Logger;
42 import com.android.ims.internal.uce.presence.PresTupleInfo;
43 import com.android.ims.internal.uce.presence.PresRlmiInfo;
44 import com.android.ims.internal.uce.presence.PresResInfo;
45 import com.android.ims.internal.uce.presence.PresResInstanceInfo;
46 import com.android.ims.RcsPresenceInfo;
47 import com.android.ims.RcsPresenceInfo.ServiceType;
48 import com.android.ims.RcsPresenceInfo.ServiceState;
49 import com.android.service.ims.presence.PresenceUtils;
50 
51 public class PresenceInfoParser{
52     /*
53      * The logger
54      */
55     static private Logger logger = Logger.getLogger("PresenceInfoParser");
56 
PresenceInfoParser()57     public PresenceInfoParser() {
58     }
59 
getPresenceInfoFromTuple(String pPresentityURI, PresTupleInfo[] pTupleInfo)60     static public RcsPresenceInfo getPresenceInfoFromTuple(String pPresentityURI,
61             PresTupleInfo[] pTupleInfo){
62         logger.debug("getPresenceInfoFromTuple: pPresentityURI=" + pPresentityURI +
63                 " pTupleInfo=" + Arrays.toString(pTupleInfo));
64 
65         if(pPresentityURI == null){
66             logger.error("pPresentityURI=" + pPresentityURI);
67             return null;
68         }
69 
70         String contactNumber = getPhoneFromUri(pPresentityURI);
71         // Now that we got notification if the content didn't indicate it is not Volte
72         // then it should be Volte enabled. So default to Volte enabled.
73         int volteStatus = RcsPresenceInfo.VolteStatus.VOLTE_ENABLED;
74 
75         int ipVoiceCallState = RcsPresenceInfo.ServiceState.UNKNOWN;
76         String ipVoiceCallServiceNumber = null;
77          // We use the timestamp which when we received it instead of the one in PDU
78         long ipVoiceCallTimestamp = System.currentTimeMillis();
79 
80         int ipVideoCallState = RcsPresenceInfo.ServiceState.UNKNOWN;
81         String ipVideoCallServiceNumber = null;
82         long ipVideoCallTimestamp = System.currentTimeMillis();
83 
84         if( pTupleInfo == null){
85             logger.debug("pTupleInfo=null");
86             return (new RcsPresenceInfo(contactNumber, volteStatus,
87                 ipVoiceCallState, ipVoiceCallServiceNumber, ipVoiceCallTimestamp,
88                 ipVideoCallState, ipVideoCallServiceNumber, ipVideoCallTimestamp));
89         }
90 
91         for(int i = 0; i < pTupleInfo.length; i++){
92             // Video call has high priority. If it supports video call it will support voice call.
93             String featureTag = pTupleInfo[i].getFeatureTag();
94             logger.debug("getFeatureTag " + i + ": " + featureTag);
95             if(featureTag.equals(
96                 "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";video") ||
97                 featureTag.equals("+g.gsma.rcs.telephony=\"cs,volte\";video")) {
98                 logger.debug("Got Volte and VT enabled tuple");
99                 ipVoiceCallState = RcsPresenceInfo.ServiceState.ONLINE;
100                 ipVideoCallState = RcsPresenceInfo.ServiceState.ONLINE;
101 
102                 if(pTupleInfo[i].getContactUri() != null){
103                     ipVideoCallServiceNumber = getPhoneFromUri(
104                             pTupleInfo[i].getContactUri().toString());
105                 }
106             } else if(featureTag.equals("+g.gsma.rcs.telephony=\"cs\";video")) {
107                 logger.debug("Got Volte disabled but VT enabled tuple");
108                 ipVoiceCallState = RcsPresenceInfo.ServiceState.OFFLINE;
109                 ipVideoCallState = RcsPresenceInfo.ServiceState.ONLINE;
110 
111                 if(pTupleInfo[i].getContactUri() != null){
112                     ipVideoCallServiceNumber = getPhoneFromUri(
113                             pTupleInfo[i].getContactUri().toString());
114                 }
115             }else{
116                 if(featureTag.equals(
117                     "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"") ||
118                     featureTag.equals("+g.gsma.rcs.telephony=\"cs,volte\"")) {
119 
120                     logger.debug("Got Volte only tuple");
121                     ipVoiceCallState = RcsPresenceInfo.ServiceState.ONLINE;
122                     // "OR" for multiple tuples.
123                     if(RcsPresenceInfo.ServiceState.UNKNOWN == ipVideoCallState) {
124                         ipVideoCallState = RcsPresenceInfo.ServiceState.OFFLINE;
125                     }
126 
127                     if(pTupleInfo[i].getContactUri() != null){
128                         ipVoiceCallServiceNumber = getPhoneFromUri(
129                                 pTupleInfo[i].getContactUri().toString());
130                     }
131                 }else{
132                     logger.debug("Ignoring feature tag: " + pTupleInfo[i].getFeatureTag());
133                 }
134            }
135         }
136 
137         RcsPresenceInfo retPresenceInfo = new RcsPresenceInfo(contactNumber,volteStatus,
138                 ipVoiceCallState, ipVoiceCallServiceNumber, ipVoiceCallTimestamp,
139                 ipVideoCallState, ipVideoCallServiceNumber, ipVideoCallTimestamp);
140 
141         logger.debug("getPresenceInfoFromTuple: " + retPresenceInfo);
142 
143         return retPresenceInfo;
144     }
145 
addPresenceInfo(ArrayList<RcsPresenceInfo> presenceInfoList, RcsPresenceInfo presenceInfo)146     static private void addPresenceInfo(ArrayList<RcsPresenceInfo> presenceInfoList,
147             RcsPresenceInfo presenceInfo){
148         logger.debug("addPresenceInfo presenceInfoList=" + presenceInfoList +
149                     " presenceInfo=" + presenceInfo);
150 
151         if(presenceInfoList == null || presenceInfo == null){
152             logger.debug("addPresenceInfo presenceInfoList=" + presenceInfoList +
153                     " presenceInfo=" + presenceInfo);
154             return;
155         }
156 
157         for(int i=0; i< presenceInfoList.size(); i++){
158             RcsPresenceInfo presenceInfoTmp = presenceInfoList.get(i);
159             if((presenceInfoTmp != null) && (presenceInfoTmp.getContactNumber() != null) &&
160                     presenceInfoTmp.getContactNumber().equalsIgnoreCase(
161                     presenceInfo.getContactNumber())){
162                 // merge the information
163                 presenceInfoList.set(i, new RcsPresenceInfo(presenceInfoTmp.getContactNumber(),
164                         presenceInfoTmp.getVolteStatus(),
165                         ((ServiceState.ONLINE == presenceInfo.getServiceState(
166                                 ServiceType.VOLTE_CALL)) ||
167                                 (ServiceState.ONLINE == presenceInfoTmp.getServiceState(
168                                 ServiceType.VOLTE_CALL)))?ServiceState.ONLINE:
169                                     presenceInfoTmp.getServiceState(ServiceType.VOLTE_CALL),
170                         presenceInfoTmp.getServiceContact(ServiceType.VOLTE_CALL),
171                         presenceInfoTmp.getTimeStamp(ServiceType.VOLTE_CALL),
172                         ((ServiceState.ONLINE == presenceInfo.getServiceState(
173                                 ServiceType.VT_CALL)) ||
174                                 (ServiceState.ONLINE == presenceInfoTmp.getServiceState(
175                                 ServiceType.VT_CALL)))?ServiceState.ONLINE:
176                                     presenceInfoTmp.getServiceState(ServiceType.VT_CALL),
177                         presenceInfoTmp.getServiceContact(ServiceType.VT_CALL),
178                         presenceInfoTmp.getTimeStamp(ServiceType.VT_CALL)));
179                 return;
180             }
181         }
182 
183         // didn't merge, so add the new one.
184         presenceInfoList.add(presenceInfo);
185     }
186 
getPresenceInfosFromPresenceRes( PresRlmiInfo pRlmiInfo, PresResInfo[] pRcsPresenceInfo)187     static public RcsPresenceInfo[] getPresenceInfosFromPresenceRes(
188             PresRlmiInfo pRlmiInfo, PresResInfo[] pRcsPresenceInfo) {
189         if(pRcsPresenceInfo == null){
190             logger.debug("getPresenceInfosFromPresenceRes pRcsPresenceInfo=null");
191             return null;
192         }
193 
194         ArrayList<RcsPresenceInfo> presenceInfoList = new ArrayList<RcsPresenceInfo>();
195         for(int i=0; i < pRcsPresenceInfo.length; i++ ) {
196             if(pRcsPresenceInfo[i].getInstanceInfo() == null){
197                 logger.error("invalid data getInstanceInfo = null");
198                 continue;
199             }
200 
201             String resUri = pRcsPresenceInfo[i].getResUri();
202             if(resUri == null){
203                 logger.error("invalid data getResUri = null");
204                 continue;
205             }
206 
207             String contactNumber = getPhoneFromUri(resUri);
208             if(contactNumber == null){
209                 logger.error("invalid data contactNumber=null");
210                 continue;
211             }
212 
213             if(pRcsPresenceInfo[i].getInstanceInfo().getResInstanceState() ==
214                     PresResInstanceInfo.UCE_PRES_RES_INSTANCE_STATE_TERMINATED){
215                 logger.debug("instatance state is terminated");
216                 String reason = pRcsPresenceInfo[i].getInstanceInfo().getReason();
217                 if(reason != null){
218                     reason = reason.toLowerCase();
219                     // Noresource: 404. Device shall consider the resource as non-EAB capable
220                     // Rejected: 403. Device shall consider the resource as non-EAB  capable
221                     // Deactivated: 408, 481, 603, other 4xx, 5xx 6xx. Ignore.
222                     // Give Up: 480. Ignore.
223                     // Probation: 503. Ignore.
224                     if(reason.equals("rejected") || reason.equals("noresource")){
225                         RcsPresenceInfo presenceInfo = new RcsPresenceInfo(contactNumber,
226                                 RcsPresenceInfo.VolteStatus.VOLTE_DISABLED,
227                                 RcsPresenceInfo.ServiceState.OFFLINE, null,
228                                 System.currentTimeMillis(),
229                                 RcsPresenceInfo.ServiceState.OFFLINE, null,
230                                 System.currentTimeMillis());
231                         addPresenceInfo(presenceInfoList, presenceInfo);
232                         logger.debug("reason=" + reason + " presenceInfo=" + presenceInfo);
233                         continue;
234                     }
235                 }
236             }
237 
238             RcsPresenceInfo presenceInfo = getPresenceInfoFromTuple(resUri,
239                     pRcsPresenceInfo[i].getInstanceInfo().getTupleInfo());
240             if(presenceInfo != null){
241                 addPresenceInfo(presenceInfoList, presenceInfo);
242             }else{
243                 logger.debug("presenceInfo["+ i + "] = null");
244                 addPresenceInfo(presenceInfoList, getPresenceInfoFromTuple(resUri, null));
245             }
246         }
247 
248         logger.debug("getPresenceInfoFromPresenceRes, presenceInfos:" + presenceInfoList);
249         if(presenceInfoList.size() == 0){
250             return null;
251         }
252 
253         RcsPresenceInfo[] theArray = new RcsPresenceInfo[presenceInfoList.size()];
254         return (RcsPresenceInfo[])presenceInfoList.toArray(theArray);
255     }
256 
getPhoneFromUri(String uriValue)257     static public String getPhoneFromUri(String uriValue) {
258         if(uriValue == null){
259             return null;
260         }
261 
262         int startIndex = uriValue.indexOf(":", 0);
263         int endIndex = uriValue.indexOf("@", startIndex);
264 
265         logger.debug("getPhoneFromUri uriValue=" + uriValue +
266             " startIndex=" + startIndex + " endIndex=" + endIndex);
267 
268         String number = uriValue;
269         if(endIndex != -1){
270             number = uriValue.substring(0, endIndex);
271         }
272 
273         if (startIndex != -1) {
274             return number = number.substring(startIndex + 1);
275         }
276 
277         return number;
278     }
279 
getUceCapability(RcsPresenceInfo info)280     public static RcsContactUceCapability getUceCapability(RcsPresenceInfo info) {
281         boolean volteCapable = false;
282         if (ServiceState.ONLINE == info.getServiceState(ServiceType.VOLTE_CALL)) {
283             volteCapable = true;
284         }
285 
286         boolean vtCapable = false;
287         if (ServiceState.ONLINE == info.getServiceState(ServiceType.VT_CALL)) {
288             vtCapable = true;
289         }
290 
291         ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(
292             volteCapable, vtCapable);
293         servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
294 
295         Uri contactUri = PresenceUtils.convertContactNumber(info.getContactNumber());
296 
297         RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
298                 RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
299                 RcsContactPresenceTuple.SERVICE_ID_MMTEL, "1.0");
300         tupleBuilder.setContactUri(contactUri).setServiceCapabilities(servCapsBuilder.build());
301 
302         PresenceBuilder presenceBuilder = new PresenceBuilder(contactUri,
303                 RcsContactUceCapability.SOURCE_TYPE_CACHED,
304                 RcsContactUceCapability.REQUEST_RESULT_FOUND);
305         presenceBuilder.addCapabilityTuple(tupleBuilder.build());
306 
307         return presenceBuilder.build();
308     }
309 
getRcsPresenceInfo(RcsContactUceCapability capability)310     public static RcsPresenceInfo getRcsPresenceInfo(RcsContactUceCapability capability) {
311         int volteCapable = ServiceState.OFFLINE;
312         int vtCapable = ServiceState.OFFLINE;
313         RcsContactPresenceTuple presenceTuple = capability.getCapabilityTuple(
314                 RcsContactPresenceTuple.SERVICE_ID_MMTEL);
315         if (presenceTuple != null) {
316             ServiceCapabilities serviceCaps = presenceTuple.getServiceCapabilities();
317             if (serviceCaps != null && serviceCaps.isAudioCapable()) {
318                 volteCapable = ServiceState.ONLINE;
319             }
320             if (serviceCaps != null && serviceCaps.isVideoCapable()) {
321                 vtCapable = ServiceState.ONLINE;
322             }
323         }
324         return new RcsPresenceInfo(capability.getContactUri().getSchemeSpecificPart(),
325                 // Not sure what the difference is, just track voice capable.
326                 (volteCapable == ServiceState.ONLINE) ? RcsPresenceInfo.VolteStatus.VOLTE_ENABLED :
327                         RcsPresenceInfo.VolteStatus.VOLTE_DISABLED, volteCapable,
328                 PresenceUtils.getNumber(capability.getContactUri()),
329                 // We always use system current time instead of time from server
330                 System.currentTimeMillis(), vtCapable,
331                 PresenceUtils.getNumber(capability.getContactUri()),
332                 // We always use system current time instead of time from server
333                 System.currentTimeMillis());
334     }
335 }
336