blob: e76b395e9e2d4d55045937a7db5eecb3aee51def [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
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070017package com.android.internal.os;
Dianne Hackborne17aeb32011-04-07 15:11:57 -070018
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 */
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070035public final class TransferPipe implements Runnable {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070036 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
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070056 public TransferPipe() throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070057 mThread = new Thread(this, "TransferPipe");
58 mFds = ParcelFileDescriptor.createPipe();
59 }
60
61 ParcelFileDescriptor getReadFd() {
62 return mFds[0];
63 }
64
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070065 public ParcelFileDescriptor getWriteFd() {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070066 return mFds[1];
67 }
68
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070069 public void setBufferPrefix(String prefix) {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070070 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
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700123 public void go(FileDescriptor out) throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700124 go(out, DEFAULT_TIMEOUT);
125 }
126
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700127 public void go(FileDescriptor out, long timeout) throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700128 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
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700176 public void kill() {
Xin Guan5ec679a2014-08-21 13:05:34 -0500177 synchronized (this) {
178 closeFd(0);
179 closeFd(1);
180 }
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700181 }
182
183 @Override
184 public void run() {
185 final byte[] buffer = new byte[1024];
Xin Guan5ec679a2014-08-21 13:05:34 -0500186 final FileInputStream fis;
187 final FileOutputStream fos;
188
189 synchronized (this) {
190 ParcelFileDescriptor readFd = getReadFd();
191 if (readFd == null) {
192 Slog.w(TAG, "Pipe has been closed...");
193 return;
194 }
195 fis = new FileInputStream(readFd.getFileDescriptor());
196 fos = new FileOutputStream(mOutFd);
197 }
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700198
199 if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
200 byte[] bufferPrefix = null;
201 boolean needPrefix = true;
202 if (mBufferPrefix != null) {
203 bufferPrefix = mBufferPrefix.getBytes();
204 }
205
206 int size;
207 try {
208 while ((size=fis.read(buffer)) > 0) {
209 if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
210 if (bufferPrefix == null) {
211 fos.write(buffer, 0, size);
212 } else {
213 int start = 0;
214 for (int i=0; i<size; i++) {
215 if (buffer[i] != '\n') {
216 if (i > start) {
217 fos.write(buffer, start, i-start);
218 }
219 start = i;
220 if (needPrefix) {
221 fos.write(bufferPrefix);
222 needPrefix = false;
223 }
224 do {
225 i++;
226 } while (i<size && buffer[i] != '\n');
227 if (i < size) {
228 needPrefix = true;
229 }
230 }
231 }
232 if (size > start) {
233 fos.write(buffer, start, size-start);
234 }
235 }
236 }
237 if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
238 if (mThread.isInterrupted()) {
239 if (DEBUG) Slog.i(TAG, "Interrupted!");
240 }
241 } catch (IOException e) {
242 synchronized (this) {
243 mFailure = e.toString();
244 notifyAll();
245 return;
246 }
247 }
248
249 synchronized (this) {
250 mComplete = true;
251 notifyAll();
252 }
253 }
254}