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 mCloseGuard.warnIfOpen(); 162 IoUtils.closeQuietly(this); 163 } finally { 164 super.finalize(); 165 } 166 } 167 168 @Override 169 public int describeContents() { 170 return CONTENTS_FILE_DESCRIPTOR; 171 } 172 173 @Override 174 public void writeToParcel(Parcel parcel, int flags) { 175 ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd); 176 try { 177 // Don't let writing to a parcel to close our fd - plz 178 parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 179 } finally { 180 pfd.detachFd(); 181 } 182 } 183 184 @Override 185 public boolean equals(Object obj) { 186 if (obj == null) { 187 return false; 188 } 189 if (this == obj) { 190 return true; 191 } 192 if (getClass() != obj.getClass()) { 193 return false; 194 } 195 MemoryIntArray other = (MemoryIntArray) obj; 196 return mFd == other.mFd; 197 } 198 199 @Override 200 public int hashCode() { 201 return mFd; 202 } 203 204 private void enforceNotClosed() { 205 if (isClosed()) { 206 throw new IllegalStateException("cannot interact with a closed instance"); 207 } 208 } 209 210 private void enforceValidIndex(int index) throws IOException { 211 final int size = size(); 212 if (index < 0 || index > size - 1) { 213 throw new IndexOutOfBoundsException( 214 index + " not between 0 and " + (size - 1)); 215 } 216 } 217 218 private void enforceWritable() { 219 if (!isWritable()) { 220 throw new UnsupportedOperationException("array is not writable"); 221 } 222 } 223 224 private native int nativeCreate(String name, int size); 225 private native long nativeOpen(int fd, boolean owner); 226 private native void nativeClose(int fd, long memoryAddr, boolean owner); 227 private native int nativeGet(int fd, long memoryAddr, int index); 228 private native void nativeSet(int fd, long memoryAddr, int index, int value); 229 private native int nativeSize(int fd); 230 231 /** 232 * @return The max array size. 233 */ 234 public static int getMaxSize() { 235 return MAX_SIZE; 236 } 237 238 public static final Parcelable.Creator<MemoryIntArray> CREATOR = 239 new Parcelable.Creator<MemoryIntArray>() { 240 @Override 241 public MemoryIntArray createFromParcel(Parcel parcel) { 242 try { 243 return new MemoryIntArray(parcel); 244 } catch (IOException ioe) { 245 throw new IllegalArgumentException("Error unparceling MemoryIntArray"); 246 } 247 } 248 249 @Override 250 public MemoryIntArray[] newArray(int size) { 251 return new MemoryIntArray[size]; 252 } 253 }; 254 } 255