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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Binder; 22 import android.os.IBinder; 23 import android.os.IInterface; 24 import android.os.ParcelFileDescriptor; 25 import android.os.RemoteException; 26 import android.os.SystemClock; 27 import android.util.Slog; 28 29 import libcore.io.IoUtils; 30 31 import java.io.ByteArrayOutputStream; 32 import java.io.Closeable; 33 import java.io.FileDescriptor; 34 import java.io.FileInputStream; 35 import java.io.FileOutputStream; 36 import java.io.IOException; 37 import java.io.OutputStream; 38 39 /** 40 * Helper for transferring data through a pipe from a client app. 41 */ 42 public class TransferPipe implements Runnable, Closeable { 43 static final String TAG = "TransferPipe"; 44 static final boolean DEBUG = false; 45 46 static final long DEFAULT_TIMEOUT = 5000; // 5 seconds 47 48 final Thread mThread; 49 final ParcelFileDescriptor[] mFds; 50 51 FileDescriptor mOutFd; 52 long mEndTime; 53 String mFailure; 54 boolean mComplete; 55 56 String mBufferPrefix; 57 58 interface Caller { go(IInterface iface, FileDescriptor fd, String prefix, String[] args)59 void go(IInterface iface, FileDescriptor fd, String prefix, 60 String[] args) throws RemoteException; 61 } 62 TransferPipe()63 public TransferPipe() throws IOException { 64 this(null); 65 } 66 TransferPipe(String bufferPrefix)67 public TransferPipe(String bufferPrefix) throws IOException { 68 this(bufferPrefix, "TransferPipe"); 69 } 70 TransferPipe(String bufferPrefix, String threadName)71 protected TransferPipe(String bufferPrefix, String threadName) throws IOException { 72 mThread = new Thread(this, threadName); 73 mFds = ParcelFileDescriptor.createPipe(); 74 mBufferPrefix = bufferPrefix; 75 } 76 getReadFd()77 ParcelFileDescriptor getReadFd() { 78 return mFds[0]; 79 } 80 getWriteFd()81 public ParcelFileDescriptor getWriteFd() { 82 return mFds[1]; 83 } 84 setBufferPrefix(String prefix)85 public void setBufferPrefix(String prefix) { 86 mBufferPrefix = prefix; 87 } 88 dumpAsync(IBinder binder, FileDescriptor out, String[] args)89 public static void dumpAsync(IBinder binder, FileDescriptor out, String[] args) 90 throws IOException, RemoteException { 91 goDump(binder, out, args); 92 } 93 94 /** 95 * Read raw bytes from a service's dump function. 96 * 97 * <p>This can be used for dumping {@link android.util.proto.ProtoOutputStream protos}. 98 * 99 * @param binder The service providing the data 100 * @param args The arguments passed to the dump function of the service 101 */ dumpAsync(@onNull IBinder binder, @Nullable String... args)102 public static byte[] dumpAsync(@NonNull IBinder binder, @Nullable String... args) 103 throws IOException, RemoteException { 104 ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 105 try { 106 TransferPipe.dumpAsync(binder, pipe[1].getFileDescriptor(), args); 107 108 // Data is written completely when dumpAsync is done 109 pipe[1].close(); 110 pipe[1] = null; 111 112 byte[] buffer = new byte[4096]; 113 try (ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream()) { 114 try (FileInputStream is = new FileInputStream(pipe[0].getFileDescriptor())) { 115 while (true) { 116 int numRead = is.read(buffer); 117 if (numRead == -1) { 118 break; 119 } 120 121 combinedBuffer.write(buffer, 0, numRead); 122 } 123 } 124 125 return combinedBuffer.toByteArray(); 126 } 127 } finally { 128 pipe[0].close(); 129 IoUtils.closeQuietly(pipe[1]); 130 } 131 } 132 go(Caller caller, IInterface iface, FileDescriptor out, String prefix, String[] args)133 static void go(Caller caller, IInterface iface, FileDescriptor out, 134 String prefix, String[] args) throws IOException, RemoteException { 135 go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT); 136 } 137 go(Caller caller, IInterface iface, FileDescriptor out, String prefix, String[] args, long timeout)138 static void go(Caller caller, IInterface iface, FileDescriptor out, 139 String prefix, String[] args, long timeout) throws IOException, RemoteException { 140 if ((iface.asBinder()) instanceof Binder) { 141 // This is a local object... just call it directly. 142 try { 143 caller.go(iface, out, prefix, args); 144 } catch (RemoteException e) { 145 } 146 return; 147 } 148 149 try (TransferPipe tp = new TransferPipe()) { 150 caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args); 151 tp.go(out, timeout); 152 } 153 } 154 goDump(IBinder binder, FileDescriptor out, String[] args)155 static void goDump(IBinder binder, FileDescriptor out, 156 String[] args) throws IOException, RemoteException { 157 goDump(binder, out, args, DEFAULT_TIMEOUT); 158 } 159 goDump(IBinder binder, FileDescriptor out, String[] args, long timeout)160 static void goDump(IBinder binder, FileDescriptor out, 161 String[] args, long timeout) throws IOException, RemoteException { 162 if (binder instanceof Binder) { 163 // This is a local object... just call it directly. 164 try { 165 binder.dump(out, args); 166 } catch (RemoteException e) { 167 } 168 return; 169 } 170 171 try (TransferPipe tp = new TransferPipe()) { 172 binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args); 173 tp.go(out, timeout); 174 } 175 } 176 go(FileDescriptor out)177 public void go(FileDescriptor out) throws IOException { 178 go(out, DEFAULT_TIMEOUT); 179 } 180 go(FileDescriptor out, long timeout)181 public void go(FileDescriptor out, long timeout) throws IOException { 182 try { 183 synchronized (this) { 184 mOutFd = out; 185 mEndTime = SystemClock.uptimeMillis() + timeout; 186 187 if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd() 188 + " out=" + out); 189 190 // Close the write fd, so we know when the other side is done. 191 closeFd(1); 192 193 mThread.start(); 194 195 while (mFailure == null && !mComplete) { 196 long waitTime = mEndTime - SystemClock.uptimeMillis(); 197 if (waitTime <= 0) { 198 if (DEBUG) Slog.i(TAG, "TIMEOUT!"); 199 mThread.interrupt(); 200 throw new IOException("Timeout"); 201 } 202 203 try { 204 wait(waitTime); 205 } catch (InterruptedException e) { 206 } 207 } 208 209 if (DEBUG) Slog.i(TAG, "Finished: " + mFailure); 210 if (mFailure != null) { 211 throw new IOException(mFailure); 212 } 213 } 214 } finally { 215 kill(); 216 } 217 } 218 closeFd(int num)219 void closeFd(int num) { 220 if (mFds[num] != null) { 221 if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]); 222 try { 223 mFds[num].close(); 224 } catch (IOException e) { 225 } 226 mFds[num] = null; 227 } 228 } 229 230 @Override close()231 public void close() { 232 kill(); 233 } 234 kill()235 public void kill() { 236 synchronized (this) { 237 closeFd(0); 238 closeFd(1); 239 } 240 } 241 getNewOutputStream()242 protected OutputStream getNewOutputStream() { 243 return new FileOutputStream(mOutFd); 244 } 245 246 @Override run()247 public void run() { 248 final byte[] buffer = new byte[1024]; 249 final FileInputStream fis; 250 final OutputStream fos; 251 252 synchronized (this) { 253 ParcelFileDescriptor readFd = getReadFd(); 254 if (readFd == null) { 255 Slog.w(TAG, "Pipe has been closed..."); 256 return; 257 } 258 fis = new FileInputStream(readFd.getFileDescriptor()); 259 fos = getNewOutputStream(); 260 } 261 262 if (DEBUG) Slog.i(TAG, "Ready to read pipe..."); 263 byte[] bufferPrefix = null; 264 boolean needPrefix = true; 265 if (mBufferPrefix != null) { 266 bufferPrefix = mBufferPrefix.getBytes(); 267 } 268 269 int size; 270 try { 271 while ((size=fis.read(buffer)) > 0) { 272 if (DEBUG) Slog.i(TAG, "Got " + size + " bytes"); 273 if (bufferPrefix == null) { 274 fos.write(buffer, 0, size); 275 } else { 276 int start = 0; 277 for (int i=0; i<size; i++) { 278 if (buffer[i] != '\n') { 279 if (i > start) { 280 fos.write(buffer, start, i-start); 281 } 282 start = i; 283 if (needPrefix) { 284 fos.write(bufferPrefix); 285 needPrefix = false; 286 } 287 do { 288 i++; 289 } while (i<size && buffer[i] != '\n'); 290 if (i < size) { 291 needPrefix = true; 292 } 293 } 294 } 295 if (size > start) { 296 fos.write(buffer, start, size-start); 297 } 298 } 299 } 300 if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size); 301 if (mThread.isInterrupted()) { 302 if (DEBUG) Slog.i(TAG, "Interrupted!"); 303 } 304 } catch (IOException e) { 305 synchronized (this) { 306 mFailure = e.toString(); 307 notifyAll(); 308 return; 309 } 310 } 311 312 synchronized (this) { 313 mComplete = true; 314 notifyAll(); 315 } 316 } 317 } 318