android音频系统(7):通话过程中的音频输出设备切换

前言:由于通话比较特殊,Android对于通话过程中音频输出设备的切换做了特殊处理,它在上层也是通过切换音频播放状态来完成切换操作的,android用CallAudioState来封装通话过程中的音频播放状态;

1.CallAudioState解析
CallAudioState的属性

CallAudioState.java
public static final int ROUTE_EARPIECE      = 0x00000001;//听筒
public static final int ROUTE_BLUETOOTH     = 0x00000002;//蓝牙
public static final int ROUTE_WIRED_HEADSET = 0x00000004;//有线耳机
public static final int ROUTE_SPEAKER       = 0x00000008;//扬声器
public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;//听筒或者耳机
public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
        ROUTE_SPEAKER;//全部状态

通话过程中的音频播放状态共有4种,分别是听筒,蓝牙,有线耳机,扬声器;

CallAudioState中的重要方法有:

CallAudioState.java
//是否静音
public boolean isMuted() {
    return isMuted;
}

//返回音频状态
public int getRoute() {
    return route;
}

 

2.InCallService
切换通话过程中的音频播放状态需要用到InCallService,InCallService是一个代理;
InCallService是一个抽象类,需要写一个子类来继承它,看下InCallService中几个比较重要的方法;

void onCallAudioStateChanged(CallAudioState audioState);//当音频播放状态发生了切换时,就会调用该函数,该方法经常用到
void onBringToForeground(boolean showDialpad);
void onCallAdded(Call call);
void onCallRemoved(Call call);
void onCanAddCallChanged(boolean canAddCall);

上面只是罗列了比较常用的方法,更多的方法剖析请去InCallService里面查看;


3.下面就来看下几种状态之间的切换
假设现在的通话模式是扬声器,想切换到听筒或者耳机,可以调用:mInCallService.setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
mInCallService就是InCallService;

CallAudioState.ROUTE_WIRED_OR_EARPIECE表示:如果此时是扬声器模式,当调用CallAudioState.ROUTE_WIRED_OR_EARPIECE参数时,系统会将音频输出设备切换成听筒或者是耳机,至于最后到底是听筒还是耳机播放,这个取决于它们的优先级,如果手机当中插入了耳机,耳机的优先级会比听筒要高;

假如此时现在的通话模式是耳机,想切换到扬声器,可以调用:mInCallService.setAudioRoute(CallAudioState.ROUTE_SPEAKER);

 

4.我们来分析下mInCallService.setAudioRoute(CallAudioState.ROUTE_SPEAKER)的流程:

InCallService.java
public final void setAudioRoute(int route) {
    if (mPhone != null) {
        mPhone.setAudioRoute(route);
    }
}

会看到一个mPhone,这个Phone是这么来的呢?

InCallService.java
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
            return;
        }

        switch (msg.what) {
            case MSG_SET_IN_CALL_ADAPTER:
                String callingPackage = getApplicationContext().getOpPackageName();
                mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
                        getApplicationContext().getApplicationInfo().targetSdkVersion);
                mPhone.addListener(mPhoneListener);
                //调用onPhoneCreated()
                onPhoneCreated(mPhone);
                break;
                
            ............
        }
    }
}

mPhone是在处理MSG_SET_IN_CALL_ADAPTER消息时被创建的,那MSG_SET_IN_CALL_ADAPTER消息又是谁传递的呢?

InCallService.java
private final class InCallServiceBinder extends IInCallService.Stub {
    @Override
    public void setInCallAdapter(IInCallAdapter inCallAdapter) {
        mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
    }

    @Override
    public void addCall(ParcelableCall call) {
        mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
    }

    @Override
    public void updateCall(ParcelableCall call) {
        mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
    }
    ............
}

由于我们在后面会用到inCallAdapter,所以分析下inCallAdapter的由来;
InCallServiceBinder很明显就是个服务代理,也就是绑定服务时传递进来的,绑定服务需要ServiceConnection,那这个ServiceConnection到底是谁呢?
答案在packages/services/Telecomm/src/com/android/server/telecom/InCallController.java中,它在绑定InCallService时,调用到了setInCallAdapter()方法,代码如下:

