summaryrefslogtreecommitdiff
path: root/platform/android/export/export.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/export/export.cpp')
-rw-r--r--platform/android/export/export.cpp239
1 files changed, 191 insertions, 48 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 1b9d31d752..441fa38bff 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -32,6 +32,7 @@
#include "core/io/marshalls.h"
#include "core/io/zip_io.h"
+#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"
@@ -725,8 +726,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]);
string_at += st_offset + string_count * 4;
- ERR_EXPLAIN("Unimplemented, can't read utf8 string table.");
- ERR_FAIL_COND(string_flags & UTF8_FLAG);
+ ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table.");
if (string_flags & UTF8_FLAG) {
@@ -847,6 +847,136 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
String tname = string_table[name];
+ int dof_index = p_preset->get("graphics/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
+
+ if (tname == "uses-feature" && dof_index > 0) {
+ if (xr_mode_index == 0) {
+ WARN_PRINT("VR DOF feature setting is only valid for oculus HMDs with an XR mode set to VR");
+ }
+ ofs += 24; // skip over end tag
+
+ // save manifest ending so we can restore it
+ Vector<uint8_t> manifest_end;
+ uint32_t manifest_cur_size = p_manifest.size();
+
+ manifest_end.resize(p_manifest.size() - ofs);
+ memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
+
+ int32_t attr_name_string = string_table.find("name");
+ ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
+
+ int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android");
+ if (ns_android_string == -1) {
+ string_table.push_back("http://schemas.android.com/apk/res/android");
+ ns_android_string = string_table.size() - 1;
+ }
+
+ int32_t attr_uses_permission_string = string_table.find("uses-feature");
+ if (attr_uses_permission_string == -1) {
+ string_table.push_back("uses-feature");
+ attr_uses_permission_string = string_table.size() - 1;
+ }
+
+ int32_t attr_required_string = string_table.find("required");
+ if (attr_required_string == -1) {
+ string_table.push_back("required");
+ attr_required_string = string_table.size() - 1;
+ }
+
+ int32_t attr_version_string = string_table.find("version");
+ if (attr_version_string == -1) {
+ string_table.push_back("version");
+ attr_version_string = string_table.size() - 1;
+ }
+
+ String required_value_string;
+ if (dof_index == 1) {
+ required_value_string = "false";
+ } else if (dof_index == 2) {
+ required_value_string = "true";
+ } else {
+ ERR_FAIL_MSG("Unknown DoF index: " + itos(dof_index) + ".");
+ }
+ int32_t required_value = string_table.find(required_value_string);
+ if (required_value == -1) {
+ string_table.push_back(required_value_string);
+ required_value = string_table.size() - 1;
+ }
+
+ int32_t version_value = string_table.find("1");
+ if (version_value == -1) {
+ string_table.push_back("1");
+ version_value = string_table.size() - 1;
+ }
+
+ int32_t feature_string = string_table.find("android.hardware.vr.headtracking");
+ if (feature_string == -1) {
+ string_table.push_back("android.hardware.vr.headtracking");
+ feature_string = string_table.size() - 1;
+ }
+
+ {
+ manifest_cur_size += 96 + 20; // node and three attrs + end node
+ p_manifest.resize(manifest_cur_size);
+
+ // start tag
+ encode_uint16(0x102, &p_manifest.write[ofs]); // type
+ encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+ encode_uint32(96, &p_manifest.write[ofs + 4]); // size
+ encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+ encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+ encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+ encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
+ encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start
+ encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size
+ encode_uint16(3, &p_manifest.write[ofs + 28]); // num_attrs
+ encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index
+ encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index
+ encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index
+
+ // android:name attribute
+ encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns
+ encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name'
+ encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value
+ encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size
+ p_manifest.write[ofs + 50] = 0; // typedvalue_always0
+ p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string)
+ encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference
+
+ // android:required attribute
+ encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns
+ encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name'
+ encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value
+ encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size
+ p_manifest.write[ofs + 70] = 0; // typedvalue_always0
+ p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string)
+ encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference
+
+ // android:version attribute
+ encode_uint32(ns_android_string, &p_manifest.write[ofs + 76]); // ns
+ encode_uint32(attr_version_string, &p_manifest.write[ofs + 80]); // 'name'
+ encode_uint32(version_value, &p_manifest.write[ofs + 84]); // raw_value
+ encode_uint16(8, &p_manifest.write[ofs + 88]); // typedvalue_size
+ p_manifest.write[ofs + 90] = 0; // typedvalue_always0
+ p_manifest.write[ofs + 91] = 0x03; // typedvalue_type (string)
+ encode_uint32(version_value, &p_manifest.write[ofs + 92]); // typedvalue reference
+
+ ofs += 96;
+
+ // end tag
+ encode_uint16(0x103, &p_manifest.write[ofs]); // type
+ encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize
+ encode_uint32(24, &p_manifest.write[ofs + 4]); // size
+ encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno
+ encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment
+ encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns
+ encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name
+
+ ofs += 24;
+ }
+ memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size());
+ ofs -= 24; // go back over back end
+ }
if (tname == "manifest") {
// save manifest ending so we can restore it
@@ -857,12 +987,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size());
int32_t attr_name_string = string_table.find("name");
- ERR_EXPLAIN("Template does not have 'name' attribute");
- ERR_FAIL_COND(attr_name_string == -1);
+ ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute.");
int32_t ns_android_string = string_table.find("android");
- ERR_EXPLAIN("Template does not have 'android' namespace");
- ERR_FAIL_COND(ns_android_string == -1);
+ ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace.");
int32_t attr_uses_permission_string = string_table.find("uses-permission");
if (attr_uses_permission_string == -1) {
@@ -1156,6 +1284,7 @@ public:
virtual void get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
@@ -1270,8 +1399,9 @@ public:
return ERR_UNCONFIGURED;
}
- //export_temp
+ // Export_temp APK.
if (ep.step("Exporting APK", 0)) {
+ device_lock->unlock();
return ERR_SKIP;
}
@@ -1281,11 +1411,20 @@ public:
if (use_reverse)
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
- String export_to = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
- Error err = export_project(p_preset, true, export_to, p_debug_flags);
- if (err) {
- device_lock->unlock();
- return err;
+ String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ DirAccess::remove_file_or_error(tmp_export_path); \
+ device_lock->unlock(); \
+ return m_err; \
+ }
+
+ // Export to temporary APK before sending to device.
+ Error err = export_project(p_preset, true, tmp_export_path, p_debug_flags);
+
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
}
List<String> args;
@@ -1297,7 +1436,7 @@ public:
if (remove_prev) {
if (ep.step("Uninstalling...", 1)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
print_line("Uninstalling previous version: " + devices[p_device].name);
@@ -1312,7 +1451,7 @@ public:
print_line("Installing to device (please wait...): " + devices[p_device].name);
if (ep.step("Installing to device (please wait...)", 2)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
args.clear();
@@ -1320,13 +1459,12 @@ public:
args.push_back(devices[p_device].id);
args.push_back("install");
args.push_back("-r");
- args.push_back(export_to);
+ args.push_back(tmp_export_path);
err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not install to device.");
- device_lock->unlock();
- return ERR_CANT_CREATE;
+ CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
if (use_remote) {
@@ -1380,7 +1518,7 @@ public:
}
if (ep.step("Running on Device...", 3)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
args.clear();
args.push_back("-s");
@@ -1400,11 +1538,11 @@ public:
err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not execute on device.");
- device_lock->unlock();
- return ERR_CANT_CREATE;
+ CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
- device_lock->unlock();
- return OK;
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
}
virtual Ref<Texture> get_run_icon() const {
@@ -1472,19 +1610,16 @@ public:
valid = false;
} else {
Error errn;
- DirAccess *da = DirAccess::open(sdk_path.plus_file("tools"), &errn);
+ DirAccessRef da = DirAccess::open(sdk_path.plus_file("tools"), &errn);
if (errn != OK) {
err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n";
valid = false;
}
- if (da) {
- memdelete(da);
- }
}
if (!FileAccess::exists("res://android/build/build.gradle")) {
- err += TTR("Android project is not installed for compiling. Install from Editor menu.") + "\n";
+ err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
valid = false;
}
}
@@ -1895,8 +2030,16 @@ public:
zlib_filefunc_def io2 = io;
FileAccess *dst_f = NULL;
io2.opaque = &dst_f;
- String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
- zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
+
+ String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ DirAccess::remove_file_or_error(tmp_unaligned_path); \
+ return m_err; \
+ }
+
+ zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
bool immersive = p_preset->get("screen/immersive_mode");
@@ -2024,7 +2167,7 @@ public:
}
if (ep.step("Adding Files...", 1)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
Error err = OK;
Vector<String> cl = cmdline.strip_edges().split(" ");
@@ -2056,7 +2199,7 @@ public:
unzClose(pkg);
EditorNode::add_io_error("Could not write expansion package file: " + apkfname);
- return OK;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
cl.push_back("--use_apk_expansion");
@@ -2143,8 +2286,8 @@ public:
zipClose(unaligned_apk, NULL);
unzClose(pkg);
- if (err) {
- return err;
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
}
if (_signed) {
@@ -2152,7 +2295,7 @@ public:
String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner");
if (!FileAccess::exists(jarsigner)) {
EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned.");
- return OK;
+ CLEANUP_AND_RETURN(OK);
}
String keystore;
@@ -2172,7 +2315,7 @@ public:
}
if (ep.step("Signing debug APK...", 103)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
} else {
@@ -2181,13 +2324,13 @@ public:
user = release_username;
if (ep.step("Signing release APK...", 103)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
}
if (!FileAccess::exists(keystore)) {
EditorNode::add_io_error("Could not find keystore, unable to export.");
- return ERR_FILE_CANT_OPEN;
+ CLEANUP_AND_RETURN(ERR_FILE_CANT_OPEN);
}
List<String> args;
@@ -2205,30 +2348,30 @@ public:
args.push_back(keystore);
args.push_back("-storepass");
args.push_back(password);
- args.push_back(unaligned_path);
+ args.push_back(tmp_unaligned_path);
args.push_back(user);
int retval;
OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
if (retval) {
EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval));
- return ERR_CANT_CREATE;
+ CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
if (ep.step("Verifying APK...", 104)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
args.clear();
args.push_back("-verify");
args.push_back("-keystore");
args.push_back(keystore);
- args.push_back(unaligned_path);
+ args.push_back(tmp_unaligned_path);
args.push_back("-verbose");
OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
if (retval) {
EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8.");
- return ERR_CANT_CREATE;
+ CLEANUP_AND_RETURN(ERR_CANT_CREATE);
}
}
@@ -2237,14 +2380,14 @@ public:
static const int ZIP_ALIGNMENT = 4;
if (ep.step("Aligning APK...", 105)) {
- return ERR_SKIP;
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
- unzFile tmp_unaligned = unzOpen2(unaligned_path.utf8().get_data(), &io);
+ unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io);
if (!tmp_unaligned) {
- EditorNode::add_io_error("Could not find temp unaligned APK.");
- return ERR_FILE_NOT_FOUND;
+ EditorNode::add_io_error("Could not unzip temporary unaligned APK.");
+ CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND);
}
ret = unzGoToFirstFile(tmp_unaligned);
@@ -2314,7 +2457,7 @@ public:
zipClose(final_apk, NULL);
unzClose(tmp_unaligned);
- return OK;
+ CLEANUP_AND_RETURN(OK);
}
virtual void get_platform_features(List<String> *r_features) {
@@ -2367,7 +2510,7 @@ void register_android_exporter() {
EDITOR_DEF("export/android/debug_keystore_pass", "android");
EDITOR_DEF("export/android/force_system_user", false);
EDITOR_DEF("export/android/custom_build_sdk_path", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/custom_build_sdk_path", PROPERTY_HINT_GLOBAL_DIR, "*.keystore"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/custom_build_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF("export/android/timestamping_authority_url", "");
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);