blob: cacedbe002f03fb899c0b4a91636d04ea52fbdaf [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.mojo.system.impl;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.DataPipe;
import org.chromium.mojo.system.DataPipe.ConsumerHandle;
import org.chromium.mojo.system.DataPipe.ProducerHandle;
import org.chromium.mojo.system.Handle;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojo.system.MojoResult;
import org.chromium.mojo.system.Pair;
import org.chromium.mojo.system.SharedBufferHandle;
import org.chromium.mojo.system.SharedBufferHandle.DuplicateOptions;
import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
import org.chromium.mojo.system.UntypedHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of {@link Core}.
*/
@JNINamespace("mojo::android")
public class CoreImpl implements Core, AsyncWaiter {
/**
* Discard flag for the |MojoReadData| operation.
*/
private static final int MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
/**
* the size of a handle, in bytes.
*/
private static final int HANDLE_SIZE = 4;
/**
* the size of a flag, in bytes.
*/
private static final int FLAG_SIZE = 4;
/**
* The mojo handle for an invalid handle.
*/
static final int INVALID_HANDLE = 0;
private static class LazyHolder {
private static final Core INSTANCE = new CoreImpl();
}
/**
* @return the instance.
*/
public static Core getInstance() {
return LazyHolder.INSTANCE;
}
private CoreImpl() {
nativeConstructor();
}
/**
* @see Core#getTimeTicksNow()
*/
@Override
public long getTimeTicksNow() {
return nativeGetTimeTicksNow();
}
/**
* @see Core#waitMany(List, long)
*/
@Override
public WaitManyResult waitMany(List<Pair<Handle, WaitFlags>> handles, long deadline) {
// Allocate a direct buffer to allow native code not to reach back to java. Buffer will
// contain all mojo handles, followed by all flags values.
ByteBuffer buffer = allocateDirectBuffer(handles.size() * 8);
int index = 0;
for (Pair<Handle, WaitFlags> handle : handles) {
buffer.putInt(HANDLE_SIZE * index, getMojoHandle(handle.first));
buffer.putInt(HANDLE_SIZE * handles.size() + FLAG_SIZE * index,
handle.second.getFlags());
index++;
}
int code = nativeWaitMany(buffer, deadline);
WaitManyResult result = new WaitManyResult();
// If result is greater than 0, result is the indexed of the available handle. To make sure
// it cannot be misinterpreted, set handleIndex to a negative number in case of error.
result.setHandleIndex(code);
result.setMojoResult(filterMojoResultForWait(code));
return result;
}
/**
* @see Core#wait(Handle, WaitFlags, long)
*/
@Override
public int wait(Handle handle, WaitFlags flags, long deadline) {
return filterMojoResultForWait(nativeWait(getMojoHandle(handle),
flags.getFlags(), deadline));
}
/**
* @see Core#createMessagePipe()
*/
@Override
public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe() {
NativeCreationResult result = nativeCreateMessagePipe();
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
return Pair.<MessagePipeHandle, MessagePipeHandle> create(
new MessagePipeHandleImpl(this, result.getMojoHandle1()),
new MessagePipeHandleImpl(this, result.getMojoHandle2()));
}
/**
* @see Core#createDataPipe(DataPipe.CreateOptions)
*/
@Override
public Pair<ProducerHandle, ConsumerHandle> createDataPipe(DataPipe.CreateOptions options) {
ByteBuffer optionsBuffer = null;
if (options != null) {
optionsBuffer = allocateDirectBuffer(16);
optionsBuffer.putInt(0, 16);
optionsBuffer.putInt(4, options.getFlags().getFlags());
optionsBuffer.putInt(8, options.getElementNumBytes());
optionsBuffer.putInt(12, options.getCapacityNumBytes());
}
NativeCreationResult result = nativeCreateDataPipe(optionsBuffer);
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
return Pair.<ProducerHandle, ConsumerHandle> create(
new DataPipeProducerHandleImpl(this, result.getMojoHandle1()),
new DataPipeConsumerHandleImpl(this, result.getMojoHandle2()));
}
/**
* @see Core#createSharedBuffer(SharedBufferHandle.CreateOptions, long)
*/
@Override
public SharedBufferHandle createSharedBuffer(
SharedBufferHandle.CreateOptions options, long numBytes) {
ByteBuffer optionsBuffer = null;
if (options != null) {
optionsBuffer = allocateDirectBuffer(8);
optionsBuffer.putInt(0, 8);
optionsBuffer.putInt(4, options.getFlags().getFlags());
}
NativeCreationResult result = nativeCreateSharedBuffer(optionsBuffer, numBytes);
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
assert result.getMojoHandle2() == 0;
return new SharedBufferHandleImpl(this, result.getMojoHandle1());
}
/**
* @see Core#getDefaultAsyncWaiter()
*/
@Override
public AsyncWaiter getDefaultAsyncWaiter() {
return this;
}
/**
* @see AsyncWaiter#asyncWait(Handle, Core.WaitFlags, long, Callback)
*/
@Override
public Cancellable asyncWait(Handle handle, WaitFlags flags, long deadline,
Callback callback) {
return nativeAsyncWait(getMojoHandle(handle),
flags.getFlags(), deadline, callback);
}
int closeWithResult(int mojoHandle) {
return nativeClose(mojoHandle);
}
void close(int mojoHandle) {
int mojoResult = nativeClose(mojoHandle);
if (mojoResult != MojoResult.OK) {
throw new MojoException(mojoResult);
}
}
/**
* @see MessagePipeHandle#writeMessage(ByteBuffer, List, MessagePipeHandle.WriteFlags)
*/
void writeMessage(MessagePipeHandleImpl pipeHandle, ByteBuffer bytes,
List<? extends Handle> handles, MessagePipeHandle.WriteFlags flags) {
ByteBuffer handlesBuffer = null;
if (handles != null && !handles.isEmpty()) {
handlesBuffer = allocateDirectBuffer(handles.size() * HANDLE_SIZE);
for (Handle handle : handles) {
handlesBuffer.putInt(getMojoHandle(handle));
}
handlesBuffer.position(0);
}
int mojoResult = nativeWriteMessage(pipeHandle.getMojoHandle(), bytes,
bytes == null ? 0 : bytes.limit(), handlesBuffer,
flags.getFlags());
if (mojoResult != MojoResult.OK) {
throw new MojoException(mojoResult);
}
// Success means the handles have been invalidated.
if (handles != null) {
for (Handle handle : handles) {
if (handle.isValid()) {
((HandleBase) handle).invalidateHandle();
}
}
}
}
/**
* @see MessagePipeHandle#readMessage(ByteBuffer, int, MessagePipeHandle.ReadFlags)
*/
MessagePipeHandle.ReadMessageResult readMessage(MessagePipeHandleImpl handle,
ByteBuffer bytes, int maxNumberOfHandles,
MessagePipeHandle.ReadFlags flags) {
ByteBuffer handlesBuffer = null;
if (maxNumberOfHandles > 0) {
handlesBuffer = allocateDirectBuffer(maxNumberOfHandles * HANDLE_SIZE);
}
MessagePipeHandle.ReadMessageResult result = nativeReadMessage(
handle.getMojoHandle(), bytes, handlesBuffer, flags.getFlags());
if (result.getMojoResult() != MojoResult.OK &&
result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED &&
result.getMojoResult() != MojoResult.SHOULD_WAIT) {
throw new MojoException(result.getMojoResult());
}
if (result.getMojoResult() == MojoResult.OK) {
if (bytes != null) {
bytes.position(0);
bytes.limit(result.getMessageSize());
}
List<UntypedHandle> handles = new ArrayList<UntypedHandle>(
result.getHandlesCount());
for (int i = 0; i < result.getHandlesCount(); ++i) {
int mojoHandle = handlesBuffer.getInt(HANDLE_SIZE * i);
handles.add(new UntypedHandleImpl(this, mojoHandle));
}
result.setHandles(handles);
}
return result;
}
/**
* @see ConsumerHandle#discardData(int, DataPipe.ReadFlags)
*/
int discardData(DataPipeConsumerHandleImpl handle, int numBytes,
DataPipe.ReadFlags flags) {
int result = nativeReadData(handle.getMojoHandle(), null, numBytes,
flags.getFlags() | MOJO_READ_DATA_FLAG_DISCARD);
if (result < 0) {
throw new MojoException(result);
}
return result;
}
/**
* @see ConsumerHandle#readData(ByteBuffer, DataPipe.ReadFlags)
*/
int readData(DataPipeConsumerHandleImpl handle, ByteBuffer elements,
DataPipe.ReadFlags flags) {
int result = nativeReadData(handle.getMojoHandle(), elements,
elements == null ? 0 : elements.capacity(),
flags.getFlags());
if (result < 0) {
throw new MojoException(result);
}
if (elements != null) {
elements.limit(result);
}
return result;
}
/**
* @see ConsumerHandle#beginReadData(int, DataPipe.ReadFlags)
*/
ByteBuffer beginReadData(DataPipeConsumerHandleImpl handle,
int numBytes, DataPipe.ReadFlags flags) {
NativeCodeAndBufferResult result = nativeBeginReadData(
handle.getMojoHandle(),
numBytes,
flags.getFlags());
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
return result.getBuffer().asReadOnlyBuffer();
}
/**
* @see ConsumerHandle#endReadData(int)
*/
void endReadData(DataPipeConsumerHandleImpl handle,
int numBytesRead) {
int result = nativeEndReadData(handle.getMojoHandle(), numBytesRead);
if (result != MojoResult.OK) {
throw new MojoException(result);
}
}
/**
* @see ProducerHandle#writeData(ByteBuffer, DataPipe.WriteFlags)
*/
int writeData(DataPipeProducerHandleImpl handle, ByteBuffer elements,
DataPipe.WriteFlags flags) {
return nativeWriteData(handle.getMojoHandle(), elements, elements.limit(),
flags.getFlags());
}
/**
* @see ProducerHandle#beginWriteData(int, DataPipe.WriteFlags)
*/
ByteBuffer beginWriteData(DataPipeProducerHandleImpl handle,
int numBytes, DataPipe.WriteFlags flags) {
NativeCodeAndBufferResult result = nativeBeginWriteData(
handle.getMojoHandle(),
numBytes,
flags.getFlags());
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
return result.getBuffer();
}
/**
* @see ProducerHandle#endWriteData(int)
*/
void endWriteData(DataPipeProducerHandleImpl handle,
int numBytesWritten) {
int result = nativeEndWriteData(handle.getMojoHandle(), numBytesWritten);
if (result != MojoResult.OK) {
throw new MojoException(result);
}
}
/**
* @see SharedBufferHandle#duplicate(DuplicateOptions)
*/
SharedBufferHandle duplicate(SharedBufferHandleImpl handle,
DuplicateOptions options) {
ByteBuffer optionsBuffer = null;
if (options != null) {
optionsBuffer = allocateDirectBuffer(8);
optionsBuffer.putInt(0, 8);
optionsBuffer.putInt(4, options.getFlags().getFlags());
}
NativeCreationResult result = nativeDuplicate(handle.getMojoHandle(),
optionsBuffer);
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
assert result.getMojoHandle2() == 0;
return new SharedBufferHandleImpl(this, result.getMojoHandle1());
}
/**
* @see SharedBufferHandle#map(long, long, MapFlags)
*/
ByteBuffer map(SharedBufferHandleImpl handle, long offset, long numBytes,
MapFlags flags) {
NativeCodeAndBufferResult result = nativeMap(handle.getMojoHandle(), offset, numBytes,
flags.getFlags());
if (result.getMojoResult() != MojoResult.OK) {
throw new MojoException(result.getMojoResult());
}
return result.getBuffer();
}
/**
* @see SharedBufferHandle#unmap(ByteBuffer)
*/
void unmap(ByteBuffer buffer) {
int result = nativeUnmap(buffer);
if (result != MojoResult.OK) {
throw new MojoException(result);
}
}
/**
* @return the mojo handle associated to the given handle, considering invalid handles.
*/
private int getMojoHandle(Handle handle) {
if (handle.isValid()) {
return ((HandleBase) handle).getMojoHandle();
}
return 0;
}
private static boolean isUnrecoverableError(int code) {
switch (code) {
case MojoResult.OK:
case MojoResult.DEADLINE_EXCEEDED:
case MojoResult.CANCELLED:
case MojoResult.FAILED_PRECONDITION:
return false;
default:
return true;
}
}
private static int filterMojoResult(int code) {
if (code >= 0) {
return MojoResult.OK;
}
return code;
}
private static int filterMojoResultForWait(int code) {
int finalCode = filterMojoResult(code);
if (isUnrecoverableError(finalCode)) {
throw new MojoException(finalCode);
}
return finalCode;
}
private static ByteBuffer allocateDirectBuffer(int capacity) {
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
buffer.order(ByteOrder.nativeOrder());
return buffer;
}
private static class NativeCodeAndBufferResult {
private int mMojoResult;
private ByteBuffer mBuffer;
/**
* @return the mojoResult
*/
public int getMojoResult() {
return mMojoResult;
}
/**
* @param mojoResult the mojoResult to set
*/
public void setMojoResult(int mojoResult) {
mMojoResult = mojoResult;
}
/**
* @return the buffer
*/
public ByteBuffer getBuffer() {
return mBuffer;
}
/**
* @param buffer the buffer to set
*/
public void setBuffer(ByteBuffer buffer) {
mBuffer = buffer;
}
}
/**
* Implementation of {@link org.chromium.mojo.system.AsyncWaiter.Cancellable}.
*/
private class AsyncWaiterCancellableImpl implements AsyncWaiter.Cancellable {
private final long mId;
private final long mDataPtr;
private boolean mActive = true;
private AsyncWaiterCancellableImpl(long id, long dataPtr) {
this.mId = id;
this.mDataPtr = dataPtr;
}
/**
* @see org.chromium.mojo.system.AsyncWaiter.Cancellable#cancel()
*/
@Override
public void cancel() {
if (mActive) {
mActive = false;
nativeCancelAsyncWait(mId, mDataPtr);
}
}
private boolean isActive() {
return mActive;
}
private void deactivate() {
mActive = false;
}
}
@CalledByNative
private AsyncWaiterCancellableImpl newAsyncWaiterCancellableImpl(long id, long dataPtr) {
return new AsyncWaiterCancellableImpl(id, dataPtr);
}
@CalledByNative
private void onAsyncWaitResult(int mojoResult,
AsyncWaiter.Callback callback,
AsyncWaiterCancellableImpl cancellable) {
if (!cancellable.isActive()) {
// If cancellable is not active, the user cancelled the wait.
return;
}
cancellable.deactivate();
int finalCode = filterMojoResult(mojoResult);
if (isUnrecoverableError(finalCode)) {
callback.onError(new MojoException(finalCode));
return;
}
callback.onResult(finalCode);
}
@CalledByNative
private static NativeCodeAndBufferResult newNativeCodeAndBufferResult(int mojoResult,
ByteBuffer buffer) {
NativeCodeAndBufferResult result = new NativeCodeAndBufferResult();
result.setMojoResult(mojoResult);
result.setBuffer(buffer);
return result;
}
@CalledByNative
private static MessagePipeHandle.ReadMessageResult newReadMessageResult(int mojoResult,
int messageSize,
int handlesCount) {
MessagePipeHandle.ReadMessageResult result = new MessagePipeHandle.ReadMessageResult();
if (mojoResult >= 0) {
result.setMojoResult(MojoResult.OK);
} else {
result.setMojoResult(mojoResult);
}
result.setMessageSize(messageSize);
result.setHandlesCount(handlesCount);
return result;
}
private static class NativeCreationResult {
private int mMojoResult;
private int mMojoHandle1;
private int mMojoHandle2;
/**
* @return the mojoResult
*/
public int getMojoResult() {
return mMojoResult;
}
/**
* @param mojoResult the mojoResult to set
*/
public void setMojoResult(int mojoResult) {
mMojoResult = mojoResult;
}
/**
* @return the mojoHandle1
*/
public int getMojoHandle1() {
return mMojoHandle1;
}
/**
* @param mojoHandle1 the mojoHandle1 to set
*/
public void setMojoHandle1(int mojoHandle1) {
mMojoHandle1 = mojoHandle1;
}
/**
* @return the mojoHandle2
*/
public int getMojoHandle2() {
return mMojoHandle2;
}
/**
* @param mojoHandle2 the mojoHandle2 to set
*/
public void setMojoHandle2(int mojoHandle2) {
mMojoHandle2 = mojoHandle2;
}
}
@CalledByNative
private static NativeCreationResult newNativeCreationResult(int mojoResult,
int mojoHandle1, int mojoHandle2) {
NativeCreationResult result = new NativeCreationResult();
result.setMojoResult(mojoResult);
result.setMojoHandle1(mojoHandle1);
result.setMojoHandle2(mojoHandle2);
return result;
}
private native void nativeConstructor();
private native long nativeGetTimeTicksNow();
private native int nativeWaitMany(ByteBuffer buffer, long deadline);
private native NativeCreationResult nativeCreateMessagePipe();
private native NativeCreationResult nativeCreateDataPipe(ByteBuffer optionsBuffer);
private native NativeCreationResult nativeCreateSharedBuffer(ByteBuffer optionsBuffer,
long numBytes);
private native int nativeClose(int mojoHandle);
private native int nativeWait(int mojoHandle, int flags, long deadline);
private native int nativeWriteMessage(int mojoHandle, ByteBuffer bytes, int numBytes,
ByteBuffer handlesBuffer, int flags);
private native MessagePipeHandle.ReadMessageResult nativeReadMessage(int mojoHandle,
ByteBuffer bytes,
ByteBuffer handlesBuffer,
int flags);
private native int nativeReadData(int mojoHandle, ByteBuffer elements, int elementsSize,
int flags);
private native NativeCodeAndBufferResult nativeBeginReadData(int mojoHandle, int numBytes,
int flags);
private native int nativeEndReadData(int mojoHandle, int numBytesRead);
private native int nativeWriteData(int mojoHandle, ByteBuffer elements, int limit, int flags);
private native NativeCodeAndBufferResult nativeBeginWriteData(int mojoHandle, int numBytes,
int flags);
private native int nativeEndWriteData(int mojoHandle, int numBytesWritten);
private native NativeCreationResult nativeDuplicate(int mojoHandle, ByteBuffer optionsBuffer);
private native NativeCodeAndBufferResult nativeMap(int mojoHandle, long offset, long numBytes,
int flags);
private native int nativeUnmap(ByteBuffer buffer);
private native AsyncWaiterCancellableImpl nativeAsyncWait(int mojoHandle, int flags,
long deadline, AsyncWaiter.Callback callback);
private native void nativeCancelAsyncWait(long mId, long dataPtr);
}