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
17package android.media;
18
19import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22import java.util.ArrayList;
23import java.lang.ref.WeakReference;
24
25/**
26 * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks
27 * posted from JNI
28 * @hide
29 */
30
31class AudioPortEventHandler {
32    private Handler mHandler;
33    private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners =
34            new ArrayList<AudioManager.OnAudioPortUpdateListener>();
35
36    private static final String TAG = "AudioPortEventHandler";
37
38    private static final int AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1;
39    private static final int AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2;
40    private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
41    private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
42
43    /**
44     * Accessed by native methods: JNI Callback context.
45     */
46    @SuppressWarnings("unused")
47    private long mJniCallback;
48
49    void init() {
50        synchronized (this) {
51            if (mHandler != null) {
52                return;
53            }
54            // find the looper for our new event handler
55            Looper looper = Looper.getMainLooper();
56
57            if (looper != null) {
58                mHandler = new Handler(looper) {
59                    @Override
60                    public void handleMessage(Message msg) {
61                        ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
62                        synchronized (this) {
63                            if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
64                                listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
65                                if (mListeners.contains(msg.obj)) {
66                                    listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
67                                }
68                            } else {
69                                listeners = mListeners;
70                            }
71                        }
72                        // reset audio port cache if the event corresponds to a change coming
73                        // from audio policy service or if mediaserver process died.
74                        if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
75                                msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
76                                msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
77                            AudioManager.resetAudioPortGeneration();
78                        }
79
80                        if (listeners.isEmpty()) {
81                            return;
82                        }
83
84                        ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
85                        ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
86                        if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
87                            int status = AudioManager.updateAudioPortCache(ports, patches, null);
88                            if (status != AudioManager.SUCCESS) {
89                                return;
90                            }
91                        }
92
93                        switch (msg.what) {
94                        case AUDIOPORT_EVENT_NEW_LISTENER:
95                        case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
96                            AudioPort[] portList = ports.toArray(new AudioPort[0]);
97                            for (int i = 0; i < listeners.size(); i++) {
98                                listeners.get(i).onAudioPortListUpdate(portList);
99                            }
100                            if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
101                                break;
102                            }
103                            // FALL THROUGH
104
105                        case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
106                            AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
107                            for (int i = 0; i < listeners.size(); i++) {
108                                listeners.get(i).onAudioPatchListUpdate(patchList);
109                            }
110                            break;
111
112                        case AUDIOPORT_EVENT_SERVICE_DIED:
113                            for (int i = 0; i < listeners.size(); i++) {
114                                listeners.get(i).onServiceDied();
115                            }
116                            break;
117
118                        default:
119                            break;
120                        }
121                    }
122                };
123                native_setup(new WeakReference<AudioPortEventHandler>(this));
124            } else {
125                mHandler = null;
126            }
127        }
128    }
129
130    private native void native_setup(Object module_this);
131
132    @Override
133    protected void finalize() {
134        native_finalize();
135    }
136    private native void native_finalize();
137
138    void registerListener(AudioManager.OnAudioPortUpdateListener l) {
139        synchronized (this) {
140            mListeners.add(l);
141        }
142        if (mHandler != null) {
143            Message m = mHandler.obtainMessage(AUDIOPORT_EVENT_NEW_LISTENER, 0, 0, l);
144            mHandler.sendMessage(m);
145        }
146    }
147
148    void unregisterListener(AudioManager.OnAudioPortUpdateListener l) {
149        synchronized (this) {
150            mListeners.remove(l);
151        }
152    }
153
154    Handler handler() {
155        return mHandler;
156    }
157
158    @SuppressWarnings("unused")
159    private static void postEventFromNative(Object module_ref,
160                                            int what, int arg1, int arg2, Object obj) {
161        AudioPortEventHandler eventHandler =
162                (AudioPortEventHandler)((WeakReference)module_ref).get();
163        if (eventHandler == null) {
164            return;
165        }
166
167        if (eventHandler != null) {
168            Handler handler = eventHandler.handler();
169            if (handler != null) {
170                Message m = handler.obtainMessage(what, arg1, arg2, obj);
171                handler.sendMessage(m);
172            }
173        }
174    }
175
176}
177