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