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.annotation.RequiresFeature;
     20 import android.annotation.SystemService;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.content.Context;
     23 import android.content.pm.PackageManager;
     24 import android.os.Binder;
     25 import android.os.IBinder;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 
     31 import java.util.concurrent.ConcurrentHashMap;
     32 
     33 /**
     34  * This class is the public application interface to the MIDI service.
     35  */
     36 @SystemService(Context.MIDI_SERVICE)
     37 @RequiresFeature(PackageManager.FEATURE_MIDI)
     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      * 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             throw e.rethrowFromSystemServer();
    190         }
    191         mDeviceListeners.put(callback, deviceListener);
    192     }
    193 
    194     /**
    195      * Unregisters a {@link DeviceCallback}.
    196       *
    197      * @param callback a {@link DeviceCallback} to unregister
    198      */
    199     public void unregisterDeviceCallback(DeviceCallback callback) {
    200         DeviceListener deviceListener = mDeviceListeners.remove(callback);
    201         if (deviceListener != null) {
    202             try {
    203                 mService.unregisterListener(mToken, deviceListener);
    204             } catch (RemoteException e) {
    205                 throw e.rethrowFromSystemServer();
    206             }
    207         }
    208     }
    209 
    210     /**
    211      * Gets the list of all connected MIDI devices.
    212      *
    213      * @return an array of all MIDI devices
    214      */
    215     public MidiDeviceInfo[] getDevices() {
    216         try {
    217            return mService.getDevices();
    218         } catch (RemoteException e) {
    219             throw e.rethrowFromSystemServer();
    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         final MidiDeviceInfo deviceInfoF = deviceInfo;
    249         final OnDeviceOpenedListener listenerF = listener;
    250         final Handler handlerF = handler;
    251 
    252         IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
    253             @Override
    254             public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
    255                 MidiDevice device;
    256                 if (server != null) {
    257                     device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
    258                 } else {
    259                     device = null;
    260                 }
    261                 sendOpenDeviceResponse(device, listenerF, handlerF);
    262             }
    263         };
    264 
    265         try {
    266             mService.openDevice(mToken, deviceInfo, callback);
    267         } catch (RemoteException e) {
    268             throw e.rethrowFromSystemServer();
    269         }
    270     }
    271 
    272     /**
    273      * Opens a Bluetooth MIDI device for reading and writing.
    274      *
    275      * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} to open as a MIDI device
    276      * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called to receive the
    277      * result
    278      * @param handler the {@link android.os.Handler Handler} that will be used for delivering
    279      *                the result. If handler is null, then the thread used for the
    280      *                listener is unspecified.
    281      */
    282     public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
    283             OnDeviceOpenedListener listener, Handler handler) {
    284         final OnDeviceOpenedListener listenerF = listener;
    285         final Handler handlerF = handler;
    286 
    287         IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
    288             @Override
    289             public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
    290                 MidiDevice device = null;
    291                 if (server != null) {
    292                     try {
    293                         // fetch MidiDeviceInfo from the server
    294                         MidiDeviceInfo deviceInfo = server.getDeviceInfo();
    295                         device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
    296                     } catch (RemoteException e) {
    297                         Log.e(TAG, "remote exception in getDeviceInfo()");
    298                     }
    299                 }
    300                 sendOpenDeviceResponse(device, listenerF, handlerF);
    301             }
    302         };
    303 
    304         try {
    305             mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
    306         } catch (RemoteException e) {
    307             throw e.rethrowFromSystemServer();
    308         }
    309     }
    310 
    311     /** @hide */
    312     public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
    313             int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
    314             Bundle properties, int type, MidiDeviceServer.Callback callback) {
    315         try {
    316             MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
    317                     numOutputPorts, callback);
    318             MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
    319                     inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
    320                     properties, type);
    321             if (deviceInfo == null) {
    322                 Log.e(TAG, "registerVirtualDevice failed");
    323                 return null;
    324             }
    325             return server;
    326         } catch (RemoteException e) {
    327             throw e.rethrowFromSystemServer();
    328         }
    329     }
    330 }
    331