public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        string[] Architectures         = UnrealBuildTool.AndroidToolChain.GetAllArchitectures();
        string[] GPUArchitectures      = UnrealBuildTool.AndroidToolChain.GetAllGPUArchitectures();
        bool     bMakeSeparateApks     = UnrealBuildTool.Android.UEDeployAndroid.ShouldMakeSeparateApks();
        bool     bPackageDataInsideApk = UnrealBuildTool.Android.UEDeployAndroid.PackageDataInsideApk(false);

        var Deploy = UEBuildDeploy.GetBuildDeploy(UnrealTargetPlatform.Android);

        string BaseApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, "", "");

        Log("BaseApkName = {0}", BaseApkName);

        // Create main OBB with entire contents of staging dir. This
        // includes any PAK files, movie files, etc.

        string LocalObbName = SC.StageDirectory.TrimEnd(new char[] { '/', '\\' }) + ".obb";

        // Always delete the target OBB file if it exists
        if (File.Exists(LocalObbName))
        {
            File.Delete(LocalObbName);
        }

        // Now create the OBB as a ZIP archive.
        Log("Creating {0} from {1}", LocalObbName, SC.StageDirectory);
        using (ZipFile ObbFile = new ZipFile(LocalObbName))
        {
            ObbFile.CompressionMethod = CompressionMethod.None;
            ObbFile.CompressionLevel  = Ionic.Zlib.CompressionLevel.None;
            int ObbFileCount = 0;
            ObbFile.AddProgress +=
                delegate(object sender, AddProgressEventArgs e)
            {
                if (e.EventType == ZipProgressEventType.Adding_AfterAddEntry)
                {
                    ObbFileCount += 1;
                    Log("[{0}/{1}] Adding {2} to OBB",
                        ObbFileCount, e.EntriesTotal,
                        e.CurrentEntry.FileName);
                }
            };
            ObbFile.AddDirectory(SC.StageDirectory + "/" + SC.ShortProjectName, SC.ShortProjectName);
            ObbFile.Save();
        }

        foreach (string Architecture in Architectures)
        {
            foreach (string GPUArchitecture in GPUArchitectures)
            {
                string ApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
                if (!SC.IsCodeBasedProject)
                {
                    string UE4GameApkName = GetFinalApkName(Params, SC.StageExecutables[0], false, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
                    if (FileExists_NoExceptions(UE4GameApkName) == false)
                    {
                        Log("Failed to find game apk " + UE4GameApkName);
                        AutomationTool.ErrorReporter.Error("Stage Failed.", (int)AutomationTool.ErrorCodes.Error_MissingExecutable);
                        throw new AutomationException("Could not find apk {0}. You may need to build the UE4 project with your target configuration and platform.", UE4GameApkName);
                    }
                }

                string BatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");

                if (!Params.Prebuilt)
                {
                    string CookFlavor = SC.FinalCookPlatform.IndexOf("_") > 0 ? SC.FinalCookPlatform.Substring(SC.FinalCookPlatform.IndexOf("_")) : "";
                    string SOName     = GetSONameWithoutArchitecture(Params, SC.StageExecutables[0]);
                    Deploy.PrepForUATPackageOrDeploy(Params.ShortProjectName, SC.ProjectRoot, SOName, SC.LocalRoot + "/Engine", Params.Distribution, CookFlavor, false);
                }

                // Create APK specific OBB in case we have a detached OBB.
                string DeviceObbName = "";
                string ObbName       = "";
                if (!bPackageDataInsideApk)
                {
                    DeviceObbName = GetDeviceObbName(ApkName);
                    ObbName       = GetFinalObbName(ApkName);
                    CopyFile(LocalObbName, ObbName);
                }

                // Write install batch file(s).

                string PackageName = GetPackageInfo(ApkName, false);
                // make a batch file that can be used to install the .apk and .obb files
                string[] BatchLines;
                if (Utils.IsRunningOnMono)
                {
                    Log("Writing shell script for install with {0}", bPackageDataInsideApk ? "data in APK" : "separate obb");
                    BatchLines = new string[] {
                        "#!/bin/sh",
                        "cd \"`dirname \"$0\"`\"",
                        "ADB=",
                        "if [ \"$ANDROID_HOME\" != \"\" ]; then ADB=$ANDROID_HOME/platform-tools/adb; else ADB=" + Environment.GetEnvironmentVariable("ANDROID_HOME") + "; fi",
                        "DEVICE=",
                        "if [ \"$1\" != \"\" ]; then DEVICE=\"-s $1\"; fi",
                        "echo",
                        "echo Uninstalling existing application. Failures here can almost always be ignored.",
                        "$ADB $DEVICE uninstall " + PackageName,
                        "echo",
                        "echo Installing existing application. Failures here indicate a problem with the device \\(connection or storage permissions\\) and are fatal.",
                        "$ADB $DEVICE install " + Path.GetFileName(ApkName),
                        "if [ $? -eq 0 ]; then",
                        "\techo",
                        "\techo Removing old data. Failures here are usually fine - indicating the files were not on the device.",
                        "\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/" + Params.ShortProjectName + "'",
                        "\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/UE4CommandLine.txt" + "'",
                        "\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/" + TargetAndroidLocation + PackageName + "'",
                        bPackageDataInsideApk ? "" : "\techo",
                        bPackageDataInsideApk ? "" : "\techo Installing new data. Failures here indicate storage problems \\(missing SD card or bad permissions\\) and are fatal.",
                        bPackageDataInsideApk ? "" : "\tSTORAGE=$(echo \"`$ADB $DEVICE shell 'echo $EXTERNAL_STORAGE'`\" | cat -v | tr -d '^M')",
                        bPackageDataInsideApk ? "" : "\t$ADB $DEVICE push " + Path.GetFileName(ObbName) + " $STORAGE/" + DeviceObbName,
                        bPackageDataInsideApk ? "if [ 1 ]; then" : "\tif [ $? -eq 0 ]; then",
                        "\t\techo",
                        "\t\techo Installation successful",
                        "\t\texit 0",
                        "\tfi",
                        "fi",
                        "echo",
                        "echo There was an error installing the game or the obb file. Look above for more info.",
                        "echo",
                        "echo Things to try:",
                        "echo Check that the device (and only the device) is listed with \\\"$ADB devices\\\" from a command prompt.",
                        "echo Make sure all Developer options look normal on the device",
                        "echo Check that the device has an SD card.",
                        "exit 1"
                    };
                }
                else
                {
                    Log("Writing bat for install with {0}", bPackageDataInsideApk ? "data in APK" : "separate OBB");
                    BatchLines = new string[] {
                        "setlocal",
                        "set ANDROIDHOME=%ANDROID_HOME%",
                        "if \"%ANDROIDHOME%\"==\"\" set ANDROIDHOME=" + Environment.GetEnvironmentVariable("ANDROID_HOME"),
                        "set ADB=%ANDROIDHOME%\\platform-tools\\adb.exe",
                        "set DEVICE=",
                        "if not \"%1\"==\"\" set DEVICE=-s %1",
                        "for /f \"delims=\" %%A in ('%ADB% %DEVICE% " + GetStorageQueryCommand() + "') do @set STORAGE=%%A",
                        "@echo.",
                        "@echo Uninstalling existing application. Failures here can almost always be ignored.",
                        "%ADB% %DEVICE% uninstall " + PackageName,
                        "@echo.",
                        "@echo Installing existing application. Failures here indicate a problem with the device (connection or storage permissions) and are fatal.",
                        "%ADB% %DEVICE% install " + Path.GetFileName(ApkName),
                        "@if \"%ERRORLEVEL%\" NEQ \"0\" goto Error",
                        "%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/" + Params.ShortProjectName,
                        "%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/UE4CommandLine.txt",                         // we need to delete the commandline in UE4Game or it will mess up loading
                        "%ADB% %DEVICE% shell rm -r %STORAGE%/" + TargetAndroidLocation + PackageName,
                        bPackageDataInsideApk ? "" : "@echo.",
                        bPackageDataInsideApk ? "" : "@echo Installing new data. Failures here indicate storage problems (missing SD card or bad permissions) and are fatal.",
                        bPackageDataInsideApk ? "" : "%ADB% %DEVICE% push " + Path.GetFileName(ObbName) + " %STORAGE%/" + DeviceObbName,
                        bPackageDataInsideApk ? "" : "if \"%ERRORLEVEL%\" NEQ \"0\" goto Error",
                        "@echo.",
                        "@echo Installation successful",
                        "goto:eof",
                        ":Error",
                        "@echo.",
                        "@echo There was an error installing the game or the obb file. Look above for more info.",
                        "@echo.",
                        "@echo Things to try:",
                        "@echo Check that the device (and only the device) is listed with \"%ADB$ devices\" from a command prompt.",
                        "@echo Make sure all Developer options look normal on the device",
                        "@echo Check that the device has an SD card.",
                        "@pause"
                    };
                }
                File.WriteAllLines(BatchName, BatchLines);

                if (Utils.IsRunningOnMono)
                {
                    CommandUtils.FixUnixFilePermissions(BatchName);
                }
            }
        }

        PrintRunTime();
    }
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        var  Architectures         = UnrealBuildTool.AndroidToolChain.GetAllArchitectures();
        var  GPUArchitectures      = UnrealBuildTool.AndroidToolChain.GetAllGPUArchitectures();
        bool bMakeSeparateApks     = UnrealBuildTool.Android.UEDeployAndroid.ShouldMakeSeparateApks();
        bool bPackageDataInsideApk = UnrealBuildTool.Android.UEDeployAndroid.PackageDataInsideApk(false);

        var Deploy = UEBuildDeploy.GetBuildDeploy(UnrealTargetPlatform.Android);

        string BaseApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, "", "");

        Log("BaseApkName = {0}", BaseApkName);

        // Create main OBB with entire contents of staging dir. This
        // includes any PAK files, movie files, etc.

        string LocalObbName = SC.StageDirectory.TrimEnd(new char[] { '/', '\\' }) + ".obb";

        // Always delete the target OBB file if it exists
        if (File.Exists(LocalObbName))
        {
            File.Delete(LocalObbName);
        }

        // Now create the OBB as a ZIP archive.
        Log("Creating {0} from {1}", LocalObbName, SC.StageDirectory);
        using (ZipFile ObbFile = new ZipFile(LocalObbName))
        {
            ObbFile.CompressionMethod = CompressionMethod.None;
            ObbFile.CompressionLevel  = Ionic.Zlib.CompressionLevel.None;
            int ObbFileCount = 0;
            ObbFile.AddProgress +=
                delegate(object sender, AddProgressEventArgs e)
            {
                if (e.EventType == ZipProgressEventType.Adding_AfterAddEntry)
                {
                    ObbFileCount += 1;
                    Log("[{0}/{1}] Adding {2} to OBB",
                        ObbFileCount, e.EntriesTotal,
                        e.CurrentEntry.FileName);
                }
            };
            ObbFile.AddDirectory(SC.StageDirectory + "/" + SC.ShortProjectName, SC.ShortProjectName);
            ObbFile.Save();
        }

        foreach (string Architecture in Architectures)
        {
            foreach (string GPUArchitecture in GPUArchitectures)
            {
                string ApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
                if (!SC.IsCodeBasedProject)
                {
                    string UE4SOName = GetFinalApkName(Params, SC.StageExecutables[0], false, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
                    UE4SOName = UE4SOName.Replace(".apk", ".so");
                    if (FileExists_NoExceptions(UE4SOName) == false)
                    {
                        Log("Failed to find game .so " + UE4SOName);
                        throw new AutomationException(ErrorCodes.Error_MissingExecutable, "Stage Failed. Could not find .so {0}. You may need to build the UE4 project with your target configuration and platform.", UE4SOName);
                    }
                }


                if (!Params.Prebuilt)
                {
                    string CookFlavor = SC.FinalCookPlatform.IndexOf("_") > 0 ? SC.FinalCookPlatform.Substring(SC.FinalCookPlatform.IndexOf("_")) : "";
                    string SOName     = GetSONameWithoutArchitecture(Params, SC.StageExecutables[0]);
                    Deploy.PrepForUATPackageOrDeploy(Params.ShortProjectName, SC.ProjectRoot, SOName, SC.LocalRoot + "/Engine", Params.Distribution, CookFlavor, false);
                }

                // Create APK specific OBB in case we have a detached OBB.
                string DeviceObbName = "";
                string ObbName       = "";
                if (!bPackageDataInsideApk)
                {
                    DeviceObbName = GetDeviceObbName(ApkName);
                    ObbName       = GetFinalObbName(ApkName);
                    CopyFile(LocalObbName, ObbName);
                }

                // Write install batch file(s).
                string BatchName   = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", false);
                string PackageName = GetPackageInfo(ApkName, false);
                // make a batch file that can be used to install the .apk and .obb files
                string[] BatchLines = GenerateInstallBatchFile(bPackageDataInsideApk, PackageName, ApkName, Params, ObbName, DeviceObbName, false);
                File.WriteAllLines(BatchName, BatchLines);

                // If we aren't packaging data in the APK then lets write out a bat file to also let us test without the OBB
                // on the device.
                String NoInstallBatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", true);
                // if(!bPackageDataInsideApk)
                {
                    BatchLines = GenerateInstallBatchFile(bPackageDataInsideApk, PackageName, ApkName, Params, ObbName, DeviceObbName, true);
                    File.WriteAllLines(NoInstallBatchName, BatchLines);
                }

                if (Utils.IsRunningOnMono)
                {
                    CommandUtils.FixUnixFilePermissions(BatchName);
                    if (File.Exists(NoInstallBatchName))
                    {
                        CommandUtils.FixUnixFilePermissions(NoInstallBatchName);
                    }
                }
            }
        }

        PrintRunTime();
    }