InCallController.java
Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
    intent.setComponent(mInCallServiceInfo.getComponentName());
    if (call != null && !call.isIncoming() && !call.isExternalCall()){
        intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
                call.getIntentExtras());
        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                call.getTargetPhoneAccount());
    }

    Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
    mIsConnected = true;
    if (!mContext.bindServiceAsUser(intent, mServiceConnection,
                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
                Context.BIND_ABOVE_CLIENT,
                UserHandle.CURRENT)) {
        Log.w(this, "Failed to connect.");
        mIsConnected = false;
    }
    ............
}

private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.startSession("ICSBC.oSC");
        synchronized (mLock) {
            try {
                Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                mIsBound = true;
                if (mIsConnected) {
                    // Only proceed if we are supposed to be connected.
                    onConnected(service);
                }
            } finally {
                Log.endSession();
            }
        }
    }

    ............
};

rotected void onConnected(IBinder service) {
    boolean shouldRemainConnected =
            InCallController.this.onConnected(mInCallServiceInfo, service);
    if (!shouldRemainConnected) {
        disconnect();
    }
}

private boolean onConnected(InCallServiceInfo info, IBinder service) {
    Trace.beginSection("onConnected: " + info.getComponentName());
    Log.i(this, "onConnected to %s", info.getComponentName());

    IInCallService inCallService = IInCallService.Stub.asInterface(service);
    mInCallServices.put(info, inCallService);

    try {
        inCallService.setInCallAdapter(
                new InCallAdapter(
                        mCallsManager,
                        mCallIdMapper,
                        mLock,
                        info.getComponentName().getPackageName()));
    } catch (RemoteException e) {
        Log.e(this, e, "Failed to set the in-call adapter.");
        Trace.endSection();
        return false;
    }
    ............
}

终于找到这个InCallAdapter了;
继续:

Phone.java
public final void setAudioRoute(int route) {
    mInCallAdapter.setAudioRoute(route);
}

InCallAdapter.java
public void setAudioRoute(int route) {
    try {
        mAdapter.setAudioRoute(route);
    } catch (RemoteException e) {
    }
}

这个mAdapter就是InCallController中的InCallAdapter,进入到了packages/services/Telecomm/src/com/android/server/telecom/InCallAdapter.java中;

InCallAdapter.java
public void setAudioRoute(int route) {
    try {
        Log.startSession(LogUtils.Sessions.ICA_SET_AUDIO_ROUTE, mOwnerComponentName);
        long token = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                mCallsManager.setAudioRoute(route);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    } finally {
        Log.endSession();
    }
}

mCallsManager是CallsManager,进入到了CallsManager.setAudioRoute();

CallsManager.java
void setAudioRoute(int route) {
    Call call = getDialingCall();
    if (call != null && call.getStartWithSpeakerphoneOn()) {
        /* There is a change in audio routing preferance for the call.
        * So, honour the new audio routing preferance.
        */
        call.setStartWithSpeakerphoneOn(false);
    }
    mCallAudioManager.setAudioRoute(route);
}

又进入到了CallAudioManager

CallAudioManager.java
void setAudioRoute(int route) {
    Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
    switch (route) {
        case CallAudioState.ROUTE_BLUETOOTH:
            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH);
            return;
        case CallAudioState.ROUTE_SPEAKER:
            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.USER_SWITCH_SPEAKER);
            return;
        case CallAudioState.ROUTE_WIRED_HEADSET:
            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.USER_SWITCH_HEADSET);
            return;
        case CallAudioState.ROUTE_EARPIECE:
            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.USER_SWITCH_EARPIECE);
            return;
        case CallAudioState.ROUTE_WIRED_OR_EARPIECE:
            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                    CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
                    CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE);
            return;
        default:
            Log.wtf(this, "Invalid route specified: %d", route);
    }
}


终于走到了根据地了,还记得我们的参数是什么吗,CallAudioState.ROUTE_SPEAKER,也就是调用mCallAudioRouteStateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_SPEAKER);

到了这里,暂停一会,分析下CallAudioManager和mCallAudioRouteStateMachine


5.CallAudioManager,CallAudioRouteStateMachine,CallAudioModeStateMachine

CallAudioManager可以类比于AudioManager,负责通话过程中音频播放状态的逻辑;

CallAudioManager是在CallsManager被初始化的;

CallsManager.java
CallsManager(......){
    mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                this,new CallAudioModeStateMachine((AudioManager)
                        mContext.getSystemService(Context.AUDIO_SERVICE)),
                playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer);
}

CallAudioModeStateMachine是什么,它是处理通话过程中的音频模式的,如语音模式,通话模式等,看代码:

