/*
 * 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
/** * 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.
/** * 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); } } } }