MidiManager.java revision 0ca998d79fe95ded26ec4ff0a1e0a8dc4802684b
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.midi;
18
19import android.bluetooth.BluetoothDevice;
20import android.os.Binder;
21import android.os.IBinder;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.RemoteException;
25import android.util.Log;
26
27import java.util.concurrent.ConcurrentHashMap;
28
29/**
30 * This class is the public application interface to the MIDI service.
31 *
32 * <p>You can obtain an instance of this class by calling
33 * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
34 *
35 * {@samplecode
36 * MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);}
37 */
38public final class MidiManager {
39    private static final String TAG = "MidiManager";
40
41    /**
42     * Intent for starting BluetoothMidiService
43     * @hide
44     */
45    public static final String BLUETOOTH_MIDI_SERVICE_INTENT =
46                "android.media.midi.BluetoothMidiService";
47
48    /**
49     * BluetoothMidiService package name
50     * @hide
51     */
52    public static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
53
54    /**
55     * BluetoothMidiService class name
56     * @hide
57     */
58    public static final String BLUETOOTH_MIDI_SERVICE_CLASS =
59                "com.android.bluetoothmidiservice.BluetoothMidiService";
60
61    private final IMidiManager mService;
62    private final IBinder mToken = new Binder();
63
64    private ConcurrentHashMap<DeviceCallback,DeviceListener> mDeviceListeners =
65        new ConcurrentHashMap<DeviceCallback,DeviceListener>();
66
67    // Binder stub for receiving device notifications from MidiService
68    private class DeviceListener extends IMidiDeviceListener.Stub {
69        private final DeviceCallback mCallback;
70        private final Handler mHandler;
71
72        public DeviceListener(DeviceCallback callback, Handler handler) {
73            mCallback = callback;
74            mHandler = handler;
75        }
76
77        @Override
78        public void onDeviceAdded(MidiDeviceInfo device) {
79            if (mHandler != null) {
80                final MidiDeviceInfo deviceF = device;
81                mHandler.post(new Runnable() {
82                        @Override public void run() {
83                            mCallback.onDeviceAdded(deviceF);
84                        }
85                    });
86            } else {
87                mCallback.onDeviceAdded(device);
88            }
89        }
90
91        @Override
92        public void onDeviceRemoved(MidiDeviceInfo device) {
93            if (mHandler != null) {
94                final MidiDeviceInfo deviceF = device;
95                mHandler.post(new Runnable() {
96                        @Override public void run() {
97                            mCallback.onDeviceRemoved(deviceF);
98                        }
99                    });
100            } else {
101                mCallback.onDeviceRemoved(device);
102            }
103        }
104
105        @Override
106        public void onDeviceStatusChanged(MidiDeviceStatus status) {
107            if (mHandler != null) {
108                final MidiDeviceStatus statusF = status;
109                mHandler.post(new Runnable() {
110                        @Override public void run() {
111                            mCallback.onDeviceStatusChanged(statusF);
112                        }
113                    });
114            } else {
115                mCallback.onDeviceStatusChanged(status);
116            }
117        }
118    }
119
120    /**
121     * Callback class used for clients to receive MIDI device added and removed notifications
122     */
123    public static class DeviceCallback {
124        /**
125         * Called to notify when a new MIDI device has been added
126         *
127         * @param device a {@link MidiDeviceInfo} for the newly added device
128         */
129        public void onDeviceAdded(MidiDeviceInfo device) {
130        }
131
132        /**
133         * Called to notify when a MIDI device has been removed
134         *
135         * @param device a {@link MidiDeviceInfo} for the removed device
136         */
137        public void onDeviceRemoved(MidiDeviceInfo device) {
138        }
139
140        /**
141         * Called to notify when the status of a MIDI device has changed
142         *
143         * @param status a {@link MidiDeviceStatus} for the changed device
144         */
145        public void onDeviceStatusChanged(MidiDeviceStatus status) {
146        }
147    }
148
149    /**
150     * Listener class used for receiving the results of {@link #openDevice} and
151     * {@link #openBluetoothDevice}
152     */
153    public interface OnDeviceOpenedListener {
154        /**
155         * Called to respond to a {@link #openDevice} request
156         *
157         * @param device a {@link MidiDevice} for opened device, or null if opening failed
158         */
159        abstract public void onDeviceOpened(MidiDevice device);
160    }
161
162    /**
163     * @hide
164     */
165    public MidiManager(IMidiManager service) {
166        mService = service;
167    }
168
169    /**
170     * Registers a callback to receive notifications when MIDI devices are added and removed.
171     *
172     * @param callback a {@link DeviceCallback} for MIDI device notifications
173     * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
174     *                device notifications. If handler is null, then the thread used for the
175     *                callback is unspecified.
176     */
177    public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
178        DeviceListener deviceListener = new DeviceListener(callback, handler);
179        try {
180            mService.registerListener(mToken, deviceListener);
181        } catch (RemoteException e) {
182            Log.e(TAG, "RemoteException in registerDeviceListener");
183            return;
184        }
185        mDeviceListeners.put(callback, deviceListener);
186    }
187
188    /**
189     * Unregisters a {@link DeviceCallback}.
190      *
191     * @param callback a {@link DeviceCallback} to unregister
192     */
193    public void unregisterDeviceCallback(DeviceCallback callback) {
194        DeviceListener deviceListener = mDeviceListeners.remove(callback);
195        if (deviceListener != null) {
196            try {
197                mService.unregisterListener(mToken, deviceListener);
198            } catch (RemoteException e) {
199                Log.e(TAG, "RemoteException in unregisterDeviceListener");
200            }
201        }
202    }
203
204    /**
205     * Gets the list of all connected MIDI devices.
206     *
207     * @return an array of all MIDI devices
208     */
209    public MidiDeviceInfo[] getDevices() {
210        try {
211           return mService.getDevices();
212        } catch (RemoteException e) {
213            Log.e(TAG, "RemoteException in getDevices");
214            return new MidiDeviceInfo[0];
215        }
216    }
217
218    private void sendOpenDeviceResponse(final MidiDevice device,
219            final OnDeviceOpenedListener listener, Handler handler) {
220        if (handler != null) {
221            handler.post(new Runnable() {
222                    @Override public void run() {
223                        listener.onDeviceOpened(device);
224                    }
225                });
226        } else {
227            listener.onDeviceOpened(device);
228        }
229    }
230
231    /**
232     * Opens a MIDI device for reading and writing.
233     *
234     * @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open
235     * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
236     *                 to receive the result
237     * @param handler the {@link android.os.Handler Handler} that will be used for delivering
238     *                the result. If handler is null, then the thread used for the
239     *                listener is unspecified.
240     */
241    public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,
242            Handler handler) {
243        final MidiDeviceInfo deviceInfoF = deviceInfo;
244        final OnDeviceOpenedListener listenerF = listener;
245        final Handler handlerF = handler;
246
247        IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
248            @Override
249            public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
250                MidiDevice device;
251                if (server != null) {
252                    device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
253                } else {
254                    device = null;
255                }
256                sendOpenDeviceResponse(device, listenerF, handlerF);
257            }
258        };
259
260        try {
261            mService.openDevice(mToken, deviceInfo, callback);
262        } catch (RemoteException e) {
263            Log.e(TAG, "RemoteException in openDevice");
264        }
265    }
266
267    /**
268     * Opens a Bluetooth MIDI device for reading and writing.
269     *
270     * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} to open as a MIDI device
271     * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called to receive the
272     * result
273     * @param handler the {@link android.os.Handler Handler} that will be used for delivering
274     *                the result. If handler is null, then the thread used for the
275     *                listener is unspecified.
276     */
277    public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
278            OnDeviceOpenedListener listener, Handler handler) {
279        final OnDeviceOpenedListener listenerF = listener;
280        final Handler handlerF = handler;
281
282        IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
283            @Override
284            public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
285                MidiDevice device = null;
286                if (server != null) {
287                    try {
288                        // fetch MidiDeviceInfo from the server
289                        MidiDeviceInfo deviceInfo = server.getDeviceInfo();
290                        device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
291                    } catch (RemoteException e) {
292                        Log.e(TAG, "remote exception in getDeviceInfo()");
293                    }
294                }
295                sendOpenDeviceResponse(device, listenerF, handlerF);
296            }
297        };
298
299        try {
300            mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
301        } catch (RemoteException e) {
302            Log.e(TAG, "RemoteException in openDevice");
303        }
304    }
305
306    /** @hide */
307    public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
308            int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
309            Bundle properties, int type, MidiDeviceServer.Callback callback) {
310        try {
311            MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
312                    numOutputPorts, callback);
313            MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
314                    inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
315                    properties, type);
316            if (deviceInfo == null) {
317                Log.e(TAG, "registerVirtualDevice failed");
318                return null;
319            }
320            return server;
321        } catch (RemoteException e) {
322            Log.e(TAG, "RemoteException in createVirtualDevice");
323            return null;
324        }
325    }
326}
327