summaryrefslogtreecommitdiff
path: root/media/native
diff options
context:
space:
mode:
authorPaul McLean <pmclean@google.com>2017-03-14 15:20:51 -0700
committerPaul McLean <pmclean@google.com>2018-03-15 16:20:07 -0700
commit8a3e33b36a6259708ed44a9c41078d49c0fa9da4 (patch)
tree4b0e47637cd67486224ffbc865b75e2ecf229ab2 /media/native
parentcbeeed1255c0414afea45292a58bd533b67efa07 (diff)
(re)integrating Native MIDI API into NDK - base API
Implement native MIDI API (amidi) Bug: 30252756 Bug: 37090545 Test: manual - Connect PreSonus AudioBox 22VSL and run tests in NativeMidiTestbed app. Verify MIDI messages sent to external MIDI synthesizer. Verify MIDI messages received from external MIDI synthesizer. CTS Change-Id: I7bb02b8926d01090132ce873c785b5323a9fa5f8
Diffstat (limited to 'media/native')
-rw-r--r--media/native/midi/Android.bp29
-rw-r--r--media/native/midi/include/NOTICE13
-rw-r--r--media/native/midi/include/midi.h255
-rw-r--r--media/native/midi/libamidi.map.txt18
-rw-r--r--media/native/midi/midi.cpp394
-rw-r--r--media/native/midi/midi.h182
-rw-r--r--media/native/midi/midi_internal.h22
7 files changed, 621 insertions, 292 deletions
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 11f0deabbcd5..d069bd2f6284 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
cc_library_shared {
- name: "libmidi",
+ name: "libamidi",
srcs: [
"midi.cpp",
@@ -22,13 +22,13 @@ cc_library_shared {
aidl: {
include_dirs: ["frameworks/base/media/java"],
- export_aidl_headers: true,
+ export_aidl_headers: false,
},
cflags: [
"-Wall",
"-Werror",
- "-O0",
+ "-fvisibility=hidden",
],
shared_libs: [
@@ -36,7 +36,28 @@ cc_library_shared {
"libbinder",
"libutils",
"libmedia",
+ "libmediandk",
+ "libandroid_runtime",
],
- export_include_dirs: ["."],
+ export_include_dirs: ["include"],
+}
+
+ndk_headers {
+ name: "amidi",
+
+ from: "include",
+
+ to: "amidi",
+
+ srcs: ["include/midi.h"],
+ license: "include/NOTICE",
+}
+
+ndk_library {
+ name: "libamidi",
+
+ symbol_file: "libamidi.map.txt",
+
+ first_version: "29",
}
diff --git a/media/native/midi/include/NOTICE b/media/native/midi/include/NOTICE
new file mode 100644
index 000000000000..e72ff94a8401
--- /dev/null
+++ b/media/native/midi/include/NOTICE
@@ -0,0 +1,13 @@
+Copyright (C) 2018 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.
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/midi.h
new file mode 100644
index 000000000000..780d8a7bfd40
--- /dev/null
+++ b/media/native/midi/include/midi.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_MEDIA_MIDI_H_
+#define ANDROID_MEDIA_MIDI_H_
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <jni.h>
+
+#include <media/NdkMediaError.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AMidiDevice AMidiDevice;
+typedef struct AMidiInputPort AMidiInputPort;
+typedef struct AMidiOutputPort AMidiOutputPort;
+
+#define AMIDI_API __attribute__((visibility("default")))
+
+/*
+ * Message Op Codes. Used to parse MIDI data packets
+ */
+enum {
+ AMIDI_OPCODE_DATA = 1, /* The MIDI packet contains normal MIDI data */
+ AMIDI_OPCODE_FLUSH = 2, /* The MIDI packet contains just a MIDI FLUSH command. */
+ /* Forces the send of any pending MIDI data. */
+};
+
+/*
+ * Type IDs for various MIDI devices.
+ */
+enum {
+ AMIDI_DEVICE_TYPE_USB = 1, /* A MIDI device connected to the Android USB port */
+ AMIDI_DEVICE_TYPE_VIRTUAL = 2, /* A software object implementing MidiDeviceService */
+ AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */
+};
+
+/*
+ * Device API
+ */
+/**
+ * Connects a native Midi Device object to the associated Java MidiDevice object. Use this
+ * AMidiDevice to access the rest of the native MIDI API. Use AMidiDevice_release() to
+ * disconnect from the Java object when not being used any more.
+ *
+ * @param env Points to the Java Environment.
+ * @param midiDeviceObj The Java MidiDevice Object.
+ * @param outDevicePtrPtr Points to the pointer to receive the AMidiDevice
+ *
+ * @return AMEDIA_OK on success, or a negative error value:
+ * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - the midiDeviceObj
+ * is null or already connected to a native AMidiDevice
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
+ */
+media_status_t AMIDI_API AMidiDevice_fromJava(
+ JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr);
+
+/**
+ * Disconnects the native Midi Device Object from the associated Java MidiDevice object.
+ *
+ * @param midiDevice Points to the native AMIDI_MidiDevice.
+ *
+ * @return AMEDIA_OK on success,
+ * or a negative error value:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER}
+ * - the device parameter is NULL.
+ * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
+ * - the device is not consistent with the associated Java MidiDevice.
+ * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
+ * - the JNI interface initialization to the associated java MidiDevice failed.
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ */
+media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice);
+
+/**
+ * Gets the MIDI device type.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return The identifier of the MIDI device type:
+ * AMIDI_DEVICE_TYPE_USB
+ * AMIDI_DEVICE_TYPE_VIRTUAL
+ * AMIDI_DEVICE_TYPE_BLUETOOTH
+ * or a negative error value:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
+ * parameter is NULL.
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
+ */
+int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device);
+
+/**
+ * Gets the number of input (sending) ports available on the specified MIDI device.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return If successful, returns the number of MIDI input (sending) ports available on the
+ * device. If an error occurs, returns a negative value indicating the error:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
+ * parameter is NULL.
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ */
+ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device);
+
+/**
+ * Gets the number of output (receiving) ports available on the specified MIDI device.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return If successful, returns the number of MIDI output (receiving) ports available on the
+ * device. If an error occurs, returns a negative value indicating the error:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
+ * parameter is NULL.
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
+ */
+ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device);
+
+/*
+ * API for receiving data from the Output port of a device.
+ */
+/**
+ * Opens the output port so that the client can receive data from it. The port remains open and
+ * valid until AMidiOutputPort_close() is called for the returned AMidiOutputPort.
+ *
+ * @param device Specifies the MIDI device.
+ * @param portNumber Specifies the zero-based port index on the device to open. This value ranges
+ * between 0 and one less than the number of output ports reported by the
+ * AMidiDevice_getNumOutputPorts function.
+ * @param outOutputPortPtr Receives the native API port identifier of the opened port.
+ *
+ * @return AMEDIA_OK, or a negative error code:
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ */
+media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
+ AMidiOutputPort **outOutputPortPtr);
+
+/**
+ * Closes the output port.
+ *
+ * @param outputPort The native API port identifier of the port.
+ */
+void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort);
+
+/**
+ * Receives the next pending MIDI message. To retrieve all pending messages, the client should
+ * repeatedly call this method until it returns 0.
+ *
+ * Note that this is a non-blocking call. If there are no Midi messages are available, the function
+ * returns 0 immediately (for 0 messages received).
+ *
+ * @param outputPort Identifies the port to receive messages from.
+ * @param opcodePtr Receives the message Op Code.
+ * @param buffer Points to the buffer to receive the message data bytes.
+ * @param maxBytes Specifies the size of the buffer pointed to by the buffer parameter.
+ * @param numBytesReceivedPtr On exit, receives the actual number of bytes stored in buffer.
+ * @param outTimestampPtr If non-NULL, receives the timestamp associated with the message.
+ * (the current value of the running Java Virtual Machine's high-resolution time source,
+ * in nanoseconds)
+ * @return the number of messages received (either 0 or 1), or a negative error code:
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ */
+ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
+ uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr);
+
+/*
+ * API for sending data to the Input port of a device.
+ */
+/**
+ * Opens the input port so that the client can send data to it. The port remains open and
+ * valid until AMidiInputPort_close() is called for the returned AMidiInputPort.
+ *
+ * @param device Specifies the MIDI device.
+ * @param portNumber Specifies the zero-based port index on the device to open. This value ranges
+ * between 0 and one less than the number of input ports reported by the
+ * AMidiDevice_getNumInputPorts() function..
+ * @param outInputPortPtr Receives the native API port identifier of the opened port.
+ *
+ * @return AMEDIA_OK, or a negative error code:
+ * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ */
+media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
+ AMidiInputPort **outInputPortPtr);
+
+/**
+ * Sends data to the specified input port.
+ *
+ * @param inputPort The identifier of the port to send data to.
+ * @param buffer Points to the array of bytes containing the data to send.
+ * @param numBytes Specifies the number of bytes to write.
+ *
+ * @return The number of bytes sent, which could be less than specified or a negative error code:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
+ * was NULL, the specified buffer was NULL.
+ */
+ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
+ size_t numBytes);
+
+/**
+ * Sends data to the specified input port with a timestamp.
+ *
+ * @param inputPort The identifier of the port to send data to.
+ * @param buffer Points to the array of bytes containing the data to send.
+ * @param numBytes Specifies the number of bytes to write.
+ * @param timestamp The CLOCK_MONOTONIC time in nanoseconds to associate with the sent data.
+ *
+ * @return The number of bytes sent, which could be less than specified or a negative error code:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
+ * was NULL, the specified buffer was NULL.
+ */
+ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
+ const uint8_t *buffer, size_t numBytes, int64_t timestamp);
+
+/**
+ * Sends a message with a 'MIDI flush command code' to the specified port. This should cause
+ * a receiver to discard any pending MIDI data it may have accumulated and not processed.
+ *
+ * @param inputPort The identifier of the port to send the flush command to.
+ *
+ * @returns @see AMEDIA_OK {@link AMEDIA_OK} if successful, otherwise a negative error code:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
+ * was NULL
+ * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
+ * be sent.
+ */
+media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort);
+
+/**
+ * Closes the input port.
+ *
+ * @param inputPort Identifies the input (sending) port to close.
+ */
+void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_MEDIA_MIDI_H_ */
diff --git a/media/native/midi/libamidi.map.txt b/media/native/midi/libamidi.map.txt
new file mode 100644
index 000000000000..62627f8c5ef7
--- /dev/null
+++ b/media/native/midi/libamidi.map.txt
@@ -0,0 +1,18 @@
+LIBAMIDI {
+ global:
+ AMidiDevice_fromJava;
+ AMidiDevice_release;
+ AMidiDevice_getType;
+ AMidiDevice_getNumInputPorts;
+ AMidiDevice_getNumOutputPorts;
+ AMidiOutputPort_open;
+ AMidiOutputPort_close;
+ AMidiOutputPort_receive;
+ AMidiInputPort_open;
+ AMidiInputPort_send;
+ AMidiInputPort_sendWithTimestamp;
+ AMidiInputPort_sendFlush;
+ AMidiInputPort_close;
+ local:
+ *;
+};
diff --git a/media/native/midi/midi.cpp b/media/native/midi/midi.cpp
index 17b92da3325e..a9b498ffe0c8 100644
--- a/media/native/midi/midi.cpp
+++ b/media/native/midi/midi.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -20,15 +20,19 @@
#include <unistd.h>
#include <binder/Binder.h>
-#include <utils/Errors.h>
+#include <android_util_Binder.h>
#include <utils/Log.h>
+#include <core_jni_helpers.h>
+
#include "android/media/midi/BpMidiDeviceServer.h"
#include "media/MidiDeviceInfo.h"
-#include "midi.h"
+#include "include/midi.h"
#include "midi_internal.h"
+using namespace android::media::midi;
+
using android::IBinder;
using android::BBinder;
using android::OK;
@@ -36,23 +40,26 @@ using android::sp;
using android::status_t;
using android::base::unique_fd;
using android::binder::Status;
-using android::media::midi::MidiDeviceInfo;
struct AMIDI_Port {
- std::atomic_int state;
- AMIDI_Device *device;
- sp<IBinder> binderToken;
- unique_fd ufd;
+ std::atomic_int state; // One of the port status constants below.
+ const AMidiDevice *device; // Points to the AMidiDevice associated with the port.
+ sp<IBinder> binderToken;// The Binder token associated with the port.
+ unique_fd ufd; // The unique file descriptor associated with the port.
};
-#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
-
+/*
+ * Port Status Constants
+ */
enum {
MIDI_PORT_STATE_CLOSED = 0,
MIDI_PORT_STATE_OPEN_IDLE,
MIDI_PORT_STATE_OPEN_ACTIVE
};
+/*
+ * Port Type Constants
+ */
enum {
PORTTYPE_OUTPUT = 0,
PORTTYPE_INPUT = 1
@@ -76,32 +83,192 @@ enum {
* boundaries, and delivers messages in the order that they were sent.
* So 'read()' always returns a whole message.
*/
+#define AMIDI_PACKET_SIZE 1024
+#define AMIDI_PACKET_OVERHEAD 9
+#define AMIDI_BUFFER_SIZE (AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD)
+
+// MidiDevice Fields
+static jobject deviceClassGlobalRef = nullptr; // A GlobalRef for MidiDevice Class
+static jfieldID fidDeviceClosed = nullptr; // MidiDevice.mIsDeviceClosed
+static jfieldID fidNativeHandle = nullptr; // MidiDevice.mNativeHandle
+static jfieldID fidDeviceServerBinder = nullptr; // MidiDevice.mDeviceServerBinder
+static jfieldID fidDeviceInfo = nullptr; // MidiDevice.mDeviceInfo
+
+// MidiDeviceInfo Fields
+static jobject deviceInfoClassGlobalRef = nullptr; // A GlobalRef for MidiDeviceInfoClass
+static jfieldID fidDeviceId = nullptr; // MidiDeviceInfo.mId
+
+static std::mutex openMutex; // Ensure that the device can be connected just once to 1 thread
+
+static void AMIDI_initJNI(JNIEnv *env) {
+ jclass deviceClass = android::FindClassOrDie(env, "android/media/midi/MidiDevice");
+ deviceClassGlobalRef = env->NewGlobalRef(deviceClass);
+
+ // MidiDevice Field IDs
+ fidDeviceClosed = android::GetFieldIDOrDie(env, deviceClass, "mIsDeviceClosed", "Z");
+ fidNativeHandle = android::GetFieldIDOrDie(env, deviceClass, "mNativeHandle", "J");
+ fidDeviceServerBinder = android::GetFieldIDOrDie(env, deviceClass,
+ "mDeviceServerBinder", "Landroid/os/IBinder;");
+ fidDeviceInfo = android::GetFieldIDOrDie(env, deviceClass,
+ "mDeviceInfo", "Landroid/media/midi/MidiDeviceInfo;");
+
+ // MidiDeviceInfo Field IDs
+ jclass deviceInfoClass = android::FindClassOrDie(env, "android/media/midi/MidiDeviceInfo");
+ deviceInfoClassGlobalRef = env->NewGlobalRef(deviceInfoClass);
+ fidDeviceId = android::GetFieldIDOrDie(env, deviceInfoClass, "mId", "I");
+}
+
+//// Handy debugging function.
+//static void AMIDI_logBuffer(const uint8_t *data, size_t numBytes) {
+// for (size_t index = 0; index < numBytes; index++) {
+// ALOGI(" data @%zu [0x%X]", index, data[index]);
+// }
+//}
/*
* Device Functions
*/
-status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) {
+/**
+ * Retrieves information for the native MIDI device.
+ *
+ * device The Native API token for the device. This value is obtained from the
+ * AMidiDevice_fromJava().
+ * outDeviceInfoPtr Receives the associated device info.
+ *
+ * Returns AMEDIA_OK or a negative error code.
+ * - AMEDIA_ERROR_INVALID_PARAMETER
+ * AMEDIA_ERROR_UNKNOWN
+ */
+static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device,
+ AMidiDeviceInfo *outDeviceInfoPtr) {
+ if (device == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
MidiDeviceInfo deviceInfo;
Status txResult = device->server->getDeviceInfo(&deviceInfo);
if (!txResult.isOk()) {
ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
- return txResult.transactionError();
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ outDeviceInfoPtr->type = deviceInfo.getType();
+ outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
+ outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+
+ return AMEDIA_OK;
+}
+
+media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
+ AMidiDevice** devicePtrPtr)
+{
+ // Ensures JNI initialization is performed just once.
+ static std::once_flag initCallFlag;
+ std::call_once(initCallFlag, AMIDI_initJNI, env);
+
+ if (j_midiDeviceObj == nullptr) {
+ ALOGE("AMidiDevice_fromJava() invalid MidiDevice object.");
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ {
+ std::lock_guard<std::mutex> guard(openMutex);
+
+ long handle = env->GetLongField(j_midiDeviceObj, fidNativeHandle);
+ if (handle != 0) {
+ // Already opened by someone.
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ jobject serverBinderObj = env->GetObjectField(j_midiDeviceObj, fidDeviceServerBinder);
+ sp<IBinder> serverBinder = android::ibinderForJavaObject(env, serverBinderObj);
+ if (serverBinder.get() == nullptr) {
+ ALOGE("AMidiDevice_fromJava couldn't connect to native MIDI server.");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ // don't check allocation failures, just abort..
+ AMidiDevice* devicePtr = new AMidiDevice;
+ devicePtr->server = new BpMidiDeviceServer(serverBinder);
+ jobject midiDeviceInfoObj = env->GetObjectField(j_midiDeviceObj, fidDeviceInfo);
+ devicePtr->deviceId = env->GetIntField(midiDeviceInfoObj, fidDeviceId);
+
+ // Synchronize with the associated Java MidiDevice.
+ env->SetLongField(j_midiDeviceObj, fidNativeHandle, (long)devicePtr);
+ env->GetJavaVM(&devicePtr->javaVM);
+ devicePtr->midiDeviceObj = env->NewGlobalRef(j_midiDeviceObj);
+
+ if (AMIDI_getDeviceInfo(devicePtr, &devicePtr->deviceInfo) != AMEDIA_OK) {
+ // This is weird, but maybe not fatal?
+ ALOGE("AMidiDevice_fromJava couldn't retrieve attributes of native device.");
+ }
+
+ *devicePtrPtr = devicePtr;
+ }
+
+ return AMEDIA_OK;
+}
+
+media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device)
+{
+ if (device == nullptr || device->midiDeviceObj == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ JNIEnv* env;
+ jint err = device->javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
+ LOG_ALWAYS_FATAL_IF(err != JNI_OK, "AMidiDevice_release Error accessing JNIEnv err:%d", err);
+
+ // Synchronize with the associated Java MidiDevice.
+ // env->CallVoidMethod(j_midiDeviceObj, midClearNativeHandle);
+ {
+ std::lock_guard<std::mutex> guard(openMutex);
+ long handle = env->GetLongField(device->midiDeviceObj, fidNativeHandle);
+ if (handle == 0) {
+ // Not opened as native.
+ ALOGE("AMidiDevice_release() device not opened in native client.");
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ env->SetLongField(device->midiDeviceObj, fidNativeHandle, 0L);
}
+ env->DeleteGlobalRef(device->midiDeviceObj);
+
+ delete device;
- deviceInfoPtr->type = deviceInfo.getType();
- deviceInfoPtr->uid = deviceInfo.getUid();
- deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
- deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
- deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+ return AMEDIA_OK;
+}
- return OK;
+int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) {
+ if (device == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return device->deviceInfo.type;
+}
+
+ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
+ if (device == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return device->deviceInfo.inputPortCount;
+}
+
+ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
+ if (device == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return device->deviceInfo.outputPortCount;
}
/*
* Port Helpers
*/
-static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
+static media_status_t AMIDI_openPort(const AMidiDevice *device, int32_t portNumber, int type,
AMIDI_Port **portPtr) {
+ if (device == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
sp<BBinder> portToken(new BBinder());
unique_fd ufd;
Status txResult = type == PORTTYPE_OUTPUT
@@ -109,10 +276,10 @@ static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
: device->server->openInputPort(portToken, portNumber, &ufd);
if (!txResult.isOk()) {
ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError());
- return txResult.transactionError();
+ return AMEDIA_ERROR_UNKNOWN;
}
- AMIDI_Port* port = new AMIDI_Port;
+ AMIDI_Port *port = new AMIDI_Port;
port->state = MIDI_PORT_STATE_OPEN_IDLE;
port->device = device;
port->binderToken = portToken;
@@ -120,155 +287,178 @@ static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
*portPtr = port;
- return OK;
+ return AMEDIA_OK;
}
-static status_t AMIDI_closePort(AMIDI_Port *port) {
+static void AMIDI_closePort(AMIDI_Port *port) {
+ if (port == nullptr) {
+ return;
+ }
+
int portState = MIDI_PORT_STATE_OPEN_IDLE;
while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
if (portState == MIDI_PORT_STATE_CLOSED) {
- return -EINVAL; // Already closed
+ return; // Already closed
}
}
Status txResult = port->device->server->closePort(port->binderToken);
if (!txResult.isOk()) {
- return txResult.transactionError();
+ ALOGE("Transaction error closing MIDI port:%d", txResult.transactionError());
}
delete port;
-
- return OK;
}
/*
* Output (receiving) API
*/
-status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
- AMIDI_OutputPort **outputPortPtr) {
- return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr);
+media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
+ AMidiOutputPort **outOutputPortPtr) {
+ return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr);
}
-ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
- AMIDI_Port *port = (AMIDI_Port*)outputPort;
- int portState = MIDI_PORT_STATE_OPEN_IDLE;
- if (!port->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
- // The port has been closed.
- return -EPIPE;
+/*
+ * A little RAII (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
+ * class to ensure that the port state is correct irrespective of errors.
+ */
+class MidiReceiver {
+public:
+ MidiReceiver(AMIDI_Port *port) : mPort(port) {}
+
+ ~MidiReceiver() {
+ // flag the port state to idle
+ mPort->state.store(MIDI_PORT_STATE_OPEN_IDLE);
}
- status_t result = OK;
- ssize_t messagesRead = 0;
- while (messagesRead < maxMessages) {
- struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } };
- int pollResult = poll(checkFds, 1, 0);
- if (pollResult < 1) {
- result = android::INVALID_OPERATION;
- break;
+ ssize_t receive(int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes,
+ size_t *numBytesReceivedPtr, int64_t *timestampPtr) {
+ int portState = MIDI_PORT_STATE_OPEN_IDLE;
+ // check to see if the port is idle, then set to active
+ if (!mPort->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
+ // The port not idle or has been closed.
+ return AMEDIA_ERROR_UNKNOWN;
}
- AMIDI_Message *message = &messages[messagesRead];
- uint8_t readBuffer[AMIDI_PACKET_SIZE];
- memset(readBuffer, 0, sizeof(readBuffer));
- ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer));
- if (readCount == EINTR) {
- continue;
+ struct pollfd checkFds[1] = { { mPort->ufd, POLLIN, 0 } };
+ if (poll(checkFds, 1, 0) < 1) {
+ // Nothing there
+ return 0;
}
- if (readCount < 1) {
- result = android::NOT_ENOUGH_DATA;
- break;
+
+ uint8_t readBuffer[AMIDI_PACKET_SIZE];
+ ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer));
+ if (readCount == EINTR || readCount < 1) {
+ return AMEDIA_ERROR_UNKNOWN;
}
- // set Packet Format definition at the top of this file.
- size_t dataSize = 0;
- message->opcode = readBuffer[0];
- message->timestamp = 0;
- if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
- dataSize = readCount - AMIDI_PACKET_OVERHEAD;
- if (dataSize) {
- memcpy(message->buffer, readBuffer + 1, dataSize);
+ // see Packet Format definition at the top of this file.
+ size_t numMessageBytes = 0;
+ *opcodePtr = readBuffer[0];
+ if (*opcodePtr == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
+ numMessageBytes = readCount - AMIDI_PACKET_OVERHEAD;
+ numMessageBytes = std::min(maxBytes, numMessageBytes);
+ memcpy(buffer, readBuffer + 1, numMessageBytes);
+ if (timestampPtr != nullptr) {
+ *timestampPtr = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
}
- message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
}
- message->len = dataSize;
- ++messagesRead;
+ *numBytesReceivedPtr = numMessageBytes;
+ return 1;
}
- port->state.store(MIDI_PORT_STATE_OPEN_IDLE);
+private:
+ AMIDI_Port *mPort;
+};
+
+ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
+ uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) {
+
+ if (outputPort == nullptr || buffer == nullptr) {
+ return -EINVAL;
+ }
- return result == OK ? messagesRead : result;
+ return MidiReceiver((AMIDI_Port*)outputPort).receive(opcodePtr, buffer, maxBytes,
+ numBytesReceivedPtr, timestampPtr);
}
-status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) {
- return AMIDI_closePort((AMIDI_Port*)outputPort);
+void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
+ AMIDI_closePort((AMIDI_Port*)outputPort);
}
/*
* Input (sending) API
*/
-status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr) {
- return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)inputPortPtr);
-}
-
-status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) {
- return AMIDI_closePort((AMIDI_Port*)inputPort);
+media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
+ AMidiInputPort **outInputPortPtr) {
+ return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr);
}
-ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) {
- return SIZE_MIDIRECEIVEBUFFER;
+void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) {
+ AMIDI_closePort((AMIDI_Port*)inputPort);
}
static ssize_t AMIDI_makeSendBuffer(
- uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) {
+ uint8_t *buffer, const uint8_t *data, size_t numBytes, uint64_t timestamp) {
+ // Error checking will happen in the caller since this isn't an API function.
buffer[0] = AMIDI_OPCODE_DATA;
memcpy(buffer + 1, data, numBytes);
memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
return numBytes + AMIDI_PACKET_OVERHEAD;
}
-// Handy debugging function.
-//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) {
-// for (size_t index = 0; index < numBytes; index++) {
-// ALOGI(" data @%zu [0x%X]", index, data[index]);
-// }
-//}
-
-ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) {
- return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
+ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
+ size_t numBytes) {
+ return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0);
}
-ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data,
- ssize_t numBytes, int64_t timestamp) {
-
- if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
- return android::BAD_VALUE;
+ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
+ const uint8_t *data, size_t numBytes, int64_t timestamp) {
+ if (inputPort == nullptr || data == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
}
// AMIDI_logBuffer(data, numBytes);
- uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
- ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
- ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
+ uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
+ size_t numSent = 0;
+ while (numSent < numBytes) {
+ size_t blockSize = AMIDI_BUFFER_SIZE;
+ blockSize = std::min(blockSize, numBytes - numSent);
+
+ ssize_t numTransferBytes =
+ AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp);
+ ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
+ if (numWritten < 0) {
+ break; // error so bail out.
+ }
+ if (numWritten < numTransferBytes) {
+ ALOGE("AMidiInputPort_sendWithTimestamp Couldn't write MIDI data buffer."
+ " requested:%zu, written%zu",numTransferBytes, numWritten);
+ break; // bail
+ }
- if (numWritten < numTransferBytes) {
- ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
- numTransferBytes, numWritten);
+ numSent += numWritten - AMIDI_PACKET_OVERHEAD;
}
- return numWritten - AMIDI_PACKET_OVERHEAD;
+ return numSent;
}
-status_t AMIDI_flush(AMIDI_InputPort *inputPort) {
+media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
+ if (inputPort == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
uint8_t opCode = AMIDI_OPCODE_FLUSH;
ssize_t numTransferBytes = 1;
ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
if (numWritten < numTransferBytes) {
- ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu",
+ ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd",
numTransferBytes, numWritten);
- return android::INVALID_OPERATION;
+ return AMEDIA_ERROR_UNSUPPORTED;
}
- return OK;
+ return AMEDIA_OK;
}
diff --git a/media/native/midi/midi.h b/media/native/midi/midi.h
deleted file mode 100644
index 9332b44dddd2..000000000000
--- a/media/native/midi/midi.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef ANDROID_MEDIA_MIDI_H_
-#define ANDROID_MEDIA_MIDI_H_
-
-#include <stdarg.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-
-using android::status_t;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct AMIDI_Device;
-struct AMIDI_InputPort;
-struct AMIDI_OutputPort;
-
-#define AMIDI_INVALID_HANDLE NULL
-
-enum {
- AMIDI_OPCODE_DATA = 1,
- AMIDI_OPCODE_FLUSH = 2,
- AMIDI_PACKET_SIZE = 1024, /* !!! Currently MidiPortImpl.MAX_PACKET_SIZE !!! */
- AMIDI_PACKET_OVERHEAD = 9,
- AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD
- /* !!! TBD, currently MidiPortImpl.MAX_PACKET_DATA_SIZE !!! */
-};
-
-typedef struct {
- uint32_t opcode;
- uint8_t buffer[AMIDI_BUFFER_SIZE];
- size_t len;
- int64_t timestamp;
-} AMIDI_Message;
-
-enum {
- AMIDI_DEVICE_TYPE_USB = 1,
- AMIDI_DEVICE_TYPE_VIRTUAL = 2,
- AMIDI_DEVICE_TYPE_BLUETOOTH = 3
-};
-
-typedef struct {
- int32_t type;
- int32_t uid;
- int32_t isPrivate;
- int32_t inputPortCount;
- int32_t outputPortCount;
-} AMIDI_DeviceInfo;
-
-/*
- * Device API
- */
-/*
- * Retrieves information for the native MIDI device.
- *
- * device The Native API token for the device.
- * deviceInfoPtr Receives the associated device info.
- *
- * Returns OK or a (negative) error code.
- */
-status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr);
-
-/*
- * API for receiving data from the Output port of a device.
- */
-/*
- * Opens the output port.
- *
- * device Identifies the device.
- * portNumber Specifies the zero-based port index on the device to open.
- * outputPortPtr Receives the native API port identifier of the opened port.
- *
- * Returns OK, or a (negative) error code.
- */
-status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
- AMIDI_OutputPort **outputPortPtr);
-
-/*
- * Receives any pending MIDI messages (up to the specified maximum number of messages).
- *
- * outputPort Identifies the port to receive messages from.
- * messages Points to an array (size maxMessages) to receive the MIDI messages.
- * maxMessages The number of messages allocated in the messages array.
- *
- * Returns the number of messages received, or a (negative) error code.
- */
-ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages);
-
-/*
- * Closes the output port.
- *
- * outputPort The native API port identifier of the port.
- *
- * Returns OK, or a (negative) error code.
- */
-status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort);
-
-/*
- * API for sending data to the Input port of a device.
- */
-/*
- * Opens the input port.
- *
- * device Identifies the device.
- * portNumber Specifies the zero-based port index on the device to open.
- * inputPortPtr Receives the native API port identifier of the opened port.
- *
- * Returns OK, or a (negative) error code.
- */
-status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr);
-
-/*
- * Returns the maximum number of bytes that can be received in a single MIDI message.
- */
-ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort *inputPort);
-
-/*
- * Sends data to the specified input port.
- *
- * inputPort The native API identifier of the port to send data to.
- * buffer Points to the array of bytes containing the data to send.
- * numBytes Specifies the number of bytes to write.
- *
- * Returns The number of bytes sent or a (negative) error code.
- */
-ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes);
-
-/*
- * Sends data to the specified input port with a timestamp.
- *
- * inputPort The native API identifier of the port to send data to.
- * buffer Points to the array of bytes containing the data to send.
- * numBytes Specifies the number of bytes to write.
- * timestamp The time stamp to associate with the sent data.
- *
- * Returns The number of bytes sent or a (negative) error code.
- */
-ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *buffer,
- ssize_t numBytes, int64_t timestamp);
-
-/*
- * Sends a message with a 'MIDI flush command code' to the specified port.
- *
- * inputPort The native API identifier of the port to send the flush message to.
- *
- * Returns OK, or a (negative) error code.
- */
-status_t AMIDI_flush(AMIDI_InputPort *inputPort);
-
-/*
- * Closes the input port.
- *
- * inputPort The native API port identifier of the port.
- *
- *
- * Returns OK, or a (negative) error code.
- */
-status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_MEDIA_MIDI_H_ */
diff --git a/media/native/midi/midi_internal.h b/media/native/midi/midi_internal.h
index fd4770e05d6c..cb3ecce13533 100644
--- a/media/native/midi/midi_internal.h
+++ b/media/native/midi/midi_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -17,11 +17,25 @@
#ifndef ANDROID_MEDIA_MIDI_INTERNAL_H_
#define ANDROID_MEDIA_MIDI_INTERNAL_H_
+#include <jni.h>
+
#include "android/media/midi/BpMidiDeviceServer.h"
-struct AMIDI_Device {
- android::sp<android::media::midi::BpMidiDeviceServer> server;
- int32_t deviceId;
+typedef struct {
+ int32_t type; /* one of AMIDI_DEVICE_TYPE_* constants */
+ int32_t inputPortCount; /* number of input (send) ports associated with the device */
+ int32_t outputPortCount; /* number of output (received) ports associated with the device */
+} AMidiDeviceInfo;
+
+struct AMidiDevice {
+ android::sp<android::media::midi::BpMidiDeviceServer>
+ server; /* The Binder interface to the MIDI server (from the Java MidiDevice) */
+ int32_t deviceId; /* The integer id of the device assigned in the Java API */
+ JavaVM* javaVM; /* The Java VM (so we can obtain the JNIEnv in the
+ AMidiDevice_close function) */
+ jobject midiDeviceObj; /* NewGlobalRef() reference to the Java MidiDevice associated with
+ this native AMidiDevice. */
+ AMidiDeviceInfo deviceInfo; /* Attributes of the device. */
};
#endif // ANDROID_MEDIA_MIDI_INTERNAL_H_