////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public AndroidDevice GetPrioritisedConnectedDevice()
        {
            //
            // Refresh ADB service and evaluate a list of connected devices or emulators.
            //
            // We want to prioritise devices over emulators here, which makes the logic a little dodgy.
            //

            LoggingUtils.PrintFunction();

            var connectedDevices = AndroidAdb.GetConnectedDevices();

            foreach (var device in connectedDevices)
            {
                if (!device.IsEmulator)
                {
                    return(device);
                }
            }

            foreach (var device in connectedDevices)
            {
                if (device.IsEmulator)
                {
                    return(device);
                }
            }

            return(null);
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        protected int CreatePort(IDebugPortRequest2 portRequest, out IDebugPort2 port)
        {
            LoggingUtils.PrintFunction();

            try
            {
                string requestPortName;

                LoggingUtils.RequireOk(portRequest.GetPortName(out requestPortName));

                if (string.IsNullOrWhiteSpace(requestPortName))
                {
                    throw new InvalidOperationException("Invalid/empty port name");
                }

                AndroidDevice device = AndroidAdb.GetConnectedDeviceById(requestPortName);

                if (device == null)
                {
                    throw new InvalidOperationException("Failed to find a device with the name: " + requestPortName);
                }

                port = new DebuggeePort(this, device);

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

                port = null;

                return(Constants.E_FAIL);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public AndroidDevice GetPrioritisedConnectedDevice()
        {
            //
            // Refresh ADB service and evaluate a list of connected devices or emulators.
            //
            // We want to prioritise devices over emulators here, which makes the logic a little dodgy.
            //

            LoggingUtils.PrintFunction();

            AndroidDevice debuggingDevice = null;

            AndroidDevice [] connectedDevices = AndroidAdb.GetConnectedDevices();

            if (connectedDevices.Length > 0)
            {
                for (int i = 0; i < connectedDevices.Length; ++i)
                {
                    if (!connectedDevices [i].IsEmulator)
                    {
                        debuggingDevice = connectedDevices [i];
                    }
                }

                if (debuggingDevice == null)
                {
                    debuggingDevice = connectedDevices [0];
                }
            }

            return(debuggingDevice);
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public async Task <int> OnDebuggerLogcatEvent(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, Guid riidEvent, uint dwAttrib)
        {
            LoggingUtils.PrintFunction();

            try
            {
                DebugEngineEvent.DebuggerLogcatEvent debuggerLogcatEvent = pEvent as DebugEngineEvent.DebuggerLogcatEvent;

                using (SyncRedirectProcess command = AndroidAdb.AdbCommand(debuggerLogcatEvent.HostDevice, "logcat", "-c"))
                {
                    command.StartAndWaitForExit();
                }

                m_adbLogcatProcess = AndroidAdb.AdbCommandAsync(debuggerLogcatEvent.HostDevice, "logcat", "");

                m_adbLogcatListener = new DeviceLogcatListener();

                m_adbLogcatProcess.Start(m_adbLogcatListener);

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

                return(VSConstants.E_FAIL);
            }
        }
示例#5
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        protected DebuggeePort CreatePort(IDebugPortRequest2 portRequest)
        {
            AndroidAdb.Refresh();

            string requestPortName;

            LoggingUtils.RequireOk(portRequest.GetPortName(out requestPortName));

            AndroidDevice device = AndroidAdb.GetConnectedDeviceById(requestPortName);

            return(new DebuggeePort(this, device));
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public Task <DebugLaunchSettings> StartWithoutDebugging(int launchOptions, LaunchConfiguration launchConfig, ICollection <LaunchProps> launchProps, IDictionary <string, string> projectProperties)
        {
            LoggingUtils.PrintFunction();

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

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

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

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

            AndroidAdb.Refresh();

            AndroidDevice debuggingDevice = GetPrioritisedConnectedDevice() ?? 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(Task.FromResult(nonDebuglaunchSettings));
        }
示例#7
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

                AndroidAdb.Refresh();

                AndroidDevice debuggingDevice = GetPriortisedConnectedDevice();

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

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

                launchOptionsFlags |= (int)DebugLaunchOptions.Silent;

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

                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 int EnumPersistedPorts(BSTR_ARRAY PortNames, out IEnumDebugPorts2 ppEnum)
        {
            //
            // This method retrieves an object that allows enumeration of the list of persisted ports.
            //

            LoggingUtils.PrintFunction();

            try
            {
#if false
                AndroidAdb.Refresh();

                if (PortNames.dwCount > 0)
                {
                    // TODO: This conversion process is tricky, and still broken.
                    _BSTR_ARRAY portNames = (_BSTR_ARRAY)Marshal.PtrToStructure(PortNames.Members, typeof(_BSTR_ARRAY));

                    for (int i = 0; i < PortNames.dwCount; ++i)
                    {
                        IDebugPort2 ppPort;

                        AndroidDevice device = AndroidAdb.GetConnectedDeviceById(portNames.Members [i]);

                        LoggingUtils.RequireOk(AddPort(new DevicePortRequest(device), out ppPort));
                    }
                }
#endif

                LoggingUtils.RequireOk(EnumPorts(out ppEnum));

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

                ppEnum = null;

                return(Constants.E_FAIL);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public int EnumPorts(out IEnumDebugPorts2 ppEnum)
        {
            //
            // Retrieves a list of all the ports supplied by a port supplier.
            //

            LoggingUtils.PrintFunction();

            try
            {
                AndroidAdb.Refresh();

                AndroidDevice [] connectedDevices = AndroidAdb.GetConnectedDevices();

                foreach (AndroidDevice device in connectedDevices)
                {
                    IDebugPort2 ppPort;

                    LoggingUtils.RequireOk(AddPort(new DevicePortRequest(device), out ppPort));
                }

                IDebugPort2 [] ports = new IDebugPort2 [m_registeredPorts.Count];

                m_registeredPorts.Values.CopyTo(ports, 0);

                ppEnum = new DebugPortEnumerator(ports);

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

                ppEnum = null;

                return(Constants.E_FAIL);
            }
        }
示例#10
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public int OnDebuggerLogcatEvent(IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib)
        {
            LoggingUtils.PrintFunction();

            try
            {
                DebugEngineEvent.DebuggerLogcatEvent debuggerLogcatEvent = pEvent as DebugEngineEvent.DebuggerLogcatEvent;

                using (SyncRedirectProcess command = AndroidAdb.AdbCommand(debuggerLogcatEvent.HostDevice, "logcat", "-c"))
                {
                    command.StartAndWaitForExit();
                }

                m_adbLogcatProcess = AndroidAdb.AdbCommandAsync(debuggerLogcatEvent.HostDevice, "logcat", "");

                m_adbLogcatListener = new DeviceLogcatListener();

                if (m_adbLogcatProcess == null)
                {
                    throw new InvalidOperationException("Failed to launch logcat application.");
                }

                if (m_adbLogcatListener == null)
                {
                    throw new InvalidOperationException("Failed to launch logcat listener.");
                }

                m_adbLogcatProcess.Start(m_adbLogcatListener);

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

                return(VSConstants.E_FAIL);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public async Task <DebugLaunchSettings> StartWithDebugging(int launchOptions, LaunchConfiguration launchConfig, ICollection <LaunchProps> launchProps, IDictionary <string, string> projectProperties)
        {
            LoggingUtils.PrintFunction();

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

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

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

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

            AndroidAdb.Refresh();

            AndroidDevice debuggingDevice = GetPrioritisedConnectedDevice() ?? throw new InvalidOperationException("No device/emulator found or connected. Check status using \"adb devices\".");

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

            foreach (var 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
                    {
                        var 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(await 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
                                {
                                    var deviceDateOutput = debuggingDevice.Shell("date", "-u").Replace("\r", "").Split(new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                                    string debuggingDeviceUtcTimestamp = deviceDateOutput [0];

                                    var 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;

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

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

                                await 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
                                {
                                    var extendedLsOutput = debuggingDevice.Shell("ls -l", path).Replace("\r", "").Split(new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                                    var 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;

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

                                await 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);

                                await 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)
                                {
                                    await 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
                {
                    await m_debugConnectionService.LaunchDialogUpdate("Skipping up-to-date check.", false);
                }

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

                    InstallApplicationAsync(debuggingDevice, launchConfig);

                    await m_debugConnectionService.LaunchDialogUpdate(string.Format(CultureInfo.InvariantCulture, "'{0}' installed successfully.", launchConfig ["PackageName"]), false);
                }
                else
                {
                    await 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);
        }