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.presence; 30 31 import java.lang.String; 32 import java.util.ArrayList; 33 import java.util.List; 34 import android.text.TextUtils; 35 36 import com.android.ims.internal.uce.presence.PresTupleInfo; 37 import com.android.ims.internal.uce.presence.PresRlmiInfo; 38 import com.android.ims.internal.uce.presence.PresResInfo; 39 import com.android.ims.internal.uce.presence.PresResInstanceInfo; 40 41 import com.android.ims.RcsManager.ResultCode; 42 import com.android.ims.RcsPresenceInfo; 43 import com.android.ims.RcsPresenceInfo.ServiceType; 44 import com.android.ims.RcsPresenceInfo.ServiceState; 45 46 import com.android.ims.internal.Logger; 47 48 public class PresenceInfoParser{ 49 /* 50 * The logger 51 */ 52 static private Logger logger = Logger.getLogger("PresenceInfoParser"); 53 54 public PresenceInfoParser() { 55 } 56 57 static public RcsPresenceInfo getPresenceInfoFromTuple(String pPresentityURI, 58 PresTupleInfo[] pTupleInfo){ 59 logger.debug("getPresenceInfoFromTuple: pPresentityURI=" + pPresentityURI + 60 " pTupleInfo=" + pTupleInfo); 61 62 if(pPresentityURI == null){ 63 logger.error("pPresentityURI=" + pPresentityURI); 64 return null; 65 } 66 67 String contactNumber = getPhoneFromUri(pPresentityURI); 68 // Now that we got notification if the content didn't indicate it is not Volte 69 // then it should be Volte enabled. So default to Volte enabled. 70 int volteStatus = RcsPresenceInfo.VolteStatus.VOLTE_ENABLED; 71 72 int ipVoiceCallState = RcsPresenceInfo.ServiceState.UNKNOWN; 73 String ipVoiceCallServiceNumber = null; 74 // We use the timestamp which when we received it instead of the one in PDU 75 long ipVoiceCallTimestamp = System.currentTimeMillis(); 76 77 int ipVideoCallState = RcsPresenceInfo.ServiceState.UNKNOWN; 78 String ipVideoCallServiceNumber = null; 79 long ipVideoCallTimestamp = System.currentTimeMillis(); 80 81 if( pTupleInfo == null){ 82 logger.debug("pTupleInfo=null"); 83 return (new RcsPresenceInfo(contactNumber, volteStatus, 84 ipVoiceCallState, ipVoiceCallServiceNumber, ipVoiceCallTimestamp, 85 ipVideoCallState, ipVideoCallServiceNumber, ipVideoCallTimestamp)); 86 } 87 88 for(int i = 0; i < pTupleInfo.length; i++){ 89 // Video call has high priority. If it supports video call it will support voice call. 90 String featureTag = pTupleInfo[i].getFeatureTag(); 91 logger.debug("getFeatureTag " + i + ": " + featureTag); 92 if(featureTag.equals( 93 "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";video") || 94 featureTag.equals("+g.gsma.rcs.telephony=\"cs,volte\";video")) { 95 logger.debug("Got Volte and VT enabled tuple"); 96 ipVoiceCallState = RcsPresenceInfo.ServiceState.ONLINE; 97 ipVideoCallState = RcsPresenceInfo.ServiceState.ONLINE; 98 99 if(pTupleInfo[i].getContactUri() != null){ 100 ipVideoCallServiceNumber = getPhoneFromUri( 101 pTupleInfo[i].getContactUri().toString()); 102 } 103 } else if(featureTag.equals("+g.gsma.rcs.telephony=\"cs\";video")) { 104 logger.debug("Got Volte disabled but VT enabled tuple"); 105 ipVoiceCallState = RcsPresenceInfo.ServiceState.OFFLINE; 106 ipVideoCallState = RcsPresenceInfo.ServiceState.ONLINE; 107 108 if(pTupleInfo[i].getContactUri() != null){ 109 ipVideoCallServiceNumber = getPhoneFromUri( 110 pTupleInfo[i].getContactUri().toString()); 111 } 112 }else{ 113 if(featureTag.equals( 114 "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"") || 115 featureTag.equals("+g.gsma.rcs.telephony=\"cs,volte\"")) { 116 117 logger.debug("Got Volte only tuple"); 118 ipVoiceCallState = RcsPresenceInfo.ServiceState.ONLINE; 119 // "OR" for multiple tuples. 120 if(RcsPresenceInfo.ServiceState.UNKNOWN == ipVideoCallState) { 121 ipVideoCallState = RcsPresenceInfo.ServiceState.OFFLINE; 122 } 123 124 if(pTupleInfo[i].getContactUri() != null){ 125 ipVoiceCallServiceNumber = getPhoneFromUri( 126 pTupleInfo[i].getContactUri().toString()); 127 } 128 }else{ 129 logger.debug("Ignoring feature tag: " + pTupleInfo[i].getFeatureTag()); 130 } 131 } 132 } 133 134 RcsPresenceInfo retPresenceInfo = new RcsPresenceInfo(contactNumber,volteStatus, 135 ipVoiceCallState, ipVoiceCallServiceNumber, ipVoiceCallTimestamp, 136 ipVideoCallState, ipVideoCallServiceNumber, ipVideoCallTimestamp); 137 138 logger.debug("getPresenceInfoFromTuple: " + retPresenceInfo); 139 140 return retPresenceInfo; 141 } 142 143 static private void addPresenceInfo(ArrayList<RcsPresenceInfo> presenceInfoList, 144 RcsPresenceInfo presenceInfo){ 145 logger.debug("addPresenceInfo presenceInfoList=" + presenceInfoList + 146 " presenceInfo=" + presenceInfo); 147 148 if(presenceInfoList == null || presenceInfo == null){ 149 logger.debug("addPresenceInfo presenceInfoList=" + presenceInfoList + 150 " presenceInfo=" + presenceInfo); 151 return; 152 } 153 154 for(int i=0; i< presenceInfoList.size(); i++){ 155 RcsPresenceInfo presenceInfoTmp = presenceInfoList.get(i); 156 if((presenceInfoTmp != null) && (presenceInfoTmp.getContactNumber() != null) && 157 presenceInfoTmp.getContactNumber().equalsIgnoreCase( 158 presenceInfo.getContactNumber())){ 159 // merge the information 160 presenceInfoList.set(i, new RcsPresenceInfo(presenceInfoTmp.getContactNumber(), 161 presenceInfoTmp.getVolteStatus(), 162 ((ServiceState.ONLINE == presenceInfo.getServiceState( 163 ServiceType.VOLTE_CALL)) || 164 (ServiceState.ONLINE == presenceInfoTmp.getServiceState( 165 ServiceType.VOLTE_CALL)))?ServiceState.ONLINE: 166 presenceInfoTmp.getServiceState(ServiceType.VOLTE_CALL), 167 presenceInfoTmp.getServiceContact(ServiceType.VOLTE_CALL), 168 presenceInfoTmp.getTimeStamp(ServiceType.VOLTE_CALL), 169 ((ServiceState.ONLINE == presenceInfo.getServiceState( 170 ServiceType.VT_CALL)) || 171 (ServiceState.ONLINE == presenceInfoTmp.getServiceState( 172 ServiceType.VT_CALL)))?ServiceState.ONLINE: 173 presenceInfoTmp.getServiceState(ServiceType.VT_CALL), 174 presenceInfoTmp.getServiceContact(ServiceType.VOLTE_CALL), 175 presenceInfoTmp.getTimeStamp(ServiceType.VOLTE_CALL))); 176 return; 177 } 178 } 179 180 // didn't merge, so add the new one. 181 presenceInfoList.add(presenceInfo); 182 } 183 184 static public RcsPresenceInfo[] getPresenceInfosFromPresenceRes( 185 PresRlmiInfo pRlmiInfo, PresResInfo[] pRcsPresenceInfo) { 186 if(pRcsPresenceInfo == null){ 187 logger.debug("getPresenceInfosFromPresenceRes pRcsPresenceInfo=null"); 188 return null; 189 } 190 191 ArrayList<RcsPresenceInfo> presenceInfoList = new ArrayList<RcsPresenceInfo>(); 192 for(int i=0; i < pRcsPresenceInfo.length; i++ ) { 193 if(pRcsPresenceInfo[i].getInstanceInfo() == null){ 194 logger.error("invalid data getInstanceInfo = null"); 195 continue; 196 } 197 198 String resUri = pRcsPresenceInfo[i].getResUri(); 199 if(resUri == null){ 200 logger.error("invalid data getResUri = null"); 201 continue; 202 } 203 204 String contactNumber = getPhoneFromUri(resUri); 205 if(contactNumber == null){ 206 logger.error("invalid data contactNumber=null"); 207 continue; 208 } 209 210 if(pRcsPresenceInfo[i].getInstanceInfo().getResInstanceState() == 211 PresResInstanceInfo.UCE_PRES_RES_INSTANCE_STATE_TERMINATED){ 212 logger.debug("instatance state is terminated"); 213 String reason = pRcsPresenceInfo[i].getInstanceInfo().getReason(); 214 if(reason != null){ 215 reason = reason.toLowerCase(); 216 // Noresource: 404. Device shall consider the resource as non-EAB capable 217 // Rejected: 403. Device shall consider the resource as non-EAB capable 218 // Deactivated: 408, 481, 603, other 4xx, 5xx 6xx. Ignore. 219 // Give Up: 480. Ignore. 220 // Probation: 503. Ignore. 221 if(reason.equals("rejected") || reason.equals("noresource")){ 222 RcsPresenceInfo presenceInfo = new RcsPresenceInfo(contactNumber, 223 RcsPresenceInfo.VolteStatus.VOLTE_DISABLED, 224 RcsPresenceInfo.ServiceState.OFFLINE, null, 225 System.currentTimeMillis(), 226 RcsPresenceInfo.ServiceState.OFFLINE, null, 227 System.currentTimeMillis()); 228 addPresenceInfo(presenceInfoList, presenceInfo); 229 logger.debug("reason=" + reason + " presenceInfo=" + presenceInfo); 230 continue; 231 } 232 } 233 } 234 235 RcsPresenceInfo presenceInfo = getPresenceInfoFromTuple(resUri, 236 pRcsPresenceInfo[i].getInstanceInfo().getTupleInfo()); 237 if(presenceInfo != null){ 238 addPresenceInfo(presenceInfoList, presenceInfo); 239 }else{ 240 logger.debug("presenceInfo["+ i + "] = null"); 241 addPresenceInfo(presenceInfoList, getPresenceInfoFromTuple(resUri, null)); 242 } 243 } 244 245 logger.debug("getPresenceInfoFromPresenceRes, presenceInfos:" + presenceInfoList); 246 if(presenceInfoList.size() == 0){ 247 return null; 248 } 249 250 RcsPresenceInfo[] theArray = new RcsPresenceInfo[presenceInfoList.size()]; 251 return (RcsPresenceInfo[])presenceInfoList.toArray(theArray); 252 } 253 254 static public String getPhoneFromUri(String uriValue) { 255 if(uriValue == null){ 256 return null; 257 } 258 259 int startIndex = uriValue.indexOf(":", 0); 260 int endIndex = uriValue.indexOf("@", startIndex); 261 262 logger.debug("getPhoneFromUri uriValue=" + uriValue + 263 " startIndex=" + startIndex + " endIndex=" + endIndex); 264 265 String number = uriValue; 266 if(endIndex != -1){ 267 number = uriValue.substring(0, endIndex); 268 } 269 270 if (startIndex != -1) { 271 return number = number.substring(startIndex + 1); 272 } 273 274 return number; 275 } 276 } 277