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