259 lines
8.9 KiB
Java
259 lines
8.9 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2014 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.media.midi;
|
||
|
|
||
|
import android.os.Binder;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.Process;
|
||
|
import android.os.RemoteException;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import dalvik.system.CloseGuard;
|
||
|
|
||
|
import libcore.io.IoUtils;
|
||
|
|
||
|
import java.io.Closeable;
|
||
|
import java.io.FileDescriptor;
|
||
|
import java.io.IOException;
|
||
|
|
||
|
import java.util.HashSet;
|
||
|
|
||
|
/**
|
||
|
* This class is used for sending and receiving data to and from a MIDI device
|
||
|
* Instances of this class are created by {@link MidiManager#openDevice}.
|
||
|
*/
|
||
|
public final class MidiDevice implements Closeable {
|
||
|
private static final String TAG = "MidiDevice";
|
||
|
|
||
|
private final MidiDeviceInfo mDeviceInfo; // accessed from native code
|
||
|
private final IMidiDeviceServer mDeviceServer;
|
||
|
private final IBinder mDeviceServerBinder; // accessed from native code
|
||
|
private final IMidiManager mMidiManager;
|
||
|
private final IBinder mClientToken;
|
||
|
private final IBinder mDeviceToken;
|
||
|
private boolean mIsDeviceClosed; // accessed from native code
|
||
|
|
||
|
private long mNativeHandle; // accessed from native code
|
||
|
|
||
|
private final CloseGuard mGuard = CloseGuard.get();
|
||
|
|
||
|
/**
|
||
|
* This class represents a connection between the output port of one device
|
||
|
* and the input port of another. Created by {@link #connectPorts}.
|
||
|
* Close this object to terminate the connection.
|
||
|
*/
|
||
|
public class MidiConnection implements Closeable {
|
||
|
private final IMidiDeviceServer mInputPortDeviceServer;
|
||
|
private final IBinder mInputPortToken;
|
||
|
private final IBinder mOutputPortToken;
|
||
|
private final CloseGuard mGuard = CloseGuard.get();
|
||
|
private boolean mIsClosed;
|
||
|
|
||
|
MidiConnection(IBinder outputPortToken, MidiInputPort inputPort) {
|
||
|
mInputPortDeviceServer = inputPort.getDeviceServer();
|
||
|
mInputPortToken = inputPort.getToken();
|
||
|
mOutputPortToken = outputPortToken;
|
||
|
mGuard.open("close");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
synchronized (mGuard) {
|
||
|
if (mIsClosed) return;
|
||
|
mGuard.close();
|
||
|
try {
|
||
|
// close input port
|
||
|
mInputPortDeviceServer.closePort(mInputPortToken);
|
||
|
// close output port
|
||
|
mDeviceServer.closePort(mOutputPortToken);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "RemoteException in MidiConnection.close");
|
||
|
}
|
||
|
mIsClosed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void finalize() throws Throwable {
|
||
|
try {
|
||
|
if (mGuard != null) {
|
||
|
mGuard.warnIfOpen();
|
||
|
}
|
||
|
|
||
|
close();
|
||
|
} finally {
|
||
|
super.finalize();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
|
||
|
IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) {
|
||
|
mDeviceInfo = deviceInfo;
|
||
|
mDeviceServer = server;
|
||
|
mDeviceServerBinder = mDeviceServer.asBinder();
|
||
|
mMidiManager = midiManager;
|
||
|
mClientToken = clientToken;
|
||
|
mDeviceToken = deviceToken;
|
||
|
mGuard.open("close");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a {@link MidiDeviceInfo} object, which describes this device.
|
||
|
*
|
||
|
* @return the {@link MidiDeviceInfo} object
|
||
|
*/
|
||
|
public MidiDeviceInfo getInfo() {
|
||
|
return mDeviceInfo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called to open a {@link MidiInputPort} for the specified port number.
|
||
|
*
|
||
|
* An input port can only be used by one sender at a time.
|
||
|
* Opening an input port will fail if another application has already opened it for use.
|
||
|
* A {@link MidiDeviceStatus} can be used to determine if an input port is already open.
|
||
|
*
|
||
|
* @param portNumber the number of the input port to open
|
||
|
* @return the {@link MidiInputPort} if the open is successful,
|
||
|
* or null in case of failure.
|
||
|
*/
|
||
|
public MidiInputPort openInputPort(int portNumber) {
|
||
|
if (mIsDeviceClosed) {
|
||
|
return null;
|
||
|
}
|
||
|
try {
|
||
|
IBinder token = new Binder();
|
||
|
FileDescriptor fd = mDeviceServer.openInputPort(token, portNumber);
|
||
|
if (fd == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return new MidiInputPort(mDeviceServer, token, fd, portNumber);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "RemoteException in openInputPort");
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called to open a {@link MidiOutputPort} for the specified port number.
|
||
|
*
|
||
|
* An output port may be opened by multiple applications.
|
||
|
*
|
||
|
* @param portNumber the number of the output port to open
|
||
|
* @return the {@link MidiOutputPort} if the open is successful,
|
||
|
* or null in case of failure.
|
||
|
*/
|
||
|
public MidiOutputPort openOutputPort(int portNumber) {
|
||
|
if (mIsDeviceClosed) {
|
||
|
return null;
|
||
|
}
|
||
|
try {
|
||
|
IBinder token = new Binder();
|
||
|
FileDescriptor fd = mDeviceServer.openOutputPort(token, portNumber);
|
||
|
if (fd == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return new MidiOutputPort(mDeviceServer, token, fd, portNumber);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "RemoteException in openOutputPort");
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Connects the supplied {@link MidiInputPort} to the output port of this device
|
||
|
* with the specified port number. Once the connection is made, the MidiInput port instance
|
||
|
* can no longer receive data via its {@link MidiReceiver#onSend} method.
|
||
|
* This method returns a {@link MidiDevice.MidiConnection} object, which can be used
|
||
|
* to close the connection.
|
||
|
*
|
||
|
* @param inputPort the inputPort to connect
|
||
|
* @param outputPortNumber the port number of the output port to connect inputPort to.
|
||
|
* @return {@link MidiDevice.MidiConnection} object if the connection is successful,
|
||
|
* or null in case of failure.
|
||
|
*/
|
||
|
public MidiConnection connectPorts(MidiInputPort inputPort, int outputPortNumber) {
|
||
|
if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) {
|
||
|
throw new IllegalArgumentException("outputPortNumber out of range");
|
||
|
}
|
||
|
if (mIsDeviceClosed) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
FileDescriptor fd = inputPort.claimFileDescriptor();
|
||
|
if (fd == null) {
|
||
|
return null;
|
||
|
}
|
||
|
try {
|
||
|
IBinder token = new Binder();
|
||
|
int calleePid = mDeviceServer.connectPorts(token, fd, outputPortNumber);
|
||
|
// If the service is a different Process then it will duplicate the fd
|
||
|
// and we can safely close this one.
|
||
|
// But if the service is in the same Process then closing the fd will
|
||
|
// kill the connection. So don't do that.
|
||
|
if (calleePid != Process.myPid()) {
|
||
|
// close our copy of the file descriptor
|
||
|
IoUtils.closeQuietly(fd);
|
||
|
}
|
||
|
|
||
|
return new MidiConnection(token, inputPort);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "RemoteException in connectPorts");
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
synchronized (mGuard) {
|
||
|
// What if there is a native reference to this?
|
||
|
if (mNativeHandle != 0) {
|
||
|
Log.w(TAG, "MidiDevice#close() called while there is an outstanding native client 0x"
|
||
|
+ Long.toHexString(mNativeHandle));
|
||
|
}
|
||
|
if (!mIsDeviceClosed && mNativeHandle == 0) {
|
||
|
mGuard.close();
|
||
|
mIsDeviceClosed = true;
|
||
|
try {
|
||
|
mMidiManager.closeDevice(mClientToken, mDeviceToken);
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "RemoteException in closeDevice");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void finalize() throws Throwable {
|
||
|
try {
|
||
|
if (mGuard != null) {
|
||
|
mGuard.warnIfOpen();
|
||
|
}
|
||
|
|
||
|
close();
|
||
|
} finally {
|
||
|
super.finalize();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return ("MidiDevice: " + mDeviceInfo.toString());
|
||
|
}
|
||
|
}
|