前言:由于通话比较特殊,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)等的内容,前面的博客已经做了剖析