mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 05:31:01 +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
 | ||||
| 			String adb = EditorSettings::get_singleton()->get("export/android/adb"); | ||||
| 			String adb = get_adb_path(); | ||||
| 			if (FileAccess::exists(adb)) { | ||||
| 				String devices; | ||||
| 				List<String> args; | ||||
|  | @ -425,7 +425,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { | |||
| 		} | ||||
| 
 | ||||
| 		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)) { | ||||
| 				return; //adb not configured
 | ||||
| 			} | ||||
|  | @ -1752,7 +1752,7 @@ public: | |||
| 
 | ||||
| 		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.
 | ||||
| 		if (ep.step("Exporting APK...", 0)) { | ||||
|  | @ -1899,6 +1899,55 @@ public: | |||
| 		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 { | ||||
| 		String err; | ||||
| 		bool valid = false; | ||||
|  | @ -1934,25 +1983,16 @@ public: | |||
| 			} | ||||
| 		} else { | ||||
| 			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; | ||||
| 
 | ||||
| 		// 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"); | ||||
| 
 | ||||
| 		if (!FileAccess::exists(dk)) { | ||||
|  | @ -1970,25 +2010,45 @@ public: | |||
| 			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/custom_build_sdk_path"); | ||||
| 		String sdk_path = EditorSettings::get_singleton()->get("export/android/android_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; | ||||
| 		} else { | ||||
| 			Error errn; | ||||
| 			// Check for the platform-tools directory.
 | ||||
| 			DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); | ||||
| 			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 += "\n"; | ||||
| 				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")) { | ||||
| 				err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; | ||||
| 			// Check for the build-tools directory.
 | ||||
| 			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; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -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")); | ||||
| 		String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; | ||||
| 		String release_keystore = p_preset->get("keystore/release"); | ||||
| 		String release_username = p_preset->get("keystore/release_user"); | ||||
| 		String release_password = p_preset->get("keystore/release_password"); | ||||
| 
 | ||||
| 		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 " + export_label + " is unsigned."); | ||||
| 		String apksigner = get_apksigner_path(); | ||||
| 		if (!FileAccess::exists(apksigner)) { | ||||
| 			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; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -2198,7 +2258,7 @@ public: | |||
| 				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; | ||||
| 			} | ||||
| 
 | ||||
|  | @ -2207,7 +2267,7 @@ public: | |||
| 			password = release_password; | ||||
| 			user = release_username; | ||||
| 
 | ||||
| 			if (ep.step("Signing release " + export_label + "...", 103)) { | ||||
| 			if (ep.step("Signing release " + export_label + "...", 104)) { | ||||
| 				return ERR_SKIP; | ||||
| 			} | ||||
| 		} | ||||
|  | @ -2218,43 +2278,34 @@ public: | |||
| 		} | ||||
| 
 | ||||
| 		List<String> args; | ||||
| 		args.push_back("-digestalg"); | ||||
| 		args.push_back("SHA-256"); | ||||
| 		args.push_back("-sigalg"); | ||||
| 		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("sign"); | ||||
| 		args.push_back("--verbose"); | ||||
| 		args.push_back("--ks"); | ||||
| 		args.push_back(keystore); | ||||
| 		args.push_back("-storepass"); | ||||
| 		args.push_back(password); | ||||
| 		args.push_back(export_path); | ||||
| 		args.push_back("--ks-pass"); | ||||
| 		args.push_back("pass:" + password); | ||||
| 		args.push_back("--ks-key-alias"); | ||||
| 		args.push_back(user); | ||||
| 		args.push_back(export_path); | ||||
| 		int retval; | ||||
| 		OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); | ||||
| 		OS::get_singleton()->execute(apksigner, args, true, NULL, NULL, &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; | ||||
| 		} | ||||
| 
 | ||||
| 		if (ep.step("Verifying " + export_label + "...", 104)) { | ||||
| 		if (ep.step("Verifying " + export_label + "...", 105)) { | ||||
| 			return ERR_SKIP; | ||||
| 		} | ||||
| 
 | ||||
| 		args.clear(); | ||||
| 		args.push_back("-verify"); | ||||
| 		args.push_back("-keystore"); | ||||
| 		args.push_back(keystore); | ||||
| 		args.push_back("verify"); | ||||
| 		args.push_back("--verbose"); | ||||
| 		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) { | ||||
| 			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 OK; | ||||
|  | @ -2337,8 +2388,8 @@ public: | |||
| 					return ERR_UNCONFIGURED; | ||||
| 				} | ||||
| 			} | ||||
| 			String sdk_path = EDITOR_GET("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/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/android_sdk_path'."); | ||||
| 
 | ||||
| 			// TODO: should we use "package/name" or "application/config/name"?
 | ||||
| 			String project_name = get_project_name(p_preset->get("package/name")); | ||||
|  | @ -2685,18 +2736,13 @@ public: | |||
| 			CLEANUP_AND_RETURN(err); | ||||
| 		} | ||||
| 
 | ||||
| 		if (should_sign) { | ||||
| 			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)
 | ||||
| 		// Let's zip-align (must be done before signing)
 | ||||
| 
 | ||||
| 		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); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -2770,6 +2816,15 @@ public: | |||
| 		zipClose(final_apk, nullptr); | ||||
| 		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); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -2809,19 +2864,14 @@ void register_android_exporter() { | |||
| 		exe_ext = "*.exe"; | ||||
| 	} | ||||
| 
 | ||||
| 	EDITOR_DEF("export/android/adb", ""); | ||||
| 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/adb", PROPERTY_HINT_GLOBAL_FILE, exe_ext)); | ||||
| 	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/android_sdk_path", ""); | ||||
| 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR)); | ||||
| 	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")); | ||||
| 	EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); | ||||
| 	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)); | ||||
| 
 | ||||
| 	EDITOR_DEF("export/android/timestamping_authority_url", ""); | ||||
| 	EDITOR_DEF("export/android/shutdown_adb_on_exit", true); | ||||
| 
 | ||||
| 	Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid)); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Rémi Verschelde
						Rémi Verschelde