1b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood/* 2b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * Copyright (C) 2014 The Android Open Source Project 3b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * 4b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 5b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * you may not use this file except in compliance with the License. 6b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * You may obtain a copy of the License at 7b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * 8b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 9b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * 10b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * Unless required by applicable law or agreed to in writing, software 11b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 12b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * See the License for the specific language governing permissions and 14b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood * limitations under the License. 15b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood */ 16b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 17b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodpackage android.media.midi; 18b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 194a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport android.os.IBinder; 2010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.ParcelFileDescriptor; 214a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport android.os.RemoteException; 2210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.util.Log; 2310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 24d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwoodimport com.android.internal.midi.MidiDispatcher; 25d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwood 264a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport dalvik.system.CloseGuard; 274a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 2810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport libcore.io.IoUtils; 2910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 3020821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwoodimport java.io.Closeable; 3117bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganovimport java.io.FileDescriptor; 32b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwoodimport java.io.FileInputStream; 3310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 34b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 35b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood/** 36a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * This class is used for receiving data from a port on a MIDI device 37b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood */ 38be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodpublic final class MidiOutputPort extends MidiSender implements Closeable { 3910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "MidiOutputPort"; 4010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 41be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private IMidiDeviceServer mDeviceServer; 424a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final IBinder mToken; 4320821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood private final int mPortNumber; 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final FileInputStream mInputStream; 4511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiDispatcher mDispatcher = new MidiDispatcher(); 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 474a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final CloseGuard mGuard = CloseGuard.get(); 48be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private boolean mIsClosed; 494a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // This thread reads MIDI events from a socket and distributes them to the list of 5110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // MidiReceivers attached to this device. 5210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final Thread mThread = new Thread() { 5310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 5410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void run() { 5520821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood byte[] buffer = new byte[MidiPortImpl.MAX_PACKET_SIZE]; 56b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 5710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 5810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood while (true) { 5910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // read next event 6010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int count = mInputStream.read(buffer); 6198cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood if (count < 0) { 6210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood break; 6311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // FIXME - inform receivers here? 6410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 65b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 66b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood int packetType = MidiPortImpl.getPacketType(buffer, count); 67b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood switch (packetType) { 68b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood case MidiPortImpl.PACKET_TYPE_DATA: { 69b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood int offset = MidiPortImpl.getDataOffset(buffer, count); 70b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood int size = MidiPortImpl.getDataSize(buffer, count); 71b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood long timestamp = MidiPortImpl.getPacketTimestamp(buffer, count); 72b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood 73b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood // dispatch to all our receivers 747eb441cb4abcd3230a4d243469c5044f49e707c8Mike Lockwood mDispatcher.send(buffer, offset, size, timestamp); 75b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood break; 76b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 77b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood case MidiPortImpl.PACKET_TYPE_FLUSH: 78b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood mDispatcher.flush(); 79b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood break; 80b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood default: 81b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood Log.e(TAG, "Unknown packet type " + packetType); 82b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood break; 83b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 8410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 8510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } catch (IOException e) { 8611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // FIXME report I/O failure? 87382cb48848cb3968d5a0088a825706d66cdfbd50Mikhail Naganov Log.e(TAG, "read failed", e); 8870a8147012f2f0e364424e788a11b8ad50f44421Phil Burk } finally { 8910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood IoUtils.closeQuietly(mInputStream); 9010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 9110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 9210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 9310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 944a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood /* package */ MidiOutputPort(IMidiDeviceServer server, IBinder token, 9517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov FileDescriptor fd, int portNumber) { 964a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mDeviceServer = server; 974a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken = token; 9820821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood mPortNumber = portNumber; 9917bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(new ParcelFileDescriptor(fd)); 10011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mThread.start(); 1014a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mGuard.open("close"); 1024a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1034a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 10417bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov /* package */ MidiOutputPort(FileDescriptor fd, int portNumber) { 10517bbb50edc8ccc56c4ecc932a19884c4cc1f5b6fMikhail Naganov this(null, null, fd, portNumber); 106b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood } 107b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 10820821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood /** 10920821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood * Returns the port number of this port 11020821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood * 11120821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood * @return the port's port number 11220821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood */ 11320821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood public final int getPortNumber() { 11420821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood return mPortNumber; 11520821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood } 11620821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood 11711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood @Override 1187eb441cb4abcd3230a4d243469c5044f49e707c8Mike Lockwood public void onConnect(MidiReceiver receiver) { 11911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mDispatcher.getSender().connect(receiver); 120b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood } 121b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood 12211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood @Override 1237eb441cb4abcd3230a4d243469c5044f49e707c8Mike Lockwood public void onDisconnect(MidiReceiver receiver) { 12411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mDispatcher.getSender().disconnect(receiver); 12510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 12610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 12710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 12810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 129be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood synchronized (mGuard) { 130be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood if (mIsClosed) return; 131be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 132be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.close(); 133be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputStream.close(); 134be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood if (mDeviceServer != null) { 135be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood try { 136be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mDeviceServer.closePort(mToken); 137be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } catch (RemoteException e) { 138be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood Log.e(TAG, "RemoteException in MidiOutputPort.close()"); 139be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 1404a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 141be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mIsClosed = true; 1424a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1434a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1444a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1454a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1464a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood protected void finalize() throws Throwable { 1474a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood try { 148be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.warnIfOpen(); 149be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood // not safe to make binder calls from finalize() 150be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mDeviceServer = null; 1514a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1524a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } finally { 1534a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super.finalize(); 1544a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 155b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood } 156b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood} 157