CallAudioModeStateMachine.java
//音频状态
private final BaseState mUnfocusedState = new UnfocusedState();//待机
private final BaseState mRingingFocusState = new RingingFocusState();//响铃
private final BaseState mSimCallFocusState = new SimCallFocusState();//Call
private final BaseState mVoipCallFocusState = new VoipCallFocusState();//COMMUNICATION
private final BaseState mOtherFocusState = new OtherFocusState();

private final AudioManager mAudioManager;
private CallAudioManager mCallAudioManager;

private int mMostRecentMode;
private boolean mIsInitialized = false;

public CallAudioModeStateMachine(AudioManager audioManager) {
    super(CallAudioModeStateMachine.class.getSimpleName());
    mAudioManager = audioManager;
    mMostRecentMode = AudioManager.MODE_NORMAL;

    //将这些状态添加到mStateInfo中
    addState(mUnfocusedState);
    addState(mRingingFocusState);
    addState(mSimCallFocusState);
    addState(mVoipCallFocusState);
    addState(mOtherFocusState);
    
    //待机状态是默认状态
    setInitialState(mUnfocusedState);
    
    //完成CallAudioModeStateMachine的构造
    start();
    sendMessage(INITIALIZE, new MessageArgs());
}
............


CallAudioModeStateMachine继承自StateMachine,addState方法在StateMachine中:

StateMachine.java
public final void addState(State state) {
    mSmHandler.addState(state, null);
}

private final StateInfo addState(State state, State parent) {
    StateInfo parentStateInfo = null;
    if (parent != null) {//不成立
        parentStateInfo = mStateInfo.get(parent);
        if (parentStateInfo == null) {
            // Recursively add our parent as it's not been added yet.
            parentStateInfo = addState(parent, null);
        }
    }
    StateInfo stateInfo = mStateInfo.get(state);
    if (stateInfo == null) {
        //创建StateInfo
        stateInfo = new StateInfo();
        //以State为key,保存stateInfo
        mStateInfo.put(state, stateInfo);
    }

    // Validate that we aren't adding the same state in two different hierarchies.
    if ((stateInfo.parentStateInfo != null)
            && (stateInfo.parentStateInfo != parentStateInfo)) {
        throw new RuntimeException("state already added");
    }
    //State与stateInfo进行绑定
    stateInfo.state = state;
    stateInfo.parentStateInfo = parentStateInfo;//为null
    stateInfo.active = false;//不活跃
    if (mDbg) mSm.log("addStateInternal: X stateInfo: " + stateInfo);
    return stateInfo;
}

addState()方法就是将通话过程中所有的音频状态State添加到CallAudioModeStateMachine的mStateInfo容器中;我们来看下CallAudioModeStateMachine构造函数中的start()方法:

public void start() {
    // mSmHandler can be null if the state machine has quit.
    SmHandler smh = mSmHandler;
    if (smh == null) return;

    /** Send the complete construction message */
    smh.completeConstruction();
}

private final void completeConstruction() {
    int maxDepth = 0;
    //由于i.parentStateInfo为null.所以maxDepth = depth = 1
    for (StateInfo si : mStateInfo.values()) {
        int depth = 0;
        for (StateInfo i = si; i != null; depth++) {
            i = i.parentStateInfo;
        }
        if (maxDepth < depth) {
            maxDepth = depth;
        }
    }

    //长度为1
    mStateStack = new StateInfo[maxDepth];
    mTempStateStack = new StateInfo[maxDepth];
    //初始化mStateStack
    setupInitialStateStack();

    /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
    //构造完成后,发送SM_INIT_CMD消息
    sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));

}


private final void setupInitialStateStack() {
    //mInitialState是mUnfocusedState
    StateInfo curStateInfo = mStateInfo.get(mInitialState);
    //由于curStateInfo.parentStateInfo==null.所以在for循环结束后,
    //mTempStateStack只有一个mUnfocusedState所对应的StateInfo,并且mTempStateStackCount为1
    for (mTempStateStackCount = 0; curStateInfo != null; mTempStateStackCount++) {
        mTempStateStack[mTempStateStackCount] = curStateInfo;
        curStateInfo = curStateInfo.parentStateInfo;
    }

    // Empty the StateStack
    mStateStackTopIndex = -1;

    //数据从mTempStateStack移动到mStateStack中
    moveTempStateStackToStateStack();
}

