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 17 package android.media.midi; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.os.Binder; 21 import android.os.IBinder; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import 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 */ 38 public 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 sendOpenDeviceResponse(device, listenerF, handlerF); 292 } catch (RemoteException e) { 293 Log.e(TAG, "remote exception in getDeviceInfo()"); 294 } 295 } 296 sendOpenDeviceResponse(device, listenerF, handlerF); 297 } 298 }; 299 300 try { 301 mService.openBluetoothDevice(mToken, bluetoothDevice, callback); 302 } catch (RemoteException e) { 303 Log.e(TAG, "RemoteException in openDevice"); 304 } 305 } 306 307 /** @hide */ 308 public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers, 309 int numOutputPorts, String[] inputPortNames, String[] outputPortNames, 310 Bundle properties, int type, MidiDeviceServer.Callback callback) { 311 try { 312 MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers, 313 numOutputPorts, callback); 314 MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(), 315 inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames, 316 properties, type); 317 if (deviceInfo == null) { 318 Log.e(TAG, "registerVirtualDevice failed"); 319 return null; 320 } 321 return server; 322 } catch (RemoteException e) { 323 Log.e(TAG, "RemoteException in createVirtualDevice"); 324 return null; 325 } 326 } 327 } 328