Files
frameworks_base/media/java/android/media/AudioPortEventHandler.java
jiabin c4ecaa52b8 Reduce time consuming when a lot of audio port event happen continually.
Since the AudioPortEventHandler runs in application's main thread, it
may cause ANR if there are a lot of setForceUse calling by the
application continually in a short time. The root cause is that there
may be a lot of audio port event from the native side when there are a
lot of device switching request. In this case, updating audio port cache
may take a long time due to inconsistency between audio port generation
and patches generation.
To solve this problem, we only check the generation once when ports and
patches are both requested to return earlier. In the meantime, we only
repeat the last audio port event since we will refresh the ports cache
every time when we receive the event.

Bug: 64952619
Test: keep switching output device in hangout video/voice chat
Change-Id: I3164c4e331950a481b76ce890d8c1403fd9b98ee
2017-09-27 14:26:08 -07:00

195 lines
7.6 KiB
Java

/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import java.util.ArrayList;
import java.lang.ref.WeakReference;
/**
* The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
* posted from JNI
* @hide
*/
class AudioPortEventHandler {
private Handler mHandler;
private HandlerThread mHandlerThread;
private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
new ArrayList<AudioManager.OnAudioPortUpdateListener>();
private static final String TAG = "AudioPortEventHandler";
private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
private static final long RESCHEDULE_MESSAGE_DELAY_MS = 100;
/**
* Accessed by native methods: JNI Callback context.
*/
@SuppressWarnings("unused")
private long mJniCallback;
void init() {
synchronized (this) {
if (mHandler != null) {
return;
}
// create a new thread for our new event handler
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
if (mHandlerThread.getLooper() != null) {
mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
synchronized (this) {
if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
if (mListeners.contains(msg.obj)) {
listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
}
} else {
listeners = mListeners;
}
}
// reset audio port cache if the event corresponds to a change coming
// from audio policy service or if mediaserver process died.
if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
AudioManager.resetAudioPortGeneration();
}
if (listeners.isEmpty()) {
return;
}
ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
int status = AudioManager.updateAudioPortCache(ports, patches, null);
if (status != AudioManager.SUCCESS) {
// Since audio ports and audio patches are not null, the return
// value could be ERROR due to inconsistency between port generation
// and patch generation. In this case, we need to reschedule the
// message to make sure the native callback is done.
sendMessageDelayed(obtainMessage(msg.what, msg.obj),
RESCHEDULE_MESSAGE_DELAY_MS);
return;
}
}
switch (msg.what) {
case AUDIOPORT_EVENT_NEW_LISTENER:
case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
AudioPort[] portList = ports.toArray(new AudioPort[0]);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onAudioPortListUpdate(portList);
}
if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
break;
}
// FALL THROUGH
case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onAudioPatchListUpdate(patchList);
}
break;
case AUDIOPORT_EVENT_SERVICE_DIED:
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onServiceDied();
}
break;
default:
break;
}
}
};
native_setup(new WeakReference<AudioPortEventHandler>(this));
} else {
mHandler = null;
}
}
}
private native void native_setup(Object module_this);
@Override
protected void finalize() {
native_finalize();
if (mHandlerThread.isAlive()) {
mHandlerThread.quit();
}
}
private native void native_finalize();
void registerListener(AudioManager.OnAudioPortUpdateListener l) {
synchronized (this) {
mListeners.add(l);
}
if (mHandler != null) {
Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
mHandler.sendMessage(m);
}
}
void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
synchronized (this) {
mListeners.remove(l);
}
}
Handler handler() {
return mHandler;
}
@SuppressWarnings("unused")
private static void postEventFromNative(Object module_ref,
int what, int arg1, int arg2, Object obj) {
AudioPortEventHandler eventHandler =
(AudioPortEventHandler)((WeakReference)module_ref).get();
if (eventHandler == null) {
return;
}
if (eventHandler != null) {
Handler handler = eventHandler.handler();
if (handler != null) {
Message m = handler.obtainMessage(what, arg1, arg2, obj);
if (what != AUDIOPORT_EVENT_NEW_LISTENER) {
// Except AUDIOPORT_EVENT_NEW_LISTENER, we can only respect the last message.
handler.removeMessages(what);
}
handler.sendMessage(m);
}
}
}
}