MemoryIntArray.java revision 1f06508bc640b90e613179f6371f53b9a839fa53
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 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 */ 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 int mOwnerPid; 55 private final boolean mClientWritable; 56 private final long mMemoryAddr; 57 private int 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 = nativeCreate(name, size); 75 mMemoryAddr = nativeOpen(mFd, true, clientWritable); 76 } 77 78 private MemoryIntArray(Parcel parcel) throws IOException { 79 mOwnerPid = parcel.readInt(); 80 mClientWritable = (parcel.readInt() == 1); 81 ParcelFileDescriptor pfd = parcel.readParcelable(null); 82 if (pfd == null) { 83 throw new IOException("No backing file descriptor"); 84 } 85 mFd = pfd.detachFd(); 86 final long memoryAddress = parcel.readLong(); 87 if (isOwner()) { 88 mMemoryAddr = memoryAddress; 89 } else { 90 mMemoryAddr = nativeOpen(mFd, false, mClientWritable); 91 } 92 } 93 94 /** 95 * @return Gets whether this array is mutable. 96 */ 97 public boolean isWritable() { 98 enforceNotClosed(); 99 return isOwner() || mClientWritable; 100 } 101 102 /** 103 * Gets the value at a given index. 104 * 105 * @param index The index. 106 * @return The value at this index. 107 * @throws IOException If an error occurs while accessing the shared memory. 108 */ 109 public int get(int index) throws IOException { 110 enforceNotClosed(); 111 enforceValidIndex(index); 112 return nativeGet(mFd, mMemoryAddr, index, isOwner()); 113 } 114 115 /** 116 * Sets the value at a given index. This method can be called only if 117 * {@link #isWritable()} returns true which means your process is the 118 * owner. 119 * 120 * @param index The index. 121 * @param value The value to set. 122 * @throws IOException If an error occurs while accessing the shared memory. 123 */ 124 public void set(int index, int value) throws IOException { 125 enforceNotClosed(); 126 enforceWritable(); 127 enforceValidIndex(index); 128 nativeSet(mFd, mMemoryAddr, index, value, isOwner()); 129 } 130 131 /** 132 * Gets the array size. 133 * 134 * @throws IOException If an error occurs while accessing the shared memory. 135 */ 136 public int size() throws IOException { 137 enforceNotClosed(); 138 return nativeSize(mFd); 139 } 140 141 /** 142 * Closes the array releasing resources. 143 * 144 * @throws IOException If an error occurs while accessing the shared memory. 145 */ 146 @Override 147 public void close() throws IOException { 148 if (!isClosed()) { 149 nativeClose(mFd, mMemoryAddr, isOwner()); 150 mFd = -1; 151 } 152 } 153 154 /** 155 * @return Whether this array is closed and shouldn't be used. 156 */ 157 public boolean isClosed() { 158 return mFd == -1; 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 ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd); 175 try { 176 parcel.writeInt(mOwnerPid); 177 parcel.writeInt(mClientWritable ? 1 : 0); 178 parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 179 parcel.writeLong(mMemoryAddr); 180 } finally { 181 pfd.detachFd(); 182 } 183 } 184 185 @Override 186 public boolean equals(Object obj) { 187 if (obj == null) { 188 return false; 189 } 190 if (this == obj) { 191 return true; 192 } 193 if (getClass() != obj.getClass()) { 194 return false; 195 } 196 MemoryIntArray other = (MemoryIntArray) obj; 197 return mFd == other.mFd; 198 } 199 200 @Override 201 public int hashCode() { 202 return mFd; 203 } 204 205 private boolean isOwner() { 206 return mOwnerPid == Process.myPid(); 207 } 208 209 private void enforceNotClosed() { 210 if (isClosed()) { 211 throw new IllegalStateException("cannot interact with a closed instance"); 212 } 213 } 214 215 private void enforceValidIndex(int index) throws IOException { 216 final int size = size(); 217 if (index < 0 || index > size - 1) { 218 throw new IndexOutOfBoundsException( 219 index + " not between 0 and " + (size - 1)); 220 } 221 } 222 223 private void enforceWritable() { 224 if (!isWritable()) { 225 throw new UnsupportedOperationException("array is not writable"); 226 } 227 } 228 229 private native int nativeCreate(String name, int size); 230 private native long nativeOpen(int fd, boolean owner, boolean writable); 231 private native void nativeClose(int fd, long memoryAddr, boolean owner); 232 private native int nativeGet(int fd, long memoryAddr, int index, boolean owner); 233 private native void nativeSet(int fd, long memoryAddr, int index, int value, boolean owner); 234 private native int nativeSize(int fd); 235 236 /** 237 * @return The max array size. 238 */ 239 public static int getMaxSize() { 240 return MAX_SIZE; 241 } 242 243 public static final Parcelable.Creator<MemoryIntArray> CREATOR = 244 new Parcelable.Creator<MemoryIntArray>() { 245 @Override 246 public MemoryIntArray createFromParcel(Parcel parcel) { 247 try { 248 return new MemoryIntArray(parcel); 249 } catch (IOException ioe) { 250 Log.e(TAG, "Error unparceling MemoryIntArray"); 251 return null; 252 } 253 } 254 255 @Override 256 public MemoryIntArray[] newArray(int size) { 257 return new MemoryIntArray[size]; 258 } 259 }; 260} 261