////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public object StartWithoutDebugging(int launchOptions, LaunchConfiguration launchConfig, LaunchProps [] launchProps, IDictionary<string, string> projectProperties)
        {
            LoggingUtils.PrintFunction ();

              if (launchConfig == null)
              {
            throw new ArgumentNullException ("launchConfig");
              }

              if (launchProps == null)
              {
            throw new ArgumentNullException ("launchProps");
              }

              if (projectProperties == null)
              {
            throw new ArgumentNullException ("projectProperties");
              }

              try
              {
            //
            // Refresh ADB service and evaluate a list of connected devices or emulators.
            //

            AndroidAdb.Refresh ();

            AndroidDevice debuggingDevice = GetPrioritisedConnectedDevice ();

            if (debuggingDevice == null)
            {
              throw new InvalidOperationException ("No device/emulator found or connected. Check status using \"adb devices\".");
            }

            //
            // Construct VS launch settings to debug or attach to the specified target application.
            //

            launchOptions |= (int) DebugLaunchOptions.Silent;

            DebugLaunchSettings nonDebuglaunchSettings = new DebugLaunchSettings ((DebugLaunchOptions) launchOptions);

            nonDebuglaunchSettings.LaunchDebugEngineGuid = new Guid ("8310DAF9-1043-4C8E-85A0-FF68896E1922");

            nonDebuglaunchSettings.PortSupplierGuid = new Guid ("3AEE417F-E5F9-4B89-BC31-20534C99B7F5");

            nonDebuglaunchSettings.PortName = debuggingDevice.ID;

            nonDebuglaunchSettings.Options = launchConfig.ToString ();

            nonDebuglaunchSettings.Executable = launchConfig ["TargetApk"];

            nonDebuglaunchSettings.LaunchOperation = DebugLaunchOperation.Custom;

            return nonDebuglaunchSettings;
              }
              catch (Exception e)
              {
            LoggingUtils.HandleException (e);

            throw;
              }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public object StartWithDebugging(int launchOptions, LaunchConfiguration launchConfig, LaunchProps [] launchProps, IDictionary<string, string> projectProperties)
        {
            LoggingUtils.PrintFunction ();

              if (launchConfig == null)
              {
            throw new ArgumentNullException ("launchConfig");
              }

              if (launchProps == null)
              {
            throw new ArgumentNullException ("launchProps");
              }

              if (projectProperties == null)
              {
            throw new ArgumentNullException ("projectProperties");
              }

              try
              {
            //
            // Refresh ADB service and evaluate a list of connected devices or emulators.
            //

            AndroidAdb.Refresh ();

            AndroidDevice debuggingDevice = GetPrioritisedConnectedDevice ();

            if (debuggingDevice == null)
            {
              throw new InvalidOperationException ("No device/emulator found or connected. Check status using \"adb devices\".");
            }

            //
            // Enforce required device/emulator properties.
            //

            foreach (LaunchProps prop in launchProps)
            {
              debuggingDevice.Shell ("setprop", string.Format (CultureInfo.InvariantCulture, "{0} {1}", prop.Item1, prop.Item2));
            }

            //
            // Construct VS launch settings to debug or attach to the specified target application.
            //

            bool shouldAttach = false;

            #if false
            AndroidProcess [] debuggingDeviceProcesses = debuggingDevice.GetProcesses ();

            foreach (AndroidProcess process in debuggingDeviceProcesses)
            {
              if (process.Name.Equals (applicationPackageName))
              {
            shouldAttach = true;

            break;
              }
            }
            #endif

            launchOptions |= (int) DebugLaunchOptions.Silent;

            DebugLaunchSettings debugLaunchSettings = new DebugLaunchSettings ((DebugLaunchOptions) launchOptions);

            debugLaunchSettings.LaunchDebugEngineGuid = new Guid ("8310DAF9-1043-4C8E-85A0-FF68896E1922");

            debugLaunchSettings.PortSupplierGuid = new Guid ("3AEE417F-E5F9-4B89-BC31-20534C99B7F5");

            debugLaunchSettings.PortName = debuggingDevice.ID;

            debugLaunchSettings.Options = launchConfig.ToString ();

            if (shouldAttach)
            {
              debugLaunchSettings.Executable = launchConfig ["PackageName"];

              debugLaunchSettings.LaunchOperation = DebugLaunchOperation.AlreadyRunning;
            }
            else
            {
              //
              // Determine whether the application is currently installed, and if it is;
              // check last modified date to ensure we don't re-installed unchanged binaries.
              //

              bool upToDateCheck = launchConfig ["UpToDateCheck"].Equals ("true");

              bool appIsInstalled = false;

              bool appIsOutOfDate = true;

              if (upToDateCheck)
              {
            FileInfo targetApkFileInfo = new FileInfo (launchConfig ["TargetApk"]);

            try
            {
              string [] adbPmPathOutput = debuggingDevice.Shell ("pm", "path " + launchConfig ["PackageName"]).Replace ("\r", "").Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

              foreach (string line in adbPmPathOutput)
              {
                if (line.StartsWith ("package:", StringComparison.OrdinalIgnoreCase))
                {
                  appIsInstalled = true;

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' already installed on target '{1}'.", launchConfig ["PackageName"], debuggingDevice.ID), false));

                  string path = line.Substring ("package:".Length);

                  //
                  // Get the target device/emulator's UTC current time.
                  //
                  //   This is done by specifying the '-u' argument to 'date'. Despite this though,
                  //   the returned string will always claim to be in GMT:
                  //
                  //   i.e: "Fri Jan  9 14:35:23 GMT 2015"
                  //

                  DateTime debuggingDeviceUtcTime;

                  try
                  {
                    string [] deviceDateOutput = debuggingDevice.Shell ("date", "-u").Replace ("\r", "").Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                    string debuggingDeviceUtcTimestamp = deviceDateOutput [0];

                    string [] debuggingDeviceUtcTimestampComponents = debuggingDeviceUtcTimestamp.Split (new char [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    debuggingDeviceUtcTimestampComponents [4] = "-00:00";

                    if (!DateTime.TryParseExact (string.Join (" ", debuggingDeviceUtcTimestampComponents), "ddd MMM  d HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out debuggingDeviceUtcTime))
                    {
                      break;
                    }

                    debuggingDeviceUtcTime = debuggingDeviceUtcTime.ToUniversalTime ();
                  }
                  catch (Exception e)
                  {
                    throw new InvalidOperationException ("Failed to evaluate device local time.", e);
                  }

                  //
                  // Convert current device/emulator time to UTC, and probe the working machine's time too.
                  //

                  DateTime thisMachineUtcTime = DateTime.UtcNow;

                  TimeSpan thisMachineUtcVersusDeviceUtc = debuggingDeviceUtcTime - thisMachineUtcTime;

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "Current UTC time on '{0}': {1}", debuggingDevice.ID, debuggingDeviceUtcTime.ToString ()), false));

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "Current UTC time on '{0}': {1}", System.Environment.MachineName, thisMachineUtcTime.ToString ()), false));

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "Difference in UTC time between '{0}' and '{1}': {2}", System.Environment.MachineName, debuggingDevice.ID, thisMachineUtcVersusDeviceUtc.ToString ()), false));

                  //
                  // Check the last modified date; ls output currently uses this format:
                  //
                  // -rw-r--r-- system   system   11533274 2015-01-09 13:47 com.example.native_activity-2.apk
                  //

                  DateTime lastModifiedTimestampDeviceLocalTime;

                  try
                  {
                    string [] extendedLsOutput = debuggingDevice.Shell ("ls -l", path).Replace ("\r", "").Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                    string [] extendedLsOutputComponents = extendedLsOutput [0].Split (new char [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    string date = extendedLsOutputComponents [4];

                    string time = extendedLsOutputComponents [5];

                    if (!DateTime.TryParseExact (date + " " + time, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out lastModifiedTimestampDeviceLocalTime))
                    {
                      break;
                    }
                  }
                  catch (Exception e)
                  {
                    throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture, "Failed to evaluate device local modified time of: {0}", path), e);
                  }

                  //
                  // Calculate how long ago the APK was changed, according to the device's local time.
                  //

                  TimeSpan timeSinceLastModification = debuggingDeviceUtcTime - lastModifiedTimestampDeviceLocalTime;

                  DateTime debuggingDeviceUtcTimeAtLastModification = debuggingDeviceUtcTime - timeSinceLastModification;

                  DateTime thisMachineUtcTimeAtLastModification = thisMachineUtcTime - timeSinceLastModification;

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' was last modified on '{1}' at: {2}.", launchConfig ["PackageName"], debuggingDevice.ID, debuggingDeviceUtcTimeAtLastModification.ToString ()), false));

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "{0} (on {1}) was around {2} (on {3}).", debuggingDeviceUtcTimeAtLastModification.ToString (), debuggingDevice.ID, thisMachineUtcTimeAtLastModification.ToString (), System.Environment.MachineName), false));

                  LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' was last modified on '{1}' at: {2}.", Path.GetFileName (targetApkFileInfo.FullName), System.Environment.MachineName, targetApkFileInfo.LastWriteTime.ToString ()), false));

                  if ((targetApkFileInfo.LastWriteTime + thisMachineUtcVersusDeviceUtc) > thisMachineUtcTimeAtLastModification)
                  {
                    LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' was determined to be out-of-date. Reinstalling...", launchConfig ["PackageName"]), false));
                  }
                  else
                  {
                    appIsOutOfDate = false;
                  }

                  break;
                }
              }
            }
            catch (Exception)
            {
              appIsInstalled = false;
            }
              }
              else
              {
            LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate ("Skipping up-to-date check.", false));
              }

              if (!appIsInstalled || appIsOutOfDate)
              {
            LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "Installing '{0}' to '{1}'...", launchConfig ["PackageName"], debuggingDevice.ID), false));

            InstallApplicationAsync (debuggingDevice, launchConfig);

            LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' installed successfully.", launchConfig ["PackageName"]), false));
              }
              else
              {
            LoggingUtils.RequireOk (m_debugConnectionService.LaunchDialogUpdate (string.Format (CultureInfo.InvariantCulture, "'{0}' on '{1}' is up-to-date. Skipping installation...", launchConfig ["PackageName"], debuggingDevice.ID), false));
              }

              debugLaunchSettings.Executable = launchConfig ["TargetApk"];

              debugLaunchSettings.LaunchOperation = DebugLaunchOperation.Custom;
            }

            return debugLaunchSettings;
              }
              catch (Exception e)
              {
            LoggingUtils.HandleException (e);

            throw;
              }
        }