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 { onReceiverFailure(MidiReceiver receiver, IOException failure)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 MidiDispatcher()61 public MidiDispatcher() { 62 this(null); 63 } 64 MidiDispatcher(MidiReceiverFailureHandler failureHandler)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 */ getReceiverCount()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 */ getSender()83 public MidiSender getSender() { 84 return mSender; 85 } 86 87 @Override onSend(byte[] msg, int offset, int count, long timestamp)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 onFlush()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