我们使用AudioManager最多的Api可能就是申请音频焦点和调节声音音量的了,其实随着Android版本的不断迭代,AudioManager的功能也是不断的完善和增加,那么今天我们就来分析一下AudioManager的getDevices
先上源码Api之getDevices
/**
* Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
* currently connected to the system and meeting the criteria specified in the
* flags
parameter.
* @param flags A set of bitflags specifying the criteria to test.
* @see #GET_DEVICES_OUTPUTS
* @see #GET_DEVICES_INPUTS
* @see #GET_DEVICES_ALL
* @return A (possibly zero-length) array of AudioDeviceInfo objects.
*/
public AudioDeviceInfo[] getDevices(int flags) {
return getDevicesStatic(flags);
}
通过注释我们知道flags可以是GET_DEVICES_OUTPUTS、GET_DEVICES_INPUTS或者GET_DEVICES_ALL,关于AudioDeviceInfo的属性这个我们可参照audio_policy_configuration.xml里的元素
<devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
address="bus0_media_out">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
</gains>
</devicePort>
主要有samplingRates、channelMasks、format、address、等,我们继续分析
public static AudioDeviceInfo[] getDevicesStatic(int flags) {
ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
int status = AudioManager.listAudioDevicePorts(ports);
if (status != AudioManager.SUCCESS) {
// fail and bail!
return new AudioDeviceInfo[0]; // Always return an array.
}
return infoListFromPortList(ports, flags);
}
这里分了两步,为了方便梳理时序,我们假设是 1 和2。第1步是通过AudioManager.listAudioDevicePorts(ports)去拿设备节点,第2步infoListFromPortList(ports, flags)去构造我们需要的AudioDeviceInfo[]我们先来看listAudioDevicePorts
public static int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
if (devices == null) {
return ERROR_BAD_VALUE;
}
ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
int status = updateAudioPortCache(ports, null, null);
if (status == SUCCESS) {
filterDevicePorts(ports, devices);
}
return status;
}
也是分了两步,假设1.1和1.2,我们还是一点一点分析,先看1.1 updateAudioPortCache
static int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches,
ArrayList<AudioPort> previousPorts) {
sAudioPortEventHandler.init();
synchronized (sAudioPortGeneration) {
if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
// 可能最近年纪大了,也可能是java学的比较烂,这块竟然纠结了好久,为什么要new int[1]而不是直接用int,
// 后来才明白原来是为了处理参数的传递。如果有不明白的自己百度吧关于java参数的传递
int[] patchGeneration = new int[1];
int[] portGeneration = new int[1];
int status;
// 又分了两步,拿ports和patches。port和patch又是啥呢,我个人理解port更像是device的描述,而patch是把source 和sink连起来了,把输入输出端串上。
ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
do {
newPorts.clear();
// 1.1.1
status = AudioSystem.listAudioPorts(newPorts, portGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
return status;
}
newPatches.clear();
// 1.1.2
status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
if (status != SUCCESS) {
Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
return status;
}
// Loop until patch generation is the same as port generation unless audio ports
// and audio patches are not null.
} while (patchGeneration[0] != portGeneration[0]
&& (ports == null || patches == null));
// If the patch generation doesn't equal port generation, return ERROR here in case
// of mismatch between audio ports and audio patches.
if (patchGeneration[0] != portGeneration[0]) {
return ERROR;
}
// 1.1.3
for (int i = 0; i < newPatches.size(); i++) {
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
newPorts);
newPatches.get(i).sources()[j] = portCfg;
}
for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
newPorts);
newPatches.get(i).sinks()[j] = portCfg;
}
}
for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) {
AudioPatch newPatch = i.next();
boolean hasInvalidPort = false;
for (AudioPortConfig portCfg : newPatch.sources()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
for (AudioPortConfig portCfg : newPatch.sinks()) {
if (portCfg == null) {
hasInvalidPort = true;
break;
}
}
if (hasInvalidPort) {
// Temporarily remove patches with invalid ports. One who created the patch
// is responsible for dealing with the port change.
i.remove();
}
}
sPreviousAudioPortsCached = sAudioPortsCached;
sAudioPortsCached = newPorts;
sAudioPatchesCached = newPatches;
sAudioPortGeneration = portGeneration[0];
}
if (ports != null) {
ports.clear();
ports.addAll(sAudioPortsCached);
}
if (patches != null) {
patches.clear();
patches.addAll(sAudioPatchesCached);
}
if (previousPorts != null) {
previousPorts.clear();
previousPorts.addAll(sPreviousAudioPortsCached);
}
}
return SUCCESS;
}
先看下 1.1.1AudioSystem.listAudioPorts(newPorts, portGeneration)通过AudioSystem直接调到了jni
android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz,
jobject jPorts, jintArray jGeneration)
{
ALOGV("listAudioPorts");
if (jPorts == NULL) {
ALOGE("listAudioPorts NULL AudioPort ArrayList");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (!env->IsInstanceOf(jPorts, gArrayListClass)) {
ALOGE("listAudioPorts not an arraylist");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
return (jint)AUDIO_JAVA_BAD_VALUE;
}
status_t status;
unsigned int generation1;
unsigned int generation;
unsigned int numPorts;
jint *nGeneration;
struct audio_port *nPorts = NULL;
int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
jint jStatus;
// get the port count and all the ports until they both return the same generation
do {
if (attempts-- < 0) {
status = TIMED_OUT;
break;
}
numPorts = 0;
// 其实调用的是AudioPolicyManager的listAudioPorts,这次调用主要是遍历numPorts的数量
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
AUDIO_PORT_TYPE_NONE,
&numPorts,
NULL,
&generation1);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status);
break;
}
if (numPorts == 0) {
jStatus = (jint)AUDIO_JAVA_SUCCESS;
goto exit;
}
// 拿到numPorts数量后重新分配内存大小
nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port));
// // 真正遍历audioport
status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE,
AUDIO_PORT_TYPE_NONE,
&numPorts,
nPorts,
&generation);
ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d",
numPorts, generation, generation1);
} while (generation1 != generation && status == NO_ERROR);
jStatus = nativeToJavaStatus(status);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
for (size_t i = 0; i < numPorts; i++) {
jobject jAudioPort = NULL;
jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort);
if (jAudioPort != NULL) {
env->DeleteLocalRef(jAudioPort);
}
}
exit:
nGeneration = env->GetIntArrayElements(jGeneration, NULL);
if (nGeneration == NULL) {
jStatus = (jint)AUDIO_JAVA_ERROR;
} else {
nGeneration[0] = generation1;
env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
}
free(nPorts);
return jStatus;
}
通过jni我们看到调用了两次 AudioSystem::listAudioPorts,其实只有第二次才是真正的调用,第一次只是为了遍历audioport的数量以便重新分配内存。我们来具体看下AudioPolicyManager的listAudioPorts过程
status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
audio_port_type_t type,
unsigned int *num_ports,
struct audio_port *ports,
unsigned int *generation)
{
if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
generation == NULL) {
return BAD_VALUE;
}
ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
if (ports == NULL) {
*num_ports = 0;
}
size_t portsWritten = 0;
size_t portsMax = *num_ports;
*num_ports = 0;
if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_DEVICE) {
// do not report devices with type AUDIO_DEVICE_IN_STUB or AUDIO_DEVICE_OUT_STUB
// as they are used by stub HALs by convention
if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
for (const auto& dev : mAvailableOutputDevices) {
if (dev->type() == AUDIO_DEVICE_OUT_STUB) {
continue;
}
//第一次传入的portsMax为0.因此不会走dev->toAudioPort只会 (*num_ports)++
if (portsWritten < portsMax) {
dev->toAudioPort(&ports[portsWritten++]);
}
(*num_ports)++;
}
}
if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
for (const auto& dev : mAvailableInputDevices) {
if (dev->type() == AUDIO_DEVICE_IN_STUB) {
continue;
}
// //第一次传入的portsMax为0.因此不会走dev->toAudioPort只会 (*num_ports)++
if (portsWritten < portsMax) {
dev->toAudioPort(&ports[portsWritten++]);
}
(*num_ports)++;
}
}
}
if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_MIX) {
if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
for (size_t i = 0; i < mInputs.size() && portsWritten < portsMax; i++) {
mInputs[i]->toAudioPort(&ports[portsWritten++]);
}
*num_ports += mInputs.size();
}
if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
size_t numOutputs = 0;
for (size_t i = 0; i < mOutputs.size(); i++) {
if (!mOutputs[i]->isDuplicated()) {
numOutputs++;
if (portsWritten < portsMax) {
mOutputs[i]->toAudioPort(&ports[portsWritten++]);
}
}
}
*num_ports += numOutputs;
}
}
*generation = curAudioPortGeneration();
ALOGV("listAudioPorts() got %zu ports needed %d", portsWritten, *num_ports);
return NO_ERROR;
}
我们看到,因为第一次传入的*num_ports为0,即portsMax为0.因此不会走dev->toAudioPort只会 (*num_ports)++,所以只有第二次才会真正的toAudioPort,那么都加载了哪些设备?有mAvailableInputDevices和mOutputs以及mAvailableInputDevices和mInputs,其实最终也就是取了我们在audiopolicyManager初始化的时候解析xml的那些attachedDevices 对应的audioport和mixPort对应的audioport取出来了。
我们继续往回看,来分析下1.1.2listAudioPatches,也是先看jni的逻辑,代码有点长
android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz,
jobject jPatches, jintArray jGeneration)
{
ALOGV("listAudioPatches");
if (jPatches == NULL) {
ALOGE("listAudioPatches NULL AudioPatch ArrayList");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (!env->IsInstanceOf(jPatches, gArrayListClass)) {
ALOGE("listAudioPatches not an arraylist");
return (jint)AUDIO_JAVA_BAD_VALUE;
}
if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) {
return (jint)AUDIO_JAVA_BAD_VALUE;
}
status_t status;
unsigned int generation1;
unsigned int generation;
unsigned int numPatches;
jint *nGeneration;
struct audio_patch *nPatches = NULL;
jobjectArray jSources = NULL;
jobject jSource = NULL;
jobjectArray jSinks = NULL;
jobject jSink = NULL;
jobject jPatch = NULL;
int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS;
jint jStatus;
// get the patch count and all the patches until they both return the same generation
do {
if (attempts-- < 0) {
status = TIMED_OUT;
break;
}
numPatches = 0;
status = AudioSystem::listAudioPatches(&numPatches,
NULL,
&generation1);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d",
status);
break;
}
if (numPatches == 0) {
jStatus = (jint)AUDIO_JAVA_SUCCESS;
goto exit;
}
nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch));
status = AudioSystem::listAudioPatches(&numPatches,
nPatches,
&generation);
ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d",
numPatches, generation, generation1);
} while (generation1 != generation && status == NO_ERROR);
jStatus = nativeToJavaStatus(status);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
for (size_t i = 0; i < numPatches; i++) {
jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor,
nPatches[i].id);
if (patchHandle == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d",
i, nPatches[i].num_sources, nPatches[i].num_sinks);
env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id);
// load sources
jSources = env->NewObjectArray(nPatches[i].num_sources,
gAudioPortConfigClass, NULL);
if (jSources == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
for (size_t j = 0; j < nPatches[i].num_sources; j++) {
jStatus = convertAudioPortConfigFromNative(env,
NULL,
&jSource,
&nPatches[i].sources[j]);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
env->SetObjectArrayElement(jSources, j, jSource);
env->DeleteLocalRef(jSource);
jSource = NULL;
ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d",
i, j,
nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
nPatches[i].sources[j].id);
}
// load sinks
jSinks = env->NewObjectArray(nPatches[i].num_sinks,
gAudioPortConfigClass, NULL);
if (jSinks == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
for (size_t j = 0; j < nPatches[i].num_sinks; j++) {
jStatus = convertAudioPortConfigFromNative(env,
NULL,
&jSink,
&nPatches[i].sinks[j]);
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
env->SetObjectArrayElement(jSinks, j, jSink);
env->DeleteLocalRef(jSink);
jSink = NULL;
ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d",
i, j,
nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix",
nPatches[i].sinks[j].id);
}
jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor,
patchHandle, jSources, jSinks);
env->DeleteLocalRef(jSources);
jSources = NULL;
env->DeleteLocalRef(jSinks);
jSinks = NULL;
if (jPatch == NULL) {
jStatus = AUDIO_JAVA_ERROR;
goto exit;
}
env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch);
env->DeleteLocalRef(jPatch);
jPatch = NULL;
}
exit:
nGeneration = env->GetIntArrayElements(jGeneration, NULL);
if (nGeneration == NULL) {
jStatus = AUDIO_JAVA_ERROR;
} else {
nGeneration[0] = generation1;
env->ReleaseIntArrayElements(jGeneration, nGeneration, 0);
}
if (jSources != NULL) {
env->DeleteLocalRef(jSources);
}
if (jSource != NULL) {
env->DeleteLocalRef(jSource);
}
if (jSinks != NULL) {
env->DeleteLocalRef(jSinks);
}
if (jSink != NULL) {
env->DeleteLocalRef(jSink);
}
if (jPatch != NULL) {
env->DeleteLocalRef(jPatch);
}
free(nPatches);
return jStatus;
}
如果没啥耐心,不看也罢,基本跟toAudioPort逻辑一样,,不罗嗦了,我们这里看下AudioPolicyManager中的
status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches,
unsigned int *generation)
{
if (generation == NULL) {
return BAD_VALUE;
}
*generation = curAudioPortGeneration();
return mAudioPatches.listAudioPatches(num_patches, patches);
}
最终又调用到了AudioPatch中
status_t AudioPatchCollection::listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches) const
{
if (num_patches == NULL || (*num_patches != 0 && patches == NULL)) {
return BAD_VALUE;
}
ALOGV("listAudioPatches() num_patches %d patches %p available patches %zu",
*num_patches, patches, size());
if (patches == NULL) {
*num_patches = 0;
}
size_t patchesWritten = 0;
size_t patchesMax = *num_patches;
*num_patches = 0;
for (size_t patchIndex = 0; patchIndex < size(); patchIndex++) {
// do not report patches with AUDIO_DEVICE_IN_STUB as source or
// AUDIO_DEVICE_OUT_STUB as sink as those devices are used by stub HALs by convention
const sp<AudioPatch> patch = valueAt(patchIndex);
bool skip = false;
for (size_t srcIndex = 0; srcIndex < patch->mPatch.num_sources && !skip; srcIndex++) {
if (patch->mPatch.sources[srcIndex].type == AUDIO_PORT_TYPE_DEVICE &&
patch->mPatch.sources[srcIndex].ext.device.type == AUDIO_DEVICE_IN_STUB) {
skip = true;
}
}
for (size_t sinkIndex = 0; sinkIndex < patch->mPatch.num_sinks && !skip; sinkIndex++) {
if (patch->mPatch.sinks[sinkIndex].type == AUDIO_PORT_TYPE_DEVICE &&
patch->mPatch.sinks[sinkIndex].ext.device.type == AUDIO_DEVICE_OUT_STUB) {
skip = true;
}
}
if (skip) {
continue; // to next audio patch
}
if (patchesWritten < patchesMax) {
patches[patchesWritten] = patch->mPatch;
patches[patchesWritten++].id = patch->mHandle;
}
(*num_patches)++;
ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d",
patchIndex, patch->mPatch.num_sources, patch->mPatch.num_sinks);
}
ALOGV("listAudioPatches() got %zu patches needed %d", patchesWritten, *num_patches);
return NO_ERROR;
}
和audioport逻辑一样,这里不多说了。
代码有点多,简单总结下,从AudioManager的getDevices开始通过AudioSystem调到android_media_AudioSystem(jni)最终从AudioPolicyManager中拿到我们要的audioport和audiopatch。
下章我们继续向回分析,拿到数据后又是如何最终通过AudioManager返给应用的。
(以上分析如有错误,欢迎指正沟通~感激不尽)