• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.internal.os;
18 
19 import java.io.FileDescriptor;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 
24 import android.os.Binder;
25 import android.os.IBinder;
26 import android.os.IInterface;
27 import android.os.ParcelFileDescriptor;
28 import android.os.RemoteException;
29 import android.os.SystemClock;
30 import android.util.Slog;
31 
32 /**
33  * Helper for transferring data through a pipe from a client app.
34  */
35 public final class 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 {
go(IInterface iface, FileDescriptor fd, String prefix, String[] args)52         void go(IInterface iface, FileDescriptor fd, String prefix,
53                 String[] args) throws RemoteException;
54     }
55 
TransferPipe()56     public TransferPipe() throws IOException {
57         mThread = new Thread(this, "TransferPipe");
58         mFds = ParcelFileDescriptor.createPipe();
59     }
60 
getReadFd()61     ParcelFileDescriptor getReadFd() {
62         return mFds[0];
63     }
64 
getWriteFd()65     public ParcelFileDescriptor getWriteFd() {
66         return mFds[1];
67     }
68 
setBufferPrefix(String prefix)69     public void setBufferPrefix(String prefix) {
70         mBufferPrefix = prefix;
71     }
72 
go(Caller caller, IInterface iface, FileDescriptor out, String prefix, String[] args)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 
go(Caller caller, IInterface iface, FileDescriptor out, String prefix, String[] args, long timeout)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 
goDump(IBinder binder, FileDescriptor out, String[] args)98     static void goDump(IBinder binder, FileDescriptor out,
99             String[] args) throws IOException, RemoteException {
100         goDump(binder, out, args, DEFAULT_TIMEOUT);
101     }
102 
goDump(IBinder binder, FileDescriptor out, String[] args, long timeout)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 
go(FileDescriptor out)123     public void go(FileDescriptor out) throws IOException {
124         go(out, DEFAULT_TIMEOUT);
125     }
126 
go(FileDescriptor out, long timeout)127     public 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 
closeFd(int num)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 
kill()176     public void kill() {
177         synchronized (this) {
178             closeFd(0);
179             closeFd(1);
180         }
181     }
182 
183     @Override
run()184     public void run() {
185         final byte[] buffer = new byte[1024];
186         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         }
198 
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 }
255