blob: f9041507ffddca94a7ea34777fdce314b00de67e [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
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070019import java.io.Closeable;
Dianne Hackborne17aeb32011-04-07 15:11:57 -070020import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24
25import android.os.Binder;
26import android.os.IBinder;
27import android.os.IInterface;
28import android.os.ParcelFileDescriptor;
29import android.os.RemoteException;
30import android.os.SystemClock;
31import android.util.Slog;
32
33/**
34 * Helper for transferring data through a pipe from a client app.
35 */
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070036public final class TransferPipe implements Runnable, Closeable {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070037 static final String TAG = "TransferPipe";
38 static final boolean DEBUG = false;
39
40 static final long DEFAULT_TIMEOUT = 5000; // 5 seconds
41
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070042 final Thread mThread;
Dianne Hackborne17aeb32011-04-07 15:11:57 -070043 final ParcelFileDescriptor[] mFds;
44
45 FileDescriptor mOutFd;
46 long mEndTime;
47 String mFailure;
48 boolean mComplete;
49
50 String mBufferPrefix;
51
52 interface Caller {
53 void go(IInterface iface, FileDescriptor fd, String prefix,
54 String[] args) throws RemoteException;
55 }
56
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070057 public TransferPipe() throws IOException {
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070058 this(null);
59 }
60
61 public TransferPipe(String bufferPrefix) throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070062 mThread = new Thread(this, "TransferPipe");
63 mFds = ParcelFileDescriptor.createPipe();
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070064 mBufferPrefix = bufferPrefix;
Dianne Hackborne17aeb32011-04-07 15:11:57 -070065 }
66
67 ParcelFileDescriptor getReadFd() {
68 return mFds[0];
69 }
70
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070071 public ParcelFileDescriptor getWriteFd() {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070072 return mFds[1];
73 }
74
Dianne Hackborncbfd23e2013-06-11 14:26:53 -070075 public void setBufferPrefix(String prefix) {
Dianne Hackborne17aeb32011-04-07 15:11:57 -070076 mBufferPrefix = prefix;
77 }
78
Jeff Sharkeyc06f1842016-11-09 12:25:44 -070079 public static void dumpAsync(IBinder binder, FileDescriptor out, String[] args)
80 throws IOException, RemoteException {
81 goDump(binder, out, args);
82 }
83
Dianne Hackborne17aeb32011-04-07 15:11:57 -070084 static void go(Caller caller, IInterface iface, FileDescriptor out,
85 String prefix, String[] args) throws IOException, RemoteException {
86 go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
87 }
88
89 static void go(Caller caller, IInterface iface, FileDescriptor out,
90 String prefix, String[] args, long timeout) throws IOException, RemoteException {
91 if ((iface.asBinder()) instanceof Binder) {
92 // This is a local object... just call it directly.
93 try {
94 caller.go(iface, out, prefix, args);
95 } catch (RemoteException e) {
96 }
97 return;
98 }
99
Jeff Sharkeyc06f1842016-11-09 12:25:44 -0700100 try (TransferPipe tp = new TransferPipe()) {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700101 caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
102 tp.go(out, timeout);
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700103 }
104 }
105
106 static void goDump(IBinder binder, FileDescriptor out,
107 String[] args) throws IOException, RemoteException {
108 goDump(binder, out, args, DEFAULT_TIMEOUT);
109 }
110
111 static void goDump(IBinder binder, FileDescriptor out,
112 String[] args, long timeout) throws IOException, RemoteException {
113 if (binder instanceof Binder) {
114 // This is a local object... just call it directly.
115 try {
116 binder.dump(out, args);
117 } catch (RemoteException e) {
118 }
119 return;
120 }
121
Jeff Sharkeyc06f1842016-11-09 12:25:44 -0700122 try (TransferPipe tp = new TransferPipe()) {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700123 binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
124 tp.go(out, timeout);
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700125 }
126 }
127
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700128 public void go(FileDescriptor out) throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700129 go(out, DEFAULT_TIMEOUT);
130 }
131
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700132 public void go(FileDescriptor out, long timeout) throws IOException {
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700133 try {
134 synchronized (this) {
135 mOutFd = out;
136 mEndTime = SystemClock.uptimeMillis() + timeout;
137
138 if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
139 + " out=" + out);
140
141 // Close the write fd, so we know when the other side is done.
142 closeFd(1);
143
144 mThread.start();
145
146 while (mFailure == null && !mComplete) {
147 long waitTime = mEndTime - SystemClock.uptimeMillis();
148 if (waitTime <= 0) {
149 if (DEBUG) Slog.i(TAG, "TIMEOUT!");
150 mThread.interrupt();
151 throw new IOException("Timeout");
152 }
153
154 try {
155 wait(waitTime);
156 } catch (InterruptedException e) {
157 }
158 }
159
160 if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
161 if (mFailure != null) {
162 throw new IOException(mFailure);
163 }
164 }
165 } finally {
166 kill();
167 }
168 }
169
170 void closeFd(int num) {
171 if (mFds[num] != null) {
172 if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
173 try {
174 mFds[num].close();
175 } catch (IOException e) {
176 }
177 mFds[num] = null;
178 }
179 }
180
Jeff Sharkeyc06f1842016-11-09 12:25:44 -0700181 @Override
182 public void close() {
183 kill();
184 }
185
Dianne Hackborncbfd23e2013-06-11 14:26:53 -0700186 public void kill() {
Xin Guan5ec679a2014-08-21 13:05:34 -0500187 synchronized (this) {
188 closeFd(0);
189 closeFd(1);
190 }
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700191 }
192
193 @Override
194 public void run() {
195 final byte[] buffer = new byte[1024];
Xin Guan5ec679a2014-08-21 13:05:34 -0500196 final FileInputStream fis;
197 final FileOutputStream fos;
198
199 synchronized (this) {
200 ParcelFileDescriptor readFd = getReadFd();
201 if (readFd == null) {
202 Slog.w(TAG, "Pipe has been closed...");
203 return;
204 }
205 fis = new FileInputStream(readFd.getFileDescriptor());
206 fos = new FileOutputStream(mOutFd);
207 }
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700208
209 if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
210 byte[] bufferPrefix = null;
211 boolean needPrefix = true;
212 if (mBufferPrefix != null) {
213 bufferPrefix = mBufferPrefix.getBytes();
214 }
215
216 int size;
217 try {
218 while ((size=fis.read(buffer)) > 0) {
219 if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
220 if (bufferPrefix == null) {
221 fos.write(buffer, 0, size);
222 } else {
223 int start = 0;
224 for (int i=0; i<size; i++) {
225 if (buffer[i] != '\n') {
226 if (i > start) {
227 fos.write(buffer, start, i-start);
228 }
229 start = i;
230 if (needPrefix) {
231 fos.write(bufferPrefix);
232 needPrefix = false;
233 }
234 do {
235 i++;
236 } while (i<size && buffer[i] != '\n');
237 if (i < size) {
238 needPrefix = true;
239 }
240 }
241 }
242 if (size > start) {
243 fos.write(buffer, start, size-start);
244 }
245 }
246 }
247 if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
248 if (mThread.isInterrupted()) {
249 if (DEBUG) Slog.i(TAG, "Interrupted!");
250 }
251 } catch (IOException e) {
252 synchronized (this) {
253 mFailure = e.toString();
254 notifyAll();
255 return;
256 }
257 }
258
259 synchronized (this) {
260 mComplete = true;
261 notifyAll();
262 }
263 }
264}