• 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.server.am;
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 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     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     ParcelFileDescriptor getWriteFd() {
66         return mFds[1];
67     }
68 
setBufferPrefix(String prefix)69     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     void go(FileDescriptor out) throws IOException {
124         go(out, DEFAULT_TIMEOUT);
125     }
126 
go(FileDescriptor out, long timeout)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 
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     void kill() {
177         closeFd(0);
178         closeFd(1);
179     }
180 
181     @Override
run()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 }
243