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 Build.IS_ENG ? 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) 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); 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 public void accessLastElementInRemoteProcess(MemoryIntArray array) { 152 try { 153 mRemoteInstance.accessLastElementInRemoteProcess(array); 154 } catch (RemoteException e) { 155 throw new RuntimeException(e); 156 } 157 } 158 159 @Override 160 public void onServiceConnected(ComponentName name, IBinder service) { 161 synchronized (mLock) { 162 mRemoteInstance = android.util.IRemoteMemoryIntArray.Stub.asInterface(service); 163 mLock.notifyAll(); 164 } 165 } 166 167 @Override 168 public void onServiceDisconnected(ComponentName name) { 169 synchronized (mLock) { 170 mRemoteInstance = null; 171 } 172 } 173}