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