110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood/* 210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Copyright (C) 2014 The Android Open Source Project 310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * you may not use this file except in compliance with the License. 610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * You may obtain a copy of the License at 710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 1010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Unless required by applicable law or agreed to in writing, software 1110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 1210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * See the License for the specific language governing permissions and 1410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * limitations under the License. 1510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 1610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodpackage android.media.midi; 1810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.Binder; 205ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwoodimport android.os.IBinder; 2111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.Process; 2210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.RemoteException; 2317bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganovimport android.system.ErrnoException; 2417bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganovimport android.system.Os; 2510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.OsConstants; 2610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.util.Log; 2710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 28d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwoodimport com.android.internal.midi.MidiDispatcher; 29d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwood 30be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport dalvik.system.CloseGuard; 31be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 3211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport libcore.io.IoUtils; 3311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 3410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganovimport java.io.FileDescriptor; 3610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 374a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport java.util.HashMap; 38be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport java.util.concurrent.CopyOnWriteArrayList; 3910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 40a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood/** 4111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Internal class used for providing an implementation for a MIDI device. 42a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * 43a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * @hide 44a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood */ 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class MidiDeviceServer implements Closeable { 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "MidiDeviceServer"; 4710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiManager mMidiManager; 4910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // MidiDeviceInfo for the device implemented by this server 5110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private MidiDeviceInfo mDeviceInfo; 5211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mInputPortCount; 5311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mOutputPortCount; 5410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiReceivers for receiving data on our input ports 5611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiReceiver[] mInputPortReceivers; 5710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiDispatchers for sending data on our output ports 5911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private MidiDispatcher[] mOutputPortDispatchers; 6010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 6111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiOutputPorts for clients connected to our input ports 6211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiOutputPort[] mInputPortOutputPorts; 6310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 64be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood // List of all MidiInputPorts we created 65be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CopyOnWriteArrayList<MidiInputPort> mInputPorts 66be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood = new CopyOnWriteArrayList<MidiInputPort>(); 67be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 685ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 695ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // for reporting device status 7081b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood private final boolean[] mInputPortOpen; 715ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final int[] mOutputPortOpenCount; 725ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 73be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CloseGuard mGuard = CloseGuard.get(); 745ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private boolean mIsClosed; 755ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 765ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final Callback mCallback; 775ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 78382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>(); 79382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov private final HashMap<MidiInputPort, PortClient> mInputPortClients = 80382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov new HashMap<MidiInputPort, PortClient>(); 81382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov 825ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public interface Callback { 835ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood /** 845ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * Called to notify when an our device status has changed 855ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param server the {@link MidiDeviceServer} that changed 865ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param status the {@link MidiDeviceStatus} for the device 875ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood */ 885ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status); 89e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood 90e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood /** 91e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood * Called to notify when the device is closed 92e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood */ 93e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood public void onClose(); 945ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 95be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 964a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract private class PortClient implements IBinder.DeathRecipient { 974a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood final IBinder mToken; 984a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 994a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient(IBinder token) { 1004a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken = token; 1014a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1024a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood try { 1034a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood token.linkToDeath(this, 0); 1044a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } catch (RemoteException e) { 1054a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1064a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1074a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1084a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1094a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract void close(); 1104a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 111382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov MidiInputPort getInputPort() { 112382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov return null; 113382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 114382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov 1154a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1164a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void binderDied() { 1174a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1184a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1194a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1204a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1214a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class InputPortClient extends PortClient { 1224a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiOutputPort mOutputPort; 1234a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1244a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient(IBinder token, MidiOutputPort outputPort) { 1254a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1264a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mOutputPort = outputPort; 1274a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1284a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1294a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1304a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1314a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1324a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mInputPortOutputPorts) { 1335ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mOutputPort.getPortNumber(); 1345ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortOutputPorts[portNumber] = null; 13581b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen[portNumber] = false; 1365ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1374a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1384a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mOutputPort); 1394a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1404a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1414a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1424a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class OutputPortClient extends PortClient { 1434a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiInputPort mInputPort; 1444a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1454a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient(IBinder token, MidiInputPort inputPort) { 1464a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1474a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mInputPort = inputPort; 1484a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1494a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1504a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1514a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1524a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1535ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mInputPort.getPortNumber(); 1545ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 1555ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 1565ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().disconnect(mInputPort); 1575ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 1585ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 1595ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1605ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 1615ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 162be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.remove(mInputPort); 1634a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mInputPort); 1644a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1654a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 166382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov @Override 167382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov MidiInputPort getInputPort() { 168382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov return mInputPort; 169382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 170382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 1714a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 17217bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov private static FileDescriptor[] createSeqPacketSocketPair() throws IOException { 17317bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov try { 17417bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov final FileDescriptor fd0 = new FileDescriptor(); 17517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov final FileDescriptor fd1 = new FileDescriptor(); 17617bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, fd0, fd1); 17717bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov return new FileDescriptor[] { fd0, fd1 }; 17817bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov } catch (ErrnoException e) { 17917bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov throw e.rethrowAsIOException(); 18017bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov } 18117bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov } 18217bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov 18310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // Binder interface stub for receiving connection requests from clients 18410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { 18510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 18610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 18717bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov public FileDescriptor openInputPort(IBinder token, int portNumber) { 18811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 18911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 19011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 19111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 19211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 19311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 19410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mInputPortCount) { 19510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); 19610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 19710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 19911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood synchronized (mInputPortOutputPorts) { 20011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mInputPortOutputPorts[portNumber] != null) { 20110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "port " + portNumber + " already open"); 20210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 20310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 20410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 20510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 20617bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov FileDescriptor[] pair = createSeqPacketSocketPair(); 2074a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); 20811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts[portNumber] = outputPort; 2094a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood outputPort.connect(mInputPortReceivers[portNumber]); 2104a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient client = new InputPortClient(token, outputPort); 2114a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2124a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 2134a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 21481b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen[portNumber] = true; 2155ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 21611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 21798cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood } catch (IOException e) { 21817bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov Log.e(TAG, "unable to create FileDescriptors in openInputPort"); 21998cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood return null; 22010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 22110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 22210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 22310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 22410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 22517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov public FileDescriptor openOutputPort(IBinder token, int portNumber) { 22611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 22711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 22811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 22911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 23011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 23111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 23210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mOutputPortCount) { 23310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); 23410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 23510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 23611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 23711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood try { 23817bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov FileDescriptor[] pair = createSeqPacketSocketPair(); 2394a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); 2407ea3e54b729e90770aab1275b31303ae65bcecc6Mikhail Naganov // Undo the default blocking-mode of the server-side socket for 2417ea3e54b729e90770aab1275b31303ae65bcecc6Mikhail Naganov // physical devices to avoid stalling the Java device handler if 2427ea3e54b729e90770aab1275b31303ae65bcecc6Mikhail Naganov // client app code gets stuck inside 'onSend' handler. 2437ea3e54b729e90770aab1275b31303ae65bcecc6Mikhail Naganov if (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL) { 24417bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov IoUtils.setBlocking(pair[0], false); 2457ea3e54b729e90770aab1275b31303ae65bcecc6Mikhail Naganov } 2465ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 2475ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 2485ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().connect(inputPort); 2495ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 2505ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 2515ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 2525ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 2535ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 254be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.add(inputPort); 2554a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 2564a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2574a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 2584a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 259382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov synchronized (mInputPortClients) { 260382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov mInputPortClients.put(inputPort, client); 261382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 26211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 26311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } catch (IOException e) { 26417bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov Log.e(TAG, "unable to create FileDescriptors in openOutputPort"); 26511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return null; 26610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 26710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 2684a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 2694a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 2704a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void closePort(IBinder token) { 271382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov MidiInputPort inputPort = null; 2724a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2734a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient client = mPortClients.remove(token); 2744a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood if (client != null) { 275382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov inputPort = client.getInputPort(); 2764a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood client.close(); 2774a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 2784a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 279382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov if (inputPort != null) { 280382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov synchronized (mInputPortClients) { 281382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov mInputPortClients.remove(inputPort); 282382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 283382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 2844a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 28546326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood 28646326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood @Override 287e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood public void closeDevice() { 288e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood if (mCallback != null) { 289e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood mCallback.onClose(); 290e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood } 291e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood IoUtils.closeQuietly(MidiDeviceServer.this); 292e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood } 293e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood 294e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood @Override 29517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov public int connectPorts(IBinder token, FileDescriptor fd, 29646326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood int outputPortNumber) { 29717bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov MidiInputPort inputPort = new MidiInputPort(fd, outputPortNumber); 2982aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber]; 2992aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood synchronized (dispatcher) { 3002aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood dispatcher.getSender().connect(inputPort); 3012aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood int openCount = dispatcher.getReceiverCount(); 3022aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood mOutputPortOpenCount[outputPortNumber] = openCount; 3032aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood updateDeviceStatus(); 3042aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood } 3052aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood 30646326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mInputPorts.add(inputPort); 30746326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 30846326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood synchronized (mPortClients) { 30946326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mPortClients.put(token, client); 31046326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 311382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov synchronized (mInputPortClients) { 312382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov mInputPortClients.put(inputPort, client); 313382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 31480299748cc7ed5f59cb65122459b036b74150d4aPhil Burk return Process.myPid(); // for caller to detect same process ID 31546326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 316f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood 317f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood @Override 318f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood public MidiDeviceInfo getDeviceInfo() { 319f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood return mDeviceInfo; 320f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood } 321acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood 322acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood @Override 323acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood public void setDeviceInfo(MidiDeviceInfo deviceInfo) { 324acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood if (Binder.getCallingUid() != Process.SYSTEM_UID) { 325acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood throw new SecurityException("setDeviceInfo should only be called by MidiService"); 326acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 327acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood if (mDeviceInfo != null) { 328acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood throw new IllegalStateException("setDeviceInfo should only be called once"); 329acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 330acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood mDeviceInfo = deviceInfo; 331acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 33210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 33310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 334acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood // Constructor for MidiManager.createDeviceServer() 33511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 3365ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int numOutputPorts, Callback callback) { 33710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mMidiManager = midiManager; 33811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortReceivers = inputPortReceivers; 33911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortCount = inputPortReceivers.length; 34011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortCount = numOutputPorts; 3415ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback = callback; 34211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 34311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; 34411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 34511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; 34611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood for (int i = 0; i < numOutputPorts; i++) { 347382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov mOutputPortDispatchers[i] = new MidiDispatcher(mInputPortFailureHandler); 34811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 349be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 35081b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen = new boolean[mInputPortCount]; 3515ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount = new int[numOutputPorts]; 3525ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 353be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.open("close"); 35410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 35510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 356382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov private final MidiDispatcher.MidiReceiverFailureHandler mInputPortFailureHandler = 357382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov new MidiDispatcher.MidiReceiverFailureHandler() { 358382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov public void onReceiverFailure(MidiReceiver receiver, IOException failure) { 359382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov Log.e(TAG, "MidiInputPort failed to send data", failure); 360382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov PortClient client = null; 361382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov synchronized (mInputPortClients) { 362382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov client = mInputPortClients.remove(receiver); 363382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 364382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov if (client != null) { 365382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov client.close(); 366382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 367382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov } 368382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov }; 369382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov 370acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood // Constructor for MidiDeviceService.onCreate() 371acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 372acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood MidiDeviceInfo deviceInfo, Callback callback) { 373acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood this(midiManager, inputPortReceivers, deviceInfo.getOutputPortCount(), callback); 374acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood mDeviceInfo = deviceInfo; 375acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 376acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood 37710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /* package */ IMidiDeviceServer getBinderInterface() { 37810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return mServer; 37910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 38010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 381f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood public IBinder asBinder() { 382f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood return mServer.asBinder(); 383f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood } 384f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood 3855ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private void updateDeviceStatus() { 3865ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // clear calling identity, since we may be in a Binder call from one of our clients 3875ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood long identityToken = Binder.clearCallingIdentity(); 3885ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 38981b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortOpen, 3905ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount); 3915ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mCallback != null) { 3925ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback.onDeviceStatusChanged(this, status); 3935ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3945ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood try { 395e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood mMidiManager.setDeviceStatus(mServer, status); 3965ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } catch (RemoteException e) { 3975ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Log.e(TAG, "RemoteException in updateDeviceStatus"); 3985ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } finally { 3995ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Binder.restoreCallingIdentity(identityToken); 4005ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 4015ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 4025ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 40310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 40410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 405be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood synchronized (mGuard) { 4065ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mIsClosed) return; 407be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.close(); 408be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 409be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (int i = 0; i < mInputPortCount; i++) { 410be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood MidiOutputPort outputPort = mInputPortOutputPorts[i]; 411be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood if (outputPort != null) { 412be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(outputPort); 413be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPortOutputPorts[i] = null; 414be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 415be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 416be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (MidiInputPort inputPort : mInputPorts) { 417be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(inputPort); 418be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 419be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.clear(); 420be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood try { 421be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mMidiManager.unregisterDeviceServer(mServer); 422be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } catch (RemoteException e) { 423be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood Log.e(TAG, "RemoteException in unregisterDeviceServer"); 424be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 4255ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mIsClosed = true; 426be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 427be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 428be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 429be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood @Override 430be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood protected void finalize() throws Throwable { 43110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 432492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath if (mGuard != null) { 433492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath mGuard.warnIfOpen(); 434492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath } 435492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath 436be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood close(); 437be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } finally { 438be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood super.finalize(); 43910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 44010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 44110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 44210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /** 44311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Returns an array of {@link MidiReceiver} for the device's output ports. 44411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Clients can use these receivers to send data out the device's output ports. 44511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * @return array of MidiReceivers 44610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 44711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood public MidiReceiver[] getOutputPortReceivers() { 44811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; 44911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); 45011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return receivers; 45110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 45210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 453