• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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