1 /* 2 * Copyright (C) 2021 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.services.telephony.rcs; 18 19 import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING; 20 import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING; 21 22 import android.telephony.ims.DelegateRegistrationState; 23 import android.telephony.ims.FeatureTagState; 24 import android.telephony.ims.SipDelegateConfiguration; 25 import android.telephony.ims.SipDelegateImsConfiguration; 26 import android.telephony.ims.SipDelegateManager; 27 import android.telephony.ims.SipMessage; 28 import android.util.LocalLog; 29 import android.util.Log; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.telephony.SipMessageParsingUtils; 33 import com.android.internal.telephony.metrics.RcsStats; 34 import com.android.internal.util.IndentingPrintWriter; 35 import com.android.services.telephony.rcs.validator.IncomingTransportStateValidator; 36 import com.android.services.telephony.rcs.validator.MalformedSipMessageValidator; 37 import com.android.services.telephony.rcs.validator.OutgoingTransportStateValidator; 38 import com.android.services.telephony.rcs.validator.RestrictedOutgoingSipRequestValidator; 39 import com.android.services.telephony.rcs.validator.RestrictedOutgoingSubscribeValidator; 40 import com.android.services.telephony.rcs.validator.SipMessageValidator; 41 import com.android.services.telephony.rcs.validator.ValidationResult; 42 43 import java.io.PrintWriter; 44 import java.util.Collections; 45 import java.util.Set; 46 import java.util.concurrent.ScheduledExecutorService; 47 import java.util.concurrent.ScheduledFuture; 48 import java.util.concurrent.TimeUnit; 49 import java.util.function.Consumer; 50 import java.util.stream.Collectors; 51 import java.util.stream.Stream; 52 53 /** 54 * Track incoming and outgoing SIP messages passing through this delegate and verify these messages 55 * by doing the following: 56 * <ul> 57 * <li>Track the SipDelegate's registration state to ensure that a registration event has 58 * occurred before allowing outgoing messages. Once it has occurred, filter outgoing SIP messages 59 * based on the open/restricted feature tag registration state.</li> 60 * <li>Track the SipDelegate's IMS configuration version and deny any outgoing SipMessages 61 * associated with a stale IMS configuration version.</li> 62 * <li>Track the SipDelegate open/close state to allow/deny outgoing messages based on the 63 * session's state.</li> 64 * <li>Validate outgoing SIP messages for both restricted request methods as well as restricted/ 65 * malformed headers.</li> 66 * </ul> 67 */ 68 public class TransportSipMessageValidator { 69 70 private static final String LOG_TAG = "SipMessageV"; 71 72 /** 73 * the time in milliseconds that we will wait for SIP sessions to close before we will timeout 74 * and force the sessions to be cleaned up. 75 */ 76 private static final int PENDING_CLOSE_TIMEOUT_MS = 1000; 77 /** 78 * time in milliseconds that we will wait for SIP sessions to be closed before we timeout and 79 * force the sessions associated with the deregistering feature tags to be cleaned up. 80 */ 81 private static final int PENDING_REGISTRATION_CHANGE_TIMEOUT_MS = 1000; 82 83 /** 84 * Timeouts used in this class that are visible for testing. 85 */ 86 @VisibleForTesting 87 public interface Timeouts { 88 /** 89 * @return the time in milliseconds that we will wait for SIP sessions to close before we 90 * will timeout and force the sessions to be cleaned up. 91 */ getPendingCloseTimeoutMs()92 int getPendingCloseTimeoutMs(); 93 94 /** 95 * @return the time in milliseconds that we will wait for SIP sessions to be closed before 96 * we timeout and force the sessions associated with the deregistering feature tags to be 97 * cleaned up. 98 */ getPendingRegistrationChangeTimeoutMs()99 int getPendingRegistrationChangeTimeoutMs(); 100 } 101 102 /** 103 * Tracks a pending task that has been scheduled on the associated Executor. 104 */ 105 private abstract static class PendingTask implements Runnable { 106 107 private ScheduledFuture<?> mFuture; 108 scheduleDelayed(ScheduledExecutorService executor, int timeMs)109 public void scheduleDelayed(ScheduledExecutorService executor, int timeMs) { 110 mFuture = executor.schedule(this, timeMs, TimeUnit.MILLISECONDS); 111 } 112 isDone()113 public boolean isDone() { 114 return mFuture != null && mFuture.isDone(); 115 } 116 cancel()117 public void cancel() { 118 if (mFuture == null) return; 119 mFuture.cancel(false /*interrupt*/); 120 } 121 } 122 123 /** 124 * Tracks a pending reg cleanup task that has been scheduled on the associated Executor. 125 */ 126 private abstract static class PendingRegCleanupTask extends PendingTask { 127 public final Set<String> pendingCallIds; 128 public final Set<String> featureTags; 129 PendingRegCleanupTask(Set<String> tags, Set<String> callIds)130 PendingRegCleanupTask(Set<String> tags, Set<String> callIds) { 131 featureTags = tags; 132 pendingCallIds = callIds; 133 } 134 } 135 136 private final int mSubId; 137 private final ScheduledExecutorService mExecutor; 138 private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE); 139 private final SipSessionTracker mSipSessionTracker; 140 // Validators 141 private final IncomingTransportStateValidator mIncomingTransportStateValidator; 142 private final OutgoingTransportStateValidator mOutgoingTransportStateValidator; 143 private final SipMessageValidator mOutgoingMessageValidator; 144 private final SipMessageValidator mIncomingMessageValidator; 145 146 private Set<String> mSupportedFeatureTags; 147 private Set<FeatureTagState> mDeniedFeatureTags; 148 private long mConfigVersion = -1; 149 private Consumer<Set<String>> mClosingCompleteConsumer; 150 private PendingTask mPendingClose; 151 private PendingRegCleanupTask mPendingRegCleanup; 152 private Consumer<Set<String>> mRegistrationAppliedConsumer; 153 private final RcsStats mRcsStats; 154 TransportSipMessageValidator(int subId, ScheduledExecutorService executor)155 public TransportSipMessageValidator(int subId, ScheduledExecutorService executor) { 156 mSubId = subId; 157 mExecutor = executor; 158 mRcsStats = RcsStats.getInstance(); 159 mSipSessionTracker = new SipSessionTracker(subId, mRcsStats); 160 mOutgoingTransportStateValidator = new OutgoingTransportStateValidator(mSipSessionTracker); 161 mIncomingTransportStateValidator = new IncomingTransportStateValidator(); 162 mOutgoingMessageValidator = new MalformedSipMessageValidator().andThen( 163 new RestrictedOutgoingSipRequestValidator()).andThen( 164 new RestrictedOutgoingSubscribeValidator()).andThen( 165 mOutgoingTransportStateValidator); 166 mIncomingMessageValidator = mIncomingTransportStateValidator; 167 } 168 169 @VisibleForTesting TransportSipMessageValidator(int subId, ScheduledExecutorService executor, SipSessionTracker sipSessionTracker, OutgoingTransportStateValidator outgoingStateValidator, IncomingTransportStateValidator incomingStateValidator, RcsStats rcsStats)170 public TransportSipMessageValidator(int subId, ScheduledExecutorService executor, 171 SipSessionTracker sipSessionTracker, 172 OutgoingTransportStateValidator outgoingStateValidator, 173 IncomingTransportStateValidator incomingStateValidator, RcsStats rcsStats) { 174 mSubId = subId; 175 mExecutor = executor; 176 mSipSessionTracker = sipSessionTracker; 177 mOutgoingTransportStateValidator = outgoingStateValidator; 178 mIncomingTransportStateValidator = incomingStateValidator; 179 mOutgoingMessageValidator = mOutgoingTransportStateValidator; 180 mIncomingMessageValidator = mIncomingTransportStateValidator; 181 mRcsStats = rcsStats; 182 } 183 184 /** 185 * Notify this tracker that a registration state change has occurred. 186 * <p> 187 * In some scenarios, this will require that existing SIP dialogs are closed (for example, when 188 * moving a feature tag from REGISTERED->DEREGISTERING). This method allows the caller to 189 * provide a Consumer that will be called when either there are no SIP dialogs active on 190 * DEREGISTERING feature tags, or a timeout has occurred. In the case that a timeout has 191 * occurred, this Consumer will accept a list of callIds that will be manually closed by the 192 * framework to unblock the IMS stack. 193 * <p> 194 * @param stateChangeComplete A one time Consumer that when completed, will contain a List of 195 * callIds corresponding to SIP Dialogs that have not been closed yet. It is the callers 196 * responsibility to close the dialogs associated with the provided callIds. If another 197 * state update occurs before the previous was completed, the previous consumer will be 198 * completed with an empty list and the new Consumer will be executed when the new state 199 * changes. 200 * @param regState The new registration state. 201 */ onRegistrationStateChanged(Consumer<Set<String>> stateChangeComplete, DelegateRegistrationState regState)202 public void onRegistrationStateChanged(Consumer<Set<String>> stateChangeComplete, 203 DelegateRegistrationState regState) { 204 if (mRegistrationAppliedConsumer != null) { 205 logw("onRegistrationStateChanged: pending registration change, completing now."); 206 // complete the pending consumer with no dialogs pending, this will be re-evaluated 207 // and new configuration will be applied. 208 cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet()); 209 } 210 Set<String> restrictedTags = Stream.concat( 211 regState.getDeregisteringFeatureTags().stream(), 212 regState.getDeregisteredFeatureTags().stream()).map(FeatureTagState::getFeatureTag) 213 .collect(Collectors.toSet()); 214 mOutgoingTransportStateValidator.restrictFeatureTags(restrictedTags); 215 mRegistrationAppliedConsumer = stateChangeComplete; 216 if (mPendingClose == null || mPendingClose.isDone()) { 217 // Only update the pending registration cleanup task if we do not already have a pending 218 // close in progress. 219 updatePendingRegCleanupTask(restrictedTags); 220 } else { 221 logi("skipping update reg cleanup due to pending close task."); 222 } 223 } 224 225 /** 226 * Notify this tracker that the IMS configuration has changed. 227 * 228 * Parameters contained in the IMS configuration will be used to validate outgoing messages, 229 * such as the configuration version. 230 * @param c The newest IMS configuration. 231 */ onImsConfigurationChanged(SipDelegateImsConfiguration c)232 public void onImsConfigurationChanged(SipDelegateImsConfiguration c) { 233 if (c.getVersion() == mConfigVersion) { 234 return; 235 } 236 logi("onImsConfigurationChanged: " + mConfigVersion + "->" + c.getVersion()); 237 mConfigVersion = c.getVersion(); 238 } 239 240 /** 241 * Notify this tracker that the IMS configuration has changed. 242 * 243 * Parameters contained in the IMS configuration will be used to validate outgoing messages, 244 * such as the configuration version. 245 * @param c The newest IMS configuration. 246 */ onConfigurationChanged(SipDelegateConfiguration c)247 public void onConfigurationChanged(SipDelegateConfiguration c) { 248 if (c.getVersion() == mConfigVersion) { 249 return; 250 } 251 logi("onConfigurationChanged: " + mConfigVersion + "->" + c.getVersion()); 252 mConfigVersion = c.getVersion(); 253 } 254 255 /** 256 * A new message transport has been opened to a SipDelegate. 257 * <p> 258 * Initializes this tracker and resets any state required to process messages. 259 * @param supportedFeatureTags feature tags that are supported and should pass message 260 * verification. 261 * @param deniedFeatureTags feature tags that were denied and should fail message verification. 262 */ onTransportOpened(Set<String> supportedFeatureTags, Set<FeatureTagState> deniedFeatureTags)263 public void onTransportOpened(Set<String> supportedFeatureTags, 264 Set<FeatureTagState> deniedFeatureTags) { 265 logi("onTransportOpened: moving to open state"); 266 mSupportedFeatureTags = supportedFeatureTags; 267 mDeniedFeatureTags = deniedFeatureTags; 268 mOutgoingTransportStateValidator.open(supportedFeatureTags, deniedFeatureTags.stream().map( 269 FeatureTagState::getFeatureTag).collect(Collectors.toSet())); 270 mIncomingTransportStateValidator.open(); 271 } 272 273 /** 274 * A SIP session has been cleaned up and should no longer be tracked. 275 * @param callId The call ID associated with the SIP session. 276 */ onSipSessionCleanup(String callId)277 public void onSipSessionCleanup(String callId) { 278 mSipSessionTracker.cleanupSession(callId); 279 onCallIdsChanged(); 280 } 281 282 /** 283 * Move this tracker into a restricted state, where only outgoing SIP messages associated with 284 * an ongoing SIP Session may be sent. Any out-of-dialog outgoing SIP messages will be denied. 285 * This does not affect incoming SIP messages (for example, an incoming SIP INVITE). 286 * <p> 287 * This tracker will stay in this state until either all open SIP Sessions are closed by the 288 * remote application, or a timeout occurs. Once this happens, the provided Consumer will accept 289 * a List of call IDs associated with the open SIP Sessions that did not close before the 290 * timeout. The caller must then close all open SIP Sessions before closing the transport. 291 * @param closingCompleteConsumer A Consumer that will be called when the transport can be 292 * closed and may contain a list of callIds associated with SIP sessions that have not 293 * been closed. 294 * @param closingReason The reason that will be provided if an outgoing out-of-dialog SIP 295 * message is sent while the transport is closing. 296 * @param closedReason The reason that will be provided if any outgoing SIP message is sent 297 * once the transport is closed. 298 */ closeSessionsGracefully(Consumer<Set<String>> closingCompleteConsumer, int closingReason, int closedReason)299 public void closeSessionsGracefully(Consumer<Set<String>> closingCompleteConsumer, 300 int closingReason, int closedReason) { 301 if (closingCompleteConsumer == null) { 302 logw("closeSessionsGracefully: unexpected - called with null consumer... closing now"); 303 closeSessions(closedReason); 304 return; 305 } 306 if (mClosingCompleteConsumer != null) { 307 // In this case, all we can do is combine the consumers and wait for the other pending 308 // close to complete, finishing both. 309 logw("closeSessionsGracefully: unexpected - existing close pending, combining" 310 + " consumers."); 311 mClosingCompleteConsumer = callIds -> { 312 mClosingCompleteConsumer.accept(callIds); 313 closingCompleteConsumer.accept(callIds); 314 }; 315 return; 316 } else { 317 mClosingCompleteConsumer = closingCompleteConsumer; 318 } 319 if (getTrackedSipSessionCallIds().isEmpty()) { 320 logi("closeSessionsGracefully: moving to closed state now, reason=" + closedReason); 321 closeSessionsInternal(closedReason); 322 cancelClosingTimeoutAndSendComplete(Collections.emptySet()); 323 return; 324 } 325 cancelPendingRegCleanupTask(); 326 logi("closeSessionsGracefully: moving to restricted state, reason=" + closingReason); 327 mOutgoingTransportStateValidator.restrict(closingReason); 328 mPendingClose = new PendingTask() { 329 @Override 330 public void run() { 331 closeSessions(closingReason); 332 } 333 }; 334 mPendingClose.scheduleDelayed(mExecutor, PENDING_CLOSE_TIMEOUT_MS); 335 } 336 337 /** 338 * Close the transport now. If there are any open SIP sessions and this is closed due to a 339 * configuration change (SIM subscription change, user disabled RCS, the service is dead, 340 * etc...) then we will return the call IDs of all open sessions and ask them to be closed. 341 * @param closedReason The error reason for why the message transport was closed that will be 342 * sent back to the caller if a new SIP message is sent. 343 * @return A List of call IDs associated with sessions that were still open at the time that the 344 * tracker closed the transport. 345 */ closeSessions(int closedReason)346 public Set<String> closeSessions(int closedReason) { 347 Set<String> openCallIds = getTrackedSipSessionCallIds(); 348 logi("closeSessions: moving to closed state, reason=" + closedReason + ", open call ids: " 349 + openCallIds); 350 closeSessionsInternal(closedReason); 351 boolean consumerHandledPendingSessions = cancelClosingTimeoutAndSendComplete(openCallIds); 352 if (consumerHandledPendingSessions) { 353 logw("closeSessions: call ID closure handled through consumer"); 354 // sent the open call IDs through the pending complete mechanism to unblock any previous 355 // graceful close command and close them early. 356 return Collections.emptySet(); 357 } 358 return openCallIds; 359 } 360 361 /** 362 * Verify a new outgoing SIP message before sending to the SipDelegate (ImsService). 363 * @param message The SIP message being verified 364 * @return The result of verifying the outgoing message. 365 */ 366 verifyOutgoingMessage(SipMessage message, long configVersion)367 public ValidationResult verifyOutgoingMessage(SipMessage message, long configVersion) { 368 if (mConfigVersion != configVersion) { 369 return new ValidationResult( 370 SipDelegateManager.MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION, 371 "stale IMS configuration: " + configVersion + ", expected: " 372 + mConfigVersion); 373 } 374 ValidationResult result = mOutgoingMessageValidator.validate(message); 375 logi("verifyOutgoingMessage: " + result + ", message=" + message); 376 if (result.isValidated) { 377 mSipSessionTracker.filterSipMessage( 378 SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, message); 379 } 380 updateForMetrics(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__OUTGOING, message, result); 381 return result; 382 } 383 384 /** 385 * Verify a new incoming SIP message before sending it to the 386 * DelegateConnectionMessageCallback (remote application). 387 * @param message The SipMessage to verify. 388 * @return The result of verifying the incoming message. 389 */ verifyIncomingMessage(SipMessage message)390 public ValidationResult verifyIncomingMessage(SipMessage message) { 391 ValidationResult result = mIncomingMessageValidator.validate(message); 392 logi("verifyIncomingMessage: " + result + ", message=" + message); 393 if (result.isValidated) { 394 mSipSessionTracker.filterSipMessage( 395 SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, message); 396 } 397 updateForMetrics(SIP_TRANSPORT_SESSION__SIP_MESSAGE_DIRECTION__INCOMING, message, result); 398 return result; 399 } 400 401 /** 402 * Acknowledge that a pending incoming or outgoing SIP message has been delivered successfully 403 * to the remote. 404 * @param transactionId The transaction ID associated with the message. 405 */ acknowledgePendingMessage(String transactionId)406 public void acknowledgePendingMessage(String transactionId) { 407 logi("acknowledgePendingMessage: id=" + transactionId); 408 mSipSessionTracker.acknowledgePendingMessage(transactionId); 409 onCallIdsChanged(); 410 } 411 412 /** 413 * A pending incoming or outgoing SIP message has failed and should not be tracked. 414 * @param transactionId The transaction ID associated with the message. 415 */ notifyPendingMessageFailed(String transactionId)416 public void notifyPendingMessageFailed(String transactionId) { 417 logi("notifyPendingMessageFailed: id=" + transactionId); 418 mSipSessionTracker.pendingMessageFailed(transactionId); 419 } 420 421 /** Dump state about this tracker that should be included in the dumpsys */ dump(PrintWriter printWriter)422 public void dump(PrintWriter printWriter) { 423 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 424 pw.println("Supported Tags:" + mSupportedFeatureTags); 425 pw.println("Denied Tags:" + mDeniedFeatureTags); 426 pw.println(mOutgoingTransportStateValidator); 427 pw.println(mIncomingTransportStateValidator); 428 pw.println("Reg consumer pending: " + (mRegistrationAppliedConsumer != null)); 429 pw.println("Close consumer pending: " + (mClosingCompleteConsumer != null)); 430 pw.println(); 431 mSipSessionTracker.dump(pw); 432 pw.println(); 433 pw.println("Most recent logs:"); 434 mLocalLog.dump(printWriter); 435 } 436 437 /** 438 * A event has occurred that can change the list of active call IDs. 439 */ onCallIdsChanged()440 private void onCallIdsChanged() { 441 if (getTrackedSipSessionCallIds().isEmpty() && mPendingClose != null 442 && !mPendingClose.isDone()) { 443 logi("onCallIdsChanged: no open sessions, completing any pending close events."); 444 // do not wait for timeout if pending sessions closed. 445 mPendingClose.cancel(); 446 mPendingClose.run(); 447 } 448 if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) { 449 logi("onCallIdsChanged: updating pending reg cleanup task."); 450 // Recalculate the open call IDs based on the same feature tag set in the case that the 451 // call ID change has caused a change in pending reg cleanup task. 452 updatePendingRegCleanupTask(mPendingRegCleanup.featureTags); 453 } 454 } 455 456 /** 457 * If there are any pending registration clean up tasks, cancel them and clean up consumers. 458 */ cancelPendingRegCleanupTask()459 private void cancelPendingRegCleanupTask() { 460 if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) { 461 logi("cancelPendingRegCleanupTask: cancelling..."); 462 mPendingRegCleanup.cancel(); 463 } 464 cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet()); 465 } 466 467 /** 468 * Update the pending registration change clean up task based on the new set of restricted 469 * feature tags generated from the deregistering/deregistered feature tags. 470 * 471 * <p> 472 * This set of restricted tags will generate a set of call IDs associated to dialogs that 473 * are active and associated with the restricted tags. If there is no pending cleanup task, it 474 * will create a new one. If there was already a pending reg cleanup task, it will compare them 475 * and create a new one and cancel the old one if the new set of call ids is different from the 476 * old one. 477 */ updatePendingRegCleanupTask(Set<String> restrictedTags)478 private void updatePendingRegCleanupTask(Set<String> restrictedTags) { 479 Set<String> pendingCallIds = mSipSessionTracker.getCallIdsAssociatedWithFeatureTag( 480 restrictedTags); 481 if (pendingCallIds.isEmpty()) { 482 if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) { 483 logi("updatePendingRegCleanupTask: no remaining call ids, finish cleanup task " 484 + "now."); 485 mPendingRegCleanup.cancel(); 486 mPendingRegCleanup.run(); 487 } else { 488 if (mRegistrationAppliedConsumer != null) { 489 logi("updatePendingRegCleanupTask: notify no pending call ids."); 490 cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet()); 491 } 492 } 493 return; 494 } 495 if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) { 496 if (mPendingRegCleanup.pendingCallIds.equals(pendingCallIds)) { 497 logi("updatePendingRegCleanupTask: pending reg change has same set of pending call" 498 + " IDs, so keeping pending task"); 499 return; 500 } 501 logi("updatePendingRegCleanupTask: cancelling, call ids have changed."); 502 mPendingRegCleanup.cancel(); 503 } 504 mPendingRegCleanup = new PendingRegCleanupTask(restrictedTags, pendingCallIds) { 505 @Override 506 public void run() { 507 cleanupAndNotifyRegistrationAppliedConsumer(pendingCallIds); 508 } 509 }; 510 logi("updatePendingRegCleanupTask: scheduling for call ids: " + pendingCallIds); 511 mPendingRegCleanup.scheduleDelayed(mExecutor, PENDING_REGISTRATION_CHANGE_TIMEOUT_MS); 512 } 513 514 /** 515 * Notify the pending registration applied consumer of the call ids that need to be cleaned up. 516 */ cleanupAndNotifyRegistrationAppliedConsumer(Set<String> pendingCallIds)517 private void cleanupAndNotifyRegistrationAppliedConsumer(Set<String> pendingCallIds) { 518 if (mRegistrationAppliedConsumer != null) { 519 mRegistrationAppliedConsumer.accept(pendingCallIds); 520 mRegistrationAppliedConsumer = null; 521 } 522 } 523 524 /** 525 * Cancel any pending timeout to close pending sessions and send the provided call IDs to any 526 * pending closing complete consumer. 527 * @return {@code true} if a consumer was notified, {@code false} if there were no consumers. 528 */ cancelClosingTimeoutAndSendComplete(Set<String> openCallIds)529 private boolean cancelClosingTimeoutAndSendComplete(Set<String> openCallIds) { 530 if (mPendingClose != null && !mPendingClose.isDone()) { 531 logi("completing pending close consumer"); 532 mPendingClose.cancel(); 533 } 534 // Complete the pending consumer with no open sessions. 535 if (mClosingCompleteConsumer != null) { 536 mClosingCompleteConsumer.accept(openCallIds); 537 mClosingCompleteConsumer = null; 538 return true; 539 } 540 return false; 541 } 542 543 /** 544 * Close and clear all stateful trackers and validators. 545 */ closeSessionsInternal(int closedReason)546 private void closeSessionsInternal(int closedReason) { 547 cancelPendingRegCleanupTask(); 548 mOutgoingTransportStateValidator.close(closedReason); 549 mIncomingTransportStateValidator.close(closedReason); 550 mSipSessionTracker.clearAllSessions(); 551 } 552 getTrackedSipSessionCallIds()553 private Set<String> getTrackedSipSessionCallIds() { 554 return mSipSessionTracker.getTrackedDialogs().stream().map(SipDialog::getCallId) 555 .collect(Collectors.toSet()); 556 } 557 updateForMetrics(int direction, SipMessage m, ValidationResult result)558 private void updateForMetrics(int direction, SipMessage m, ValidationResult result) { 559 String[] startLineSegments = SipMessageParsingUtils 560 .splitStartLineAndVerify(m.getStartLine()); 561 if (SipMessageParsingUtils.isSipRequest(m.getStartLine())) { 562 if (result.isValidated) { 563 // SipMessage add to list for Metrics stats 564 mRcsStats.onSipMessageRequest(m.getCallIdParameter(), startLineSegments[0], 565 direction); 566 } else { 567 //Message sending fail and there is no response. 568 mRcsStats.invalidatedMessageResult(m.getCallIdParameter(), mSubId, 569 startLineSegments[0], direction, result.restrictedReason); 570 } 571 } else if (SipMessageParsingUtils.isSipResponse(m.getStartLine())) { 572 int statusCode = Integer.parseInt(startLineSegments[1]); 573 mRcsStats.onSipMessageResponse(mSubId, m.getCallIdParameter(), statusCode, 574 result.restrictedReason); 575 } else { 576 logw("Message is Restricted"); 577 } 578 } 579 580 /** 581 * This is a listener to handle SipDialog state of delegate 582 * @param listener {@link SipDialogsStateListener} 583 * @param isNeedNotify It indicates whether the current dialogs state should be notified. 584 */ setSipDialogsListener(SipDialogsStateListener listener, boolean isNeedNotify)585 public void setSipDialogsListener(SipDialogsStateListener listener, 586 boolean isNeedNotify) { 587 mSipSessionTracker.setSipDialogsListener(listener, isNeedNotify); 588 } 589 logi(String log)590 private void logi(String log) { 591 Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log); 592 mLocalLog.log("[I] " + log); 593 } 594 logw(String log)595 private void logw(String log) { 596 Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log); 597 mLocalLog.log("[W] " + log); 598 } 599 } 600