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