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.annotation.SystemService; 20 import android.bluetooth.BluetoothDevice; 21 import android.content.Context; 22 import android.os.Binder; 23 import android.os.IBinder; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.util.concurrent.ConcurrentHashMap; 30 31 /** 32 * This class is the public application interface to the MIDI service. 33 */ 34 @SystemService(Context.MIDI_SERVICE) 35 public final class MidiManager { 36 private static final String TAG = "MidiManager"; 37 38 /** 39 * Intent for starting BluetoothMidiService 40 * @hide 41 */ 42 public static final String BLUETOOTH_MIDI_SERVICE_INTENT = 43 "android.media.midi.BluetoothMidiService"; 44 45 /** 46 * BluetoothMidiService package name 47 * @hide 48 */ 49 public static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice"; 50 51 /** 52 * BluetoothMidiService class name 53 * @hide 54 */ 55 public static final String BLUETOOTH_MIDI_SERVICE_CLASS = 56 "com.android.bluetoothmidiservice.BluetoothMidiService"; 57 58 private final IMidiManager mService; 59 private final IBinder mToken = new Binder(); 60 61 private ConcurrentHashMap<DeviceCallback,DeviceListener> mDeviceListeners = 62 new ConcurrentHashMap<DeviceCallback,DeviceListener>(); 63 64 // Binder stub for receiving device notifications from MidiService 65 private class DeviceListener extends IMidiDeviceListener.Stub { 66 private final DeviceCallback mCallback; 67 private final Handler mHandler; 68 DeviceListener(DeviceCallback callback, Handler handler)69 public DeviceListener(DeviceCallback callback, Handler handler) { 70 mCallback = callback; 71 mHandler = handler; 72 } 73 74 @Override onDeviceAdded(MidiDeviceInfo device)75 public void onDeviceAdded(MidiDeviceInfo device) { 76 if (mHandler != null) { 77 final MidiDeviceInfo deviceF = device; 78 mHandler.post(new Runnable() { 79 @Override public void run() { 80 mCallback.onDeviceAdded(deviceF); 81 } 82 }); 83 } else { 84 mCallback.onDeviceAdded(device); 85 } 86 } 87 88 @Override onDeviceRemoved(MidiDeviceInfo device)89 public void onDeviceRemoved(MidiDeviceInfo device) { 90 if (mHandler != null) { 91 final MidiDeviceInfo deviceF = device; 92 mHandler.post(new Runnable() { 93 @Override public void run() { 94 mCallback.onDeviceRemoved(deviceF); 95 } 96 }); 97 } else { 98 mCallback.onDeviceRemoved(device); 99 } 100 } 101 102 @Override onDeviceStatusChanged(MidiDeviceStatus status)103 public void onDeviceStatusChanged(MidiDeviceStatus status) { 104 if (mHandler != null) { 105 final MidiDeviceStatus statusF = status; 106 mHandler.post(new Runnable() { 107 @Override public void run() { 108 mCallback.onDeviceStatusChanged(statusF); 109 } 110 }); 111 } else { 112 mCallback.onDeviceStatusChanged(status); 113 } 114 } 115 } 116 117 /** 118 * Callback class used for clients to receive MIDI device added and removed notifications 119 */ 120 public static class DeviceCallback { 121 /** 122 * Called to notify when a new MIDI device has been added 123 * 124 * @param device a {@link MidiDeviceInfo} for the newly added device 125 */ onDeviceAdded(MidiDeviceInfo device)126 public void onDeviceAdded(MidiDeviceInfo device) { 127 } 128 129 /** 130 * Called to notify when a MIDI device has been removed 131 * 132 * @param device a {@link MidiDeviceInfo} for the removed device 133 */ onDeviceRemoved(MidiDeviceInfo device)134 public void onDeviceRemoved(MidiDeviceInfo device) { 135 } 136 137 /** 138 * Called to notify when the status of a MIDI device has changed 139 * 140 * @param status a {@link MidiDeviceStatus} for the changed device 141 */ onDeviceStatusChanged(MidiDeviceStatus status)142 public void onDeviceStatusChanged(MidiDeviceStatus status) { 143 } 144 } 145 146 /** 147 * Listener class used for receiving the results of {@link #openDevice} and 148 * {@link #openBluetoothDevice} 149 */ 150 public interface OnDeviceOpenedListener { 151 /** 152 * Called to respond to a {@link #openDevice} request 153 * 154 * @param device a {@link MidiDevice} for opened device, or null if opening failed 155 */ onDeviceOpened(MidiDevice device)156 abstract public void onDeviceOpened(MidiDevice device); 157 } 158 159 /** 160 * @hide 161 */ MidiManager(IMidiManager service)162 public MidiManager(IMidiManager service) { 163 mService = service; 164 } 165 166 /** 167 * Registers a callback to receive notifications when MIDI devices are added and removed. 168 * 169 * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately 170 * for any devices that have open ports. This allows applications to know which input 171 * ports are already in use and, therefore, unavailable. 172 * 173 * Applications should call {@link #getDevices} before registering the callback 174 * to get a list of devices already added. 175 * 176 * @param callback a {@link DeviceCallback} for MIDI device notifications 177 * @param handler The {@link android.os.Handler Handler} that will be used for delivering the 178 * device notifications. If handler is null, then the thread used for the 179 * callback is unspecified. 180 */ registerDeviceCallback(DeviceCallback callback, Handler handler)181 public void registerDeviceCallback(DeviceCallback callback, Handler handler) { 182 DeviceListener deviceListener = new DeviceListener(callback, handler); 183 try { 184 mService.registerListener(mToken, deviceListener); 185 } catch (RemoteException e) { 186 throw e.rethrowFromSystemServer(); 187 } 188 mDeviceListeners.put(callback, deviceListener); 189 } 190 191 /** 192 * Unregisters a {@link DeviceCallback}. 193 * 194 * @param callback a {@link DeviceCallback} to unregister 195 */ unregisterDeviceCallback(DeviceCallback callback)196 public void unregisterDeviceCallback(DeviceCallback callback) { 197 DeviceListener deviceListener = mDeviceListeners.remove(callback); 198 if (deviceListener != null) { 199 try { 200 mService.unregisterListener(mToken, deviceListener); 201 } catch (RemoteException e) { 202 throw e.rethrowFromSystemServer(); 203 } 204 } 205 } 206 207 /** 208 * Gets the list of all connected MIDI devices. 209 * 210 * @return an array of all MIDI devices 211 */ getDevices()212 public MidiDeviceInfo[] getDevices() { 213 try { 214 return mService.getDevices(); 215 } catch (RemoteException e) { 216 throw e.rethrowFromSystemServer(); 217 } 218 } 219 sendOpenDeviceResponse(final MidiDevice device, final OnDeviceOpenedListener listener, Handler handler)220 private void sendOpenDeviceResponse(final MidiDevice device, 221 final OnDeviceOpenedListener listener, Handler handler) { 222 if (handler != null) { 223 handler.post(new Runnable() { 224 @Override public void run() { 225 listener.onDeviceOpened(device); 226 } 227 }); 228 } else { 229 listener.onDeviceOpened(device); 230 } 231 } 232 233 /** 234 * Opens a MIDI device for reading and writing. 235 * 236 * @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open 237 * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called 238 * to receive the result 239 * @param handler the {@link android.os.Handler Handler} that will be used for delivering 240 * the result. If handler is null, then the thread used for the 241 * listener is unspecified. 242 */ openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener, Handler handler)243 public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener, 244 Handler handler) { 245 final MidiDeviceInfo deviceInfoF = deviceInfo; 246 final OnDeviceOpenedListener listenerF = listener; 247 final Handler handlerF = handler; 248 249 IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() { 250 @Override 251 public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) { 252 MidiDevice device; 253 if (server != null) { 254 device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken); 255 } else { 256 device = null; 257 } 258 sendOpenDeviceResponse(device, listenerF, handlerF); 259 } 260 }; 261 262 try { 263 mService.openDevice(mToken, deviceInfo, callback); 264 } catch (RemoteException e) { 265 throw e.rethrowFromSystemServer(); 266 } 267 } 268 269 /** 270 * Opens a Bluetooth MIDI device for reading and writing. 271 * 272 * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} to open as a MIDI device 273 * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called to receive the 274 * result 275 * @param handler the {@link android.os.Handler Handler} that will be used for delivering 276 * the result. If handler is null, then the thread used for the 277 * listener is unspecified. 278 */ openBluetoothDevice(BluetoothDevice bluetoothDevice, OnDeviceOpenedListener listener, Handler handler)279 public void openBluetoothDevice(BluetoothDevice bluetoothDevice, 280 OnDeviceOpenedListener listener, Handler handler) { 281 final OnDeviceOpenedListener listenerF = listener; 282 final Handler handlerF = handler; 283 284 IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() { 285 @Override 286 public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) { 287 MidiDevice device = null; 288 if (server != null) { 289 try { 290 // fetch MidiDeviceInfo from the server 291 MidiDeviceInfo deviceInfo = server.getDeviceInfo(); 292 device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken); 293 } catch (RemoteException e) { 294 Log.e(TAG, "remote exception in getDeviceInfo()"); 295 } 296 } 297 sendOpenDeviceResponse(device, listenerF, handlerF); 298 } 299 }; 300 301 try { 302 mService.openBluetoothDevice(mToken, bluetoothDevice, callback); 303 } catch (RemoteException e) { 304 throw e.rethrowFromSystemServer(); 305 } 306 } 307 308 /** @hide */ createDeviceServer(MidiReceiver[] inputPortReceivers, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, MidiDeviceServer.Callback callback)309 public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers, 310 int numOutputPorts, String[] inputPortNames, String[] outputPortNames, 311 Bundle properties, int type, MidiDeviceServer.Callback callback) { 312 try { 313 MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers, 314 numOutputPorts, callback); 315 MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(), 316 inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames, 317 properties, type); 318 if (deviceInfo == null) { 319 Log.e(TAG, "registerVirtualDevice failed"); 320 return null; 321 } 322 return server; 323 } catch (RemoteException e) { 324 throw e.rethrowFromSystemServer(); 325 } 326 } 327 } 328