MidiDevice.java revision 9db9326ad47279709a0f7989addaf4b45221b6b9
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.content.Context;
20import android.content.ServiceConnection;
21import android.os.Binder;
22import android.os.IBinder;
23import android.os.ParcelFileDescriptor;
24import android.os.RemoteException;
25import android.util.Log;
26
27import dalvik.system.CloseGuard;
28
29import libcore.io.IoUtils;
30
31import java.io.Closeable;
32import java.io.IOException;
33
34/**
35 * This class is used for sending and receiving data to and from a MIDI device
36 * Instances of this class are created by {@link MidiManager#openDevice}.
37 */
38public final class MidiDevice implements Closeable {
39    private static final String TAG = "MidiDevice";
40
41    private final MidiDeviceInfo mDeviceInfo;
42    private final IMidiDeviceServer mDeviceServer;
43    private Context mContext;
44    private ServiceConnection mServiceConnection;
45
46
47    private final CloseGuard mGuard = CloseGuard.get();
48
49    /**
50     * This class represents a connection between the output port of one device
51     * and the input port of another. Created by {@link #connectPorts}.
52     * Close this object to terminate the connection.
53     */
54    public class MidiConnection implements Closeable {
55        private final IBinder mToken;
56        private final MidiInputPort mInputPort;
57
58        MidiConnection(IBinder token, MidiInputPort inputPort) {
59            mToken = token;
60            mInputPort = inputPort;
61        }
62
63        @Override
64        public void close() throws IOException {
65            try {
66                mDeviceServer.closePort(mToken);
67                IoUtils.closeQuietly(mInputPort);
68            } catch (RemoteException e) {
69                Log.e(TAG, "RemoteException in MidiConnection.close");
70            }
71        }
72    }
73
74    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
75        this(deviceInfo, server, null, null);
76    }
77
78    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
79            Context context, ServiceConnection serviceConnection) {
80        mDeviceInfo = deviceInfo;
81        mDeviceServer = server;
82        mContext = context;
83        mServiceConnection = serviceConnection;
84        mGuard.open("close");
85    }
86
87    /**
88     * Returns a {@link MidiDeviceInfo} object, which describes this device.
89     *
90     * @return the {@link MidiDeviceInfo} object
91     */
92    public MidiDeviceInfo getInfo() {
93        return mDeviceInfo;
94    }
95
96    /**
97     * Called to open a {@link MidiInputPort} for the specified port number.
98     *
99     * @param portNumber the number of the input port to open
100     * @return the {@link MidiInputPort}
101     */
102    public MidiInputPort openInputPort(int portNumber) {
103        try {
104            IBinder token = new Binder();
105            ParcelFileDescriptor pfd = mDeviceServer.openInputPort(token, portNumber);
106            if (pfd == null) {
107                return null;
108            }
109            return new MidiInputPort(mDeviceServer, token, pfd, portNumber);
110        } catch (RemoteException e) {
111            Log.e(TAG, "RemoteException in openInputPort");
112            return null;
113        }
114    }
115
116    /**
117     * Called to open a {@link MidiOutputPort} for the specified port number.
118     *
119     * @param portNumber the number of the output port to open
120     * @return the {@link MidiOutputPort}
121     */
122    public MidiOutputPort openOutputPort(int portNumber) {
123        try {
124            IBinder token = new Binder();
125            ParcelFileDescriptor pfd = mDeviceServer.openOutputPort(token, portNumber);
126            if (pfd == null) {
127                return null;
128            }
129            return new MidiOutputPort(mDeviceServer, token, pfd, portNumber);
130        } catch (RemoteException e) {
131            Log.e(TAG, "RemoteException in openOutputPort");
132            return null;
133        }
134    }
135
136    /**
137     * Connects the supplied {@link MidiInputPort} to the output port of this device
138     * with the specified port number. Once the connection is made, the MidiInput port instance
139     * can no longer receive data via its {@link MidiReceiver#onReceive} method.
140     * This method returns a {@link MidiDevice.MidiConnection} object, which can be used to close the connection
141     * @param inputPort the inputPort to connect
142     * @param outputPortNumber the port number of the output port to connect inputPort to.
143     * @return {@link MidiDevice.MidiConnection} object if the connection is successful, or null in case of failure
144     */
145    public MidiConnection connectPorts(MidiInputPort inputPort, int outputPortNumber) {
146        if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) {
147            throw new IllegalArgumentException("outputPortNumber out of range");
148        }
149
150        ParcelFileDescriptor pfd = inputPort.claimFileDescriptor();
151        if (pfd == null) {
152            return null;
153        }
154         try {
155            IBinder token = new Binder();
156            mDeviceServer.connectPorts(token, pfd, outputPortNumber);
157            // close our copy of the file descriptor
158            IoUtils.closeQuietly(pfd);
159            return new MidiConnection(token, inputPort);
160        } catch (RemoteException e) {
161            Log.e(TAG, "RemoteException in connectPorts");
162            return null;
163        }
164    }
165
166    @Override
167    public void close() throws IOException {
168        synchronized (mGuard) {
169            mGuard.close();
170            if (mContext != null && mServiceConnection != null) {
171                mContext.unbindService(mServiceConnection);
172                mContext = null;
173                mServiceConnection = null;
174            }
175        }
176    }
177
178    @Override
179    protected void finalize() throws Throwable {
180        try {
181            mGuard.warnIfOpen();
182            close();
183        } finally {
184            super.finalize();
185        }
186    }
187
188    @Override
189    public String toString() {
190        return ("MidiDevice: " + mDeviceInfo.toString());
191    }
192}
193