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