content: package com.genymobile.scrcpy;
  
  import android.net.LocalServerSocket;
  import android.net.LocalSocket;
  import android.net.LocalSocketAddress;
  
  import java.io.Closeable;
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.nio.charset.StandardCharsets;
  
  public final class DesktopConnection implements Closeable {
  
      private static final int DEVICE_NAME_FIELD_LENGTH = 64;
  
      private static final String SOCKET_NAME_PREFIX = "scrcpy";
  
      private final LocalSocket videoSocket;
      private final FileDescriptor videoFd;
  
      private final LocalSocket audioSocket;
      private final FileDescriptor audioFd;
  
      private final LocalSocket controlSocket;
      private final ControlChannel controlChannel;
  
      private DesktopConnection(LocalSocket videoSocket, LocalSocket audioSocket, LocalSocket controlSocket) throws IOException {
          this.videoSocket = videoSocket;
          this.audioSocket = audioSocket;
          this.controlSocket = controlSocket;
  
          videoFd = videoSocket != null ? videoSocket.getFileDescriptor() : null;
          audioFd = audioSocket != null ? audioSocket.getFileDescriptor() : null;
          controlChannel = controlSocket != null ? new ControlChannel(controlSocket) : null;
      }
  
      private static LocalSocket connect(String abstractName) throws IOException {
          LocalSocket localSocket = new LocalSocket();
          localSocket.connect(new LocalSocketAddress(abstractName));
          return localSocket;
      }
  
      private static String getSocketName(int scid) {
          if (scid == -1) {
              // If no SCID is set, use "scrcpy" to simplify using scrcpy-server alone
              return SOCKET_NAME_PREFIX;
          }
  
          return SOCKET_NAME_PREFIX + String.format("_%08x", scid);
      }
  
      public static DesktopConnection open(int scid, boolean tunnelForward, boolean video, boolean audio, boolean control, boolean sendDummyByte)
              throws IOException {
          String socketName = getSocketName(scid);
  
          LocalSocket videoSocket = null;
          LocalSocket audioSocket = null;
          LocalSocket controlSocket = null;
          try {
              if (tunnelForward) {
                  try (LocalServerSocket localServerSocket = new LocalServerSocket(socketName)) {
                      if (video) {
                          videoSocket = localServerSocket.accept();
                          if (sendDummyByte) {
                              // send one byte so the client may read() to detect a connection error
                              videoSocket.getOutputStream().write(0);
                              sendDummyByte = false;
                          }
                      }
                      if (audio) {
                          audioSocket = localServerSocket.accept();
                          if (sendDummyByte) {
                              // send one byte so the client may read() to detect a connection error
                              audioSocket.getOutputStream().write(0);
                              sendDummyByte = false;
                          }
                      }
                      if (control) {
                          controlSocket = localServerSocket.accept();
                          if (sendDummyByte) {
                              // send one byte so the client may read() to detect a connection error
                              controlSocket.getOutputStream().write(0);
                              sendDummyByte = false;
                          }
                      }
                  }
              } else {
                  if (video) {
                      videoSocket = connect(socketName);
                  }
                  if (audio) {
                      audioSocket = connect(socketName);
                  }
                  if (control) {
                      controlSocket = connect(socketName);
                  }
              }
          } catch (IOException | RuntimeException e) {
              if (videoSocket != null) {
                  videoSocket.close();
              }
              if (audioSocket != null) {
                  audioSocket.close();
              }
              if (controlSocket != null) {
                  controlSocket.close();
              }
              throw e;
          }
  
          return new DesktopConnection(videoSocket, audioSocket, controlSocket);
      }
  
      private LocalSocket getFirstSocket() {
          if (videoSocket != null) {
              return videoSocket;
          }
          if (audioSocket != null) {
              return audioSocket;
          }
          return controlSocket;
      }
  
      public void shutdown() throws IOException {
          if (videoSocket != null) {
              videoSocket.shutdownInput();
              videoSocket.shutdownOutput();
          }
          if (audioSocket != null) {
              audioSocket.shutdownInput();
              audioSocket.shutdownOutput();
          }
          if (controlSocket != null) {
              controlSocket.shutdownInput();
              controlSocket.shutdownOutput();
          }
      }
  
      public void close() throws IOException {
          if (videoSocket != null) {
              videoSocket.close();
          }
          if (audioSocket != null) {
              audioSocket.close();
          }
          if (controlSocket != null) {
              controlSocket.close();
          }
      }
  
      public void sendDeviceMeta(String deviceName) throws IOException {
          byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH];
  
          byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
          int len = StringUtils.getUtf8TruncationIndex(deviceNameBytes, DEVICE_NAME_FIELD_LENGTH - 1);
          System.arraycopy(deviceNameBytes, 0, buffer, 0, len);
          // byte[] are always 0-initialized in java, no need to set '\0' explicitly
  
          FileDescriptor fd = getFirstSocket().getFileDescriptor();
          IO.writeFully(fd, buffer, 0, buffer.length);
      }
  
      public FileDescriptor getVideoFd() {
          return videoFd;
      }
  
      public FileDescriptor getAudioFd() {
          return audioFd;
      }
  
      public ControlChannel getControlChannel() {
          return controlChannel;
      }
  }
  