最近发现一个奇怪的bug,注册OnAudioPortUpdateListener后,却没有收到onAudioPatchListUpdate callback
debug后发现android在这一块有个bug
可以看下OnAudioPortUpdateListener的注册过程
AudioManager.java:
public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
sAudioPortEventHandler.init();
sAudioPortEventHandler.registerListener(l);
}
sAudioPortEventHandler是一个event handle,它接受来自native层的event,并调用OnAudioPortUpdateListener中的call,在它的Init函数中和native层联系起来
void init() {
synchronized (this) {
if (mHandler != null) {
return;
}
// find the looper for our new event handler
Looper looper = Looper.getMainLooper();
if (looper != null) {
mHandler = new Handler(looper) {
};
native_setup(new WeakReference<AudioPortEventHandler>(this));
} else {
mHandler = null;
}
}
}
native_setup会通过jni调到native层:
static void
android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("eventHandlerSetup");
sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
if (AudioSystem::addAudioPortCallback(callback) == NO_ERROR) {
setJniCallback(env, thiz, callback);
}
}
这里象调用了AudioSystem的addAudioPortCallback, JNIAudioPortCallback在收到natvie层的callback后,向java层的AudioPortEventHandler发送消息
status_t AudioSystem::addAudioPortCallback(const sp<AudioPortCallback>& callback)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
Mutex::Autolock _l(gLockAPS);
if (gAudioPolicyServiceClient == 0) {
return NO_INIT;
}
int ret = gAudioPolicyServiceClient->addAudioPortCallback(callback);
if (ret == 1) {
aps->setAudioPortCallbacksEnabled(true);
}
return (ret < 0) ? INVALID_OPERATION : NO_ERROR;
}
每个AudioSystem都是AudioPolicyService的一个client,在get_audio_policy_service()里会生成两个全局的binder,一个BpBinder,用于AudioSystem向AudioPolicyService发生消息,一个BnBinder,用于AudioPolicyService向AudioSystem发生消息,BnBinder就是AudioPolicyServiceClient. 然后把callback加到AudioPolicyServiceClient中去.
int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
const sp<AudioPortCallback>& callback)
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mAudioPortCallbacks.size(); i++) {
if (mAudioPortCallbacks[i] == callback) {
return -1;
}
}
mAudioPortCallbacks.add(callback);
return mAudioPortCallbacks.size();
}
bug马上要出现了,接下来看看 get_audio_policy_service()
// establish binder interface to AudioPolicy service
const sp<IAudioPolicyService> AudioSystem::get_audio_policy_service()
{
sp<IAudioPolicyService> ap;
sp<AudioPolicyServiceClient> apc;
{
Mutex::Autolock _l(gLockAPS);
if (gAudioPolicyService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.audio_policy"));
if (binder != 0)
break;
ALOGW("AudioPolicyService not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (gAudioPolicyServiceClient == NULL) {
gAudioPolicyServiceClient = new AudioPolicyServiceClient();
}
binder->linkToDeath(gAudioPolicyServiceClient);
gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
LOG_ALWAYS_FATAL_IF(gAudioPolicyService == 0);
apc = gAudioPolicyServiceClient;
}
ap = gAudioPolicyService;
}
if (apc != 0) {
ap->registerClient(apc);
}
return ap;
}
按照前面的解释,很好理解,为当前进程生成全局的BpBInder( gAudioPolicyService) 和BnBinder(gAudioPolicyServiceClient), 然后调用gAudioPolicyService->registerClient
// A notification client is always registered by AudioSystem when the client process
// connects to AudioPolicyService.
void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
{
Mutex::Autolock _l(mNotificationClientsLock);
uid_t uid = IPCThreadState::self()->getCallingUid();
if (mNotificationClients.indexOfKey(uid) < 0) {
sp<NotificationClient> notificationClient = new NotificationClient(this,
client,
uid);
ALOGV("registerClient() client %p, uid %d", client.get(), uid);
mNotificationClients.add(uid, notificationClient);
sp<IBinder> binder = IInterface::asBinder(client);
binder->linkToDeath(notificationClient);
}
}
问题就出在这里了,这里通过检查uid来查看client是否已经注册了,如果几个进程共享uid的话,这里的client就没有加mNotificationClients里去,也就不会收到callback,
可以看一下,这IAudioPolicyServiceClient目前只有三个callback,几乎也没有app使用,所以还没有什么问题.如果用进程号的话,应该能解决这个问题.
class IAudioPolicyServiceClient : public IInterface
{
public:
DECLARE_META_INTERFACE(AudioPolicyServiceClient);
// Notifies a change of audio port configuration.
virtual void onAudioPortListUpdate() = 0;
// Notifies a change of audio patch configuration.
virtual void onAudioPatchListUpdate() = 0;
// Notifies a change in the mixing state of a specific mix in a dynamic audio policy
virtual void onDynamicPolicyMixStateUpdate(String8 regId, int32_t state) = 0;
};
至于callack的过程,就是一个相反的过程,event的源头都是AudioPolicyManager中的openOutput, closeOutput, startOutput, openInput, closeInput, startInput
下一遍打算详细介绍下audioPatch...