content: package com.termux.shared.net.socket.local;
  
  import androidx.annotation.NonNull;
  
  import com.termux.shared.file.FileUtils;
  import com.termux.shared.logger.Logger;
  import com.termux.shared.markdown.MarkdownUtils;
  
  import java.io.Serializable;
  import java.nio.charset.StandardCharsets;
  
  
  /**
   * Run config for {@link LocalSocketManager}.
   */
  public class LocalSocketRunConfig implements Serializable {
  
      /** The {@link LocalSocketManager} title. */
      protected final String mTitle;
  
      /**
       * The {@link LocalServerSocket} path.
       *
       * For a filesystem socket, this must be an absolute path to the socket file. Creation of a new
       * socket will fail if the server starter app process does not have write and search (execute)
       * permission on the directory in which the socket is created. The client process must have write
       * permission on the socket to connect to it. Other app will not be able to connect to socket
       * if its created in private app data directory.
       *
       * For an abstract namespace socket, the first byte must be a null `\0` character. Note that on
       * Android 9+, if server app is using `targetSdkVersion` `28`, then other apps will not be able
       * to connect to it due to selinux restrictions.
       * > Per-app SELinux domains
       * > Apps that target Android 9 or higher cannot share data with other apps using world-accessible
       * Unix permissions. This change improves the integrity of the Android Application Sandbox,
       * particularly the requirement that an app's private data is accessible only by that app.
       * https://developer.android.com/about/versions/pie/android-9.0-changes-28
       * https://github.com/android/ndk/issues/1469
       * https://stackoverflow.com/questions/63806516/avc-denied-connectto-when-using-uds-on-android-10
       *
       * Max allowed length is 108 bytes as per sun_path size (UNIX_PATH_MAX) on Linux.
       */
      protected final String mPath;
  
      /** If abstract namespace {@link LocalServerSocket} instead of filesystem. */
      protected final boolean mAbstractNamespaceSocket;
  
      /** The {@link ILocalSocketManager} client for the {@link LocalSocketManager}. */
      protected final ILocalSocketManager mLocalSocketManagerClient;
  
      /**
       * The {@link LocalServerSocket} file descriptor.
       * Value will be `>= 0` if socket has been created successfully and `-1` if not created or closed.
       */
      protected int mFD = -1;
  
      /**
       * The {@link LocalClientSocket} receiving (SO_RCVTIMEO) timeout in milliseconds.
       *
       * https://manpages.debian.org/testing/manpages/socket.7.en.html
       * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/services/core/java/com/android/server/am/NativeCrashListener.java;l=55
       * Defaults to {@link #DEFAULT_RECEIVE_TIMEOUT}.
       */
      protected Integer mReceiveTimeout;
      public static final int DEFAULT_RECEIVE_TIMEOUT = 10000;
  
      /**
       * The {@link LocalClientSocket} sending (SO_SNDTIMEO) timeout in milliseconds.
       *
       * https://manpages.debian.org/testing/manpages/socket.7.en.html
       * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/services/core/java/com/android/server/am/NativeCrashListener.java;l=55
       * Defaults to {@link #DEFAULT_SEND_TIMEOUT}.
       */
      protected Integer mSendTimeout;
      public static final int DEFAULT_SEND_TIMEOUT = 10000;
  
      /**
       * The {@link LocalClientSocket} deadline in milliseconds. When the deadline has elapsed after
       * creation time of client socket, all reads and writes will error out. Set to 0, for no
       * deadline.
       * Defaults to {@link #DEFAULT_DEADLINE}.
       */
      protected Long mDeadline;
      public static final int DEFAULT_DEADLINE = 0;
  
      /**
       * The {@link LocalServerSocket} backlog for the maximum length to which the queue of pending connections
       * for the socket may grow. This value may be ignored or may not have one-to-one mapping
       * in kernel implementation. Value must be greater than 0.
       *
       * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/core/java/android/net/LocalSocketManager.java;l=31
       * Defaults to {@link #DEFAULT_BACKLOG}.
       */
      protected Integer mBacklog;
      public static final int DEFAULT_BACKLOG = 50;
  
  
      /**
       * Create an new instance of {@link LocalSocketRunConfig}.
       *
       * @param title The {@link #mTitle} value.
       * @param path The {@link #mPath} value.
       * @param localSocketManagerClient The {@link #mLocalSocketManagerClient} value.
       */
      public LocalSocketRunConfig(@NonNull String title, @NonNull String path, @NonNull ILocalSocketManager localSocketManagerClient) {
          mTitle = title;
          mLocalSocketManagerClient = localSocketManagerClient;
          mAbstractNamespaceSocket = path.getBytes(StandardCharsets.UTF_8)[0] == 0;
  
          if (mAbstractNamespaceSocket)
              mPath = path;
          else
              mPath = FileUtils.getCanonicalPath(path, null);
      }
  
  
  
      /** Get {@link #mTitle}. */
      public String getTitle() {
          return mTitle;
      }
  
      /** Get log title that should be used for {@link LocalSocketManager}. */
      public String getLogTitle() {
          return Logger.getDefaultLogTag() + "." + mTitle;
      }
  
      /** Get {@link #mPath}. */
      public String getPath() {
          return mPath;
      }
  
      /** Get {@link #mAbstractNamespaceSocket}. */
      public boolean isAbstractNamespaceSocket() {
          return mAbstractNamespaceSocket;
      }
  
      /** Get {@link #mLocalSocketManagerClient}. */
      public ILocalSocketManager getLocalSocketManagerClient() {
          return mLocalSocketManagerClient;
      }
  
      /** Get {@link #mFD}. */
      public Integer getFD() {
          return mFD;
      }
  
      /** Set {@link #mFD}. Value must be greater than 0 or -1. */
      public void setFD(int fd) {
          if (fd >= 0)
              mFD = fd;
          else
              mFD = -1;
      }
  
      /** Get {@link #mReceiveTimeout} if set, otherwise {@link #DEFAULT_RECEIVE_TIMEOUT}. */
      public Integer getReceiveTimeout() {
          return mReceiveTimeout != null ? mReceiveTimeout : DEFAULT_RECEIVE_TIMEOUT;
      }
  
      /** Set {@link #mReceiveTimeout}. */
      public void setReceiveTimeout(Integer receiveTimeout) {
          mReceiveTimeout = receiveTimeout;
      }
  
      /** Get {@link #mSendTimeout} if set, otherwise {@link #DEFAULT_SEND_TIMEOUT}. */
      public Integer getSendTimeout() {
          return mSendTimeout != null ? mSendTimeout : DEFAULT_SEND_TIMEOUT;
      }
  
      /** Set {@link #mSendTimeout}. */
      public void setSendTimeout(Integer sendTimeout) {
          mSendTimeout = sendTimeout;
      }
  
      /** Get {@link #mDeadline} if set, otherwise {@link #DEFAULT_DEADLINE}. */
      public Long getDeadline() {
          return mDeadline != null ? mDeadline : DEFAULT_DEADLINE;
      }
  
      /** Set {@link #mDeadline}. */
      public void setDeadline(Long deadline) {
          mDeadline = deadline;
      }
  
      /** Get {@link #mBacklog} if set, otherwise {@link #DEFAULT_BACKLOG}. */
      public Integer getBacklog() {
          return mBacklog != null ? mBacklog : DEFAULT_BACKLOG;
      }
  
      /** Set {@link #mBacklog}. Value must be greater than 0. */
      public void setBacklog(Integer backlog) {
          if (backlog > 0)
              mBacklog = backlog;
      }
  
  
      /**
       * Get a log {@link String} for {@link LocalSocketRunConfig}.
       *
       * @param config The {@link LocalSocketRunConfig} to get info of.
       * @return Returns the log {@link String}.
       */
      @NonNull
      public static String getRunConfigLogString(final LocalSocketRunConfig config) {
          if (config == null) return "null";
          return config.getLogString();
      }
  
      /** Get a log {@link String} for the {@link LocalSocketRunConfig}. */
      @NonNull
      public String getLogString() {
          StringBuilder logString = new StringBuilder();
  
          logString.append(mTitle).append(" Socket Server Run Config:");
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("Path", mPath, "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("AbstractNamespaceSocket", mAbstractNamespaceSocket, "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("LocalSocketManagerClient", mLocalSocketManagerClient.getClass().getName(), "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("FD", mFD, "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("ReceiveTimeout", getReceiveTimeout(), "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("SendTimeout", getSendTimeout(), "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("Deadline", getDeadline(), "-"));
          logString.append("\n").append(Logger.getSingleLineLogStringEntry("Backlog", getBacklog(), "-"));
  
          return logString.toString();
      }
  
      /**
       * Get a markdown {@link String} for {@link LocalSocketRunConfig}.
       *
       * @param config The {@link LocalSocketRunConfig} to get info of.
       * @return Returns the markdown {@link String}.
       */
      public static String getRunConfigMarkdownString(final LocalSocketRunConfig config) {
          if (config == null) return "null";
          return config.getMarkdownString();
      }
  
      /** Get a markdown {@link String} for the {@link LocalSocketRunConfig}. */
      @NonNull
      public String getMarkdownString() {
          StringBuilder markdownString = new StringBuilder();
  
          markdownString.append("## ").append(mTitle).append(" Socket Server Run Config");
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Path", mPath, "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("AbstractNamespaceSocket", mAbstractNamespaceSocket, "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("LocalSocketManagerClient", mLocalSocketManagerClient.getClass().getName(), "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("FD", mFD, "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("ReceiveTimeout", getReceiveTimeout(), "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("SendTimeout", getSendTimeout(), "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Deadline", getDeadline(), "-"));
          markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Backlog", getBacklog(), "-"));
  
          return markdownString.toString();
      }
  
  
  
      @NonNull
      @Override
      public String toString() {
          return getLogString();
      }
  
  }
  