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