• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Build;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Message;
24 
25 import com.android.internal.annotations.GuardedBy;
26 
27 import java.lang.ref.WeakReference;
28 import java.util.ArrayList;
29 
30 /**
31  * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
32  * posted from JNI
33  * @hide
34  */
35 
36 class AudioPortEventHandler {
37     private Handler mHandler;
38     private HandlerThread mHandlerThread;
39     private final Object mLock = new Object();
40 
41     @GuardedBy("mLock")
42     private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
43             new ArrayList<AudioManager.OnAudioPortUpdateListener>();
44 
45     private static final String TAG = "AudioPortEventHandler";
46 
47     private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
48     private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
49     private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
50     private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
51 
52     private static final long RESCHEDULE_MESSAGE_DELAY_MS = 100;
53 
54     /**
55      * Accessed by native methods: JNI Callback context.
56      */
57     @SuppressWarnings("unused")
58     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
59     private long mJniCallback;
60 
init()61     void init() {
62         synchronized (mLock) {
63             if (mHandler != null) {
64                 return;
65             }
66             // create a new thread for our new event handler
67             mHandlerThread = new HandlerThread(TAG);
68             mHandlerThread.start();
69 
70             if (mHandlerThread.getLooper() != null) {
71                 mHandler = new Handler(mHandlerThread.getLooper()) {
72                     @Override
73                     public void handleMessage(Message msg) {
74                         ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
75                         synchronized (mLock) {
76                             if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
77                                 listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
78                                 if (mListeners.contains(msg.obj)) {
79                                     listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
80                                 }
81                             } else {
82                                 listeners = (ArrayList<AudioManager.OnAudioPortUpdateListener>)
83                                         mListeners.clone();
84                             }
85                         }
86                         // reset audio port cache if the event corresponds to a change coming
87                         // from audio policy service or if mediaserver process died.
88                         if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
89                                 msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
90                                 msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
91                             AudioManager.resetAudioPortGeneration();
92                         }
93 
94                         if (listeners.isEmpty()) {
95                             return;
96                         }
97 
98                         ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
99                         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
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                         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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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