MidiOutputPort.java revision b6f50d357bd3d4d296be6bb047f5ce93a79cbca1
1/*
2 * Copyright (C) 2014 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
17package android.media.midi;
18
19import android.os.IBinder;
20import android.os.ParcelFileDescriptor;
21import android.os.RemoteException;
22import android.util.Log;
23
24import com.android.internal.midi.MidiDispatcher;
25
26import dalvik.system.CloseGuard;
27
28import libcore.io.IoUtils;
29
30import java.io.Closeable;
31import java.io.FileInputStream;
32import java.io.IOException;
33
34/**
35 * This class is used for receiving data from a port on a MIDI device
36 */
37public final class MidiOutputPort extends MidiSender implements Closeable {
38    private static final String TAG = "MidiOutputPort";
39
40    private IMidiDeviceServer mDeviceServer;
41    private final IBinder mToken;
42    private final int mPortNumber;
43    private final FileInputStream mInputStream;
44    private final MidiDispatcher mDispatcher = new MidiDispatcher();
45
46    private final CloseGuard mGuard = CloseGuard.get();
47    private boolean mIsClosed;
48
49    // This thread reads MIDI events from a socket and distributes them to the list of
50    // MidiReceivers attached to this device.
51    private final Thread mThread = new Thread() {
52        @Override
53        public void run() {
54            byte[] buffer = new byte[MidiPortImpl.MAX_PACKET_SIZE];
55
56            try {
57                while (true) {
58                    // read next event
59                    int count = mInputStream.read(buffer);
60                    if (count < 0) {
61                        break;
62                        // FIXME - inform receivers here?
63                    }
64
65                    int packetType = MidiPortImpl.getPacketType(buffer, count);
66                    switch (packetType) {
67                        case MidiPortImpl.PACKET_TYPE_DATA: {
68                            int offset = MidiPortImpl.getDataOffset(buffer, count);
69                            int size = MidiPortImpl.getDataSize(buffer, count);
70                            long timestamp = MidiPortImpl.getPacketTimestamp(buffer, count);
71
72                            // dispatch to all our receivers
73                            mDispatcher.sendWithTimestamp(buffer, offset, size, timestamp);
74                            break;
75                        }
76                        case MidiPortImpl.PACKET_TYPE_FLUSH:
77                            mDispatcher.flush();
78                            break;
79                        default:
80                            Log.e(TAG, "Unknown packet type " + packetType);
81                            break;
82                    }
83                }
84            } catch (IOException e) {
85                // FIXME report I/O failure?
86                Log.e(TAG, "read failed");
87            } finally {
88                IoUtils.closeQuietly(mInputStream);
89            }
90        }
91    };
92
93    /* package */ MidiOutputPort(IMidiDeviceServer server, IBinder token,
94            ParcelFileDescriptor pfd, int portNumber) {
95        mDeviceServer = server;
96        mToken = token;
97        mPortNumber = portNumber;
98        mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
99        mThread.start();
100        mGuard.open("close");
101    }
102
103    /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
104        this(null, null, pfd, portNumber);
105    }
106
107    /**
108     * Returns the port number of this port
109     *
110     * @return the port's port number
111     */
112    public final int getPortNumber() {
113        return mPortNumber;
114    }
115
116    @Override
117    public void connect(MidiReceiver receiver) {
118        mDispatcher.getSender().connect(receiver);
119    }
120
121    @Override
122    public void disconnect(MidiReceiver receiver) {
123        mDispatcher.getSender().disconnect(receiver);
124    }
125
126    @Override
127    public void close() throws IOException {
128        synchronized (mGuard) {
129            if (mIsClosed) return;
130
131            mGuard.close();
132            mInputStream.close();
133            if (mDeviceServer != null) {
134                try {
135                    mDeviceServer.closePort(mToken);
136                } catch (RemoteException e) {
137                    Log.e(TAG, "RemoteException in MidiOutputPort.close()");
138                }
139            }
140            mIsClosed = true;
141        }
142    }
143
144    @Override
145    protected void finalize() throws Throwable {
146        try {
147            mGuard.warnIfOpen();
148            // not safe to make binder calls from finalize()
149            mDeviceServer = null;
150            close();
151        } finally {
152            super.finalize();
153        }
154    }
155}
156