Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63690320 authored by yongnamcha's avatar yongnamcha
Browse files

Add CallAnomalyWatchdog to Telecom.

We define a transitory call state and intermediate call state.
  - A call stuck in NEW, CONNECTING, DISCONNECTING, or ANSWERED state
    for > 5 seconds;
    these states are considered “transitory” states and the underlying
    connection service that services the call should update the state
    almost immediately.
  - A call stuck in DIALING or RINGING state for > 60 seconds;
    if a call is stuck in a DIALING or RINGING state for longer it is
    likely never going to connect.
    These states are “intermediate” states which we expect a call to be
    in for a longer term than the transitory states.
We have seen cases where calls get stuck in these states; it is the job
of this watchdog to monitor for these types of situations and to
automatically disconnect a call which is stuck in such a state.

This takes place of the mitigation made in b/180154311 to force a call
stuck in CONNECTING state to be disconnected in preference of a new call.
The watchdog will clear these calls after a timeout.
One Pager link : go/telecom-anomaly-detection

Bug: 234468655
Test: Manual - modify test app to behave poorly; verify that the transient
call states get cleared up.
Test: Auto - added unit test coverage for watchdog.

Change-Id: Id94223a8815d5b1e99d19b49a9124accc333e572
parent d1986aba
Loading
Loading
Loading
Loading
+89 −52
Original line number Diff line number Diff line
@@ -117,55 +117,60 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    /**
     * Listener for events on the call.
     */
    @VisibleForTesting
    public interface Listener {
        void onSuccessfulOutgoingCall(Call call, int callState);
        void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
        void onSuccessfulIncomingCall(Call call);
        void onFailedIncomingCall(Call call);
        void onSuccessfulUnknownCall(Call call, int callState);
        void onFailedUnknownCall(Call call);
        void onRingbackRequested(Call call, boolean ringbackRequested);
        void onPostDialWait(Call call, String remaining);
        void onPostDialChar(Call call, char nextChar);
        void onConnectionCapabilitiesChanged(Call call);
        void onConnectionPropertiesChanged(Call call, boolean didRttChange);
        void onParentChanged(Call call);
        void onChildrenChanged(Call call);
        void onCannedSmsResponsesLoaded(Call call);
        void onVideoCallProviderChanged(Call call);
        void onCallerInfoChanged(Call call);
        void onIsVoipAudioModeChanged(Call call);
        void onStatusHintsChanged(Call call);
        void onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName);
        void onExtrasRemoved(Call c, int source, List<String> keys);
        void onHandleChanged(Call call);
        void onCallerDisplayNameChanged(Call call);
        void onCallDirectionChanged(Call call);
        void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
        void onTargetPhoneAccountChanged(Call call);
        void onConnectionManagerPhoneAccountChanged(Call call);
        void onPhoneAccountChanged(Call call);
        void onConferenceableCallsChanged(Call call);
        void onConferenceStateChanged(Call call, boolean isConference);
        void onCdmaConferenceSwap(Call call);
        boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
        void onHoldToneRequested(Call call);
        void onCallHoldFailed(Call call);
        void onCallSwitchFailed(Call call);
        void onConnectionEvent(Call call, String event, Bundle extras);
        void onCallStreamingStateChanged(Call call, boolean isStreaming);
        void onExternalCallChanged(Call call, boolean isExternalCall);
        void onRttInitiationFailure(Call call, int reason);
        void onRemoteRttRequest(Call call, int requestId);
        void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
                                 Bundle extras, boolean isLegacy);
        void onHandoverFailed(Call call, int error);
        void onHandoverComplete(Call call);
        void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report);
        void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue);
        void onReceivedCallQualityReport(Call call, CallQuality callQuality);
        void onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus);
        default void onSuccessfulOutgoingCall(Call call, int callState) {};
        default void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {};
        default void onSuccessfulIncomingCall(Call call) {};
        default void onFailedIncomingCall(Call call) {};
        default void onSuccessfulUnknownCall(Call call, int callState) {};
        default void onFailedUnknownCall(Call call) {};
        default void onRingbackRequested(Call call, boolean ringbackRequested) {};
        default void onPostDialWait(Call call, String remaining) {};
        default void onPostDialChar(Call call, char nextChar) {};
        default void onConnectionCapabilitiesChanged(Call call) {};
        default void onConnectionPropertiesChanged(Call call, boolean didRttChange) {};
        default void onParentChanged(Call call) {};
        default void onChildrenChanged(Call call) {};
        default void onCannedSmsResponsesLoaded(Call call) {};
        default void onVideoCallProviderChanged(Call call) {};
        default void onCallerInfoChanged(Call call) {};
        default void onIsVoipAudioModeChanged(Call call) {};
        default void onStatusHintsChanged(Call call) {};
        default void onExtrasChanged(Call c, int source, Bundle extras,
                String requestingPackageName) {};
        default void onExtrasRemoved(Call c, int source, List<String> keys) {};
        default void onHandleChanged(Call call) {};
        default void onCallerDisplayNameChanged(Call call) {};
        default void onCallDirectionChanged(Call call) {};
        default void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {};
        default void onTargetPhoneAccountChanged(Call call) {};
        default void onConnectionManagerPhoneAccountChanged(Call call) {};
        default void onPhoneAccountChanged(Call call) {};
        default void onConferenceableCallsChanged(Call call) {};
        default void onConferenceStateChanged(Call call, boolean isConference) {};
        default void onCdmaConferenceSwap(Call call) {};
        default boolean onCanceledViaNewOutgoingCallBroadcast(Call call,
                long disconnectionTimeout) {
            return false;
        };
        default void onHoldToneRequested(Call call) {};
        default void onCallHoldFailed(Call call) {};
        default void onCallSwitchFailed(Call call) {};
        default void onConnectionEvent(Call call, String event, Bundle extras) {};
        default void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
        default void onExternalCallChanged(Call call, boolean isExternalCall) {};
        default void onRttInitiationFailure(Call call, int reason) {};
        default void onRemoteRttRequest(Call call, int requestId) {};
        default void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
                Bundle extras, boolean isLegacy)  {};
        default void onHandoverFailed(Call call, int error) {};
        default void onHandoverComplete(Call call)  {};
        default void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {};
        default void onReceivedDeviceToDeviceMessage(Call call, int messageType,
                int messageValue) {};
        default void onReceivedCallQualityReport(Call call, CallQuality callQuality) {};
        default void onCallerNumberVerificationStatusChanged(Call call,
                int callerNumberVerificationStatus) {};
    }

    public abstract static class ListenerBase implements Listener {
@@ -356,6 +361,17 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    /** The state of the call. */
    private int mState;

    /**
     * Determines whether the {@link ConnectionService} has responded to the initial request to
     * create the connection.
     *
     * {@code false} indicates the {@link Call} has been added to Telecom, but the
     * {@link Connection} has not yet been returned by the associated {@link ConnectionService}.
     * {@code true} indicates the {@link Call} has an associated {@link Connection} reported by the
     * {@link ConnectionService}.
     */
    private boolean mIsCreateConnectionComplete = false;

    /** The handle with which to establish this call. */
    private Uri mHandle;

@@ -1053,7 +1069,6 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return (!mIsTransactionalCall ? mConnectionService : mTransactionalService);
    }

    @VisibleForTesting
    public int getState() {
        return mState;
    }
