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 
     23 import libcore.io.IoUtils;
     24 import dalvik.system.CloseGuard;
     25 
     26 import java.io.Closeable;
     27 import java.io.IOException;
     28 import java.util.UUID;
     29 
     30 /**
     31  * This class is an array of integers that is backed by shared memory.
     32  * It is useful for efficiently sharing state between processes. The
     33  * write and read operations are guaranteed to not result in read/
     34  * write memory tear, i.e. they are atomic. However, multiple read/
     35  * write operations are <strong>not</strong> synchronized between
     36  * each other.
     37  * <p>
     38  * The data structure is designed to have one owner process that can
     39  * read/write. There may be multiple client processes that can only read.
     40  * The owner process is the process that created the array. The shared
     41  * memory is pinned (not reclaimed by the system) until the owning process
     42  * dies or the data structure is closed. This class is <strong>not</strong>
     43  * thread safe. You should not interact with an instance of this class
     44  * once it is closed. If you pass back to the owner process an instance
     45  * it will be read only even in the owning process.
     46  * </p>
     47  *
     48  * @hide
     49  */
     50 public final class MemoryIntArray implements Parcelable, Closeable {
     51     private static final String TAG = "MemoryIntArray";
     52 
     53     private static final int MAX_SIZE = 1024;
     54 
     55     private final CloseGuard mCloseGuard = CloseGuard.get();
     56 
     57     private final boolean mIsOwner;
     58     private final long mMemoryAddr;
     59     private int mFd = -1;
     60 
     61     /**
     62      * Creates a new instance.
     63      *
     64      * @param size The size of the array in terms of integer slots. Cannot be
     65      *     more than {@link #getMaxSize()}.
     66      * @throws IOException If an error occurs while accessing the shared memory.
     67      */
     68     public MemoryIntArray(int size) throws IOException {
     69         if (size > MAX_SIZE) {
     70             throw new IllegalArgumentException("Max size is " + MAX_SIZE);
     71         }
     72         mIsOwner = true;
     73         final String name = UUID.randomUUID().toString();
     74         mFd = nativeCreate(name, size);
     75         mMemoryAddr = nativeOpen(mFd, mIsOwner);
     76         mCloseGuard.open("close");
     77     }
     78 
     79     private MemoryIntArray(Parcel parcel) throws IOException {
     80         mIsOwner = false;
     81         ParcelFileDescriptor pfd = parcel.readParcelable(null);
     82         if (pfd == null) {
     83             throw new IOException("No backing file descriptor");
     84         }
     85         mFd = pfd.detachFd();
     86         mMemoryAddr = nativeOpen(mFd, mIsOwner);
     87         mCloseGuard.open("close");
     88     }
     89 
     90     /**
     91      * @return Gets whether this array is mutable.
     92      */
     93     public boolean isWritable() {
     94         enforceNotClosed();
     95         return mIsOwner;
     96     }
     97 
     98     /**
     99      * Gets the value at a given index.
    100      *
    101      * @param index The index.
    102      * @return The value at this index.
    103      * @throws IOException If an error occurs while accessing the shared memory.
    104      */
    105     public int get(int index) throws IOException {
    106         enforceNotClosed();
    107         enforceValidIndex(index);
    108         return nativeGet(mFd, mMemoryAddr, index);
    109     }
    110 
    111     /**
    112      * Sets the value at a given index. This method can be called only if
    113      * {@link #isWritable()} returns true which means your process is the
    114      * owner.
    115      *
    116      * @param index The index.
    117      * @param value The value to set.
    118      * @throws IOException If an error occurs while accessing the shared memory.
    119      */
    120     public void set(int index, int value) throws IOException {
    121         enforceNotClosed();
    122         enforceWritable();
    123         enforceValidIndex(index);
    124         nativeSet(mFd, mMemoryAddr, index, value);
    125     }
    126 
    127     /**
    128      * Gets the array size.
    129      *
    130      * @throws IOException If an error occurs while accessing the shared memory.
    131      */
    132     public int size() throws IOException {
    133         enforceNotClosed();
    134         return nativeSize(mFd);
    135     }
    136 
    137     /**
    138      * Closes the array releasing resources.
    139      *
    140      * @throws IOException If an error occurs while accessing the shared memory.
    141      */
    142     @Override
    143     public void close() throws IOException {
    144         if (!isClosed()) {
    145             nativeClose(mFd, mMemoryAddr, mIsOwner);
    146             mFd = -1;
    147             mCloseGuard.close();
    148         }
    149     }
    150 
    151     /**
    152      * @return Whether this array is closed and shouldn't be used.
    153      */
    154     public boolean isClosed() {
    155         return mFd == -1;
    156     }
    157 
    158     @Override
    159     protected void finalize() throws Throwable {
    160         try {
    161             if (mCloseGuard != null) {
    162                 mCloseGuard.warnIfOpen();
    163             }
    164 
    165             IoUtils.closeQuietly(this);
    166         } finally {
    167             super.finalize();
    168         }
    169     }
    170 
    171     @Override
    172     public int describeContents() {
    173         return CONTENTS_FILE_DESCRIPTOR;
    174     }
    175 
    176     @Override
    177     public void writeToParcel(Parcel parcel, int flags) {
    178         ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd);
    179         try {
    180             // Don't let writing to a parcel to close our fd - plz
    181             parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    182         } finally {
    183             pfd.detachFd();
    184         }
    185     }
    186 
    187     @Override
    188     public boolean equals(Object obj) {
    189         if (obj == null) {
    190             return false;
    191         }
    192         if (this == obj) {
    193             return true;
    194         }
    195         if (getClass() != obj.getClass()) {
    196             return false;
    197         }
    198         MemoryIntArray other = (MemoryIntArray) obj;
    199         return mFd == other.mFd;
    200     }
    201 
    202     @Override
    203     public int hashCode() {
    204         return mFd;
    205     }
    206 
    207     private void enforceNotClosed() {
    208         if (isClosed()) {
    209             throw new IllegalStateException("cannot interact with a closed instance");
    210         }
    211     }
    212 
    213     private void enforceValidIndex(int index) throws IOException {
    214         final int size = size();
    215         if (index < 0 || index > size - 1) {
    216             throw new IndexOutOfBoundsException(
    217                     index + " not between 0 and " + (size - 1));
    218         }
    219     }
    220 
    221     private void enforceWritable() {
    222         if (!isWritable()) {
    223             throw new UnsupportedOperationException("array is not writable");
    224         }
    225     }
    226 
    227     private native int nativeCreate(String name, int size);
    228     private native long nativeOpen(int fd, boolean owner);
    229     private native void nativeClose(int fd, long memoryAddr, boolean owner);
    230     private native int nativeGet(int fd, long memoryAddr, int index);
    231     private native void nativeSet(int fd, long memoryAddr, int index, int value);
    232     private native int nativeSize(int fd);
    233 
    234     /**
    235      * @return The max array size.
    236      */
    237     public static int getMaxSize() {
    238         return MAX_SIZE;
    239     }
    240 
    241     public static final Parcelable.Creator<MemoryIntArray> CREATOR =
    242             new Parcelable.Creator<MemoryIntArray>() {
    243         @Override
    244         public MemoryIntArray createFromParcel(Parcel parcel) {
    245             try {
    246                 return new MemoryIntArray(parcel);
    247             } catch (IOException ioe) {
    248                 throw new IllegalArgumentException("Error unparceling MemoryIntArray");
    249             }
    250         }
    251 
    252         @Override
    253         public MemoryIntArray[] newArray(int size) {
    254             return new MemoryIntArray[size];
    255         }
    256     };
    257 }
    258