blob: 74fea3f4dd30756fd87743f5794a21f94104c67f [file] [log] [blame]
Svet Ganov53a441c2016-04-19 19:38:00 -07001/*
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;
Svet Ganov9d723d32016-08-27 11:05:56 -070022
Svet Ganov9d723d32016-08-27 11:05:56 -070023import dalvik.system.CloseGuard;
Svet Ganov53a441c2016-04-19 19:38:00 -070024
Josh Gaoc81f53f2018-08-09 15:09:02 -070025import libcore.io.IoUtils;
26
Svet Ganov53a441c2016-04-19 19:38:00 -070027import java.io.Closeable;
28import java.io.IOException;
29import java.util.UUID;
30
31/**
32 * This class is an array of integers that is backed by shared memory.
33 * It is useful for efficiently sharing state between processes. The
34 * write and read operations are guaranteed to not result in read/
35 * write memory tear, i.e. they are atomic. However, multiple read/
36 * write operations are <strong>not</strong> synchronized between
37 * each other.
38 * <p>
39 * The data structure is designed to have one owner process that can
Svetoslav Ganov74c99832016-12-05 20:07:20 -080040 * read/write. There may be multiple client processes that can only read.
41 * The owner process is the process that created the array. The shared
42 * memory is pinned (not reclaimed by the system) until the owning process
43 * dies or the data structure is closed. This class is <strong>not</strong>
44 * thread safe. You should not interact with an instance of this class
45 * once it is closed. If you pass back to the owner process an instance
46 * it will be read only even in the owning process.
Svet Ganov53a441c2016-04-19 19:38:00 -070047 * </p>
48 *
49 * @hide
50 */
51public final class MemoryIntArray implements Parcelable, Closeable {
Svetoslav Ganov04df7382016-05-10 18:55:47 -070052 private static final String TAG = "MemoryIntArray";
53
Svet Ganov53a441c2016-04-19 19:38:00 -070054 private static final int MAX_SIZE = 1024;
55
Svet Ganov9d723d32016-08-27 11:05:56 -070056 private final CloseGuard mCloseGuard = CloseGuard.get();
57
Svetoslav Ganov74c99832016-12-05 20:07:20 -080058 private final boolean mIsOwner;
Svet Ganov53a441c2016-04-19 19:38:00 -070059 private final long mMemoryAddr;
Josh Gaoc81f53f2018-08-09 15:09:02 -070060 private ParcelFileDescriptor mFd;
Svet Ganov53a441c2016-04-19 19:38:00 -070061
62 /**
63 * Creates a new instance.
64 *
65 * @param size The size of the array in terms of integer slots. Cannot be
66 * more than {@link #getMaxSize()}.
Svet Ganov53a441c2016-04-19 19:38:00 -070067 * @throws IOException If an error occurs while accessing the shared memory.
68 */
Svetoslav Ganov74c99832016-12-05 20:07:20 -080069 public MemoryIntArray(int size) throws IOException {
Svet Ganov53a441c2016-04-19 19:38:00 -070070 if (size > MAX_SIZE) {
71 throw new IllegalArgumentException("Max size is " + MAX_SIZE);
72 }
Svetoslav Ganov74c99832016-12-05 20:07:20 -080073 mIsOwner = true;
Svet Ganov53a441c2016-04-19 19:38:00 -070074 final String name = UUID.randomUUID().toString();
Josh Gaoc81f53f2018-08-09 15:09:02 -070075 mFd = ParcelFileDescriptor.adoptFd(nativeCreate(name, size));
76 mMemoryAddr = nativeOpen(mFd.getFd(), mIsOwner);
Svet Ganov9d723d32016-08-27 11:05:56 -070077 mCloseGuard.open("close");
Svet Ganov53a441c2016-04-19 19:38:00 -070078 }
79
80 private MemoryIntArray(Parcel parcel) throws IOException {
Svetoslav Ganov74c99832016-12-05 20:07:20 -080081 mIsOwner = false;
Svetoslav Ganovfe2462f2016-08-29 11:14:05 -070082 ParcelFileDescriptor pfd = parcel.readParcelable(null);
83 if (pfd == null) {
Svet Ganov53a441c2016-04-19 19:38:00 -070084 throw new IOException("No backing file descriptor");
85 }
Josh Gaoc81f53f2018-08-09 15:09:02 -070086 mFd = ParcelFileDescriptor.adoptFd(pfd.detachFd());
87 mMemoryAddr = nativeOpen(mFd.getFd(), mIsOwner);
Svet Ganov9d723d32016-08-27 11:05:56 -070088 mCloseGuard.open("close");
Svet Ganov53a441c2016-04-19 19:38:00 -070089 }
90
91 /**
92 * @return Gets whether this array is mutable.
93 */
94 public boolean isWritable() {
95 enforceNotClosed();
Svetoslav Ganov74c99832016-12-05 20:07:20 -080096 return mIsOwner;
Svet Ganov53a441c2016-04-19 19:38:00 -070097 }
98
99 /**
100 * Gets the value at a given index.
101 *
102 * @param index The index.
103 * @return The value at this index.
104 * @throws IOException If an error occurs while accessing the shared memory.
105 */
106 public int get(int index) throws IOException {
107 enforceNotClosed();
108 enforceValidIndex(index);
Josh Gaoc81f53f2018-08-09 15:09:02 -0700109 return nativeGet(mFd.getFd(), mMemoryAddr, index);
Svet Ganov53a441c2016-04-19 19:38:00 -0700110 }
111
112 /**
113 * Sets the value at a given index. This method can be called only if
114 * {@link #isWritable()} returns true which means your process is the
115 * owner.
116 *
117 * @param index The index.
118 * @param value The value to set.
119 * @throws IOException If an error occurs while accessing the shared memory.
120 */
121 public void set(int index, int value) throws IOException {
122 enforceNotClosed();
123 enforceWritable();
124 enforceValidIndex(index);
Josh Gaoc81f53f2018-08-09 15:09:02 -0700125 nativeSet(mFd.getFd(), mMemoryAddr, index, value);
Svet Ganov53a441c2016-04-19 19:38:00 -0700126 }
127
128 /**
129 * Gets the array size.
130 *
131 * @throws IOException If an error occurs while accessing the shared memory.
132 */
133 public int size() throws IOException {
134 enforceNotClosed();
Josh Gaoc81f53f2018-08-09 15:09:02 -0700135 return nativeSize(mFd.getFd());
Svet Ganov53a441c2016-04-19 19:38:00 -0700136 }
137
138 /**
139 * Closes the array releasing resources.
140 *
141 * @throws IOException If an error occurs while accessing the shared memory.
142 */
143 @Override
144 public void close() throws IOException {
145 if (!isClosed()) {
Josh Gaoc81f53f2018-08-09 15:09:02 -0700146 nativeClose(mFd.getFd(), mMemoryAddr, mIsOwner);
147 mFd.close();
148 mFd = null;
Svet Ganov9d723d32016-08-27 11:05:56 -0700149 mCloseGuard.close();
Svet Ganov53a441c2016-04-19 19:38:00 -0700150 }
151 }
152
153 /**
154 * @return Whether this array is closed and shouldn't be used.
155 */
156 public boolean isClosed() {
Josh Gaoc81f53f2018-08-09 15:09:02 -0700157 return mFd == null;
Svet Ganov53a441c2016-04-19 19:38:00 -0700158 }
159
160 @Override
161 protected void finalize() throws Throwable {
Svet Ganov9d723d32016-08-27 11:05:56 -0700162 try {
Narayan Kamath492e9e82017-03-22 14:28:08 +0000163 if (mCloseGuard != null) {
164 mCloseGuard.warnIfOpen();
165 }
166
Svet Ganov9d723d32016-08-27 11:05:56 -0700167 IoUtils.closeQuietly(this);
168 } finally {
169 super.finalize();
170 }
Svet Ganov53a441c2016-04-19 19:38:00 -0700171 }
172
173 @Override
174 public int describeContents() {
175 return CONTENTS_FILE_DESCRIPTOR;
176 }
177
178 @Override
179 public void writeToParcel(Parcel parcel, int flags) {
Josh Gaoc81f53f2018-08-09 15:09:02 -0700180 // Don't let writing to a parcel to close our fd - plz
181 parcel.writeParcelable(mFd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
Svet Ganov53a441c2016-04-19 19:38:00 -0700182 }
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 }
Josh Gaoc81f53f2018-08-09 15:09:02 -0700195
196 return false;
Svet Ganov53a441c2016-04-19 19:38:00 -0700197 }
198
199 @Override
200 public int hashCode() {
Josh Gaoc81f53f2018-08-09 15:09:02 -0700201 return mFd.hashCode();
Svet Ganov53a441c2016-04-19 19:38:00 -0700202 }
203
Svet Ganov53a441c2016-04-19 19:38:00 -0700204 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);
Svetoslav Ganov74c99832016-12-05 20:07:20 -0800225 private native long nativeOpen(int fd, boolean owner);
Svet Ganov53a441c2016-04-19 19:38:00 -0700226 private native void nativeClose(int fd, long memoryAddr, boolean owner);
Svetoslav Ganov74c99832016-12-05 20:07:20 -0800227 private native int nativeGet(int fd, long memoryAddr, int index);
228 private native void nativeSet(int fd, long memoryAddr, int index, int value);
Svet Ganov53a441c2016-04-19 19:38:00 -0700229 private native int nativeSize(int fd);
Svet Ganov53a441c2016-04-19 19:38:00 -0700230
231 /**
232 * @return The max array size.
233 */
234 public static int getMaxSize() {
235 return MAX_SIZE;
236 }
237
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700238 public static final @android.annotation.NonNull Parcelable.Creator<MemoryIntArray> CREATOR =
Svet Ganov53a441c2016-04-19 19:38:00 -0700239 new Parcelable.Creator<MemoryIntArray>() {
240 @Override
241 public MemoryIntArray createFromParcel(Parcel parcel) {
242 try {
243 return new MemoryIntArray(parcel);
244 } catch (IOException ioe) {
Svetoslav Ganov74c99832016-12-05 20:07:20 -0800245 throw new IllegalArgumentException("Error unparceling MemoryIntArray");
Svet Ganov53a441c2016-04-19 19:38:00 -0700246 }
247 }
248
249 @Override
250 public MemoryIntArray[] newArray(int size) {
251 return new MemoryIntArray[size];
252 }
253 };
254}