MemoryIntArray.java revision 4694cad51122c20880d00389ef95833d7a14b358
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.
39 * The owner process is the process that created the array. The shared
40 * memory is pinned (not reclaimed by the system) until the owning process
41 * dies or the data structure is closed. This class is <strong>not</strong>
42 * thread safe. You should not interact with an instance of this class
43 * once it is closed. If you pass back to the owner process an instance
44 * it will be read only even in the owning process.
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 boolean mIsOwner;
55    private final long mMemoryAddr;
56    private int mFd;
57
58    /**
59     * Creates a new instance.
60     *
61     * @param size The size of the array in terms of integer slots. Cannot be
62     *     more than {@link #getMaxSize()}.
63     * @param clientWritable Whether other processes can write to the array.
64     * @throws IOException If an error occurs while accessing the shared memory.
65     */
66    public MemoryIntArray(int size) throws IOException {
67        if (size > MAX_SIZE) {
68            throw new IllegalArgumentException("Max size is " + MAX_SIZE);
69        }
70        mIsOwner = true;
71        final String name = UUID.randomUUID().toString();
72        mFd = nativeCreate(name, size);
73        mMemoryAddr = nativeOpen(mFd, mIsOwner);
74    }
75
76    private MemoryIntArray(Parcel parcel) throws IOException {
77        mIsOwner = false;
78        ParcelFileDescriptor pfd = parcel.readParcelable(null);
79        if (pfd == null) {
80            throw new IOException("No backing file descriptor");
81        }
82        mFd = pfd.detachFd();
83        mMemoryAddr = nativeOpen(mFd, mIsOwner);
84    }
85
86    /**
87     * @return Gets whether this array is mutable.
88     */
89    public boolean isWritable() {
90        enforceNotClosed();
91        return mIsOwner;
92    }
93
94    /**
95     * Gets the value at a given index.
96     *
97     * @param index The index.
98     * @return The value at this index.
99     * @throws IOException If an error occurs while accessing the shared memory.
100     */
101    public int get(int index) throws IOException {
102        enforceNotClosed();
103        enforceValidIndex(index);
104        return nativeGet(mFd, mMemoryAddr, index);
105    }
106
107    /**
108     * Sets the value at a given index. This method can be called only if
109     * {@link #isWritable()} returns true which means your process is the
110     * owner.
111     *
112     * @param index The index.
113     * @param value The value to set.
114     * @throws IOException If an error occurs while accessing the shared memory.
115     */
116    public void set(int index, int value) throws IOException {
117        enforceNotClosed();
118        enforceWritable();
119        enforceValidIndex(index);
120        nativeSet(mFd, mMemoryAddr, index, value);
121    }
122
123    /**
124     * Gets the array size.
125     *
126     * @throws IOException If an error occurs while accessing the shared memory.
127     */
128    public int size() throws IOException {
129        enforceNotClosed();
130        return nativeSize(mFd);
131    }
132
133    /**
134     * Closes the array releasing resources.
135     *
136     * @throws IOException If an error occurs while accessing the shared memory.
137     */
138    @Override
139    public void close() throws IOException {
140        if (!isClosed()) {
141            nativeClose(mFd, mMemoryAddr, mIsOwner);
142            mFd = -1;
143        }
144    }
145
146    /**
147     * @return Whether this array is closed and shouldn't be used.
148     */
149    public boolean isClosed() {
150        return mFd == -1;
151    }
152
153    @Override
154    protected void finalize() throws Throwable {
155        IoUtils.closeQuietly(this);
156        super.finalize();
157    }
158
159    @Override
160    public int describeContents() {
161        return CONTENTS_FILE_DESCRIPTOR;
162    }
163
164    @Override
165    public void writeToParcel(Parcel parcel, int flags) {
166        ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd);
167        try {
168            parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
169        } finally {
170            pfd.detachFd();
171        }
172    }
173
174    @Override
175    public boolean equals(Object obj) {
176        if (obj == null) {
177            return false;
178        }
179        if (this == obj) {
180            return true;
181        }
182        if (getClass() != obj.getClass()) {
183            return false;
184        }
185        MemoryIntArray other = (MemoryIntArray) obj;
186        return mFd == other.mFd;
187    }
188
189    @Override
190    public int hashCode() {
191        return mFd;
192    }
193
194    private void enforceNotClosed() {
195        if (isClosed()) {
196            throw new IllegalStateException("cannot interact with a closed instance");
197        }
198    }
199
200    private void enforceValidIndex(int index) throws IOException {
201        final int size = size();
202        if (index < 0 || index > size - 1) {
203            throw new IndexOutOfBoundsException(
204                    index + " not between 0 and " + (size - 1));
205        }
206    }
207
208    private void enforceWritable() {
209        if (!isWritable()) {
210            throw new UnsupportedOperationException("array is not writable");
211        }
212    }
213
214    private native int nativeCreate(String name, int size);
215    private native long nativeOpen(int fd, boolean owner);
216    private native void nativeClose(int fd, long memoryAddr, boolean owner);
217    private native int nativeGet(int fd, long memoryAddr, int index);
218    private native void nativeSet(int fd, long memoryAddr, int index, int value);
219    private native int nativeSize(int fd);
220
221    /**
222     * @return The max array size.
223     */
224    public static int getMaxSize() {
225        return MAX_SIZE;
226    }
227
228    public static final Parcelable.Creator<MemoryIntArray> CREATOR =
229            new Parcelable.Creator<MemoryIntArray>() {
230        @Override
231        public MemoryIntArray createFromParcel(Parcel parcel) {
232            try {
233                return new MemoryIntArray(parcel);
234            } catch (IOException ioe) {
235                throw new IllegalArgumentException("Error unparceling MemoryIntArray");
236            }
237        }
238
239        @Override
240        public MemoryIntArray[] newArray(int size) {
241            return new MemoryIntArray[size];
242        }
243    };
244}
245