private final int moveTempStateStackToStateStack() {
    //mStateStackTopIndex = -1,所以startingIndex = 0
    int startingIndex = mStateStackTopIndex + 1;
    //mTempStateStackCount = 1,所以i=0
    int i = mTempStateStackCount - 1;
    int j = startingIndex;//j = 0
    while (i >= 0) {
        if (mDbg) mSm.log("moveTempStackToStateStack: i=" + i + ",j=" + j);
        mStateStack[j] = mTempStateStack[i];//mStateStack[0] = mTempStateStack[0]后跳出while循环
        j += 1;
        i -= 1;
    }

    //跳出循环后,j = 1,i = -1

    mStateStackTopIndex = j - 1;//mStateStackTopIndex又回到了0
    
    return startingIndex;
}

搞一个mTempStateStack,是为了层层解析StateInfo中的parentStateInfo,我们没用到parentStateInfo;
元素在mStateStack和mTempStateStack中的顺序是相反的,新添加进来的音频状态被放在mStateStack的最前面,mStateStack就是栈的数据结构;继续来看completeConstruction()中的sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj)),当构造完成之后,会发送SM_INIT_CMD消息,该消息在SmHandler中被处理:

public final void handleMessage(Message msg) {
    if (!mHasQuit) {
        if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
            mSm.onPreHandleMessage(msg);
        }

        if (mDbg) mSm.log("handleMessage: E msg.what=" + msg.what);

        /** Save the current message */
        mMsg = msg;

        /** State that processed the message */
        State msgProcessedState = null;
        if (mIsConstructionCompleted) {
  
            msgProcessedState = processMsg(msg);
        } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                && (mMsg.obj == mSmHandlerObj)) {//成立
            //mIsConstructionCompleted = true表示构造已经完成
            mIsConstructionCompleted = true;
            //调用相应音频播放模式的enter()方法
            invokeEnterMethods(0);
        } else {
            throw new RuntimeException("StateMachine.handleMessage: "
                    + "The start method not called, received msg: " + msg);
        }
        
        //执行音频播放模式的切换,比如从Normal进入到Call
        //此时的msgProcessedState为null
        performTransitions(msgProcessedState, msg);

        // We need to check if mSm == null here as we could be quitting.
        if (mDbg && mSm != null) mSm.log("handleMessage: X");

        if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
            mSm.onPostHandleMessage(msg);
        }
    }
}

先看invokeEnterMethods(0)方法:

private final void invokeEnterMethods(int stateStackEnteringIndex) {
    //stateStackEnteringIndex = 0 ,mStateStackTopIndex = 0 ;
    for (int i = stateStackEnteringIndex; i <= mStateStackTopIndex; i++) {
        if (stateStackEnteringIndex == mStateStackTopIndex) {
            // Last enter state for transition
            mTransitionInProgress = false;
        }
        if (mDbg) mSm.log("invokeEnterMethods: " + mStateStack[i].state.getName());
        //i = 0,所以mStateStack[i].state是mUnfocusedState,调用mUnfocusedState.enter()
        mStateStack[i].state.enter();
        //mUnfocusedState进入活跃状态,也就是表示此时的音频播放模式是待机模式
        mStateStack[i].active = true;
    }
    mTransitionInProgress = false; // ensure flag set to false if no methods called
}

调用mUnfocusedState.enter(),看下代码:

private class UnfocusedState extends BaseState {
    @Override
    public void enter() {
        if (mIsInitialized) {
            Log.i(LOG_TAG, "Abandoning audio focus: now UNFOCUSED");
            //放弃通话过程中的音频焦点,通话过程中的音频播放也是要获取焦点的,在呼叫或铃声结束,或者呼叫被拒绝或未应答时需要释放焦点,
            //这和播放音乐时的获取焦点类似
            mAudioManager.abandonAudioFocusForCall();

            //设置当前的音频播放模式为待机模式,这就与UnfocusedState对应了
            mAudioManager.setMode(AudioManager.MODE_NORMAL);

            //用mMostRecentMode保存最新的音频播放模式
            mMostRecentMode = AudioManager.MODE_NORMAL;
            
            //将音频焦点类型传递给CallAudioRouteStateMachine
            mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
        }
    }
    ............
}

继续来看:performTransitions(msgProcessedState, msg)执行音频播放模式的切换操作,

