/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.mozglue; import android.annotation.SuppressLint; import android.os.MemoryFile; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.lang.reflect.Method; @SuppressLint("DiscouragedPrivateApi") public class SharedMemory implements Parcelable { private static final String LOGTAG = "GeckoShmem"; private static final Method sGetFDMethod; private ParcelFileDescriptor mDescriptor; private int mSize; private int mId; private long mHandle; // The native pointer. private boolean mIsMapped; private MemoryFile mBackedFile; // MemoryFile.getFileDescriptor() is hidden. :( static { Method method = null; try { method = MemoryFile.class.getDeclaredMethod("getFileDescriptor"); } catch (final NoSuchMethodException e) { e.printStackTrace(); } sGetFDMethod = method; } private SharedMemory(final Parcel in) { mDescriptor = in.readFileDescriptor(); mSize = in.readInt(); mId = in.readInt(); } public static final Creator CREATOR = new Creator() { @Override public SharedMemory createFromParcel(final Parcel in) { return new SharedMemory(in); } @Override public SharedMemory[] newArray(final int size) { return new SharedMemory[size]; } }; @Override public int describeContents() { return CONTENTS_FILE_DESCRIPTOR; } @Override public void writeToParcel(final Parcel dest, final int flags) { // We don't want ParcelFileDescriptor.writeToParcel() to close the fd. dest.writeFileDescriptor(mDescriptor.getFileDescriptor()); dest.writeInt(mSize); dest.writeInt(mId); } public SharedMemory(final int id, final int size) throws NoSuchMethodException, IOException { if (sGetFDMethod == null) { throw new NoSuchMethodException("MemoryFile.getFileDescriptor() doesn't exist."); } mBackedFile = new MemoryFile(null, size); try { final FileDescriptor fd = (FileDescriptor) sGetFDMethod.invoke(mBackedFile); mDescriptor = ParcelFileDescriptor.dup(fd); mSize = size; mId = id; mBackedFile.allowPurging(false); } catch (final Exception e) { e.printStackTrace(); close(); throw new IOException(e.getMessage()); } } public void flush() { if (!mIsMapped) { return; } unmap(mHandle, mSize); mHandle = 0; mIsMapped = false; } public void close() { flush(); if (mDescriptor != null) { try { mDescriptor.close(); } catch (final IOException e) { e.printStackTrace(); } mDescriptor = null; } } // Should only be called by process that allocates shared memory. public void dispose() { if (!isValid()) { return; } close(); if (mBackedFile != null) { mBackedFile.close(); mBackedFile = null; } } private native void unmap(long address, int size); public boolean isValid() { return mDescriptor != null; } public int getSize() { return mSize; } private int getFD() { return isValid() ? mDescriptor.getFd() : -1; } public long getPointer() { if (!isValid()) { return 0; } if (!mIsMapped) { try { mHandle = map(getFD(), mSize); } catch (final NullPointerException e) { Log.e(LOGTAG, "SharedMemory#" + mId + " error.", e); throw e; } if (mHandle != 0) { mIsMapped = true; } } return mHandle; } private native long map(int fd, int size); @Override protected void finalize() throws Throwable { if (mBackedFile != null) { Log.w(LOGTAG, "dispose() not called before finalizing"); } dispose(); super.finalize(); } @Override public String toString() { return "SHM(" + getSize() + " bytes): id=" + mId + ", backing=" + mBackedFile + ",fd=" + mDescriptor; } @Override public boolean equals(final Object that) { return (this == that) || ((that instanceof SharedMemory) && (hashCode() == that.hashCode())); } @Override public int hashCode() { return mId; } }