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