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