Merge "Add SharedMemory API"
This commit is contained in:
@@ -729,6 +729,7 @@ aidl_files := \
|
||||
frameworks/base/core/java/android/os/DropBoxManager.aidl \
|
||||
frameworks/base/core/java/android/os/Bundle.aidl \
|
||||
frameworks/base/core/java/android/os/Debug.aidl \
|
||||
frameworks/base/core/java/android/os/SharedMemory.aidl \
|
||||
frameworks/base/core/java/android/os/StrictMode.aidl \
|
||||
frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl \
|
||||
frameworks/base/core/java/android/net/Network.aidl \
|
||||
|
||||
@@ -31023,12 +31023,12 @@ package android.os {
|
||||
|
||||
public class MemoryFile {
|
||||
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
|
||||
method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public void close();
|
||||
method protected void finalize();
|
||||
method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
|
||||
method public java.io.InputStream getInputStream();
|
||||
method public java.io.OutputStream getOutputStream();
|
||||
method public boolean isPurgingAllowed();
|
||||
method public deprecated boolean isPurgingAllowed();
|
||||
method public int length();
|
||||
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
@@ -31453,6 +31453,22 @@ package android.os {
|
||||
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
|
||||
}
|
||||
|
||||
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
|
||||
method public void close();
|
||||
method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
|
||||
method public int describeContents();
|
||||
method public int getFd();
|
||||
method public java.io.FileDescriptor getFileDescriptor();
|
||||
method public int getSize();
|
||||
method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
|
||||
method public boolean setProtect(int);
|
||||
method public static void unmap(java.nio.ByteBuffer);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
|
||||
}
|
||||
|
||||
public class StatFs {
|
||||
ctor public StatFs(java.lang.String);
|
||||
method public deprecated int getAvailableBlocks();
|
||||
|
||||
@@ -33773,12 +33773,12 @@ package android.os {
|
||||
|
||||
public class MemoryFile {
|
||||
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
|
||||
method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public void close();
|
||||
method protected void finalize();
|
||||
method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
|
||||
method public java.io.InputStream getInputStream();
|
||||
method public java.io.OutputStream getOutputStream();
|
||||
method public boolean isPurgingAllowed();
|
||||
method public deprecated boolean isPurgingAllowed();
|
||||
method public int length();
|
||||
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
@@ -34232,6 +34232,22 @@ package android.os {
|
||||
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
|
||||
}
|
||||
|
||||
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
|
||||
method public void close();
|
||||
method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
|
||||
method public int describeContents();
|
||||
method public int getFd();
|
||||
method public java.io.FileDescriptor getFileDescriptor();
|
||||
method public int getSize();
|
||||
method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
|
||||
method public boolean setProtect(int);
|
||||
method public static void unmap(java.nio.ByteBuffer);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
|
||||
}
|
||||
|
||||
public class StatFs {
|
||||
ctor public StatFs(java.lang.String);
|
||||
method public deprecated int getAvailableBlocks();
|
||||
|
||||
@@ -31154,12 +31154,12 @@ package android.os {
|
||||
|
||||
public class MemoryFile {
|
||||
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
|
||||
method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
|
||||
method public void close();
|
||||
method protected void finalize();
|
||||
method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
|
||||
method public java.io.InputStream getInputStream();
|
||||
method public java.io.OutputStream getOutputStream();
|
||||
method public boolean isPurgingAllowed();
|
||||
method public deprecated boolean isPurgingAllowed();
|
||||
method public int length();
|
||||
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
|
||||
@@ -31585,6 +31585,22 @@ package android.os {
|
||||
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
|
||||
}
|
||||
|
||||
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
|
||||
method public void close();
|
||||
method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
|
||||
method public int describeContents();
|
||||
method public int getFd();
|
||||
method public java.io.FileDescriptor getFileDescriptor();
|
||||
method public int getSize();
|
||||
method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
|
||||
method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
|
||||
method public boolean setProtect(int);
|
||||
method public static void unmap(java.nio.ByteBuffer);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
|
||||
}
|
||||
|
||||
public class StatFs {
|
||||
ctor public StatFs(java.lang.String);
|
||||
method public deprecated int getAvailableBlocks();
|
||||
|
||||
@@ -16,68 +16,50 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.util.Log;
|
||||
import android.system.ErrnoException;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* MemoryFile is a wrapper for the Linux ashmem driver.
|
||||
* MemoryFiles are backed by shared memory, which can be optionally
|
||||
* set to be purgeable.
|
||||
* MemoryFile is a wrapper for {@link SharedMemory} which can optionally be set to purgeable.
|
||||
*
|
||||
* Applications should generally prefer to use {@link SharedMemory} which offers more flexible
|
||||
* access & control over the shared memory region than MemoryFile does.
|
||||
*
|
||||
* Purgeable files may have their contents reclaimed by the kernel
|
||||
* in low memory conditions (only if allowPurging is set to true).
|
||||
* After a file is purged, attempts to read or write the file will
|
||||
* cause an IOException to be thrown.
|
||||
*/
|
||||
public class MemoryFile
|
||||
{
|
||||
public class MemoryFile {
|
||||
private static String TAG = "MemoryFile";
|
||||
|
||||
// mmap(2) protection flags from <sys/mman.h>
|
||||
private static final int PROT_READ = 0x1;
|
||||
private static final int PROT_WRITE = 0x2;
|
||||
|
||||
private static native FileDescriptor native_open(String name, int length) throws IOException;
|
||||
// returns memory address for ashmem region
|
||||
private static native long native_mmap(FileDescriptor fd, int length, int mode)
|
||||
throws IOException;
|
||||
private static native void native_munmap(long addr, int length) throws IOException;
|
||||
private static native void native_close(FileDescriptor fd);
|
||||
private static native int native_read(FileDescriptor fd, long address, byte[] buffer,
|
||||
int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
|
||||
private static native void native_write(FileDescriptor fd, long address, byte[] buffer,
|
||||
int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
|
||||
private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
|
||||
// Returns 'true' if purged, 'false' otherwise
|
||||
private static native boolean native_pin(FileDescriptor fd, boolean pin) throws IOException;
|
||||
private static native int native_get_size(FileDescriptor fd) throws IOException;
|
||||
|
||||
private FileDescriptor mFD; // ashmem file descriptor
|
||||
private long mAddress; // address of ashmem memory
|
||||
private int mLength; // total length of our ashmem region
|
||||
private SharedMemory mSharedMemory;
|
||||
private ByteBuffer mMapping;
|
||||
private boolean mAllowPurging = false; // true if our ashmem region is unpinned
|
||||
|
||||
/**
|
||||
* Allocates a new ashmem region. The region is initially not purgable.
|
||||
*
|
||||
* @param name optional name for the file (can be null).
|
||||
* @param length of the memory file in bytes, must be non-negative.
|
||||
* @param length of the memory file in bytes, must be positive.
|
||||
* @throws IOException if the memory file could not be created.
|
||||
*/
|
||||
public MemoryFile(String name, int length) throws IOException {
|
||||
mLength = length;
|
||||
if (length >= 0) {
|
||||
mFD = native_open(name, length);
|
||||
} else {
|
||||
throw new IOException("Invalid length: " + length);
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
|
||||
} else {
|
||||
mAddress = 0;
|
||||
try {
|
||||
mMapping = mSharedMemory.mapReadWrite();
|
||||
mSharedMemory = SharedMemory.create(name, length);
|
||||
} catch (ErrnoException ex) {
|
||||
ex.rethrowAsIOException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +69,7 @@ public class MemoryFile
|
||||
*/
|
||||
public void close() {
|
||||
deactivate();
|
||||
if (!isClosed()) {
|
||||
native_close(mFD);
|
||||
}
|
||||
mSharedMemory.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,35 +80,30 @@ public class MemoryFile
|
||||
* @hide
|
||||
*/
|
||||
void deactivate() {
|
||||
if (!isDeactivated()) {
|
||||
try {
|
||||
native_munmap(mAddress, mLength);
|
||||
mAddress = 0;
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, ex.toString());
|
||||
if (mMapping != null) {
|
||||
SharedMemory.unmap(mMapping);
|
||||
mMapping = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkActive() throws IOException {
|
||||
if (mMapping == null) {
|
||||
throw new IOException("MemoryFile has been deactivated");
|
||||
}
|
||||
}
|
||||
|
||||
private void beginAccess() throws IOException {
|
||||
checkActive();
|
||||
if (mAllowPurging) {
|
||||
if (native_pin(mSharedMemory.getFileDescriptor(), true)) {
|
||||
throw new IOException("MemoryFile has been purged");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the memory file has been deactivated.
|
||||
*/
|
||||
private boolean isDeactivated() {
|
||||
return mAddress == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the memory file has been closed.
|
||||
*/
|
||||
private boolean isClosed() {
|
||||
return !mFD.valid();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (!isClosed()) {
|
||||
Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
|
||||
close();
|
||||
private void endAccess() throws IOException {
|
||||
if (mAllowPurging) {
|
||||
native_pin(mSharedMemory.getFileDescriptor(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,14 +113,19 @@ public class MemoryFile
|
||||
* @return file length.
|
||||
*/
|
||||
public int length() {
|
||||
return mLength;
|
||||
return mSharedMemory.getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is memory file purging enabled?
|
||||
*
|
||||
* @return true if the file may be purged.
|
||||
*
|
||||
* @deprecated Purgable is considered generally fragile and hard to use safely. Applications
|
||||
* are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
|
||||
* to react to memory events and release shared memory regions as appropriate.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isPurgingAllowed() {
|
||||
return mAllowPurging;
|
||||
}
|
||||
@@ -156,11 +136,16 @@ public class MemoryFile
|
||||
* @param allowPurging true if the operating system can purge the contents
|
||||
* of the file in low memory situations
|
||||
* @return previous value of allowPurging
|
||||
*
|
||||
* @deprecated Purgable is considered generally fragile and hard to use safely. Applications
|
||||
* are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
|
||||
* to react to memory events and release shared memory regions as appropriate.
|
||||
*/
|
||||
@Deprecated
|
||||
synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
|
||||
boolean oldValue = mAllowPurging;
|
||||
if (oldValue != allowPurging) {
|
||||
native_pin(mFD, !allowPurging);
|
||||
native_pin(mSharedMemory.getFileDescriptor(), !allowPurging);
|
||||
mAllowPurging = allowPurging;
|
||||
}
|
||||
return oldValue;
|
||||
@@ -197,16 +182,14 @@ public class MemoryFile
|
||||
*/
|
||||
public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
|
||||
throws IOException {
|
||||
if (isDeactivated()) {
|
||||
throw new IOException("Can't read from deactivated memory file.");
|
||||
beginAccess();
|
||||
try {
|
||||
mMapping.position(srcOffset);
|
||||
mMapping.get(buffer, destOffset, count);
|
||||
} finally {
|
||||
endAccess();
|
||||
}
|
||||
if (destOffset < 0 || destOffset > buffer.length || count < 0
|
||||
|| count > buffer.length - destOffset
|
||||
|| srcOffset < 0 || srcOffset > mLength
|
||||
|| count > mLength - srcOffset) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,16 +204,13 @@ public class MemoryFile
|
||||
*/
|
||||
public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
|
||||
throws IOException {
|
||||
if (isDeactivated()) {
|
||||
throw new IOException("Can't write to deactivated memory file.");
|
||||
beginAccess();
|
||||
try {
|
||||
mMapping.position(destOffset);
|
||||
mMapping.put(buffer, srcOffset, count);
|
||||
} finally {
|
||||
endAccess();
|
||||
}
|
||||
if (srcOffset < 0 || srcOffset > buffer.length || count < 0
|
||||
|| count > buffer.length - srcOffset
|
||||
|| destOffset < 0 || destOffset > mLength
|
||||
|| count > mLength - destOffset) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,11 +219,9 @@ public class MemoryFile
|
||||
* The returned file descriptor is not duplicated.
|
||||
*
|
||||
* @throws IOException If the memory file has been closed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public FileDescriptor getFileDescriptor() throws IOException {
|
||||
return mFD;
|
||||
return mSharedMemory.getFileDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,10 +244,10 @@ public class MemoryFile
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
if (mOffset >= mLength) {
|
||||
if (mOffset >= mSharedMemory.getSize()) {
|
||||
return 0;
|
||||
}
|
||||
return mLength - mOffset;
|
||||
return mSharedMemory.getSize() - mOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -319,8 +297,8 @@ public class MemoryFile
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
if (mOffset + n > mLength) {
|
||||
n = mLength - mOffset;
|
||||
if (mOffset + n > mSharedMemory.getSize()) {
|
||||
n = mSharedMemory.getSize() - mOffset;
|
||||
}
|
||||
mOffset += n;
|
||||
return n;
|
||||
|
||||
20
core/java/android/os/SharedMemory.aidl
Normal file
20
core/java/android/os/SharedMemory.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/* //device/java/android/android/os/SharedMemory.aidl
|
||||
**
|
||||
** Copyright 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.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
parcelable SharedMemory;
|
||||
351
core/java/android/os/SharedMemory.java
Normal file
351
core/java/android/os/SharedMemory.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DirectByteBuffer;
|
||||
|
||||
import sun.misc.Cleaner;
|
||||
|
||||
/**
|
||||
* SharedMemory enables the creation, mapping, and protection control over anonymous shared memory.
|
||||
*/
|
||||
public final class SharedMemory implements Parcelable, Closeable {
|
||||
|
||||
private final FileDescriptor mFileDescriptor;
|
||||
private final int mSize;
|
||||
private final MemoryRegistration mMemoryRegistration;
|
||||
private Cleaner mCleaner;
|
||||
|
||||
private SharedMemory(FileDescriptor fd) {
|
||||
// This constructor is only used internally so it should be impossible to hit any of the
|
||||
// exceptions unless something goes horribly wrong.
|
||||
if (fd == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to create SharedMemory from a null FileDescriptor");
|
||||
}
|
||||
if (!fd.valid()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to create SharedMemory from closed FileDescriptor");
|
||||
}
|
||||
mFileDescriptor = fd;
|
||||
mSize = nGetSize(mFileDescriptor);
|
||||
if (mSize <= 0) {
|
||||
throw new IllegalArgumentException("FileDescriptor is not a valid ashmem fd");
|
||||
}
|
||||
|
||||
mMemoryRegistration = new MemoryRegistration(mSize);
|
||||
mCleaner = Cleaner.create(this, new Closer(mFileDescriptor, mMemoryRegistration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an anonymous SharedMemory instance with the provided debug name and size. The name
|
||||
* is only used for debugging purposes and can help identify what the shared memory is used
|
||||
* for when inspecting memory maps for the processes that have mapped this SharedMemory
|
||||
* instance.
|
||||
*
|
||||
* @param name The debug name to use for this SharedMemory instance. This can be null, however
|
||||
* a debug name is recommended to help identify memory usage when using tools
|
||||
* such as lsof or examining /proc/[pid]/maps
|
||||
* @param size The size of the shared memory to create. Must be greater than 0.
|
||||
* @return A SharedMemory instance of the requested size
|
||||
* @throws ErrnoException if the requested allocation fails.
|
||||
*/
|
||||
public static @NonNull SharedMemory create(@Nullable String name, int size)
|
||||
throws ErrnoException {
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("Size must be greater than zero");
|
||||
}
|
||||
return new SharedMemory(nCreate(name, size));
|
||||
}
|
||||
|
||||
private void checkOpen() {
|
||||
if (!mFileDescriptor.valid()) {
|
||||
throw new IllegalStateException("SharedMemory is closed");
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
|
||||
| OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
|
||||
|
||||
private static void validateProt(int prot) {
|
||||
if ((prot & ~PROT_MASK) != 0) {
|
||||
throw new IllegalArgumentException("Invalid prot value");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the protection on the shared memory to the combination specified in prot, which
|
||||
* is either a bitwise-or'd combination of {@link android.system.OsConstants#PROT_READ},
|
||||
* {@link android.system.OsConstants#PROT_WRITE}, {@link android.system.OsConstants#PROT_EXEC}
|
||||
* from {@link android.system.OsConstants}, or {@link android.system.OsConstants#PROT_NONE},
|
||||
* to remove all further access.
|
||||
*
|
||||
* Note that protection can only ever be removed, not added. By default shared memory
|
||||
* is created with protection set to PROT_READ | PROT_WRITE | PROT_EXEC. The protection
|
||||
* passed here also only applies to any mappings created after calling this method. Existing
|
||||
* mmaps of the shared memory retain whatever protection they had when they were created.
|
||||
*
|
||||
* A common usage of this is to share a read-only copy of the data with something else. To do
|
||||
* that first create the read/write mapping with PROT_READ | PROT_WRITE,
|
||||
* then call setProtect(PROT_READ) to remove write capability, then send the SharedMemory
|
||||
* to another process. That process will only be able to mmap with PROT_READ.
|
||||
*
|
||||
* @param prot Any bitwise-or'ed combination of
|
||||
* {@link android.system.OsConstants#PROT_READ},
|
||||
* {@link android.system.OsConstants#PROT_WRITE}, and
|
||||
* {@link android.system.OsConstants#PROT_EXEC}; or
|
||||
* {@link android.system.OsConstants#PROT_NONE}
|
||||
* @return Whether or not the requested protection was applied. Returns true on success,
|
||||
* false if the requested protection was broader than the existing protection.
|
||||
*/
|
||||
public boolean setProtect(int prot) {
|
||||
checkOpen();
|
||||
validateProt(prot);
|
||||
int errno = nSetProt(mFileDescriptor, prot);
|
||||
return errno == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the backing {@link FileDescriptor} for this SharedMemory object. The SharedMemory
|
||||
* instance retains ownership of the FileDescriptor.
|
||||
*
|
||||
* This FileDescriptor is interoperable with the ASharedMemory NDK APIs.
|
||||
*
|
||||
* @return Returns the FileDescriptor associated with this object.
|
||||
*/
|
||||
public @NonNull FileDescriptor getFileDescriptor() {
|
||||
return mFileDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the backing native fd int for this SharedMemory object. The SharedMemory
|
||||
* instance retains ownership of the fd.
|
||||
*
|
||||
* This fd is interoperable with the ASharedMemory NDK APIs.
|
||||
*
|
||||
* @return Returns the native fd associated with this object, or -1 if it is already closed.
|
||||
*/
|
||||
public int getFd() {
|
||||
return mFileDescriptor.getInt$();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The size of the SharedMemory region.
|
||||
*/
|
||||
public int getSize() {
|
||||
checkOpen();
|
||||
return mSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a read/write mapping of the entire shared memory region. This requires the the
|
||||
* protection level of the shared memory is at least PROT_READ|PROT_WRITE or the map will fail.
|
||||
*
|
||||
* Use {@link #map(int, int, int)} to have more control over the mapping if desired.
|
||||
* This is equivalent to map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, getSize())
|
||||
*
|
||||
* @return A ByteBuffer mapping
|
||||
* @throws ErrnoException if the mmap call failed.
|
||||
*/
|
||||
public @NonNull ByteBuffer mapReadWrite() throws ErrnoException {
|
||||
return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a read-only mapping of the entire shared memory region. This requires the the
|
||||
* protection level of the shared memory is at least PROT_READ or the map will fail.
|
||||
*
|
||||
* Use {@link #map(int, int, int)} to have more control over the mapping if desired.
|
||||
* This is equivalent to map(OsConstants.PROT_READ, 0, getSize())
|
||||
*
|
||||
* @return A ByteBuffer mapping
|
||||
* @throws ErrnoException if the mmap call failed.
|
||||
*/
|
||||
public @NonNull ByteBuffer mapReadOnly() throws ErrnoException {
|
||||
return map(OsConstants.PROT_READ, 0, mSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an mmap of the SharedMemory with the specified prot, offset, and length.
|
||||
*
|
||||
* @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
|
||||
* @param offset The offset into the shared memory to begin mapping
|
||||
* @param length The length of the region to map
|
||||
* @return A ByteBuffer mapping.
|
||||
* @throws ErrnoException if the mmap call failed.
|
||||
*/
|
||||
public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {
|
||||
checkOpen();
|
||||
validateProt(prot);
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("Offset must be > 0");
|
||||
}
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Length must be > 0");
|
||||
}
|
||||
if (offset + length > mSize) {
|
||||
throw new IllegalArgumentException("offset + length must not exceed getSize()");
|
||||
}
|
||||
long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);
|
||||
boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0;
|
||||
Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire());
|
||||
return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmaps a buffer previously returned by {@link #map(int, int, int)}
|
||||
* @param buffer The buffer to unmap
|
||||
*/
|
||||
public static void unmap(@NonNull ByteBuffer buffer) {
|
||||
if (buffer instanceof DirectByteBuffer) {
|
||||
Cleaner cleaner = ((DirectByteBuffer) buffer).cleaner();
|
||||
if (cleaner != null) {
|
||||
cleaner.clean();
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"ByteBuffer wasn't created by #map(int, int, int); can't unmap");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the backing {@link FileDescriptor} of this SharedMemory instance. Note that all
|
||||
* open mappings of the shared memory will remain valid and may continue to be used. The
|
||||
* shared memory will not be freed until all file descriptor handles are closed and all
|
||||
* memory mappings are unmapped.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (mCleaner != null) {
|
||||
mCleaner.clean();
|
||||
mCleaner = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return CONTENTS_FILE_DESCRIPTOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
checkOpen();
|
||||
dest.writeFileDescriptor(mFileDescriptor);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SharedMemory> CREATOR =
|
||||
new Parcelable.Creator<SharedMemory>() {
|
||||
@Override
|
||||
public SharedMemory createFromParcel(Parcel source) {
|
||||
FileDescriptor descriptor = source.readRawFileDescriptor();
|
||||
return new SharedMemory(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedMemory[] newArray(int size) {
|
||||
return new SharedMemory[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleaner that closes the FD
|
||||
*/
|
||||
private static final class Closer implements Runnable {
|
||||
private FileDescriptor mFd;
|
||||
private MemoryRegistration mMemoryReference;
|
||||
|
||||
private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
|
||||
mFd = fd;
|
||||
mMemoryReference = memoryReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Os.close(mFd);
|
||||
} catch (ErrnoException e) { /* swallow error */ }
|
||||
mMemoryReference.release();
|
||||
mMemoryReference = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleaner that munmap regions
|
||||
*/
|
||||
private static final class Unmapper implements Runnable {
|
||||
private long mAddress;
|
||||
private int mSize;
|
||||
private MemoryRegistration mMemoryReference;
|
||||
|
||||
private Unmapper(long address, int size, MemoryRegistration memoryReference) {
|
||||
mAddress = address;
|
||||
mSize = size;
|
||||
mMemoryReference = memoryReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Os.munmap(mAddress, mSize);
|
||||
} catch (ErrnoException e) { /* swallow exception */ }
|
||||
mMemoryReference.release();
|
||||
mMemoryReference = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that ensures that the native allocation pressure against the VM heap stays
|
||||
* active until the FD is closed as well as all mappings from that FD are closed.
|
||||
*/
|
||||
private static final class MemoryRegistration {
|
||||
private int mSize;
|
||||
private int mReferenceCount;
|
||||
|
||||
private MemoryRegistration(int size) {
|
||||
mSize = size;
|
||||
mReferenceCount = 1;
|
||||
VMRuntime.getRuntime().registerNativeAllocation(mSize);
|
||||
}
|
||||
|
||||
public synchronized MemoryRegistration acquire() {
|
||||
mReferenceCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
mReferenceCount--;
|
||||
if (mReferenceCount == 0) {
|
||||
VMRuntime.getRuntime().registerNativeFree(mSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;
|
||||
private static native int nGetSize(FileDescriptor fd);
|
||||
private static native int nSetProt(FileDescriptor fd, int prot);
|
||||
}
|
||||
@@ -90,6 +90,7 @@ cc_library_shared {
|
||||
"android_os_Parcel.cpp",
|
||||
"android_os_SELinux.cpp",
|
||||
"android_os_seccomp.cpp",
|
||||
"android_os_SharedMemory.cpp",
|
||||
"android_os_SystemClock.cpp",
|
||||
"android_os_SystemProperties.cpp",
|
||||
"android_os_Trace.cpp",
|
||||
|
||||
@@ -167,6 +167,7 @@ extern int register_android_os_Trace(JNIEnv* env);
|
||||
extern int register_android_os_FileObserver(JNIEnv *env);
|
||||
extern int register_android_os_UEventObserver(JNIEnv* env);
|
||||
extern int register_android_os_MemoryFile(JNIEnv* env);
|
||||
extern int register_android_os_SharedMemory(JNIEnv* env);
|
||||
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
|
||||
extern int register_android_net_NetworkUtils(JNIEnv* env);
|
||||
extern int register_android_net_TrafficStats(JNIEnv* env);
|
||||
@@ -1392,6 +1393,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_net_NetworkUtils),
|
||||
REG_JNI(register_android_net_TrafficStats),
|
||||
REG_JNI(register_android_os_MemoryFile),
|
||||
REG_JNI(register_android_os_SharedMemory),
|
||||
REG_JNI(register_com_android_internal_os_PathClassLoaderFactory),
|
||||
REG_JNI(register_com_android_internal_os_Zygote),
|
||||
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
|
||||
|
||||
@@ -26,95 +26,14 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
|
||||
{
|
||||
const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
|
||||
|
||||
int result = ashmem_create_region(namestr, length);
|
||||
|
||||
if (name)
|
||||
env->ReleaseStringUTFChars(name, namestr);
|
||||
|
||||
if (result < 0) {
|
||||
jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return jniCreateFileDescriptor(env, result);
|
||||
}
|
||||
|
||||
static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
|
||||
jint length, jint prot)
|
||||
{
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
|
||||
if (result == MAP_FAILED) {
|
||||
jniThrowException(env, "java/io/IOException", "mmap failed");
|
||||
}
|
||||
return reinterpret_cast<jlong>(result);
|
||||
}
|
||||
|
||||
static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jlong addr, jint length)
|
||||
{
|
||||
int result = munmap(reinterpret_cast<void *>(addr), length);
|
||||
if (result < 0)
|
||||
jniThrowException(env, "java/io/IOException", "munmap failed");
|
||||
}
|
||||
|
||||
static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
|
||||
{
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
if (fd >= 0) {
|
||||
jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
|
||||
jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
|
||||
jint count, jboolean unpinned)
|
||||
{
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
|
||||
ashmem_unpin_region(fd, 0, 0);
|
||||
jniThrowException(env, "java/io/IOException", "ashmem region was purged");
|
||||
return -1;
|
||||
}
|
||||
|
||||
env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
|
||||
|
||||
if (unpinned) {
|
||||
ashmem_unpin_region(fd, 0, 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
|
||||
jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
|
||||
jint count, jboolean unpinned)
|
||||
{
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
|
||||
ashmem_unpin_region(fd, 0, 0);
|
||||
jniThrowException(env, "java/io/IOException", "ashmem region was purged");
|
||||
return -1;
|
||||
}
|
||||
|
||||
env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
|
||||
|
||||
if (unpinned) {
|
||||
ashmem_unpin_region(fd, 0, 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
|
||||
{
|
||||
static jboolean android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor,
|
||||
jboolean pin) {
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));
|
||||
if (result < 0) {
|
||||
jniThrowException(env, "java/io/IOException", NULL);
|
||||
}
|
||||
return result == ASHMEM_WAS_PURGED;
|
||||
}
|
||||
|
||||
static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
|
||||
@@ -138,19 +57,12 @@ static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
|
||||
}
|
||||
|
||||
static const JNINativeMethod methods[] = {
|
||||
{"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
|
||||
{"native_mmap", "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap},
|
||||
{"native_munmap", "(JI)V", (void*)android_os_MemoryFile_munmap},
|
||||
{"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
|
||||
{"native_read", "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read},
|
||||
{"native_write", "(Ljava/io/FileDescriptor;J[BIIIZ)V", (void*)android_os_MemoryFile_write},
|
||||
{"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
|
||||
{"native_pin", "(Ljava/io/FileDescriptor;Z)Z", (void*)android_os_MemoryFile_pin},
|
||||
{"native_get_size", "(Ljava/io/FileDescriptor;)I",
|
||||
(void*)android_os_MemoryFile_get_size}
|
||||
};
|
||||
|
||||
int register_android_os_MemoryFile(JNIEnv* env)
|
||||
{
|
||||
int register_android_os_MemoryFile(JNIEnv* env) {
|
||||
return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods));
|
||||
}
|
||||
|
||||
|
||||
105
core/jni/android_os_SharedMemory.cpp
Normal file
105
core/jni/android_os_SharedMemory.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "SharedMemory"
|
||||
|
||||
#include "core_jni_helpers.h"
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
#include <utils/Log.h>
|
||||
#include "JNIHelp.h"
|
||||
#include "JniConstants.h"
|
||||
#include "ScopedLocalRef.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <limits>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
|
||||
static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass,
|
||||
"<init>", "(Ljava/lang/String;I)V");
|
||||
|
||||
ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
|
||||
if (detailMessage.get() == NULL) {
|
||||
// Not really much we can do here. We're probably dead in the water,
|
||||
// but let's try to stumble on...
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
|
||||
detailMessage.get(), error);
|
||||
env->Throw(reinterpret_cast<jthrowable>(exception));
|
||||
}
|
||||
|
||||
static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
|
||||
|
||||
// Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
|
||||
const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
|
||||
|
||||
int fd = ashmem_create_region(name, size);
|
||||
|
||||
// Capture the error, if there is one, before calling ReleaseStringUTFChars
|
||||
int err = fd < 0 ? errno : 0;
|
||||
|
||||
if (name) {
|
||||
env->ReleaseStringUTFChars(jname, name);
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
throwErrnoException(env, "SharedMemory_create", err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return jniCreateFileDescriptor(env, fd);
|
||||
}
|
||||
|
||||
static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) {
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
if (!ashmem_valid(fd)) {
|
||||
return -1;
|
||||
}
|
||||
size_t size = ashmem_get_size_region(fd);
|
||||
return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max())));
|
||||
}
|
||||
|
||||
static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
|
||||
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
|
||||
int err = 0;
|
||||
if (ashmem_set_prot_region(fd, prot)) {
|
||||
err = errno;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static const JNINativeMethod methods[] = {
|
||||
{"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create},
|
||||
{"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize},
|
||||
{"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt},
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace android {
|
||||
|
||||
int register_android_os_SharedMemory(JNIEnv* env)
|
||||
{
|
||||
return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
Reference in New Issue
Block a user