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