diff --git a/Directory.Build.props b/Directory.Build.props index 56c99dd0f760f8..e9d93efb2aecfa 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,19 +27,21 @@ - eng/native/build-commons.sh - eng/native/gen-buildsys.cmd - src/native/libs/build-native.sh - - src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs + - src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs - src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets - - src/mono/mono/tools/offsets-tool/offsets-tool.py + - src/mono/mono/offsets/offsets-tool.py - src/mono/msbuild/apple/build/AppleBuild.targets - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-x64.xml - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-arm64.xml - src/mono/msbuild/common/MonoAOTCompiler.props - src/tasks/AppleAppBuilder/Xcode.cs - src/tasks/MobileBuildTasks/Apple/AppleProject.cs + - src/tasks/MobileBuildTasks/Android/AndroidProject.cs + - src/tasks/AndroidAppBuilder/ApkBuilder.cs - src/tasks/Crossgen2Tasks/Microsoft.NET.CrossGen.targets - https://github.com/dotnet/sdk repo > src/Installer/redist-installer/targets/GeneratePKG.targets --> - 21 + 24 13.0 13.0 14.0 diff --git a/docs/workflow/building/coreclr/android.md b/docs/workflow/building/coreclr/android.md index 81f2d52b7262a1..f23bfaa9295554 100644 --- a/docs/workflow/building/coreclr/android.md +++ b/docs/workflow/building/coreclr/android.md @@ -44,7 +44,7 @@ Supported target architectures: - Download and install [OpenJDK 23](https://openjdk.org/projects/jdk/23/) - Download and install [Android Studio](https://developer.android.com/studio/install) and the following: - - Android SDK (minimum supported API level is 21) + - Android SDK (minimum supported API level is 24) - Android NDK r27c > [!NOTE] diff --git a/eng/common/cross/build-android-rootfs.sh b/eng/common/cross/build-android-rootfs.sh index fbd8d80848a6ce..09d65eaff91237 100755 --- a/eng/common/cross/build-android-rootfs.sh +++ b/eng/common/cross/build-android-rootfs.sh @@ -21,7 +21,7 @@ usage() exit 1 } -__ApiLevel=28 # The minimum platform for arm64 is API level 21 but the minimum version that support glob(3) is 28. See $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/glob.h +__ApiLevel=28 # The minimum platform for arm64 is API level 24 but the minimum version that supports glob(3) is 28. See $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/glob.h __BuildArch=arm64 __AndroidArch=aarch64 __AndroidToolchain=aarch64-linux-android diff --git a/eng/native/build-commons.sh b/eng/native/build-commons.sh index c0631ecda1308e..2a036677feaf66 100755 --- a/eng/native/build-commons.sh +++ b/eng/native/build-commons.sh @@ -101,7 +101,7 @@ build_native() if [[ "$targetOS" == android || "$targetOS" == linux-bionic ]]; then # Keep in sync with $(AndroidApiLevelMin) in Directory.Build.props in the repository rooot - local ANDROID_API_LEVEL=21 + local ANDROID_API_LEVEL=24 if [[ -z "$ANDROID_NDK_ROOT" ]]; then echo "Error: You need to set the ANDROID_NDK_ROOT environment variable pointing to the Android NDK root." exit 1 diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd index 0f5cccf8c89aed..4782c4723ab27f 100644 --- a/eng/native/gen-buildsys.cmd +++ b/eng/native/gen-buildsys.cmd @@ -76,7 +76,7 @@ if /i "%__Arch%" == "wasm" ( if /i "%__Os%" == "android" ( :: Keep in sync with $(AndroidApiLevelMin) in Directory.Build.props in the repository rooot - set __ANDROID_API_LEVEL=21 + set __ANDROID_API_LEVEL=24 if "%ANDROID_NDK_ROOT%" == "" ( echo Error: You need to set the ANDROID_NDK_ROOT environment variable pointing to the Android NDK root. exit /B 1 diff --git a/eng/pipelines/helix-platforms.yml b/eng/pipelines/helix-platforms.yml index dbceeb4b25eca2..3641106827121a 100644 --- a/eng/pipelines/helix-platforms.yml +++ b/eng/pipelines/helix-platforms.yml @@ -288,9 +288,9 @@ variables: - name: helix_android_ubuntu_latest value: Ubuntu.2204.Amd64.Android.29.Open - # Oldest: API 21 + # Oldest: API 24 - name: helix_android_ubuntu_oldest - value: Ubuntu.2204.Amd64.Android.21.Open + value: Ubuntu.2204.Amd64.Android.24.Open # =========================================== # Common Aliases (default to latest) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 48b8e6ef368054..997d2dd6cdcb3f 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -48,10 +48,10 @@ The .NET Foundation licenses this file to you under the MIT license. armv7 gnu - android21 + android24 musl gnueabihf - androideabi21 + androideabi24 musleabihf diff --git a/src/coreclr/nativeaot/docs/android-bionic.md b/src/coreclr/nativeaot/docs/android-bionic.md index 8a7aa566466f88..389ee69756a98a 100644 --- a/src/coreclr/nativeaot/docs/android-bionic.md +++ b/src/coreclr/nativeaot/docs/android-bionic.md @@ -4,7 +4,7 @@ Starting with .NET 8 Preview 7, it's possible to build shared libraries and comm Not a full Android experience is available - it's only possible to publish for two Bionic RID: linux-bionic-arm64 and linux-bionic-x64. Publishing for Android RIDs (android-arm64/android-x64) is not possible. This limited experience corresponds to building with [Android NDK](https://developer.android.com/ndk) from Native code - the limitations are similar. Interop with Java needs to be done manually through JNI, if necessary. -The minimum API level is 21 at the time of writing the document, but search for AndroidApiLevelMin in this repo for more up-to-date information. +The minimum API level has been raised to 24 in .NET 11. Search for `AndroidApiLevelMin` in this repo for the current value. To build for Bionic: diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/OperatingSystemTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/OperatingSystemTests.cs index 89715afadead85..6c2f1867e67d40 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/OperatingSystemTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/OperatingSystemTests.cs @@ -107,7 +107,7 @@ public static void IsOSPlatformVersionAtLeast_InvalidArgs_Throws() public static void TestIsOSVersionAtLeast_Android() => TestIsOSVersionAtLeast("Android"); [Fact, PlatformSpecific(TestPlatforms.Android)] - public static void TestIsOSVersionAtLeast_Android_21() => Assert.True(OperatingSystem.IsAndroidVersionAtLeast(21)); // 21 is our min supported version + public static void TestIsOSVersionAtLeast_Android_24() => Assert.True(OperatingSystem.IsAndroidVersionAtLeast(24)); // 24 is our min supported version [Fact, PlatformSpecific(TestPlatforms.iOS)] public static void TestIsOSPlatform_IOS() => TestIsOSPlatform("iOS", OperatingSystem.IsIOS); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.AndroidKeyStore.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.AndroidKeyStore.cs index 72824f6a004cd4..647b8caaf02d20 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.AndroidKeyStore.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.AndroidKeyStore.cs @@ -53,9 +53,6 @@ public void Add(ICertificatePal cert) { Interop.AndroidCrypto.PAL_KeyAlgorithm algorithm = certPal.PrivateKeyHandle switch { - // The AndroidKeyStore doesn't support adding DSA private key entries in newer versions (API 23+) - // Our minimum supported version (API 21) does support it, but for simplicity, we simply block adding - // certificates with DSA private keys on all versions instead of trying to support it on two versions. SafeDsaHandle => throw new PlatformNotSupportedException(SR.Cryptography_X509_StoreDSAPrivateKeyNotSupported), SafeEcKeyHandle => Interop.AndroidCrypto.PAL_KeyAlgorithm.EC, SafeRsaHandle => Interop.AndroidCrypto.PAL_KeyAlgorithm.RSA, diff --git a/src/mono/mono/offsets/offsets-tool.py b/src/mono/mono/offsets/offsets-tool.py index aec4a637dd614b..5e788412726480 100644 --- a/src/mono/mono/offsets/offsets-tool.py +++ b/src/mono/mono/offsets/offsets-tool.py @@ -109,7 +109,7 @@ def require_emscipten_path (args): self.sys_includes=[] self.target = None self.target_args = [] - android_api_level = "-D__ANDROID_API=21" + android_api_level = "-D__ANDROID_API=24" if args.libclang_headers: self.sys_includes+= [args.libclang_headers] diff --git a/src/mono/msbuild/android/build/AndroidBuild.targets b/src/mono/msbuild/android/build/AndroidBuild.targets index 96e8607dd80dab..f393985c8a3d38 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.targets +++ b/src/mono/msbuild/android/build/AndroidBuild.targets @@ -137,7 +137,7 @@ - 21 + 24 diff --git a/src/native/libs/System.Native/CMakeLists.txt b/src/native/libs/System.Native/CMakeLists.txt index 0beca0c2bb0ba2..90cac1c0cae2e3 100644 --- a/src/native/libs/System.Native/CMakeLists.txt +++ b/src/native/libs/System.Native/CMakeLists.txt @@ -116,14 +116,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake) set(NATIVE_LIBS_EXTRA) append_extra_system_libs(NATIVE_LIBS_EXTRA) -if (CLR_CMAKE_TARGET_ANDROID AND NOT HAVE_GETIFADDRS) - add_definitions(-DANDROID_GETIFADDRS_WORKAROUND) - add_compile_options(-Wno-gnu-zero-variadic-macro-arguments) - - list (APPEND NATIVE_LIBS_EXTRA -llog) - list (APPEND NATIVE_SOURCES pal_ifaddrs.c) -endif () - if (GEN_SHARED_LIB) add_library(System.Native SHARED diff --git a/src/native/libs/System.Native/pal_ifaddrs.c b/src/native/libs/System.Native/pal_ifaddrs.c deleted file mode 100644 index e95265afbfa235..00000000000000 --- a/src/native/libs/System.Native/pal_ifaddrs.c +++ /dev/null @@ -1,760 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "pal_ifaddrs.h" -#include "pal_safecrt.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#define LOG(level, ...) __android_log_print(level, "DOTNET_NETLINK", ## __VA_ARGS__) -#define LOG_INFO(...) LOG(ANDROID_LOG_INFO, ## __VA_ARGS__) -#define LOG_WARN(...) LOG(ANDROID_LOG_WARN, ## __VA_ARGS__) -#ifdef DEBUG -#define LOG_DEBUG(...) LOG(ANDROID_LOG_DEBUG, ## __VA_ARGS__) -#else -#define LOG_DEBUG(...) do {} while (0) -#endif - -/* Maximum interface address label size, should be more than enough */ -#define MAX_IFA_LABEL_SIZE 1024 - -/* This is the message we send to the kernel */ -struct netlink_request { - struct nlmsghdr header; - struct rtgenmsg message; -}; - -struct netlink_session { - int sock_fd; - int seq; - struct sockaddr_nl them; /* kernel end */ - struct sockaddr_nl us; /* our end */ - struct msghdr message_header; /* for use with sendmsg */ - struct iovec payload_vector; /* Used to send netlink_request */ -}; - -struct sockaddr_ll_extended { - unsigned short int sll_family; - unsigned short int sll_protocol; - int sll_ifindex; - unsigned short int sll_hatype; - unsigned char sll_pkttype; - unsigned char sll_halen; - unsigned char sll_addr[24]; -}; - -static void free_single_ifaddrs(struct ifaddrs **ifap) -{ - struct ifaddrs *ifa = ifap ? *ifap : NULL; - if (!ifa) - return; - - if (ifa->ifa_name) - free(ifa->ifa_name); - - if (ifa->ifa_addr) - free(ifa->ifa_addr); - - if (ifa->ifa_netmask) - free(ifa->ifa_netmask); - - if (ifa->ifa_broadaddr) - free(ifa->ifa_broadaddr); - - if (ifa->ifa_data) - free(ifa->ifa_data); - - free(ifa); - *ifap = NULL; -} - -static int open_netlink_session(struct netlink_session *session) -{ - abort_if_invalid_pointer_argument(session); - - memset(session, 0, sizeof(*session)); - session->sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (session->sock_fd == -1) { - LOG_WARN("Failed to create a netlink socket. %s\n", strerror(errno)); - return -1; - } - - /* Fill out addresses */ - session->us.nl_family = AF_NETLINK; - - /* We have previously used `getpid()` here but it turns out that WebView/Chromium does the same - and there can only be one session with the same PID. Setting it to 0 will cause the kernel to - assign some PID that's unique and valid instead. - See: https://bugzilla.xamarin.com/show_bug.cgi?id=41860 - */ - session->us.nl_pid = 0; - session->us.nl_groups = 0; - - session->them.nl_family = AF_NETLINK; - - if (bind (session->sock_fd, (struct sockaddr *)&session->us, sizeof(session->us)) < 0) { - LOG_WARN("Failed to bind to the netlink socket. %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int send_netlink_dump_request(struct netlink_session *session, int type) -{ - struct netlink_request request; - - memset(&request, 0, sizeof(request)); - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - /* Flags (from netlink.h): - NLM_F_REQUEST - it's a request message - NLM_F_DUMP - gives us the root of the link tree and returns all links matching our requested - AF, which in our case means all of them (AF_PACKET) - */ - request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH; - request.header.nlmsg_seq = (uint32_t)++session->seq; - request.header.nlmsg_pid = session->us.nl_pid; - request.header.nlmsg_type = (uint16_t)type; - - /* AF_PACKET means we want to see everything */ - request.message.rtgen_family = AF_PACKET; - - memset(&session->payload_vector, 0, sizeof(session->payload_vector)); - session->payload_vector.iov_len = request.header.nlmsg_len; - session->payload_vector.iov_base = &request; - - memset(&session->message_header, 0, sizeof(session->message_header)); - session->message_header.msg_namelen = sizeof(session->them); - session->message_header.msg_name = &session->them; - session->message_header.msg_iovlen = 1; - session->message_header.msg_iov = &session->payload_vector; - - if (sendmsg(session->sock_fd, (const struct msghdr*)&session->message_header, 0) < 0) { - LOG_WARN("Failed to send netlink message. %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int append_ifaddr(struct ifaddrs *addr, struct ifaddrs **ifaddrs_head, struct ifaddrs **last_ifaddr) -{ - abort_if_invalid_pointer_argument(addr); - abort_if_invalid_pointer_argument(ifaddrs_head); - abort_if_invalid_pointer_argument(last_ifaddr); - - if (addr->ifa_name == NULL) { - LOG_WARN("ifa_name is NULL -- skipping"); - return 0; // skip this addr - } - - if (!*ifaddrs_head) { - *ifaddrs_head = *last_ifaddr = addr; - if (!*ifaddrs_head) - return -1; - } else if (!*last_ifaddr) { - struct ifaddrs *last = *ifaddrs_head; - - while (last->ifa_next) - last = last->ifa_next; - *last_ifaddr = last; - } - - addr->ifa_next = NULL; - if (addr == *last_ifaddr) - return 0; - - (*last_ifaddr)->ifa_next = addr; - *last_ifaddr = addr; - - return 0; -} - -static int fill_sa_address(struct sockaddr **sa, struct ifaddrmsg *net_address, void *rta_data, size_t rta_payload_length) -{ - abort_if_invalid_pointer_argument(sa); - abort_if_invalid_pointer_argument(net_address); - abort_if_invalid_pointer_argument(rta_data); - - switch (net_address->ifa_family) { - case AF_INET: { - struct sockaddr_in *sa4; - if (rta_payload_length != 4) /* IPv4 address length */ { - LOG_WARN("Unexpected IPv4 address payload length %zu", rta_payload_length); - return -1; - } - sa4 = (struct sockaddr_in*)calloc(1, sizeof(*sa4)); - if (sa4 == NULL) - return -1; - - sa4->sin_family = AF_INET; - memcpy(&sa4->sin_addr, rta_data, rta_payload_length); - *sa = (struct sockaddr*)sa4; - break; - } - - case AF_INET6: { - struct sockaddr_in6 *sa6; - if (rta_payload_length != 16) /* IPv6 address length */ { - LOG_WARN("Unexpected IPv6 address payload length %zu", rta_payload_length); - return -1; - } - sa6 = (struct sockaddr_in6*)calloc(1, sizeof(*sa6)); - if (sa6 == NULL) - return -1; - - sa6->sin6_family = AF_INET6; - memcpy(&sa6->sin6_addr, rta_data, rta_payload_length); - if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL (&sa6->sin6_addr)) - sa6->sin6_scope_id = net_address->ifa_index; - *sa = (struct sockaddr*)sa6; - break; - } - - default: { - struct sockaddr *sagen; - if (rta_payload_length > sizeof(sagen->sa_data)) { - LOG_WARN("Unexpected RTA payload length %zu (wanted at most %zu)", rta_payload_length, sizeof(sagen->sa_data)); - return -1; - } - - *sa = sagen = (struct sockaddr*)calloc(1, sizeof(*sagen)); - if (!sagen) - return -1; - - sagen->sa_family = net_address->ifa_family; - memcpy(&sagen->sa_data, rta_data, rta_payload_length); - break; - } - } - - return 0; -} - -static int fill_ll_address(struct sockaddr_ll_extended **sa, struct ifinfomsg *net_interface, void *rta_data, size_t rta_payload_length) -{ - abort_if_invalid_pointer_argument(sa); - abort_if_invalid_pointer_argument(net_interface); - - /* Always allocate, do not free - caller may reuse the same variable */ - *sa = (struct sockaddr_ll_extended*)calloc(1, sizeof(**sa)); - if (!*sa) - return -1; - - (*sa)->sll_family = AF_PACKET; /* Always for physical links */ - - /* The assert can only fail for Iniband links, which are quite unlikely to be found - * in any mobile devices - */ - LOG_DEBUG("rta_payload_length == %zu; sizeof sll_addr == %zu; hw type == 0x%X\n", rta_payload_length, sizeof((*sa)->sll_addr), net_interface->ifi_type); - if ((size_t)(rta_payload_length) > sizeof((*sa)->sll_addr)) { - LOG_INFO("Address is too long to place in sockaddr_ll (%zu > %zu)", rta_payload_length, sizeof((*sa)->sll_addr)); - free(*sa); - *sa = NULL; - return -1; - } - - if (rta_payload_length > UCHAR_MAX) { - LOG_INFO("Payload length too big to fit in the address structure"); - free(*sa); - *sa = NULL; - return -1; - } - - (*sa)->sll_ifindex = net_interface->ifi_index; - (*sa)->sll_hatype = net_interface->ifi_type; - (*sa)->sll_halen = (unsigned char)(rta_payload_length); - memcpy((*sa)->sll_addr, rta_data, rta_payload_length); - - return 0; -} - - -static struct ifaddrs *get_link_info(struct nlmsghdr *message) -{ - ssize_t length; - struct rtattr *attribute; - struct ifinfomsg *net_interface; - struct ifaddrs *ifa = NULL; - struct sockaddr_ll_extended *sa = NULL; - - abort_if_invalid_pointer_argument(message); - net_interface = (struct ifinfomsg*)(NLMSG_DATA(message)); - length = (ssize_t)(message->nlmsg_len - NLMSG_LENGTH(sizeof(*net_interface))); - if (length <= 0) { - goto error; - } - - ifa = (struct ifaddrs*)calloc(1, sizeof(*ifa)); - if (!ifa) { - goto error; - } - - ifa->ifa_flags = net_interface->ifi_flags; - attribute = IFLA_RTA(net_interface); - while (RTA_OK(attribute, length)) { - switch (attribute->rta_type) { - case IFLA_IFNAME: - ifa->ifa_name = strdup((const char*)(RTA_DATA(attribute))); - if (!ifa->ifa_name) { - goto error; - } - LOG_DEBUG(" interface name (payload length: %zu; string length: %zu)\n", RTA_PAYLOAD(attribute), strlen(ifa->ifa_name)); - LOG_DEBUG(" %s\n", ifa->ifa_name); - break; - - case IFLA_BROADCAST: - LOG_DEBUG(" interface broadcast (%zu bytes)\n", RTA_PAYLOAD(attribute)); - if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { - goto error; - } - ifa->ifa_broadaddr = (struct sockaddr*)sa; - break; - - case IFLA_ADDRESS: - LOG_DEBUG(" interface address (%zu bytes)\n", RTA_PAYLOAD(attribute)); - if (fill_ll_address(&sa, net_interface, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { - goto error; - } - ifa->ifa_addr = (struct sockaddr*)sa; - break; - - default: - break; - } - - attribute = RTA_NEXT (attribute, length); - } - - LOG_DEBUG("link flags: 0x%X", ifa->ifa_flags); - return ifa; - - error: - if (sa) - free(sa); - free_single_ifaddrs(&ifa); - - return NULL; -} - -static struct ifaddrs *find_interface_by_index(int index, struct ifaddrs **ifaddrs_head) -{ - struct ifaddrs *cur; - if (!ifaddrs_head || !*ifaddrs_head) - return NULL; - - /* Normally expensive, but with the small amount of links in the chain we'll deal with it's not - * worth the extra housekeeping and memory overhead - */ - cur = *ifaddrs_head; - while (cur) { - if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_PACKET && ((struct sockaddr_ll_extended*)cur->ifa_addr)->sll_ifindex == index) - return cur; - if (cur == cur->ifa_next) - break; - cur = cur->ifa_next; - } - - return NULL; -} - -static char *get_interface_name_by_index(int index, struct ifaddrs **ifaddrs_head) -{ - struct ifaddrs *iface = find_interface_by_index(index, ifaddrs_head); - if (!iface || !iface->ifa_name) - return NULL; - - return iface->ifa_name; -} - -static int get_interface_flags_by_index(int index, struct ifaddrs **ifaddrs_head) -{ - struct ifaddrs *iface = find_interface_by_index(index, ifaddrs_head); - if (!iface) - return 0; - - return (int)(iface->ifa_flags); -} - -static int calculate_address_netmask(struct ifaddrs *ifa, struct ifaddrmsg *net_address) -{ - if (ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_UNSPEC && ifa->ifa_addr->sa_family != AF_PACKET) { - uint32_t prefix_length = 0; - uint32_t data_length = 0; - unsigned char *netmask_data = NULL; - - switch (ifa->ifa_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *sa = (struct sockaddr_in*)calloc(1, sizeof(struct sockaddr_in)); - if (!sa) - return -1; - - ifa->ifa_netmask = (struct sockaddr*)sa; - prefix_length = net_address->ifa_prefixlen; - if (prefix_length > 32) - prefix_length = 32; - data_length = sizeof(sa->sin_addr); - netmask_data = (unsigned char*)&sa->sin_addr; - break; - } - - case AF_INET6: { - struct sockaddr_in6 *sa = (struct sockaddr_in6*)calloc(1, sizeof(struct sockaddr_in6)); - if (!sa) - return -1; - - ifa->ifa_netmask = (struct sockaddr*)sa; - prefix_length = net_address->ifa_prefixlen; - if (prefix_length > 128) - prefix_length = 128; - data_length = sizeof(sa->sin6_addr); - netmask_data = (unsigned char*)&sa->sin6_addr; - break; - } - } - - if (ifa->ifa_netmask && netmask_data) { - /* Fill the first X bytes with 255 */ - uint32_t prefix_bytes = prefix_length / 8; - uint32_t postfix_bytes; - - if (prefix_bytes > data_length) { - errno = EINVAL; - return -1; - } - postfix_bytes = data_length - prefix_bytes; - memset(netmask_data, 0xFF, prefix_bytes); - if (postfix_bytes > 0) - memset(netmask_data + prefix_bytes + 1, 0x00, postfix_bytes); - LOG_DEBUG(" calculating netmask, prefix length is %u bits (%u bytes), data length is %u bytes\n", prefix_length, prefix_bytes, data_length); - if (prefix_bytes + 2 < data_length) - /* Set the rest of the mask bits in the byte following the last 0xFF value */ - netmask_data[prefix_bytes + 1] = (unsigned char)(0xff << (8 - (prefix_length % 8))); - } - } - - return 0; -} - - -static struct ifaddrs *get_link_address(struct nlmsghdr *message, struct ifaddrs **ifaddrs_head) -{ - ssize_t length = 0; - struct rtattr *attribute; - struct ifaddrmsg *net_address; - struct ifaddrs *ifa = NULL; - struct sockaddr **sa; - size_t payload_size; - - abort_if_invalid_pointer_argument(message); - net_address = (struct ifaddrmsg*)(NLMSG_DATA(message)); - length = (ssize_t)(IFA_PAYLOAD(message)); - LOG_DEBUG(" address data length: %zd", length); - if (length <= 0) { - goto error; - } - - ifa = (struct ifaddrs*)(calloc(1, sizeof(*ifa))); - if (!ifa) { - goto error; - } - - // values < 0 are never returned, the cast is safe - ifa->ifa_flags = (unsigned int)(get_interface_flags_by_index((int)(net_address->ifa_index), ifaddrs_head)); - - attribute = IFA_RTA(net_address); - LOG_DEBUG(" reading attributes"); - while (RTA_OK(attribute, length)) { - payload_size = RTA_PAYLOAD(attribute); - LOG_DEBUG(" attribute payload_size == %zu\n", payload_size); - sa = NULL; - - switch (attribute->rta_type) { - case IFA_LABEL: { - size_t room_for_trailing_null = 0; - - LOG_DEBUG(" attribute type: LABEL"); - if (payload_size > MAX_IFA_LABEL_SIZE) { - payload_size = MAX_IFA_LABEL_SIZE; - room_for_trailing_null = 1; - } - - if (payload_size > 0) { - size_t alloc_size; - if (!multiply_s(payload_size, room_for_trailing_null, &alloc_size)) { - goto error; - } - - ifa->ifa_name = (char*)malloc(alloc_size); - if (!ifa->ifa_name) { - goto error; - } - - memcpy(ifa->ifa_name, RTA_DATA (attribute), payload_size); - if (room_for_trailing_null) - ifa->ifa_name[payload_size] = '\0'; - } - break; - } - - case IFA_LOCAL: - LOG_DEBUG(" attribute type: LOCAL"); - if (ifa->ifa_addr) { - /* P2P protocol, set the dst/broadcast address union from the original address. - * Since ifa_addr is set it means IFA_ADDRESS occurred earlier and that address - * is indeed the P2P destination one. - */ - ifa->ifa_dstaddr = ifa->ifa_addr; - ifa->ifa_addr = 0; - } - sa = &ifa->ifa_addr; - break; - - case IFA_BROADCAST: - LOG_DEBUG(" attribute type: BROADCAST"); - if (ifa->ifa_dstaddr) { - /* IFA_LOCAL happened earlier, undo its effect here */ - free(ifa->ifa_dstaddr); - ifa->ifa_dstaddr = NULL; - } - sa = &ifa->ifa_broadaddr; - break; - - case IFA_ADDRESS: - LOG_DEBUG(" attribute type: ADDRESS"); - if (ifa->ifa_addr) { - /* Apparently IFA_LOCAL occurred earlier and we have a P2P connection - * here. IFA_LOCAL carries the destination address, move it there - */ - ifa->ifa_dstaddr = ifa->ifa_addr; - ifa->ifa_addr = NULL; - } - sa = &ifa->ifa_addr; - break; - - case IFA_UNSPEC: - LOG_DEBUG(" attribute type: UNSPEC"); - break; - - case IFA_ANYCAST: - LOG_DEBUG(" attribute type: ANYCAST"); - break; - - case IFA_CACHEINFO: - LOG_DEBUG(" attribute type: CACHEINFO"); - break; - - case IFA_MULTICAST: - LOG_DEBUG(" attribute type: MULTICAST"); - break; - - default: - LOG_DEBUG(" attribute type: %u", attribute->rta_type); - break; - } - - if (sa) { - if (fill_sa_address(sa, net_address, RTA_DATA(attribute), RTA_PAYLOAD(attribute)) < 0) { - goto error; - } - } - - attribute = RTA_NEXT(attribute, length); - } - - /* glibc stores the associated interface name in the address if IFA_LABEL never occurred */ - if (!ifa->ifa_name) { - char *name = get_interface_name_by_index((int)(net_address->ifa_index), ifaddrs_head); - LOG_DEBUG(" address has no name/label, getting one from interface\n"); - ifa->ifa_name = name ? strdup(name) : NULL; - } - LOG_DEBUG(" address label: %s\n", ifa->ifa_name); - - if (calculate_address_netmask(ifa, net_address) < 0) { - goto error; - } - - return ifa; - - error: - { - /* errno may be modified by free, or any other call inside the free_single_xamarin_ifaddrs - * function. We don't care about errors in there since it is more important to know how we - * failed to obtain the link address and not that we went OOM. Save and restore the value - * after the resources are freed. - */ - int errno_save = errno; - free_single_ifaddrs (&ifa); - errno = errno_save; - return NULL; - } - -} - -static int parse_netlink_reply(struct netlink_session *session, struct ifaddrs **ifaddrs_head, struct ifaddrs **last_ifaddr) -{ - struct msghdr netlink_reply; - struct iovec reply_vector; - struct nlmsghdr *current_message; - struct ifaddrs *addr; - int ret = -1; - unsigned char *response = NULL; - - abort_if_invalid_pointer_argument(session); - abort_if_invalid_pointer_argument(ifaddrs_head); - abort_if_invalid_pointer_argument(last_ifaddr); - - size_t buf_size = (size_t)(getpagesize()); - LOG_DEBUG("receive buffer size == %zu", buf_size); - - size_t alloc_size; - if (!multiply_s(sizeof(*response), buf_size, &alloc_size)) { - goto cleanup; - } - - response = (unsigned char*)malloc(alloc_size); - ssize_t length = 0; - if (!response) { - goto cleanup; - } - - while (1) { - memset(response, 0, buf_size); - memset(&reply_vector, 0, sizeof(reply_vector)); - reply_vector.iov_len = buf_size; - reply_vector.iov_base = response; - - memset(&netlink_reply, 0, sizeof(netlink_reply)); - netlink_reply.msg_namelen = sizeof(&session->them); - netlink_reply.msg_name = &session->them; - netlink_reply.msg_iovlen = 1; - netlink_reply.msg_iov = &reply_vector; - - length = recvmsg(session->sock_fd, &netlink_reply, 0); - LOG_DEBUG(" length == %d\n", (int)length); - - if (length < 0) { - LOG_DEBUG("Failed to receive reply from netlink. %s\n", strerror(errno)); - goto cleanup; - } - -#ifdef DEBUG - LOG_DEBUG("response flags:"); - if (netlink_reply.msg_flags == 0) - LOG_DEBUG(" [NONE]"); - else { - if (netlink_reply.msg_flags & MSG_EOR) - LOG_DEBUG(" MSG_EOR"); - if (netlink_reply.msg_flags & MSG_TRUNC) - LOG_DEBUG(" MSG_TRUNC"); - if (netlink_reply.msg_flags & MSG_CTRUNC) - LOG_DEBUG(" MSG_CTRUNC"); - if (netlink_reply.msg_flags & MSG_OOB) - LOG_DEBUG(" MSG_OOB"); - if (netlink_reply.msg_flags & MSG_ERRQUEUE) - LOG_DEBUG(" MSG_ERRQUEUE"); - } -#endif - - if (length == 0) - break; - - for (current_message = (struct nlmsghdr*)response; current_message && NLMSG_OK(current_message, (size_t)length); current_message = NLMSG_NEXT(current_message, length)) { - LOG_DEBUG("next message... (type: %u)\n", current_message->nlmsg_type); - switch (current_message->nlmsg_type) { - /* See rtnetlink.h */ - case RTM_NEWLINK: - LOG_DEBUG(" dumping link...\n"); - addr = get_link_info(current_message); - if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0) { - ret = -1; - goto cleanup; - } - LOG_DEBUG(" done\n"); - break; - - case RTM_NEWADDR: - LOG_DEBUG(" got an address\n"); - addr = get_link_address(current_message, ifaddrs_head); - if (!addr || append_ifaddr(addr, ifaddrs_head, last_ifaddr) < 0) { - ret = -1; - goto cleanup; - } - break; - - case NLMSG_DONE: - LOG_DEBUG(" message done\n"); - ret = 0; - goto cleanup; - - default: - LOG_DEBUG(" message type: %u", current_message->nlmsg_type); - break; - } - } - } - - cleanup: - if (response) - free(response); - return ret; -} - -int _netlink_getifaddrs(struct ifaddrs **ifap) -{ - int ret = -1; - - *ifap = NULL; - struct ifaddrs *ifaddrs_head = 0; - struct ifaddrs *last_ifaddr = 0; - struct netlink_session session; - - if (open_netlink_session(&session) < 0) { - goto cleanup; - } - - /* Request information about the specified link. In our case it will be all of them since we - request the root of the link tree below - */ - if ((send_netlink_dump_request(&session, RTM_GETLINK) < 0) || - (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0) || - (send_netlink_dump_request(&session, RTM_GETADDR) < 0) || - (parse_netlink_reply(&session, &ifaddrs_head, &last_ifaddr) < 0)) { - _netlink_freeifaddrs (ifaddrs_head); - goto cleanup; - } - - ret = 0; - *ifap = ifaddrs_head; - -cleanup: - if (session.sock_fd >= 0) { - close(session.sock_fd); - session.sock_fd = -1; - } - - return ret; -} - -void _netlink_freeifaddrs(struct ifaddrs *ifa) -{ - struct ifaddrs *cur, *next; - - if (!ifa) - return; - - cur = ifa; - while (cur) { - next = cur->ifa_next; - free_single_ifaddrs (&cur); - cur = next; - } -} diff --git a/src/native/libs/System.Native/pal_ifaddrs.h b/src/native/libs/System.Native/pal_ifaddrs.h deleted file mode 100644 index 0944eeb43eb9f1..00000000000000 --- a/src/native/libs/System.Native/pal_ifaddrs.h +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#pragma once - -#ifndef TARGET_ANDROID -#error The pal_ifaddrs.h shim is intended only for Android -#endif - -#if __ANDROID_API__ >= 24 -#error The pal_ifaddrs.h shim is only necessary for Android API 21-23 and it should be removed now that the minimum supported API level is 24 or higher -#endif - -// Android doesn't include the getifaddrs and freeifaddrs functions in older Bionic libc (pre API 24). -// This shim is a port of Xamarin Android's implementation of getifaddrs using Netlink. -// https://github.com/xamarin/xamarin-android/blob/681887ebdbd192ce7ce1cd02221d4939599ba762/src/monodroid/jni/xamarin_getifaddrs.h - -#include - -int _netlink_getifaddrs (struct ifaddrs **ifap); -void _netlink_freeifaddrs (struct ifaddrs *ifap); diff --git a/src/native/libs/System.Native/pal_interfaceaddresses.c b/src/native/libs/System.Native/pal_interfaceaddresses.c index e93eeb07b97731..ed0be3a880b1d1 100644 --- a/src/native/libs/System.Native/pal_interfaceaddresses.c +++ b/src/native/libs/System.Native/pal_interfaceaddresses.c @@ -11,18 +11,9 @@ #include #include #include -#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND) +#if HAVE_GETIFADDRS #include #endif -#ifdef ANDROID_GETIFADDRS_WORKAROUND -#if HAVE_DLFCN_H -#include -#endif -#if HAVE_PTHREAD_H -#include -#endif -#include "pal_ifaddrs.h" // fallback for Android API 21-23 -#endif #if HAVE_NET_IF_H #include #endif @@ -118,56 +109,12 @@ static inline uint8_t mask2prefix(uint8_t* mask, int length) } #endif /* TARGET_WASI */ -#ifdef ANDROID_GETIFADDRS_WORKAROUND -// This workaround is necessary as long as we support Android API 21-23 and it can be removed once -// we drop support for these old Android versions. -static int (*getifaddrs)(struct ifaddrs**) = NULL; -static void (*freeifaddrs)(struct ifaddrs*) = NULL; - -static void try_loading_getifaddrs(void) -{ - if (android_get_device_api_level() >= 24) - { - // Bionic on API 24+ contains the getifaddrs/freeifaddrs functions but the NDK doesn't expose those functions - // in ifaddrs.h when the minimum supported SDK is lower than 24 and therefore we need to load them manually - void *libc = dlopen("libc.so", RTLD_NOW); - if (libc) - { - getifaddrs = (int (*)(struct ifaddrs**)) dlsym(libc, "getifaddrs"); - freeifaddrs = (void (*)(struct ifaddrs*)) dlsym(libc, "freeifaddrs"); - } - } - else - { - // Bionic on API 21-23 doesn't contain the implementation of getifaddrs/freeifaddrs at all - // and we need to reimplement it using netlink (see pal_ifaddrs) - getifaddrs = _netlink_getifaddrs; - freeifaddrs = _netlink_freeifaddrs; - } -} - -static bool ensure_getifaddrs_is_loaded(void) -{ - static pthread_once_t getifaddrs_is_loaded = PTHREAD_ONCE_INIT; - pthread_once(&getifaddrs_is_loaded, try_loading_getifaddrs); - return getifaddrs != NULL && freeifaddrs != NULL; -} -#endif - int32_t SystemNative_EnumerateInterfaceAddresses(void* context, IPv4AddressFound onIpv4Found, IPv6AddressFound onIpv6Found, LinkLayerAddressFound onLinkLayerFound) { -#ifdef ANDROID_GETIFADDRS_WORKAROUND - if (!ensure_getifaddrs_is_loaded()) - { - errno = ENOTSUP; - return -1; - } -#endif - -#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND) +#if HAVE_GETIFADDRS struct ifaddrs* headAddr; if (getifaddrs(&headAddr) == -1) { @@ -316,15 +263,7 @@ c_static_assert(sizeof(NetworkInterfaceInfo) >= sizeof(IpAddressInfo)); int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList ) { -#ifdef ANDROID_GETIFADDRS_WORKAROUND - if (!ensure_getifaddrs_is_loaded()) - { - errno = ENOTSUP; - return -1; - } -#endif - -#if HAVE_GETIFADDRS || defined(ANDROID_GETIFADDRS_WORKAROUND) +#if HAVE_GETIFADDRS struct ifaddrs* head; // Pointer to block allocated by getifaddrs(). struct ifaddrs* ifaddrsEntry; IpAddressInfo *ai; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index ecb0c1f8ec43f9..81f11ab25c2ea7 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -83,15 +83,6 @@ jmethodID g_SSLParametersGetProtocols; jmethodID g_SSLParametersSetApplicationProtocols; jmethodID g_SSLParametersSetServerNames; -// com/android/org/conscrypt/OpenSSLEngineImpl -jclass g_ConscryptOpenSSLEngineImplClass; -jfieldID g_ConscryptOpenSSLEngineImplSslParametersField; -jfieldID g_ConscryptOpenSSLEngineImplHandshakeSessionField; - -// com/android/org/conscrypt/SSLParametersImpl -jclass g_ConscryptSSLParametersImplClass; -jmethodID g_ConscryptSSLParametersImplSetUseSni; - // javax/net/ssl/SSLContext jclass g_sslCtxClass; jmethodID g_sslCtxGetDefaultMethod; @@ -199,14 +190,14 @@ jmethodID g_CertPathBuilderBuild; jclass g_CertPathValidatorClass; jmethodID g_CertPathValidatorGetInstance; jmethodID g_CertPathValidatorValidate; -jmethodID g_CertPathValidatorGetRevocationChecker; // only in API level 24+ +jmethodID g_CertPathValidatorGetRevocationChecker; // java/security/cert/CertPathValidatorException jclass g_CertPathValidatorExceptionClass; jmethodID g_CertPathValidatorExceptionGetIndex; -jmethodID g_CertPathValidatorExceptionGetReason; // only in API level 24+ +jmethodID g_CertPathValidatorExceptionGetReason; -// java/security/cert/CertPathValidatorException$BasicReason - only in API level 24+ +// java/security/cert/CertPathValidatorException$BasicReason jclass g_CertPathExceptionBasicReasonClass; // java/security/cert/CertStore @@ -231,14 +222,14 @@ jclass g_PKIXCertPathBuilderResultClass; jmethodID g_PKIXCertPathBuilderResultGetCertPath; jmethodID g_PKIXCertPathBuilderResultGetTrustAnchor; -// java/security/cert/PKIXReason - only in API level 24+ +// java/security/cert/PKIXReason jclass g_PKIXReasonClass; -// java/security/cert/PKIXRevocationChecker - only in API level 24+ +// java/security/cert/PKIXRevocationChecker jclass g_PKIXRevocationCheckerClass; jmethodID g_PKIXRevocationCheckerSetOptions; -// java/security/cert/PKIXRevocationChecker$Option - only in API level 24+ +// java/security/cert/PKIXRevocationChecker$Option jclass g_PKIXRevocationCheckerOptionClass; jfieldID g_PKIXRevocationCheckerOptionOnlyEndEntity; @@ -474,7 +465,6 @@ jmethodID g_SSLSessionGetProtocol; jclass g_SSLEngineResult; jmethodID g_SSLEngineResultGetStatus; jmethodID g_SSLEngineResultGetHandshakeStatus; -bool g_SSLEngineResultStatusLegacyOrder; jmethodID g_SSLEngineResultBytesConsumed; // javax/crypto/KeyAgreement @@ -778,16 +768,6 @@ jint AndroidCryptoNative_InitLibraryOnLoad (JavaVM *vm, void *reserved) g_SSLParametersGetProtocols = GetMethod(env, false, g_SSLParametersClass, "getProtocols", "()[Ljava/lang/String;"); g_SSLParametersSetApplicationProtocols = GetOptionalMethod(env, false, g_SSLParametersClass, "setApplicationProtocols", "([Ljava/lang/String;)V"); - g_ConscryptOpenSSLEngineImplClass = GetOptionalClassGRef(env, "com/android/org/conscrypt/OpenSSLEngineImpl"); - if (g_ConscryptOpenSSLEngineImplClass != NULL) - { - g_ConscryptOpenSSLEngineImplSslParametersField = GetField(env, false, g_ConscryptOpenSSLEngineImplClass, "sslParameters", "Lcom/android/org/conscrypt/SSLParametersImpl;"); - g_ConscryptOpenSSLEngineImplHandshakeSessionField = GetOptionalField(env, false, g_ConscryptOpenSSLEngineImplClass, "handshakeSession", "Lcom/android/org/conscrypt/OpenSSLSessionImpl;"); - - g_ConscryptSSLParametersImplClass = GetClassGRef(env, "com/android/org/conscrypt/SSLParametersImpl"); - g_ConscryptSSLParametersImplSetUseSni = GetMethod(env, false, g_ConscryptSSLParametersImplClass, "setUseSni", "(Z)V"); - } - g_sslCtxClass = GetClassGRef(env, "javax/net/ssl/SSLContext"); g_sslCtxGetDefaultMethod = GetMethod(env, true, g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;"); g_sslCtxGetDefaultSslParamsMethod = GetMethod(env, false, g_sslCtxClass, "getDefaultSSLParameters", "()Ljavax/net/ssl/SSLParameters;"); @@ -810,13 +790,13 @@ jint AndroidCryptoNative_InitLibraryOnLoad (JavaVM *vm, void *reserved) g_CertPathValidatorClass = GetClassGRef(env, "java/security/cert/CertPathValidator"); g_CertPathValidatorGetInstance = GetMethod(env, true, g_CertPathValidatorClass, "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertPathValidator;"); g_CertPathValidatorValidate = GetMethod(env, false, g_CertPathValidatorClass, "validate", "(Ljava/security/cert/CertPath;Ljava/security/cert/CertPathParameters;)Ljava/security/cert/CertPathValidatorResult;"); - g_CertPathValidatorGetRevocationChecker = GetOptionalMethod(env, false, g_CertPathValidatorClass, "getRevocationChecker", "()Ljava/security/cert/CertPathChecker;"); + g_CertPathValidatorGetRevocationChecker = GetMethod(env, false, g_CertPathValidatorClass, "getRevocationChecker", "()Ljava/security/cert/CertPathChecker;"); g_CertPathValidatorExceptionClass = GetClassGRef(env, "java/security/cert/CertPathValidatorException"); g_CertPathValidatorExceptionGetIndex = GetMethod(env, false, g_CertPathValidatorExceptionClass, "getIndex", "()I"); - g_CertPathValidatorExceptionGetReason = GetOptionalMethod(env, false, g_CertPathValidatorExceptionClass, "getReason", "()Ljava/security/cert/CertPathValidatorException$Reason;"); + g_CertPathValidatorExceptionGetReason = GetMethod(env, false, g_CertPathValidatorExceptionClass, "getReason", "()Ljava/security/cert/CertPathValidatorException$Reason;"); - g_CertPathExceptionBasicReasonClass = GetOptionalClassGRef(env, "java/security/cert/CertPathValidatorException$BasicReason"); + g_CertPathExceptionBasicReasonClass = GetClassGRef(env, "java/security/cert/CertPathValidatorException$BasicReason"); g_CertStoreClass = GetClassGRef(env, "java/security/cert/CertStore"); g_CertStoreGetInstance = GetMethod(env, true, g_CertStoreClass, "getInstance", "(Ljava/lang/String;Ljava/security/cert/CertStoreParameters;)Ljava/security/cert/CertStore;"); @@ -836,16 +816,12 @@ jint AndroidCryptoNative_InitLibraryOnLoad (JavaVM *vm, void *reserved) g_PKIXCertPathBuilderResultGetCertPath = GetMethod(env, false, g_PKIXCertPathBuilderResultClass, "getCertPath", "()Ljava/security/cert/CertPath;"); g_PKIXCertPathBuilderResultGetTrustAnchor = GetMethod(env, false, g_PKIXCertPathBuilderResultClass, "getTrustAnchor", "()Ljava/security/cert/TrustAnchor;"); - g_PKIXReasonClass = GetOptionalClassGRef(env, "java/security/cert/PKIXReason"); + g_PKIXReasonClass = GetClassGRef(env, "java/security/cert/PKIXReason"); + g_PKIXRevocationCheckerClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker"); + g_PKIXRevocationCheckerSetOptions = GetMethod(env, false, g_PKIXRevocationCheckerClass, "setOptions", "(Ljava/util/Set;)V"); - if (g_CertPathValidatorGetRevocationChecker != NULL) - { - g_PKIXRevocationCheckerClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker"); - g_PKIXRevocationCheckerSetOptions = GetMethod(env, false, g_PKIXRevocationCheckerClass, "setOptions", "(Ljava/util/Set;)V"); - - g_PKIXRevocationCheckerOptionClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker$Option"); - g_PKIXRevocationCheckerOptionOnlyEndEntity = GetField(env, true, g_PKIXRevocationCheckerOptionClass, "ONLY_END_ENTITY", "Ljava/security/cert/PKIXRevocationChecker$Option;"); - } + g_PKIXRevocationCheckerOptionClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker$Option"); + g_PKIXRevocationCheckerOptionOnlyEndEntity = GetField(env, true, g_PKIXRevocationCheckerOptionClass, "ONLY_END_ENTITY", "Ljava/security/cert/PKIXRevocationChecker$Option;"); g_TrustAnchorClass = GetClassGRef(env, "java/security/cert/TrustAnchor"); g_TrustAnchorCtor = GetMethod(env, false, g_TrustAnchorClass, "", "(Ljava/security/cert/X509Certificate;[B)V"); @@ -1040,19 +1016,15 @@ jint AndroidCryptoNative_InitLibraryOnLoad (JavaVM *vm, void *reserved) g_KeyManagerFactoryInit = GetMethod(env, false, g_KeyManagerFactory, "init", "(Ljava/security/KeyStore;[C)V"); g_KeyManagerFactoryGetKeyManagers = GetMethod(env, false, g_KeyManagerFactory, "getKeyManagers", "()[Ljavax/net/ssl/KeyManager;"); - // Supported on API Level 24 and above - g_SNIHostName = GetOptionalClassGRef(env, "javax/net/ssl/SNIHostName"); - if (g_SNIHostName != NULL) - { - g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); - g_SSLParametersSetServerNames = GetOptionalMethod(env, false, g_SSLParametersClass, "setServerNames", "(Ljava/util/List;)V"); - } + g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); + g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); + g_SSLParametersSetServerNames = GetMethod(env, false, g_SSLParametersClass, "setServerNames", "(Ljava/util/List;)V"); g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); g_SSLEngineGetApplicationProtocol = GetOptionalMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); - g_SSLEngineGetHandshakeSession = GetOptionalMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); + g_SSLEngineGetHandshakeSession = GetMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetSSLParameters = GetMethod(env, false, g_SSLEngine, "getSSLParameters", "()Ljavax/net/ssl/SSLParameters;"); @@ -1093,7 +1065,6 @@ jint AndroidCryptoNative_InitLibraryOnLoad (JavaVM *vm, void *reserved) g_SSLEngineResultGetStatus = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); g_SSLEngineResultGetHandshakeStatus = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineResultBytesConsumed = GetMethod(env, false, g_SSLEngineResult, "bytesConsumed", "()I"); - g_SSLEngineResultStatusLegacyOrder = android_get_device_api_level() < 24; g_KeyAgreementClass = GetClassGRef(env, "javax/crypto/KeyAgreement"); g_KeyAgreementGetInstance = GetMethod(env, true, g_KeyAgreementClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/KeyAgreement;"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index 2828a68dc03c03..9285c123525a27 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -95,15 +95,6 @@ extern jmethodID g_SSLParametersGetProtocols; extern jmethodID g_SSLParametersSetApplicationProtocols; extern jmethodID g_SSLParametersSetServerNames; -// com/android/org/conscrypt/OpenSSLEngineImpl -extern jclass g_ConscryptOpenSSLEngineImplClass; -extern jfieldID g_ConscryptOpenSSLEngineImplSslParametersField; -extern jfieldID g_ConscryptOpenSSLEngineImplHandshakeSessionField; - -// com/android/org/conscrypt/SSLParametersImpl -extern jclass g_ConscryptSSLParametersImplClass; -extern jmethodID g_ConscryptSSLParametersImplSetUseSni; - // javax/net/ssl/SSLContext extern jclass g_sslCtxClass; extern jmethodID g_sslCtxGetDefaultMethod; @@ -150,14 +141,14 @@ extern jmethodID g_CertPathBuilderBuild; extern jclass g_CertPathValidatorClass; extern jmethodID g_CertPathValidatorGetInstance; extern jmethodID g_CertPathValidatorValidate; -extern jmethodID g_CertPathValidatorGetRevocationChecker; // only in API level 24+ +extern jmethodID g_CertPathValidatorGetRevocationChecker; // java/security/cert/CertPathValidatorException extern jclass g_CertPathValidatorExceptionClass; extern jmethodID g_CertPathValidatorExceptionGetIndex; extern jmethodID g_CertPathValidatorExceptionGetReason; -// java/security/cert/CertPathValidatorException$BasicReason - only in API level 24+ +// java/security/cert/CertPathValidatorException$BasicReason extern jclass g_CertPathExceptionBasicReasonClass; // java/security/cert/CertStore @@ -182,14 +173,14 @@ extern jclass g_PKIXCertPathBuilderResultClass; extern jmethodID g_PKIXCertPathBuilderResultGetCertPath; extern jmethodID g_PKIXCertPathBuilderResultGetTrustAnchor; -// java/security/cert/PKIXReason - only in API level 24+ +// java/security/cert/PKIXReason extern jclass g_PKIXReasonClass; -// java/security/cert/PKIXRevocationChecker - only in API level 24+ +// java/security/cert/PKIXRevocationChecker extern jclass g_PKIXRevocationCheckerClass; extern jmethodID g_PKIXRevocationCheckerSetOptions; -// java/security/cert/PKIXRevocationChecker$Option - only in API level 24+ +// java/security/cert/PKIXRevocationChecker$Option extern jclass g_PKIXRevocationCheckerOptionClass; extern jfieldID g_PKIXRevocationCheckerOptionOnlyEndEntity; @@ -488,7 +479,6 @@ extern jmethodID g_SSLSessionGetProtocol; extern jclass g_SSLEngineResult; extern jmethodID g_SSLEngineResultGetStatus; extern jmethodID g_SSLEngineResultGetHandshakeStatus; -extern bool g_SSLEngineResultStatusLegacyOrder; extern jmethodID g_SSLEngineResultBytesConsumed; // javax/crypto/KeyAgreement diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index a7fda9f371fbd5..c45e12b1319d6e 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -16,7 +16,6 @@ enum }; // javax/net/ssl/SSLEngineResult$Status -// Android API 24+ enum { STATUS__BUFFER_UNDERFLOW = 0, @@ -25,16 +24,6 @@ enum STATUS__CLOSED = 3, }; -// javax/net/ssl/SSLEngineResult$Status -// Android API 21-23 -enum -{ - LEGACY__STATUS__BUFFER_OVERFLOW = 0, - LEGACY__STATUS__BUFFER_UNDERFLOW = 1, - LEGACY__STATUS__OK = 3, - LEGACY__STATUS__CLOSED = 2, -}; - struct ApplicationProtocolData_t { uint8_t* data; @@ -64,39 +53,13 @@ static bool IsHandshaking(int handshakeStatus) ARGS_NON_NULL(1, 2) static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) { - // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates - // which we need for hostname verification. There are different ways to access the handshake session - // in different Android API levels. - // SSLEngine.getHandshakeSession() is available since API 24. - // In older Android versions (API 21-23) we need to access the handshake session by accessing - // a private field instead. - - if (g_SSLEngineGetHandshakeSession != NULL) - { - jobject sslSession = IsHandshaking(handshakeStatus) - ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) - : (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); - if (CheckJNIExceptions(env)) - return NULL; - - return sslSession; - } - else if (g_ConscryptOpenSSLEngineImplHandshakeSessionField != NULL) - { - jobject sslSession = IsHandshaking(handshakeStatus) - ? (*env)->GetObjectField(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplHandshakeSessionField) - : (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); - if (CheckJNIExceptions(env)) - return NULL; - - return sslSession; - } - else - { - LOG_ERROR("Unable to get the current SSLSession from SSLEngine."); - assert(false && "Unable to get the current SSLSession from SSLEngine."); + jobject sslSession = IsHandshaking(handshakeStatus) + ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) + : (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); + if (CheckJNIExceptions(env)) return NULL; - } + + return sslSession; } ARGS_NON_NULL_ALL static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream) @@ -187,27 +150,6 @@ ARGS_NON_NULL_ALL static jobject EnsureRemaining(JNIEnv* env, jobject oldBuffer, } } -// There has been a change in the SSLEngineResult.Status enum between API 23 and 24 that changed -// the order/interger values of the enum options. -static int MapLegacySSLEngineResultStatus(int legacyStatus) -{ - switch (legacyStatus) - { - case LEGACY__STATUS__BUFFER_OVERFLOW: - return STATUS__BUFFER_OVERFLOW; - case LEGACY__STATUS__BUFFER_UNDERFLOW: - return STATUS__BUFFER_UNDERFLOW; - case LEGACY__STATUS__CLOSED: - return STATUS__CLOSED; - case LEGACY__STATUS__OK: - return STATUS__OK; - default: - LOG_ERROR("Unknown legacy SSLEngineResult status: %d", legacyStatus); - assert(false && "Unknown SSLEngineResult status"); - return -1; - } -} - ARGS_NON_NULL_ALL static PAL_SSLStreamStatus WrapAndProcessResult(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus, int* bytesConsumed, bool* repeat) { // SSLEngineResult result = sslEngine.wrap(appOutBuffer, netOutBuffer); @@ -224,11 +166,6 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus WrapAndProcessResult(JNIEnv* env, S int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); (*env)->DeleteLocalRef(env, result); - if (g_SSLEngineResultStatusLegacyOrder) - { - status = MapLegacySSLEngineResultStatus(status); - } - switch (status) { case STATUS__OK: @@ -332,11 +269,6 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); (*env)->DeleteLocalRef(env, result); - if (g_SSLEngineResultStatusLegacyOrder) - { - status = MapLegacySSLEngineResultStatus(status); - } - switch (status) { case STATUS__OK: @@ -717,48 +649,12 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( return ret; } -// This method calls internal Android APIs that are specific to Android API 21-23 and it won't work -// on newer API levels. By calling the sslEngine.sslParameters.useSni(true) method, the SSLEngine -// will include the peerHost that was passed in to the SSLEngine factory method in the client hello -// message. -ARGS_NON_NULL_ALL static int32_t ApplyLegacyAndroidSNIWorkaround(JNIEnv* env, SSLStream* sslStream) -{ - if (g_ConscryptOpenSSLEngineImplClass == NULL || !(*env)->IsInstanceOf(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplClass)) - return FAIL; - - int32_t ret = FAIL; - INIT_LOCALS(loc, sslParameters); - - loc[sslParameters] = (*env)->GetObjectField(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplSslParametersField); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - if (!loc[sslParameters]) - goto cleanup; - - (*env)->CallVoidMethod(env, loc[sslParameters], g_ConscryptSSLParametersImplSetUseSni, true); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - ret = SUCCESS; - -cleanup: - RELEASE_LOCALS(loc, env); - return ret; -} - int32_t AndroidCryptoNative_SSLStreamSetTargetHost(SSLStream* sslStream, char* targetHost) { abort_if_invalid_pointer_argument (sslStream); abort_if_invalid_pointer_argument (targetHost); JNIEnv* env = GetJNIEnv(); - - if (g_SNIHostName == NULL || g_SSLParametersSetServerNames == NULL) - { - // SNIHostName is only available since API 24 - // on APIs 21-23 we use a workaround to force the SSLEngine to use SNI - return ApplyLegacyAndroidSNIWorkaround(env, sslStream); - } - int32_t ret = FAIL; INIT_LOCALS(loc, hostStr, nameList, hostName, params); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_x509chain.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_x509chain.c index f3d99362967c3b..d33bef23de4454 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_x509chain.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_x509chain.c @@ -255,8 +255,7 @@ enum static PAL_X509ChainStatusFlags ChainStatusFromValidatorExceptionReason(JNIEnv* env, jobject reason) { int value = (*env)->CallIntMethod(env, reason, g_EnumOrdinal); - if (g_CertPathExceptionBasicReasonClass != NULL && - (*env)->IsInstanceOf(env, reason, g_CertPathExceptionBasicReasonClass)) + if ((*env)->IsInstanceOf(env, reason, g_CertPathExceptionBasicReasonClass)) { switch (value) { @@ -275,7 +274,7 @@ static PAL_X509ChainStatusFlags ChainStatusFromValidatorExceptionReason(JNIEnv* return PAL_X509ChainPartialChain; } } - else if (g_PKIXReasonClass != NULL && (*env)->IsInstanceOf(env, reason, g_PKIXReasonClass)) + else if ((*env)->IsInstanceOf(env, reason, g_PKIXReasonClass)) { switch (value) { @@ -309,10 +308,10 @@ static void PopulateValidationError(JNIEnv* env, jobject error, bool isRevocatio { index = (*env)->CallIntMethod(env, error, g_CertPathValidatorExceptionGetIndex); - // Get the reason (if the API is available) and convert it to a chain status flag - if (g_CertPathValidatorExceptionGetReason != NULL) + // Get the reason and convert it to a chain status flag. + jobject reason = (*env)->CallObjectMethod(env, error, g_CertPathValidatorExceptionGetReason); + if (!CheckJNIExceptions(env) && reason != NULL) { - jobject reason = (*env)->CallObjectMethod(env, error, g_CertPathValidatorExceptionGetReason); chainStatus = ChainStatusFromValidatorExceptionReason(env, reason); (*env)->DeleteLocalRef(env, reason); } @@ -428,11 +427,6 @@ int32_t AndroidCryptoNative_X509ChainSetCustomTrustStore(X509ChainContext* ctx, return CheckJNIExceptions(env) ? FAIL : SUCCESS; } -static bool X509ChainSupportsRevocationOptions(void) -{ - return g_CertPathValidatorGetRevocationChecker != NULL && g_PKIXRevocationCheckerClass != NULL; -} - static jobject /*CertPath*/ CreateCertPathFromAnchor(JNIEnv* env, jobject /*TrustAnchor*/ trustAnchor) { jobject ret = NULL; @@ -507,24 +501,14 @@ static int32_t ValidateWithRevocation(JNIEnv* env, else { certPathToUse = ctx->certPath; - if (X509ChainSupportsRevocationOptions()) - { - // Only add the ONLY_END_ENTITY if we are not just checking the trust anchor. If ONLY_END_ENTITY is - // specified, revocation checking will skip the trust anchor even if it is the only certificate. - - // HashSet options = new HashSet(3); - // options.add(PKIXRevocationChecker.Option.ONLY_END_ENTITY); - loc[options] = (*env)->NewObject(env, g_HashSetClass, g_HashSetCtorWithCapacity, 3); - jobject endOnly = (*env)->GetStaticObjectField( - env, g_PKIXRevocationCheckerOptionClass, g_PKIXRevocationCheckerOptionOnlyEndEntity); - (*env)->CallBooleanMethod(env, loc[options], g_HashSetAdd, endOnly); - (*env)->DeleteLocalRef(env, endOnly); - } - else - { - LOG_INFO("Treating revocation flag 'EndCertificateOnly' as 'ExcludeRoot'. " - "Revocation will be checked for non-end certificates."); - } + + // Only add the ONLY_END_ENTITY if we are not just checking the trust anchor. If ONLY_END_ENTITY is + // specified, revocation checking will skip the trust anchor even if it is the only certificate. + loc[options] = (*env)->NewObject(env, g_HashSetClass, g_HashSetCtorWithCapacity, 3); + jobject endOnly = (*env)->GetStaticObjectField( + env, g_PKIXRevocationCheckerOptionClass, g_PKIXRevocationCheckerOptionOnlyEndEntity); + (*env)->CallBooleanMethod(env, loc[options], g_HashSetAdd, endOnly); + (*env)->DeleteLocalRef(env, endOnly); } (*env)->DeleteLocalRef(env, certPathList); @@ -536,23 +520,21 @@ static int32_t ValidateWithRevocation(JNIEnv* env, } jobject params = ctx->params; - if (X509ChainSupportsRevocationOptions()) - { - // PKIXRevocationChecker checker = validator.getRevocationChecker(); - loc[checker] = (*env)->CallObjectMethod(env, validator, g_CertPathValidatorGetRevocationChecker); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // Set any specific options - if (loc[options] != NULL) - { - // checker.setOptions(options); - (*env)->CallVoidMethod(env, loc[checker], g_PKIXRevocationCheckerSetOptions, loc[options]); - } + // PKIXRevocationChecker checker = validator.getRevocationChecker(); + loc[checker] = (*env)->CallObjectMethod(env, validator, g_CertPathValidatorGetRevocationChecker); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // params.addCertPathChecker(checker); - (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersAddCertPathChecker, loc[checker]); + // Set any specific options + if (loc[options] != NULL) + { + // checker.setOptions(options); + (*env)->CallVoidMethod(env, loc[checker], g_PKIXRevocationCheckerSetOptions, loc[options]); } + // params.addCertPathChecker(checker); + (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersAddCertPathChecker, loc[checker]); + // params.setRevocationEnabled(true); // PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPathToUse, params); (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersSetRevocationEnabled, true); diff --git a/src/native/minipal/memorybarrierprocesswide.c b/src/native/minipal/memorybarrierprocesswide.c index 6a9704c9920a98..2ab74040c09256 100644 --- a/src/native/minipal/memorybarrierprocesswide.c +++ b/src/native/minipal/memorybarrierprocesswide.c @@ -44,7 +44,7 @@ static bool CanFlushUsingMembarrier(void) { #ifdef TARGET_ANDROID - // Avoid calling membarrier on older Android versions where membarrier + // Avoid calling membarrier on older Android versions (older than API 29) where membarrier // may be barred by seccomp causing the process to be killed. int apiLevel = android_get_device_api_level(); if (apiLevel < __ANDROID_API_Q__) diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index df0a274e9d4895..bb2caee5b25850 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -23,7 +23,7 @@ public enum RuntimeFlavorEnum public partial class ApkBuilder { - private const string DefaultMinApiLevel = "21"; + private const string DefaultMinApiLevel = "24"; private const string DefaultTargetApiLevel = "31"; public string? ProjectName { get; set; } diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs index b3c3ab55ba85da..c83155fdbd2cd6 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -14,7 +14,7 @@ namespace Microsoft.Android.Build { public sealed class AndroidProject { - private const string DefaultMinApiLevel = "21"; + private const string DefaultMinApiLevel = "24"; private const string Cmake = "cmake"; private TaskLoggingHelper logger;