content: package com.lody.virtual.server.pm.installer;
  
  import android.annotation.TargetApi;
  import android.os.Build;
  import android.system.ErrnoException;
  import android.system.Os;
  import android.system.OsConstants;
  import android.util.Log;
  
  import com.lody.virtual.helper.utils.ArrayUtils;
  import com.lody.virtual.helper.utils.FileUtils;
  
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.nio.ByteOrder;
  
  import static android.system.OsConstants.AF_UNIX;
  import static android.system.OsConstants.SOCK_STREAM;
  
  /**
   * Simple bridge that allows file access across process boundaries without
   * returning the underlying {@link FileDescriptor}. This is useful when the
   * server side needs to strongly assert that a client side is completely
   * hands-off.
   */
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public class FileBridge extends Thread {
      private static final String TAG = "FileBridge";
  
      // TODO: consider extending to support bidirectional IO
  
      private static final int MSG_LENGTH = 8;
  
      /**
       * CMD_WRITE [len] [data]
       */
      private static final int CMD_WRITE = 1;
      /**
       * CMD_FSYNC
       */
      private static final int CMD_FSYNC = 2;
      /**
       * CMD_CLOSE
       */
      private static final int CMD_CLOSE = 3;
  
      private FileDescriptor mTarget;
  
      private final FileDescriptor mServer = new FileDescriptor();
      private final FileDescriptor mClient = new FileDescriptor();
  
      private volatile boolean mClosed;
  
  
      public FileBridge() {
          try {
              Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
          } catch (ErrnoException e) {
              throw new RuntimeException("Failed to create bridge");
          }
      }
  
      public boolean isClosed() {
          return mClosed;
      }
  
      public void forceClose() {
          closeQuietly(mTarget);
          closeQuietly(mServer);
          closeQuietly(mClient);
          mClosed = true;
      }
  
      public void setTargetFile(FileDescriptor target) {
          mTarget = target;
      }
  
      public FileDescriptor getClientSocket() {
          return mClient;
      }
  
      @Override
      public void run() {
          final byte[] temp = new byte[8192];
          try {
              while (read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
                  final int cmd = FileUtils.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
                  if (cmd == CMD_WRITE) {
                      // Shuttle data into local file
                      int len = FileUtils.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
                      while (len > 0) {
                          int n = read(mServer, temp, 0, Math.min(temp.length, len));
                          if (n == -1) {
                              throw new IOException(
                                      "Unexpected EOF; still expected " + len + " bytes");
                          }
                          write(mTarget, temp, 0, n);
                          len -= n;
                      }
  
                  } else if (cmd == CMD_FSYNC) {
                      // Sync and echo back to confirm
                      Os.fsync(mTarget);
                      write(mServer, temp, 0, MSG_LENGTH);
  
                  } else if (cmd == CMD_CLOSE) {
                      // Close and echo back to confirm
                      Os.fsync(mTarget);
                      Os.close(mTarget);
                      mClosed = true;
                      write(mServer, temp, 0, MSG_LENGTH);
                      break;
                  }
              }
  
          } catch (ErrnoException | IOException e) {
              Log.wtf(TAG, "Failed during bridge", e);
          } finally {
              forceClose();
          }
      }
  
  
      public static void closeQuietly(FileDescriptor fd) {
  
          if (fd != null && fd.valid()) {
              try {
                  Os.close(fd);
              } catch (ErrnoException e) {
                  e.printStackTrace();
              }
          }
      }
  
      /**
       * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
       * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
       */
      public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
          ArrayUtils.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
          if (byteCount == 0) {
              return 0;
          }
          try {
              int readCount = Os.read(fd, bytes, byteOffset, byteCount);
              if (readCount == 0) {
                  return -1;
              }
              return readCount;
          } catch (ErrnoException errnoException) {
              if (errnoException.errno == OsConstants.EAGAIN) {
                  // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
                  return 0;
              }
              throw new IOException(errnoException);
          }
      }
  
      /**
       * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
       * Unix it never just writes as many bytes as happens to be convenient.)
       */
      public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
          ArrayUtils.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
          if (byteCount == 0) {
              return;
          }
          try {
              while (byteCount > 0) {
                  int bytesWritten = Os.write(fd, bytes, byteOffset, byteCount);
                  byteCount -= bytesWritten;
                  byteOffset += bytesWritten;
              }
          } catch (ErrnoException errnoException) {
              throw new IOException(errnoException);
          }
      }
  }
  