// Microsoft using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml; // Unity using UnityEditor.Android; // GUPS - AntiCheat using GUPS.AntiCheat.Settings; namespace GUPS.AntiCheat.Editor.Build { /// /// Handles post-processing of the Gradle Android project after it is generated by Unity. /// Implements the interface. /// /// /// This class modifies the AndroidManifest.xml file of the generated Gradle project by adding /// specific `` elements under the `` section to ensure compatibility with Android's /// runtime permissions model. /// internal class PostProcessAndroidBuild : IPostGenerateGradleAndroidProject { /// /// Gets the callback order of this postprocessor. /// /// /// The callback order is set to a high value ( - 1) to ensure /// that this postprocessor is executed near the very end of the post-processing pipeline. /// This allows it to modify the AndroidManifest.xml as the final step without overwriting /// changes made by other postprocessors. /// public int callbackOrder => Int32.MaxValue - 1; /// /// Retrieves the path to the `AndroidManifest.xml` file within the generated Gradle project. /// /// The base path of the Gradle project. /// The full file path to the `AndroidManifest.xml` file. /// /// The method constructs the path by combining the base path with the relative path to the manifest file, /// which is typically located in `src/main/AndroidManifest.xml`. /// private String GetManifestPath(String _BasePath) { return Path.Combine(_BasePath, "src", "main", "AndroidManifest.xml"); } /// /// Processes the generated Gradle Android project after it is created by Unity. /// /// The base path of the generated Gradle project. /// /// This method performs the following steps: /// /// Locates the `AndroidManifest.xml` file in the Gradle project. /// Loads the manifest using the class. /// Adds `` elements under the `` section for applications specified in the global settings. /// Saves the updated manifest back to the file system. /// /// public void OnPostGenerateGradleAndroidProject(String _BasePath) { #if UNITY_ANDROID // Get the path to the AndroidManifest.xml file. String var_ManifestPath = this.GetManifestPath(_BasePath); // Load the AndroidManifest.xml file. AndroidManifest var_Manifest = new AndroidManifest(var_ManifestPath); // Add the packages into the AndroidManifest.xml file. List var_AppPackages = this.GetAppPackagesToFind(); for (int i = 0; i < var_AppPackages.Count; i++) { var_Manifest.AddQueryPackage(var_AppPackages[i]); } // Save the changes to the AndroidManifest.xml file. var_Manifest.Save(); #endif } /// /// Retrieves the list of application package names to add to the `` section of the manifest. /// /// A list of application package names, or an empty list if no global settings are available. /// /// The list of applications is determined by accessing the global settings instance and retrieving /// the `Android_BlacklistedApplications` property. If the global settings are unavailable, an empty /// list is returned. /// private List GetAppPackagesToFind() { return GlobalSettings.Instance?.Android_BlacklistedApplications ?? new List(); } /// /// Represents an Android XML document that handles Android-specific XML namespace and file operations. /// /// /// This class extends XmlDocument to provide specialized functionality for working with Android XML files, /// including proper namespace management and file handling. /// internal class AndroidXmlDocument : XmlDocument { /// /// The file path of the Android XML document. /// private String filePath; /// /// The namespace manager for handling XML namespaces in the document. /// protected XmlNamespaceManager namespaceManager; /// /// The standard Android XML namespace URI. /// public readonly String AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; /// /// Initializes a new instance of the AndroidXmlDocument class. /// /// The file path of the Android XML document to load. /// /// Loads the XML document from the specified path and initializes the namespace manager /// with the Android XML namespace. /// public AndroidXmlDocument(String _Path) { this.filePath = _Path; using (var var_Reader = new XmlTextReader(this.filePath)) { var_Reader.Read(); this.Load(var_Reader); } this.namespaceManager = new XmlNamespaceManager(this.NameTable); this.namespaceManager.AddNamespace("android", this.AndroidXmlNamespace); } /// /// Saves the XML document to its original file path. /// /// /// Uses the file path specified during initialization to save the document. /// public void Save() { this.SaveAt(this.filePath); } /// /// Saves the XML document to a specified file path. /// /// The file path where the XML document should be saved. /// /// Saves the document with proper formatting and UTF-8 encoding without BOM (Byte Order Mark). /// public void SaveAt(String _Path) { using (var var_Writer = new XmlTextWriter(_Path, new UTF8Encoding(false))) { var_Writer.Formatting = Formatting.Indented; this.Save(var_Writer); } } } /// /// Represents an AndroidManifest.xml document and provides methods for common operations /// such as adding permissions or query packages. /// Inherits from . /// /// /// This class simplifies the management of AndroidManifest.xml files by providing methods /// to manipulate key elements, such as `` and ``. /// internal class AndroidManifest : AndroidXmlDocument { /// /// The root `` element of the AndroidManifest.xml document. /// private readonly XmlElement ManifestElement; /// /// Initializes a new instance of the class. /// /// The file path of the AndroidManifest.xml document to load. /// /// The constructor loads the AndroidManifest.xml file and initializes the root `` element for further manipulation. /// public AndroidManifest(String _Path) : base(_Path) { // Select the root element. this.ManifestElement = this.SelectSingleNode("/manifest") as XmlElement; } /// /// Creates an Android-specific XML attribute with the specified key and value. /// /// The key of the Android attribute (e.g., "name"). /// The value of the attribute. /// A new instance representing the Android attribute. /// /// This method simplifies the creation of namespaced attributes for the Android XML namespace. /// private XmlAttribute CreateAndroidAttribute(String key, String value) { // Create an attribute in the Android namespace. XmlAttribute attr = CreateAttribute("android", key, this.AndroidXmlNamespace); attr.Value = value; return attr; } /// /// Adds a `` element to the AndroidManifest.xml document. /// /// The name of the permission to add (e.g., "android.permission.INTERNET"). /// /// This method appends a `` element with the specified permission to the root `` element. /// public void AddUsesPermission(String _Permission) { // Create a element. XmlElement child = this.CreateElement("uses-permission"); this.ManifestElement.AppendChild(child); // Create and append the "android:name" attribute. XmlAttribute newAttribute = this.CreateAndroidAttribute("name", _Permission); child.Attributes.Append(newAttribute); } /// /// Adds a `` element under the `` section in the AndroidManifest.xml document. /// /// The name of the package to add (e.g., "com.example.app"). /// /// This method ensures that a `` element exists in the manifest and appends a `` element /// with the specified package name to it. /// public void AddQueryPackage(String _Name) { // Retrieve or create the element. XmlElement queryPackageElement = this.ManifestElement.SelectSingleNode("queries") as XmlElement; if (queryPackageElement == null) { // If does not exist, create it. queryPackageElement = this.CreateElement("queries"); this.ManifestElement.AppendChild(queryPackageElement); } // Create the element. XmlElement packageElement = this.CreateElement("package"); queryPackageElement.AppendChild(packageElement); // Create and append the "android:name" attribute. XmlAttribute newAttribute = this.CreateAndroidAttribute("name", _Name); packageElement.Attributes.Append(newAttribute); } } } }