Home | History | Annotate | Download | only in os
      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         synchronized (this) {
    178             closeFd(0);
    179             closeFd(1);
    180         }
    181     }
    182 
    183     @Override
    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