summaryrefslogtreecommitdiffstats
path: root/platform/android/export/export_plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/export/export_plugin.cpp')
-rw-r--r--platform/android/export/export_plugin.cpp167
1 files changed, 103 insertions, 64 deletions
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index a0da9019c6..e3d9807af7 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -57,6 +57,10 @@
#include "modules/svg/image_loader_svg.h"
#endif
+#ifdef ANDROID_ENABLED
+#include "../os_android.h"
+#endif
+
#include <string.h>
static const char *android_perms[] = {
@@ -290,7 +294,9 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
// Check for devices updates
String adb = get_adb_path();
- if (ea->has_runnable_preset.is_set() && FileAccess::exists(adb)) {
+ // adb.exe was locking the editor_doc_cache file on startup. Adding a check for is_editor_ready provides just enough time
+ // to regenerate the doc cache.
+ if (ea->has_runnable_preset.is_set() && FileAccess::exists(adb) && EditorNode::get_singleton()->is_editor_ready()) {
String devices;
List<String> args;
args.push_back("devices");
@@ -1998,7 +2004,7 @@ String EditorExportPlatformAndroid::get_device_architecture(int p_index) const {
return devices[p_index].architecture;
}
-Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
String can_export_error;
@@ -2020,11 +2026,11 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
}
const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug");
- const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug;
if (use_reverse) {
- p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
+ p_debug_flags.set_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST);
}
String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
@@ -2103,7 +2109,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
OS::get_singleton()->execute(adb, args, &output, &rv, true);
print_verbose(output);
- if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) {
int dbg_port = EDITOR_GET("network/debug/remote_port");
args.clear();
args.push_back("-s");
@@ -2118,7 +2124,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
print_line("Reverse result: " + itos(rv));
}
- if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
int fs_port = EDITOR_GET("filesystem/file_server/port");
args.clear();
@@ -2272,6 +2278,11 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
bool failed = false;
String version_to_use;
+ String java_sdk_path = EDITOR_GET("export/android/java_sdk_path");
+ if (!java_sdk_path.is_empty()) {
+ OS::get_singleton()->set_environment("JAVA_HOME", java_sdk_path);
+ }
+
List<String> args;
args.push_back("--version");
String output;
@@ -2323,7 +2334,8 @@ static bool has_valid_keystore_credentials(String &r_error_str, const String &p_
args.push_back(p_password);
args.push_back("-alias");
args.push_back(p_username);
- Error error = OS::get_singleton()->execute("keytool", args, &output, nullptr, true);
+ String keytool_path = EditorExportPlatformAndroid::get_keytool_path();
+ Error error = OS::get_singleton()->execute(keytool_path, args, &output, nullptr, true);
String keytool_error = "keytool error:";
bool valid = output.substr(0, keytool_error.length()) != keytool_error;
@@ -2369,19 +2381,6 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
#ifdef MODULE_MONO_ENABLED
// Android export is still a work in progress, keep a message as a warning.
err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n";
-
- bool unsupported_arch = false;
- Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
- for (ABI abi : enabled_abis) {
- if (abi.arch != "arm64" && abi.arch != "x86_64") {
- err += vformat(TTR("Android architecture %s not supported in C# projects."), abi.arch) + "\n";
- unsupported_arch = true;
- }
- }
- if (unsupported_arch) {
- r_error = err;
- return false;
- }
#endif
// Look for export templates (first official, and if defined custom templates).
@@ -2416,6 +2415,10 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
err += template_err;
}
} else {
+#ifdef ANDROID_ENABLED
+ err += TTR("Gradle build is not supported for the Android editor.") + "\n";
+ valid = false;
+#else
// Validate the custom gradle android source template.
bool android_source_template_valid = false;
const String android_source_template = p_preset->get("gradle_build/android_source_template");
@@ -2438,6 +2441,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
valid = installed_android_build_template && !r_missing_templates;
+#endif
}
// Validate the rest of the export configuration.
@@ -2474,6 +2478,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
err += TTR("Release keystore incorrectly configured in the export preset.") + "\n";
}
+#ifndef ANDROID_ENABLED
String java_sdk_path = EDITOR_GET("export/android/java_sdk_path");
if (java_sdk_path.is_empty()) {
err += TTR("A valid Java SDK path is required in Editor Settings.") + "\n";
@@ -2546,6 +2551,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
valid = false;
}
}
+#endif
if (!err.is_empty()) {
r_error = err;
@@ -2655,7 +2661,7 @@ Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExpor
return err;
}
-void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) {
+void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags) {
String cmdline = p_preset->get("command_line/extra_args");
Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
for (int i = 0; i < command_line_strings.size(); i++) {
@@ -2665,7 +2671,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
}
}
- gen_export_flags(command_line_strings, p_flags);
+ command_line_strings.append_array(gen_export_flags(p_flags));
bool apk_expansion = p_preset->get("apk_expansion/enable");
if (apk_expansion) {
@@ -2688,7 +2694,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
bool immersive = p_preset->get("screen/immersive_mode");
if (immersive) {
- command_line_strings.push_back("--use_immersive");
+ command_line_strings.push_back("--fullscreen");
}
bool debug_opengl = p_preset->get("graphics/opengl_debug");
@@ -2716,23 +2722,9 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
- String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
- String release_keystore = _get_keystore_path(p_preset, false);
- String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
- String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
- String target_sdk_version = p_preset->get("gradle_build/target_sdk");
- if (!target_sdk_version.is_valid_int()) {
- target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
- }
- String apksigner = get_apksigner_path(target_sdk_version.to_int(), true);
- print_verbose("Starting signing of the " + export_label + " binary using " + apksigner);
- if (apksigner == "<FAILED>") {
- add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("All 'apksigner' tools located in Android SDK 'build-tools' directory failed to execute. Please check that you have the correct version installed for your target sdk version. The resulting %s is unsigned."), export_label));
- return OK;
- }
- if (!FileAccess::exists(apksigner)) {
- add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' could not be found. Please check that the command is available in the Android SDK build-tools directory. The resulting %s is unsigned."), export_label));
- return OK;
+ if (export_format == EXPORT_FORMAT_AAB) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("AAB signing is not supported"));
+ return FAILED;
}
String keystore;
@@ -2749,15 +2741,15 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
user = EDITOR_GET("export/android/debug_keystore_user");
}
- if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) {
+ if (ep.step(TTR("Signing debug APK..."), 104)) {
return ERR_SKIP;
}
} else {
- keystore = release_keystore;
- password = release_password;
- user = release_username;
+ keystore = _get_keystore_path(p_preset, false);
+ password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
+ user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
- if (ep.step(vformat(TTR("Signing release %s..."), export_label), 104)) {
+ if (ep.step(TTR("Signing release APK..."), 104)) {
return ERR_SKIP;
}
}
@@ -2767,6 +2759,36 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
return ERR_FILE_CANT_OPEN;
}
+ String apk_path = export_path;
+ if (apk_path.is_relative_path()) {
+ apk_path = OS::get_singleton()->get_resource_dir().path_join(apk_path);
+ }
+ apk_path = ProjectSettings::get_singleton()->globalize_path(apk_path).simplify_path();
+
+ Error err;
+#ifdef ANDROID_ENABLED
+ err = OS_Android::get_singleton()->sign_apk(apk_path, apk_path, keystore, user, password);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Unable to sign apk."));
+ return err;
+ }
+#else
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
+ if (!target_sdk_version.is_valid_int()) {
+ target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
+ }
+
+ String apksigner = get_apksigner_path(target_sdk_version.to_int(), true);
+ print_verbose("Starting signing of the APK binary using " + apksigner);
+ if (apksigner == "<FAILED>") {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("All 'apksigner' tools located in Android SDK 'build-tools' directory failed to execute. Please check that you have the correct version installed for your target sdk version. The resulting APK is unsigned."));
+ return OK;
+ }
+ if (!FileAccess::exists(apksigner)) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("'apksigner' could not be found. Please check that the command is available in the Android SDK build-tools directory. The resulting APK is unsigned."));
+ return OK;
+ }
+
String output;
List<String> args;
args.push_back("sign");
@@ -2777,7 +2799,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
args.push_back("pass:" + password);
args.push_back("--ks-key-alias");
args.push_back(user);
- args.push_back(export_path);
+ args.push_back(apk_path);
if (OS::get_singleton()->is_stdout_verbose() && p_debug) {
// We only print verbose logs with credentials for debug builds to avoid leaking release keystore credentials.
print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
@@ -2789,7 +2811,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
print_line("Signing binary using: " + String("\n") + apksigner + " " + join_list(redacted_args, String(" ")));
}
int retval;
- Error err = OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
+ err = OS::get_singleton()->execute(apksigner, args, &output, &retval, true);
if (err != OK) {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start apksigner executable."));
return err;
@@ -2801,15 +2823,23 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("output: \n%s"), output));
return ERR_CANT_CREATE;
}
+#endif
- if (ep.step(vformat(TTR("Verifying %s..."), export_label), 105)) {
+ if (ep.step(TTR("Verifying APK..."), 105)) {
return ERR_SKIP;
}
+#ifdef ANDROID_ENABLED
+ err = OS_Android::get_singleton()->verify_apk(apk_path);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Unable to verify signed apk."));
+ return err;
+ }
+#else
args.clear();
args.push_back("verify");
args.push_back("--verbose");
- args.push_back(export_path);
+ args.push_back(apk_path);
if (p_debug) {
print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
@@ -2822,10 +2852,11 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
}
print_verbose(output);
if (retval) {
- add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("'apksigner' verification of %s failed."), export_label));
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("'apksigner' verification of APK failed."));
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("output: \n%s"), output));
return ERR_CANT_CREATE;
}
+#endif
print_verbose("Successfully completed signing build.");
return OK;
@@ -2963,13 +2994,13 @@ bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExpor
return have_plugins_changed || has_build_dir_changed || first_build;
}
-Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
int export_format = int(p_preset->get("gradle_build/export_format"));
bool should_sign = p_preset->get("package/signed");
return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
}
-Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
+Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
const String base_dir = p_path.get_base_dir();
@@ -2985,7 +3016,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
String gradle_build_directory = use_gradle_build ? ExportTemplateManager::get_android_build_directory(p_preset) : "";
- bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
+ bool p_give_internet = p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT) || p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -3090,7 +3121,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
user_data.assets_directory = assets_directory;
user_data.libs_directory = gradle_build_directory.path_join("libs");
user_data.debug = p_debug;
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
} else {
err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
@@ -3157,6 +3188,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins, android_dependencies_maven_repos);
#endif // DISABLE_DEPRECATED
+ bool has_dotnet_project = false;
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) {
@@ -3174,6 +3206,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
PackedStringArray export_plugin_android_dependencies_maven_repos = export_plugins[i]->get_android_dependencies_maven_repos(Ref<EditorExportPlatform>(this), p_debug);
android_dependencies_maven_repos.append_array(export_plugin_android_dependencies_maven_repos);
}
+
+ PackedStringArray features = export_plugins[i]->get_export_features(Ref<EditorExportPlatform>(this), p_debug);
+ if (features.has("dotnet")) {
+ has_dotnet_project = true;
+ }
}
bool clean_build_required = _is_clean_build_required(p_preset);
@@ -3187,12 +3224,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("clean");
}
+ String edition = has_dotnet_project ? "Mono" : "Standard";
String build_type = p_debug ? "Debug" : "Release";
if (export_format == EXPORT_FORMAT_AAB) {
String bundle_build_command = vformat("bundle%s", build_type);
cmdline.push_back(bundle_build_command);
} else if (export_format == EXPORT_FORMAT_APK) {
- String apk_build_command = vformat("assemble%s", build_type);
+ String apk_build_command = vformat("assemble%s%s", edition, build_type);
cmdline.push_back(apk_build_command);
}
@@ -3269,18 +3307,19 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
List<String> copy_args;
- String copy_command;
- if (export_format == EXPORT_FORMAT_AAB) {
- copy_command = vformat("copyAndRename%sAab", build_type);
- } else if (export_format == EXPORT_FORMAT_APK) {
- copy_command = vformat("copyAndRename%sApk", build_type);
- }
-
+ String copy_command = "copyAndRenameBinary";
copy_args.push_back(copy_command);
copy_args.push_back("-p"); // argument to specify the start directory.
copy_args.push_back(build_path); // start directory.
+ copy_args.push_back("-Pexport_edition=" + edition.to_lower());
+
+ copy_args.push_back("-Pexport_build_type=" + build_type.to_lower());
+
+ String export_format_arg = export_format == EXPORT_FORMAT_AAB ? "aab" : "apk";
+ copy_args.push_back("-Pexport_format=" + export_format_arg);
+
String export_filename = p_path.get_file();
String export_path = p_path.get_base_dir();
if (export_path.is_relative_path()) {
@@ -3319,7 +3358,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
src_apk = find_export_template("android_release.apk");
}
if (src_apk.is_empty()) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Package not found: \"%s\"."), src_apk));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("%s export template not found: \"%s\"."), (p_debug ? "Debug" : "Release"), src_apk));
return ERR_FILE_NOT_FOUND;
}
}
@@ -3464,7 +3503,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
err = OK;
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;