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 {
148492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath            if (mGuard != null) {
149492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath                mGuard.warnIfOpen();
150492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath            }
151492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath
152be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood            // not safe to make binder calls from finalize()
153be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood            mDeviceServer = null;
1544a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood            close();
1554a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood        } finally {
1564a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood            super.finalize();
1574a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood        }
158b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood    }
159b6b9a91c02b7a44cf56943e5358cee68fa4aece5Mike Lockwood}
160