在系统音频播放时,能够使用的通路有很多,而蓝牙使用的是a2dp(Advanced Audio Distribution Profile 蓝牙音频传输模型协定),A2DP是能够采用耳机内的芯片来堆栈数据,达到声音的高清晰度。然而并非支持A2DP的耳机就是蓝牙立体声耳机,立体声实现的基本要求是双声道,所以单声道的蓝牙耳机是不能实现立体声的。声音能达到44.1kHz,一般的耳机只能达到8kHz。如果手机支持蓝牙,只要装载A2DP协议,就能使用A2DP耳机了。还有消费者看到技术参数提到蓝牙V1.0 V1.1 V1.2 V2.0——这些是指蓝牙的技术版本,是指通过蓝牙传输的速度,他们是否支持A2DP具体要看蓝牙产品制造商是否使用这个技术。
在framework中有一个音频很关键的服务AudioService,这个服务主要承载着应用层的操作,其中蓝牙的操作也是经由这个类,首先来看下它的初始化,audioservice的开始起源于systemserver 的startotherservice:
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
t.traceBegin("StartAudioService");
if (!isArc) {
mSystemServiceManager.startService(AudioService.Lifecycle.class);
} else {
String className = context.getResources()
.getString(R.string.config_deviceSpecificAudioService);
try {
mSystemServiceManager.startService(className + "$Lifecycle");
} catch (Throwable e) {
reportWtf("starting " + className, e);
}
}
t.traceEnd();
...
}
先是systemserver启动,然后启动各种服务这里用的是SystemServiceManager(简称SSM)来startService,然后在SSM中通过反射来获取AudioService的子类Lifecycle,接着调用Lifecycle的构造方法,AudioService就被创建了,然后再调用Lifecycle的onStart方法,audioservice会被注册到servicemanager中,最后在系统准备好之后会调用到startBootPhase,这个函数对应到Lifecycle的onBootPhase,接着到AudioService的systemReady。这就是AudioService的诞生过程,接着看AudioService的初始化:
// AudioService
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
// 开始获取一堆服务的代理
mContext = context;
mContentResolver = context.getContentResolver();
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
mAudioSystem = audioSystem;
mSystemServer = systemServer;
mSettings = settings;
mPlatformType = AudioSystem.getPlatformType(context);
mIsSingleVolume = AudioSystem.isSingleVolume(context);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mSensorPrivacyManagerInternal =
LocalServices.getService(SensorPrivacyManagerInternal.class);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
mSfxHelper = new SoundEffectsHelper(mContext);
mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
// 接着是获取一些音量,最大音量、最小音量、默认音量等等
...
...
// device 代理
mDeviceBroker = new AudioDeviceBroker(mContext, this);
mRecordMonitor = new RecordingActivityMonitor(mContext);
...
// 焦点控制
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
...
}
// AudioDeviceBroker
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
mAudioService = service;
// 这个蓝牙相关处理类
mBtHelper = new BtHelper(this);
mDeviceInventory = new AudioDeviceInventory(this);
mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
init();
}
// BtHelper
BtHelper(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
}
// AudioDeviceInventory
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
}
上面代码列出来和蓝牙相关处理的类,接下来是systemready的时候看下这些类都有哪些处理:
systemReady() {
sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
0, 0, null, 0); ------> onSystemReady
public void onSystemReady() {
mSystemReady = true;
scheduleLoadSoundEffects();
mDeviceBroker.onSystemReady();
...
}
// AudioDeviceBroker
/*package*/ void onSystemReady() {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mModeOwnerPid = mAudioService.getModeOwnerPid();
mBtHelper.onSystemReady();
}
}
}
// BtHelper
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
if (AudioService.DEBUG_SCO) {
Log.i(TAG, "In onSystemReady(), calling resetBluetoothSco()");
}
resetBluetoothSco();
getBluetoothHeadset();
//FIXME: this is to maintain compatibility with deprecated intent
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
sendStickyBroadcastToAll(newIntent);
// 通过BluetoothAdapter 注册了三个链接类型的回调A2DP/HEARING_AID/LE_AUDIO,我们只关注A2DP
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
// 这个是监听相关协议连接断开的标准函数
adapter.getProfileProxy(mDeviceBroker.getContext(),
mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
adapter.getProfileProxy(mDeviceBroker.getContext(),
mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
adapter.getProfileProxy(mDeviceBroker.getContext(),
mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO);
}
}
// BtHelper
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
// 这个接口监听了服务的两种状态分别在服务连接和断开时会回调
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (AudioService.DEBUG_SCO) {
Log.i(TAG, "In onServiceConnected(), profile: " + profile +
", proxy: "+proxy);
}
switch(profile) {
case BluetoothProfile.A2DP:
case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"BT profile service: connecting "
+ BluetoothProfile.getProfileName(profile) + " profile"));
mDeviceBroker.postBtProfileConnected(profile, proxy);
break;
default:
break;
}
}
public void onServiceDisconnected(int profile) {
Log.i(TAG, "In onServiceDisconnected(), profile: " + profile);
switch (profile) {
case BluetoothProfile.A2DP:
case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
case BluetoothProfile.LE_AUDIO_BROADCAST:
mDeviceBroker.postBtProfileDisconnected(profile);
break;
default:
break;
}
}
};
在systemready之后BThelper便会去监听A2DP连接和断开device的状态,并把这两个状态会分别传递给音频处理分别通过函数mDeviceBroker.postBtProfileConnected(profile, proxy)和mDeviceBroker.postBtProfileDisconnected(profile),这两个函数实现是在AudioDeviceBroker,分别看下这两个函数的具体实现先看postBtProfileConnected:
// AudioDeviceBroker
/*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) {
sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy); ----> BrokerHandler.handleMessage.what = MSG_IL_BT_SERVICE_CONNECTED_PROFILE
}
BrokerHandler.handleMessage {
...
case MSG_IL_BT_SERVICE_CONNECTED_PROFILE:
// 此处我们参数是A2DP所以走IF
if (msg.arg1 != BluetoothProfile.HEADSET) {
synchronized (mDeviceStateLock) {
mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj);
}
} else {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
}
}
}
break;
...
}
// BtHelper
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
// 不是HEADSET
if (profile == BluetoothProfile.HEADSET) {
onHeadsetProfileConnected((BluetoothHeadset) proxy);
return;
}
// A2DP
if (profile == BluetoothProfile.A2DP) {
mA2dp = (BluetoothA2dp) proxy;
} else if (profile == BluetoothProfile.HEARING_AID) {
mHearingAid = (BluetoothHearingAid) proxy;
} else if (profile == BluetoothProfile.LE_AUDIO) {
mLeAudio = (BluetoothLeAudio) proxy;
}
// 获取蓝牙连接的设备
final List<BluetoothDevice> deviceList = proxy.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
// 获取设备状态是STATE_CONNECTED,已连接
if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
new BluetoothProfileConnectionInfo(profile),
"mBluetoothProfileServiceListener"));
} else {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
new BluetoothProfileConnectionInfo(profile),
"mBluetoothProfileServiceListener"));
}
}
// AudioDeviceBroker
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
&& data.mPreviousDevice.equals(data.mNewDevice)) {
final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+ "queueOnBluetoothActiveDeviceChanged_update")
.set(MediaMetrics.Property.NAME, name)
.set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
.record();
synchronized (mDeviceStateLock) {
postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
}
} else {
// else 符合
synchronized (mDeviceStateLock) {
if (data.mPreviousDevice != null) {
btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED,
data);
sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
createBtDeviceInfo(data, data.mPreviousDevice,
BluetoothProfile.STATE_DISCONNECTED));
}
// connect 连接状态
if (data.mNewDevice != null) {
btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data);
sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE,
createBtDeviceInfo(data, data.mNewDevice,
BluetoothProfile.STATE_CONNECTED));
}
}
}
}
// AudioDeviceBroker
case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
if (info.mDevice == null) break;
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
"msg: onBluetoothActiveDeviceChange "
+ " state=" + info.mState
// only querying address as this is the only readily available
// field on the device
+ " addr=" + info.mDevice.getAddress()
+ " prof=" + info.mProfile
+ " supprNoisy=" + info.mSupprNoisy
+ " src=" + info.mEventSource
)).printLog(TAG));
synchronized (mDeviceStateLock) {
mDeviceInventory.setBluetoothActiveDevice(info);
}
} break;
// AudioDeviceInventory
public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) {
int delay;
synchronized (mDevicesLock) {
if (!info.mSupprNoisy
&& (((info.mProfile == BluetoothProfile.LE_AUDIO
|| info.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST)
&& info.mIsLeOutput)
|| info.mProfile == BluetoothProfile.HEARING_AID
|| info.mProfile == BluetoothProfile.A2DP)) {
@AudioService.ConnectionState int asState =
(info.mState == BluetoothProfile.STATE_CONNECTED)
? AudioService.CONNECTION_STATE_CONNECTED
: AudioService.CONNECTION_STATE_DISCONNECTED;
delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState,
info.mMusicDevice);
} else {
delay = 0;
}
if (AudioService.DEBUG_DEVICES) {
Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice
+ " profile: " + BluetoothProfile.getProfileName(info.mProfile)
+ " state: " + BluetoothProfile.getConnectionStateName(info.mState)
+ " delay(ms): " + delay
+ " codec:" + Integer.toHexString(info.mCodec)
+ " suppressNoisyIntent: " + info.mSupprNoisy);
}
// 激活蓝牙设备
mDeviceBroker.postBluetoothActiveDevice(info, delay);
if (info.mProfile == BluetoothProfile.HEARING_AID
&& info.mState == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
"HEARING_AID set to CONNECTED");
}
}
return delay;
}
// AudioDeviceBroker
/*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) {
sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay);
}
// AudioDeviceBroker.handleMessage
case MSG_L_SET_BT_ACTIVE_DEVICE:
synchronized (mDeviceStateLock) {
BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
mDeviceInventory.onSetBtActiveDevice(btInfo,
(btInfo.mProfile != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
? mAudioService.getBluetoothContextualVolumeStream()
: AudioSystem.STREAM_DEFAULT);
}
break;
// AudioDeviceInventory
void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
...
synchronized (mDevicesLock) {
final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address);
final DeviceInfo di = mConnectedDevices.get(key);
final boolean isConnected = di != null;
final boolean switchToUnavailable = isConnected
&& btInfo.mState != BluetoothProfile.STATE_CONNECTED;
final boolean switchToAvailable = !isConnected
&& btInfo.mState == BluetoothProfile.STATE_CONNECTED;
switch (btInfo.mProfile) {
...
case BluetoothProfile.A2DP:
if (switchToUnavailable) {
// 蓝牙设备断开
makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
} else if (switchToAvailable) {
// device is not already connected
if (btInfo.mVolume != -1) {
mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
// convert index to internal representation in VolumeStreamState
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
}
// 蓝牙设备连接
makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
"onSetBtActiveDevice", btInfo.mCodec);
}
break;
...
}
}
}
以上代码是蓝牙设备通过A2DP连接之后触发音频这边的激活的过程,蓝牙断开的过程类似,最后他们都会到类AudioDeviceInventory中,由两个函数处理:makeA2dpDeviceAvailable 和makeA2dpDeviceUnavailableNow,因为过程类似,断开的过程不予展示直接看这两个函数:
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
int a2dpCodec) {
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
// 激活蓝牙设备,最终是通过AudioSystem.setForceUse强制切换usecase
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
// at this point there could be another A2DP device already connected in APM, but it
// doesn't matter as this new one will overwrite the previous one
// 设置设备的连接状态,最后会到AudioPolicyManager中处理
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"APM failed to make available A2DP device addr=" + address
+ " error=" + res).printLog(TAG));
// If error is audioserver died,add device to the list,so that during restart AS will
// restore by triggering onRestoreDevices to add A2DP device to APM by calling
// setDeviceConnection
if (res != AudioSystem.AUDIO_STATUS_SERVER_DIED) {
return;
}
} else {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"A2DP device addr=" + address + " now available").printLog(TAG));
}
// The convention for head tracking sensors associated with A2DP devices is to
// use a UUID derived from the MAC address as follows:
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address, a2dpCodec, sensorUuid);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
// calling AudioSystem
mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey);
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
}
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
.set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec))
.set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow");
if (address == null) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "address null").record();
return;
}
final String deviceToRemoveKey =
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
mConnectedDevices.remove(deviceToRemoveKey);
if (!deviceToRemoveKey
.equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
"A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
return;
}
// device to remove was visible by APM, update APM
mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"APM failed to make unavailable A2DP device addr=" + address
+ " error=" + res).printLog(TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
"A2DP device addr=" + address + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
mmi.record();
}
上面两个函数其实使用的方法都差不多达到相反的目的,其最后的处理都是交由给AudioPolicyManager,可见apm在音频中扮演的角色应该是控制流的总负责,大部分控制流都要经过它。在激活蓝牙设备的时候会强制切换usecase,之后蓝牙设备的状态都是通过AudioSystem.setDeviceConnectionState来修改,可见这个函数在这一过程中也是扮演很重要的角色,至于它主要做了什么可能还要等后续做深入分析,目前蓝牙控制音频usecase的地方就这些,请待后续更新