Merge "Switching Native MIDI API to an "opaque pointers" model."
This commit is contained in:
@@ -30,6 +30,8 @@ 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}.
|
||||
@@ -47,7 +49,18 @@ public final class MidiDevice implements Closeable {
|
||||
private final IBinder mClientToken;
|
||||
private final IBinder mDeviceToken;
|
||||
private boolean mIsDeviceClosed;
|
||||
private boolean mIsMirroredToNative;
|
||||
|
||||
// Native API Helpers
|
||||
/**
|
||||
* Keep a static list of MidiDevice objects that are mirrorToNative()'d so they
|
||||
* don't get inadvertantly garbage collected.
|
||||
*/
|
||||
private static HashSet<MidiDevice> mMirroredDevices = new HashSet<MidiDevice>();
|
||||
|
||||
/**
|
||||
* If this device is mirrorToNatived(), this is the native device handler.
|
||||
*/
|
||||
private long mNativeHandle;
|
||||
|
||||
private final CloseGuard mGuard = CloseGuard.get();
|
||||
|
||||
@@ -218,34 +231,39 @@ public final class MidiDevice implements Closeable {
|
||||
* Makes Midi Device available to the Native API
|
||||
* @hide
|
||||
*/
|
||||
public void mirrorToNative() throws IOException {
|
||||
if (mIsDeviceClosed || mIsMirroredToNative) {
|
||||
return;
|
||||
public long mirrorToNative() throws IOException {
|
||||
if (mIsDeviceClosed || mNativeHandle != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = mirrorToNative(mDeviceServer.asBinder(), mDeviceInfo.getId());
|
||||
if (result != 0) {
|
||||
throw new IOException("Failed mirroring to native: " + result);
|
||||
mNativeHandle = native_mirrorToNative(mDeviceServer.asBinder(), mDeviceInfo.getId());
|
||||
if (mNativeHandle == 0) {
|
||||
throw new IOException("Failed mirroring to native");
|
||||
}
|
||||
|
||||
mIsMirroredToNative = true;
|
||||
synchronized (mMirroredDevices) {
|
||||
mMirroredDevices.add(this);
|
||||
}
|
||||
return mNativeHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes Midi Device no longer available to the Native API
|
||||
* @hide
|
||||
*/
|
||||
public void removeFromNative() throws IOException {
|
||||
if (!mIsMirroredToNative) {
|
||||
public void removeFromNative() {
|
||||
if (mNativeHandle == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int result = removeFromNative(mDeviceInfo.getId());
|
||||
if (result != 0) {
|
||||
throw new IOException("Failed removing from native: " + result);
|
||||
synchronized (mGuard) {
|
||||
native_removeFromNative(mNativeHandle);
|
||||
mNativeHandle = 0;
|
||||
}
|
||||
|
||||
mIsMirroredToNative = false;
|
||||
synchronized (mMirroredDevices) {
|
||||
mMirroredDevices.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,6 +297,6 @@ public final class MidiDevice implements Closeable {
|
||||
return ("MidiDevice: " + mDeviceInfo.toString());
|
||||
}
|
||||
|
||||
private native int mirrorToNative(IBinder deviceServerBinder, int uid);
|
||||
private native int removeFromNative(int uid);
|
||||
private native long native_mirrorToNative(IBinder deviceServerBinder, int id);
|
||||
private native void native_removeFromNative(long deviceHandle);
|
||||
}
|
||||
|
||||
@@ -18,32 +18,33 @@
|
||||
#define LOG_TAG "Midi-JNI"
|
||||
|
||||
#include <android_util_Binder.h>
|
||||
#include <midi/MidiDeviceRegistry.h>
|
||||
#include <midi/midi_internal.h>
|
||||
#include <nativehelper/jni.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace android::media::midi;
|
||||
|
||||
extern "C" jint Java_android_media_midi_MidiDevice_mirrorToNative(
|
||||
JNIEnv *env, jobject thiz, jobject midiDeviceServer, jint id)
|
||||
extern "C" jlong Java_android_media_midi_MidiDevice_native_1mirrorToNative(
|
||||
JNIEnv *env, jobject, jobject midiDeviceServer, jint id)
|
||||
{
|
||||
(void)thiz;
|
||||
// ALOGI("native_mirrorToNative(%p)...", midiDeviceServer);
|
||||
sp<IBinder> serverBinder = ibinderForJavaObject(env, midiDeviceServer);
|
||||
if (serverBinder.get() == NULL) {
|
||||
ALOGE("Could not obtain IBinder from passed jobject");
|
||||
return -EINVAL;
|
||||
}
|
||||
// return MidiDeviceManager::getInstance().addDevice(serverBinder, uid);
|
||||
return MidiDeviceRegistry::getInstance().addDevice(
|
||||
new BpMidiDeviceServer(serverBinder), id);
|
||||
|
||||
AMIDI_Device* devicePtr = new AMIDI_Device;
|
||||
devicePtr->server = new BpMidiDeviceServer(serverBinder);
|
||||
devicePtr->deviceId = id;
|
||||
|
||||
return (jlong)devicePtr;
|
||||
}
|
||||
|
||||
extern "C" jint Java_android_media_midi_MidiDevice_removeFromNative(
|
||||
JNIEnv *env, jobject thiz, jint uid)
|
||||
extern "C" void Java_android_media_midi_MidiDevice_native_removeFromNative(
|
||||
JNIEnv *, jobject , jlong nativeToken)
|
||||
{
|
||||
(void)env;
|
||||
(void)thiz;
|
||||
// return MidiDeviceManager::getInstance().removeDevice(uid);
|
||||
return MidiDeviceRegistry::getInstance().removeDevice(uid);
|
||||
AMIDI_Device* devicePtr = (AMIDI_Device*)nativeToken;
|
||||
delete devicePtr;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
../../java/android/media/midi/IMidiDeviceServer.aidl \
|
||||
midi.cpp \
|
||||
MidiDeviceRegistry.cpp \
|
||||
MidiPortRegistry.cpp
|
||||
midi.cpp
|
||||
|
||||
LOCAL_AIDL_INCLUDES := \
|
||||
$(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
|
||||
|
||||
@@ -1,106 +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.
|
||||
*/
|
||||
|
||||
#include "MidiDeviceRegistry.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
ANDROID_SINGLETON_STATIC_INSTANCE(media::midi::MidiDeviceRegistry);
|
||||
|
||||
namespace media {
|
||||
namespace midi {
|
||||
|
||||
MidiDeviceRegistry::MidiDeviceRegistry() : mNextDeviceToken(1) {
|
||||
}
|
||||
|
||||
status_t MidiDeviceRegistry::addDevice(sp<BpMidiDeviceServer> server, int32_t deviceId) {
|
||||
if (server.get() == nullptr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(mMapsLock);
|
||||
mServers[deviceId] = server;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiDeviceRegistry::removeDevice(int32_t deviceId) {
|
||||
std::lock_guard<std::mutex> guard(mMapsLock);
|
||||
mServers.erase(deviceId);
|
||||
const auto& iter = mUidToToken.find(deviceId);
|
||||
if (iter != mUidToToken.end()) {
|
||||
mTokenToUid.erase(iter->second);
|
||||
mUidToToken.erase(iter);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
//NOTE: This creates an entry if not found, or returns an existing one.
|
||||
status_t MidiDeviceRegistry::obtainDeviceToken(int32_t deviceId, AMIDI_Device *deviceTokenPtr) {
|
||||
std::lock_guard<std::mutex> guard(mMapsLock);
|
||||
const auto& serversIter = mServers.find(deviceId);
|
||||
if (serversIter == mServers.end()) {
|
||||
// Not found.
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const auto& iter = mUidToToken.find(deviceId);
|
||||
if (iter != mUidToToken.end()) {
|
||||
*deviceTokenPtr = iter->second;
|
||||
} else {
|
||||
*deviceTokenPtr = mNextDeviceToken++;
|
||||
mTokenToUid[*deviceTokenPtr] = deviceId;
|
||||
mUidToToken[deviceId] = *deviceTokenPtr;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiDeviceRegistry::releaseDevice(AMIDI_Device deviceToken) {
|
||||
std::lock_guard<std::mutex> guard(mMapsLock);
|
||||
const auto& iter = mTokenToUid.find(deviceToken);
|
||||
if (iter == mTokenToUid.end()) {
|
||||
// Not found
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mServers.erase(iter->second);
|
||||
mUidToToken.erase(iter->second);
|
||||
mTokenToUid.erase(iter);
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiDeviceRegistry::getDeviceByToken(
|
||||
AMIDI_Device deviceToken, sp<BpMidiDeviceServer> *devicePtr) {
|
||||
std::lock_guard<std::mutex> guard(mMapsLock);
|
||||
int32_t id = -1;
|
||||
{
|
||||
const auto& iter = mTokenToUid.find(deviceToken);
|
||||
if (iter == mTokenToUid.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
id = iter->second;
|
||||
}
|
||||
const auto& iter = mServers.find(id);
|
||||
if (iter == mServers.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*devicePtr = iter->second;
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace midi
|
||||
} // namespace media
|
||||
} // namespace android
|
||||
@@ -1,104 +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_DEVICE_REGISTRY_H_
|
||||
#define ANDROID_MEDIA_MIDI_DEVICE_REGISTRY_H_
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include <binder/IBinder.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Singleton.h>
|
||||
|
||||
#include "android/media/midi/BpMidiDeviceServer.h"
|
||||
#include "midi.h"
|
||||
|
||||
namespace android {
|
||||
namespace media {
|
||||
namespace midi {
|
||||
|
||||
/*
|
||||
* Maintains a thread-safe, (singleton) list of MIDI devices with associated Binder interfaces,
|
||||
* which are exposed to the Native API via (Java) MidiDevice.mirrorToNative() &
|
||||
* MidiDevice.removeFromNative().
|
||||
* (Called via MidiDeviceManager::addDevice() MidiManager::removeDevice()).
|
||||
*/
|
||||
class MidiDeviceRegistry : public Singleton<MidiDeviceRegistry> {
|
||||
public:
|
||||
/* Add a MIDI Device to the registry.
|
||||
*
|
||||
* server The Binder interface to the MIDI device server.
|
||||
* deviceUId The unique ID of the device obtained from
|
||||
* the Java API via MidiDeviceInfo.getId().
|
||||
*/
|
||||
status_t addDevice(sp<BpMidiDeviceServer> server, int32_t deviceId);
|
||||
|
||||
/* Remove the device (and associated server) from the Device registry.
|
||||
*
|
||||
* deviceUid The ID of the device which was used in the call to addDevice().
|
||||
*/
|
||||
status_t removeDevice(int32_t deviceId);
|
||||
|
||||
/* Gets a device token associated with the device ID. This is used by the
|
||||
* native API to identify/access the device.
|
||||
* Multiple calls without releasing the token will return the same value.
|
||||
*
|
||||
* deviceUid The ID of the device.
|
||||
* deviceTokenPtr Receives the device (native) token associated with the device ID.
|
||||
* returns: OK on success, error code otherwise.
|
||||
*/
|
||||
status_t obtainDeviceToken(int32_t deviceId, AMIDI_Device *deviceTokenPtr);
|
||||
|
||||
/*
|
||||
* Releases the native API device token associated with a MIDI device.
|
||||
*
|
||||
* deviceToken The device (native) token associated with the device ID.
|
||||
*/
|
||||
status_t releaseDevice(AMIDI_Device deviceToken);
|
||||
|
||||
/*
|
||||
* Gets the Device server binder interface associated with the device token.
|
||||
*
|
||||
* deviceToken The device (native) token associated with the device ID.
|
||||
*/
|
||||
status_t getDeviceByToken(AMIDI_Device deviceToken, sp<BpMidiDeviceServer> *devicePtr);
|
||||
|
||||
private:
|
||||
friend class Singleton<MidiDeviceRegistry>;
|
||||
MidiDeviceRegistry();
|
||||
|
||||
// Access Mutex
|
||||
std::mutex mMapsLock;
|
||||
|
||||
// maps device IDs to servers
|
||||
std::map<int32_t, sp<BpMidiDeviceServer>> mServers;
|
||||
|
||||
// maps device tokens to device ID
|
||||
std::map<AMIDI_Device, int32_t> mTokenToUid;
|
||||
|
||||
// maps device IDs to device tokens
|
||||
std::map<int32_t, AMIDI_Device> mUidToToken;
|
||||
|
||||
// Value of next device token to dole out.
|
||||
AMIDI_Device mNextDeviceToken;
|
||||
};
|
||||
|
||||
} // namespace midi
|
||||
} // namespace media
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_MEDIA_MIDI_DEVICE_REGISTRY_H_
|
||||
@@ -1,200 +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.
|
||||
*/
|
||||
|
||||
#include "MidiPortRegistry.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
ANDROID_SINGLETON_STATIC_INSTANCE(media::midi::MidiPortRegistry);
|
||||
|
||||
namespace media {
|
||||
namespace midi {
|
||||
|
||||
//TODO Note that these 2 are identical
|
||||
struct MidiPortRegistry::OutputPort {
|
||||
AMIDI_Device device;
|
||||
sp<IBinder> binderToken;
|
||||
base::unique_fd ufd;
|
||||
};
|
||||
|
||||
struct MidiPortRegistry::InputPort {
|
||||
AMIDI_Device device;
|
||||
sp<IBinder> binderToken;
|
||||
base::unique_fd ufd;
|
||||
};
|
||||
|
||||
MidiPortRegistry::MidiPortRegistry() : mNextOutputPortToken(0), mNextInputPortToken(0) {
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::addOutputPort(
|
||||
AMIDI_Device device,
|
||||
sp<IBinder> portToken,
|
||||
base::unique_fd &&ufd,
|
||||
AMIDI_OutputPort *portPtr) {
|
||||
*portPtr = mNextOutputPortToken++;
|
||||
|
||||
OutputPortEntry* portEntry = new OutputPortEntry;
|
||||
portEntry->port = new OutputPort;
|
||||
portEntry->state = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
|
||||
portEntry->port = new OutputPort;
|
||||
portEntry->port->device = device;
|
||||
portEntry->port->binderToken = portToken;
|
||||
portEntry->port->ufd = std::move(ufd);
|
||||
|
||||
mOutputPortMap[*portPtr] = portEntry;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::removeOutputPort(
|
||||
AMIDI_OutputPort port,
|
||||
AMIDI_Device *devicePtr,
|
||||
sp<IBinder> *portTokenPtr) {
|
||||
OutputPortMap::iterator itr = mOutputPortMap.find(port);
|
||||
if (itr == mOutputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
OutputPortEntry *entry = mOutputPortMap[port];
|
||||
int portState = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
|
||||
while (!entry->state.compare_exchange_weak(portState, MIDI_OUTPUT_PORT_STATE_CLOSED)) {
|
||||
if (portState == MIDI_OUTPUT_PORT_STATE_CLOSED) {
|
||||
return -EINVAL; // Already closed
|
||||
}
|
||||
}
|
||||
*devicePtr = entry->port->device;
|
||||
*portTokenPtr = entry->port->binderToken;
|
||||
delete entry->port;
|
||||
entry->port = nullptr;
|
||||
|
||||
mOutputPortMap.erase(itr);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::getOutputPortFdAndLock(
|
||||
AMIDI_OutputPort port, base::unique_fd **ufdPtr) {
|
||||
if (mOutputPortMap.find(port) == mOutputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
OutputPortEntry *entry = mOutputPortMap[port];
|
||||
int portState = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
|
||||
if (!entry->state.compare_exchange_strong(portState, MIDI_OUTPUT_PORT_STATE_OPEN_ACTIVE)) {
|
||||
// The port has been closed.
|
||||
return -EPIPE;
|
||||
}
|
||||
*ufdPtr = &entry->port->ufd;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::unlockOutputPort(AMIDI_OutputPort port) {
|
||||
if (mOutputPortMap.find(port) == mOutputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
OutputPortEntry *entry = mOutputPortMap[port];
|
||||
entry->state.store(MIDI_OUTPUT_PORT_STATE_OPEN_IDLE);
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::addInputPort(
|
||||
AMIDI_Device device,
|
||||
sp<IBinder> portToken,
|
||||
base::unique_fd &&ufd,
|
||||
AMIDI_InputPort *portPtr) {
|
||||
*portPtr = mNextInputPortToken++;
|
||||
|
||||
InputPortEntry *entry = new InputPortEntry;
|
||||
|
||||
entry->state = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
|
||||
entry->port = new InputPort;
|
||||
entry->port->device = device;
|
||||
entry->port->binderToken = portToken;
|
||||
entry->port->ufd = std::move(ufd);
|
||||
|
||||
mInputPortMap[*portPtr] = entry;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::removeInputPort(
|
||||
AMIDI_InputPort port,
|
||||
AMIDI_Device *devicePtr,
|
||||
sp<IBinder> *portTokenPtr) {
|
||||
InputPortMap::iterator itr = mInputPortMap.find(port);
|
||||
if (itr == mInputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
InputPortEntry *entry = mInputPortMap[port];
|
||||
int portState = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
|
||||
while (!entry->state.compare_exchange_weak(portState, MIDI_INPUT_PORT_STATE_CLOSED)) {
|
||||
if (portState == MIDI_INPUT_PORT_STATE_CLOSED) return -EINVAL; // Already closed
|
||||
}
|
||||
|
||||
*devicePtr = entry->port->device;
|
||||
*portTokenPtr = entry->port->binderToken;
|
||||
delete entry->port;
|
||||
entry->port = nullptr;
|
||||
|
||||
mInputPortMap.erase(itr);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::getInputPortFd(AMIDI_InputPort port, base::unique_fd **ufdPtr) {
|
||||
if (mInputPortMap.find(port) == mInputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
InputPortEntry *entry = mInputPortMap[port];
|
||||
|
||||
*ufdPtr = &entry->port->ufd;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::getInputPortFdAndLock(AMIDI_InputPort port, base::unique_fd **ufdPtr) {
|
||||
if (mInputPortMap.find(port) == mInputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
InputPortEntry *entry = mInputPortMap[port];
|
||||
|
||||
int portState = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
|
||||
if (!entry->state.compare_exchange_strong(portState, MIDI_INPUT_PORT_STATE_OPEN_ACTIVE)) {
|
||||
// The port has been closed.
|
||||
return -EPIPE;
|
||||
}
|
||||
*ufdPtr = &entry->port->ufd;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MidiPortRegistry::MidiPortRegistry::unlockInputPort(AMIDI_InputPort port) {
|
||||
if (mInputPortMap.find(port) == mInputPortMap.end()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
InputPortEntry *entry = mInputPortMap[port];
|
||||
entry->state.store(MIDI_INPUT_PORT_STATE_OPEN_IDLE);
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace midi
|
||||
} // namespace media
|
||||
} // namespace android
|
||||
@@ -1,192 +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_PORT_REGISTRY_H_
|
||||
#define ANDROID_MEDIA_MIDI_PORT_REGISTRY_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <binder/IBinder.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Singleton.h>
|
||||
|
||||
#include "midi.h"
|
||||
|
||||
namespace android {
|
||||
namespace media {
|
||||
namespace midi {
|
||||
|
||||
/*
|
||||
* Maintains lists of all active input and output MIDI ports and controls access to them. Provides
|
||||
* exclusive access to specific MIDI ports.
|
||||
*/
|
||||
class MidiPortRegistry : public Singleton<MidiPortRegistry> {
|
||||
public:
|
||||
/*
|
||||
* Creates an output port entry and associates it with the specified MIDI device.
|
||||
* Called by AMIDI_openOutputPort();
|
||||
*
|
||||
* device The native API device ID.
|
||||
* portToken The port token (returned from the device server).
|
||||
* udf File descriptor for the data port associated with the MIDI output port.
|
||||
* portPtr Receives the native API port ID of the port being opened.
|
||||
*/
|
||||
status_t addOutputPort(
|
||||
AMIDI_Device device,
|
||||
sp<IBinder> portToken,
|
||||
base::unique_fd &&ufd,
|
||||
AMIDI_OutputPort *portPtr);
|
||||
|
||||
/*
|
||||
* Removes for the output port list a previously added output port.
|
||||
* Called by AMIDI_closeOutputPort();
|
||||
*
|
||||
* port The native API port ID of the port being closed.
|
||||
* devicePtr Receives the native API device ID associated with the port.
|
||||
* portTokenPtr Receives the binder token associated with the port.
|
||||
*/
|
||||
status_t removeOutputPort(
|
||||
AMIDI_OutputPort port,
|
||||
AMIDI_Device *devicePtr,
|
||||
sp<IBinder> *portTokenPtr);
|
||||
|
||||
/*
|
||||
* Creates an input port entry and associates it with the specified MIDI device.
|
||||
* Called by AMIDI_openInputPort();
|
||||
*
|
||||
* device The native API device ID.
|
||||
* portToken The port token (returned from the device server).
|
||||
* udf File descriptor for the data port associated with the MIDI input port.
|
||||
* portPtr Receives the native API port ID of the port being opened.
|
||||
*/
|
||||
status_t addInputPort(
|
||||
AMIDI_Device device,
|
||||
sp<IBinder> portToken,
|
||||
base::unique_fd &&ufd,
|
||||
AMIDI_InputPort *portPtr);
|
||||
|
||||
/*
|
||||
* Removes for the input port list a previously added input port.
|
||||
* Called by AMIDI_closeINputPort();
|
||||
*
|
||||
* port The native API port ID of the port being closed.
|
||||
* devicePtr Receives the native API device ID associated with the port.
|
||||
* portTokenPtr Receives the binder token associated with the port.
|
||||
*/
|
||||
status_t removeInputPort(
|
||||
AMIDI_InputPort port,
|
||||
AMIDI_Device *devicePtr,
|
||||
sp<IBinder> *portTokenPtr);
|
||||
|
||||
/*
|
||||
* Retrieves an exclusive-access file descriptor for an output port.
|
||||
* Called from AMIDI_receive().
|
||||
*
|
||||
* port The native API id of the output port.
|
||||
* ufdPtr Receives the exclusive-access file descriptor for the output port.
|
||||
*/
|
||||
status_t getOutputPortFdAndLock(AMIDI_OutputPort port, base::unique_fd **ufdPtr);
|
||||
|
||||
/*
|
||||
* Releases exclusive-access to the port and invalidates the previously received file
|
||||
* descriptor.
|
||||
* Called from AMIDI_receive().
|
||||
*
|
||||
* port The native API id of the output port.
|
||||
*/
|
||||
status_t unlockOutputPort(AMIDI_OutputPort port);
|
||||
|
||||
/*
|
||||
* Retrieves an exclusive-access file descriptor for an input port.
|
||||
* (Not being used as (perhaps) AMIDI_sendWithTimestamp() doesn't need exclusive access
|
||||
* to the port).
|
||||
*
|
||||
* port The native API id of the input port.
|
||||
* ufdPtr Receives the exclusive-access file descriptor for the input port.
|
||||
*/
|
||||
status_t getInputPortFdAndLock(AMIDI_InputPort port, base::unique_fd **ufdPtr);
|
||||
|
||||
/*
|
||||
* Releases exclusive-access to the port and invalidates the previously received file
|
||||
* descriptor.
|
||||
* (Not used. See above).
|
||||
*
|
||||
* port The native API id of the input port.
|
||||
*/
|
||||
status_t unlockInputPort(AMIDI_InputPort port);
|
||||
|
||||
/*
|
||||
* Retrieves an unlocked (multi-access) file descriptor for an input port.
|
||||
* Used by AMIDI_sendWith(), AMIDI_sendWithTimestamp & AMIDI_flush.
|
||||
*
|
||||
* port The native API id of the input port.
|
||||
* ufdPtr Receives the multi-access file descriptor for the input port.
|
||||
*/
|
||||
status_t getInputPortFd(AMIDI_InputPort port, base::unique_fd **ufdPtr);
|
||||
|
||||
private:
|
||||
friend class Singleton<MidiPortRegistry>;
|
||||
MidiPortRegistry();
|
||||
|
||||
/*
|
||||
* Output (data receiving) ports.
|
||||
*/
|
||||
struct OutputPort;
|
||||
enum {
|
||||
MIDI_OUTPUT_PORT_STATE_CLOSED = 0,
|
||||
MIDI_OUTPUT_PORT_STATE_OPEN_IDLE,
|
||||
MIDI_OUTPUT_PORT_STATE_OPEN_ACTIVE
|
||||
};
|
||||
|
||||
struct OutputPortEntry {
|
||||
std::atomic_int state;
|
||||
OutputPort *port;
|
||||
};
|
||||
|
||||
typedef std::map<AMIDI_OutputPort, OutputPortEntry*> OutputPortMap;
|
||||
// Access is synchronized per record via 'state' field.
|
||||
std::atomic<AMIDI_OutputPort> mNextOutputPortToken;
|
||||
OutputPortMap mOutputPortMap;
|
||||
|
||||
/*
|
||||
* Input (data sending) ports.
|
||||
*/
|
||||
struct InputPort;
|
||||
enum {
|
||||
MIDI_INPUT_PORT_STATE_CLOSED = 0,
|
||||
MIDI_INPUT_PORT_STATE_OPEN_IDLE,
|
||||
MIDI_INPUT_PORT_STATE_OPEN_ACTIVE
|
||||
};
|
||||
|
||||
struct InputPortEntry {
|
||||
std::atomic_int state;
|
||||
InputPort *port;
|
||||
};
|
||||
|
||||
typedef std::map<AMIDI_OutputPort, InputPortEntry*> InputPortMap;
|
||||
// Access is synchronized per record via 'state' field.
|
||||
std::atomic<AMIDI_InputPort> mNextInputPortToken;
|
||||
InputPortMap mInputPortMap;
|
||||
|
||||
};
|
||||
|
||||
} // namespace midi
|
||||
} // namespace media
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_MEDIA_MIDI_PORT_REGISTRY_H_
|
||||
@@ -25,10 +25,9 @@
|
||||
|
||||
#include "android/media/midi/BpMidiDeviceServer.h"
|
||||
#include "media/MidiDeviceInfo.h"
|
||||
#include "MidiDeviceRegistry.h"
|
||||
#include "MidiPortRegistry.h"
|
||||
|
||||
#include "midi.h"
|
||||
#include "midi_internal.h"
|
||||
|
||||
using android::IBinder;
|
||||
using android::BBinder;
|
||||
@@ -37,13 +36,28 @@ using android::sp;
|
||||
using android::status_t;
|
||||
using android::base::unique_fd;
|
||||
using android::binder::Status;
|
||||
using android::media::midi::BpMidiDeviceServer;
|
||||
using android::media::midi::MidiDeviceInfo;
|
||||
using android::media::midi::MidiDeviceRegistry;
|
||||
using android::media::midi::MidiPortRegistry;
|
||||
|
||||
struct AMIDI_Port {
|
||||
std::atomic_int state;
|
||||
AMIDI_Device *device;
|
||||
sp<IBinder> binderToken;
|
||||
unique_fd ufd;
|
||||
};
|
||||
|
||||
#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
|
||||
|
||||
enum {
|
||||
MIDI_PORT_STATE_CLOSED = 0,
|
||||
MIDI_PORT_STATE_OPEN_IDLE,
|
||||
MIDI_PORT_STATE_OPEN_ACTIVE
|
||||
};
|
||||
|
||||
enum {
|
||||
PORTTYPE_OUTPUT = 0,
|
||||
PORTTYPE_INPUT = 1
|
||||
};
|
||||
|
||||
/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
|
||||
*
|
||||
* Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
|
||||
@@ -63,20 +77,12 @@ using android::media::midi::MidiPortRegistry;
|
||||
* So 'read()' always returns a whole message.
|
||||
*/
|
||||
|
||||
status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr) {
|
||||
return MidiDeviceRegistry::getInstance().obtainDeviceToken(id, devicePtr);
|
||||
}
|
||||
|
||||
status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPtr) {
|
||||
sp<BpMidiDeviceServer> deviceServer;
|
||||
status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_getDeviceInfo bad device token %d: %d", device, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Device Functions
|
||||
*/
|
||||
status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) {
|
||||
MidiDeviceInfo deviceInfo;
|
||||
Status txResult = deviceServer->getDeviceInfo(&deviceInfo);
|
||||
Status txResult = device->server->getDeviceInfo(&deviceInfo);
|
||||
if (!txResult.isOk()) {
|
||||
ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
|
||||
return txResult.transactionError();
|
||||
@@ -87,49 +93,74 @@ status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPt
|
||||
deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
|
||||
deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
|
||||
deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Port Helpers
|
||||
*/
|
||||
static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
|
||||
AMIDI_Port **portPtr) {
|
||||
sp<BBinder> portToken(new BBinder());
|
||||
unique_fd ufd;
|
||||
Status txResult = type == PORTTYPE_OUTPUT
|
||||
? device->server->openOutputPort(portToken, portNumber, &ufd)
|
||||
: device->server->openInputPort(portToken, portNumber, &ufd);
|
||||
if (!txResult.isOk()) {
|
||||
ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError());
|
||||
return txResult.transactionError();
|
||||
}
|
||||
|
||||
AMIDI_Port* port = new AMIDI_Port;
|
||||
port->state = MIDI_PORT_STATE_OPEN_IDLE;
|
||||
port->device = device;
|
||||
port->binderToken = portToken;
|
||||
port->ufd = std::move(ufd);
|
||||
|
||||
*portPtr = port;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static status_t AMIDI_closePort(AMIDI_Port *port) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Status txResult = port->device->server->closePort(port->binderToken);
|
||||
if (!txResult.isOk()) {
|
||||
return txResult.transactionError();
|
||||
}
|
||||
|
||||
delete port;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Output (receiving) API
|
||||
*/
|
||||
status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputPort *outputPortPtr) {
|
||||
sp<BpMidiDeviceServer> deviceServer;
|
||||
status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_openOutputPort bad device token %d: %d", device, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sp<BBinder> portToken(new BBinder());
|
||||
unique_fd ufd;
|
||||
Status txResult = deviceServer->openOutputPort(portToken, portNumber, &ufd);
|
||||
if (!txResult.isOk()) {
|
||||
ALOGE("AMIDI_openOutputPort transaction error: %d", txResult.transactionError());
|
||||
return txResult.transactionError();
|
||||
}
|
||||
|
||||
result = MidiPortRegistry::getInstance().addOutputPort(
|
||||
device, portToken, std::move(ufd), outputPortPtr);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_openOutputPort port registration error: %d", result);
|
||||
// Close port
|
||||
return result;
|
||||
}
|
||||
return OK;
|
||||
status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
|
||||
AMIDI_OutputPort **outputPortPtr) {
|
||||
return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr);
|
||||
}
|
||||
|
||||
ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
|
||||
unique_fd *ufd;
|
||||
// TODO: May return a nicer self-unlocking object
|
||||
status_t result = MidiPortRegistry::getInstance().getOutputPortFdAndLock(outputPort, &ufd);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
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;
|
||||
}
|
||||
|
||||
status_t result = OK;
|
||||
ssize_t messagesRead = 0;
|
||||
while (messagesRead < maxMessages) {
|
||||
struct pollfd checkFds[1] = { { *ufd, POLLIN, 0 } };
|
||||
struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } };
|
||||
int pollResult = poll(checkFds, 1, 0);
|
||||
if (pollResult < 1) {
|
||||
result = android::INVALID_OPERATION;
|
||||
@@ -139,7 +170,7 @@ ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssiz
|
||||
AMIDI_Message *message = &messages[messagesRead];
|
||||
uint8_t readBuffer[AMIDI_PACKET_SIZE];
|
||||
memset(readBuffer, 0, sizeof(readBuffer));
|
||||
ssize_t readCount = read(*ufd, readBuffer, sizeof(readBuffer));
|
||||
ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer));
|
||||
if (readCount == EINTR) {
|
||||
continue;
|
||||
}
|
||||
@@ -157,100 +188,38 @@ ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssiz
|
||||
if (dataSize) {
|
||||
memcpy(message->buffer, readBuffer + 1, dataSize);
|
||||
}
|
||||
message->timestamp = *(uint64_t*) (readBuffer + readCount - sizeof(uint64_t));
|
||||
message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
|
||||
}
|
||||
message->len = dataSize;
|
||||
++messagesRead;
|
||||
}
|
||||
|
||||
MidiPortRegistry::getInstance().unlockOutputPort(outputPort);
|
||||
port->state.store(MIDI_PORT_STATE_OPEN_IDLE);
|
||||
|
||||
return result == OK ? messagesRead : result;
|
||||
}
|
||||
|
||||
status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort) {
|
||||
AMIDI_Device device;
|
||||
sp<IBinder> portToken;
|
||||
status_t result =
|
||||
MidiPortRegistry::getInstance().removeOutputPort(outputPort, &device, &portToken);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
sp<BpMidiDeviceServer> deviceServer;
|
||||
result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Status txResult = deviceServer->closePort(portToken);
|
||||
if (!txResult.isOk()) {
|
||||
return txResult.transactionError();
|
||||
}
|
||||
return OK;
|
||||
status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) {
|
||||
return AMIDI_closePort((AMIDI_Port*)outputPort);
|
||||
}
|
||||
|
||||
/*
|
||||
* Input (sending) API
|
||||
*/
|
||||
status_t AMIDI_openInputPort(AMIDI_Device device, int portNumber, AMIDI_InputPort *inputPortPtr) {
|
||||
sp<BpMidiDeviceServer> deviceServer;
|
||||
status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_openInputPort bad device token %d: %d", device, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sp<BBinder> portToken(new BBinder());
|
||||
unique_fd ufd; // this is the file descriptor of the "receive" port s
|
||||
Status txResult = deviceServer->openInputPort(portToken, portNumber, &ufd);
|
||||
if (!txResult.isOk()) {
|
||||
ALOGE("AMIDI_openInputPort transaction error: %d", txResult.transactionError());
|
||||
return txResult.transactionError();
|
||||
}
|
||||
|
||||
result = MidiPortRegistry::getInstance().addInputPort(
|
||||
device, portToken, std::move(ufd), inputPortPtr);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_openInputPort port registration error: %d", result);
|
||||
// Close port
|
||||
return result;
|
||||
}
|
||||
|
||||
return OK;
|
||||
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) {
|
||||
AMIDI_Device device;
|
||||
sp<IBinder> portToken;
|
||||
status_t result = MidiPortRegistry::getInstance().removeInputPort(
|
||||
inputPort, &device, &portToken);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_closeInputPort remove port error: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sp<BpMidiDeviceServer> deviceServer;
|
||||
result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
|
||||
if (result != OK) {
|
||||
ALOGE("AMIDI_closeInputPort can't find device error: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Status txResult = deviceServer->closePort(portToken);
|
||||
if (!txResult.isOk()) {
|
||||
result = txResult.transactionError();
|
||||
ALOGE("AMIDI_closeInputPort transaction error: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return OK;
|
||||
status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) {
|
||||
return AMIDI_closePort((AMIDI_Port*)inputPort);
|
||||
}
|
||||
|
||||
ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort /*inputPort*/) {
|
||||
ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) {
|
||||
return SIZE_MIDIRECEIVEBUFFER;
|
||||
}
|
||||
|
||||
static ssize_t AMIDI_makeSendBuffer(uint8_t *buffer, uint8_t *data, ssize_t numBytes, uint64_t timestamp) {
|
||||
static ssize_t AMIDI_makeSendBuffer(
|
||||
uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) {
|
||||
buffer[0] = AMIDI_OPCODE_DATA;
|
||||
memcpy(buffer + 1, data, numBytes);
|
||||
memcpy(buffer + 1 + numBytes, ×tamp, sizeof(timestamp));
|
||||
@@ -264,11 +233,11 @@ static ssize_t AMIDI_makeSendBuffer(uint8_t *buffer, uint8_t *data, ssize_t numB
|
||||
// }
|
||||
//}
|
||||
|
||||
ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes) {
|
||||
ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) {
|
||||
return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
|
||||
}
|
||||
|
||||
ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *data,
|
||||
ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data,
|
||||
ssize_t numBytes, int64_t timestamp) {
|
||||
|
||||
if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
|
||||
@@ -277,15 +246,9 @@ ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *data,
|
||||
|
||||
// AMIDI_logBuffer(data, numBytes);
|
||||
|
||||
unique_fd *ufd = NULL;
|
||||
status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
|
||||
ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
|
||||
ssize_t numWritten = write(*ufd, writeBuffer, numTransferBytes);
|
||||
ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
|
||||
|
||||
if (numWritten < numTransferBytes) {
|
||||
ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
|
||||
@@ -295,16 +258,10 @@ ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *data,
|
||||
return numWritten - AMIDI_PACKET_OVERHEAD;
|
||||
}
|
||||
|
||||
status_t AMIDI_flush(AMIDI_InputPort inputPort) {
|
||||
unique_fd *ufd = NULL;
|
||||
status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t AMIDI_flush(AMIDI_InputPort *inputPort) {
|
||||
uint8_t opCode = AMIDI_OPCODE_FLUSH;
|
||||
ssize_t numTransferBytes = 1;
|
||||
ssize_t numWritten = write(*ufd, &opCode, numTransferBytes);
|
||||
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",
|
||||
|
||||
@@ -29,22 +29,11 @@ using android::status_t;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//typedef struct _AMIDI_Device;
|
||||
//typedef struct _AMIDI_InputPort;
|
||||
//typedef struct _AMIDI_OutputPort;
|
||||
//typedef _AMIDI_Device* AMIDI_Device;
|
||||
//typedef _AMIDI_InputPort* AMIDI_InputPort;
|
||||
//typedef _AMIDI_OutputPort* AMIDI_OutputPort;
|
||||
struct AMIDI_Device;
|
||||
struct AMIDI_InputPort;
|
||||
struct AMIDI_OutputPort;
|
||||
|
||||
typedef int32_t AMIDI_Device;
|
||||
typedef int32_t AMIDI_InputPort;
|
||||
typedef int32_t AMIDI_OutputPort;
|
||||
|
||||
//TODO - Do we want to wrap this stuff in namespace android { namespace media { namespace midi {?
|
||||
|
||||
enum {
|
||||
AMIDI_INVALID_HANDLE = -1
|
||||
};
|
||||
#define AMIDI_INVALID_HANDLE NULL
|
||||
|
||||
enum {
|
||||
AMIDI_OPCODE_DATA = 1,
|
||||
@@ -56,10 +45,10 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t opcode;
|
||||
uint8_t buffer[AMIDI_BUFFER_SIZE];
|
||||
size_t len;
|
||||
int64_t timestamp;
|
||||
uint32_t opcode;
|
||||
uint8_t buffer[AMIDI_BUFFER_SIZE];
|
||||
size_t len;
|
||||
int64_t timestamp;
|
||||
} AMIDI_Message;
|
||||
|
||||
enum {
|
||||
@@ -79,16 +68,6 @@ typedef struct {
|
||||
/*
|
||||
* Device API
|
||||
*/
|
||||
/*
|
||||
* Retrieves the native API device token for the specified Java API device ID.
|
||||
*
|
||||
* uid The Java API id of the device.
|
||||
* devicePtr Receives the associated native API token for the device.
|
||||
*
|
||||
* Returns OK or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr);
|
||||
|
||||
/*
|
||||
* Retrieves information for the native MIDI device.
|
||||
*
|
||||
@@ -97,7 +76,7 @@ status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr);
|
||||
*
|
||||
* Returns OK or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPtr);
|
||||
status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr);
|
||||
|
||||
/*
|
||||
* API for receiving data from the Output port of a device.
|
||||
@@ -111,7 +90,8 @@ status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPt
|
||||
*
|
||||
* Returns OK, or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputPort *outputPortPtr);
|
||||
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).
|
||||
@@ -122,7 +102,7 @@ status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputP
|
||||
*
|
||||
* Returns the number of messages received, or a (negative) error code.
|
||||
*/
|
||||
ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssize_t maxMessages);
|
||||
ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages);
|
||||
|
||||
/*
|
||||
* Closes the output port.
|
||||
@@ -131,7 +111,7 @@ ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssiz
|
||||
*
|
||||
* Returns OK, or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort);
|
||||
status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort);
|
||||
|
||||
/*
|
||||
* API for sending data to the Input port of a device.
|
||||
@@ -145,12 +125,12 @@ status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort);
|
||||
*
|
||||
* Returns OK, or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_openInputPort(AMIDI_Device device, int portNumber, AMIDI_InputPort *inputPortPtr);
|
||||
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);
|
||||
ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort *inputPort);
|
||||
|
||||
/*
|
||||
* Sends data to the specified input port.
|
||||
@@ -161,7 +141,7 @@ ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort inputPort);
|
||||
*
|
||||
* Returns The number of bytes sent or a (negative) error code.
|
||||
*/
|
||||
ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes);
|
||||
ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes);
|
||||
|
||||
/*
|
||||
* Sends data to the specified input port with a timestamp.
|
||||
@@ -173,7 +153,7 @@ ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes)
|
||||
*
|
||||
* Returns The number of bytes sent or a (negative) error code.
|
||||
*/
|
||||
ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *buffer,
|
||||
ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *buffer,
|
||||
ssize_t numBytes, int64_t timestamp);
|
||||
|
||||
/*
|
||||
@@ -183,7 +163,7 @@ ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *buffer,
|
||||
*
|
||||
* Returns OK, or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_flush(AMIDI_InputPort inputPort);
|
||||
status_t AMIDI_flush(AMIDI_InputPort *inputPort);
|
||||
|
||||
/*
|
||||
* Closes the input port.
|
||||
@@ -193,7 +173,7 @@ status_t AMIDI_flush(AMIDI_InputPort inputPort);
|
||||
*
|
||||
* Returns OK, or a (negative) error code.
|
||||
*/
|
||||
status_t AMIDI_closeInputPort(AMIDI_InputPort inputPort);
|
||||
status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
27
media/native/midi/midi_internal.h
Normal file
27
media/native/midi/midi_internal.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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_INTERNAL_H_
|
||||
#define ANDROID_MEDIA_MIDI_INTERNAL_H_
|
||||
|
||||
#include "android/media/midi/BpMidiDeviceServer.h"
|
||||
|
||||
struct AMIDI_Device {
|
||||
android::sp<android::media::midi::BpMidiDeviceServer> server;
|
||||
int32_t deviceId;
|
||||
};
|
||||
|
||||
#endif // ANDROID_MEDIA_MIDI_INTERNAL_H_
|
||||
@@ -66,8 +66,8 @@ static short playBuffer[maxPlaySamples];
|
||||
|
||||
static std::atomic_ullong sharedCounter;
|
||||
|
||||
static AMIDI_Device midiDevice = AMIDI_INVALID_HANDLE;
|
||||
static std::atomic<AMIDI_OutputPort> midiOutputPort(AMIDI_INVALID_HANDLE);
|
||||
static AMIDI_Device* midiDevice = AMIDI_INVALID_HANDLE;
|
||||
static std::atomic<AMIDI_OutputPort*> midiOutputPort(AMIDI_INVALID_HANDLE);
|
||||
|
||||
static int setPlaySamples(int newPlaySamples)
|
||||
{
|
||||
@@ -86,7 +86,7 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void */*context*/
|
||||
{
|
||||
sharedCounter++;
|
||||
|
||||
AMIDI_OutputPort outputPort = midiOutputPort.load();
|
||||
AMIDI_OutputPort* outputPort = midiOutputPort.load();
|
||||
if (outputPort != AMIDI_INVALID_HANDLE) {
|
||||
char midiDumpBuffer[1024];
|
||||
ssize_t midiReceived = AMIDI_receive(
|
||||
@@ -235,20 +235,21 @@ jobjectArray Java_com_example_android_nativemididemo_NativeMidi_getRecentMessage
|
||||
}
|
||||
|
||||
void Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
|
||||
JNIEnv*, jobject, jint deviceId, jint portNumber) {
|
||||
JNIEnv*, jobject, jlong deviceHandle, jint portNumber) {
|
||||
char buffer[1024];
|
||||
|
||||
int result = AMIDI_getDeviceById(deviceId, &midiDevice);
|
||||
if (result == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Obtained device token for uid %d: token %d", deviceId, midiDevice);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Could not obtain device token for uid %d: %d", deviceId, result);
|
||||
}
|
||||
midiDevice = (AMIDI_Device*)deviceHandle;
|
||||
// int result = AMIDI_getDeviceById(deviceId, &midiDevice);
|
||||
// if (result == 0) {
|
||||
// snprintf(buffer, sizeof(buffer), "Obtained device token for uid %d: token %d", deviceId, midiDevice);
|
||||
// } else {
|
||||
// snprintf(buffer, sizeof(buffer), "Could not obtain device token for uid %d: %d", deviceId, result);
|
||||
// }
|
||||
nativemididemo::writeMessage(buffer);
|
||||
if (result) return;
|
||||
// if (result) return;
|
||||
|
||||
AMIDI_DeviceInfo deviceInfo;
|
||||
result = AMIDI_getDeviceInfo(midiDevice, &deviceInfo);
|
||||
int result = AMIDI_getDeviceInfo(midiDevice, &deviceInfo);
|
||||
if (result == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Device info: uid %d, type %d, priv %d, ports %d I / %d O",
|
||||
deviceInfo.uid, deviceInfo.type, deviceInfo.isPrivate,
|
||||
@@ -259,27 +260,27 @@ void Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
|
||||
nativemididemo::writeMessage(buffer);
|
||||
if (result) return;
|
||||
|
||||
AMIDI_OutputPort outputPort;
|
||||
AMIDI_OutputPort* outputPort;
|
||||
result = AMIDI_openOutputPort(midiDevice, portNumber, &outputPort);
|
||||
if (result == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Opened port %d: token %d", portNumber, outputPort);
|
||||
snprintf(buffer, sizeof(buffer), "Opened port %d: token %p", portNumber, outputPort);
|
||||
midiOutputPort.store(outputPort);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Could not open port %d: %d", deviceId, result);
|
||||
snprintf(buffer, sizeof(buffer), "Could not open port %p: %d", midiDevice, result);
|
||||
}
|
||||
nativemididemo::writeMessage(buffer);
|
||||
}
|
||||
|
||||
void Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi(
|
||||
JNIEnv*, jobject) {
|
||||
AMIDI_OutputPort outputPort = midiOutputPort.exchange(AMIDI_INVALID_HANDLE);
|
||||
AMIDI_OutputPort* outputPort = midiOutputPort.exchange(AMIDI_INVALID_HANDLE);
|
||||
if (outputPort == AMIDI_INVALID_HANDLE) return;
|
||||
int result = AMIDI_closeOutputPort(outputPort);
|
||||
char buffer[1024];
|
||||
if (result == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Closed port by token %d", outputPort);
|
||||
snprintf(buffer, sizeof(buffer), "Closed port by token %p", outputPort);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Could not close port by token %d: %d", outputPort, result);
|
||||
snprintf(buffer, sizeof(buffer), "Could not close port by token %p: %d", outputPort, result);
|
||||
}
|
||||
nativemididemo::writeMessage(buffer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user