Skip to content

Commit 49595bd

Browse files
author
Luke
authored
Android Libcurl (#99)
1 parent f28d49e commit 49595bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1218
-156
lines changed

.github/workflows/build_android.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
timeout-minutes: 30
66
strategy:
77
matrix:
8-
preset: ["android-armeabi-v7a-host_linux-x64", "android-arm64-v8a-host_linux-x64", "android-x64-host_linux-x64"]
8+
preset: ["android-arm64-v8a-host_osx_x64", "android-x64-host_osx_x64"]
99
runs-on: ubuntu-22.04
1010
steps:
1111
- uses: actions/checkout@v3

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,11 @@ out
44
.idea
55
/test/Engine
66
CMakeUserPresets.json
7-
local.properties
7+
local.properties
8+
9+
# Ignore Gradle project-specific cache directory
10+
android/.gradle
11+
12+
# Ignore Gradle build output directory
13+
android/build
14+
android/app/.cxx

CMakeLists.txt

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
cmake_minimum_required(VERSION 3.23)
1+
# I think we are constrained to the version that the latest android sdk uses, for Android builds.
2+
cmake_minimum_required(VERSION 3.22.1)
23
include(version.cmake)
34

4-
55
if (CMAKE_OSX_SYSROOT STREQUAL "iphonesimulator")
66
# this hack seems to be required for now, otherwise CMake still thinks we are building for arm...
77
set(CMAKE_XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] "arm64")
@@ -50,6 +50,8 @@ else()
5050
set(HTTP_IMPL "libhttpclient")
5151
elseif (WITH_CPPRESTSDK)
5252
set(HTTP_IMPL "cpprestsdk")
53+
elseif (BUILD_CURL_HTTP)
54+
set(HTTP_IMPL "curl")
5355
endif()
5456

5557
if (WITH_LIBHTTPCLIENT_WS)
@@ -63,18 +65,21 @@ endif()
6365

6466
if (DEFINED ANDROID_ABI)
6567
if (DEFINED VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
66-
message(FATAL "Cannot specify an external chainload toolchain if building for Android.")
68+
message(FATAL_ERROR "Cannot specify an external chainload toolchain if building for Android.")
6769
endif()
6870

6971
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake)
7072
endif()
7173

7274
cmake_policy(SET CMP0012 NEW)
7375

74-
# Kickstarts vcpkg dependency manager. It will parse vcpkg.json and install all configured
75-
# dependencies. Some dependencies are optional and enabled via VCPKG_MANIFEST_FEATURES
76-
set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake
77-
CACHE FILEPATH "Vcpkg toolchain file")
76+
## force so that gradle doesn't stomp vcpkg toolchain with the android toolchain
77+
set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake CACHE FILEPATH "Vcpkg toolchain file" FORCE)
78+
79+
if (DEFINED ANDROID_ABI AND NOT DEFINED VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
80+
# CmakePresets.json doesn't allow us to use cmake vars in values, so set it here
81+
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake)
82+
endif()
7883

7984
project(nakama-sdk LANGUAGES CXX)
8085

@@ -205,6 +210,8 @@ endif()
205210

206211
if (HTTP_IMPL STREQUAL "cpprestsdk")
207212
add_subdirectory(impl/httpCppRest EXCLUDE_FROM_ALL)
213+
elseif(HTTP_IMPL STREQUAL "curl")
214+
add_subdirectory(impl/httpCurl EXCLUDE_FROM_ALL)
208215
endif()
209216

210217
add_library(nakama-ws-impl INTERFACE)
@@ -225,6 +232,41 @@ if(HTTP_IMPL STREQUAL "libhttpclient")
225232
add_subdirectory(impl/httpLibHttpClient EXCLUDE_FROM_ALL)
226233
endif()
227234

