| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.am; |
| |
| import java.io.FileDescriptor; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.os.IInterface; |
| import android.os.ParcelFileDescriptor; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.util.Slog; |
| |
| /** |
| * Helper for transferring data through a pipe from a client app. |
| */ |
| class TransferPipe implements Runnable { |
| static final String TAG = "TransferPipe"; |
| static final boolean DEBUG = false; |
| |
| static final long DEFAULT_TIMEOUT = 5000; // 5 seconds |
| |
| final Thread mThread;; |
| final ParcelFileDescriptor[] mFds; |
| |
| FileDescriptor mOutFd; |
| long mEndTime; |
| String mFailure; |
| boolean mComplete; |
| |
| String mBufferPrefix; |
| |
| interface Caller { |
| void go(IInterface iface, FileDescriptor fd, String prefix, |
| String[] args) throws RemoteException; |
| } |
| |
| TransferPipe() throws IOException { |
| mThread = new Thread(this, "TransferPipe"); |
| mFds = ParcelFileDescriptor.createPipe(); |
| } |
| |
| ParcelFileDescriptor getReadFd() { |
| return mFds[0]; |
| } |
| |
| ParcelFileDescriptor getWriteFd() { |
| return mFds[1]; |
| } |
| |
| void setBufferPrefix(String prefix) { |
| mBufferPrefix = prefix; |
| } |
| |
| static void go(Caller caller, IInterface iface, FileDescriptor out, |
| String prefix, String[] args) throws IOException, RemoteException { |
| go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT); |
| } |
| |
| static void go(Caller caller, IInterface iface, FileDescriptor out, |
| String prefix, String[] args, long timeout) throws IOException, RemoteException { |
| if ((iface.asBinder()) instanceof Binder) { |
| // This is a local object... just call it directly. |
| try { |
| caller.go(iface, out, prefix, args); |
| } catch (RemoteException e) { |
| } |
| return; |
| } |
| |
| TransferPipe tp = new TransferPipe(); |
| try { |
| caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args); |
| tp.go(out, timeout); |
| } finally { |
| tp.kill(); |
| } |
| } |
| |
| static void goDump(IBinder binder, FileDescriptor out, |
| String[] args) throws IOException, RemoteException { |
| goDump(binder, out, args, DEFAULT_TIMEOUT); |
| } |
| |
| static void goDump(IBinder binder, FileDescriptor out, |
| String[] args, long timeout) throws IOException, RemoteException { |
| if (binder instanceof Binder) { |
| // This is a local object... just call it directly. |
| try { |
| binder.dump(out, args); |
| } catch (RemoteException e) { |
| } |
| return; |
| } |
| |
| TransferPipe tp = new TransferPipe(); |
| try { |
| binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args); |
| tp.go(out, timeout); |
| } finally { |
| tp.kill(); |
| } |
| } |
| |
| void go(FileDescriptor out) throws IOException { |
| go(out, DEFAULT_TIMEOUT); |
| } |
| |
| void go(FileDescriptor out, long timeout) throws IOException { |
| try { |
| synchronized (this) { |
| mOutFd = out; |
| mEndTime = SystemClock.uptimeMillis() + timeout; |
| |
| if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd() |
| + " out=" + out); |
| |
| // Close the write fd, so we know when the other side is done. |
| closeFd(1); |
| |
| mThread.start(); |
| |
| while (mFailure == null && !mComplete) { |
| long waitTime = mEndTime - SystemClock.uptimeMillis(); |
| if (waitTime <= 0) { |
| if (DEBUG) Slog.i(TAG, "TIMEOUT!"); |
| mThread.interrupt(); |
| throw new IOException("Timeout"); |
| } |
| |
| try { |
| wait(waitTime); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| if (DEBUG) Slog.i(TAG, "Finished: " + mFailure); |
| if (mFailure != null) { |
| throw new IOException(mFailure); |
| } |
| } |
| } finally { |
| kill(); |
| } |
| } |
| |
| void closeFd(int num) { |
| if (mFds[num] != null) { |
| if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]); |
| try { |
| mFds[num].close(); |
| } catch (IOException e) { |
| } |
| mFds[num] = null; |
| } |
| } |
| |
| void kill() { |
| closeFd(0); |
| closeFd(1); |
| } |
| |
| @Override |
| public void run() { |
| final byte[] buffer = new byte[1024]; |
| final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor()); |
| final FileOutputStream fos = new FileOutputStream(mOutFd); |
| |
| if (DEBUG) Slog.i(TAG, "Ready to read pipe..."); |
| byte[] bufferPrefix = null; |
| boolean needPrefix = true; |
| if (mBufferPrefix != null) { |
| bufferPrefix = mBufferPrefix.getBytes(); |
| } |
| |
| int size; |
| try { |
| while ((size=fis.read(buffer)) > 0) { |
| if (DEBUG) Slog.i(TAG, "Got " + size + " bytes"); |
| if (bufferPrefix == null) { |
| fos.write(buffer, 0, size); |
| } else { |
| int start = 0; |
| for (int i=0; i<size; i++) { |
| if (buffer[i] != '\n') { |
| if (i > start) { |
| fos.write(buffer, start, i-start); |
| } |
| start = i; |
| if (needPrefix) { |
| fos.write(bufferPrefix); |
| needPrefix = false; |
| } |
| do { |
| i++; |
| } while (i<size && buffer[i] != '\n'); |
| if (i < size) { |
| needPrefix = true; |
| } |
| } |
| } |
| if (size > start) { |
| fos.write(buffer, start, size-start); |
| } |
| } |
| } |
| if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size); |
| if (mThread.isInterrupted()) { |
| if (DEBUG) Slog.i(TAG, "Interrupted!"); |
| } |
| } catch (IOException e) { |
| synchronized (this) { |
| mFailure = e.toString(); |
| notifyAll(); |
| return; |
| } |
| } |
| |
| synchronized (this) { |
| mComplete = true; |
| notifyAll(); |
| } |
| } |
| } |