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