blob: c3f4f935d97d0772e9842c4ebaf47844b15fe36c [file] [log] [blame]
Dianne Hackborne17aeb32011-04-07 15:11:57 -07001/*
2 * Copyright (C) 2011 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 com.android.server.am;
18
19import java.io.FileDescriptor;
20import java.io.FileInputStream;
21import java.io.FileOutputStream;
22import java.io.IOException;
23
24import android.os.Binder;
25import android.os.IBinder;
26import android.os.IInterface;
27import android.os.ParcelFileDescriptor;
28import android.os.RemoteException;
29import android.os.SystemClock;
30import android.util.Slog;
31
32/**
33 * Helper for transferring data through a pipe from a client app.
34 */
35class TransferPipe implements Runnable {
36 static final String TAG = "TransferPipe";
37 static final boolean DEBUG = false;
38
39 static final long DEFAULT_TIMEOUT = 5000; // 5 seconds
40
41 final Thread mThread;;
42 final ParcelFileDescriptor[] mFds;
43
44 FileDescriptor mOutFd;
45 long mEndTime;
46 String mFailure;
47 boolean mComplete;
48
49 String mBufferPrefix;
50
51 interface Caller {
52 void go(IInterface iface, FileDescriptor fd, String prefix,
53 String[] args) throws RemoteException;
54 }
55
56 TransferPipe() throws IOException {
57 mThread = new Thread(this, "TransferPipe");
58 mFds = ParcelFileDescriptor.createPipe();
59 }
60
61 ParcelFileDescriptor getReadFd() {
62 return mFds[0];
63 }
64
65 ParcelFileDescriptor getWriteFd() {
66 return mFds[1];
67 }
68
69 void setBufferPrefix(String prefix) {
70 mBufferPrefix = prefix;
71 }
72
73 static void go(Caller caller, IInterface iface, FileDescriptor out,
74 String prefix, String[] args) throws IOException, RemoteException {
75 go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
76 }
77
78 static void go(Caller caller, IInterface iface, FileDescriptor out,
79 String prefix, String[] args, long timeout) throws IOException, RemoteException {
80 if ((iface.asBinder()) instanceof Binder) {
81 // This is a local object... just call it directly.
82 try {
83 caller.go(iface, out, prefix, args);
84 } catch (RemoteException e) {
85 }
86 return;
87 }
88
89 TransferPipe tp = new TransferPipe();
90 try {
91 caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
92 tp.go(out, timeout);
93 } finally {
94 tp.kill();
95 }
96 }
97
98 static void goDump(IBinder binder, FileDescriptor out,
99 String[] args) throws IOException, RemoteException {
100 goDump(binder, out, args, DEFAULT_TIMEOUT);
101 }
102
103 static void goDump(IBinder binder, FileDescriptor out,
104 String[] args, long timeout) throws IOException, RemoteException {
105 if (binder instanceof Binder) {
106 // This is a local object... just call it directly.
107 try {
108 binder.dump(out, args);
109 } catch (RemoteException e) {
110 }
111 return;
112 }
113
114 TransferPipe tp = new TransferPipe();
115 try {
116 binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
117 tp.go(out, timeout);
118 } finally {
119 tp.kill();
120 }
121 }
122
123 void go(FileDescriptor out) throws IOException {
124 go(out, DEFAULT_TIMEOUT);
125 }
126
127 void go(FileDescriptor out, long timeout) throws IOException {
128 try {
129 synchronized (this) {
130 mOutFd = out;
131 mEndTime = SystemClock.uptimeMillis() + timeout;
132
133 if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
134 + " out=" + out);
135
136 // Close the write fd, so we know when the other side is done.
137 closeFd(1);
138
139 mThread.start();
140
141 while (mFailure == null && !mComplete) {
142 long waitTime = mEndTime - SystemClock.uptimeMillis();
143 if (waitTime <= 0) {
144 if (DEBUG) Slog.i(TAG, "TIMEOUT!");
145 mThread.interrupt();
146 throw new IOException("Timeout");
147 }
148
149 try {
150 wait(waitTime);
151 } catch (InterruptedException e) {
152 }
153 }
154
155 if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
156 if (mFailure != null) {
157 throw new IOException(mFailure);
158 }
159 }
160 } finally {
161 kill();
162 }
163 }
164
165 void closeFd(int num) {
166 if (mFds[num] != null) {
167 if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
168 try {
169 mFds[num].close();
170 } catch (IOException e) {
171 }
172 mFds[num] = null;
173 }
174 }
175
176 void kill() {
177 closeFd(0);
178 closeFd(1);
179 }
180
181 @Override
182 public void run() {
183 final byte[] buffer = new byte[1024];
184 final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor());
185 final FileOutputStream fos = new FileOutputStream(mOutFd);
186
187 if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
188 byte[] bufferPrefix = null;
189 boolean needPrefix = true;
190 if (mBufferPrefix != null) {
191 bufferPrefix = mBufferPrefix.getBytes();
192 }
193
194 int size;
195 try {
196 while ((size=fis.read(buffer)) > 0) {
197 if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
198 if (bufferPrefix == null) {
199 fos.write(buffer, 0, size);
200 } else {
201 int start = 0;
202 for (int i=0; i<size; i++) {
203 if (buffer[i] != '\n') {
204 if (i > start) {
205 fos.write(buffer, start, i-start);
206 }
207 start = i;
208 if (needPrefix) {
209 fos.write(bufferPrefix);
210 needPrefix = false;
211 }
212 do {
213 i++;
214 } while (i<size && buffer[i] != '\n');
215 if (i < size) {
216 needPrefix = true;
217 }
218 }
219 }
220 if (size > start) {
221 fos.write(buffer, start, size-start);
222 }
223 }
224 }
225 if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
226 if (mThread.isInterrupted()) {
227 if (DEBUG) Slog.i(TAG, "Interrupted!");
228 }
229 } catch (IOException e) {
230 synchronized (this) {
231 mFailure = e.toString();
232 notifyAll();
233 return;
234 }
235 }
236
237 synchronized (this) {
238 mComplete = true;
239 notifyAll();
240 }
241 }
242}