235+
if (ANDROID AND (HTTP_IMPL STREQUAL "curl" OR WS_IMPL STREQUAL "wslay"))
236+
add_subdirectory(impl/android)
237+
set(CREATE_JAR TRUE) # we need the code for accessing native Android CA store.
238+
239+
if (HTTP_IMPL STREQUAL "curl")
240+
target_link_libraries(nakama-impl-http-libcurl PUBLIC android-ca)
241+
endif()
242+
243+
if (WS_IMPL STREQUAL "wslay")
244+
target_link_libraries(nakama-impl-ws-wslay INTERFACE android-ca)
245+
endif()
246+
247+
list(APPEND NAKAMA_SDK_DEPS $<TARGET_OBJECTS:android-ca>)
248+
else()
249+
set(CREATE_JAR FALSE)
250+
endif()
251+
252+
if (CREATE_JAR)
253+
message("-- Configured to build Java code with Gradle.")
254+
255+
set(LIBNAKAMA_AAR "${CMAKE_SOURCE_DIR}/android/app/build/outputs/aar/libnakama-sdk.aar")
256+
# TODO it's very odd that add_custom_command did not seem to trigger, so we use add_custom_target
257+
add_custom_target(
258+
nakama-aar ALL
259+
COMMAND ./gradlew assemble
260+
BYPRODUCTS ${LIBNAKAMA_AAR}
261+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/android
262+
VERBATIM
263+
USES_TERMINAL
264+
DEPENDS ${CMAKE_SOURCE_DIR}/test/android/src/main/java/com/heroiclabs/nakamatest/MainActivity.java
265+
)
266+
267+
install(FILES ${LIBNAKAMA_AAR} DESTINATION ${CMAKE_INSTALL_LIBDIR})
268+
endif()
269+
228270
find_package(optional-lite CONFIG REQUIRED)
229271
# SDK API
230272
add_subdirectory(interface)
@@ -245,7 +287,6 @@ list(APPEND NAKAMA_SDK_DEPS
245287
$<TARGET_OBJECTS:nakama-sdk-client-factory>
246288
$<TARGET_OBJECTS:nakama-sdk-rtclient-factory>
247289

248-
249290
# Xcode generator doesn't produce library for a target with no "own" files
250291
# https://gitlab.kitware.com/cmake/cmake/-/issues/23688
251292
${DUMMY_CPP}
@@ -258,6 +299,7 @@ if (BUILD_GRPC_CLIENT)
258299
list(APPEND NAKAMA_SDK_DEPS $<TARGET_OBJECTS:nakama-sdk-core-grpc>)
259300
endif()
260301

302+
## TODO this can be massively simplified
261303
if (WITH_LIBHTTPCLIENT_WS)
262304
target_compile_definitions(nakama-sdk-core-rt PUBLIC BUILD_WEBSOCKET_LIBHTTPCLIENT)
263305
target_compile_definitions(nakama-sdk-core-common PUBLIC BUILD_WEBSOCKET_LIBHTTPCLIENT)
@@ -296,7 +338,7 @@ set_target_properties(nakama-sdk PROPERTIES
296338
MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${LIBNAKAMA_VERSION}
297339
VERSION ${LIBNAKAMA_VERSION}
298340
SOVERSION ${LIBNAKAMA_SOVERSION}
299-
)
341+
)
300342

