From 1d0a7ab02320349ea9db5a178c45c9fdacf742a6 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Thu, 12 Oct 2023 20:57:44 +0800 Subject: [PATCH] Support Android API levels 24 - 27 (#13884) --- lib/reply/src/term_size.cr | 6 +- src/crystal/iconv.cr | 2 +- src/crystal/system/unix/file_descriptor.cr | 68 +++++++++++++++++++--- src/io/console.cr | 8 +-- src/lib_c.cr | 2 +- src/lib_c/aarch64-android/c/iconv.cr | 4 +- src/lib_c/aarch64-android/c/sys/ioctl.cr | 8 +++ src/lib_c/aarch64-android/c/termios.cr | 2 - 8 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 src/lib_c/aarch64-android/c/sys/ioctl.cr diff --git a/lib/reply/src/term_size.cr b/lib/reply/src/term_size.cr index d0a4c2e79699..fd0c60421c4f 100644 --- a/lib/reply/src/term_size.cr +++ b/lib/reply/src/term_size.cr @@ -145,6 +145,10 @@ end {% end %} {% end %} - fun ioctl(fd : Int, request : ULong, ...) : Int + {% if flag?(:android) %} + fun ioctl(__fd : Int, __request : Int, ...) : Int + {% else %} + fun ioctl(fd : Int, request : ULong, ...) : Int + {% end %} end {% end %} diff --git a/src/crystal/iconv.cr b/src/crystal/iconv.cr index 64d4e17f8112..593c492d4ce3 100644 --- a/src/crystal/iconv.cr +++ b/src/crystal/iconv.cr @@ -1,4 +1,4 @@ -{% if flag?(:use_libiconv) || flag?(:win32) %} +{% if flag?(:use_libiconv) || flag?(:win32) || (flag?(:android) && LibC::ANDROID_API < 28) %} require "./lib_iconv" private USE_LIBICONV = true {% else %} diff --git a/src/crystal/system/unix/file_descriptor.cr b/src/crystal/system/unix/file_descriptor.cr index 8a5c01cff44e..d77708f314bb 100644 --- a/src/crystal/system/unix/file_descriptor.cr +++ b/src/crystal/system/unix/file_descriptor.cr @@ -1,6 +1,9 @@ require "c/fcntl" require "io/evented" require "termios" +{% if flag?(:android) && LibC::ANDROID_API < 28 %} + require "c/sys/ioctl" +{% end %} # :nodoc: module Crystal::System::FileDescriptor @@ -198,7 +201,7 @@ module Crystal::System::FileDescriptor system_console_mode do |mode| flags = LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL mode.c_lflag = enable ? (mode.c_lflag | flags) : (mode.c_lflag & ~flags) - if LibC.tcsetattr(fd, LibC::TCSANOW, pointerof(mode)) != 0 + if FileDescriptor.tcsetattr(fd, LibC::TCSANOW, pointerof(mode)) != 0 raise IO::Error.from_errno("tcsetattr") end yield @@ -208,13 +211,13 @@ module Crystal::System::FileDescriptor private def system_raw(enable : Bool, & : ->) system_console_mode do |mode| if enable - LibC.cfmakeraw(pointerof(mode)) + FileDescriptor.cfmakeraw(pointerof(mode)) else mode.c_iflag |= LibC::BRKINT | LibC::ISTRIP | LibC::ICRNL | LibC::IXON mode.c_oflag |= LibC::OPOST mode.c_lflag |= LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL | LibC::ICANON | LibC::ISIG | LibC::IEXTEN end - if LibC.tcsetattr(fd, LibC::TCSANOW, pointerof(mode)) != 0 + if FileDescriptor.tcsetattr(fd, LibC::TCSANOW, pointerof(mode)) != 0 raise IO::Error.from_errno("tcsetattr") end yield @@ -223,13 +226,60 @@ module Crystal::System::FileDescriptor @[AlwaysInline] private def system_console_mode(&) - if LibC.tcgetattr(fd, out mode) != 0 - raise IO::Error.from_errno("tcgetattr") + before = FileDescriptor.tcgetattr(fd) + begin + yield before + ensure + FileDescriptor.tcsetattr(fd, LibC::TCSANOW, pointerof(before)) end + end + + @[AlwaysInline] + def self.tcgetattr(fd) + termios = uninitialized LibC::Termios + ret = {% if flag?(:android) && !LibC.has_method?(:tcgetattr) %} + LibC.ioctl(fd, LibC::TCGETS, pointerof(termios)) + {% else %} + LibC.tcgetattr(fd, pointerof(termios)) + {% end %} + raise IO::Error.from_errno("tcgetattr") if ret != 0 + termios + end + + @[AlwaysInline] + def self.tcsetattr(fd, optional_actions, termios_p) + {% if flag?(:android) && !LibC.has_method?(:tcsetattr) %} + optional_actions = optional_actions.value if optional_actions.is_a?(Termios::LineControl) + cmd = case optional_actions + when LibC::TCSANOW + LibC::TCSETS + when LibC::TCSADRAIN + LibC::TCSETSW + when LibC::TCSAFLUSH + LibC::TCSETSF + else + Errno.value = Errno::EINVAL + return LibC::Int.new(-1) + end + + LibC.ioctl(fd, cmd, termios_p) + {% else %} + LibC.tcsetattr(fd, optional_actions, termios_p) + {% end %} + end - before = mode - ret = yield mode - LibC.tcsetattr(fd, LibC::TCSANOW, pointerof(before)) - ret + @[AlwaysInline] + def self.cfmakeraw(termios_p) + {% if flag?(:android) && !LibC.has_method?(:cfmakeraw) %} + s.value.c_iflag &= ~(LibC::IGNBRK | LibC::BRKINT | LibC::PARMRK | LibC::ISTRIP | LibC::INLCR | LibC::IGNCR | LibC::ICRNL | LibC::IXON) + s.value.c_oflag &= ~LibC::OPOST + s.value.c_lflag &= ~(LibC::ECHO | LibC::ECHONL | LibC::ICANON | LibC::ISIG | LibC::IEXTEN) + s.value.c_cflag &= ~(LibC::CSIZE | LibC::PARENB) + s.value.c_cflag |= LibC::CS8 + s.value.c_cc[LibC::VMIN] = 1 + s.value.c_cc[LibC::VTIME] = 0 + {% else %} + LibC.cfmakeraw(termios_p) + {% end %} end end diff --git a/src/io/console.cr b/src/io/console.cr index d5c756edc7e8..5ac51b497c29 100644 --- a/src/io/console.cr +++ b/src/io/console.cr @@ -92,7 +92,7 @@ class IO::FileDescriptor < IO @[Deprecated] macro noecho_from_tc_mode! mode.c_lflag &= ~(Termios::LocalMode.flags(ECHO, ECHOE, ECHOK, ECHONL).value) - LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) + Crystal::System::FileDescriptor.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) end @[Deprecated] @@ -109,12 +109,12 @@ class IO::FileDescriptor < IO Termios::LocalMode::ICANON | Termios::LocalMode::ISIG | Termios::LocalMode::IEXTEN).value - LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) + Crystal::System::FileDescriptor.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) end @[Deprecated] macro raw_from_tc_mode! - LibC.cfmakeraw(pointerof(mode)) - LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) + Crystal::System::FileDescriptor.cfmakeraw(pointerof(mode)) + Crystal::System::FileDescriptor.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode)) end end diff --git a/src/lib_c.cr b/src/lib_c.cr index b707d18a3a9d..b859a4c85061 100644 --- a/src/lib_c.cr +++ b/src/lib_c.cr @@ -27,7 +27,7 @@ lib LibC {% if flag?(:android) %} {% default_api_version = 31 %} - {% min_supported_version = 28 %} + {% min_supported_version = 24 %} {% api_version_var = env("ANDROID_PLATFORM") || env("ANDROID_NATIVE_API_LEVEL") %} {% api_version = api_version_var ? api_version_var.gsub(/^android-/, "").to_i : default_api_version %} {% raise "TODO: Support Android API level below #{min_supported_version}" unless api_version >= min_supported_version %} diff --git a/src/lib_c/aarch64-android/c/iconv.cr b/src/lib_c/aarch64-android/c/iconv.cr index 6a9a20a6eb7a..ea48b1122c32 100644 --- a/src/lib_c/aarch64-android/c/iconv.cr +++ b/src/lib_c/aarch64-android/c/iconv.cr @@ -1,9 +1,9 @@ require "./stddef" lib LibC - type IconvT = Void* - {% if ANDROID_API >= 28 %} + type IconvT = Void* + fun iconv(__converter : IconvT, __src_buf : Char**, __src_bytes_left : SizeT*, __dst_buf : Char**, __dst_bytes_left : SizeT*) : SizeT fun iconv_close(__converter : IconvT) : Int fun iconv_open(__src_encoding : Char*, __dst_encoding : Char*) : IconvT diff --git a/src/lib_c/aarch64-android/c/sys/ioctl.cr b/src/lib_c/aarch64-android/c/sys/ioctl.cr new file mode 100644 index 000000000000..4667c87864a4 --- /dev/null +++ b/src/lib_c/aarch64-android/c/sys/ioctl.cr @@ -0,0 +1,8 @@ +lib LibC + TCGETS = 0x5401 + TCSETS = 0x5402 + TCSETSW = 0x5403 + TCSETSF = 0x5404 + + fun ioctl(__fd : Int, __request : Int, ...) : Int +end diff --git a/src/lib_c/aarch64-android/c/termios.cr b/src/lib_c/aarch64-android/c/termios.cr index 01f5a2831a0b..cf96bf3eb3c7 100644 --- a/src/lib_c/aarch64-android/c/termios.cr +++ b/src/lib_c/aarch64-android/c/termios.cr @@ -168,8 +168,6 @@ lib LibC c_cc : StaticArray(CcT, 19) # cc_t[NCCS] end - # TODO: defined inline for `21 <= ANDROID_API < 28` in terms of `ioctl`, but - # `lib/reply/src/term_size.cr` contains an incompatible definition of it {% if ANDROID_API >= 28 %} fun tcgetattr(__fd : Int, __t : Termios*) : Int fun tcsetattr(__fd : Int, __optional_actions : Int, __t : Termios*) : Int