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

Commit 303a2996 authored by Grace Jia's avatar Grace Jia Committed by Android (Google) Code Review
Browse files

Merge "Implement call streaming related APIs."

parents 9cae670a f0e883f1
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        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);
@@ -243,6 +244,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        @Override
        public void onConnectionEvent(Call call, String event, Bundle extras) {}
        @Override
        public void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
        @Override
        public void onExternalCallChanged(Call call, boolean isExternalCall) {}
        @Override
        public void onRttInitiationFailure(Call call, int reason) {}
@@ -534,6 +537,11 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,

    private boolean mIsTransactionalCall = false;

    /**
     * Indicates whether this call is streaming.
     */
    private boolean mIsStreaming = false;

    /**
     * Indicates whether the {@link PhoneAccount} associated with an self-managed call want to
     * expose the call to an {@link android.telecom.InCallService} which declares the metadata
@@ -4390,4 +4398,43 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            mDisconnectFuture = null;
        }
    }

    public boolean isStreaming() {
        synchronized (mLock) {
            return mIsStreaming;
        }
    }

    public void startStreaming() {
        if (!mIsTransactionalCall) {
            throw new UnsupportedOperationException(
                    "Can't streaming call created by non voip apps");
        }

        synchronized (mLock) {
            if (mIsStreaming) {
                // ignore
                return;
            }

            mIsStreaming = true;
            for (Listener listener : mListeners) {
                listener.onCallStreamingStateChanged(this, true /** isStreaming */);
            }
        }
    }

    public void stopStreaming() {
        synchronized (mLock) {
            if (!mIsStreaming) {
                // ignore
                return;
            }

            mIsStreaming = false;
            for (Listener listener : mListeners) {
                listener.onCallStreamingStateChanged(this, false /** isStreaming */);
            }
        }
    }
}
+33 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ public class CallAudioManager extends CallsManagerListenerBase {
    private final RingbackPlayer mRingbackPlayer;
    private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;

    private Call mStreamingCall;
    private Call mForegroundCall;
    private boolean mIsTonePlaying = false;
    private boolean mIsDisconnectedTonePlaying = false;
@@ -78,6 +79,7 @@ public class CallAudioManager extends CallsManagerListenerBase {
        mRingingCalls = new LinkedHashSet<>(1);
        mHoldingCalls = new LinkedHashSet<>(1);
        mAudioProcessingCalls = new LinkedHashSet<>(1);
        mStreamingCall = null;
        mCalls = new HashSet<>();
        mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{
            put(CallState.CONNECTING, mActiveDialingOrConnectingCalls);
@@ -221,6 +223,36 @@ public class CallAudioManager extends CallsManagerListenerBase {
        }
    }

    /**
     * Handles the changes to the streaming state of a call.
     * @param call The call
     * @param isStreaming {@code true} if the call is streaming, {@code false} otherwise
     */
    @Override
    public void onCallStreamingStateChanged(Call call, boolean isStreaming) {
        if (isStreaming) {
            if (mStreamingCall == null) {
                mStreamingCall = call;
                mCallAudioModeStateMachine.sendMessageWithArgs(
                        CallAudioModeStateMachine.START_CALL_STREAMING,
                        makeArgsForModeStateMachine());
            } else {
                Log.w(LOG_TAG, "Unexpected streaming call request for call %s while call "
                        + "s is streaming.", call.getId(), mStreamingCall.getId());
            }
        } else {
            if (mStreamingCall == call) {
                mStreamingCall = null;
                mCallAudioModeStateMachine.sendMessageWithArgs(
                        CallAudioModeStateMachine.STOP_CALL_STREAMING,
                        makeArgsForModeStateMachine());
            } else {
                Log.w(LOG_TAG, "Unexpected call streaming stop request for call %s while this call "
                        + "is not streaming.", call.getId());
            }
        }
    }

    /**
     * Determines if {@link CallAudioManager} should do any audio routing operations for a call.
     * We ignore child calls of a conference and external calls for audio routing purposes.
@@ -759,6 +791,7 @@ public class CallAudioManager extends CallsManagerListenerBase {
                .setHasHoldingCalls(mHoldingCalls.size() > 0)
                .setHasAudioProcessingCalls(mAudioProcessingCalls.size() > 0)
                .setIsTonePlaying(mIsTonePlaying)
                .setIsStreaming(mStreamingCall != null)
                .setForegroundCallIsVoip(
                        mForegroundCall != null && isCallVoip(mForegroundCall))
                .setSession(Log.createSubsession()).build();
+114 −9
Original line number Diff line number Diff line
@@ -50,17 +50,19 @@ public class CallAudioModeStateMachine extends StateMachine {
        public boolean hasAudioProcessingCalls;
        public boolean isTonePlaying;
        public boolean foregroundCallIsVoip;
        public boolean isStreaming;
        public Session session;

        private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
                boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying,
                boolean foregroundCallIsVoip, Session session) {
                boolean foregroundCallIsVoip, boolean isStreaming, Session session) {
            this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
            this.hasRingingCalls = hasRingingCalls;
            this.hasHoldingCalls = hasHoldingCalls;
            this.hasAudioProcessingCalls = hasAudioProcessingCalls;
            this.isTonePlaying = isTonePlaying;
            this.foregroundCallIsVoip = foregroundCallIsVoip;
            this.isStreaming = isStreaming;
            this.session = session;
        }

@@ -73,6 +75,7 @@ public class CallAudioModeStateMachine extends StateMachine {
                    ", hasAudioProcessingCalls=" + hasAudioProcessingCalls +
                    ", isTonePlaying=" + isTonePlaying +
                    ", foregroundCallIsVoip=" + foregroundCallIsVoip +
                    ", isStreaming=" + isStreaming +
                    ", session=" + session +
                    '}';
        }
@@ -84,6 +87,7 @@ public class CallAudioModeStateMachine extends StateMachine {
            private boolean mHasAudioProcessingCalls;
            private boolean mIsTonePlaying;
            private boolean mForegroundCallIsVoip;
            private boolean mIsStreaming;
            private Session mSession;

            public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) {
@@ -121,9 +125,15 @@ public class CallAudioModeStateMachine extends StateMachine {
                return this;
            }

            public Builder setIsStreaming(boolean isStraeming) {
                mIsStreaming = isStraeming;
                return this;
            }

            public MessageArgs build() {
                return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls,
                        mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, mSession);
                        mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip,
                        mIsStreaming, mSession);
            }
        }
    }
@@ -138,7 +148,8 @@ public class CallAudioModeStateMachine extends StateMachine {
    public static final int ENTER_RING_FOCUS_FOR_TESTING = 4;
    public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5;
    public static final int ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING = 6;
    public static final int ABANDON_FOCUS_FOR_TESTING = 7;
    public static final int ENTER_STREAMING_FOCUS_FOR_TESTING = 7;
    public static final int ABANDON_FOCUS_FOR_TESTING = 8;

    public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001;
    public static final int NO_MORE_RINGING_CALLS = 1002;
@@ -161,6 +172,9 @@ public class CallAudioModeStateMachine extends StateMachine {
    // to release focus for other apps to take over.
    public static final int AUDIO_OPERATIONS_COMPLETE = 6001;

    public static final int START_CALL_STREAMING = 7001;
    public static final int STOP_CALL_STREAMING = 7002;

    public static final int RUN_RUNNABLE = 9001;

    private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
@@ -183,6 +197,8 @@ public class CallAudioModeStateMachine extends StateMachine {
        put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
        put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
        put(AUDIO_OPERATIONS_COMPLETE, "AUDIO_OPERATIONS_COMPLETE");
        put(START_CALL_STREAMING, "START_CALL_STREAMING");
        put(STOP_CALL_STREAMING, "STOP_CALL_STREAMING");

        put(RUN_RUNNABLE, "RUN_RUNNABLE");
    }};
@@ -193,6 +209,7 @@ public class CallAudioModeStateMachine extends StateMachine {
            AudioProcessingFocusState.class.getSimpleName();
    public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName();
    public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName();
    public static final String STREAMING_STATE_NAME = StreamingFocusState.class.getSimpleName();
    public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();

    private class BaseState extends State {
@@ -214,6 +231,9 @@ public class CallAudioModeStateMachine extends StateMachine {
                case ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING:
                    transitionTo(mAudioProcessingFocusState);
                    return HANDLED;
                case ENTER_STREAMING_FOCUS_FOR_TESTING:
                    transitionTo(mStreamingFocusState);
                    return HANDLED;
                case ABANDON_FOCUS_FOR_TESTING:
                    transitionTo(mUnfocusedState);
                    return HANDLED;
@@ -280,6 +300,9 @@ public class CallAudioModeStateMachine extends StateMachine {
                            " Args are: \n" + args.toString());
                    transitionTo(mOtherFocusState);
                    return HANDLED;
                case START_CALL_STREAMING:
                    transitionTo(mStreamingFocusState);
                    return HANDLED;
                case TONE_STARTED_PLAYING:
                    // This shouldn't happen either, but perform the action anyway.
                    Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
@@ -353,6 +376,9 @@ public class CallAudioModeStateMachine extends StateMachine {
                    Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
                            + args.toString());
                    return HANDLED;
                case START_CALL_STREAMING:
                    transitionTo(mStreamingFocusState);
                    return HANDLED;
                case AUDIO_OPERATIONS_COMPLETE:
                    Log.i(LOG_TAG, "Abandoning audio focus: now AUDIO_PROCESSING");
                    mAudioManager.abandonAudioFocusForCall();
@@ -625,6 +651,79 @@ public class CallAudioModeStateMachine extends StateMachine {
                    Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
                            + " state");
                    return HANDLED;
                case START_CALL_STREAMING:
                    transitionTo(mStreamingFocusState);
                    return HANDLED;
                default:
                    // The forced focus switch commands are handled by BaseState.
                    return NOT_HANDLED;
            }
        }
    }

    private class StreamingFocusState extends BaseState {
        @Override
        public void enter() {
            Log.i(LOG_TAG, "Audio focus entering streaming state");
            mAudioManager.setMode(AudioManager.MODE_CALL_REDIRECT);
            mMostRecentMode = AudioManager.MODE_NORMAL;
            mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
            mCallAudioManager.getCallAudioRouteStateMachine().sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.STREAMING_FORCE_ENABLED);
        }

        private void preExit() {
            mCallAudioManager.getCallAudioRouteStateMachine().sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.STREAMING_FORCE_DISABLED);
        }

        @Override
        public boolean processMessage(Message msg) {
            if (super.processMessage(msg) == HANDLED) {
                return HANDLED;
            }
            MessageArgs args = (MessageArgs) msg.obj;
            switch (msg.what) {
                case NO_MORE_ACTIVE_OR_DIALING_CALLS:
                    // Do nothing.
                    return HANDLED;
                case NO_MORE_RINGING_CALLS:
                    // Do nothing.
                    return HANDLED;
                case NO_MORE_HOLDING_CALLS:
                    // Do nothing.
                    return HANDLED;
                case NO_MORE_AUDIO_PROCESSING_CALLS:
                    // Do nothing.
                    return HANDLED;
                case NEW_ACTIVE_OR_DIALING_CALL:
                    // Only possible for emergency call
                    BaseState destState = calculateProperStateFromArgs(args);
                    if (destState != this) {
                        preExit();
                        transitionTo(destState);
                    }
                    return HANDLED;
                case NEW_RINGING_CALL:
                    // Only possible for emergency call
                    preExit();
                    transitionTo(mRingingFocusState);
                    return HANDLED;
                case NEW_HOLDING_CALL:
                    // Do nothing.
                    return HANDLED;
                case NEW_AUDIO_PROCESSING_CALL:
                    // Do nothing.
                    return HANDLED;
                case START_CALL_STREAMING:
                    // Can happen as a duplicate message
                    return HANDLED;
                case TONE_STARTED_PLAYING:
                    // Do nothing.
                    return HANDLED;
                case STOP_CALL_STREAMING:
                    transitionTo(calculateProperStateFromArgs(args));
                    return HANDLED;
                default:
                    // The forced focus switch commands are handled by BaseState.
                    return NOT_HANDLED;
@@ -707,6 +806,7 @@ public class CallAudioModeStateMachine extends StateMachine {
    private final BaseState mSimCallFocusState = new SimCallFocusState();
    private final BaseState mVoipCallFocusState = new VoipCallFocusState();
    private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState();
    private final BaseState mStreamingFocusState = new StreamingFocusState();
    private final BaseState mOtherFocusState = new OtherFocusState();

    private final AudioManager mAudioManager;
@@ -745,6 +845,7 @@ public class CallAudioModeStateMachine extends StateMachine {
        addState(mSimCallFocusState);
        addState(mVoipCallFocusState);
        addState(mAudioProcessingFocusState);
        addState(mStreamingFocusState);
        addState(mOtherFocusState);
        setInitialState(mUnfocusedState);
        start();
@@ -754,6 +855,7 @@ public class CallAudioModeStateMachine extends StateMachine {
                .setHasHoldingCalls(false)
                .setIsTonePlaying(false)
                .setForegroundCallIsVoip(false)
                .setIsStreaming(false)
                .setSession(Log.createSubsession())
                .build());
    }
@@ -807,12 +909,15 @@ public class CallAudioModeStateMachine extends StateMachine {
        // switch to the appropriate focus.
        // Otherwise abandon focus.

        // The order matters here. If there are active calls, holding focus for them takes priority.
        // After that, we want to prioritize holding calls over ringing calls so that when a
        // call-waiting call gets answered, there's no transition in and out of the ringing focus
        // state. After that, we want tones since we actually hold focus during them, then the
        // audio processing state because that will release focus.
        if (args.hasActiveOrDialingCalls) {
        // The order matters here. If there is streaming call, holding streaming route for them
        // takes priority. After that, holding focus for active calls takes priority. After that, we
        // want to prioritize holding calls over ringing calls so that when a call-waiting call gets
        // answered, there's no transition in and out of the ringing focus state. After that, we
        // want tones since we actually hold focus during them, then the audio processing state
        // because that will release focus.
        if (args.isStreaming) {
            return mSimCallFocusState;
        } else if (args.hasActiveOrDialingCalls) {
            if (args.foregroundCallIsVoip) {
                return mVoipCallFocusState;
            } else {
+81 −0
Original line number Diff line number Diff line
@@ -108,6 +108,9 @@ public class CallAudioRouteStateMachine extends StateMachine {
    /** Direct the audio stream through the device's speakerphone. */
    public static final int ROUTE_SPEAKER       = CallAudioState.ROUTE_SPEAKER;

    /** Direct the audio stream through another device. */
    public static final int ROUTE_STREAMING     = CallAudioState.ROUTE_STREAMING;

    /** Valid values for msg.what */
    public static final int CONNECT_WIRED_HEADSET = 1;
    public static final int DISCONNECT_WIRED_HEADSET = 2;
@@ -129,6 +132,10 @@ public class CallAudioRouteStateMachine extends StateMachine {
    public static final int SPEAKER_ON = 1006;
    public static final int SPEAKER_OFF = 1007;

    // Messages denoting that the streaming route switch request was sent.
    public static final int STREAMING_FORCE_ENABLED = 1008;
    public static final int STREAMING_FORCE_DISABLED = 1009;

    public static final int USER_SWITCH_EARPIECE = 1101;
    public static final int USER_SWITCH_BLUETOOTH = 1102;
    public static final int USER_SWITCH_HEADSET = 1103;
@@ -545,6 +552,8 @@ public class CallAudioRouteStateMachine extends StateMachine {
                case DISCONNECT_DOCK:
                    // Nothing to do here
                    return HANDLED;
                case STREAMING_FORCE_ENABLED:
                    transitionTo(mStreamingState);
                default:
                    return NOT_HANDLED;
            }
@@ -629,6 +638,9 @@ public class CallAudioRouteStateMachine extends StateMachine {
                        mCallAudioManager.notifyAudioOperationsComplete();
                    }
                    return HANDLED;
                case STREAMING_FORCE_ENABLED:
                    transitionTo(mStreamingState);
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
@@ -1106,6 +1118,9 @@ public class CallAudioRouteStateMachine extends StateMachine {
                case DISCONNECT_DOCK:
                    // Nothing to do here
                    return HANDLED;
                case STREAMING_FORCE_ENABLED:
                    transitionTo(mStreamingState);
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
@@ -1331,6 +1346,68 @@ public class CallAudioRouteStateMachine extends StateMachine {
                case DISCONNECT_DOCK:
                    sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
                    return HANDLED;
                case STREAMING_FORCE_ENABLED:
                    transitionTo(mStreamingState);
                    return HANDLED;
               default:
                    return NOT_HANDLED;
            }
        }
    }

    class StreamingState extends AudioState {
        @Override
        public void enter() {
            super.enter();
            updateSystemAudioState();
        }

        @Override
        public void updateSystemAudioState() {
            updateInternalCallAudioState();
            setSystemAudioState(mCurrentCallAudioState);
        }

        @Override
        public boolean isActive() {
            return true;
        }

        @Override
        public int getRouteCode() {
            return CallAudioState.ROUTE_STREAMING;
        }

        @Override
        public boolean processMessage(Message msg) {
            if (super.processMessage(msg) == HANDLED) {
                return HANDLED;
            }
            switch (msg.what) {
                case SWITCH_EARPIECE:
                case USER_SWITCH_EARPIECE:
                case SPEAKER_OFF:
                    // Nothing to do here
                    return HANDLED;
                case SPEAKER_ON:
                    // fall through
                case BT_AUDIO_CONNECTED:
                case SWITCH_BLUETOOTH:
                case USER_SWITCH_BLUETOOTH:
                case SWITCH_HEADSET:
                case USER_SWITCH_HEADSET:
                case SWITCH_SPEAKER:
                case USER_SWITCH_SPEAKER:
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == NO_FOCUS) {
                        reinitialize();
                        mCallAudioManager.notifyAudioOperationsComplete();
                    }
                    return HANDLED;
                case STREAMING_FORCE_DISABLED:
                    reinitialize();
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
@@ -1399,6 +1476,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
    private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute();
    private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute();
    private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
    private final StreamingState mStreamingState = new StreamingState();

    /**
     * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
@@ -1495,6 +1573,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
        addState(mQuiescentHeadsetRoute);
        addState(mQuiescentBluetoothRoute);
        addState(mQuiescentSpeakerRoute);
        addState(mStreamingState);


        mStateNameToRouteCode = new HashMap<>(8);
@@ -1507,12 +1586,14 @@ public class CallAudioRouteStateMachine extends StateMachine {
        mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH);
        mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
        mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER);
        mStateNameToRouteCode.put(mStreamingState.getName(), ROUTE_STREAMING);

        mRouteCodeToQuiescentState = new HashMap<>(4);
        mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute);
        mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute);
        mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute);
        mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
        mRouteCodeToQuiescentState.put(ROUTE_STREAMING, mStreamingState);
    }

    public void setCallAudioManager(CallAudioManager callAudioManager) {
+378 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading