Пример #1
0
    public static bool UninstallAPK()
    {
        OVRBundleTool.PrintLog("Uninstalling Application . . .");

        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            string output, error;
            string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
                                    + GetTransitionApkOptionalIdentifier();
            string[] appStartCommand = { "-d shell", "pm uninstall", appPackagename };
            if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
            {
                OVRBundleTool.PrintSuccess();
                OVRBundleTool.PrintLog("App package " + appPackagename + " is uninstalled.");
                return(true);
            }

            OVRBundleTool.PrintError("Failed to uninstall APK.");
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
        return(false);
    }
Пример #2
0
    private static bool CheckADBDevices()
    {
        // Check if there are any ADB devices connected before starting the build process
        var adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            List <string> devices = adbTool.GetDevices();
            if (devices.Count == 0)
            {
                OVRPlugin.SendEvent("no_adb_devices", "", "ovrbuild");
                UnityEngine.Debug.LogError("No ADB devices connected. Cannot perform OVR Build and Run.");
                return(false);
            }
            else if (devices.Count > 1)
            {
                OVRPlugin.SendEvent("multiple_adb_devices", "", "ovrbuild");
                UnityEngine.Debug.LogError("Multiple ADB devices connected. Cannot perform OVR Build and Run.");
                return(false);
            }
        }
        else
        {
            OVRPlugin.SendEvent("ovr_adbtool_initialize_failure", "", "ovrbuild");
            UnityEngine.Debug.LogError("OVR ADB Tool failed to initialize. Check the Android SDK path in [Edit -> Preferences -> External Tools]");
            return(false);
        }
        return(true);
    }
Пример #3
0
    public static bool LaunchApplication()
    {
        OVRBundleTool.PrintLog("Launching Application . . . ");

        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            string output, error;
            string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
                                    + GetTransitionApkOptionalIdentifier();
            string   playerActivityName = "\"" + appPackagename + "/com.unity3d.player.UnityPlayerActivity\"";
            string[] appStartCommand    = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
            if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
            {
                OVRBundleTool.PrintSuccess();
                OVRBundleTool.PrintLog("App package " + appPackagename + " is launched.");
                return(true);
            }

            string completeError = "Failed to launch application. Try launching it manually through the device.\n" + (string.IsNullOrEmpty(error) ? output : error);
            OVRBundleTool.PrintError(completeError);
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
        return(false);
    }
Пример #4
0
    private static bool CheckADBDevices(out string connectedDeviceName)
    {
        // Check if there are any ADB devices connected before starting the build process
        var adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        connectedDeviceName = null;

        if (adbTool.isReady)
        {
            List <string> devices = adbTool.GetDevices();
            if (devices.Count == 0)
            {
                UnityEngine.Debug.LogError("No ADB devices connected. Connect a device to this computer to run APK.");
                return(false);
            }
            else if (devices.Count > 1)
            {
                UnityEngine.Debug.LogError("Multiple ADB devices connected. Disconnect extra devices from this computer to run APK.");
                return(false);
            }
            else
            {
                connectedDeviceName = devices[0];
                return(true);
            }
        }
        else
        {
            UnityEngine.Debug.LogError("OVR ADB Tool failed to initialize. Check the Android SDK path in [Edit -> Preferences -> External Tools]");
            return(false);
        }
    }
Пример #5
0
    public static void DeleteRemoteAssetBundles()
    {
        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            bool   failure         = false;
            string fileExistsError = "No such file or directory";
            OVRBundleTool.PrintLog("Deleting device bundles . . . ");
            string   output, error;
            string[] deleteBundleCommand = { "-d shell", "rm -r", externalSceneCache };
            if (adbTool.RunCommand(deleteBundleCommand, null, out output, out error) != 0)
            {
                if (!(output.Contains(fileExistsError) || error.Contains(fileExistsError)))
                {
                    failure = true;
                }
            }

            if (failure)
            {
                OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
                OVRBundleTool.PrintError("Failed to delete scene bundle cache directory.");
            }
            else
            {
                OVRBundleTool.PrintSuccess();
            }
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
    }
    public static bool UninstallAPK()
    {
        OVRBundleTool.PrintLog("Uninstalling Application . . .");

        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            string   output, error;
            string   packageName     = Application.identifier + ".transition";
            string[] appStartCommand = { "-d shell", "pm uninstall", packageName };
            if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
            {
                OVRBundleTool.PrintSuccess();
                return(true);
            }

            OVRBundleTool.PrintError("Failed to uninstall APK.");
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
        return(false);
    }
Пример #7
0
    public static string[] ListRemoteAssetBundleNames()
    {
        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
                                 + GetTransitionApkOptionalIdentifier() + "/cache/scenes";

            string   output, error;
            string[] listBundlesCommand = { "-d shell", "ls", externalSceneCache };
            if (adbTool.RunCommand(listBundlesCommand, null, out output, out error) == 0)
            {
                return(output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));
            }
        }

        return(null);
    }
Пример #8
0
    public static void DeleteRemoteAssetBundles()
    {
        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
                                 + GetTransitionApkOptionalIdentifier() + "/cache/scenes";

            bool   failure         = false;
            string fileExistsError = "No such file or directory";
            OVRBundleTool.PrintLog("Deleting device bundles . . . ");
            string   output, error;
            string[] deleteBundleCommand = { "-d shell", "rm -r", externalSceneCache };
            if (adbTool.RunCommand(deleteBundleCommand, null, out output, out error) != 0)
            {
                if (!(output.Contains(fileExistsError) || error.Contains(fileExistsError)))
                {
                    failure = true;
                }
            }

            if (failure)
            {
                OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
                OVRBundleTool.PrintError("Failed to delete scene bundle cache directory.");
            }
            else
            {
                OVRBundleTool.PrintSuccess();
            }
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
    }
Пример #9
0
    // The actual window code goes here
    void OnGUI()
    {
        showAndroidOptions = EditorGUILayout.Foldout(showAndroidOptions, "Android Tools");

        if (showAndroidOptions)
        {
            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Android SDK root path: ", androidSdkRootPath);
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Start Server"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.StartServer(null);
                    EditorUtility.DisplayDialog("ADB StartServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Kill Server"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.KillServer(null);
                    EditorUtility.DisplayDialog("ADB KillServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Forward Port"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.ForwardPort(remoteListeningPort, null);
                    EditorUtility.DisplayDialog("ADB ForwardPort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                    OVRPlugin.SendEvent("device_metrics_profiler", (exitCode == 0 ? "adb_forward_success" : "adb_forward_failure"));
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Release Port"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.ReleasePort(remoteListeningPort, null);
                    EditorUtility.DisplayDialog("ADB ReleasePort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            GUILayout.EndHorizontal();
        }

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

        GUILayout.BeginHorizontal();
        remoteListeningPort = EditorGUILayout.DelayedIntField("Remote Port", remoteListeningPort);

        if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Disconnected)
        {
            if (GUILayout.Button("Connect"))
            {
                ConnectPerfMetricsTcpServer();
                pauseReceiveMetrics = false;
                OVRPlugin.SendEvent("device_metrics_profiler", "connect");
            }
        }
        else
        {
            if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Connecting)
            {
                if (GUILayout.Button("Connecting ... Click again to Cancel"))
                {
                    DisconnectPerfMetricsTcpServer();
                    OVRPlugin.SendEvent("device_metrics_profiler", "cancel");
                }
            }
            else
            {
                if (GUILayout.Button("Disconnect"))
                {
                    DisconnectPerfMetricsTcpServer();
                    OVRPlugin.SendEvent("device_metrics_profiler", "disconnect");
                }

                if (GUILayout.Button(pauseReceiveMetrics ? "Continue" : "Pause"))
                {
                    pauseReceiveMetrics = !pauseReceiveMetrics;
                }
            }
        }
        GUILayout.EndHorizontal();

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

        lock (receivedMetricsList)
        {
            PresentIntProperty("Frame Count", "frameCount");
            PresentIntProperty("Dropped Frame Count", "compositorDroppedFrameCount");

            float?avgFrameTime = GetAveragePerfValueFloat("deltaFrameTime");
            if (avgFrameTime.HasValue)
            {
                float fps = 1.0f / avgFrameTime.Value;
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("FPS", GUILayout.Width(labelWidth));
                EditorGUILayout.LabelField(string.Format("{0:F1}", fps));
                EditorGUILayout.EndHorizontal();
            }

            int?  deviceCpuClockLevel          = GetLatestPerfValueInt("deviceCpuClockLevel");
            int?  deviceGpuClockLevel          = GetLatestPerfValueInt("deviceGpuClockLevel");
            float?deviceCpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceCpuClockFrequencyInMHz");
            float?deviceGpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceGpuClockFrequencyInMHz");

            if (deviceCpuClockLevel.HasValue || deviceCpuClockFrequencyInMHz.HasValue)
            {
                string cpuLabel;
                string cpuText;
                if (deviceCpuClockLevel.HasValue && deviceCpuClockFrequencyInMHz.HasValue)
                {
                    cpuLabel = "CPU Level (Freq)";
                    cpuText  = string.Format("{0} ({1:F0} MHz)", deviceCpuClockLevel, deviceCpuClockFrequencyInMHz);
                }
                else if (deviceCpuClockLevel.HasValue)
                {
                    cpuLabel = "CPU Level";
                    cpuText  = string.Format("{0}", deviceCpuClockLevel);
                }
                else
                {
                    cpuLabel = "CPU Frequency";
                    cpuText  = string.Format("{0:F0} MHz", deviceCpuClockFrequencyInMHz);
                }
                PresentText(cpuLabel, cpuText);
            }

            if (deviceGpuClockLevel.HasValue || deviceGpuClockFrequencyInMHz.HasValue)
            {
                string cpuLabel;
                string cpuText;
                if (deviceGpuClockLevel.HasValue && deviceGpuClockFrequencyInMHz.HasValue)
                {
                    cpuLabel = "GPU Level (Freq)";
                    cpuText  = string.Format("{0} ({1:F0} MHz)", deviceGpuClockLevel, deviceGpuClockFrequencyInMHz);
                }
                else if (deviceGpuClockLevel.HasValue)
                {
                    cpuLabel = "GPU Level";
                    cpuText  = string.Format("{0}", deviceGpuClockLevel);
                }
                else
                {
                    cpuLabel = "GPU Frequency";
                    cpuText  = string.Format("{0:F0} MHz", deviceGpuClockFrequencyInMHz);
                }
                PresentText(cpuLabel, cpuText);
            }

            PresentColumnTitles("Current", "Average", "Peak");

            PresentFloatTimeInMs("Frame Time", "deltaFrameTime", 0.020f, true, true);
            PresentFloatTimeInMs("App CPU Time", "appCpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("App GPU Time", "appGpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("Compositor CPU Time", "compositorCpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("Compositor GPU Time", "compositorGpuTime", 0.020f, true, true);
            PresentFloatPercentage("CPU Util (Average)", "systemCpuUtilAveragePercentage", false, false);
            PresentFloatPercentage("CPU Util (Worst Core)", "systemCpuUtilWorstPercentage", false, false);
            PresentFloatPercentage("GPU Util", "systemGpuUtilPercentage", false, false);
        }
    }
Пример #10
0
    private static bool TransferSceneBundles(OVRADBTool adbTool, string absoluteTempPath, string externalSceneCache)
    {
        List <string> bundlesToTransfer = new List <string>();
        List <string> bundlesToDelete   = new List <string>();
        string        manifestFilePath  = externalSceneCache + "/" + BUNDLE_MANAGER_MASTER_BUNDLE;

        string[] pullManifestCommand = { "-d pull", "\"" + manifestFilePath + "\"", "\"" + absoluteTempPath + "\"" };

        string output, error;

        if (adbTool.RunCommand(pullManifestCommand, null, out output, out error) == 0)
        {
            // An existing manifest file was found on device. Load hashes and upload bundles that have changed hashes.
            Debug.Log("[OVRBundleManager] - Scene bundle manifest file found. Decoding changes . . .");

            // Load hashes from remote manifest
            AssetBundle remoteBundle = AssetBundle.LoadFromFile(Path.Combine(absoluteTempPath, BUNDLE_MANAGER_MASTER_BUNDLE));
            if (remoteBundle == null)
            {
                OVRBundleTool.PrintError("Failed to load remote asset bundle manifest file.");
                return(false);
            }
            AssetBundleManifest remoteManifest = remoteBundle.LoadAsset <AssetBundleManifest>("AssetBundleManifest");

            Dictionary <string, Hash128> remoteBundleToHash = new Dictionary <string, Hash128>();
            if (remoteManifest != null)
            {
                string[] assetBundles = remoteManifest.GetAllAssetBundles();
                foreach (string bundleName in assetBundles)
                {
                    remoteBundleToHash[bundleName] = remoteManifest.GetAssetBundleHash(bundleName);
                }
            }
            remoteBundle.Unload(true);

            // Load hashes from local manifest
            AssetBundle localBundle = AssetBundle.LoadFromFile(BUNDLE_MANAGER_OUTPUT_PATH + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE
                                                               + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE);
            if (localBundle == null)
            {
                OVRBundleTool.PrintError("<color=red>Failed to load local asset bundle manifest file.\n</color>");
                return(false);
            }
            AssetBundleManifest localManifest = localBundle.LoadAsset <AssetBundleManifest>("AssetBundleManifest");

            if (localManifest != null)
            {
                Hash128 zeroHash = new Hash128(0, 0, 0, 0);

                // Build a list of dirty bundles that will have to be transfered
                string relativeSceneBundlesPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
                bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, BUNDLE_MANAGER_MASTER_BUNDLE));
                string[] assetBundles = localManifest.GetAllAssetBundles();
                foreach (string bundleName in assetBundles)
                {
                    if (!remoteBundleToHash.ContainsKey(bundleName))
                    {
                        bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
                    }
                    else
                    {
                        if (remoteBundleToHash[bundleName] != localManifest.GetAssetBundleHash(bundleName))
                        {
                            bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
                        }
                        remoteBundleToHash[bundleName] = zeroHash;
                    }
                }

                OVRBundleTool.PrintLog(bundlesToTransfer.Count + " dirty bundle(s) will be transfered.\n");
            }
        }
        else
        {
            if (output.Contains("does not exist") || output.Contains("No such file or directory"))
            {
                // Fresh install of asset bundles, transfer all asset bundles
                OVRBundleTool.PrintLog("Manifest file not found. Transfering all bundles . . . ");

                string[] mkdirCommand = { "-d shell", "mkdir -p", "\"" + externalSceneCache + "\"" };
                if (adbTool.RunCommand(mkdirCommand, null, out output, out error) == 0)
                {
                    string absoluteSceneBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."),
                                                                  Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE));

                    string[] assetBundlePaths = Directory.GetFiles(absoluteSceneBundlePath);
                    if (assetBundlePaths.Length != 0)
                    {
                        foreach (string path in assetBundlePaths)
                        {
                            if (!path.Contains(".manifest"))
                            {
                                bundlesToTransfer.Add(path);
                            }
                        }
                    }
                    else
                    {
                        OVRBundleTool.PrintError("Failed to locate scene bundles to transfer.");
                        return(false);
                    }
                }
            }
        }

        // If any adb error occured during manifest calculation, print it and return false
        if (!string.IsNullOrEmpty(error) || output.Contains("error"))
        {
            OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
            return(false);
        }

        // Transfer bundles to device
        DateTime transferStart = DateTime.Now;

        foreach (string bundle in bundlesToTransfer)
        {
            string   absoluteBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), bundle);
            string[] pushBundleCommand  = { "-d push", "\"" + absoluteBundlePath + "\"", "\"" + externalSceneCache + "\"" };
            adbTool.RunCommandAsync(pushBundleCommand, null);
        }
        Debug.Log("[OVRBundleManager] - Transfer took " + (DateTime.Now - transferStart).TotalSeconds + " seconds.");

        // Delete stale bundles on device
        if (bundlesToDelete.Count > 0)
        {
            foreach (string bundle in bundlesToDelete)
            {
                string   bundlePath          = externalSceneCache + "/" + bundle;
                string[] deleteBundleCommand = { "-d shell", "rm", "\"" + bundlePath + "\"" };
                adbTool.RunCommandAsync(deleteBundleCommand, null);
            }
            OVRBundleTool.PrintLog("Deleted " + bundlesToDelete.Count + " bundle(s) that were stale");
        }

        return(true);
    }
Пример #11
0
    public void OnPostprocessBuild(BuildReport report)
    {
#if UNITY_ANDROID
        if (autoIncrementVersion)
        {
            if ((report.summary.options & BuildOptions.Development) == 0)
            {
                PlayerSettings.Android.bundleVersionCode++;
                UnityEngine.Debug.Log("Incrementing version code to " + PlayerSettings.Android.bundleVersionCode);
            }
        }

        bool isExporting = true;
        foreach (var step in report.steps)
        {
            if (step.name.Contains("Compile scripts") ||
                step.name.Contains("Building scenes") ||
                step.name.Contains("Writing asset files") ||
                step.name.Contains("Preparing APK resources") ||
                step.name.Contains("Creating Android manifest") ||
                step.name.Contains("Processing plugins") ||
                step.name.Contains("Exporting project") ||
                step.name.Contains("Building Gradle project"))
            {
                OVRPlugin.SendEvent("build_step_" + step.name.ToLower().Replace(' ', '_'),
                                    step.duration.TotalSeconds.ToString(), "ovrbuild");
#if BUILDSESSION
                UnityEngine.Debug.LogFormat("build_step_" + step.name.ToLower().Replace(' ', '_') + ": {0}", step.duration.TotalSeconds.ToString());
#endif
                if (step.name.Contains("Building Gradle project"))
                {
                    isExporting = false;
                }
            }
        }
        OVRPlugin.AddCustomMetadata("build_step_count", report.steps.Length.ToString());
        if (report.summary.outputPath.Contains("apk"))         // Exclude Gradle Project Output
        {
            var fileInfo = new System.IO.FileInfo(report.summary.outputPath);
            OVRPlugin.AddCustomMetadata("build_output_size", fileInfo.Length.ToString());
        }
#endif
        if (!report.summary.outputPath.Contains("OVRGradleTempExport"))
        {
            OVRPlugin.SendEvent("build_complete", (System.DateTime.Now - buildStartTime).TotalSeconds.ToString(), "ovrbuild");
#if BUILDSESSION
            UnityEngine.Debug.LogFormat("build_complete: {0}", (System.DateTime.Now - buildStartTime).TotalSeconds.ToString());
#endif
        }

#if UNITY_ANDROID
        if (!isExporting)
        {
            // Get the hosts path to Android SDK
            if (adbTool == null)
            {
                adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath(false));
            }

            if (adbTool.isReady)
            {
                // Check to see if there are any ADB devices connected before continuing.
                List <string> devices = adbTool.GetDevices();
                if (devices.Count == 0)
                {
                    return;
                }

                // Clear current logs on device
                Process adbClearProcess;
                adbClearProcess = adbTool.RunCommandAsync(new string[] { "logcat --clear" }, null);

                // Add a timeout if we cannot get a response from adb logcat --clear in time.
                Stopwatch timeout = new Stopwatch();
                timeout.Start();
                while (!adbClearProcess.WaitForExit(100))
                {
                    if (timeout.ElapsedMilliseconds > 2000)
                    {
                        adbClearProcess.Kill();
                        return;
                    }
                }

                // Check if existing ADB process is still running, kill if needed
                if (adbProcess != null && !adbProcess.HasExited)
                {
                    adbProcess.Kill();
                }

                // Begin thread to time upload and install
                var thread = new Thread(delegate()
                {
                    TimeDeploy();
                });
                thread.Start();
            }
        }
#endif
    }
Пример #12
0
    public static bool IsAndroidSdkRootValid(string androidSdkRoot)
    {
        OVRADBTool tool = new OVRADBTool(androidSdkRoot);

        return(tool.isReady);
    }
Пример #13
0
    public static bool DeployAPK()
    {
        // Create new instance of ADB Tool
        var adbTool = new OVRADBTool(androidSdkPath);

        if (adbTool.isReady)
        {
            string apkPathLocal;
            string gradleExportFolder = Path.Combine(Path.Combine(gradleExport, productName), "build\\outputs\\apk\\debug");

            // Check to see if gradle output directory exists
            gradleExportFolder = gradleExportFolder.Replace("/", "\\");
            if (!Directory.Exists(gradleExportFolder))
            {
                UnityEngine.Debug.LogError("Could not find the gradle project at the expected path: " + gradleExportFolder);
                return(false);
            }

            // Search for output APK in gradle output directory
            apkPathLocal = Path.Combine(gradleExportFolder, productName + "-debug.apk");
            if (!System.IO.File.Exists(apkPathLocal))
            {
                UnityEngine.Debug.LogError(string.Format("Could not find {0} in the gradle project.", productName + "-debug.apk"));
                return(false);
            }

            string   output, error;
            DateTime timerStart;

            // Ensure that the Oculus temp directory is on the device by making it
            IncrementProgressBar("Making Temp directory on device");
            string[] mkdirCommand = { "-d shell", "mkdir -p", REMOTE_APK_PATH };
            if (adbTool.RunCommand(mkdirCommand, null, out output, out error) != 0)
            {
                return(false);
            }

            // Push APK to device, also time how long it takes
            timerStart = System.DateTime.Now;
            IncrementProgressBar("Pushing APK to device . . .");
            string[] pushCommand = { "-d push", "\"" + apkPathLocal + "\"", REMOTE_APK_PATH };
            if (adbTool.RunCommand(pushCommand, null, out output, out error) != 0)
            {
                return(false);
            }

            // Calculate the transfer speed and determine if user is using USB 2.0 or 3.0
            TimeSpan pushTime      = System.DateTime.Now - timerStart;
            FileInfo apkInfo       = new System.IO.FileInfo(apkPathLocal);
            double   transferSpeed = (apkInfo.Length / pushTime.TotalSeconds) / BYTES_TO_MEGABYTES;
            bool     informLog     = transferSpeed < USB_TRANSFER_SPEED_THRES;
            UnityEngine.Debug.Log("OVRADBTool: Push Success");

            // Install the APK package on the device
            IncrementProgressBar("Installing APK . . .");
            string apkPath = REMOTE_APK_PATH + "/" + productName + "-debug.apk";
            apkPath = apkPath.Replace(" ", "\\ ");
            string[] installCommand = { "-d shell", "pm install -r", apkPath };

            timerStart = System.DateTime.Now;
            if (adbTool.RunCommand(installCommand, null, out output, out error) != 0)
            {
                return(false);
            }
            TimeSpan installTime = System.DateTime.Now - timerStart;
            UnityEngine.Debug.Log("OVRADBTool: Install Success");

            // Start the application on the device
            IncrementProgressBar("Launching application on device . . .");
#if UNITY_2019_3_OR_NEWER
            string playerActivityName = "\"" + applicationIdentifier + "/com.unity3d.player.UnityPlayerActivity\"";
#else
            string playerActivityName = "\"" + applicationIdentifier + "/" + applicationIdentifier + ".UnityPlayerActivity\"";
#endif
            string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
            if (adbTool.RunCommand(appStartCommand, null, out output, out error) != 0)
            {
                return(false);
            }
            UnityEngine.Debug.Log("OVRADBTool: Application Start Success");

            // Send back metrics on push and install steps
            OVRPlugin.AddCustomMetadata("transfer_speed", transferSpeed.ToString());
            OVRPlugin.SendEvent("build_step_push_apk", pushTime.TotalSeconds.ToString(), "ovrbuild");
            OVRPlugin.SendEvent("build_step_install_apk", installTime.TotalSeconds.ToString(), "ovrbuild");

            IncrementProgressBar("Success!");

            // If the user is using a USB 2.0 cable, inform them about improved transfer speeds and estimate time saved
            if (informLog)
            {
                var usb3Time = apkInfo.Length / (USB_3_TRANSFER_SPEED * BYTES_TO_MEGABYTES);
                UnityEngine.Debug.Log(string.Format("OVRBuild has detected slow transfer speeds. A USB 3.0 cable is recommended to reduce the time it takes to deploy your project by approximatly {0:0.0} seconds", pushTime.TotalSeconds - usb3Time));
                return(true);
            }
        }
        else
        {
            UnityEngine.Debug.LogError("Could not find the ADB executable in the specified Android SDK directory.");
        }
        return(false);
    }
Пример #14
0
    private static void CheckForTransitionAPK()
    {
        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            string matchedPackageList, error;
            var    transitionPackageName = PlayerSettings.applicationIdentifier;
            if (useOptionalTransitionApkPackage)
            {
                transitionPackageName += ".transition";
            }
            string[] packageCheckCommand = new string[] { "-d shell pm list package", transitionPackageName };
            if (adbTool.RunCommand(packageCheckCommand, null, out matchedPackageList, out error) == 0)
            {
                if (string.IsNullOrEmpty(matchedPackageList))
                {
                    currentApkStatus = ApkStatus.NOT_INSTALLED;
                }
                else
                {
                    // adb "list package" command returns all package names that contains the given query package name
                    // Need to check if the transition package name is matched exactly
                    if (matchedPackageList.Contains("package:" + transitionPackageName + "\r\n"))
                    {
                        if (useOptionalTransitionApkPackage)
                        {
                            // If optional package name is used, it is deterministic that the transition apk is installed
                            currentApkStatus = ApkStatus.OK;
                        }
                        else
                        {
                            // get package info to check for TRANSITION_APK_VERSION_NAME
                            string[] dumpPackageInfoCommand = new string[] { "-d shell dumpsys package", transitionPackageName };
                            string   packageInfo;
                            if (adbTool.RunCommand(dumpPackageInfoCommand, null, out packageInfo, out error) == 0 &&
                                !string.IsNullOrEmpty(packageInfo) &&
                                packageInfo.Contains(OVRBundleManager.TRANSITION_APK_VERSION_NAME))
                            {
                                // Matched package name found, and the package info contains TRANSITION_APK_VERSION_NAME
                                currentApkStatus = ApkStatus.OK;
                            }
                            else
                            {
                                currentApkStatus = ApkStatus.NOT_INSTALLED;
                            }
                        }
                    }
                    else
                    {
                        // No matached package name returned
                        currentApkStatus = ApkStatus.NOT_INSTALLED;
                    }
                }
            }
            else if (error.Contains("no devices found"))
            {
                currentApkStatus = ApkStatus.DEVICE_NOT_CONNECTED;
            }
            else
            {
                currentApkStatus = ApkStatus.UNKNOWN;
            }
        }
    }
Пример #15
0
    private static bool DeploySceneBundles(List <OVRBundleTool.EditorSceneInfo> sceneList)
    {
        // Create Temp directory on local machine if it does not exist
        string tempDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "Temp");

        if (!Directory.Exists(tempDirectory))
        {
            Directory.CreateDirectory(tempDirectory);
        }
        string absoluteTempPath = Path.Combine(Path.Combine(Application.dataPath, ".."), tempDirectory);

        OVRBundleTool.PrintLog("Deploying scene bundles to device . . . ");

        OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());

        if (adbTool.isReady)
        {
            DateTime transferStart = DateTime.Now;

            for (int i = 0; i < sceneList.Count; ++i)
            {
                if (!sceneList[i].shouldDeploy)
                {
                    continue;
                }
                OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.TRANSFERRING, i);
            }

            // Transfer all scene bundles that are relavent
            if (!TransferSceneBundles(adbTool, absoluteTempPath, externalSceneCache))
            {
                return(false);
            }

            for (int i = 0; i < sceneList.Count; ++i)
            {
                if (!sceneList[i].shouldDeploy)
                {
                    continue;
                }
                OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.DEPLOYED, i);
            }

            // Create file to tell transition scene APK which scene to load and push it to the device
            string sceneLoadDataPath = Path.Combine(tempDirectory, OVRSceneLoader.sceneLoadDataName);
            if (File.Exists(sceneLoadDataPath))
            {
                File.Delete(sceneLoadDataPath);
            }

            StreamWriter writer = new StreamWriter(sceneLoadDataPath, true);
            // Write version and scene names
            long unixTime = (int)(DateTimeOffset.UtcNow.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
            writer.WriteLine(unixTime.ToString());
            for (int i = 0; i < sceneList.Count; i++)
            {
                if (!sceneList[i].shouldDeploy)
                {
                    continue;
                }
                writer.WriteLine(Path.GetFileNameWithoutExtension(sceneList[i].scenePath));
            }

            writer.Close();

            string   absoluteSceneLoadDataPath = Path.Combine(absoluteTempPath, OVRSceneLoader.sceneLoadDataName);
            string[] pushCommand = { "-d push", "\"" + absoluteSceneLoadDataPath + "\"", "\"" + externalSceneCache + "\"" };
            string   output, error;
            if (adbTool.RunCommand(pushCommand, null, out output, out error) == 0)
            {
                Debug.Log("[OVRBundleManager] Scene Load Data Pushed to Device.");
                return(true);
            }
            OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
        }
        else
        {
            OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
        }
        return(false);
    }
