Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.util;
     18 
     19 import android.os.Parcel;
     20 import android.os.ParcelFileDescriptor;
     21 import android.os.Parcelable;
     22 import android.os.Process;
     23 import libcore.io.IoUtils;
     24 
     25 import java.io.Closeable;
     26 import java.io.IOException;
     27 import java.util.UUID;
     28 
     29 /**
     30  * This class is an array of integers that is backed by shared memory.
     31  * It is useful for efficiently sharing state between processes. The
     32  * write and read operations are guaranteed to not result in read/
     33  * write memory tear, i.e. they are atomic. However, multiple read/
     34  * write operations are <strong>not</strong> synchronized between
     35  * each other.
     36  * <p>
     37  * The data structure is designed to have one owner process that can
     38  * read/write. There may be multiple client processes that can only read or
     39  * read/write depending how the data structure was configured when
     40  * instantiated. The owner process is the process that created the array.
     41  * The shared memory is pinned (not reclaimed by the system) until the
     42  * owning process dies or the data structure is closed. This class
     43  * is <strong>not</strong> thread safe. You should not interact with
     44  * an instance of this class once it is closed.
     45  * </p>
     46  *
     47  * @hide
     48  */
     49 public final class MemoryIntArray implements Parcelable, Closeable {
     50     private static final String TAG = "MemoryIntArray";
     51 
     52     private static final int MAX_SIZE = 1024;
     53 
     54     private final int mOwnerPid;
     55     private final boolean mClientWritable;
     56     private final long mMemoryAddr;
     57     private ParcelFileDescriptor mFd;
     58 
     59     /**
     60      * Creates a new instance.
     61      *
     62      * @param size The size of the array in terms of integer slots. Cannot be
     63      *     more than {@link #getMaxSize()}.
     64      * @param clientWritable Whether other processes can write to the array.
     65      * @throws IOException If an error occurs while accessing the shared memory.
     66      */
     67     public MemoryIntArray(int size, boolean clientWritable) throws IOException {
     68         if (size > MAX_SIZE) {
     69             throw new IllegalArgumentException("Max size is " + MAX_SIZE);
     70         }
     71         mOwnerPid = Process.myPid();
     72         mClientWritable = clientWritable;
     73         final String name = UUID.randomUUID().toString();
     74         mFd = ParcelFileDescriptor.fromFd(nativeCreate(name, size));
     75         mMemoryAddr = nativeOpen(mFd.getFd(), true, clientWritable);
     76     }
     77 
     78     private MemoryIntArray(Parcel parcel) throws IOException {
     79         mOwnerPid = parcel.readInt();
     80         mClientWritable = (parcel.readInt() == 1);
     81         mFd = parcel.readParcelable(null);
     82         if (mFd == null) {
     83             throw new IOException("No backing file descriptor");
     84         }
     85         final long memoryAddress = parcel.readLong();
     86         if (isOwner()) {
     87             mMemoryAddr = memoryAddress;
     88         } else {
     89             mMemoryAddr = nativeOpen(mFd.getFd(), false, mClientWritable);
     90         }
     91     }
     92 
     93     /**
     94      * @return Gets whether this array is mutable.
     95      */
     96     public boolean isWritable() {
     97         enforceNotClosed();
     98         return isOwner() || mClientWritable;
     99     }
    100 
    101     /**
    102      * Gets the value at a given index.
    103      *
    104      * @param index The index.
    105      * @return The value at this index.
    106      * @throws IOException If an error occurs while accessing the shared memory.
    107      */
    108     public int get(int index) throws IOException {
    109         enforceNotClosed();
    110         enforceValidIndex(index);
    111         return nativeGet(mFd.getFd(), mMemoryAddr, index, isOwner());
    112     }
    113 
    114     /**
    115      * Sets the value at a given index. This method can be called only if
    116      * {@link #isWritable()} returns true which means your process is the
    117      * owner.
    118      *
    119      * @param index The index.
    120      * @param value The value to set.
    121      * @throws IOException If an error occurs while accessing the shared memory.
    122      */
    123     public void set(int index, int value) throws IOException {
    124         enforceNotClosed();
    125         enforceWritable();
    126         enforceValidIndex(index);
    127         nativeSet(mFd.getFd(), mMemoryAddr, index, value, isOwner());
    128     }
    129 
    130     /**
    131      * Gets the array size.
    132      *
    133      * @throws IOException If an error occurs while accessing the shared memory.
    134      */
    135     public int size() throws IOException {
    136         enforceNotClosed();
    137         return nativeSize(mFd.getFd());
    138     }
    139 
    140     /**
    141      * Closes the array releasing resources.
    142      *
    143      * @throws IOException If an error occurs while accessing the shared memory.
    144      */
    145     @Override
    146     public void close() throws IOException {
    147         if (!isClosed()) {
    148             ParcelFileDescriptor pfd = mFd;
    149             mFd = null;
    150             nativeClose(pfd.getFd(), mMemoryAddr, isOwner());
    151         }
    152     }
    153 
    154     /**
    155      * @return Whether this array is closed and shouldn't be used.
    156      */
    157     public boolean isClosed() {
    158         return mFd == null;
    159     }
    160 
    161     @Override
    162     protected void finalize() throws Throwable {
    163         IoUtils.closeQuietly(this);
    164         super.finalize();
    165     }
    166 
    167     @Override
    168     public int describeContents() {
    169         return CONTENTS_FILE_DESCRIPTOR;
    170     }
    171 
    172     @Override
    173     public void writeToParcel(Parcel parcel, int flags) {
    174         parcel.writeInt(mOwnerPid);
    175         parcel.writeInt(mClientWritable ? 1 : 0);
    176         parcel.writeParcelable(mFd, 0);
    177         parcel.writeLong(mMemoryAddr);
    178     }
    179 
    180     @Override
    181     public boolean equals(Object obj) {
    182         if (obj == null) {
    183             return false;
    184         }
    185         if (this == obj) {
    186             return true;
    187         }
    188         if (getClass() != obj.getClass()) {
    189             return false;
    190         }
    191         MemoryIntArray other = (MemoryIntArray) obj;
    192         if (mFd == null) {
    193             if (other.mFd != null) {
    194                 return false;
    195             }
    196         } else if (mFd.getFd() != other.mFd.getFd()) {
    197             return false;
    198         }
    199         return true;
    200     }
    201 
    202     @Override
    203     public int hashCode() {
    204         return mFd != null ? mFd.hashCode() : 1;
    205     }
    206 
    207     private boolean isOwner() {
    208         return mOwnerPid == Process.myPid();
    209     }
    210 
    211     private void enforceNotClosed() {
    212         if (isClosed()) {
    213             throw new IllegalStateException("cannot interact with a closed instance");
    214         }
    215     }
    216 
    217     private void enforceValidIndex(int index) throws IOException {
    218         final int size = size();
    219         if (index < 0 || index > size - 1) {
    220             throw new IndexOutOfBoundsException(
    221                     index + " not between 0 and " + (size - 1));
    222         }
    223     }
    224 
    225     private void enforceWritable() {
    226         if (!isWritable()) {
    227             throw new UnsupportedOperationException("array is not writable");
    228         }
    229     }
    230 
    231     private native int nativeCreate(String name, int size);
    232     private native long nativeOpen(int fd, boolean owner, boolean writable);
    233     private native void nativeClose(int fd, long memoryAddr, boolean owner);
    234     private native int nativeGet(int fd, long memoryAddr, int index, boolean owner);
    235     private native void nativeSet(int fd, long memoryAddr, int index, int value, boolean owner);
    236     private native int nativeSize(int fd);
    237 
    238     /**
    239      * @return The max array size.
    240      */
    241     public static int getMaxSize() {
    242         return MAX_SIZE;
    243     }
    244 
    245     public static final Parcelable.Creator<MemoryIntArray> CREATOR =
    246             new Parcelable.Creator<MemoryIntArray>() {
    247         @Override
    248         public MemoryIntArray createFromParcel(Parcel parcel) {
    249             try {
    250                 return new MemoryIntArray(parcel);
    251             } catch (IOException ioe) {
    252                 Log.e(TAG, "Error unparceling MemoryIntArray");
    253                 return null;
    254             }
    255         }
    256 
    257         @Override
    258         public MemoryIntArray[] newArray(int size) {
    259             return new MemoryIntArray[size];
    260         }
    261     };
    262 }
    263