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 = mListeners;
82                             }
83                         }
84                         // reset audio port cache if the event corresponds to a change coming
85                         // from audio policy service or if mediaserver process died.
86                         if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
87                                 msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
88                                 msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
89                             AudioManager.resetAudioPortGeneration();
90                         }
91 
92                         if (listeners.isEmpty()) {
93                             return;
94                         }
95 
96                         ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
97                         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
98                         if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
99                             int status = AudioManager.updateAudioPortCache(ports, patches, null);
100                             if (status != AudioManager.SUCCESS) {
101                                 // Since audio ports and audio patches are not null, the return
102                                 // value could be ERROR due to inconsistency between port generation
103                                 // and patch generation. In this case, we need to reschedule the
104                                 // message to make sure the native callback is done.
105                                 sendMessageDelayed(obtainMessage(msg.what, msg.obj),
106                                         RESCHEDULE_MESSAGE_DELAY_MS);
107                                 return;
108                             }
109                         }
110 
111                         switch (msg.what) {
112                         case AUDIOPORT_EVENT_NEW_LISTENER:
113                         case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
114                             AudioPort[] portList = ports.toArray(new AudioPort[0]);
115                             for (int i = 0; i < listeners.size(); i++) {
116                                 listeners.get(i).onAudioPortListUpdate(portList);
117                             }
118                             if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
119                                 break;
120                             }
121                             // FALL THROUGH
122 
123                         case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
124                             AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
125                             for (int i = 0; i < listeners.size(); i++) {
126                                 listeners.get(i).onAudioPatchListUpdate(patchList);
127                             }
128                             break;
129 
130                         case AUDIOPORT_EVENT_SERVICE_DIED:
131                             for (int i = 0; i < listeners.size(); i++) {
132                                 listeners.get(i).onServiceDied();
133                             }
134                             break;
135 
136                         default:
137                             break;
138                         }
139                     }
140                 };
141                 native_setup(new WeakReference<AudioPortEventHandler>(this));
142             } else {
143                 mHandler = null;
144             }
145         }
146     }
147 
native_setup(Object module_this)148     private native void native_setup(Object module_this);
149 
150     @Override
finalize()151     protected void finalize() {
152         native_finalize();
153         if (mHandlerThread.isAlive()) {
154             mHandlerThread.quit();
155         }
156     }
native_finalize()157     private native void native_finalize();
158 
registerListener(AudioManager.OnAudioPortUpdateListener l)159     void registerListener(AudioManager.OnAudioPortUpdateListener l) {
160         synchronized (mLock) {
161             mListeners.add(l);
162         }
163         if (mHandler != null) {
164             Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
165             mHandler.sendMessage(m);
166         }
167     }
168 
unregisterListener(AudioManager.OnAudioPortUpdateListener l)169     void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
170         synchronized (mLock) {
171             mListeners.remove(l);
172         }
173     }
174 
handler()175     Handler handler() {
176         return mHandler;
177     }
178 
179     @SuppressWarnings("unused")
180     @UnsupportedAppUsage
postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj)181     private static void postEventFromNative(Object module_ref,
182                                             int what, int arg1, int arg2, Object obj) {
183         AudioPortEventHandler eventHandler =
184                 (AudioPortEventHandler)((WeakReference)module_ref).get();
185         if (eventHandler == null) {
186             return;
187         }
188 
189         if (eventHandler != null) {
190             Handler handler = eventHandler.handler();
191             if (handler != null) {
192                 Message m = handler.obtainMessage(what, arg1, arg2, obj);
193                 if (what != AUDIOPORT_EVENT_NEW_LISTENER) {
194                     // Except AUDIOPORT_EVENT_NEW_LISTENER, we can only respect the last message.
195                     handler.removeMessages(what);
196                 }
197                 handler.sendMessage(m);
198             }
199         }
200     }
201 
202 }
203