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