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