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 * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately 173 * for any devices that have open ports. This allows applications to know which input 174 * ports are already in use and, therefore, unavailable. 175 * 176 * Applications should call {@link #getDevices} before registering the callback 177 * to get a list of devices already added. 178 * 179 * @param callback a {@link DeviceCallback} for MIDI device notifications 180 * @param handler The {@link android.os.Handler Handler} that will be used for delivering the 181 * device notifications. If handler is null, then the thread used for the 182 * callback is unspecified. 183 */ 184 public void registerDeviceCallback(DeviceCallback callback, Handler handler) { 185 DeviceListener deviceListener = new DeviceListener(callback, handler); 186 try { 187 mService.registerListener(mToken, deviceListener); 188 } catch (RemoteException e) { 189 Log.e(TAG, "RemoteException in registerDeviceListener"); 190 return; 191 } 192 mDeviceListeners.put(callback, deviceListener); 193 } 194 195 /** 196 * Unregisters a {@link DeviceCallback}. 197 * 198 * @param callback a {@link DeviceCallback} to unregister 199 */ 200 public void unregisterDeviceCallback(DeviceCallback callback) { 201 DeviceListener deviceListener = mDeviceListeners.remove(callback); 202 if (deviceListener != null) { 203 try { 204 mService.unregisterListener(mToken, deviceListener); 205 } catch (RemoteException e) { 206 Log.e(TAG, "RemoteException in unregisterDeviceListener"); 207 } 208 } 209 } 210 211 /** 212 * Gets the list of all connected MIDI devices. 213 * 214 * @return an array of all MIDI devices 215 */ 216 public MidiDeviceInfo[] getDevices() { 217 try { 218 return mService.getDevices(); 219 } catch (RemoteException e) { 220 Log.e(TAG, "RemoteException in getDevices"); 221 return new MidiDeviceInfo[0]; 222 } 223 } 224 225 private void sendOpenDeviceResponse(final MidiDevice device, 226 final OnDeviceOpenedListener listener, Handler handler) { 227 if (handler != null) { 228 handler.post(new Runnable() { 229 @Override public void run() { 230 listener.onDeviceOpened(device); 231 } 232 }); 233 } else { 234 listener.onDeviceOpened(device); 235 } 236 } 237 238 /** 239 * Opens a MIDI device for reading and writing. 240 * 241 * @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open 242 * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called 243 * to receive the result 244 * @param handler the {@link android.os.Handler Handler} that will be used for delivering 245 * the result. If handler is null, then the thread used for the 246 * listener is unspecified. 247 */ 248 public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener, 249 Handler handler) { 250 final MidiDeviceInfo deviceInfoF = deviceInfo; 251 final OnDeviceOpenedListener listenerF = listener; 252 final Handler handlerF = handler; 253 254 IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() { 255 @Override 256 public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) { 257 MidiDevice device; 258 if (server != null) { 259 device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken); 260 } else { 261 device = null; 262 } 263 sendOpenDeviceResponse(device, listenerF, handlerF); 264 } 265 }; 266 267 try { 268 mService.openDevice(mToken, deviceInfo, callback); 269 } catch (RemoteException e) { 270 Log.e(TAG, "RemoteException in openDevice"); 271 } 272 } 273 274 /** 275 * Opens a Bluetooth MIDI device for reading and writing. 276 * 277 * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} to open as a MIDI device 278 * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called to receive the 279 * result 280 * @param handler the {@link android.os.Handler Handler} that will be used for delivering 281 * the result. If handler is null, then the thread used for the 282 * listener is unspecified. 283 */ 284 public void openBluetoothDevice(BluetoothDevice bluetoothDevice, 285 OnDeviceOpenedListener listener, Handler handler) { 286 final OnDeviceOpenedListener listenerF = listener; 287 final Handler handlerF = handler; 288 289 IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() { 290 @Override 291 public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) { 292 MidiDevice device = null; 293 if (server != null) { 294 try { 295 // fetch MidiDeviceInfo from the server 296 MidiDeviceInfo deviceInfo = server.getDeviceInfo(); 297 device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken); 298 } catch (RemoteException e) { 299 Log.e(TAG, "remote exception in getDeviceInfo()"); 300 } 301 } 302 sendOpenDeviceResponse(device, listenerF, handlerF); 303 } 304 }; 305 306 try { 307 mService.openBluetoothDevice(mToken, bluetoothDevice, callback); 308 } catch (RemoteException e) { 309 Log.e(TAG, "RemoteException in openDevice"); 310 } 311 } 312 313 /** @hide */ 314 public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers, 315 int numOutputPorts, String[] inputPortNames, String[] outputPortNames, 316 Bundle properties, int type, MidiDeviceServer.Callback callback) { 317 try { 318 MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers, 319 numOutputPorts, callback); 320 MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(), 321 inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames, 322 properties, type); 323 if (deviceInfo == null) { 324 Log.e(TAG, "registerVirtualDevice failed"); 325 return null; 326 } 327 return server; 328 } catch (RemoteException e) { 329 Log.e(TAG, "RemoteException in createVirtualDevice"); 330 return null; 331 } 332 } 333} 334