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.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.Build;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.SystemClock;
27import android.support.test.InstrumentationRegistry;
28
29import java.io.Closeable;
30import java.io.IOException;
31import java.util.concurrent.TimeoutException;
32
33final class RemoteIntArray implements ServiceConnection, Closeable {
34    private static final long BIND_REMOTE_SERVICE_TIMEOUT =
35            ("eng".equals(Build.TYPE)) ? 120000 : 10000;
36
37    private final Object mLock = new Object();
38
39    private final Intent mIntent = new Intent();
40
41    private android.util.IRemoteMemoryIntArray mRemoteInstance;
42
43    public RemoteIntArray(int size, boolean clientWritable) throws IOException, TimeoutException {
44        mIntent.setComponent(new ComponentName(InstrumentationRegistry.getContext(),
45                RemoteMemoryIntArrayService.class));
46        synchronized (mLock) {
47            if (mRemoteInstance == null) {
48                bindLocked();
49            }
50            try {
51                mRemoteInstance.create(size, clientWritable);
52            } catch (RemoteException e) {
53                throw new IOException(e);
54            }
55        }
56    }
57
58    public MemoryIntArray peekInstance() {
59        try {
60            return mRemoteInstance.peekInstance();
61        } catch (RemoteException e) {
62            throw new RuntimeException(e);
63        }
64    }
65
66    public boolean isWritable() {
67        try {
68            return mRemoteInstance.isWritable();
69        } catch (RemoteException e) {
70            throw new RuntimeException(e);
71        }
72    }
73
74    public int get(int index) throws IOException {
75        try {
76            return mRemoteInstance.get(index);
77        } catch (RemoteException e) {
78            throw new IOException(e);
79        }
80    }
81
82    public void set(int index, int value) throws IOException {
83        try {
84            mRemoteInstance.set(index, value);
85        } catch (RemoteException e) {
86            throw new IOException(e);
87        }
88    }
89
90    public int size() throws IOException {
91        try {
92            return mRemoteInstance.size();
93        } catch (RemoteException e) {
94            throw new IOException(e);
95        }
96    }
97
98    public void close() {
99        try {
100            mRemoteInstance.close();
101        } catch (RemoteException e) {
102            throw new RuntimeException(e);
103        }
104    }
105
106    public boolean isClosed() {
107        try {
108            return mRemoteInstance.isClosed();
109        } catch (RemoteException e) {
110            throw new RuntimeException(e);
111        }
112    }
113
114    private void bindLocked() throws TimeoutException {
115        if (mRemoteInstance != null) {
116            return;
117        }
118
119        InstrumentationRegistry.getContext().bindService(mIntent, this, Context.BIND_AUTO_CREATE);
120
121        final long startMillis = SystemClock.uptimeMillis();
122        while (true) {
123            if (mRemoteInstance != null) {
124                break;
125            }
126            final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
127            final long remainingMillis = BIND_REMOTE_SERVICE_TIMEOUT - elapsedMillis;
128            if (remainingMillis <= 0) {
129                throw new TimeoutException("Cannot get spooler!");
130            }
131            try {
132                mLock.wait(remainingMillis);
133            } catch (InterruptedException ie) {
134                    /* ignore */
135            }
136        }
137
138        mLock.notifyAll();
139    }
140
141    public void destroy() {
142        synchronized (mLock) {
143            if (mRemoteInstance == null) {
144                return;
145            }
146            mRemoteInstance = null;
147            InstrumentationRegistry.getContext().unbindService(this);
148        }
149    }
150
151    @Override
152    public void onServiceConnected(ComponentName name, IBinder service) {
153        synchronized (mLock) {
154            mRemoteInstance = android.util.IRemoteMemoryIntArray.Stub.asInterface(service);
155            mLock.notifyAll();
156        }
157    }
158
159    @Override
160    public void onServiceDisconnected(ComponentName name) {
161        synchronized (mLock) {
162            mRemoteInstance = null;
163        }
164    }
165}