Home | History | Annotate | Download | only in midi
      1 /*
      2  * Copyright (C) 2015 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 com.android.internal.midi;
     18 
     19 import android.media.midi.MidiReceiver;
     20 import android.media.midi.MidiSender;
     21 
     22 import java.io.IOException;
     23 import java.util.concurrent.CopyOnWriteArrayList;
     24 
     25 /**
     26  * Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
     27  * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
     28  * to its receiver list. Any receivers that throw an exception upon receiving data will
     29  * be automatically removed from the receiver list. If a MidiReceiverFailureHandler has been
     30  * provided to the MidiDispatcher, it will be notified about the failure, but the exception
     31  * itself will be swallowed.
     32  */
     33 public final class MidiDispatcher extends MidiReceiver {
     34 
     35     // MidiDispatcher's client and MidiReceiver's owner can be different
     36     // classes (e.g. MidiDeviceService is a client, but MidiDeviceServer is
     37     // the owner), and errors occuring during sending need to be reported
     38     // to the owner rather than to the sender.
     39     //
     40     // Note that the callbacks will be called on the sender's thread.
     41     public interface MidiReceiverFailureHandler {
     42         void onReceiverFailure(MidiReceiver receiver, IOException failure);
     43     }
     44 
     45     private final MidiReceiverFailureHandler mFailureHandler;
     46     private final CopyOnWriteArrayList<MidiReceiver> mReceivers
     47             = new CopyOnWriteArrayList<MidiReceiver>();
     48 
     49     private final MidiSender mSender = new MidiSender() {
     50         @Override
     51         public void onConnect(MidiReceiver receiver) {
     52             mReceivers.add(receiver);
     53         }
     54 
     55         @Override
     56         public void onDisconnect(MidiReceiver receiver) {
     57             mReceivers.remove(receiver);
     58         }
     59     };
     60 
     61     public MidiDispatcher() {
     62         this(null);
     63     }
     64 
     65     public MidiDispatcher(MidiReceiverFailureHandler failureHandler) {
     66         mFailureHandler = failureHandler;
     67     }
     68 
     69     /**
     70      * Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
     71      * @return the number of receivers
     72      */
     73     public int getReceiverCount() {
     74         return mReceivers.size();
     75     }
     76 
     77     /**
     78      * Returns a {@link android.media.midi.MidiSender} which is used to add and remove
     79      * {@link android.media.midi.MidiReceiver}s
     80      * to the dispatcher's receiver list.
     81      * @return the dispatcher's MidiSender
     82      */
     83     public MidiSender getSender() {
     84         return mSender;
     85     }
     86 
     87     @Override
     88     public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
     89        for (MidiReceiver receiver : mReceivers) {
     90             try {
     91                 receiver.send(msg, offset, count, timestamp);
     92             } catch (IOException e) {
     93                 // If the receiver fails we remove the receiver but do not propagate the exception.
     94                 // Note that this may also happen if the client code stalls, and thus underlying
     95                 // MidiInputPort.onSend has raised IOException for EAGAIN / EWOULDBLOCK error.
     96                 mReceivers.remove(receiver);
     97                 if (mFailureHandler != null) {
     98                     mFailureHandler.onReceiverFailure(receiver, e);
     99                 }
    100             }
    101         }
    102     }
    103 
    104     @Override
    105     public void onFlush() throws IOException {
    106        for (MidiReceiver receiver : mReceivers) {
    107             try {
    108                 receiver.flush();
    109             } catch (IOException e) {
    110                 // This is just a special case of 'send' thus handle in the same way.
    111                 mReceivers.remove(receiver);
    112                 if (mFailureHandler != null) {
    113                     mFailureHandler.onReceiverFailure(receiver, e);
    114                 }
    115             }
    116        }
    117     }
    118 }
    119