mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	Merge pull request #44645 from m4gr3d/update_apk_signing_logic
Update the logic to sign prebuilt Godot Android apks
This commit is contained in:
		
						commit
						3433780c49
					
				
					 1 changed files with 130 additions and 80 deletions
				
			
		|  | @ -302,7 +302,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Check for devices updates
 | 			// Check for devices updates
 | ||||||
| 			String adb = EditorSettings::get_singleton()->get("export/android/adb"); | 			String adb = get_adb_path(); | ||||||
| 			if (FileAccess::exists(adb)) { | 			if (FileAccess::exists(adb)) { | ||||||
| 				String devices; | 				String devices; | ||||||
| 				List<String> args; | 				List<String> args; | ||||||
|  | @ -425,7 +425,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { | 		if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { | ||||||
| 			String adb = EditorSettings::get_singleton()->get("export/android/adb"); | 			String adb = get_adb_path(); | ||||||
| 			if (!FileAccess::exists(adb)) { | 			if (!FileAccess::exists(adb)) { | ||||||
| 				return; //adb not configured
 | 				return; //adb not configured
 | ||||||
| 			} | 			} | ||||||
|  | @ -1752,7 +1752,7 @@ public: | ||||||
| 
 | 
 | ||||||
| 		EditorProgress ep("run", "Running on " + devices[p_device].name, 3); | 		EditorProgress ep("run", "Running on " + devices[p_device].name, 3); | ||||||
| 
 | 
 | ||||||
| 		String adb = EditorSettings::get_singleton()->get("export/android/adb"); | 		String adb = get_adb_path(); | ||||||
| 
 | 
 | ||||||
| 		// Export_temp APK.
 | 		// Export_temp APK.
 | ||||||
| 		if (ep.step("Exporting APK...", 0)) { | 		if (ep.step("Exporting APK...", 0)) { | ||||||
|  | @ -1899,6 +1899,55 @@ public: | ||||||
| 		return run_icon; | 		return run_icon; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	static String get_adb_path() { | ||||||
|  | 		String exe_ext = ""; | ||||||
|  | 		if (OS::get_singleton()->get_name() == "Windows") { | ||||||
|  | 			exe_ext = ".exe"; | ||||||
|  | 		} | ||||||
|  | 		String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); | ||||||
|  | 		return sdk_path.plus_file("platform-tools/adb" + exe_ext); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	static String get_apksigner_path() { | ||||||
|  | 		String exe_ext = ""; | ||||||
|  | 		if (OS::get_singleton()->get_name() == "Windows") { | ||||||
|  | 			exe_ext = ".bat"; | ||||||
|  | 		} | ||||||
|  | 		String apksigner_command_name = "apksigner" + exe_ext; | ||||||
|  | 		String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); | ||||||
|  | 		String apksigner_path = ""; | ||||||
|  | 
 | ||||||
|  | 		Error errn; | ||||||
|  | 		String build_tools_dir = sdk_path.plus_file("build-tools"); | ||||||
|  | 		DirAccessRef da = DirAccess::open(build_tools_dir, &errn); | ||||||
|  | 		if (errn != OK) { | ||||||
|  | 			print_error("Unable to open Android 'build-tools' directory."); | ||||||
|  | 			return apksigner_path; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// There are additional versions directories we need to go through.
 | ||||||
|  | 		da->list_dir_begin(); | ||||||
|  | 		String sub_dir = da->get_next(); | ||||||
|  | 		while (!sub_dir.is_empty()) { | ||||||
|  | 			if (!sub_dir.begins_with(".") && da->current_is_dir()) { | ||||||
|  | 				// Check if the tool is here.
 | ||||||
|  | 				String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name); | ||||||
|  | 				if (FileAccess::exists(tool_path)) { | ||||||
|  | 					apksigner_path = tool_path; | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			sub_dir = da->get_next(); | ||||||
|  | 		} | ||||||
|  | 		da->list_dir_end(); | ||||||
|  | 
 | ||||||
|  | 		if (apksigner_path.is_empty()) { | ||||||
|  | 			EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool.")); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return apksigner_path; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { | 	virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { | ||||||
| 		String err; | 		String err; | ||||||
| 		bool valid = false; | 		bool valid = false; | ||||||
|  | @ -1934,25 +1983,16 @@ public: | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			valid = exists_export_template("android_source.zip", &err); | 			valid = exists_export_template("android_source.zip", &err); | ||||||
|  | 
 | ||||||
|  | 			if (!FileAccess::exists("res://android/build/build.gradle")) { | ||||||
|  | 				err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; | ||||||
|  | 				valid = false; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		r_missing_templates = !valid; | 		r_missing_templates = !valid; | ||||||
| 
 | 
 | ||||||
| 		// Validate the rest of the configuration.
 | 		// Validate the rest of the configuration.
 | ||||||
| 
 | 
 | ||||||
| 		String adb = EditorSettings::get_singleton()->get("export/android/adb"); |  | ||||||
| 
 |  | ||||||
| 		if (!FileAccess::exists(adb)) { |  | ||||||
| 			valid = false; |  | ||||||
| 			err += TTR("ADB executable not configured in the Editor Settings.") + "\n"; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		String js = EditorSettings::get_singleton()->get("export/android/jarsigner"); |  | ||||||
| 
 |  | ||||||
| 		if (!FileAccess::exists(js)) { |  | ||||||
| 			valid = false; |  | ||||||
| 			err += TTR("OpenJDK jarsigner not configured in the Editor Settings.") + "\n"; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		String dk = p_preset->get("keystore/debug"); | 		String dk = p_preset->get("keystore/debug"); | ||||||
| 
 | 
 | ||||||
| 		if (!FileAccess::exists(dk)) { | 		if (!FileAccess::exists(dk)) { | ||||||
|  | @ -1970,25 +2010,45 @@ public: | ||||||
| 			err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; | 			err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (bool(p_preset->get("custom_template/use_custom_build"))) { | 		String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); | ||||||
| 			String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path"); |  | ||||||
| 		if (sdk_path == "") { | 		if (sdk_path == "") { | ||||||
| 				err += TTR("Custom build requires a valid Android SDK path in Editor Settings.") + "\n"; | 			err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; | ||||||
| 			valid = false; | 			valid = false; | ||||||
| 		} else { | 		} else { | ||||||
| 			Error errn; | 			Error errn; | ||||||
| 			// Check for the platform-tools directory.
 | 			// Check for the platform-tools directory.
 | ||||||
| 			DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); | 			DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); | ||||||
| 			if (errn != OK) { | 			if (errn != OK) { | ||||||
| 					err += TTR("Invalid Android SDK path for custom build in Editor Settings."); | 				err += TTR("Invalid Android SDK path in Editor Settings."); | ||||||
| 				err += TTR("Missing 'platform-tools' directory!"); | 				err += TTR("Missing 'platform-tools' directory!"); | ||||||
| 				err += "\n"; | 				err += "\n"; | ||||||
| 				valid = false; | 				valid = false; | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			// Validate that adb is available
 | ||||||
|  | 			String adb_path = get_adb_path(); | ||||||
|  | 			if (!FileAccess::exists(adb_path)) { | ||||||
|  | 				err += TTR("Unable to find Android SDK platform-tools' adb command."); | ||||||
|  | 				err += TTR("Please check in the Android SDK directory specified in Editor Settings."); | ||||||
|  | 				err += "\n"; | ||||||
|  | 				valid = false; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (!FileAccess::exists("res://android/build/build.gradle")) { | 			// Check for the build-tools directory.
 | ||||||
| 				err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; | 			DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn); | ||||||
|  | 			if (errn != OK) { | ||||||
|  | 				err += TTR("Invalid Android SDK path in Editor Settings."); | ||||||
|  | 				err += TTR("Missing 'build-tools' directory!"); | ||||||
|  | 				err += "\n"; | ||||||
|  | 				valid = false; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Validate that apksigner is available
 | ||||||
|  | 			String apksigner_path = get_apksigner_path(); | ||||||
|  | 			if (!FileAccess::exists(apksigner_path)) { | ||||||
|  | 				err += TTR("Unable to find Android SDK build-tools' apksigner command."); | ||||||
|  | 				err += TTR("Please check in the Android SDK directory specified in Editor Settings."); | ||||||
|  | 				err += "\n"; | ||||||
| 				valid = false; | 				valid = false; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -2171,16 +2231,16 @@ public: | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, String export_path, EditorProgress ep) { | 	Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { | ||||||
| 		int export_format = int(p_preset->get("custom_template/export_format")); | 		int export_format = int(p_preset->get("custom_template/export_format")); | ||||||
| 		String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; | 		String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; | ||||||
| 		String release_keystore = p_preset->get("keystore/release"); | 		String release_keystore = p_preset->get("keystore/release"); | ||||||
| 		String release_username = p_preset->get("keystore/release_user"); | 		String release_username = p_preset->get("keystore/release_user"); | ||||||
| 		String release_password = p_preset->get("keystore/release_password"); | 		String release_password = p_preset->get("keystore/release_password"); | ||||||
| 
 | 
 | ||||||
| 		String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner"); | 		String apksigner = get_apksigner_path(); | ||||||
| 		if (!FileAccess::exists(jarsigner)) { | 		if (!FileAccess::exists(apksigner)) { | ||||||
| 			EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting " + export_label + " is unsigned."); | 			EditorNode::add_io_error("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting " + export_label + " is unsigned."); | ||||||
| 			return OK; | 			return OK; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -2198,7 +2258,7 @@ public: | ||||||
| 				user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); | 				user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (ep.step("Signing debug " + export_label + "...", 103)) { | 			if (ep.step("Signing debug " + export_label + "...", 104)) { | ||||||
| 				return ERR_SKIP; | 				return ERR_SKIP; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -2207,7 +2267,7 @@ public: | ||||||
| 			password = release_password; | 			password = release_password; | ||||||
| 			user = release_username; | 			user = release_username; | ||||||
| 
 | 
 | ||||||
| 			if (ep.step("Signing release " + export_label + "...", 103)) { | 			if (ep.step("Signing release " + export_label + "...", 104)) { | ||||||
| 				return ERR_SKIP; | 				return ERR_SKIP; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -2218,43 +2278,34 @@ public: | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		List<String> args; | 		List<String> args; | ||||||
| 		args.push_back("-digestalg"); | 		args.push_back("sign"); | ||||||
| 		args.push_back("SHA-256"); | 		args.push_back("--verbose"); | ||||||
| 		args.push_back("-sigalg"); | 		args.push_back("--ks"); | ||||||
| 		args.push_back("SHA256withRSA"); |  | ||||||
| 		String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url"); |  | ||||||
| 		if (tsa_url != "") { |  | ||||||
| 			args.push_back("-tsa"); |  | ||||||
| 			args.push_back(tsa_url); |  | ||||||
| 		} |  | ||||||
| 		args.push_back("-verbose"); |  | ||||||
| 		args.push_back("-keystore"); |  | ||||||
| 		args.push_back(keystore); | 		args.push_back(keystore); | ||||||
| 		args.push_back("-storepass"); | 		args.push_back("--ks-pass"); | ||||||
| 		args.push_back(password); | 		args.push_back("pass:" + password); | ||||||
| 		args.push_back(export_path); | 		args.push_back("--ks-key-alias"); | ||||||
| 		args.push_back(user); | 		args.push_back(user); | ||||||
|  | 		args.push_back(export_path); | ||||||
| 		int retval; | 		int retval; | ||||||
| 		OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); | 		OS::get_singleton()->execute(apksigner, args, true, NULL, NULL, &retval); | ||||||
| 		if (retval) { | 		if (retval) { | ||||||
| 			EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval)); | 			EditorNode::add_io_error("'apksigner' returned with error #" + itos(retval)); | ||||||
| 			return ERR_CANT_CREATE; | 			return ERR_CANT_CREATE; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (ep.step("Verifying " + export_label + "...", 104)) { | 		if (ep.step("Verifying " + export_label + "...", 105)) { | ||||||
| 			return ERR_SKIP; | 			return ERR_SKIP; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		args.clear(); | 		args.clear(); | ||||||
| 		args.push_back("-verify"); | 		args.push_back("verify"); | ||||||
| 		args.push_back("-keystore"); | 		args.push_back("--verbose"); | ||||||
| 		args.push_back(keystore); |  | ||||||
| 		args.push_back(export_path); | 		args.push_back(export_path); | ||||||
| 		args.push_back("-verbose"); |  | ||||||
| 
 | 
 | ||||||
| 		OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); | 		OS::get_singleton()->execute(apksigner, args, true, NULL, NULL, &retval); | ||||||
| 		if (retval) { | 		if (retval) { | ||||||
| 			EditorNode::add_io_error("'jarsigner' verification of " + export_label + " failed. Make sure to use a jarsigner from OpenJDK 8."); | 			EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed."); | ||||||
| 			return ERR_CANT_CREATE; | 			return ERR_CANT_CREATE; | ||||||
| 		} | 		} | ||||||
| 		return OK; | 		return OK; | ||||||
|  | @ -2337,8 +2388,8 @@ public: | ||||||
| 					return ERR_UNCONFIGURED; | 					return ERR_UNCONFIGURED; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			String sdk_path = EDITOR_GET("export/android/custom_build_sdk_path"); | 			String sdk_path = EDITOR_GET("export/android/android_sdk_path"); | ||||||
| 			ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/custom_build_sdk_path'."); | 			ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); | ||||||
| 
 | 
 | ||||||
| 			// TODO: should we use "package/name" or "application/config/name"?
 | 			// TODO: should we use "package/name" or "application/config/name"?
 | ||||||
| 			String project_name = get_project_name(p_preset->get("package/name")); | 			String project_name = get_project_name(p_preset->get("package/name")); | ||||||
|  | @ -2685,18 +2736,13 @@ public: | ||||||
| 			CLEANUP_AND_RETURN(err); | 			CLEANUP_AND_RETURN(err); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (should_sign) { | 		// Let's zip-align (must be done before signing)
 | ||||||
| 			err = sign_apk(p_preset, p_debug, tmp_unaligned_path, ep); |  | ||||||
| 			if (err != OK) { |  | ||||||
| 				CLEANUP_AND_RETURN(err); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Let's zip-align (must be done after signing)
 |  | ||||||
| 
 | 
 | ||||||
| 		static const int ZIP_ALIGNMENT = 4; | 		static const int ZIP_ALIGNMENT = 4; | ||||||
| 
 | 
 | ||||||
| 		if (ep.step("Aligning APK...", 105)) { | 		// If we're not signing the apk, then the next step should be the last.
 | ||||||
|  | 		const int next_step = should_sign ? 103 : 105; | ||||||
|  | 		if (ep.step("Aligning APK...", next_step)) { | ||||||
| 			CLEANUP_AND_RETURN(ERR_SKIP); | 			CLEANUP_AND_RETURN(ERR_SKIP); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -2770,6 +2816,15 @@ public: | ||||||
| 		zipClose(final_apk, nullptr); | 		zipClose(final_apk, nullptr); | ||||||
| 		unzClose(tmp_unaligned); | 		unzClose(tmp_unaligned); | ||||||
| 
 | 
 | ||||||
|  | 		if (should_sign) { | ||||||
|  | 			// Signing must be done last as any additional modifications to the
 | ||||||
|  | 			// file will invalidate the signature.
 | ||||||
|  | 			err = sign_apk(p_preset, p_debug, p_path, ep); | ||||||
|  | 			if (err != OK) { | ||||||
|  | 				CLEANUP_AND_RETURN(err); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		CLEANUP_AND_RETURN(OK); | 		CLEANUP_AND_RETURN(OK); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2809,19 +2864,14 @@ void register_android_exporter() { | ||||||
| 		exe_ext = "*.exe"; | 		exe_ext = "*.exe"; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	EDITOR_DEF("export/android/adb", ""); | 	EDITOR_DEF("export/android/android_sdk_path", ""); | ||||||
| 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/adb", PROPERTY_HINT_GLOBAL_FILE, exe_ext)); | 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR)); | ||||||
| 	EDITOR_DEF("export/android/jarsigner", ""); |  | ||||||
| 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext)); |  | ||||||
| 	EDITOR_DEF("export/android/debug_keystore", ""); | 	EDITOR_DEF("export/android/debug_keystore", ""); | ||||||
| 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks")); | 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks")); | ||||||
| 	EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); | 	EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); | ||||||
| 	EDITOR_DEF("export/android/debug_keystore_pass", "android"); | 	EDITOR_DEF("export/android/debug_keystore_pass", "android"); | ||||||
| 	EDITOR_DEF("export/android/force_system_user", false); | 	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)); |  | ||||||
| 
 | 
 | ||||||
| 	EDITOR_DEF("export/android/timestamping_authority_url", ""); |  | ||||||
| 	EDITOR_DEF("export/android/shutdown_adb_on_exit", true); | 	EDITOR_DEF("export/android/shutdown_adb_on_exit", true); | ||||||
| 
 | 
 | ||||||
| 	Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); | 	Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Rémi Verschelde
						Rémi Verschelde