private void performTransitions(State msgProcessedState, Message msg) {
    /**
     * If transitionTo has been called, exit and then enter
     * the appropriate states. We loop on this to allow
     * enter and exit methods to use transitionTo.
     */
    State orgState = mStateStack[mStateStackTopIndex].state;

    ............

    State destState = mDestState;
    if (destState != null) {
        /**
         * Process the transitions including transitions in the enter/exit methods
         */
        while (true) {
            if (mDbg) mSm.log("handleMessage: new destination call exit/enter");

            /**
             * Determine the states to exit and enter and return the
             * common ancestor state of the enter/exit states. Then
             * invoke the exit methods then the enter methods.
             */
            StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
            // flag is cleared in invokeEnterMethods before entering the target state
            mTransitionInProgress = true;
            //退出旧的音频状态,调用State.exit()
            invokeExitMethods(commonStateInfo);
            int stateStackEnteringIndex = moveTempStateStackToStateStack();
            //进入新的音频状态,调用State.enter()
            invokeEnterMethods(stateStackEnteringIndex);

            /**
             * Since we have transitioned to a new state we need to have
             * any deferred messages moved to the front of the message queue
             * so they will be processed before any other messages in the
             * message queue.
             */
            moveDeferredMessageAtFrontOfQueue();

            if (destState != mDestState) {
                // A new mDestState so continue looping
                destState = mDestState;
            } else {
                // No change in mDestState so we're done
                break;
            }
        }
        mDestState = null;
    }

    ............
}

分析了这么多,来总结下CallAudioModeStateMachine,简单点说CallAudioModeStateMachine封装了4种通话过程中的音频播放模式,以及它们之间的切换操作;


6.来走一遍UnfocusedState切换到的流程

由于CallAudioRouteStateMachine和CallAudioModeStateMachine类似,所以CallAudioRouteStateMachine就不再分析了;
假设当前是待机模式,要从待机模式切换到响铃模式;从CallAudioManager.onCallAdded()开始:

CallAudioManager.java
public void onCallAdded(Call call) {
    if (shouldIgnoreCallForAudio(call)) {
        return; // Don't do audio handling for calls in a conference, or external calls.
    }

    addCall(call);
}

private void addCall(Call call) {
    if (mCalls.contains(call)) {
        Log.w(LOG_TAG, "Call TC@%s is being added twice.", call.getId());
        return; // No guarantees that the same call won't get added twice.
    }

    Log.d(LOG_TAG, "Call added with id TC@%s in state %s", call.getId(),
            CallState.toString(call.getState()));

    if (mCallStateToCalls.get(call.getState()) != null) {
        mCallStateToCalls.get(call.getState()).add(call);
    }
    updateForegroundCall();
    mCalls.add(call);

    onCallEnteringState(call, call.getState());
}

private void onCallEnteringState(Call call, int state) {
    switch (state) {
        case CallState.ACTIVE:
        case CallState.CONNECTING:
            onCallEnteringActiveDialingOrConnecting();
            break;
        case CallState.RINGING:
            //走这个
            onCallEnteringRinging();
            break;
        case CallState.ON_HOLD:
            onCallEnteringHold();
            break;
        case CallState.PULLING:
            onCallEnteringActiveDialingOrConnecting();
            break;
        case CallState.DIALING:
            onCallEnteringActiveDialingOrConnecting();
            playRingbackForCall(call);
            break;
    }
}

private void onCallEnteringRinging() {
    if (mRingingCalls.size() == 1) {
        mCallAudioModeStateMachine.sendMessageWithArgs(
                CallAudioModeStateMachine.NEW_RINGING_CALL,
                makeArgsForModeStateMachine());
    }
}

进入到CallAudioModeStateMachine:

public void sendMessageWithArgs(int messageCode, MessageArgs args) {
    //messageCode是CallAudioModeStateMachine.NEW_RINGING_CALL
    sendMessage(messageCode, args);
}

public void sendMessage(int what, Object obj) {
    // mSmHandler can be null if the state machine has quit.
    SmHandler smh = mSmHandler;
    if (smh == null) return;

    smh.sendMessage(obtainMessage(what, obj));
}

进入到SmHandler