Пример #16
0
    // The actual window code goes here
    void OnGUI()
    {
        if (odhCalloutBackgroundStyle == null)
        {
            odhCalloutBackgroundStyle = new GUIStyle(EditorStyles.helpBox);
            var odhCalloutBackgroundStyleTex = new Texture2D(1, 1);
            odhCalloutBackgroundStyleTex.SetPixel(0, 0, new Color(0.9f, 0.8f, 0.2f, 0.2f));
            odhCalloutBackgroundStyleTex.Apply();
            odhCalloutBackgroundStyle.normal.background = odhCalloutBackgroundStyleTex;
        }

        if (odhCalloutTextStyle == null)
        {
            odhCalloutTextStyle          = new GUIStyle(EditorStyles.label);
            odhCalloutTextStyle.richText = true;
            odhCalloutTextStyle.wordWrap = true;
        }

        // ODH Callout Section
        GUILayout.BeginHorizontal(odhCalloutBackgroundStyle);
        var     script      = MonoScript.FromScriptableObject(this);
        string  assetPath   = AssetDatabase.GetAssetPath(script);
        string  editorPath  = Path.GetDirectoryName(assetPath);
        string  odhIconPath = Path.Combine(editorPath, "Textures\\odh_icon.png");
        Texture ODHIcon     = (Texture)EditorGUIUtility.Load(odhIconPath);

        GUILayout.Box(ODHIcon, GUILayout.Width(60.0f), GUILayout.Height(60.0f));

        GUILayout.BeginVertical();

        EditorGUILayout.LabelField("<b>This tool is deprecated.</b> Oculus recommends profiling builds through the Metrics section of "
                                   + "<b>Oculus Developer Hub</b>, a desktop companion tool that streamlines the Quest development workflow.",
                                   odhCalloutTextStyle);
        GUIContent ODHLabel = new GUIContent("Download Oculus Developer Hub");

#if UNITY_2021_1_OR_NEWER
        if (EditorGUILayout.LinkButton(ODHLabel))
#else
        if (GUILayout.Button(ODHLabel, GUILayout.ExpandWidth(false)))
#endif
        {
#if UNITY_EDITOR_WIN
            Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-win/?source=unity");
#elif UNITY_EDITOR_OSX
            Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-mac/?source=unity");
#endif
        }
        GUILayout.EndVertical();
        GUILayout.EndHorizontal();
        GUILayout.Space(15.0f);

        showAndroidOptions = EditorGUILayout.Foldout(showAndroidOptions, "Android Tools");

        if (showAndroidOptions)
        {
            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Android SDK root path: ", androidSdkRootPath);
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Start Server"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.StartServer(null);
                    EditorUtility.DisplayDialog("ADB StartServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Kill Server"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.KillServer(null);
                    EditorUtility.DisplayDialog("ADB KillServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Forward Port"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.ForwardPort(remoteListeningPort, null);
                    EditorUtility.DisplayDialog("ADB ForwardPort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                    OVRPlugin.SendEvent("device_metrics_profiler", (exitCode == 0 ? "adb_forward_success" : "adb_forward_failure"));
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            if (GUILayout.Button("Release Port"))
            {
                if (adbTool == null)
                {
                    adbTool = new OVRADBTool(androidSdkRootPath);
                }
                if (adbTool.isReady)
                {
                    int exitCode = adbTool.ReleasePort(remoteListeningPort, null);
                    EditorUtility.DisplayDialog("ADB ReleasePort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
                }
                else
                {
                    EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
                }
            }
            GUILayout.EndHorizontal();
        }

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

        GUILayout.BeginHorizontal();
        remoteListeningPort = EditorGUILayout.DelayedIntField("Remote Port", remoteListeningPort);

        if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Disconnected)
        {
            if (GUILayout.Button("Connect"))
            {
                ConnectPerfMetricsTcpServer();
                pauseReceiveMetrics = false;
                OVRPlugin.SendEvent("device_metrics_profiler", "connect");
            }
        }
        else
        {
            if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Connecting)
            {
                if (GUILayout.Button("Connecting ... Click again to Cancel"))
                {
                    DisconnectPerfMetricsTcpServer();
                    OVRPlugin.SendEvent("device_metrics_profiler", "cancel");
                }
            }
            else
            {
                if (GUILayout.Button("Disconnect"))
                {
                    DisconnectPerfMetricsTcpServer();
                    OVRPlugin.SendEvent("device_metrics_profiler", "disconnect");
                }

                if (GUILayout.Button(pauseReceiveMetrics ? "Continue" : "Pause"))
                {
                    pauseReceiveMetrics = !pauseReceiveMetrics;
                }
            }
        }
        GUILayout.EndHorizontal();

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);

        lock (receivedMetricsList)
        {
            PresentIntProperty("Frame Count", "frameCount");
            PresentIntProperty("Dropped Frame Count", "compositorDroppedFrameCount");

            float?avgFrameTime = GetAveragePerfValueFloat("deltaFrameTime");
            if (avgFrameTime.HasValue)
            {
                float fps = 1.0f / avgFrameTime.Value;
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("FPS", GUILayout.Width(labelWidth));
                EditorGUILayout.LabelField(string.Format("{0:F1}", fps));
                EditorGUILayout.EndHorizontal();
            }

            int?  deviceCpuClockLevel          = GetLatestPerfValueInt("deviceCpuClockLevel");
            int?  deviceGpuClockLevel          = GetLatestPerfValueInt("deviceGpuClockLevel");
            float?deviceCpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceCpuClockFrequencyInMHz");
            float?deviceGpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceGpuClockFrequencyInMHz");

            if (deviceCpuClockLevel.HasValue || deviceCpuClockFrequencyInMHz.HasValue)
            {
                string cpuLabel;
                string cpuText;
                if (deviceCpuClockLevel.HasValue && deviceCpuClockFrequencyInMHz.HasValue)
                {
                    cpuLabel = "CPU Level (Freq)";
                    cpuText  = string.Format("{0} ({1:F0} MHz)", deviceCpuClockLevel, deviceCpuClockFrequencyInMHz);
                }
                else if (deviceCpuClockLevel.HasValue)
                {
                    cpuLabel = "CPU Level";
                    cpuText  = string.Format("{0}", deviceCpuClockLevel);
                }
                else
                {
                    cpuLabel = "CPU Frequency";
                    cpuText  = string.Format("{0:F0} MHz", deviceCpuClockFrequencyInMHz);
                }
                PresentText(cpuLabel, cpuText);
            }

            if (deviceGpuClockLevel.HasValue || deviceGpuClockFrequencyInMHz.HasValue)
            {
                string cpuLabel;
                string cpuText;
                if (deviceGpuClockLevel.HasValue && deviceGpuClockFrequencyInMHz.HasValue)
                {
                    cpuLabel = "GPU Level (Freq)";
                    cpuText  = string.Format("{0} ({1:F0} MHz)", deviceGpuClockLevel, deviceGpuClockFrequencyInMHz);
                }
                else if (deviceGpuClockLevel.HasValue)
                {
                    cpuLabel = "GPU Level";
                    cpuText  = string.Format("{0}", deviceGpuClockLevel);
                }
                else
                {
                    cpuLabel = "GPU Frequency";
                    cpuText  = string.Format("{0:F0} MHz", deviceGpuClockFrequencyInMHz);
                }
                PresentText(cpuLabel, cpuText);
            }

            PresentColumnTitles("Current", "Average", "Peak");

            PresentFloatTimeInMs("Frame Time", "deltaFrameTime", 0.020f, true, true);
            PresentFloatTimeInMs("App CPU Time", "appCpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("App GPU Time", "appGpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("Compositor CPU Time", "compositorCpuTime", 0.020f, true, true);
            PresentFloatTimeInMs("Compositor GPU Time", "compositorGpuTime", 0.020f, true, true);
            PresentFloatPercentage("CPU Util (Average)", "systemCpuUtilAveragePercentage", false, false);
            PresentFloatPercentage("CPU Util (Worst Core)", "systemCpuUtilWorstPercentage", false, false);
            PresentFloatPercentage("GPU Util", "systemGpuUtilPercentage", false, false);
        }
    }
Пример #17
0
    public static bool DeployAPK()
    {
        // Create new instance of ADB Tool
        var adbTool = new OVRADBTool(androidSdkPath);

        if (adbTool.isReady)
        {
            string apkPathLocal;
            string buildFlavor        = isDevelopmentBuild ? "debug" : "release";
            string gradleExportFolder = Path.Combine(gradleExport, productName, $"build\\outputs\\apk\\{buildFlavor}");

            // Check to see if gradle output directory exists
            gradleExportFolder = gradleExportFolder.Replace("/", "\\");
            if (!Directory.Exists(gradleExportFolder))
            {
                UnityEngine.Debug.LogError("Could not find the gradle project at the expected path: " + gradleExportFolder);
                return(false);
            }

            // Search for output APK in gradle output directory
            apkPathLocal = Path.Combine(gradleExportFolder, productName + $"-{buildFlavor}.apk");
            if (!System.IO.File.Exists(apkPathLocal))
            {
                UnityEngine.Debug.LogError(string.Format("Could not find {0} in the gradle project.", productName + $"-{buildFlavor}.apk"));
                return(false);
            }

            string   output, error;
            DateTime timerStart;

            // Ensure that the Oculus temp directory is on the device by making it
            IncrementProgressBar("Making Temp directory on device");
            string[] mkdirCommand = { "-d shell", "mkdir -p", REMOTE_APK_PATH };
            if (adbTool.RunCommand(mkdirCommand, null, out output, out error) != 0)
            {
                return(false);
            }

            // Push APK to device, also time how long it takes
            timerStart = System.DateTime.Now;
            IncrementProgressBar("Pushing APK to device . . .");
            string[] pushCommand = { "-d push", "\"" + apkPathLocal + "\"", REMOTE_APK_PATH };
            if (adbTool.RunCommand(pushCommand, null, out output, out error) != 0)
            {
                return(false);
            }

            // Calculate the transfer speed and determine if user is using USB 2.0 or 3.0
            // Only bother informing the user on non-trivial transfers, as for very short
            // periods of time, things like process creation overhead can dwarf the actual
            // transfer time.
            TimeSpan pushTime      = System.DateTime.Now - timerStart;
            bool     trivialPush   = pushTime.TotalSeconds < TRANSFER_SPEED_CHECK_THRESHOLD;
            long?    apkSize       = (trivialPush ? (long?)null : new System.IO.FileInfo(apkPathLocal).Length);
            double?  transferSpeed = (apkSize / pushTime.TotalSeconds) / BYTES_TO_MEGABYTES;
            bool     informLog     = transferSpeed.HasValue && transferSpeed.Value < USB_TRANSFER_SPEED_THRES;
            UnityEngine.Debug.Log("OVRADBTool: Push Success");

            // Install the APK package on the device
            IncrementProgressBar("Installing APK . . .");
            string apkPath = REMOTE_APK_PATH + "/" + productName + "-debug.apk";
            apkPath = apkPath.Replace(" ", "\\ ");
            string[] installCommand = { "-d shell", "pm install -r", apkPath };

            timerStart = System.DateTime.Now;
            if (adbTool.RunCommand(installCommand, null, out output, out error) != 0)
            {
                return(false);
            }
            TimeSpan installTime = System.DateTime.Now - timerStart;
            UnityEngine.Debug.Log("OVRADBTool: Install Success");

            // Start the application on the device
            IncrementProgressBar("Launching application on device . . .");
#if UNITY_2019_3_OR_NEWER
            string playerActivityName = "\"" + applicationIdentifier + "/com.unity3d.player.UnityPlayerActivity\"";
#else
            string playerActivityName = "\"" + applicationIdentifier + "/" + applicationIdentifier + ".UnityPlayerActivity\"";
#endif
            string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
            if (adbTool.RunCommand(appStartCommand, null, out output, out error) != 0)
            {
                return(false);
            }
            UnityEngine.Debug.Log("OVRADBTool: Application Start Success");

            IncrementProgressBar("Success!");

            // If the user is using a USB 2.0 cable, inform them about improved transfer speeds and estimate time saved
            if (informLog)
            {
                float usb3Time = apkSize.Value / (USB_3_TRANSFER_SPEED * BYTES_TO_MEGABYTES); // `informLog` can't be true if `apkSize` is null.
                UnityEngine.Debug.Log(string.Format("OVRBuild has detected slow transfer speeds. A USB 3.0 cable is recommended to reduce the time it takes to deploy your project by approximatly {0:0.0} seconds", pushTime.TotalSeconds - usb3Time));
                return(true);
            }
        }
        else
        {
            UnityEngine.Debug.LogError("Could not find the ADB executable in the specified Android SDK directory.");
        }
        return(false);
    }