@@ -1515,11 +1530,19 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     * @return {@code true} if this is an outgoing call to emergency services. An outgoing call is
     * identified as an emergency call by the dialer phone number.
     */
    @VisibleForTesting
    public boolean isEmergencyCall() {
        return mIsEmergencyCall;
    }

    /**
     * For testing purposes, set if this call is an emergency call or not.
     * @param isEmergencyCall {@code true} if emergency, {@code false} otherwise.
     */
    @VisibleForTesting
    public void setIsEmergencyCall(boolean isEmergencyCall) {
        mIsEmergencyCall = isEmergencyCall;
    }

    /**
     * @return {@code true} if this an outgoing call to a test emergency number (and NOT to
     * emergency services). Used for testing purposes to differentiate between a real and fake
@@ -2275,6 +2298,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            CallIdMapper idMapper,
            ParcelableConference conference) {
        Log.v(this, "handleCreateConferenceSuccessful %s", conference);
        mIsCreateConnectionComplete = true;
        setTargetPhoneAccount(conference.getPhoneAccount());
        setHandle(conference.getHandle(), conference.getHandlePresentation());

@@ -2308,6 +2332,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            CallIdMapper idMapper,
            ParcelableConnection connection) {
        Log.v(this, "handleCreateConnectionSuccessful %s", connection);
        mIsCreateConnectionComplete = true;
        setTargetPhoneAccount(connection.getPhoneAccount());
        setHandle(connection.getHandle(), connection.getHandlePresentation());

@@ -2454,7 +2479,6 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        disconnect(0);
    }

    @VisibleForTesting
    public void disconnect(String reason) {
        disconnect(0, reason);
    }
@@ -2483,7 +2507,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,

        if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
                mState == CallState.CONNECTING) {
            Log.v(this, "Aborting call %s", this);
            Log.i(this, "disconnect: Aborting call %s", getId());
            abort(disconnectionTimeout);
        } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
            if (mState == CallState.AUDIO_PROCESSING && !hasGoneActiveBefore()) {
@@ -4426,6 +4450,19 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
    }

    /**
     * @return {@code true} if the connection has been created by the underlying
     * {@link ConnectionService}, {@code false} otherwise.
     */
    public boolean isCreateConnectionComplete() {
        return mIsCreateConnectionComplete;
    }

    @VisibleForTesting
    public void setIsCreateConnectionComplete(boolean isCreateConnectionComplete) {
        mIsCreateConnectionComplete = isCreateConnectionComplete;
    }

    public boolean isStreaming() {
        synchronized (mLock) {
            return mIsStreaming;
+375 −0

File added.

Preview size limit exceeded, changes collapsed.

+40 −0
Original line number Diff line number Diff line
@@ -132,6 +132,46 @@ public final class CallState {
     */
    public static final int SIMULATED_RINGING = TelecomProtoEnums.SIMULATED_RINGING; // = 13

    /**
     * Determines if a given call state is "transitory".  A transitory call state is one which a
     * call should only be in for a short duration of time before lower levels move it to a more
     * permanent stable state.
     *
     * It is tempting to consider {@link #DIALING}, for example, to be transitory, however the time
     * spent in this state is entirely dependent on how long the call is actually in that state and
     * it is expected a call can be {@link #DIALING} for potentially a minute or more.
     * @param callState the state to check
     * @return {@code true} if the state is transitory, {@code false} otherwise.
     */
    public static boolean isTransitoryState(int callState) {
        switch (callState) {
            case NEW:
            case CONNECTING:
            case DISCONNECTING:
            case ANSWERED:
                return true;
            default:
                return false;
        }
    }

    /**
     * Determines if a given call state is "intermediate".  An intermediate call state may exist
     * before a stable call state, but may be longer than a transitory state.
     *
     * @param callState the state to check
     * @return {@code true} if the state is intermediate, {@code false} otherwise.
     */
    public static boolean isIntermediateState(int callState) {
        switch (callState) {
            case DIALING:
            case RINGING:
                return true;
            default:
                return false;
        }
    }

    public static String toString(int callState) {
        switch (callState) {
            case NEW:
+34 −2
Original line number Diff line number Diff line
@@ -169,6 +169,13 @@ public class CallsManager extends Call.ListenerBase
    // TODO: Consider renaming this CallsManagerPlugin.
    @VisibleForTesting
    public interface CallsManagerListener {
        /**
         * Informs listeners when a {@link Call} is newly created but not yet added to
         * {@link #mCalls}.  This is where the call has not yet been created by the underlying
         * {@link ConnectionService}.
         * @param call the call.
         */
        default void onCallCreated(Call call) {}
        void onCallAdded(Call call);
        void onCallRemoved(Call call);
        void onCallStateChanged(Call call, int oldState, int newState);
@@ -407,6 +414,7 @@ public class CallsManager extends Call.ListenerBase
    private final EmergencyCallHelper mEmergencyCallHelper;
    private final RoleManagerAdapter mRoleManagerAdapter;
    private final CallEndpointController mCallEndpointController;
    private final CallAnomalyWatchdog mCallAnomalyWatchdog;
    private final CallStreamingController mCallStreamingController;

    private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
@@ -531,7 +539,8 @@ public class CallsManager extends Call.ListenerBase
            CallDiagnosticServiceController callDiagnosticServiceController,
            RoleManagerAdapter roleManagerAdapter,
            ToastFactory toastFactory,
            CallEndpointControllerFactory callEndpointControllerFactory) {
            CallEndpointControllerFactory callEndpointControllerFactory,
            CallAnomalyWatchdog callAnomalyWatchdog) {
        mContext = context;
        mLock = lock;
        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -627,6 +636,7 @@ public class CallsManager extends Call.ListenerBase
        mListeners.add(mHeadsetMediaButton);
        mListeners.add(mProximitySensorManager);
        mListeners.add(audioProcessingNotification);
        mListeners.add(callAnomalyWatchdog);
        mListeners.add(mCallStreamingController);

        // this needs to be after the mCallAudioManager
@@ -644,6 +654,8 @@ public class CallsManager extends Call.ListenerBase
        intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
        context.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_EXPORTED);
        mGraphHandlerThreads = new LinkedList<>();

        mCallAnomalyWatchdog = callAnomalyWatchdog;
    }

    public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -1355,6 +1367,7 @@ public class CallsManager extends Call.ListenerBase
                isConference, /* isConference */
                mClockProxy,
                mToastFactory);
        notifyCallCreated(call);

        // set properties for transactional call
        if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
@@ -1541,6 +1554,8 @@ public class CallsManager extends Call.ListenerBase
                false, /* isConference */
                mClockProxy,
                mToastFactory);
        notifyCallCreated(call);

        call.initAnalytics();

        setIntentExtrasAndStartTime(call, extras);
@@ -1667,6 +1682,8 @@ public class CallsManager extends Call.ListenerBase
            }

            call.initAnalytics(callingPackage, creationLogs.toString());
            // Let listeners know that we just created a new call but haven't added it yet.
            notifyCallCreated(call);

            // Ensure new calls related to self-managed calls/connections are set as such.  This
            // will be overridden when the actual connection is returned in startCreateConnection,
@@ -3837,6 +3854,7 @@ public class CallsManager extends Call.ListenerBase
                connectElapsedTime,
                mClockProxy,
                mToastFactory);
        notifyCallCreated(call);

        setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
                "new conference call");
