Home | History | Annotate | Download | only in midi
      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