public final void handleMessage(Message msg) {
    if (!mHasQuit) {
        if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
            //准备工作
            mSm.onPreHandleMessage(msg);
        }

        /** Save the current message */
        mMsg = msg;

        /** State that processed the message */
        State msgProcessedState = null;
        if (mIsConstructionCompleted) {//成立
            /** Normal path */
            //处理msg
            msgProcessedState = processMsg(msg);
        } else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
                && (mMsg.obj == mSmHandlerObj)) {
            mIsConstructionCompleted = true;
            invokeEnterMethods(0);
        } else {
            throw new RuntimeException("StateMachine.handleMessage: "
                    + "The start method not called, received msg: " + msg);
        }

        //执行音频播放模式的切换,比如从Normal进入到Call
        performTransitions(msgProcessedState, msg);

        if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
            //结束
            mSm.onPostHandleMessage(msg);
        }
    }
}

先看processMsg(msg):
 

private final State processMsg(Message msg) {
    //curStateInfo为UnfocusedState所对应的StateInfo
    StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
    if (mDbg) {
        mSm.log("processMsg: " + curStateInfo.state.getName());
    }

    if (isQuit(msg)) {//不成立
        transitionTo(mQuittingState);
    } else {
        while (!curStateInfo.state.processMessage(msg)) {//UnfocusedState处理msg
            /**
             * Not processed
             */
            curStateInfo = curStateInfo.parentStateInfo;
            if (curStateInfo == null) {
                /**
                 * No parents left so it's not handled
                 */
                mSm.unhandledMessage(msg);
                break;
            }
            if (mDbg) {
                mSm.log("processMsg: " + curStateInfo.state.getName());
            }
        }
    }
    return (curStateInfo != null) ? curStateInfo.state : null;
}

看UnfocusedState.processMessage(),还记得msg是什么吗?CallAudioModeStateMachine.NEW_RINGING_CALL

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 NEW_ACTIVE_OR_DIALING_CALL:
            transitionTo(args.foregroundCallIsVoip
                    ? mVoipCallFocusState : mSimCallFocusState);
            return HANDLED;
        case NEW_RINGING_CALL:
            //切换到mRingingFocusState
            transitionTo(mRingingFocusState);
            return HANDLED;
        case NEW_HOLDING_CALL:
            // This really shouldn't happen, but transition to the focused state anyway.
            Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
                    " Args are: \n" + args.toString());
            transitionTo(mOtherFocusState);
            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"
                    + args.toString());
            return HANDLED;
        default:
            // The forced focus switch commands are handled by BaseState.
            return NOT_HANDLED;
    }
}

public final void transitionTo(IState destState) {
    mSmHandler.transitionTo(destState);
}

要从UnfocusedState切换到RingingFocusState了,继续看SmHandler:

private final void transitionTo(IState destState) {
    if (mTransitionInProgress) {
        Log.wtf(mSm.mName, "transitionTo called while transition already in progress to " +
                mDestState + ", new target state=" + destState);
    }
    //使用mDestState保存要切换的目标State,所以mDestState就是mRingingFocusState
    mDestState = (State) destState;
    if (mDbg) mSm.log("transitionTo: destState=" + mDestState.getName());
}

再次回到SmHandler的handleMessage()中的“msgProcessedState = processMsg(msg)”,processMsg(msg)已经执行结束了,并返回了UnfocusedState给msgProcessedState;
再看performTransitions(msgProcessedState, msg);

private void performTransitions(State msgProcessedState, Message msg) {
    //此时的msgProcessedState为UnfocusedState
    //orgState为UnfocusedState
    State orgState = mStateStack[mStateStackTopIndex].state;

    boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj != mSmHandlerObj);

    if (mLogRecords.logOnlyTransitions()) {
        /** Record only if there is a transition */
        if (mDestState != null) {
            mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
                    orgState, mDestState);
        }
    } else if (recordLogMsg) {
        /** Record message */
        mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState, orgState,
                mDestState);
    }

    //destState为RingingFocusState
    State destState = mDestState;
    if (destState != null) {
        /**
         * Process the transitions including transitions in the enter/exit methods
         */
        while (true) {
            if (mDbg) mSm.log("handleMessage: new destination call exit/enter");

            /**
             * Determine the states to exit and enter and return the
             * common ancestor state of the enter/exit states. Then
             * invoke the exit methods then the enter methods.
             */
            StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
            // flag is cleared in invokeEnterMethods before entering the target state
            mTransitionInProgress = true;
            invokeExitMethods(commonStateInfo);
            //返回的stateStackEnteringIndex为1,mStateStack中有了两个元素,下标为0的是UnfocusedState,下标为1的是RingingFocusState,
            //mStateStackTopIndex变成了1
            int stateStackEnteringIndex = moveTempStateStackToStateStack();
            //进入新的音频状态,调用RingingFocusState.enter()
            invokeEnterMethods(stateStackEnteringIndex);

            moveDeferredMessageAtFrontOfQueue();

            if (destState != mDestState) {
                // A new mDestState so continue looping
                destState = mDestState;
            } else {
                // No change in mDestState so we're done
                break;
            }
        }
        mDestState = null;
    }

    ............
    
}

