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 android.content.Context; 32 import android.net.Uri; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.telephony.ims.RcsContactUceCapability; 36 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder; 37 import android.text.TextUtils; 38 39 import com.android.ims.ResultCode; 40 import com.android.ims.internal.ContactNumberUtils; 41 import com.android.ims.internal.Logger; 42 import com.android.service.ims.RcsSettingUtils; 43 import com.android.service.ims.Task; 44 import com.android.service.ims.TaskManager; 45 import java.util.ArrayList; 46 import java.util.List; 47 48 public class PresenceSubscriber extends PresenceBase { 49 private Logger logger = Logger.getLogger(this.getClass().getName()); 50 51 private SubscribePublisher mSubscriber; 52 private final Object mSubscriberLock = new Object(); 53 54 private String mAvailabilityRetryNumber = null; 55 private int mAssociatedSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 56 57 private final String[] mConfigVolteProvisionErrorOnSubscribeResponse; 58 private final String[] mConfigRcsProvisionErrorOnSubscribeResponse; 59 60 /* 61 * Constructor 62 */ PresenceSubscriber(SubscribePublisher subscriber, Context context, String[] configVolteProvisionErrorOnSubscribeResponse, String[] configRcsProvisionErrorOnSubscribeResponse)63 public PresenceSubscriber(SubscribePublisher subscriber, Context context, 64 String[] configVolteProvisionErrorOnSubscribeResponse, 65 String[] configRcsProvisionErrorOnSubscribeResponse){ 66 super(context); 67 synchronized(mSubscriberLock) { 68 this.mSubscriber = subscriber; 69 } 70 mConfigVolteProvisionErrorOnSubscribeResponse 71 = configVolteProvisionErrorOnSubscribeResponse; 72 mConfigRcsProvisionErrorOnSubscribeResponse = configRcsProvisionErrorOnSubscribeResponse; 73 } 74 updatePresenceSubscriber(SubscribePublisher subscriber)75 public void updatePresenceSubscriber(SubscribePublisher subscriber) { 76 synchronized(mSubscriberLock) { 77 logger.print("Update PresencePublisher"); 78 this.mSubscriber = subscriber; 79 } 80 } 81 removePresenceSubscriber()82 public void removePresenceSubscriber() { 83 synchronized(mSubscriberLock) { 84 logger.print("Remove PresenceSubscriber"); 85 this.mSubscriber = null; 86 } 87 } 88 handleAssociatedSubscriptionChanged(int newSubId)89 public void handleAssociatedSubscriptionChanged(int newSubId) { 90 if (mAssociatedSubscription == newSubId) { 91 return; 92 } 93 mAssociatedSubscription = newSubId; 94 } 95 numberToUriString(String number)96 private String numberToUriString(String number) { 97 String formattedContact = number; 98 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 99 if (tm != null && !formattedContact.startsWith("sip:") 100 && !formattedContact.startsWith("tel:")){ 101 String domain = tm.getIsimDomain(); 102 logger.debug("domain=" + domain); 103 if (domain == null || domain.length() == 0){ 104 formattedContact = "tel:" + formattedContact; 105 } else { 106 formattedContact = "sip:" + formattedContact + "@" + domain; 107 } 108 } 109 110 logger.print("numberToUriString formattedContact=" + formattedContact); 111 return formattedContact; 112 } 113 numberToTelString(String number)114 private String numberToTelString(String number){ 115 String formatedContact = number; 116 if(!formatedContact.startsWith("sip:") && !formatedContact.startsWith("tel:")){ 117 formatedContact = "tel:" + formatedContact; 118 } 119 120 logger.print("numberToTelString formatedContact=" + formatedContact); 121 return formatedContact; 122 } 123 requestCapability(List<String> contactsNumber, ContactCapabilityResponse listener)124 public int requestCapability(List<String> contactsNumber, 125 ContactCapabilityResponse listener) { 126 127 SubscribePublisher subscriber = null; 128 synchronized(mSubscriberLock) { 129 subscriber = mSubscriber; 130 } 131 132 if (subscriber == null) { 133 logger.error("requestCapability Subscribe not registered"); 134 return ResultCode.SUBSCRIBE_NOT_REGISTERED; 135 } 136 137 if (!RcsSettingUtils.hasUserEnabledContactDiscovery(mContext, mAssociatedSubscription)) { 138 logger.warn("requestCapability request has been denied due to contact discovery being " 139 + "disabled by the user"); 140 return ResultCode.ERROR_SERVICE_NOT_ENABLED; 141 } 142 143 int ret = subscriber.getStackStatusForCapabilityRequest(); 144 if (ret < ResultCode.SUCCESS) { 145 logger.error("requestCapability ret=" + ret); 146 return ret; 147 } 148 149 if(contactsNumber == null || contactsNumber.size() ==0){ 150 ret = ResultCode.SUBSCRIBE_INVALID_PARAM; 151 return ret; 152 } 153 154 logger.debug("check contact size ..."); 155 if (contactsNumber.size() > RcsSettingUtils.getMaxNumbersInRCL(mAssociatedSubscription)) { 156 ret = ResultCode.SUBSCRIBE_TOO_LARGE; 157 logger.error("requestCapability contctNumber size=" + contactsNumber.size()); 158 return ret; 159 } 160 161 String[] formatedNumbers = ContactNumberUtils.getDefault().format(contactsNumber); 162 int formatResult = ContactNumberUtils.getDefault().validate(formatedNumbers); 163 if (formatResult != ContactNumberUtils.NUMBER_VALID) { 164 logger.error("requestCapability formatResult=" + formatResult); 165 return ResultCode.SUBSCRIBE_INVALID_PARAM; 166 } 167 168 String[] formatedContacts = new String[formatedNumbers.length]; 169 for(int i=0; i<formatedContacts.length; i++){ 170 formatedContacts[i] = numberToTelString(formatedNumbers[i]); 171 } 172 // In ms 173 long timeout = RcsSettingUtils.getCapabPollListSubExp(mAssociatedSubscription) * 1000; 174 timeout += RcsSettingUtils.getSIPT1Timer(mAssociatedSubscription); 175 176 // The terminal notification may be received shortly after the time limit of 177 // the subscription due to network delays or retransmissions. 178 // Device shall wait for 3sec after the end of the subscription period in order to 179 // accept such notifications without returning spurious errors (e.g. SIP 481) 180 timeout += 3000; 181 182 logger.print("add to task manager, formatedNumbers=" + 183 PresenceUtils.toContactString(formatedNumbers)); 184 int taskId = TaskManager.getDefault().addCapabilityTask(mContext, formatedNumbers, 185 listener, timeout); 186 logger.print("taskId=" + taskId); 187 188 ret = subscriber.requestCapability(formatedContacts, taskId); 189 if(ret < ResultCode.SUCCESS) { 190 logger.error("requestCapability ret=" + ret + " remove taskId=" + taskId); 191 TaskManager.getDefault().removeTask(taskId); 192 } 193 194 ret = taskId; 195 196 return ret; 197 } 198 requestAvailability(String contactNumber, ContactCapabilityResponse listener, boolean forceToNetwork)199 public int requestAvailability(String contactNumber, ContactCapabilityResponse listener, 200 boolean forceToNetwork) { 201 202 String formatedContact = ContactNumberUtils.getDefault().format(contactNumber); 203 int ret = ContactNumberUtils.getDefault().validate(formatedContact); 204 if(ret != ContactNumberUtils.NUMBER_VALID){ 205 return ret; 206 } 207 208 if (!RcsSettingUtils.hasUserEnabledContactDiscovery(mContext, mAssociatedSubscription)) { 209 logger.warn("requestCapability request has been denied due to contact discovery being " 210 + "disabled by the user"); 211 return ResultCode.ERROR_SERVICE_NOT_ENABLED; 212 } 213 214 if(!forceToNetwork){ 215 logger.debug("check if we can use the value in cache"); 216 int availabilityExpire = 217 RcsSettingUtils.getAvailabilityCacheExpiration(mAssociatedSubscription); 218 availabilityExpire = availabilityExpire>0?availabilityExpire*1000: 219 60*1000; // by default is 60s 220 logger.print("requestAvailability availabilityExpire=" + availabilityExpire); 221 222 TaskManager.getDefault().clearTimeoutAvailabilityTask(availabilityExpire); 223 224 Task task = TaskManager.getDefault().getAvailabilityTaskByContact(formatedContact); 225 if(task != null && task instanceof PresenceAvailabilityTask) { 226 PresenceAvailabilityTask availabilityTask = (PresenceAvailabilityTask)task; 227 if(availabilityTask.getNotifyTimestamp() == 0) { 228 // The previous one didn't get response yet. 229 logger.print("requestAvailability: the request is pending in queue"); 230 return ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE; 231 }else { 232 // not expire yet. Can use the previous value. 233 logger.print("requestAvailability: the prevous valuedoesn't be expired yet"); 234 return ResultCode.SUBSCRIBE_TOO_FREQUENTLY; 235 } 236 } 237 } 238 239 // Only poll/fetch capability/availability on LTE 240 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 241 if(tm == null || (tm.getDataNetworkType() != TelephonyManager.NETWORK_TYPE_LTE)) { 242 logger.error("requestAvailability return ERROR_SERVICE_NOT_AVAILABLE" + 243 " for it is not LTE network"); 244 return ResultCode.ERROR_SERVICE_NOT_AVAILABLE; 245 } 246 247 SubscribePublisher subscriber = null; 248 synchronized(mSubscriberLock) { 249 subscriber = mSubscriber; 250 } 251 252 if (subscriber == null) { 253 logger.error("requestAvailability Subscribe not registered"); 254 return ResultCode.SUBSCRIBE_NOT_REGISTERED; 255 } 256 257 ret = subscriber.getStackStatusForCapabilityRequest(); 258 if (ret < ResultCode.SUCCESS) { 259 logger.error("requestAvailability=" + ret); 260 return ret; 261 } 262 263 // user number format in TaskManager. 264 int taskId = TaskManager.getDefault().addAvailabilityTask(formatedContact, listener); 265 266 // Change it to URI format. 267 formatedContact = numberToUriString(formatedContact); 268 269 logger.print("addAvailabilityTask formatedContact=" + formatedContact); 270 271 ret = subscriber.requestAvailability(formatedContact, taskId); 272 if (ret < ResultCode.SUCCESS) { 273 logger.error("requestAvailability ret=" + ret + " remove taskId=" + taskId); 274 TaskManager.getDefault().removeTask(taskId); 275 } 276 277 ret = taskId; 278 279 return ret; 280 } 281 translateResponse403(String reasonPhrase)282 private int translateResponse403(String reasonPhrase){ 283 if(reasonPhrase == null){ 284 // No retry. The PS provisioning has not occurred correctly. UX Decision to show errror. 285 return ResultCode.SUBSCRIBE_GENIRIC_FAILURE; 286 } 287 288 reasonPhrase = reasonPhrase.toLowerCase(); 289 if(reasonPhrase.contains("user not registered")){ 290 // Register to IMS then retry the single resource subscription if capability polling. 291 // availability fetch: no retry. ignore the availability and allow LVC? (PLM decision) 292 return ResultCode.SUBSCRIBE_NOT_REGISTERED; 293 } 294 295 if(reasonPhrase.contains("not authorized for presence")){ 296 // No retry. 297 return ResultCode.SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE; 298 } 299 300 // unknown phrase: handle it as the same as no phrase 301 return ResultCode.SUBSCRIBE_FORBIDDEN; 302 } 303 translateResponseCode(int responseCode, String reasonPhrase)304 private int translateResponseCode(int responseCode, String reasonPhrase) { 305 // pSipResponse should not be null. 306 logger.debug("translateResponseCode getSipResponseCode=" +responseCode); 307 int ret = ResultCode.SUBSCRIBE_GENIRIC_FAILURE; 308 309 if(responseCode < 100 || responseCode > 699){ 310 logger.debug("internal error code sipCode=" + responseCode); 311 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; //it is internal issue. ignore it. 312 return ret; 313 } 314 315 switch(responseCode){ 316 case 200: 317 ret = ResultCode.SUCCESS; 318 break; 319 320 case 403: 321 ret = translateResponse403(reasonPhrase); 322 break; 323 324 case 404: 325 // Target MDN is not provisioned for VoLTE or it is not known as VzW IMS subscriber 326 // Device shall not retry. Device shall remove the VoLTE status of the target MDN 327 // and update UI 328 ret = ResultCode.SUBSCRIBE_NOT_FOUND; 329 break; 330 331 case 408: 332 // Request Timeout 333 // Device shall retry with exponential back-off 334 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; 335 break; 336 337 case 413: 338 // Too Large. 339 // Application need shrink the size of request contact list and resend the request 340 ret = ResultCode.SUBSCRIBE_TOO_LARGE; 341 break; 342 343 case 423: 344 // Interval Too Short. Requested expiry interval too short and server rejects it 345 // Device shall re-attempt subscription after changing the expiration interval in 346 // the Expires header field to be equal to or greater than the expiration interval 347 // within the Min-Expires header field of the 423 response 348 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; 349 break; 350 351 case 500: 352 // 500 Server Internal Error 353 // capability polling: exponential back-off retry (same rule as resource list) 354 // availability fetch: no retry. ignore the availability and allow LVC 355 // (PLM decision) 356 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; 357 break; 358 359 case 503: 360 // capability polling: exponential back-off retry (same rule as resource list) 361 // availability fetch: no retry. ignore the availability and allow LVC? 362 // (PLM decision) 363 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; 364 break; 365 366 // capability polling: Device shall retry with exponential back-off 367 // Availability Fetch: device shall ignore the error and shall not retry 368 case 603: 369 ret = ResultCode.SUBSCRIBE_TEMPORARY_ERROR; 370 break; 371 372 default: 373 // Other 4xx/5xx/6xx 374 // Device shall not retry 375 ret = ResultCode.SUBSCRIBE_GENIRIC_FAILURE; 376 } 377 378 logger.debug("translateResponseCode ret=" + ret); 379 return ret; 380 } 381 onSipResponse(int requestId, int responseCode, String reasonPhrase)382 public void onSipResponse(int requestId, int responseCode, String reasonPhrase) { 383 SubscribePublisher subscriber = null; 384 synchronized(mSubscriberLock) { 385 subscriber = mSubscriber; 386 } 387 388 if(isInConfigList(responseCode, reasonPhrase, 389 mConfigVolteProvisionErrorOnSubscribeResponse)) { 390 logger.print("volte provision sipCode=" + responseCode + " phrase=" + reasonPhrase); 391 if (subscriber != null) { 392 subscriber.updatePublisherState(PUBLISH_STATE_VOLTE_PROVISION_ERROR); 393 } 394 395 notifyDm(); 396 } else if(isInConfigList(responseCode, reasonPhrase, 397 mConfigRcsProvisionErrorOnSubscribeResponse)) { 398 logger.print("rcs proRcsPresence.vision sipCode=" + responseCode + " phrase=" 399 + reasonPhrase); 400 if (subscriber != null) { 401 subscriber.updatePublisherState(PUBLISH_STATE_RCS_PROVISION_ERROR); 402 } 403 } 404 405 int errorCode = translateResponseCode(responseCode, reasonPhrase); 406 logger.print("handleSipResponse errorCode=" + errorCode); 407 408 if(errorCode == ResultCode.SUBSCRIBE_NOT_REGISTERED){ 409 logger.debug("setPublishState to unknown for subscribe error 403 not registered"); 410 if (subscriber != null) { 411 subscriber.updatePublisherState(PUBLISH_STATE_OTHER_ERROR); 412 } 413 } 414 415 if(errorCode == ResultCode.SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE) { 416 logger.debug("ResultCode.SUBSCRIBE_NOT_AUTHORIZED_FOR_PRESENCE"); 417 } 418 419 if(errorCode == ResultCode.SUBSCRIBE_FORBIDDEN){ 420 logger.debug("ResultCode.SUBSCRIBE_FORBIDDEN"); 421 } 422 423 // Suppose the request ID had been set when IQPresListener_CMDStatus 424 Task task = TaskManager.getDefault().getTaskByRequestId(requestId); 425 logger.debug("handleSipResponse task=" + task); 426 if(task != null){ 427 task.mSipResponseCode = responseCode; 428 task.mSipReasonPhrase = reasonPhrase; 429 TaskManager.getDefault().putTask(task.mTaskId, task); 430 } 431 432 if(errorCode == ResultCode.SUBSCRIBE_NOT_REGISTERED && 433 task != null && task.mCmdId == TaskManager.TASK_TYPE_GET_AVAILABILITY) { 434 String[] contacts = ((PresenceTask)task).mContacts; 435 if(contacts != null && contacts.length>0){ 436 mAvailabilityRetryNumber = contacts[0]; 437 } 438 logger.debug("retry to get availability for " + mAvailabilityRetryNumber); 439 } 440 441 // 404 error for single contact only as per requirement 442 // need handle 404 for multiple contacts as per CV 3.24. 443 if(errorCode == ResultCode.SUBSCRIBE_NOT_FOUND && 444 task != null && ((PresenceTask)task).mContacts != null) { 445 String[] contacts = ((PresenceTask)task).mContacts; 446 ArrayList<RcsContactUceCapability> contactCapabilities = new ArrayList<>(); 447 448 for(int i=0; i<contacts.length; i++){ 449 if(TextUtils.isEmpty(contacts[i])){ 450 continue; 451 } 452 logger.debug("onSipResponse: contact= " + contacts[i] + ", not found."); 453 // Build contacts with no capabilities. 454 contactCapabilities.add(buildContactWithNoCapabilities( 455 PresenceUtils.convertContactNumber(contacts[i]))); 456 } 457 handleCapabilityUpdate(task, contactCapabilities, true); 458 459 } else if(errorCode == ResultCode.SUBSCRIBE_GENIRIC_FAILURE) { 460 updateAvailabilityToUnknown(task); 461 } 462 463 handleCallback(task, errorCode, false); 464 } 465 buildContactWithNoCapabilities(Uri contactUri)466 private RcsContactUceCapability buildContactWithNoCapabilities(Uri contactUri) { 467 PresenceBuilder presenceBuilder = new PresenceBuilder(contactUri, 468 RcsContactUceCapability.SOURCE_TYPE_CACHED, 469 RcsContactUceCapability.REQUEST_RESULT_FOUND); 470 return presenceBuilder.build(); 471 } 472 handleCapabilityUpdate(Task task, List<RcsContactUceCapability> capabilities, boolean updateLastTimestamp)473 private void handleCapabilityUpdate(Task task, List<RcsContactUceCapability> capabilities, 474 boolean updateLastTimestamp) { 475 if (task == null || task.mListener == null ) { 476 logger.warn("handleCapabilityUpdate, invalid listener!"); 477 return; 478 } 479 task.mListener.onCapabilitiesUpdated(task.mTaskId, capabilities, updateLastTimestamp); 480 } 481 retryToGetAvailability()482 public void retryToGetAvailability() { 483 if(mAvailabilityRetryNumber == null){ 484 return; 485 } 486 requestAvailability(mAvailabilityRetryNumber, null, true); 487 //retry one time only 488 mAvailabilityRetryNumber = null; 489 } 490 updatePresence(RcsContactUceCapability capabilities)491 public void updatePresence(RcsContactUceCapability capabilities) { 492 if(mContext == null){ 493 logger.error("updatePresence mContext == null"); 494 return; 495 } 496 497 ArrayList<RcsContactUceCapability> presenceInfos = new ArrayList<>(); 498 presenceInfos.add(capabilities); 499 500 String contactNumber = capabilities.getContactUri().getSchemeSpecificPart(); 501 // For single contact number we got 1 NOTIFY only. So regard it as terminated. 502 TaskManager.getDefault().onTerminated(contactNumber); 503 504 PresenceAvailabilityTask availabilityTask = TaskManager.getDefault(). 505 getAvailabilityTaskByContact(contactNumber); 506 if (availabilityTask != null) { 507 availabilityTask.updateNotifyTimestamp(); 508 } 509 Task task = TaskManager.getDefault().getTaskForSingleContactQuery(contactNumber); 510 handleCapabilityUpdate(task, presenceInfos, true); 511 } 512 updatePresences(int requestId, List<RcsContactUceCapability> contactsCapabilities, boolean isTerminated, String terminatedReason)513 public void updatePresences(int requestId, List<RcsContactUceCapability> contactsCapabilities, 514 boolean isTerminated, String terminatedReason) { 515 if(mContext == null){ 516 logger.error("updatePresences: mContext == null"); 517 return; 518 } 519 520 if (isTerminated) { 521 TaskManager.getDefault().onTerminated(requestId, terminatedReason); 522 } 523 524 Task task = TaskManager.getDefault().getTaskByRequestId(requestId); 525 if (contactsCapabilities.size() > 0 || task != null) { 526 handleCapabilityUpdate(task, contactsCapabilities, true); 527 } 528 } 529 onCommandStatusUpdated(int taskId, int requestId, int resultCode)530 public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) { 531 Task taskTmp = TaskManager.getDefault().getTask(taskId); 532 logger.print("handleCmdStatus resultCode=" + resultCode); 533 PresenceTask task = null; 534 if(taskTmp != null && (taskTmp instanceof PresenceTask)){ 535 task = (PresenceTask)taskTmp; 536 task.mSipRequestId = requestId; 537 task.mCmdStatus = resultCode; 538 TaskManager.getDefault().putTask(task.mTaskId, task); 539 540 // handle error as the same as temporary network error 541 // set availability to false, keep old capability 542 if(resultCode != ResultCode.SUCCESS && task.mContacts != null){ 543 updateAvailabilityToUnknown(task); 544 } 545 } 546 547 handleCallback(task, resultCode, true); 548 } 549 updateAvailabilityToUnknown(Task inTask)550 private void updateAvailabilityToUnknown(Task inTask){ 551 //only used for serviceState is offline or unknown. 552 if(mContext == null){ 553 logger.error("updateAvailabilityToUnknown mContext=null"); 554 return; 555 } 556 557 if(inTask == null){ 558 logger.error("updateAvailabilityToUnknown task=null"); 559 return; 560 } 561 562 if(!(inTask instanceof PresenceTask)){ 563 logger.error("updateAvailabilityToUnknown not PresencTask"); 564 return; 565 } 566 567 PresenceTask task = (PresenceTask)inTask; 568 569 if(task.mContacts == null || task.mContacts.length ==0){ 570 logger.error("updateAvailabilityToUnknown no contacts"); 571 return; 572 } 573 574 ArrayList<RcsContactUceCapability> presenceInfoList = new ArrayList<>(); 575 for (int i = 0; i< task.mContacts.length; i++) { 576 if(TextUtils.isEmpty(task.mContacts[i])){ 577 continue; 578 } 579 // Add each contacts with no capabilities. 580 Uri uri = PresenceUtils.convertContactNumber(task.mContacts[i]); 581 PresenceBuilder presenceBuilder = new PresenceBuilder(uri, 582 RcsContactUceCapability.SOURCE_TYPE_CACHED, 583 RcsContactUceCapability.REQUEST_RESULT_FOUND); 584 presenceInfoList.add(presenceBuilder.build()); 585 } 586 587 if(presenceInfoList.size() > 0) { 588 handleCapabilityUpdate(task, presenceInfoList, false); 589 } 590 } 591 } 592 593