301343
add_library(nakama::sdk ALIAS nakama-sdk)
302344
target_link_libraries(nakama-sdk

CMakePresets.json

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"CMAKE_OSX_ARCHITECTURES": "arm64",
7575
"WITH_LIBHTTPCLIENT_HTTP": "ON",
7676
"BUILD_WSLAY": "ON",
77-
"BUILD_CURL": "ON",
77+
"BUILD_CURL_IO": "ON",
7878
"VCPKG_MANIFEST_FEATURES": "wslay;curl"
7979
},
8080
"condition": {
@@ -125,7 +125,7 @@
125125
"VCPKG_TARGET_TRIPLET": "arm64-ios-heroic",
126126
"CMAKE_SYSTEM_NAME": "iOS",
127127
"BUILD_WSLAY": "ON",
128-
"BUILD_CURL": "ON"
128+
"BUILD_CURL_IO": "ON"
129129
},
130130
"installDir": "out/ios-arm64"
131131
},
@@ -175,34 +175,18 @@
175175
"ANDROID_ARM_NEON": "ON",
176176
"ANDROID_STL": "c++_shared",
177177
"ANDROID_PLATFORM": "android-21",
178+
"BUILD_CURL_HTTP": "ON",
179+
"BUILD_WSLAY": "ON",
180+
"BUILD_CURL_IO": "ON",
178181
"CMAKE_SYSTEM_NAME": "Android",
179-
"VCPKG_MANIFEST_FEATURES": "cpprestsdk",
180-
"WITH_CPPRESTSDK": "ON"
182+
"VCPKG_MANIFEST_FEATURES": "curl;wslay"
181183
},
182184
"condition": {
183185
"type": "equals",
184186
"lhs": "${hostSystemName}",
185187
"rhs": "Darwin"
186188
}
187189
},
188-
{
189-
"name": "android-armeabi-v7a-host_osx_x64",
190-
"inherits": [
191-
"android-default"
192-
],
193-
"generator": "Ninja Multi-Config",
194-
"cacheVariables": {
195-
"VCPKG_HOST_TRIPLET": "x64-osx-heroic",
196-
"VCPKG_TARGET_TRIPLET": "arm-neon-android-heroic",
197-
"ANDROID_ABI": "armeabi-v7a"
198-
},
199-
"condition": {
200-
"type": "equals",
201-
"lhs": "${hostSystemName}",
202-
"rhs": "Darwin"
203-
},
204-
"installDir": "out/android-armeabi-v7a"
205-
},
206190
{
207191
"name": "android-arm64-v8a-host_osx_x64",
208192
"inherits": [
@@ -240,7 +224,7 @@
240224
"installDir": "out/android-x64"
241225
},
242226
{
243-
"name": "android-armeabi-v7a-host_osx_arm64",
227+
"name": "android-arm64-v8a-host_osx_arm64",
244228
"inherits": [
245229
"android-default"
246230
],
@@ -257,24 +241,6 @@
257241
},
258242
"installDir": "out/android-armeabi-v7a"
259243
},
260-
{
261-
"name": "android-arm64-v8a-host_arm64",
262-
"inherits": [
263-
"android-default"
264-
],
265-
"generator": "Ninja Multi-Config",
266-
"cacheVariables": {
267-
"VCPKG_HOST_TRIPLET": "arm64-osx-heroic",
268-
"VCPKG_TARGET_TRIPLET": "arm64-android-heroic",
269-
"ANDROID_ABI": "arm64-v8a"
270-
},
271-
"condition": {
272-
"type": "equals",
273-
"lhs": "${hostSystemName}",
274-
"rhs": "Darwin"
275-
},
276-
"installDir": "out/android-arm64-v8a"
277-
},
278244
{
279245
"name": "android-x64-host_osx_arm64",
280246
"inherits": [
@@ -304,7 +270,7 @@
304270
"VCPKG_TARGET_TRIPLET": "x64-linux-heroic",
305271
"WITH_LIBHTTPCLIENT_HTTP": "ON",
306272
"BUILD_WSLAY": "ON",
307-
"BUILD_CURL": "ON",
273+
"BUILD_CURL_IO": "ON",
308274
"VCPKG_MANIFEST_FEATURES": "curl;wslay"
309275
},
310276
"condition": {

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,18 @@ You can change ping period on server - `ping_period_ms` parameter:
198198

199199
https://heroiclabs.com/docs/install-configuration/#socket
200200

201+
#### Android
202+
203+
To use our native C++ library in your Android application, you will need to include an additional .aar file that we ship for SSL support.
204+
205+
For example, in Gradle:
206+
207+
```
208+
implementation files("<path/to/libnakama-sdk.aar>")
209+
```
210+
211+
Then you will need to load our native library from Java by calling `System.loadLibrary("nakama-sdk")` when your activity is created.
212+
201213
# How to build
202214

203215
## Prerequisite

android/app/build.gradle

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
plugins {
2+
id 'com.android.library' version '7.4.0' apply true
3+
}
4+
5+
repositories {
6+
google()
7+
mavenCentral()
8+
}
9+
10+
android {
11+
compileSdkVersion 32
12+
defaultConfig {
13+
minSdkVersion 21
14+
targetSdkVersion 32
15+
}
16+
17+
libraryVariants.all {
18+
variant ->
19+
variant.outputs.all {
20+
outputFileName = "libnakama-sdk.aar"
21+
}
22+
}
23+
}
24+
25+
dependencies {
26+
implementation 'androidx.appcompat:appcompat:1.5.1'
27+
implementation 'org.bouncycastle:bcpkix-jdk15on:1.66'
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.heroiclabs.nakamasdk"
4+
android:versionCode="1"
5+
android:versionName="1.0">
6+
7+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
8+
<application/>
9+
</manifest>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2023 The Nakama Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.heroiclabs.nakamasdk;
18+
19+
import androidx.annotation.Keep;
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.OutputStreamWriter;
22+
import java.io.Writer;
23+
import java.security.KeyStore;
24+
import java.security.cert.Certificate;
25+
import java.security.cert.CertificateException;
26+
import java.security.cert.X509Certificate;
27+
import java.util.Base64;
28+
import java.util.Enumeration;
29+
30+
public class AndroidCA {
31+
32+
@Keep
33+
public static byte[] getCaCertificates() {
34+
try {
35+
KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
36+
keyStore.load(null, null);
37+
Enumeration<String> aliases = keyStore.aliases();
38+
39+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
40+
Writer writer = new OutputStreamWriter(outputStream);
41+
42+
while (aliases.hasMoreElements()) {
43+
String alias = aliases.nextElement();
44+
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
45+
46+
writer.write("-----BEGIN CERTIFICATE-----\n");
47+
writer.write(Base64.getEncoder().encodeToString(cert.getEncoded()));
48+
writer.write("\n-----END CERTIFICATE-----\n");
49+
}
50+
51+
writer.flush();
52+
return outputStream.toByteArray();
53+
} catch (Exception e) {
54+
e.printStackTrace();
55+
return new byte[0];
56+
}
57+
}
58+
}

android/gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
android.enableJetifier=true
2+
android.useAndroidX=true
57.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)