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