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.publish; 18 19 import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_CACHED; 20 21 import android.content.Context; 22 import android.net.Uri; 23 import android.telecom.PhoneAccount; 24 import android.telecom.TelecomManager; 25 import android.telephony.AccessNetworkConstants; 26 import android.telephony.ims.ImsRegistrationAttributes; 27 import android.telephony.ims.RcsContactPresenceTuple; 28 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities; 29 import android.telephony.ims.RcsContactUceCapability; 30 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; 31 import android.telephony.ims.RcsContactUceCapability.OptionsBuilder; 32 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder; 33 import android.telephony.ims.feature.MmTelFeature; 34 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities; 35 import android.util.IndentingPrintWriter; 36 import android.util.ArraySet; 37 import android.util.LocalLog; 38 import android.util.Log; 39 40 import com.android.ims.rcs.uce.util.FeatureTags; 41 import com.android.ims.rcs.uce.util.UceUtils; 42 import com.android.internal.annotations.VisibleForTesting; 43 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.Iterator; 49 import java.util.List; 50 import java.util.Objects; 51 import java.util.Set; 52 import java.util.stream.Collectors; 53 54 /** 55 * Stores the device's capabilities information. 56 */ 57 public class DeviceCapabilityInfo { 58 private static final String LOG_TAG = UceUtils.getLogPrefix() + "DeviceCapabilityInfo"; 59 60 private final int mSubId; 61 62 private final LocalLog mLocalLog = new LocalLog(UceUtils.LOG_SIZE); 63 64 // FT overrides to add to the IMS registration, which will be added to the existing 65 // capabilities. 66 private final Set<String> mOverrideAddFeatureTags = new ArraySet<>(); 67 68 // FT overrides to remove from the existing IMS registration, which will remove the related 69 // capabilities. 70 private final Set<String> mOverrideRemoveFeatureTags = new ArraySet<>(); 71 72 // Tracks capability status based on the IMS registration. 73 private PublishServiceDescTracker mServiceCapRegTracker; 74 75 // The feature tags associated with the last IMS registration update. 76 private Set<String> mLastRegistrationFeatureTags = Collections.emptySet(); 77 // The feature tags associated with the last IMS registration update, which also include 78 // overrides 79 private Set<String> mLastRegistrationOverrideFeatureTags = Collections.emptySet(); 80 81 // The mmtel feature is registered or not 82 private boolean mMmtelRegistered; 83 84 // The network type which ims mmtel registers on. 85 private int mMmtelNetworkRegType; 86 87 // The list of the mmtel associated uris 88 private List<Uri> mMmtelAssociatedUris = Collections.emptyList(); 89 90 // The rcs feature is registered or not 91 private boolean mRcsRegistered; 92 93 // The list of the rcs associated uris 94 private List<Uri> mRcsAssociatedUris = Collections.emptyList(); 95 96 // Whether or not presence is reported as capable 97 private boolean mPresenceCapable; 98 99 // The network type which ims rcs registers on. 100 private int mRcsNetworkRegType; 101 102 // The MMTel capabilities of this subscription Id 103 private MmTelFeature.MmTelCapabilities mMmTelCapabilities; 104 105 // Whether the settings are changed or not 106 private int mTtyPreferredMode; 107 private boolean mMobileData; 108 private boolean mVtSetting; 109 110 // The service description associated with the last publication update. 111 private final Set<ServiceDescription> mLastSuccessfulCapabilities = new ArraySet<>(); 112 // The service description to temporarily store the presence capability being sent. 113 private Set<ServiceDescription> mPendingPublishCapabilities; 114 DeviceCapabilityInfo(int subId, String[] capToRegistrationMap)115 public DeviceCapabilityInfo(int subId, String[] capToRegistrationMap) { 116 mSubId = subId; 117 mServiceCapRegTracker = PublishServiceDescTracker.fromCarrierConfig(capToRegistrationMap); 118 reset(); 119 } 120 121 /** 122 * Reset all the status. 123 */ reset()124 public synchronized void reset() { 125 logd("reset"); 126 mMmtelRegistered = false; 127 mMmtelNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 128 mRcsRegistered = false; 129 mRcsNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 130 mTtyPreferredMode = TelecomManager.TTY_MODE_OFF; 131 mMobileData = true; 132 mVtSetting = true; 133 mMmTelCapabilities = new MmTelCapabilities(); 134 mMmtelAssociatedUris = Collections.EMPTY_LIST; 135 mRcsAssociatedUris = Collections.EMPTY_LIST; 136 mLastSuccessfulCapabilities.clear(); 137 mPendingPublishCapabilities = null; 138 } 139 140 /** 141 * Update the capability registration tracker feature tag override mapping. 142 * @return if true, this has caused a change in the Feature Tags associated with the device 143 * and a new PUBLISH should be generated. 144 */ updateCapabilityRegistrationTrackerMap(String[] newMap)145 public synchronized boolean updateCapabilityRegistrationTrackerMap(String[] newMap) { 146 Set<String> oldTags = mServiceCapRegTracker.copyRegistrationFeatureTags(); 147 mServiceCapRegTracker = PublishServiceDescTracker.fromCarrierConfig(newMap); 148 mServiceCapRegTracker.updateImsRegistration(mLastRegistrationOverrideFeatureTags); 149 boolean changed = !oldTags.equals(mServiceCapRegTracker.copyRegistrationFeatureTags()); 150 if (changed) logi("Carrier Config Change resulted in associated FT list change"); 151 return changed; 152 } 153 isImsRegistered()154 public synchronized boolean isImsRegistered() { 155 return mMmtelRegistered || mRcsRegistered; 156 } 157 158 /** 159 * Update the status that IMS MMTEL is registered. 160 */ updateImsMmtelRegistered(int type)161 public synchronized void updateImsMmtelRegistered(int type) { 162 StringBuilder builder = new StringBuilder(); 163 builder.append("IMS MMTEL registered: original state=").append(mMmtelRegistered) 164 .append(", changes type from ").append(mMmtelNetworkRegType) 165 .append(" to ").append(type); 166 logi(builder.toString()); 167 168 if (!mMmtelRegistered) { 169 mMmtelRegistered = true; 170 } 171 172 if (mMmtelNetworkRegType != type) { 173 mMmtelNetworkRegType = type; 174 } 175 } 176 177 /** 178 * Update the status that IMS MMTEL is unregistered. 179 */ updateImsMmtelUnregistered()180 public synchronized boolean updateImsMmtelUnregistered() { 181 logi("IMS MMTEL unregistered: original state=" + mMmtelRegistered); 182 boolean changed = false; 183 if (mMmtelRegistered) { 184 mMmtelRegistered = false; 185 changed = true; 186 } 187 mMmtelNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 188 mLastSuccessfulCapabilities.clear(); 189 mPendingPublishCapabilities = null; 190 return changed; 191 } 192 193 /** 194 * Update the MMTel associated URIs which are provided by the IMS service. 195 */ updateMmTelAssociatedUri(Uri[] uris)196 public synchronized void updateMmTelAssociatedUri(Uri[] uris) { 197 int originalSize = mMmtelAssociatedUris.size(); 198 if (uris != null) { 199 mMmtelAssociatedUris = Arrays.stream(uris) 200 .filter(Objects::nonNull) 201 .collect(Collectors.toList()); 202 } else { 203 mMmtelAssociatedUris.clear(); 204 } 205 int currentSize = mMmtelAssociatedUris.size(); 206 logd("updateMmTelAssociatedUri: size from " + originalSize + " to " + currentSize); 207 } 208 209 /** 210 * Get the MMTEL associated URI. When there are multiple uris in the list, take the first uri. 211 * Return null if the list of the MMTEL associated uri is empty. 212 */ getMmtelAssociatedUri()213 public synchronized Uri getMmtelAssociatedUri() { 214 if (!mMmtelAssociatedUris.isEmpty()) { 215 return mMmtelAssociatedUris.get(0); 216 } 217 return null; 218 } 219 220 /** 221 * Update the status that IMS RCS is registered. 222 * @return true if the IMS registration status changed, false if it did not. 223 */ updateImsRcsRegistered(ImsRegistrationAttributes attr)224 public synchronized boolean updateImsRcsRegistered(ImsRegistrationAttributes attr) { 225 StringBuilder builder = new StringBuilder(); 226 builder.append("IMS RCS registered: original state=").append(mRcsRegistered) 227 .append(", changes type from ").append(mRcsNetworkRegType) 228 .append(" to ").append(attr.getTransportType()); 229 logi(builder.toString()); 230 231 boolean changed = false; 232 if (!mRcsRegistered) { 233 mRcsRegistered = true; 234 changed = true; 235 } 236 237 if (mRcsNetworkRegType != attr.getTransportType()) { 238 mRcsNetworkRegType = attr.getTransportType(); 239 changed = true; 240 } 241 242 mLastRegistrationFeatureTags = attr.getFeatureTags(); 243 changed |= updateRegistration(mLastRegistrationFeatureTags); 244 245 return changed; 246 } 247 248 /** 249 * Update the status that IMS RCS is unregistered. 250 */ updateImsRcsUnregistered()251 public synchronized boolean updateImsRcsUnregistered() { 252 logi("IMS RCS unregistered: original state=" + mRcsRegistered); 253 boolean changed = false; 254 if (mRcsRegistered) { 255 mRcsRegistered = false; 256 changed = true; 257 } 258 259 mLastRegistrationFeatureTags = Collections.emptySet(); 260 updateRegistration(mLastRegistrationFeatureTags); 261 mRcsNetworkRegType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 262 mLastSuccessfulCapabilities.clear(); 263 mPendingPublishCapabilities = null; 264 return changed; 265 } 266 267 /** 268 * Update the RCS associated URIs which is provided by the IMS service. 269 */ updateRcsAssociatedUri(Uri[] uris)270 public synchronized void updateRcsAssociatedUri(Uri[] uris) { 271 int originalSize = mRcsAssociatedUris.size(); 272 if (uris != null) { 273 mRcsAssociatedUris = Arrays.stream(uris) 274 .filter(Objects::nonNull) 275 .collect(Collectors.toList()); 276 } else { 277 mRcsAssociatedUris.clear(); 278 } 279 int currentSize = mRcsAssociatedUris.size(); 280 logd("updateRcsAssociatedUri: size from " + originalSize + " to " + currentSize); 281 } 282 283 /** 284 * Get the RCS associated URI. When there are multiple uris in the list, take the first uri. 285 * Return null if the list of the RCS associated uri is empty. 286 */ getRcsAssociatedUri()287 public synchronized Uri getRcsAssociatedUri() { 288 if (!mRcsAssociatedUris.isEmpty()) { 289 return mRcsAssociatedUris.get(0); 290 } 291 return null; 292 } 293 294 /** 295 * Get the first URI from the "p-associated-uri" header included in the IMS registration 296 * response. 297 * @param preferTelUri If {@code true}, prefer returning the first TEL URI. If no TEL 298 * URIs exist, this method will still return the preferred (first) SIP URI 299 * in the header. If {@code false}, we will return the first URI 300 * in the "p-associated-uri" header, independent of the URI scheme. 301 */ getImsAssociatedUri(boolean preferTelUri)302 public synchronized Uri getImsAssociatedUri(boolean preferTelUri) { 303 if (preferTelUri) { 304 if (!mRcsAssociatedUris.isEmpty()) { 305 for (Uri rcsAssociatedUri : mRcsAssociatedUris) { 306 if (PhoneAccount.SCHEME_TEL.equalsIgnoreCase(rcsAssociatedUri.getScheme())) { 307 return rcsAssociatedUri; 308 } 309 } 310 } 311 if (!mMmtelAssociatedUris.isEmpty()) { 312 for (Uri mmtelAssociatedUri : mMmtelAssociatedUris) { 313 if (PhoneAccount.SCHEME_TEL.equalsIgnoreCase(mmtelAssociatedUri.getScheme())) { 314 return mmtelAssociatedUri; 315 } 316 } 317 } 318 } 319 320 // Either we have not found a TEL URI or we do not prefer TEL URIs. Get the first URI from 321 // p-associated-uri list. 322 if (!mRcsAssociatedUris.isEmpty()) { 323 return mRcsAssociatedUris.get(0); 324 } else if (!mMmtelAssociatedUris.isEmpty()) { 325 return mMmtelAssociatedUris.get(0); 326 } else { 327 return null; 328 } 329 } 330 addRegistrationOverrideCapabilities(Set<String> featureTags)331 public synchronized boolean addRegistrationOverrideCapabilities(Set<String> featureTags) { 332 logd("override - add: " + featureTags); 333 mOverrideRemoveFeatureTags.removeAll(featureTags); 334 mOverrideAddFeatureTags.addAll(featureTags); 335 // Call with the last feature tags so that the new ones will be potentially picked up. 336 return updateRegistration(mLastRegistrationFeatureTags); 337 }; 338 removeRegistrationOverrideCapabilities(Set<String> featureTags)339 public synchronized boolean removeRegistrationOverrideCapabilities(Set<String> featureTags) { 340 logd("override - remove: " + featureTags); 341 mOverrideAddFeatureTags.removeAll(featureTags); 342 mOverrideRemoveFeatureTags.addAll(featureTags); 343 // Call with the last feature tags so that the new ones will be potentially picked up. 344 return updateRegistration(mLastRegistrationFeatureTags); 345 }; 346 clearRegistrationOverrideCapabilities()347 public synchronized boolean clearRegistrationOverrideCapabilities() { 348 logd("override - clear"); 349 mOverrideAddFeatureTags.clear(); 350 mOverrideRemoveFeatureTags.clear(); 351 // Call with the last feature tags so that base tags will be restored 352 return updateRegistration(mLastRegistrationFeatureTags); 353 }; 354 355 /** 356 * Update the IMS registration tracked by the PublishServiceDescTracker if needed. 357 * @return true if the registration changed, else otherwise. 358 */ updateRegistration(Set<String> baseTags)359 private boolean updateRegistration(Set<String> baseTags) { 360 Set<String> updatedTags = updateImsRegistrationFeatureTags(baseTags); 361 if (!mLastRegistrationOverrideFeatureTags.equals(updatedTags)) { 362 mLastRegistrationOverrideFeatureTags = updatedTags; 363 mServiceCapRegTracker.updateImsRegistration(updatedTags); 364 return true; 365 } 366 return false; 367 } 368 369 /** 370 * Combine IMS registration with overrides to produce a new feature tag Set. 371 * @return true if the IMS registration changed, false otherwise. 372 */ updateImsRegistrationFeatureTags(Set<String> featureTags)373 private synchronized Set<String> updateImsRegistrationFeatureTags(Set<String> featureTags) { 374 Set<String> tags = new ArraySet<>(featureTags); 375 tags.addAll(mOverrideAddFeatureTags); 376 tags.removeAll(mOverrideRemoveFeatureTags); 377 return tags; 378 } 379 380 /** 381 * Update the TTY preferred mode. 382 * @return {@code true} if tty preferred mode is changed, {@code false} otherwise. 383 */ updateTtyPreferredMode(int ttyMode)384 public synchronized boolean updateTtyPreferredMode(int ttyMode) { 385 if (mTtyPreferredMode != ttyMode) { 386 logd("TTY preferred mode changes from " + mTtyPreferredMode + " to " + ttyMode); 387 mTtyPreferredMode = ttyMode; 388 return true; 389 } 390 return false; 391 } 392 393 /** 394 * Update mobile data setting. 395 * @return {@code true} if the mobile data setting is changed, {@code false} otherwise. 396 */ updateMobileData(boolean mobileData)397 public synchronized boolean updateMobileData(boolean mobileData) { 398 if (mMobileData != mobileData) { 399 logd("Mobile data changes from " + mMobileData + " to " + mobileData); 400 mMobileData = mobileData; 401 return true; 402 } 403 return false; 404 } 405 406 /** 407 * Update VT setting. 408 * @return {@code true} if vt setting is changed, {@code false}.otherwise. 409 */ updateVtSetting(boolean vtSetting)410 public synchronized boolean updateVtSetting(boolean vtSetting) { 411 if (mVtSetting != vtSetting) { 412 logd("VT setting changes from " + mVtSetting + " to " + vtSetting); 413 mVtSetting = vtSetting; 414 return true; 415 } 416 return false; 417 } 418 419 /** 420 * Update the MMTEL capabilities if the capabilities is changed. 421 * @return {@code true} if the mmtel capabilities are changed, {@code false} otherwise. 422 */ updateMmtelCapabilitiesChanged(MmTelCapabilities capabilities)423 public synchronized boolean updateMmtelCapabilitiesChanged(MmTelCapabilities capabilities) { 424 if (capabilities == null) { 425 return false; 426 } 427 boolean oldVolteAvailable = isVolteAvailable(mMmtelNetworkRegType, mMmTelCapabilities); 428 boolean oldVoWifiAvailable = isVoWifiAvailable(mMmtelNetworkRegType, mMmTelCapabilities); 429 boolean oldVtAvailable = isVtAvailable(mMmtelNetworkRegType, mMmTelCapabilities); 430 boolean oldViWifiAvailable = isViWifiAvailable(mMmtelNetworkRegType, mMmTelCapabilities); 431 boolean oldCallComposerAvailable = isCallComposerAvailable(mMmTelCapabilities); 432 433 boolean volteAvailable = isVolteAvailable(mMmtelNetworkRegType, capabilities); 434 boolean voWifiAvailable = isVoWifiAvailable(mMmtelNetworkRegType, capabilities); 435 boolean vtAvailable = isVtAvailable(mMmtelNetworkRegType, capabilities); 436 boolean viWifiAvailable = isViWifiAvailable(mMmtelNetworkRegType, capabilities); 437 boolean callComposerAvailable = isCallComposerAvailable(capabilities); 438 439 logd("updateMmtelCapabilitiesChanged: from " + mMmTelCapabilities + " to " + capabilities); 440 441 // Update to the new mmtel capabilities 442 mMmTelCapabilities = deepCopyCapabilities(capabilities); 443 444 if (oldVolteAvailable != volteAvailable 445 || oldVoWifiAvailable != voWifiAvailable 446 || oldVtAvailable != vtAvailable 447 || oldViWifiAvailable != viWifiAvailable 448 || oldCallComposerAvailable != callComposerAvailable) { 449 return true; 450 } 451 return false; 452 } 453 updatePresenceCapable(boolean isCapable)454 public synchronized void updatePresenceCapable(boolean isCapable) { 455 mPresenceCapable = isCapable; 456 } 457 isPresenceCapable()458 public synchronized boolean isPresenceCapable() { 459 return mPresenceCapable; 460 } 461 462 // Get the device's capabilities with the PRESENCE mechanism. getChangedPresenceCapability(Context context)463 public RcsContactUceCapability getChangedPresenceCapability(Context context) { 464 if (context == null) { 465 return null; 466 } 467 Set<ServiceDescription> capableFromReg = 468 mServiceCapRegTracker.copyRegistrationCapabilities(); 469 if (isPresenceCapabilityChanged(capableFromReg)) { 470 RcsContactUceCapability rcsContactUceCapability = getPresenceCapabilities(context); 471 if (rcsContactUceCapability != null) { 472 mPendingPublishCapabilities = mServiceCapRegTracker.copyRegistrationCapabilities(); 473 } 474 return rcsContactUceCapability; 475 } 476 return null; 477 } 478 setPresencePublishResult(boolean isSuccess)479 public void setPresencePublishResult(boolean isSuccess) { 480 if (isSuccess) { 481 mLastSuccessfulCapabilities.clear(); 482 if (mPendingPublishCapabilities != null) { 483 mLastSuccessfulCapabilities.addAll(mPendingPublishCapabilities); 484 } 485 } 486 mPendingPublishCapabilities = null; 487 } 488 resetPresenceCapability()489 public void resetPresenceCapability() { 490 mLastSuccessfulCapabilities.clear(); 491 mPendingPublishCapabilities = null; 492 } 493 getLastSuccessfulPresenceTuplesWithoutContactUri()494 public List<RcsContactPresenceTuple> getLastSuccessfulPresenceTuplesWithoutContactUri() { 495 List<RcsContactPresenceTuple> presenceTuples = new ArrayList<>(); 496 if (mLastSuccessfulCapabilities.isEmpty()) { 497 return presenceTuples; 498 } 499 500 for (ServiceDescription capability : mLastSuccessfulCapabilities) { 501 presenceTuples.add(capability.getTupleBuilder().build()); 502 } 503 return presenceTuples; 504 } 505 506 @VisibleForTesting addLastSuccessfulServiceDescription(ServiceDescription capability)507 public void addLastSuccessfulServiceDescription(ServiceDescription capability) { 508 mLastSuccessfulCapabilities.add(capability); 509 } 510 511 @VisibleForTesting isPresenceCapabilityChanged(Set<ServiceDescription> capableFromReg)512 public boolean isPresenceCapabilityChanged(Set<ServiceDescription> capableFromReg) { 513 if (mLastSuccessfulCapabilities.isEmpty()) { 514 return true; 515 } 516 517 if (capableFromReg.equals(mLastSuccessfulCapabilities)) { 518 return false; 519 } 520 return true; 521 } 522 isVolteAvailable(int networkRegType, MmTelCapabilities capabilities)523 private boolean isVolteAvailable(int networkRegType, MmTelCapabilities capabilities) { 524 return (networkRegType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) 525 && capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 526 } 527 isVoWifiAvailable(int networkRegType, MmTelCapabilities capabilities)528 private boolean isVoWifiAvailable(int networkRegType, MmTelCapabilities capabilities) { 529 return (networkRegType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) 530 && capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 531 } 532 isVtAvailable(int networkRegType, MmTelCapabilities capabilities)533 private boolean isVtAvailable(int networkRegType, MmTelCapabilities capabilities) { 534 return (networkRegType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) 535 && capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 536 } 537 isViWifiAvailable(int networkRegType, MmTelCapabilities capabilities)538 private boolean isViWifiAvailable(int networkRegType, MmTelCapabilities capabilities) { 539 return (networkRegType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) 540 && capabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 541 } 542 isCallComposerAvailable(MmTelCapabilities capabilities)543 private boolean isCallComposerAvailable(MmTelCapabilities capabilities) { 544 return capabilities.isCapable( 545 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER); 546 } 547 548 /** 549 * Get the device's capabilities. 550 */ getDeviceCapabilities( @apabilityMechanism int mechanism, Context context)551 public synchronized RcsContactUceCapability getDeviceCapabilities( 552 @CapabilityMechanism int mechanism, Context context) { 553 switch (mechanism) { 554 case RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE: 555 RcsContactUceCapability rcsContactUceCapability = getPresenceCapabilities(context); 556 if (rcsContactUceCapability != null) { 557 mPendingPublishCapabilities = 558 mServiceCapRegTracker.copyRegistrationCapabilities(); 559 } 560 return rcsContactUceCapability; 561 case RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS: 562 return getOptionsCapabilities(context); 563 default: 564 logw("getDeviceCapabilities: invalid mechanism " + mechanism); 565 return null; 566 } 567 } 568 569 // Get the device's capabilities with the PRESENCE mechanism. getPresenceCapabilities(Context context)570 private RcsContactUceCapability getPresenceCapabilities(Context context) { 571 Uri uri = PublishUtils.getDeviceContactUri(context, mSubId, this, true); 572 if (uri == null) { 573 logw("getPresenceCapabilities: uri is empty"); 574 return null; 575 } 576 Set<ServiceDescription> capableFromReg = 577 mServiceCapRegTracker.copyRegistrationCapabilities(); 578 579 PresenceBuilder presenceBuilder = new PresenceBuilder(uri, 580 RcsContactUceCapability.SOURCE_TYPE_CACHED, 581 RcsContactUceCapability.REQUEST_RESULT_FOUND); 582 // RCS presence tag (added to all presence documents) 583 ServiceDescription presDescription = getCustomizedDescription( 584 ServiceDescription.SERVICE_DESCRIPTION_PRESENCE, capableFromReg); 585 addCapability(presenceBuilder, presDescription.getTupleBuilder(), uri); 586 capableFromReg.remove(presDescription); 587 588 // mmtel 589 ServiceDescription voiceDescription = getCustomizedDescription( 590 ServiceDescription.SERVICE_DESCRIPTION_MMTEL_VOICE, capableFromReg); 591 ServiceDescription vtDescription = getCustomizedDescription( 592 ServiceDescription.SERVICE_DESCRIPTION_MMTEL_VOICE_VIDEO, capableFromReg); 593 ServiceDescription descToUse = (hasVolteCapability() && hasVtCapability()) ? 594 vtDescription : voiceDescription; 595 ServiceCapabilities servCaps = new ServiceCapabilities.Builder( 596 hasVolteCapability(), hasVtCapability()) 597 .addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL).build(); 598 addCapability(presenceBuilder, descToUse.getTupleBuilder() 599 .setServiceCapabilities(servCaps), uri); 600 capableFromReg.remove(voiceDescription); 601 capableFromReg.remove(vtDescription); 602 603 // call composer via mmtel 604 ServiceDescription composerDescription = getCustomizedDescription( 605 ServiceDescription.SERVICE_DESCRIPTION_CALL_COMPOSER_MMTEL, capableFromReg); 606 if (hasCallComposerCapability()) { 607 addCapability(presenceBuilder, composerDescription.getTupleBuilder(), uri); 608 } 609 capableFromReg.remove(composerDescription); 610 611 // External features can only be found using registration states from other components. 612 // Count these features as capable and include in PIDF XML if they are registered. 613 for (ServiceDescription capability : capableFromReg) { 614 addCapability(presenceBuilder, capability.getTupleBuilder(), uri); 615 } 616 617 return presenceBuilder.build(); 618 } 619 620 /** 621 * Search the refSet for the ServiceDescription that matches the service-id && version and 622 * return that or return the reference if there is no match. 623 */ getCustomizedDescription(ServiceDescription reference, Set<ServiceDescription> refSet)624 private ServiceDescription getCustomizedDescription(ServiceDescription reference, 625 Set<ServiceDescription> refSet) { 626 return refSet.stream().filter(s -> s.serviceId.equals(reference.serviceId) 627 && s.version.equals(reference.version)).findFirst().orElse(reference); 628 } 629 630 // Get the device's capabilities with the OPTIONS mechanism. getOptionsCapabilities(Context context)631 private RcsContactUceCapability getOptionsCapabilities(Context context) { 632 Uri uri = PublishUtils.getDeviceContactUri(context, mSubId, this, false); 633 if (uri == null) { 634 logw("getOptionsCapabilities: uri is empty"); 635 return null; 636 } 637 638 Set<String> capableFromReg = mServiceCapRegTracker.copyRegistrationFeatureTags(); 639 640 OptionsBuilder optionsBuilder = new OptionsBuilder(uri, SOURCE_TYPE_CACHED); 641 optionsBuilder.setRequestResult(RcsContactUceCapability.REQUEST_RESULT_FOUND); 642 FeatureTags.addFeatureTags(optionsBuilder, hasVolteCapability(), hasVtCapability(), 643 isPresenceCapable(), hasCallComposerCapability(), capableFromReg); 644 return optionsBuilder.build(); 645 } 646 addCapability(RcsContactUceCapability.PresenceBuilder presenceBuilder, RcsContactPresenceTuple.Builder tupleBuilder, Uri contactUri)647 private void addCapability(RcsContactUceCapability.PresenceBuilder presenceBuilder, 648 RcsContactPresenceTuple.Builder tupleBuilder, Uri contactUri) { 649 presenceBuilder.addCapabilityTuple(tupleBuilder.setContactUri(contactUri).build()); 650 } 651 652 // Check if the device has the VoLTE capability hasVolteCapability()653 private synchronized boolean hasVolteCapability() { 654 return overrideCapability(FeatureTags.FEATURE_TAG_MMTEL, mMmTelCapabilities != null 655 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE)); 656 } 657 658 // Check if the device has the VT capability hasVtCapability()659 private synchronized boolean hasVtCapability() { 660 return overrideCapability(FeatureTags.FEATURE_TAG_VIDEO, mMmTelCapabilities != null 661 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO)); 662 } 663 664 // Check if the device has the Call Composer capability hasCallComposerCapability()665 private synchronized boolean hasCallComposerCapability() { 666 return overrideCapability(FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY, 667 mMmTelCapabilities != null && mMmTelCapabilities.isCapable( 668 MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER)); 669 } 670 671 /** 672 * @return the overridden value for the provided feature tag or the original capability if there 673 * is no override. 674 */ overrideCapability(String featureTag, boolean originalCap)675 private synchronized boolean overrideCapability(String featureTag, boolean originalCap) { 676 if (mOverrideRemoveFeatureTags.contains(featureTag)) { 677 return false; 678 } 679 680 if (mOverrideAddFeatureTags.contains(featureTag)) { 681 return true; 682 } 683 684 return originalCap; 685 } 686 deepCopyCapabilities(MmTelCapabilities capabilities)687 private synchronized MmTelCapabilities deepCopyCapabilities(MmTelCapabilities capabilities) { 688 MmTelCapabilities mmTelCapabilities = new MmTelCapabilities(); 689 if (capabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE)) { 690 mmTelCapabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VOICE); 691 } 692 if (capabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO)) { 693 mmTelCapabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 694 } 695 if (capabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_UT)) { 696 mmTelCapabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_UT); 697 } 698 if (capabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_SMS)) { 699 mmTelCapabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_SMS); 700 } 701 if (capabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER)) { 702 mmTelCapabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER); 703 } 704 return mmTelCapabilities; 705 } 706 logd(String log)707 private void logd(String log) { 708 Log.d(LOG_TAG, getLogPrefix().append(log).toString()); 709 mLocalLog.log("[D] " + log); 710 } 711 logi(String log)712 private void logi(String log) { 713 Log.i(LOG_TAG, getLogPrefix().append(log).toString()); 714 mLocalLog.log("[I] " + log); 715 } 716 logw(String log)717 private void logw(String log) { 718 Log.w(LOG_TAG, getLogPrefix().append(log).toString()); 719 mLocalLog.log("[W] " + log); 720 } 721 getLogPrefix()722 private StringBuilder getLogPrefix() { 723 StringBuilder builder = new StringBuilder("["); 724 builder.append(mSubId); 725 builder.append("] "); 726 return builder; 727 } 728 dump(PrintWriter printWriter)729 public void dump(PrintWriter printWriter) { 730 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 731 pw.println("DeviceCapabilityInfo :"); 732 pw.increaseIndent(); 733 734 mServiceCapRegTracker.dump(pw); 735 736 pw.println("Log:"); 737 pw.increaseIndent(); 738 mLocalLog.dump(pw); 739 pw.decreaseIndent(); 740 741 pw.decreaseIndent(); 742 } 743 } 744