@@ -5167,6 +5185,12 @@ public class CallsManager extends Call.ListenerBase
            pw.decreaseIndent();
        }

        if (mCallAnomalyWatchdog != null) {
            pw.println("mCallAnomalyWatchdog:");
            pw.increaseIndent();
            mCallAnomalyWatchdog.dump(pw);
            pw.decreaseIndent();
        }
        if (mDefaultDialerCache != null) {
            pw.println("mDefaultDialerCache:");
            pw.increaseIndent();
@@ -5298,6 +5322,14 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    /**
     * Notify interested parties that a new call has been created, but not yet added to
     * CallsManager.
     * @param theCall the new call.
     */
    private void notifyCallCreated(final Call theCall) {
        mListeners.forEach(l -> l.onCallCreated(theCall));
    }

    /**
     * Notifies the {@link android.telecom.ConnectionService} associated with a
+1 −0
Original line number Diff line number Diff line
@@ -212,6 +212,7 @@ public class LogUtils {
                "CALL_DIAGNOSTIC_SERVICE_TIMEOUT";
        public static final String VERSTAT_CHANGED = "VERSTAT_CHANGED";
        public static final String SET_VOIP_MODE = "SET_VOIP_MODE";
        public static final String STATE_TIMEOUT = "STATE_TIMEOUT";
        public static final String ICS_EXTRAS_CHANGED = "ICS_EXTRAS_CHANGED";

        public static class Timings {
Loading