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; 22 23import libcore.io.IoUtils; 24import dalvik.system.CloseGuard; 25 26import java.io.Closeable; 27import java.io.IOException; 28import 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 */ 50public 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