Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2014 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 android.os;
     18 
     19 import static android.system.OsConstants.AF_UNIX;
     20 import static android.system.OsConstants.SOCK_STREAM;
     21 
     22 import android.system.ErrnoException;
     23 import android.system.Os;
     24 import android.util.Log;
     25 
     26 import libcore.io.IoBridge;
     27 import libcore.io.IoUtils;
     28 import libcore.io.Memory;
     29 import libcore.io.Streams;
     30 
     31 import java.io.FileDescriptor;
     32 import java.io.IOException;
     33 import java.io.OutputStream;
     34 import java.nio.ByteOrder;
     35 import java.util.Arrays;
     36 
     37 /**
     38  * Simple bridge that allows file access across process boundaries without
     39  * returning the underlying {@link FileDescriptor}. This is useful when the
     40  * server side needs to strongly assert that a client side is completely
     41  * hands-off.
     42  *
     43  * @hide
     44  * @deprecated replaced by {@link RevocableFileDescriptor}
     45  */
     46 @Deprecated
     47 public class FileBridge extends Thread {
     48     private static final String TAG = "FileBridge";
     49 
     50     // TODO: consider extending to support bidirectional IO
     51 
     52     private static final int MSG_LENGTH = 8;
     53 
     54     /** CMD_WRITE [len] [data] */
     55     private static final int CMD_WRITE = 1;
     56     /** CMD_FSYNC */
     57     private static final int CMD_FSYNC = 2;
     58     /** CMD_CLOSE */
     59     private static final int CMD_CLOSE = 3;
     60 
     61     private FileDescriptor mTarget;
     62 
     63     private final FileDescriptor mServer = new FileDescriptor();
     64     private final FileDescriptor mClient = new FileDescriptor();
     65 
     66     private volatile boolean mClosed;
     67 
     68     public FileBridge() {
     69         try {
     70             Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
     71         } catch (ErrnoException e) {
     72             throw new RuntimeException("Failed to create bridge");
     73         }
     74     }
     75 
     76     public boolean isClosed() {
     77         return mClosed;
     78     }
     79 
     80     public void forceClose() {
     81         IoUtils.closeQuietly(mTarget);
     82         IoUtils.closeQuietly(mServer);
     83         IoUtils.closeQuietly(mClient);
     84         mClosed = true;
     85     }
     86 
     87     public void setTargetFile(FileDescriptor target) {
     88         mTarget = target;
     89     }
     90 
     91     public FileDescriptor getClientSocket() {
     92         return mClient;
     93     }
     94 
     95     @Override
     96     public void run() {
     97         final byte[] temp = new byte[8192];
     98         try {
     99             while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
    100                 final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
    101                 if (cmd == CMD_WRITE) {
    102                     // Shuttle data into local file
    103                     int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
    104                     while (len > 0) {
    105                         int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
    106                         if (n == -1) {
    107                             throw new IOException(
    108                                     "Unexpected EOF; still expected " + len + " bytes");
    109                         }
    110                         IoBridge.write(mTarget, temp, 0, n);
    111                         len -= n;
    112                     }
    113 
    114                 } else if (cmd == CMD_FSYNC) {
    115                     // Sync and echo back to confirm
    116                     Os.fsync(mTarget);
    117                     IoBridge.write(mServer, temp, 0, MSG_LENGTH);
    118 
    119                 } else if (cmd == CMD_CLOSE) {
    120                     // Close and echo back to confirm
    121                     Os.fsync(mTarget);
    122                     Os.close(mTarget);
    123                     mClosed = true;
    124                     IoBridge.write(mServer, temp, 0, MSG_LENGTH);
    125                     break;
    126                 }
    127             }
    128 
    129         } catch (ErrnoException | IOException e) {
    130             Log.wtf(TAG, "Failed during bridge", e);
    131         } finally {
    132             forceClose();
    133         }
    134     }
    135 
    136     public static class FileBridgeOutputStream extends OutputStream {
    137         private final ParcelFileDescriptor mClientPfd;
    138         private final FileDescriptor mClient;
    139         private final byte[] mTemp = new byte[MSG_LENGTH];
    140 
    141         public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
    142             mClientPfd = clientPfd;
    143             mClient = clientPfd.getFileDescriptor();
    144         }
    145 
    146         public FileBridgeOutputStream(FileDescriptor client) {
    147             mClientPfd = null;
    148             mClient = client;
    149         }
    150 
    151         @Override
    152         public void close() throws IOException {
    153             try {
    154                 writeCommandAndBlock(CMD_CLOSE, "close()");
    155             } finally {
    156                 IoBridge.closeAndSignalBlockedThreads(mClient);
    157                 IoUtils.closeQuietly(mClientPfd);
    158             }
    159         }
    160 
    161         public void fsync() throws IOException {
    162             writeCommandAndBlock(CMD_FSYNC, "fsync()");
    163         }
    164 
    165         private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
    166             Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
    167             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
    168 
    169             // Wait for server to ack
    170             if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
    171                 if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
    172                     return;
    173                 }
    174             }
    175 
    176             throw new IOException("Failed to execute " + cmdString + " across bridge");
    177         }
    178 
    179         @Override
    180         public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    181             Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
    182             Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
    183             Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
    184             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
    185             IoBridge.write(mClient, buffer, byteOffset, byteCount);
    186         }
    187 
    188         @Override
    189         public void write(int oneByte) throws IOException {
    190             Streams.writeSingleByte(this, oneByte);
    191         }
    192     }
    193 }
    194