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 { 52 void go(IInterface iface, FileDescriptor fd, String prefix, 53 String[] args) throws RemoteException; 54 } 55 56 public TransferPipe() throws IOException { 57 mThread = new Thread(this, "TransferPipe"); 58 mFds = ParcelFileDescriptor.createPipe(); 59 } 60 61 ParcelFileDescriptor getReadFd() { 62 return mFds[0]; 63 } 64 65 public ParcelFileDescriptor getWriteFd() { 66 return mFds[1]; 67 } 68 69 public void setBufferPrefix(String prefix) { 70 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 123 public void go(FileDescriptor out) throws IOException { 124 go(out, DEFAULT_TIMEOUT); 125 } 126 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 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 176 public void kill() { 177 closeFd(0); 178 closeFd(1); 179 } 180 181 @Override 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