1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import android.os.Handler; 20 import android.os.HandlerThread; 21 import android.os.Looper; 22 import android.os.Message; 23 import java.util.ArrayList; 24 import java.lang.ref.WeakReference; 25 26 /** 27 * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks 28 * posted from JNI 29 * @hide 30 */ 31 32 class AudioPortEventHandler { 33 private Handler mHandler; 34 private HandlerThread mHandlerThread; 35 private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners = 36 new ArrayList<AudioManager.OnAudioPortUpdateListener>(); 37 38 private static final String TAG = "AudioPortEventHandler"; 39 40 private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1; 41 private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2; 42 private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3; 43 private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4; 44 45 private static final long RESCHEDULE_MESSAGE_DELAY_MS = 100; 46 47 /** 48 * Accessed by native methods: JNI Callback context. 49 */ 50 @SuppressWarnings("unused") 51 private long mJniCallback; 52 init()53 void init() { 54 synchronized (this) { 55 if (mHandler != null) { 56 return; 57 } 58 // create a new thread for our new event handler 59 mHandlerThread = new HandlerThread(TAG); 60 mHandlerThread.start(); 61 62 if (mHandlerThread.getLooper() != null) { 63 mHandler = new Handler(mHandlerThread.getLooper()) { 64 @Override 65 public void handleMessage(Message msg) { 66 ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; 67 synchronized (this) { 68 if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { 69 listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); 70 if (mListeners.contains(msg.obj)) { 71 listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); 72 } 73 } else { 74 listeners = mListeners; 75 } 76 } 77 // reset audio port cache if the event corresponds to a change coming 78 // from audio policy service or if mediaserver process died. 79 if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || 80 msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED || 81 msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { 82 AudioManager.resetAudioPortGeneration(); 83 } 84 85 if (listeners.isEmpty()) { 86 return; 87 } 88 89 ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); 90 ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 91 if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { 92 int status = AudioManager.updateAudioPortCache(ports, patches, null); 93 if (status != AudioManager.SUCCESS) { 94 // Since audio ports and audio patches are not null, the return 95 // value could be ERROR due to inconsistency between port generation 96 // and patch generation. In this case, we need to reschedule the 97 // message to make sure the native callback is done. 98 sendMessageDelayed(obtainMessage(msg.what, msg.obj), 99 RESCHEDULE_MESSAGE_DELAY_MS); 100 return; 101 } 102 } 103 104 switch (msg.what) { 105 case AUDIOPORT_EVENT_NEW_LISTENER: 106 case AUDIOPORT_EVENT_PORT_LIST_UPDATED: 107 AudioPort[] portList = ports.toArray(new AudioPort[0]); 108 for (int i = 0; i < listeners.size(); i++) { 109 listeners.get(i).onAudioPortListUpdate(portList); 110 } 111 if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) { 112 break; 113 } 114 // FALL THROUGH 115 116 case AUDIOPORT_EVENT_PATCH_LIST_UPDATED: 117 AudioPatch[] patchList = patches.toArray(new AudioPatch[0]); 118 for (int i = 0; i < listeners.size(); i++) { 119 listeners.get(i).onAudioPatchListUpdate(patchList); 120 } 121 break; 122 123 case AUDIOPORT_EVENT_SERVICE_DIED: 124 for (int i = 0; i < listeners.size(); i++) { 125 listeners.get(i).onServiceDied(); 126 } 127 break; 128 129 default: 130 break; 131 } 132 } 133 }; 134 native_setup(new WeakReference<AudioPortEventHandler>(this)); 135 } else { 136 mHandler = null; 137 } 138 } 139 } 140 native_setup(Object module_this)141 private native void native_setup(Object module_this); 142 143 @Override finalize()144 protected void finalize() { 145 native_finalize(); 146 if (mHandlerThread.isAlive()) { 147 mHandlerThread.quit(); 148 } 149 } native_finalize()150 private native void native_finalize(); 151 registerListener(AudioManager.OnAudioPortUpdateListener l)152 void registerListener(AudioManager.OnAudioPortUpdateListener l) { 153 synchronized (this) { 154 mListeners.add(l); 155 } 156 if (mHandler != null) { 157 Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l); 158 mHandler.sendMessage(m); 159 } 160 } 161 unregisterListener(AudioManager.OnAudioPortUpdateListener l)162 void unregisterListener(AudioManager.OnAudioPortUpdateListener l) { 163 synchronized (this) { 164 mListeners.remove(l); 165 } 166 } 167 handler()168 Handler handler() { 169 return mHandler; 170 } 171 172 @SuppressWarnings("unused") postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj)173 private static void postEventFromNative(Object module_ref, 174 int what, int arg1, int arg2, Object obj) { 175 AudioPortEventHandler eventHandler = 176 (AudioPortEventHandler)((WeakReference)module_ref).get(); 177 if (eventHandler == null) { 178 return; 179 } 180 181 if (eventHandler != null) { 182 Handler handler = eventHandler.handler(); 183 if (handler != null) { 184 Message m = handler.obtainMessage(what, arg1, arg2, obj); 185 if (what != AUDIOPORT_EVENT_NEW_LISTENER) { 186 // Except AUDIOPORT_EVENT_NEW_LISTENER, we can only respect the last message. 187 handler.removeMessages(what); 188 } 189 handler.sendMessage(m); 190 } 191 } 192 } 193 194 } 195