@frameworks/base/media/java/android/media/AudioManager.java
public void startBluetoothSco(){
IAudioService service = getService();
try {
service.startBluetoothSco(mICallBack,
getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in startBluetoothSco", e);
}
}
startBluetoothSco@frameworks/base/services/core/java/com/android/server/audio/AudioService.java
/** @see AudioManager#startBluetoothSco() */
public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
int scoAudioMode =
(targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
SCO_MODE_VIRTUAL_CALL : SCO_MODE_UNDEFINED;
startBluetoothScoInt(cb, scoAudioMode);
}
@frameworks/base/services/core/java/com/android/server/audio/AudioService.java
void startBluetoothScoInt(IBinder cb, int scoAudioMode){
if (!checkAudioSettingsPermission("startBluetoothSco()") ||
!mSystemReady) {
return;
}
ScoClient client = getScoClient(cb, true);
// The calling identity must be cleared before calling ScoClient.incCount().
// inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
// and this must be done on behalf of system server to make sure permissions are granted.
// The caller identity must be cleared after getScoClient() because it is needed if a new
// client is created.
final long ident = Binder.clearCallingIdentity();
client.incCount(scoAudioMode);
Binder.restoreCallingIdentity(ident);
}
@frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public void incCount(int scoAudioMode) {
synchronized(mScoClients) {
requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
try {
mCb.linkToDeath(this, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
}
}
mStartcount++;
}
}
private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
if (totalCount() == 0) {
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
// Make sure that the state transitions to CONNECTING even if we cannot initiate
// the connection.
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
// currently controlled by the same client process.
synchronized(mSetModeDeathHandlers) {
if ((mSetModeDeathHandlers.isEmpty() ||
mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
(mScoAudioState == SCO_STATE_INACTIVE ||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
mScoAudioMode = scoAudioMode;
if (scoAudioMode == SCO_MODE_UNDEFINED) {
if (mBluetoothHeadsetDevice != null) {
mScoAudioMode = new Integer(Settings.Global.getInt(
mContentResolver,
"bluetooth_sco_channel_"+
mBluetoothHeadsetDevice.getAddress(),
SCO_MODE_VIRTUAL_CALL));
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
}
} else {
mScoAudioMode = SCO_MODE_RAW;
}
}
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
boolean status = false;
if (mScoAudioMode == SCO_MODE_RAW) {
status = mBluetoothHeadset.connectAudio();
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
mBluetoothHeadsetDevice);
} else if (mScoAudioMode == SCO_MODE_VR) {
status = mBluetoothHeadset.startVoiceRecognition(
mBluetoothHeadsetDevice);
}
if (status) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
} else {
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
} else if (getBluetoothHeadset()) {
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
}
} else {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
}
} else {
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
}
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
(mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
boolean status = false;
if (mScoAudioMode == SCO_MODE_RAW) {
status = mBluetoothHeadset.disconnectAudio();
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
mBluetoothHeadsetDevice);
} else if (mScoAudioMode == SCO_MODE_VR) {
status = mBluetoothHeadset.stopVoiceRecognition(
mBluetoothHeadsetDevice);
}
if (!status) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
} else if (getBluetoothHeadset()) {
mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
}
} else {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
}
}
}
}
packages\apps\Bluetooth\src\com\android\bluetooth\hfp\HeadsetService.java下面的startScoUsingVirtualVoiceCall接口。
public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
if (DBG) log("startScoUsingVirtualVoiceCall()");
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.startScoUsingVirtualVoiceCall(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
} else {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
return false;
}
@packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
HeadsetService service = getService();
if (service == null) return false;
return service.startScoUsingVirtualVoiceCall(device);
}
boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
/* Do not ignore request if HSM state is still Disconnected or
Pending, it will be processed when transitioned to Connected */
mStateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device);
return true;
}
@packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java中处理:
synchronized boolean initiateScoUsingVirtualVoiceCall() {
if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
// 1. Check if the SCO state is idle
if (isInCall() || mVoiceRecognitionStarted) {
Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
return false;
}
// 2. Send virtual phone state changed to initialize SCO
processCallState(new HeadsetCallState(0, 0,
HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
processCallState(new HeadsetCallState(0, 0,
HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
processCallState(new HeadsetCallState(1, 0,
HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
setVirtualCallInProgress(true);
// Done
if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
return true;
}
这个接口最后虚拟了拨号中,响铃,与接听操作。可以看到这三个操作是拨打电话时就一起调用的,因此对于微信app来说,它是在对方接听之后或者自己接听之后才调用的这个接口,实际上此时通话已经建立了。这是为什么这种通话车机端不会显示来电通知框。因为对于车机而言,是微信在通话建立后才调用startScoUsingVirtualVoiceCall来通知车机现在在打电话。
setBluetoothScoOn@AudioManager.java
-> setBluetoothScoOnInt(on)@AudioService.java
->
public void setBluetoothScoOnInt(boolean on) {2508 if (on) {2509 mForcedUseForComm = AudioSystem.FORCE_BT_SCO;2510 } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {2511 mForcedUseForComm = AudioSystem.FORCE_NONE;2512 }25132514 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,2515 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);2516 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,2517 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);2518 }
public void handleMessage(Message msg) {4313 switch (msg.what) {
case MSG_SET_FORCE_USE:4376 case MSG_SET_FORCE_BT_A2DP_USE:4377 setForceUse(msg.arg1, msg.arg2);4378 break;
4299 private void setForceUse(int usage, int config) {4300 synchronized (mConnectedDevices) {4301 setForceUseInt_SyncDevices(usage, config);4302 }4303 }
29 private void setForceUseInt_SyncDevices(int usage, int config) {5330 switch (usage) {5331 case AudioSystem.FOR_MEDIA:5332 if (config == AudioSystem.FORCE_NO_BT_A2DP) {5333 mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ALL_A2DP;5334 } else { // config == AudioSystem.FORCE_NONE5335 mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ALL_A2DP;5336 }5337 break;5338 case AudioSystem.FOR_DOCK:5339 if (config == AudioSystem.FORCE_ANALOG_DOCK) {5340 mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;5341 } else { // config == AudioSystem.FORCE_NONE5342 mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;5343 }5344 break;5345 default:5346 // usage doesn't affect the broadcast of ACTION_AUDIO_BECOMING_NOISY5347 }5348 AudioSystem.setForceUse(usage, config);5349 }
@frameworks/av/media/libmedia/AudioSystem.cpp
735status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)736{737 const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();738 if (aps == 0) return PERMISSION_DENIED;739 return aps->setForceUse(usage, config);740}
@frameworks/av/media/libmedia/IAudioPolicyService.cpp
126 virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)127 {128 Parcel data, reply;129 data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());130 data.writeInt32(static_cast <uint32_t>(usage));131 data.writeInt32(static_cast <uint32_t>(config));132 remote()->transact(SET_FORCE_USE, data, &reply);133 return static_cast <status_t> (reply.readInt32());134 }
813 case SET_FORCE_USE: {814 CHECK_INTERFACE(IAudioPolicyService, data, reply);815 audio_policy_force_use_t usage = static_cast <audio_policy_force_use_t>(816 data.readInt32());817 audio_policy_forced_cfg_t config =818 static_cast <audio_policy_forced_cfg_t>(data.readInt32());819 reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config)));820 return NO_ERROR;821 } break;
@hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp
void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)