终于进入RingingFocusState.enter()了:

private class RingingFocusState extends BaseState {
    @Override
    public void enter() {
        Log.i(LOG_TAG, "Audio focus entering RINGING state");
        if (mCallAudioManager.startRinging()) {
            //请求焦点
            mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            //响铃模式
            mAudioManager.setMode(AudioManager.MODE_RINGTONE);
            //设置焦点
            mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.RINGING_FOCUS);
        } else {
            Log.i(LOG_TAG, "Entering RINGING but not acquiring focus -- silent ringtone");
        }

        mCallAudioManager.stopCallWaiting();
    }
    ............
}

结束了,和播放音频类似,通话过程中的也是需要获取焦点,只是变成了mAudioManager.abandonAudioFocusForCall();
音频播放模式还是调用mAudioManager.setMode()方法;

不过这个代码设计很巧妙,它可以应用在所有的状态切换中,等会在编程设计模块里面会详细分析这种设计思想;

我们发现最终调用的还是 mAudioManager.setMode(),所以CallAudioManager做的只是一些封装操作而已;

 

7.上层的模式切换以及设备切换的api调用

在平时的日常开发中,我们在设置音频模式或者切换音频设备时总是报错,我们看下源码是怎么设置和切换的,可以借鉴一下:

①.设置音频模式:

(1)待机模式:
mAudioManager.abandonAudioFocusForCall();
mAudioManager.setMode(AudioManager.MODE_NORMAL);
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);


(2)响铃模式:
①进入响铃:
if (mCallAudioManager.startRinging()) {
    //请求焦点
    mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    //响铃模式
    mAudioManager.setMode(AudioManager.MODE_RINGTONE);
    //设置焦点
    mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.RINGING_FOCUS);
} else {
    Log.i(LOG_TAG, "Entering RINGING but not acquiring focus -- silent ringtone");
}

mCallAudioManager.stopCallWaiting();


②退出响铃:
mCallAudioManager.stopRinging();


(3)通话模式:
mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mMostRecentMode = AudioManager.MODE_IN_CALL;
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);

(4)语音视频模式:
mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);

 

②切换输出设备

(1)听筒:
setSpeakerphoneOn(false);
setBluetoothOn(false);
CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
        mAvailableRoutes);
//CallsManager会调用onCallAudioStateChanged(),通知那些对输出设备切换感兴趣的对象
setSystemAudioState(newState, true);

其中:
private void setSpeakerphoneOn(boolean on) {
    mAudioManager.setSpeakerphoneOn(on);
    mStatusBarNotifier.notifySpeakerphone(on);
}

private void setBluetoothOn(boolean on) {
    if (mBluetoothRouteManager.isBluetoothAvailable()) {
        if (on != mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) {
            Log.i(this, "connecting bluetooth %s", on);
            if (on) {
                mBluetoothRouteManager.connectBluetoothAudio(null /*TODO: add real address*/);
            } else {
                mBluetoothRouteManager.disconnectBluetoothAudio();
            }
        }
    }
}

(2)耳机:
setSpeakerphoneOn(false);
setBluetoothOn(false);

(3)Active蓝牙:
mStatusBarNotifier.notifySpeakerphone(false);
setBluetoothOn(true);

(4)Ringing蓝牙:
setSpeakerphoneOn(false);

(5)Quiescent蓝牙:
setBluetoothOn(false);

(6)Active扬声器:
setSpeakerphoneOn(true);
setBluetoothOn(false);

 

8.总结

分析代码发现,CallAudioManager通过CallAudioModeStateMachine和CallAudioRouteStateMachine这两个类,保存了当前的通话音频状态以及通话音频的输出设备,当音频状态和输出设备发生变化的时候,CallAudioManager会保存新的状态,并且通知外界发生了改变,音频状态和输出设备的切换,底层原理还会调用mAudioManager.setMode()和mAudioManager.setSpeakerphoneOn(on)等AudioService的方法;

关于AudioService.setMode()和AudioSerice.setSpeakerphoneOn(on)等的内容,前面的博客已经做了剖析

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

renshuguo123723

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值