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