1 /*************************************************************************/
2 /*  export.cpp                                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "export.h"
31 #include "editor/editor_import_export.h"
32 #include "editor/editor_node.h"
33 #include "editor/editor_settings.h"
34 #include "globals.h"
35 #include "io/marshalls.h"
36 #include "io/zip_io.h"
37 #include "os/file_access.h"
38 #include "os/os.h"
39 #include "platform/android/logo.gen.h"
40 #include "version.h"
41 #include <string.h>
42 
43 static const char *android_perms[] = {
44 	"ACCESS_CHECKIN_PROPERTIES",
45 	"ACCESS_COARSE_LOCATION",
46 	"ACCESS_FINE_LOCATION",
47 	"ACCESS_LOCATION_EXTRA_COMMANDS",
48 	"ACCESS_MOCK_LOCATION",
49 	"ACCESS_NETWORK_STATE",
50 	"ACCESS_SURFACE_FLINGER",
51 	"ACCESS_WIFI_STATE",
52 	"ACCOUNT_MANAGER",
53 	"ADD_VOICEMAIL",
54 	"AUTHENTICATE_ACCOUNTS",
55 	"BATTERY_STATS",
56 	"BIND_ACCESSIBILITY_SERVICE",
57 	"BIND_APPWIDGET",
58 	"BIND_DEVICE_ADMIN",
59 	"BIND_INPUT_METHOD",
60 	"BIND_NFC_SERVICE",
61 	"BIND_NOTIFICATION_LISTENER_SERVICE",
62 	"BIND_PRINT_SERVICE",
63 	"BIND_REMOTEVIEWS",
64 	"BIND_TEXT_SERVICE",
65 	"BIND_VPN_SERVICE",
66 	"BIND_WALLPAPER",
67 	"BLUETOOTH",
68 	"BLUETOOTH_ADMIN",
69 	"BLUETOOTH_PRIVILEGED",
70 	"BRICK",
71 	"BROADCAST_PACKAGE_REMOVED",
72 	"BROADCAST_SMS",
73 	"BROADCAST_STICKY",
74 	"BROADCAST_WAP_PUSH",
75 	"CALL_PHONE",
76 	"CALL_PRIVILEGED",
77 	"CAMERA",
78 	"CAPTURE_AUDIO_OUTPUT",
79 	"CAPTURE_SECURE_VIDEO_OUTPUT",
80 	"CAPTURE_VIDEO_OUTPUT",
81 	"CHANGE_COMPONENT_ENABLED_STATE",
82 	"CHANGE_CONFIGURATION",
83 	"CHANGE_NETWORK_STATE",
84 	"CHANGE_WIFI_MULTICAST_STATE",
85 	"CHANGE_WIFI_STATE",
86 	"CLEAR_APP_CACHE",
87 	"CLEAR_APP_USER_DATA",
88 	"CONTROL_LOCATION_UPDATES",
89 	"DELETE_CACHE_FILES",
90 	"DELETE_PACKAGES",
91 	"DEVICE_POWER",
92 	"DIAGNOSTIC",
93 	"DISABLE_KEYGUARD",
94 	"DUMP",
95 	"EXPAND_STATUS_BAR",
96 	"FACTORY_TEST",
97 	"FLASHLIGHT",
98 	"FORCE_BACK",
99 	"GET_ACCOUNTS",
100 	"GET_PACKAGE_SIZE",
101 	"GET_TASKS",
102 	"GET_TOP_ACTIVITY_INFO",
103 	"GLOBAL_SEARCH",
104 	"HARDWARE_TEST",
105 	"INJECT_EVENTS",
106 	"INSTALL_LOCATION_PROVIDER",
107 	"INSTALL_PACKAGES",
108 	"INSTALL_SHORTCUT",
109 	"INTERNAL_SYSTEM_WINDOW",
110 	"INTERNET",
111 	"KILL_BACKGROUND_PROCESSES",
112 	"LOCATION_HARDWARE",
113 	"MANAGE_ACCOUNTS",
114 	"MANAGE_APP_TOKENS",
115 	"MANAGE_DOCUMENTS",
116 	"MASTER_CLEAR",
117 	"MEDIA_CONTENT_CONTROL",
118 	"MODIFY_AUDIO_SETTINGS",
119 	"MODIFY_PHONE_STATE",
120 	"MOUNT_FORMAT_FILESYSTEMS",
121 	"MOUNT_UNMOUNT_FILESYSTEMS",
122 	"NFC",
123 	"PERSISTENT_ACTIVITY",
124 	"PROCESS_OUTGOING_CALLS",
125 	"READ_CALENDAR",
126 	"READ_CALL_LOG",
127 	"READ_CONTACTS",
128 	"READ_EXTERNAL_STORAGE",
129 	"READ_FRAME_BUFFER",
130 	"READ_HISTORY_BOOKMARKS",
131 	"READ_INPUT_STATE",
132 	"READ_LOGS",
133 	"READ_PHONE_STATE",
134 	"READ_PROFILE",
135 	"READ_SMS",
136 	"READ_SOCIAL_STREAM",
137 	"READ_SYNC_SETTINGS",
138 	"READ_SYNC_STATS",
139 	"READ_USER_DICTIONARY",
140 	"REBOOT",
141 	"RECEIVE_BOOT_COMPLETED",
142 	"RECEIVE_MMS",
143 	"RECEIVE_SMS",
144 	"RECEIVE_WAP_PUSH",
145 	"RECORD_AUDIO",
146 	"REORDER_TASKS",
147 	"RESTART_PACKAGES",
148 	"SEND_RESPOND_VIA_MESSAGE",
149 	"SEND_SMS",
150 	"SET_ACTIVITY_WATCHER",
151 	"SET_ALARM",
152 	"SET_ALWAYS_FINISH",
153 	"SET_ANIMATION_SCALE",
154 	"SET_DEBUG_APP",
155 	"SET_ORIENTATION",
156 	"SET_POINTER_SPEED",
157 	"SET_PREFERRED_APPLICATIONS",
158 	"SET_PROCESS_LIMIT",
159 	"SET_TIME",
160 	"SET_TIME_ZONE",
161 	"SET_WALLPAPER",
162 	"SET_WALLPAPER_HINTS",
163 	"SIGNAL_PERSISTENT_PROCESSES",
164 	"STATUS_BAR",
165 	"SUBSCRIBED_FEEDS_READ",
166 	"SUBSCRIBED_FEEDS_WRITE",
167 	"SYSTEM_ALERT_WINDOW",
168 	"TRANSMIT_IR",
169 	"UNINSTALL_SHORTCUT",
170 	"UPDATE_DEVICE_STATS",
171 	"USE_CREDENTIALS",
172 	"USE_SIP",
173 	"VIBRATE",
174 	"WAKE_LOCK",
175 	"WRITE_APN_SETTINGS",
176 	"WRITE_CALENDAR",
177 	"WRITE_CALL_LOG",
178 	"WRITE_CONTACTS",
179 	"WRITE_EXTERNAL_STORAGE",
180 	"WRITE_GSERVICES",
181 	"WRITE_HISTORY_BOOKMARKS",
182 	"WRITE_PROFILE",
183 	"WRITE_SECURE_SETTINGS",
184 	"WRITE_SETTINGS",
185 	"WRITE_SMS",
186 	"WRITE_SOCIAL_STREAM",
187 	"WRITE_SYNC_SETTINGS",
188 	"WRITE_USER_DICTIONARY",
189 	NULL
190 };
191 
192 class EditorExportPlatformAndroid : public EditorExportPlatform {
193 
194 	OBJ_TYPE(EditorExportPlatformAndroid, EditorExportPlatform);
195 
196 	enum {
197 		MAX_USER_PERMISSIONS = 20,
198 		SCREEN_SMALL = 0,
199 		SCREEN_NORMAL = 1,
200 		SCREEN_LARGE = 2,
201 		SCREEN_XLARGE = 3,
202 		SCREEN_MAX = 4
203 	};
204 
205 	String custom_release_package;
206 	String custom_debug_package;
207 
208 	int version_code;
209 	String version_name;
210 	String package;
211 	String name;
212 	String icon;
213 	String cmdline;
214 	bool _signed;
215 	bool apk_expansion;
216 	bool remove_prev;
217 	bool use_32_fb;
218 	bool immersive;
219 	bool export_arm;
220 	bool export_arm64;
221 	bool export_x86;
222 	bool export_x86_64;
223 	String apk_expansion_salt;
224 	String apk_expansion_pkey;
225 	int orientation;
226 
227 	String release_keystore;
228 	String release_password;
229 	String release_username;
230 
231 	struct APKExportData {
232 
233 		zipFile apk;
234 		EditorProgress *ep;
235 	};
236 
237 	struct Device {
238 
239 		String id;
240 		String name;
241 		String description;
242 		int api_level;
243 	};
244 
245 	Vector<Device> devices;
246 	bool devices_changed;
247 	Mutex *device_lock;
248 	Thread *device_thread;
249 	Ref<ImageTexture> logo;
250 
251 	Set<String> perms;
252 	String user_perms[MAX_USER_PERMISSIONS];
253 	bool screen_support[SCREEN_MAX];
254 
255 	volatile bool quit_request;
256 
257 	static void _device_poll_thread(void *ud);
258 
259 	String get_package_name();
260 
261 	String get_project_name() const;
262 	void _fix_manifest(Vector<uint8_t> &p_manifest, bool p_give_internet);
263 	void _fix_resources(Vector<uint8_t> &p_manifest);
264 	static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
265 	static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data);
266 
267 protected:
268 	bool _set(const StringName &p_name, const Variant &p_value);
269 	bool _get(const StringName &p_name, Variant &r_ret) const;
270 	void _get_property_list(List<PropertyInfo> *p_list) const;
271 
272 public:
get_name() const273 	virtual String get_name() const { return "Android"; }
get_image_compression() const274 	virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; }
get_logo() const275 	virtual Ref<Texture> get_logo() const { return logo; }
276 
277 	virtual bool poll_devices();
278 	virtual int get_device_count() const;
279 	virtual String get_device_name(int p_device) const;
280 	virtual String get_device_info(int p_device) const;
281 	virtual Error run(int p_device, int p_flags = 0);
282 
requires_password(bool p_debug) const283 	virtual bool requires_password(bool p_debug) const { return !p_debug; }
get_binary_extension() const284 	virtual String get_binary_extension() const { return "apk"; }
285 	virtual Error export_project(const String &p_path, bool p_debug, int p_flags = 0);
286 
287 	virtual bool can_export(String *r_error = NULL) const;
288 
289 	EditorExportPlatformAndroid();
290 	~EditorExportPlatformAndroid();
291 };
292 
_set(const StringName & p_name,const Variant & p_value)293 bool EditorExportPlatformAndroid::_set(const StringName &p_name, const Variant &p_value) {
294 
295 	String n = p_name;
296 
297 	if (n == "one_click_deploy/clear_previous_install")
298 		remove_prev = p_value;
299 	else if (n == "custom_package/debug")
300 		custom_debug_package = p_value;
301 	else if (n == "custom_package/release")
302 		custom_release_package = p_value;
303 	else if (n == "version/code")
304 		version_code = p_value;
305 	else if (n == "version/name")
306 		version_name = p_value;
307 	else if (n == "command_line/extra_args")
308 		cmdline = p_value;
309 	else if (n == "package/unique_name")
310 		package = p_value;
311 	else if (n == "package/name")
312 		name = p_value;
313 	else if (n == "package/icon")
314 		icon = p_value;
315 	else if (n == "package/signed")
316 		_signed = p_value;
317 	else if (n == "architecture/arm")
318 		export_arm = p_value;
319 	else if (n == "architecture/arm64")
320 		export_arm64 = p_value;
321 	else if (n == "architecture/x86")
322 		export_x86 = p_value;
323 	else if (n == "architecture/x86_64")
324 		export_x86_64 = p_value;
325 	else if (n == "screen/use_32_bits_view")
326 		use_32_fb = p_value;
327 	else if (n == "screen/immersive_mode")
328 		immersive = p_value;
329 	else if (n == "screen/orientation")
330 		orientation = p_value;
331 	else if (n == "screen/support_small")
332 		screen_support[SCREEN_SMALL] = p_value;
333 	else if (n == "screen/support_normal")
334 		screen_support[SCREEN_NORMAL] = p_value;
335 	else if (n == "screen/support_large")
336 		screen_support[SCREEN_LARGE] = p_value;
337 	else if (n == "screen/support_xlarge")
338 		screen_support[SCREEN_XLARGE] = p_value;
339 	else if (n == "keystore/release")
340 		release_keystore = p_value;
341 	else if (n == "keystore/release_user")
342 		release_username = p_value;
343 	else if (n == "keystore/release_password")
344 		release_password = p_value;
345 	else if (n == "apk_expansion/enable")
346 		apk_expansion = p_value;
347 	else if (n == "apk_expansion/SALT")
348 		apk_expansion_salt = p_value;
349 	else if (n == "apk_expansion/public_key")
350 		apk_expansion_pkey = p_value;
351 	else if (n.begins_with("permissions/")) {
352 
353 		String what = n.get_slicec('/', 1).to_upper();
354 		bool state = p_value;
355 		if (state)
356 			perms.insert(what);
357 		else
358 			perms.erase(what);
359 	} else if (n.begins_with("user_permissions/")) {
360 
361 		int which = n.get_slicec('/', 1).to_int();
362 		ERR_FAIL_INDEX_V(which, MAX_USER_PERMISSIONS, false);
363 		user_perms[which] = p_value;
364 
365 	} else
366 		return false;
367 
368 	return true;
369 }
370 
_get(const StringName & p_name,Variant & r_ret) const371 bool EditorExportPlatformAndroid::_get(const StringName &p_name, Variant &r_ret) const {
372 
373 	String n = p_name;
374 	if (n == "one_click_deploy/clear_previous_install")
375 		r_ret = remove_prev;
376 	else if (n == "custom_package/debug")
377 		r_ret = custom_debug_package;
378 	else if (n == "custom_package/release")
379 		r_ret = custom_release_package;
380 	else if (n == "version/code")
381 		r_ret = version_code;
382 	else if (n == "version/name")
383 		r_ret = version_name;
384 	else if (n == "command_line/extra_args")
385 		r_ret = cmdline;
386 	else if (n == "package/unique_name")
387 		r_ret = package;
388 	else if (n == "package/name")
389 		r_ret = name;
390 	else if (n == "package/icon")
391 		r_ret = icon;
392 	else if (n == "package/signed")
393 		r_ret = _signed;
394 	else if (n == "architecture/arm")
395 		r_ret = export_arm;
396 	else if (n == "architecture/arm64")
397 		r_ret = export_arm64;
398 	else if (n == "architecture/x86")
399 		r_ret = export_x86;
400 	else if (n == "architecture/x86_64")
401 		r_ret = export_x86_64;
402 	else if (n == "screen/use_32_bits_view")
403 		r_ret = use_32_fb;
404 	else if (n == "screen/immersive_mode")
405 		r_ret = immersive;
406 	else if (n == "screen/orientation")
407 		r_ret = orientation;
408 	else if (n == "screen/support_small")
409 		r_ret = screen_support[SCREEN_SMALL];
410 	else if (n == "screen/support_normal")
411 		r_ret = screen_support[SCREEN_NORMAL];
412 	else if (n == "screen/support_large")
413 		r_ret = screen_support[SCREEN_LARGE];
414 	else if (n == "screen/support_xlarge")
415 		r_ret = screen_support[SCREEN_XLARGE];
416 	else if (n == "keystore/release")
417 		r_ret = release_keystore;
418 	else if (n == "keystore/release_user")
419 		r_ret = release_username;
420 	else if (n == "keystore/release_password")
421 		r_ret = release_password;
422 	else if (n == "apk_expansion/enable")
423 		r_ret = apk_expansion;
424 	else if (n == "apk_expansion/SALT")
425 		r_ret = apk_expansion_salt;
426 	else if (n == "apk_expansion/public_key")
427 		r_ret = apk_expansion_pkey;
428 	else if (n.begins_with("permissions/")) {
429 
430 		String what = n.get_slicec('/', 1).to_upper();
431 		r_ret = perms.has(what);
432 	} else if (n.begins_with("user_permissions/")) {
433 
434 		int which = n.get_slicec('/', 1).to_int();
435 		ERR_FAIL_INDEX_V(which, MAX_USER_PERMISSIONS, false);
436 		r_ret = user_perms[which];
437 	} else
438 		return false;
439 
440 	return true;
441 }
442 
_get_property_list(List<PropertyInfo> * p_list) const443 void EditorExportPlatformAndroid::_get_property_list(List<PropertyInfo> *p_list) const {
444 
445 	p_list->push_back(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"));
446 	p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "apk"));
447 	p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "apk"));
448 	p_list->push_back(PropertyInfo(Variant::STRING, "command_line/extra_args"));
449 	p_list->push_back(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,2147483647,1"));
450 	p_list->push_back(PropertyInfo(Variant::STRING, "version/name"));
451 	p_list->push_back(PropertyInfo(Variant::STRING, "package/unique_name"));
452 	p_list->push_back(PropertyInfo(Variant::STRING, "package/name"));
453 	p_list->push_back(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"));
454 	p_list->push_back(PropertyInfo(Variant::BOOL, "package/signed"));
455 	p_list->push_back(PropertyInfo(Variant::BOOL, "architecture/arm"));
456 	p_list->push_back(PropertyInfo(Variant::BOOL, "architecture/arm64"));
457 	p_list->push_back(PropertyInfo(Variant::BOOL, "architecture/x86"));
458 	p_list->push_back(PropertyInfo(Variant::BOOL, "architecture/x86_64"));
459 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/use_32_bits_view"));
460 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/immersive_mode"));
461 	p_list->push_back(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"));
462 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/support_small"));
463 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/support_normal"));
464 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/support_large"));
465 	p_list->push_back(PropertyInfo(Variant::BOOL, "screen/support_xlarge"));
466 	p_list->push_back(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "keystore"));
467 	p_list->push_back(PropertyInfo(Variant::STRING, "keystore/release_user"));
468 	p_list->push_back(PropertyInfo(Variant::STRING, "keystore/release_password"));
469 	p_list->push_back(PropertyInfo(Variant::BOOL, "apk_expansion/enable"));
470 	p_list->push_back(PropertyInfo(Variant::STRING, "apk_expansion/SALT"));
471 	p_list->push_back(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT));
472 
473 	const char **perms = android_perms;
474 	while (*perms) {
475 
476 		p_list->push_back(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()));
477 		perms++;
478 	}
479 
480 	for (int i = 0; i < MAX_USER_PERMISSIONS; i++) {
481 
482 		p_list->push_back(PropertyInfo(Variant::STRING, "user_permissions/" + itos(i)));
483 	}
484 
485 	//p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)"));
486 }
487 
_parse_string(const uint8_t * p_bytes,bool p_utf8)488 static String _parse_string(const uint8_t *p_bytes, bool p_utf8) {
489 
490 	uint32_t offset = 0;
491 	uint32_t len = decode_uint16(&p_bytes[offset]);
492 
493 	if (p_utf8) {
494 		//don't know how to read extended utf8, this will have to be for now
495 		len >>= 8;
496 	}
497 	offset += 2;
498 	//printf("len %i, unicode: %i\n",len,int(p_utf8));
499 
500 	if (p_utf8) {
501 
502 		Vector<uint8_t> str8;
503 		str8.resize(len + 1);
504 		for (int i = 0; i < len; i++) {
505 			str8[i] = p_bytes[offset + i];
506 		}
507 		str8[len] = 0;
508 		String str;
509 		str.parse_utf8((const char *)str8.ptr());
510 		return str;
511 	} else {
512 
513 		String str;
514 		for (int i = 0; i < len; i++) {
515 			CharType c = decode_uint16(&p_bytes[offset + i * 2]);
516 			if (c == 0)
517 				break;
518 			str += String::chr(c);
519 		}
520 		return str;
521 	}
522 }
523 
_fix_resources(Vector<uint8_t> & p_manifest)524 void EditorExportPlatformAndroid::_fix_resources(Vector<uint8_t> &p_manifest) {
525 
526 	const int UTF8_FLAG = 0x00000100;
527 	print_line("*******************GORRRGLE***********************");
528 
529 	uint32_t header = decode_uint32(&p_manifest[0]);
530 	uint32_t filesize = decode_uint32(&p_manifest[4]);
531 	uint32_t string_block_len = decode_uint32(&p_manifest[16]);
532 	uint32_t string_count = decode_uint32(&p_manifest[20]);
533 	uint32_t string_flags = decode_uint32(&p_manifest[28]);
534 	const uint32_t string_table_begins = 40;
535 
536 	Vector<String> string_table;
537 
538 	//printf("stirng block len: %i\n",string_block_len);
539 	//printf("stirng count: %i\n",string_count);
540 	//printf("flags: %x\n",string_flags);
541 
542 	for (int i = 0; i < string_count; i++) {
543 
544 		uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]);
545 		offset += string_table_begins + string_count * 4;
546 
547 		String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG);
548 
549 		if (str.begins_with("godot-project-name")) {
550 
551 			if (str == "godot-project-name") {
552 				//project name
553 				str = get_project_name();
554 
555 			} else {
556 
557 				String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_");
558 				String prop = "application/name_" + lang;
559 				if (Globals::get_singleton()->has(prop)) {
560 					str = Globals::get_singleton()->get(prop);
561 				} else {
562 					str = get_project_name();
563 				}
564 			}
565 		}
566 
567 		string_table.push_back(str);
568 	}
569 
570 	//write a new string table, but use 16 bits
571 	Vector<uint8_t> ret;
572 	ret.resize(string_table_begins + string_table.size() * 4);
573 
574 	for (int i = 0; i < string_table_begins; i++) {
575 
576 		ret[i] = p_manifest[i];
577 	}
578 
579 	int ofs = 0;
580 	for (int i = 0; i < string_table.size(); i++) {
581 
582 		encode_uint32(ofs, &ret[string_table_begins + i * 4]);
583 		ofs += string_table[i].length() * 2 + 2 + 2;
584 	}
585 
586 	ret.resize(ret.size() + ofs);
587 	uint8_t *chars = &ret[ret.size() - ofs];
588 	for (int i = 0; i < string_table.size(); i++) {
589 
590 		String s = string_table[i];
591 		encode_uint16(s.length(), chars);
592 		chars += 2;
593 		for (int j = 0; j < s.length(); j++) {
594 			encode_uint16(s[j], chars);
595 			chars += 2;
596 		}
597 		encode_uint16(0, chars);
598 		chars += 2;
599 	}
600 
601 	//pad
602 	while (ret.size() % 4)
603 		ret.push_back(0);
604 
605 	//change flags to not use utf8
606 	encode_uint32(string_flags & ~0x100, &ret[28]);
607 	//change length
608 	encode_uint32(ret.size() - 12, &ret[16]);
609 	//append the rest...
610 	int rest_from = 12 + string_block_len;
611 	int rest_to = ret.size();
612 	int rest_len = (p_manifest.size() - rest_from);
613 	ret.resize(ret.size() + (p_manifest.size() - rest_from));
614 	for (int i = 0; i < rest_len; i++) {
615 		ret[rest_to + i] = p_manifest[rest_from + i];
616 	}
617 	//finally update the size
618 	encode_uint32(ret.size(), &ret[4]);
619 
620 	p_manifest = ret;
621 	//printf("end\n");
622 }
623 
get_project_name() const624 String EditorExportPlatformAndroid::get_project_name() const {
625 
626 	String aname;
627 	if (this->name != "") {
628 		aname = this->name;
629 	} else {
630 		aname = Globals::get_singleton()->get("application/name");
631 	}
632 
633 	if (aname == "") {
634 		aname = _MKSTR(VERSION_NAME);
635 	}
636 
637 	return aname;
638 }
639 
_fix_manifest(Vector<uint8_t> & p_manifest,bool p_give_internet)640 void EditorExportPlatformAndroid::_fix_manifest(Vector<uint8_t> &p_manifest, bool p_give_internet) {
641 
642 	const int CHUNK_AXML_FILE = 0x00080003;
643 	const int CHUNK_RESOURCEIDS = 0x00080180;
644 	const int CHUNK_STRINGS = 0x001C0001;
645 	const int CHUNK_XML_END_NAMESPACE = 0x00100101;
646 	const int CHUNK_XML_END_TAG = 0x00100103;
647 	const int CHUNK_XML_START_NAMESPACE = 0x00100100;
648 	const int CHUNK_XML_START_TAG = 0x00100102;
649 	const int CHUNK_XML_TEXT = 0x00100104;
650 	const int UTF8_FLAG = 0x00000100;
651 
652 	Vector<String> string_table;
653 
654 	uint32_t ofs = 0;
655 
656 	uint32_t header = decode_uint32(&p_manifest[ofs]);
657 	uint32_t filesize = decode_uint32(&p_manifest[ofs + 4]);
658 	ofs += 8;
659 
660 	//	print_line("FILESIZE: "+itos(filesize)+" ACTUAL: "+itos(p_manifest.size()));
661 
662 	uint32_t string_count;
663 	uint32_t styles_count;
664 	uint32_t string_flags;
665 	uint32_t string_data_offset;
666 
667 	uint32_t styles_offset;
668 	uint32_t string_table_begins;
669 	uint32_t string_table_ends;
670 	Vector<uint8_t> stable_extra;
671 
672 	while (ofs < p_manifest.size()) {
673 
674 		uint32_t chunk = decode_uint32(&p_manifest[ofs]);
675 		uint32_t size = decode_uint32(&p_manifest[ofs + 4]);
676 
677 		switch (chunk) {
678 
679 			case CHUNK_STRINGS: {
680 
681 				int iofs = ofs + 8;
682 
683 				string_count = decode_uint32(&p_manifest[iofs]);
684 				styles_count = decode_uint32(&p_manifest[iofs + 4]);
685 				uint32_t string_flags = decode_uint32(&p_manifest[iofs + 8]);
686 				string_data_offset = decode_uint32(&p_manifest[iofs + 12]);
687 				styles_offset = decode_uint32(&p_manifest[iofs + 16]);
688 				/*
689 				printf("string count: %i\n",string_count);
690 				printf("flags: %i\n",string_flags);
691 				printf("sdata ofs: %i\n",string_data_offset);
692 				printf("styles ofs: %i\n",styles_offset);
693 */
694 				uint32_t st_offset = iofs + 20;
695 				string_table.resize(string_count);
696 				uint32_t string_end = 0;
697 
698 				string_table_begins = st_offset;
699 
700 				for (int i = 0; i < string_count; i++) {
701 
702 					uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]);
703 					string_at += st_offset + string_count * 4;
704 
705 					ERR_EXPLAIN("Unimplemented, can't read utf8 string table.");
706 					ERR_FAIL_COND(string_flags & UTF8_FLAG);
707 
708 					if (string_flags & UTF8_FLAG) {
709 
710 					} else {
711 						uint32_t len = decode_uint16(&p_manifest[string_at]);
712 						Vector<CharType> ucstring;
713 						ucstring.resize(len + 1);
714 						for (int j = 0; j < len; j++) {
715 							uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]);
716 							ucstring[j] = c;
717 						}
718 						string_end = MAX(string_at + 2 + 2 * len, string_end);
719 						ucstring[len] = 0;
720 						string_table[i] = ucstring.ptr();
721 					}
722 
723 					//					print_line("String "+itos(i)+": "+string_table[i]);
724 				}
725 
726 				for (int i = string_end; i < (ofs + size); i++) {
727 					stable_extra.push_back(p_manifest[i]);
728 				}
729 
730 				//				printf("stable extra: %i\n",int(stable_extra.size()));
731 				string_table_ends = ofs + size;
732 
733 				//				print_line("STABLE SIZE: "+itos(size)+" ACTUAL: "+itos(string_table_ends));
734 
735 			} break;
736 			case CHUNK_XML_START_TAG: {
737 
738 				int iofs = ofs + 8;
739 				uint32_t line = decode_uint32(&p_manifest[iofs]);
740 				uint32_t nspace = decode_uint32(&p_manifest[iofs + 8]);
741 				uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
742 				uint32_t check = decode_uint32(&p_manifest[iofs + 16]);
743 
744 				String tname = string_table[name];
745 
746 				//				printf("NSPACE: %i\n",nspace);
747 				//printf("NAME: %i (%s)\n",name,tname.utf8().get_data());
748 				//printf("CHECK: %x\n",check);
749 				uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]);
750 				iofs += 28;
751 				//printf("ATTRCOUNT: %x\n",attrcount);
752 				for (int i = 0; i < attrcount; i++) {
753 					uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]);
754 					uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]);
755 					uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]);
756 					uint32_t attr_flags = decode_uint32(&p_manifest[iofs + 12]);
757 					uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]);
758 
759 					String value;
760 					if (attr_value != 0xFFFFFFFF)
761 						value = string_table[attr_value];
762 					else
763 						value = "Res #" + itos(attr_resid);
764 					String attrname = string_table[attr_name];
765 					String nspace;
766 					if (attr_nspace != 0xFFFFFFFF)
767 						nspace = string_table[attr_nspace];
768 					else
769 						nspace = "";
770 
771 					//printf("ATTR %i NSPACE: %i\n",i,attr_nspace);
772 					//printf("ATTR %i NAME: %i (%s)\n",i,attr_name,attrname.utf8().get_data());
773 					//printf("ATTR %i VALUE: %i (%s)\n",i,attr_value,value.utf8().get_data());
774 					//printf("ATTR %i FLAGS: %x\n",i,attr_flags);
775 					//printf("ATTR %i RESID: %x\n",i,attr_resid);
776 
777 					//replace project information
778 					if (tname == "manifest" && attrname == "package") {
779 
780 						print_line("FOUND package");
781 						string_table[attr_value] = get_package_name();
782 					}
783 
784 					//print_line("tname: "+tname);
785 					//print_line("nspace: "+nspace);
786 					//print_line("attrname: "+attrname);
787 					if (tname == "manifest" && /*nspace=="android" &&*/ attrname == "versionCode") {
788 
789 						print_line("FOUND versionCode");
790 						encode_uint32(version_code, &p_manifest[iofs + 16]);
791 					}
792 
793 					if (tname == "manifest" && /*nspace=="android" &&*/ attrname == "versionName") {
794 
795 						print_line("FOUND versionName");
796 						if (attr_value == 0xFFFFFFFF) {
797 							WARN_PRINT("Version name in a resource, should be plaintext")
798 						} else
799 							string_table[attr_value] = version_name;
800 					}
801 
802 					if (tname == "activity" && /*nspace=="android" &&*/ attrname == "screenOrientation") {
803 
804 						encode_uint32(orientation == 0 ? 0 : 1, &p_manifest[iofs + 16]);
805 						/*
806 						print_line("FOUND screen orientation");
807 						if (attr_value==0xFFFFFFFF) {
808 							WARN_PRINT("Version name in a resource, should be plaintext")
809 						} else {
810 							string_table[attr_value]=(orientation==0?"landscape":"portrait");
811 						}*/
812 					}
813 
814 					if (tname == "supports-screens") {
815 
816 						if (attrname == "smallScreens") {
817 
818 							encode_uint32(screen_support[SCREEN_SMALL] ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]);
819 
820 						} else if (attrname == "normalScreens") {
821 
822 							encode_uint32(screen_support[SCREEN_NORMAL] ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]);
823 
824 						} else if (attrname == "largeScreens") {
825 
826 							encode_uint32(screen_support[SCREEN_LARGE] ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]);
827 
828 						} else if (attrname == "xlargeScreens") {
829 
830 							encode_uint32(screen_support[SCREEN_XLARGE] ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]);
831 						}
832 					}
833 
834 					iofs += 20;
835 				}
836 
837 			} break;
838 			case CHUNK_XML_END_TAG: {
839 				int iofs = ofs + 8;
840 				uint32_t name = decode_uint32(&p_manifest[iofs + 12]);
841 				String tname = string_table[name];
842 
843 				if (tname == "manifest") {
844 					print_line("Found manifest end");
845 
846 					// save manifest ending so we can restore it
847 					Vector<uint8_t> manifest_end;
848 					uint32_t manifest_cur_size = p_manifest.size();
849 					uint32_t node_size = size;
850 
851 					manifest_end.resize(p_manifest.size() - ofs);
852 					memcpy(manifest_end.ptr(), &p_manifest[ofs], manifest_end.size());
853 
854 					int32_t attr_name_string = string_table.find("name");
855 					ERR_EXPLAIN("Template does not have 'name' attribute");
856 					ERR_FAIL_COND(attr_name_string == -1);
857 
858 					int32_t ns_android_string = string_table.find("android");
859 					ERR_EXPLAIN("Template does not have 'android' namespace");
860 					ERR_FAIL_COND(ns_android_string == -1);
861 
862 					int32_t attr_uses_permission_string = string_table.find("uses-permission");
863 					if (attr_uses_permission_string == -1) {
864 						string_table.push_back("uses-permission");
865 						attr_uses_permission_string = string_table.size() - 1;
866 					}
867 
868 					Vector<String> apk_perms;
869 					const char **aperms = android_perms;
870 					while (*aperms) {
871 						if (perms.has(*aperms)) {
872 							apk_perms.push_back("android.permission." + String(*aperms));
873 						}
874 						aperms++;
875 					}
876 
877 					for (int i = 0; i < MAX_USER_PERMISSIONS; i++) {
878 						if (user_perms[i].strip_edges() != "" && user_perms[i].strip_edges() != "False")
879 							apk_perms.push_back(user_perms[i].strip_edges());
880 					}
881 
882 					if (p_give_internet) {
883 						if (apk_perms.find("android.permission.INTERNET") == -1)
884 							apk_perms.push_back("android.permission.INTERNET");
885 					}
886 
887 					for (int i = 0; i < apk_perms.size(); ++i) {
888 						print_line("Adding permission " + apk_perms[i]);
889 
890 						manifest_cur_size += 56 + 24; // node + end node
891 						p_manifest.resize(manifest_cur_size);
892 
893 						// Add permission to the string pool
894 						int32_t perm_string = string_table.find(apk_perms[i]);
895 						if (perm_string == -1) {
896 							string_table.push_back(apk_perms[i]);
897 							perm_string = string_table.size() - 1;
898 						}
899 
900 						// start tag
901 						encode_uint16(0x102, &p_manifest[ofs]); // type
902 						encode_uint16(16, &p_manifest[ofs + 2]); // headersize
903 						encode_uint32(56, &p_manifest[ofs + 4]); // size
904 						encode_uint32(0, &p_manifest[ofs + 8]); // lineno
905 						encode_uint32(-1, &p_manifest[ofs + 12]); // comment
906 						encode_uint32(-1, &p_manifest[ofs + 16]); // ns
907 						encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name
908 						encode_uint16(20, &p_manifest[ofs + 24]); // attr_start
909 						encode_uint16(20, &p_manifest[ofs + 26]); // attr_size
910 						encode_uint16(1, &p_manifest[ofs + 28]); // num_attrs
911 						encode_uint16(0, &p_manifest[ofs + 30]); // id_index
912 						encode_uint16(0, &p_manifest[ofs + 32]); // class_index
913 						encode_uint16(0, &p_manifest[ofs + 34]); // style_index
914 
915 						// attribute
916 						encode_uint32(ns_android_string, &p_manifest[ofs + 36]); // ns
917 						encode_uint32(attr_name_string, &p_manifest[ofs + 40]); // 'name'
918 						encode_uint32(perm_string, &p_manifest[ofs + 44]); // raw_value
919 						encode_uint16(8, &p_manifest[ofs + 48]); // typedvalue_size
920 						p_manifest[ofs + 50] = 0; // typedvalue_always0
921 						p_manifest[ofs + 51] = 0x03; // typedvalue_type (string)
922 						encode_uint32(perm_string, &p_manifest[ofs + 52]); // typedvalue reference
923 
924 						ofs += 56;
925 
926 						// end tag
927 						encode_uint16(0x103, &p_manifest[ofs]); // type
928 						encode_uint16(16, &p_manifest[ofs + 2]); // headersize
929 						encode_uint32(24, &p_manifest[ofs + 4]); // size
930 						encode_uint32(0, &p_manifest[ofs + 8]); // lineno
931 						encode_uint32(-1, &p_manifest[ofs + 12]); // comment
932 						encode_uint32(-1, &p_manifest[ofs + 16]); // ns
933 						encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name
934 
935 						ofs += 24;
936 					}
937 
938 					// copy footer back in
939 					memcpy(&p_manifest[ofs], manifest_end.ptr(), manifest_end.size());
940 				}
941 			} break;
942 		}
943 		//printf("chunk %x: size: %d\n",chunk,size);
944 
945 		ofs += size;
946 	}
947 
948 	//printf("end\n");
949 
950 	//create new andriodmanifest binary
951 
952 	Vector<uint8_t> ret;
953 	ret.resize(string_table_begins + string_table.size() * 4);
954 
955 	for (int i = 0; i < string_table_begins; i++) {
956 
957 		ret[i] = p_manifest[i];
958 	}
959 
960 	ofs = 0;
961 	for (int i = 0; i < string_table.size(); i++) {
962 
963 		encode_uint32(ofs, &ret[string_table_begins + i * 4]);
964 		ofs += string_table[i].length() * 2 + 2 + 2;
965 	}
966 
967 	ret.resize(ret.size() + ofs);
968 	string_data_offset = ret.size() - ofs;
969 	uint8_t *chars = &ret[string_data_offset];
970 	for (int i = 0; i < string_table.size(); i++) {
971 
972 		String s = string_table[i];
973 		encode_uint16(s.length(), chars);
974 		chars += 2;
975 		for (int j = 0; j < s.length(); j++) {
976 			encode_uint16(s[j], chars);
977 			chars += 2;
978 		}
979 		encode_uint16(0, chars);
980 		chars += 2;
981 	}
982 
983 	for (int i = 0; i < stable_extra.size(); i++) {
984 		ret.push_back(stable_extra[i]);
985 	}
986 
987 	//pad
988 	while (ret.size() % 4)
989 		ret.push_back(0);
990 
991 	uint32_t new_stable_end = ret.size();
992 
993 	uint32_t extra = (p_manifest.size() - string_table_ends);
994 	ret.resize(new_stable_end + extra);
995 	for (int i = 0; i < extra; i++)
996 		ret[new_stable_end + i] = p_manifest[string_table_ends + i];
997 
998 	while (ret.size() % 4)
999 		ret.push_back(0);
1000 	encode_uint32(ret.size(), &ret[4]); //update new file size
1001 
1002 	encode_uint32(new_stable_end - 8, &ret[12]); //update new string table size
1003 	encode_uint32(string_table.size(), &ret[16]); //update new number of strings
1004 	encode_uint32(string_data_offset - 8, &ret[28]); //update new string data offset
1005 	//print_line("file size: "+itos(ret.size()));
1006 
1007 	p_manifest = ret;
1008 
1009 #if 0
1010 	uint32_t header[9];
1011 	for(int i=0;i<9;i++) {
1012 		header[i]=decode_uint32(&p_manifest[i*4]);
1013 	}
1014 
1015 	//print_line("STO: "+itos(header[3]));
1016 	uint32_t st_offset=9*4;
1017 	//ERR_FAIL_COND(header[3]!=0x24)
1018 	uint32_t string_count=header[4];
1019 
1020 
1021 	string_table.resize(string_count);
1022 
1023 	for(int i=0;i<string_count;i++) {
1024 
1025 		uint32_t string_at = decode_uint32(&p_manifest[st_offset+i*4]);
1026 		string_at+=st_offset+string_count*4;
1027 		uint32_t len = decode_uint16(&p_manifest[string_at]);
1028 		Vector<CharType> ucstring;
1029 		ucstring.resize(len+1);
1030 		for(int j=0;j<len;j++) {
1031 			uint16_t c=decode_uint16(&p_manifest[string_at+2+2*j]);
1032 			ucstring[j]=c;
1033 		}
1034 		ucstring[len]=0;
1035 		string_table[i]=ucstring.ptr();
1036 	}
1037 
1038 #endif
1039 }
1040 
save_apk_file(void * p_userdata,const String & p_path,const Vector<uint8_t> & p_data,int p_file,int p_total)1041 Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
1042 
1043 	APKExportData *ed = (APKExportData *)p_userdata;
1044 	String dst_path = p_path;
1045 	dst_path = dst_path.replace_first("res://", "assets/");
1046 
1047 	zipOpenNewFileInZip(ed->apk,
1048 			dst_path.utf8().get_data(),
1049 			NULL,
1050 			NULL,
1051 			0,
1052 			NULL,
1053 			0,
1054 			NULL,
1055 			_should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0,
1056 			Z_DEFAULT_COMPRESSION);
1057 
1058 	zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
1059 	zipCloseFileInZip(ed->apk);
1060 	ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total);
1061 	return OK;
1062 }
1063 
_should_compress_asset(const String & p_path,const Vector<uint8_t> & p_data)1064 bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) {
1065 
1066 	/*
1067 	 *  By not compressing files with little or not benefit in doing so,
1068 	 *  a performance gain is expected at runtime. Moreover, if the APK is
1069 	 *  zip-aligned, assets stored as they are can be efficiently read by
1070 	 *  Android by memory-mapping them.
1071 	 */
1072 
1073 	// -- Unconditional uncompress to mimic AAPT plus some other
1074 
1075 	static const char *unconditional_compress_ext[] = {
1076 		// From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp
1077 		// These formats are already compressed, or don't compress well:
1078 		".jpg", ".jpeg", ".png", ".gif",
1079 		".wav", ".mp2", ".mp3", ".ogg", ".aac",
1080 		".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
1081 		".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
1082 		".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
1083 		".amr", ".awb", ".wma", ".wmv",
1084 		// Godot-specific:
1085 		".webp", // Same reasoning as .png
1086 		".cfb", // Don't let small config files slow-down startup
1087 		// Trailer for easier processing
1088 		NULL
1089 	};
1090 
1091 	for (const char **ext = unconditional_compress_ext; *ext; ++ext) {
1092 		if (p_path.to_lower().ends_with(String(*ext))) {
1093 			return false;
1094 		}
1095 	}
1096 
1097 	// -- Compressed resource?
1098 
1099 	if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') {
1100 		// Already compressed
1101 		return false;
1102 	}
1103 
1104 	// --- TODO: Decide on texture resources according to their image compression setting
1105 
1106 	return true;
1107 }
1108 
export_project(const String & p_path,bool p_debug,int p_flags)1109 Error EditorExportPlatformAndroid::export_project(const String &p_path, bool p_debug, int p_flags) {
1110 
1111 	String src_apk;
1112 
1113 	EditorProgress ep("export", "Exporting for Android", 105);
1114 
1115 	if (p_debug)
1116 		src_apk = custom_debug_package;
1117 	else
1118 		src_apk = custom_release_package;
1119 
1120 	if (src_apk == "") {
1121 		String err;
1122 		if (p_debug) {
1123 			src_apk = find_export_template("android_debug.apk", &err);
1124 		} else {
1125 			src_apk = find_export_template("android_release.apk", &err);
1126 		}
1127 		if (src_apk == "") {
1128 			EditorNode::add_io_error(err);
1129 			return ERR_FILE_NOT_FOUND;
1130 		}
1131 	}
1132 
1133 	FileAccess *src_f = NULL;
1134 	zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
1135 
1136 	ep.step("Creating APK", 0);
1137 
1138 	unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io);
1139 	if (!pkg) {
1140 
1141 		EditorNode::add_io_error("Could not find template APK to export:\n" + src_apk);
1142 		return ERR_FILE_NOT_FOUND;
1143 	}
1144 
1145 	ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN);
1146 	int ret = unzGoToFirstFile(pkg);
1147 
1148 	zlib_filefunc_def io2 = io;
1149 	FileAccess *dst_f = NULL;
1150 	io2.opaque = &dst_f;
1151 	String unaligned_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpexport-unaligned.apk";
1152 	zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
1153 
1154 	while (ret == UNZ_OK) {
1155 
1156 		//get filename
1157 		unz_file_info info;
1158 		char fname[16384];
1159 		ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
1160 
1161 		bool skip = false;
1162 
1163 		String file = fname;
1164 
1165 		Vector<uint8_t> data;
1166 		data.resize(info.uncompressed_size);
1167 
1168 		//read
1169 		unzOpenCurrentFile(pkg);
1170 		unzReadCurrentFile(pkg, data.ptr(), data.size());
1171 		unzCloseCurrentFile(pkg);
1172 
1173 		//write
1174 
1175 		if (file == "AndroidManifest.xml") {
1176 
1177 			_fix_manifest(data, p_flags & (EXPORT_DUMB_CLIENT | EXPORT_REMOTE_DEBUG));
1178 		}
1179 
1180 		if (file == "resources.arsc") {
1181 
1182 			_fix_resources(data);
1183 		}
1184 
1185 		if (file == "res/drawable/icon.png") {
1186 			bool found = false;
1187 
1188 			if (this->icon != "" && this->icon.ends_with(".png")) {
1189 
1190 				FileAccess *f = FileAccess::open(this->icon, FileAccess::READ);
1191 				if (f) {
1192 
1193 					data.resize(f->get_len());
1194 					f->get_buffer(data.ptr(), data.size());
1195 					memdelete(f);
1196 					found = true;
1197 				}
1198 			}
1199 
1200 			if (!found) {
1201 
1202 				String appicon = Globals::get_singleton()->get("application/icon");
1203 				if (appicon != "" && appicon.ends_with(".png")) {
1204 					FileAccess *f = FileAccess::open(appicon, FileAccess::READ);
1205 					if (f) {
1206 						data.resize(f->get_len());
1207 						f->get_buffer(data.ptr(), data.size());
1208 						memdelete(f);
1209 					}
1210 				}
1211 			}
1212 		}
1213 
1214 		if (file == "lib/x86/libgodot_android.so" && !export_x86) {
1215 			skip = true;
1216 		}
1217 
1218 		if (file == "lib/x86_64/libgodot_android.so" && !export_x86_64) {
1219 			skip = true;
1220 		}
1221 
1222 		if (file.match("lib/armeabi*/libgodot_android.so") && !export_arm) {
1223 			skip = true;
1224 		}
1225 
1226 		if (file.match("lib/arm64*/libgodot_android.so") && !export_arm64) {
1227 			skip = true;
1228 		}
1229 
1230 		if (file.begins_with("META-INF") && _signed) {
1231 			skip = true;
1232 		}
1233 
1234 		print_line("ADDING: " + file);
1235 
1236 		if (!skip) {
1237 
1238 			// Respect decision on compression made by AAPT for the export template
1239 			const bool uncompressed = info.compression_method == 0;
1240 
1241 			zipOpenNewFileInZip(unaligned_apk,
1242 					file.utf8().get_data(),
1243 					NULL,
1244 					NULL,
1245 					0,
1246 					NULL,
1247 					0,
1248 					NULL,
1249 					uncompressed ? 0 : Z_DEFLATED,
1250 					Z_DEFAULT_COMPRESSION);
1251 
1252 			zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size());
1253 			zipCloseFileInZip(unaligned_apk);
1254 		}
1255 
1256 		ret = unzGoToNextFile(pkg);
1257 	}
1258 
1259 	ep.step("Adding Files..", 1);
1260 	Error err = OK;
1261 	Vector<String> cl = cmdline.strip_edges().split(" ");
1262 	for (int i = 0; i < cl.size(); i++) {
1263 		if (cl[i].strip_edges().length() == 0) {
1264 			cl.remove(i);
1265 			i--;
1266 		}
1267 	}
1268 
1269 	gen_export_flags(cl, p_flags);
1270 
1271 	if (p_flags & EXPORT_DUMB_CLIENT) {
1272 
1273 		/*String host = EditorSettings::get_singleton()->get("file_server/host");
1274 		int port = EditorSettings::get_singleton()->get("file_server/post");
1275 		String passwd = EditorSettings::get_singleton()->get("file_server/password");
1276 		cl.push_back("-rfs");
1277 		cl.push_back(host+":"+itos(port));
1278 		if (passwd!="") {
1279 			cl.push_back("-rfs_pass");
1280 			cl.push_back(passwd);
1281 		}*/
1282 
1283 	} else {
1284 		//all files
1285 
1286 		if (apk_expansion) {
1287 
1288 			String apkfname = "main." + itos(version_code) + "." + get_package_name() + ".obb";
1289 			String fullpath = p_path.get_base_dir().plus_file(apkfname);
1290 			FileAccess *pf = FileAccess::open(fullpath, FileAccess::WRITE);
1291 			if (!pf) {
1292 				EditorNode::add_io_error("Could not write expansion package file: " + apkfname);
1293 				return OK;
1294 			}
1295 			err = save_pack(pf);
1296 			memdelete(pf);
1297 
1298 			cl.push_back("-use_apk_expansion");
1299 			cl.push_back("-apk_expansion_md5");
1300 			cl.push_back(FileAccess::get_md5(fullpath));
1301 			cl.push_back("-apk_expansion_key");
1302 			cl.push_back(apk_expansion_pkey.strip_edges());
1303 
1304 		} else {
1305 
1306 			APKExportData ed;
1307 			ed.ep = &ep;
1308 			ed.apk = unaligned_apk;
1309 
1310 			err = export_project_files(save_apk_file, &ed, false);
1311 		}
1312 	}
1313 
1314 	if (use_32_fb)
1315 		cl.push_back("-use_depth_32");
1316 
1317 	if (immersive)
1318 		cl.push_back("-use_immersive");
1319 
1320 	if (cl.size()) {
1321 		//add comandline
1322 		Vector<uint8_t> clf;
1323 		clf.resize(4);
1324 		encode_uint32(cl.size(), &clf[0]);
1325 		for (int i = 0; i < cl.size(); i++) {
1326 
1327 			CharString txt = cl[i].utf8();
1328 			int base = clf.size();
1329 			clf.resize(base + 4 + txt.length());
1330 			encode_uint32(txt.length(), &clf[base]);
1331 			copymem(&clf[base + 4], txt.ptr(), txt.length());
1332 			print_line(itos(i) + " param: " + cl[i]);
1333 		}
1334 
1335 		zipOpenNewFileInZip(unaligned_apk,
1336 				"assets/_cl_",
1337 				NULL,
1338 				NULL,
1339 				0,
1340 				NULL,
1341 				0,
1342 				NULL,
1343 				0, // No compress (little size gain and potentially slower startup)
1344 				Z_DEFAULT_COMPRESSION);
1345 
1346 		zipWriteInFileInZip(unaligned_apk, clf.ptr(), clf.size());
1347 		zipCloseFileInZip(unaligned_apk);
1348 	}
1349 
1350 	zipClose(unaligned_apk, NULL);
1351 	unzClose(pkg);
1352 
1353 	if (err) {
1354 		return err;
1355 	}
1356 
1357 	if (_signed) {
1358 
1359 		String jarsigner = EditorSettings::get_singleton()->get("android/jarsigner");
1360 		if (!FileAccess::exists(jarsigner)) {
1361 			EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the editor settings.\nResulting apk is unsigned.");
1362 			return OK;
1363 		}
1364 
1365 		String keystore;
1366 		String password;
1367 		String user;
1368 		if (p_debug) {
1369 			keystore = EditorSettings::get_singleton()->get("android/debug_keystore");
1370 			password = EditorSettings::get_singleton()->get("android/debug_keystore_pass");
1371 			user = EditorSettings::get_singleton()->get("android/debug_keystore_user");
1372 
1373 			ep.step("Signing Debug APK..", 103);
1374 
1375 		} else {
1376 			keystore = release_keystore;
1377 			password = release_password;
1378 			user = release_username;
1379 
1380 			ep.step("Signing Release APK..", 103);
1381 		}
1382 
1383 		if (!FileAccess::exists(keystore)) {
1384 			EditorNode::add_io_error("Could not find keystore, unable to export.");
1385 			return ERR_FILE_CANT_OPEN;
1386 		}
1387 
1388 		List<String> args;
1389 		args.push_back("-digestalg");
1390 		args.push_back("SHA-256");
1391 		args.push_back("-sigalg");
1392 		args.push_back("SHA256withRSA");
1393 		String tsa_url = EditorSettings::get_singleton()->get("android/timestamping_authority_url");
1394 		if (tsa_url != "") {
1395 			args.push_back("-tsa");
1396 			args.push_back(tsa_url);
1397 		}
1398 		args.push_back("-verbose");
1399 		args.push_back("-keystore");
1400 		args.push_back(keystore);
1401 		args.push_back("-storepass");
1402 		args.push_back(password);
1403 		args.push_back(unaligned_path);
1404 		args.push_back(user);
1405 		int retval;
1406 		int err = OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
1407 		if (retval) {
1408 			EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval));
1409 			return ERR_CANT_CREATE;
1410 		}
1411 
1412 		ep.step("Verifying APK..", 104);
1413 
1414 		args.clear();
1415 		args.push_back("-verify");
1416 		args.push_back(unaligned_path);
1417 		args.push_back("-verbose");
1418 
1419 		err = OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval);
1420 		if (retval) {
1421 			EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use jarsigner from Java 6.");
1422 			return ERR_CANT_CREATE;
1423 		}
1424 	}
1425 
1426 	// Let's zip-align (must be done after signing)
1427 
1428 	static const int ZIP_ALIGNMENT = 4;
1429 
1430 	ep.step("Aligning APK..", 105);
1431 
1432 	unzFile tmp_unaligned = unzOpen2(unaligned_path.utf8().get_data(), &io);
1433 	if (!tmp_unaligned) {
1434 
1435 		EditorNode::add_io_error("Could not find temp unaligned APK.");
1436 		return ERR_FILE_NOT_FOUND;
1437 	}
1438 
1439 	ERR_FAIL_COND_V(!tmp_unaligned, ERR_CANT_OPEN);
1440 	ret = unzGoToFirstFile(tmp_unaligned);
1441 
1442 	io2 = io;
1443 	dst_f = NULL;
1444 	io2.opaque = &dst_f;
1445 	zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
1446 
1447 	// Take files from the unaligned APK and write them out to the aligned one
1448 	// in raw mode, i.e. not uncompressing and recompressing, aligning them as needed,
1449 	// following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp
1450 	int bias = 0;
1451 	while (ret == UNZ_OK) {
1452 
1453 		unz_file_info info;
1454 		memset(&info, 0, sizeof(info));
1455 
1456 		char fname[16384];
1457 		char extra[16384];
1458 		ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, NULL, 0);
1459 
1460 		String file = fname;
1461 
1462 		Vector<uint8_t> data;
1463 		data.resize(info.compressed_size);
1464 
1465 		// read
1466 		int method, level;
1467 		unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read
1468 		long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned);
1469 		unzReadCurrentFile(tmp_unaligned, data.ptr(), data.size());
1470 		unzCloseCurrentFile(tmp_unaligned);
1471 
1472 		// align
1473 		int padding = 0;
1474 		if (!info.compression_method) {
1475 			// Uncompressed file => Align
1476 			long new_offset = file_offset + bias;
1477 			padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT;
1478 		}
1479 
1480 		memset(extra + info.size_file_extra, 0, padding);
1481 
1482 		// write
1483 		zipOpenNewFileInZip2(final_apk,
1484 				file.utf8().get_data(),
1485 				NULL,
1486 				extra,
1487 				info.size_file_extra + padding,
1488 				NULL,
1489 				0,
1490 				NULL,
1491 				method,
1492 				level,
1493 				1); // raw write
1494 		zipWriteInFileInZip(final_apk, data.ptr(), data.size());
1495 		zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc);
1496 
1497 		bias += padding;
1498 
1499 		ret = unzGoToNextFile(tmp_unaligned);
1500 	}
1501 
1502 	zipClose(final_apk, NULL);
1503 	unzClose(tmp_unaligned);
1504 
1505 	if (err) {
1506 		return err;
1507 	}
1508 
1509 	return OK;
1510 }
1511 
poll_devices()1512 bool EditorExportPlatformAndroid::poll_devices() {
1513 
1514 	bool dc = devices_changed;
1515 	devices_changed = false;
1516 	return dc;
1517 }
1518 
get_device_count() const1519 int EditorExportPlatformAndroid::get_device_count() const {
1520 
1521 	device_lock->lock();
1522 	int dc = devices.size();
1523 	device_lock->unlock();
1524 
1525 	return dc;
1526 }
1527 
get_device_name(int p_device) const1528 String EditorExportPlatformAndroid::get_device_name(int p_device) const {
1529 
1530 	ERR_FAIL_INDEX_V(p_device, devices.size(), "");
1531 	device_lock->lock();
1532 	String s = devices[p_device].name;
1533 	device_lock->unlock();
1534 	return s;
1535 }
1536 
get_device_info(int p_device) const1537 String EditorExportPlatformAndroid::get_device_info(int p_device) const {
1538 
1539 	ERR_FAIL_INDEX_V(p_device, devices.size(), "");
1540 	device_lock->lock();
1541 	String s = devices[p_device].description;
1542 	device_lock->unlock();
1543 	return s;
1544 }
1545 
_device_poll_thread(void * ud)1546 void EditorExportPlatformAndroid::_device_poll_thread(void *ud) {
1547 
1548 	EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
1549 
1550 	while (!ea->quit_request) {
1551 
1552 		String adb = EditorSettings::get_singleton()->get("android/adb");
1553 		if (FileAccess::exists(adb)) {
1554 
1555 			String devices;
1556 			List<String> args;
1557 			args.push_back("devices");
1558 			int ec;
1559 			Error err = OS::get_singleton()->execute(adb, args, true, NULL, &devices, &ec);
1560 			Vector<String> ds = devices.split("\n");
1561 			Vector<String> ldevices;
1562 			for (int i = 1; i < ds.size(); i++) {
1563 
1564 				String d = ds[i];
1565 				int dpos = d.find("device");
1566 				if (dpos == -1)
1567 					continue;
1568 				d = d.substr(0, dpos).strip_edges();
1569 				//			print_line("found devuce: "+d);
1570 				ldevices.push_back(d);
1571 			}
1572 
1573 			ea->device_lock->lock();
1574 
1575 			bool different = false;
1576 
1577 			if (devices.size() != ldevices.size()) {
1578 
1579 				different = true;
1580 			} else {
1581 
1582 				for (int i = 0; i < ea->devices.size(); i++) {
1583 
1584 					if (ea->devices[i].id != ldevices[i]) {
1585 						different = true;
1586 						break;
1587 					}
1588 				}
1589 			}
1590 
1591 			if (different) {
1592 
1593 				Vector<Device> ndevices;
1594 
1595 				for (int i = 0; i < ldevices.size(); i++) {
1596 
1597 					Device d;
1598 					d.id = ldevices[i];
1599 					for (int j = 0; j < ea->devices.size(); j++) {
1600 						if (ea->devices[j].id == ldevices[i]) {
1601 							d.description = ea->devices[j].description;
1602 							d.name = ea->devices[j].name;
1603 							d.api_level = ea->devices[j].api_level;
1604 						}
1605 					}
1606 
1607 					if (d.description == "") {
1608 						//in the oven, request!
1609 						args.clear();
1610 						args.push_back("-s");
1611 						args.push_back(d.id);
1612 						args.push_back("shell");
1613 						args.push_back("getprop");
1614 						int ec;
1615 						String dp;
1616 
1617 						Error err = OS::get_singleton()->execute(adb, args, true, NULL, &dp, &ec);
1618 
1619 						Vector<String> props = dp.split("\n");
1620 						String vendor;
1621 						String device;
1622 						d.description + "Device ID: " + d.id + "\n";
1623 						d.api_level = 0;
1624 						for (int j = 0; j < props.size(); j++) {
1625 
1626 							// got information by `shell cat /system/build.prop` before and its format is "property=value"
1627 							// it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above
1628 							// its format is "[property]: [value]" so changed it as like build.prop
1629 							String p = props[j];
1630 							p = p.replace("]: ", "=");
1631 							p = p.replace("[", "");
1632 							p = p.replace("]", "");
1633 
1634 							if (p.begins_with("ro.product.model=")) {
1635 								device = p.get_slice("=", 1).strip_edges();
1636 							} else if (p.begins_with("ro.product.brand=")) {
1637 								vendor = p.get_slice("=", 1).strip_edges().capitalize();
1638 							} else if (p.begins_with("ro.build.display.id=")) {
1639 								d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n";
1640 							} else if (p.begins_with("ro.build.version.release=")) {
1641 								d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n";
1642 							} else if (p.begins_with("ro.build.version.sdk=")) {
1643 								d.api_level = p.get_slice("=", 1).to_int();
1644 							} else if (p.begins_with("ro.product.cpu.abi=")) {
1645 								d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n";
1646 							} else if (p.begins_with("ro.product.manufacturer=")) {
1647 								d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n";
1648 							} else if (p.begins_with("ro.board.platform=")) {
1649 								d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n";
1650 							} else if (p.begins_with("ro.opengles.version=")) {
1651 								uint32_t opengl = p.get_slice("=", 1).to_int();
1652 								d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n";
1653 							}
1654 						}
1655 
1656 						d.name = vendor + " " + device;
1657 						//					print_line("name: "+d.name);
1658 						//					print_line("description: "+d.description);
1659 					}
1660 
1661 					ndevices.push_back(d);
1662 				}
1663 
1664 				ea->devices = ndevices;
1665 				ea->devices_changed = true;
1666 			}
1667 
1668 			ea->device_lock->unlock();
1669 		}
1670 
1671 		uint64_t wait = 3000000;
1672 		uint64_t time = OS::get_singleton()->get_ticks_usec();
1673 		while (OS::get_singleton()->get_ticks_usec() - time < wait) {
1674 			OS::get_singleton()->delay_usec(1000);
1675 			if (ea->quit_request)
1676 				break;
1677 		}
1678 	}
1679 
1680 	if (EditorSettings::get_singleton()->get("android/shutdown_adb_on_exit")) {
1681 		String adb = EditorSettings::get_singleton()->get("android/adb");
1682 		if (!FileAccess::exists(adb)) {
1683 			return; //adb not configured
1684 		}
1685 
1686 		List<String> args;
1687 		args.push_back("kill-server");
1688 		OS::get_singleton()->execute(adb, args, true);
1689 	};
1690 }
1691 
run(int p_device,int p_flags)1692 Error EditorExportPlatformAndroid::run(int p_device, int p_flags) {
1693 
1694 	ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
1695 	device_lock->lock();
1696 
1697 	EditorProgress ep("run", "Running on " + devices[p_device].name, 3);
1698 
1699 	String adb = EditorSettings::get_singleton()->get("android/adb");
1700 	if (adb == "") {
1701 
1702 		EditorNode::add_io_error("ADB executable not configured in settings, can't run.");
1703 		device_lock->unlock();
1704 		return ERR_UNCONFIGURED;
1705 	}
1706 
1707 	//export_temp
1708 	ep.step("Exporting APK", 0);
1709 
1710 	const bool use_remote = (p_flags & EXPORT_REMOTE_DEBUG) || (p_flags & EXPORT_DUMB_CLIENT);
1711 	const bool use_reverse = devices[p_device].api_level >= 21;
1712 
1713 	if (use_reverse)
1714 		p_flags |= EXPORT_REMOTE_DEBUG_LOCALHOST;
1715 
1716 	String export_to = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpexport.apk";
1717 	Error err = export_project(export_to, true, p_flags);
1718 	if (err) {
1719 		device_lock->unlock();
1720 		return err;
1721 	}
1722 
1723 	List<String> args;
1724 	int rv;
1725 
1726 	if (remove_prev) {
1727 		ep.step("Uninstalling..", 1);
1728 
1729 		print_line("Uninstalling previous version: " + devices[p_device].name);
1730 
1731 		args.push_back("-s");
1732 		args.push_back(devices[p_device].id);
1733 		args.push_back("uninstall");
1734 		args.push_back(get_package_name());
1735 
1736 		err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1737 #if 0
1738 	if (err || rv!=0) {
1739 		EditorNode::add_io_error("Could not install to device.");
1740 		device_lock->unlock();
1741 		return ERR_CANT_CREATE;
1742 	}
1743 #endif
1744 	}
1745 
1746 	print_line("Installing into device (please wait..): " + devices[p_device].name);
1747 	ep.step("Installing to Device (please wait..)..", 2);
1748 
1749 	args.clear();
1750 	args.push_back("-s");
1751 	args.push_back(devices[p_device].id);
1752 	args.push_back("install");
1753 	args.push_back("-r");
1754 	args.push_back(export_to);
1755 
1756 	err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1757 	if (err || rv != 0) {
1758 		EditorNode::add_io_error("Could not install to device.");
1759 		device_lock->unlock();
1760 		return ERR_CANT_CREATE;
1761 	}
1762 
1763 	if (use_remote) {
1764 		if (use_reverse) {
1765 
1766 			static const char *const msg = "** Device API >= 21; debugging over USB **";
1767 			EditorNode::get_singleton()->get_log()->add_message(msg);
1768 			print_line(String(msg).to_upper());
1769 
1770 			args.clear();
1771 			args.push_back("-s");
1772 			args.push_back(devices[p_device].id);
1773 			args.push_back("reverse");
1774 			args.push_back("--remove-all");
1775 			err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1776 
1777 			if (p_flags & EXPORT_REMOTE_DEBUG) {
1778 
1779 				int dbg_port = (int)EditorSettings::get_singleton()->get("network/debug_port");
1780 				args.clear();
1781 				args.push_back("-s");
1782 				args.push_back(devices[p_device].id);
1783 				args.push_back("reverse");
1784 				args.push_back("tcp:" + itos(dbg_port));
1785 				args.push_back("tcp:" + itos(dbg_port));
1786 
1787 				err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1788 				print_line("Reverse result: " + itos(rv));
1789 			}
1790 
1791 			if (p_flags & EXPORT_DUMB_CLIENT) {
1792 
1793 				int fs_port = EditorSettings::get_singleton()->get("file_server/port");
1794 
1795 				args.clear();
1796 				args.push_back("-s");
1797 				args.push_back(devices[p_device].id);
1798 				args.push_back("reverse");
1799 				args.push_back("tcp:" + itos(fs_port));
1800 				args.push_back("tcp:" + itos(fs_port));
1801 
1802 				err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1803 				print_line("Reverse result2: " + itos(rv));
1804 			}
1805 		} else {
1806 
1807 			static const char *const msg = "** Device API < 21; debugging over Wi-Fi **";
1808 			EditorNode::get_singleton()->get_log()->add_message(msg);
1809 			print_line(String(msg).to_upper());
1810 		}
1811 	}
1812 
1813 	ep.step("Running on Device..", 3);
1814 	args.clear();
1815 	args.push_back("-s");
1816 	args.push_back(devices[p_device].id);
1817 	args.push_back("shell");
1818 	args.push_back("am");
1819 	args.push_back("start");
1820 	if (bool(EDITOR_DEF("android/force_system_user", false)) && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17
1821 		args.push_back("--user");
1822 		args.push_back("0");
1823 	}
1824 	args.push_back("-a");
1825 	args.push_back("android.intent.action.MAIN");
1826 	args.push_back("-n");
1827 	args.push_back(get_package_name() + "/org.godotengine.godot.Godot");
1828 
1829 	err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv);
1830 	if (err || rv != 0) {
1831 		EditorNode::add_io_error("Could not execute on device.");
1832 		device_lock->unlock();
1833 		return ERR_CANT_CREATE;
1834 	}
1835 	device_lock->unlock();
1836 	return OK;
1837 }
1838 
get_package_name()1839 String EditorExportPlatformAndroid::get_package_name() {
1840 
1841 	String pname = package;
1842 	String basename = Globals::get_singleton()->get("application/name");
1843 	basename = basename.to_lower();
1844 
1845 	String name;
1846 	bool first = true;
1847 	for (int i = 0; i < basename.length(); i++) {
1848 		CharType c = basename[i];
1849 		if (c >= '0' && c <= '9' && first) {
1850 			continue;
1851 		}
1852 		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
1853 			name += String::chr(c);
1854 			first = false;
1855 		}
1856 	}
1857 	if (name == "")
1858 		name = "noname";
1859 
1860 	pname = pname.replace("$genname", name);
1861 	return pname;
1862 }
1863 
EditorExportPlatformAndroid()1864 EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
1865 
1866 	version_code = 1;
1867 	version_name = "1.0";
1868 	package = "org.godotengine.$genname";
1869 	name = "";
1870 	_signed = true;
1871 	apk_expansion = false;
1872 	device_lock = Mutex::create();
1873 	quit_request = false;
1874 	orientation = 0;
1875 	remove_prev = true;
1876 	use_32_fb = true;
1877 	immersive = true;
1878 
1879 	export_arm = true;
1880 	export_arm64 = true;
1881 	export_x86 = false;
1882 	export_x86_64 = false;
1883 
1884 	device_thread = Thread::create(_device_poll_thread, this);
1885 	devices_changed = true;
1886 
1887 	Image img(_android_logo);
1888 	logo = Ref<ImageTexture>(memnew(ImageTexture));
1889 	logo->create_from_image(img);
1890 
1891 	for (int i = 0; i < 4; i++)
1892 		screen_support[i] = true;
1893 }
1894 
can_export(String * r_error) const1895 bool EditorExportPlatformAndroid::can_export(String *r_error) const {
1896 
1897 	bool valid = true;
1898 	String adb = EditorSettings::get_singleton()->get("android/adb");
1899 	String err;
1900 
1901 	if (!FileAccess::exists(adb)) {
1902 
1903 		valid = false;
1904 		err += "ADB executable not configured in editor settings.\n";
1905 	}
1906 
1907 	String js = EditorSettings::get_singleton()->get("android/jarsigner");
1908 
1909 	if (!FileAccess::exists(js)) {
1910 
1911 		valid = false;
1912 		err += "OpenJDK 6 jarsigner not configured in editor settings.\n";
1913 	}
1914 
1915 	String dk = EditorSettings::get_singleton()->get("android/debug_keystore");
1916 
1917 	if (!FileAccess::exists(dk)) {
1918 
1919 		valid = false;
1920 		err += "Debug Keystore not configured in editor settings.\n";
1921 	}
1922 
1923 	if (!exists_export_template("android_debug.apk") || !exists_export_template("android_release.apk")) {
1924 		valid = false;
1925 		err += "No export templates found.\nDownload and install export templates.\n";
1926 	}
1927 
1928 	if (custom_debug_package != "" && !FileAccess::exists(custom_debug_package)) {
1929 		valid = false;
1930 		err += "Custom debug package not found.\n";
1931 	}
1932 
1933 	if (custom_release_package != "" && !FileAccess::exists(custom_release_package)) {
1934 		valid = false;
1935 		err += "Custom release package not found.\n";
1936 	}
1937 
1938 	if (apk_expansion) {
1939 
1940 		//if (apk_expansion_salt=="") {
1941 		//	valid=false;
1942 		//	err+="Invalid SALT for apk expansion.\n";
1943 		//}
1944 		if (apk_expansion_pkey == "") {
1945 			valid = false;
1946 			err += "Invalid public key for apk expansion.\n";
1947 		}
1948 	}
1949 
1950 	if (r_error)
1951 		*r_error = err;
1952 
1953 	return valid;
1954 }
1955 
~EditorExportPlatformAndroid()1956 EditorExportPlatformAndroid::~EditorExportPlatformAndroid() {
1957 
1958 	quit_request = true;
1959 	Thread::wait_to_finish(device_thread);
1960 	memdelete(device_lock);
1961 	memdelete(device_thread);
1962 }
1963 
register_android_exporter()1964 void register_android_exporter() {
1965 
1966 	String exe_ext = OS::get_singleton()->get_name() == "Windows" ? "exe" : "";
1967 	EDITOR_DEF("android/adb", "");
1968 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "android/adb", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
1969 	EDITOR_DEF("android/jarsigner", "");
1970 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
1971 	EDITOR_DEF("android/debug_keystore", "");
1972 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "keystore"));
1973 	EDITOR_DEF("android/debug_keystore_user", "androiddebugkey");
1974 	EDITOR_DEF("android/debug_keystore_pass", "android");
1975 	//EDITOR_DEF("android/release_keystore","");
1976 	//EDITOR_DEF("android/release_username","");
1977 	//EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"android/release_keystore",PROPERTY_HINT_GLOBAL_FILE,"*.keystore"));
1978 	EDITOR_DEF("android/force_system_user", false);
1979 	EDITOR_DEF("android/timestamping_authority_url", "");
1980 	EDITOR_DEF("android/shutdown_adb_on_exit", true);
1981 
1982 	Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
1983 	EditorImportExport::get_singleton()->add_export_platform(exporter);
1984 }
1985