From b4d29f0a2946703b63b1a189ae62bc8e9166c4fe Mon Sep 17 00:00:00 2001 From: Chris Baudin Date: Wed, 8 Feb 2017 00:06:25 -0800 Subject: [PATCH 1/5] Initial implementation of wrapper for the ETW APIs (issue #31) --- .../MockDataGenerator/Program.cs | 3 + .../UnitTestProject/Core/EtwTests.cs | 108 +++++++++++++ ...ocket_api_etw_session_realtime_Default.dat | 1 + .../api_etw_customproviders_Default.dat | 1 + .../Defaults/api_etw_providers_Default.dat | 1 + .../UnitTestProject/UnitTestProject.csproj | 10 ++ .../Core/Etw.cs | 144 +++++++++++++++++- 7 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs create mode 100644 WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/WebSocket_api_etw_session_realtime_Default.dat create mode 100644 WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat create mode 100644 WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat diff --git a/WindowsDevicePortalWrapper/MockDataGenerator/Program.cs b/WindowsDevicePortalWrapper/MockDataGenerator/Program.cs index fdf35088..a86be83a 100644 --- a/WindowsDevicePortalWrapper/MockDataGenerator/Program.cs +++ b/WindowsDevicePortalWrapper/MockDataGenerator/Program.cs @@ -40,8 +40,11 @@ public class Program new Endpoint(HttpMethods.Get, DevicePortal.IpConfigApi), new Endpoint(HttpMethods.Get, DevicePortal.SystemPerfApi), new Endpoint(HttpMethods.Get, DevicePortal.RunningProcessApi), + new Endpoint(HttpMethods.Get, DevicePortal.CustomEtwProvidersApi), + new Endpoint(HttpMethods.Get, DevicePortal.EtwProvidersApi), new Endpoint(HttpMethods.WebSocket, DevicePortal.SystemPerfApi), new Endpoint(HttpMethods.WebSocket, DevicePortal.RunningProcessApi), + new Endpoint(HttpMethods.WebSocket, DevicePortal.RealtimeEtwSessionApi), // HoloLens specific endpoints new Endpoint(HttpMethods.Get, DevicePortal.HolographicIpdApi), diff --git a/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs new file mode 100644 index 00000000..7a7f61d2 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs @@ -0,0 +1,108 @@ +//---------------------------------------------------------------------------------------------- +// +// Licensed under the MIT License. See LICENSE.TXT in the project root license information. +// +//---------------------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Microsoft.Tools.WindowsDevicePortal.DevicePortal; + +namespace Microsoft.Tools.WindowsDevicePortal.Tests.Core +{ + /// + /// Test class for ETW APIs. + /// + [TestClass] + public class EtwTests : BaseTests + { + /// + /// Basic test of GET method for getting a list of custom registered ETW providers. + /// + [TestMethod] + public void GetCustomEtwProvidersTest() + { + TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.CustomEtwProvidersApi, HttpMethods.Get); + + Task getCustomEtwProvidersTask = TestHelpers.Portal.GetCustomEtwProvidersAsync(); + getCustomEtwProvidersTask.Wait(); + + Assert.AreEqual(TaskStatus.RanToCompletion, getCustomEtwProvidersTask.Status); + + ValidateEtwProviders(getCustomEtwProvidersTask.Result); + } + + /// + /// Basic test of GET method for getting a list of registered ETW providers. + /// + [TestMethod] + public void GetEtwProvidersTest() + { + TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.EtwProvidersApi, HttpMethods.Get); + + Task getEtwProvidersTask = TestHelpers.Portal.GetEtwProvidersAsync(); + getEtwProvidersTask.Wait(); + + Assert.AreEqual(TaskStatus.RanToCompletion, getEtwProvidersTask.Status); + + ValidateEtwProviders(getEtwProvidersTask.Result); + } + + [TestMethod] + public void GetEtwEventsTest() + { + TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.RealtimeEtwSessionApi, HttpMethods.WebSocket); + + ManualResetEvent etwEventsReceived = new ManualResetEvent(false); + EtwEvents etwEvents = null; + + WindowsDevicePortal.WebSocketMessageReceivedEventHandler etwEventsReceivedHandler = + delegate (DevicePortal sender, WebSocketMessageReceivedEventArgs args) + { + if (args.Message != null) + { + etwEvents = args.Message; + etwEventsReceived.Set(); + } + }; + + TestHelpers.Portal.RealtimeEventsMessageReceived += etwEventsReceivedHandler; + + Task startListeningForEtwEventsTask = TestHelpers.Portal.StartListeningForEtwEventsAsync(); + startListeningForEtwEventsTask.Wait(); + Assert.AreEqual(TaskStatus.RanToCompletion, startListeningForEtwEventsTask.Status); + + etwEventsReceived.WaitOne(); + + Task stopListeningForEtwEventsTask = TestHelpers.Portal.StopListeningForEtwEventsAsync(); + stopListeningForEtwEventsTask.Wait(); + Assert.AreEqual(TaskStatus.RanToCompletion, stopListeningForEtwEventsTask.Status); + + TestHelpers.Portal.RealtimeEventsMessageReceived -= etwEventsReceivedHandler; + + ValidateEtwEvents(etwEvents); + } + + /// + /// Validate the returned from the tests. + /// + /// The to validate. + private static void ValidateEtwEvents(EtwEvents etwEvents) + { + Assert.IsNotNull(etwEvents); + } + + /// + /// Validate the returned from the tests. + /// + /// The to validate. + private static void ValidateEtwProviders(EtwProviders etw) + { + EtwProviderInfo providerInfo = etw?.Providers?.FirstOrDefault(); + Assert.IsNotNull(providerInfo); + } + } +} diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/WebSocket_api_etw_session_realtime_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/WebSocket_api_etw_session_realtime_Default.dat new file mode 100644 index 00000000..f7bcb871 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/WebSocket_api_etw_session_realtime_Default.dat @@ -0,0 +1 @@ +{ } \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat new file mode 100644 index 00000000..0b713c77 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat @@ -0,0 +1 @@ +{ "Providers": [ { "GUID": "ProviderGUID", "Name": "ProviderName" } ] } \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat new file mode 100644 index 00000000..0b713c77 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat @@ -0,0 +1 @@ +{ "Providers": [ { "GUID": "ProviderGUID", "Name": "ProviderName" } ] } \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj b/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj index 5f96bccb..7ff5def3 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj +++ b/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj @@ -62,6 +62,7 @@ + @@ -85,9 +86,18 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index 91d6b976..64b6ffcb 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -4,6 +4,11 @@ // //---------------------------------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading.Tasks; + namespace Microsoft.Tools.WindowsDevicePortal { /// @@ -14,16 +19,145 @@ public partial class DevicePortal /// /// API to create a realtime ETW session. /// - public static readonly string CreateRealtimeEtwSessionApi = "api/etw/session/realtime"; + public static readonly string RealtimeEtwSessionApi = "api/etw/session/realtime"; + + /// + /// API to get the list of registered custom ETW providers. + /// + public static readonly string CustomEtwProvidersApi = "api/etw/customproviders"; + + /// + /// API to get the list of registered ETW providers. + /// + public static readonly string EtwProvidersApi = "api/etw/providers"; + + /// + /// Web socket to get ETW events.! + /// + private WebSocket realtimeEventsWebSocket; /// - /// API to getthe list of registered custom ETW providers. + /// The ETW event message received handler /// - public static readonly string GetCustomEtwProvidersApi = "api/etw/customproviders"; + public event WebSocketMessageReceivedEventHandler RealtimeEventsMessageReceived; /// - /// API to getthe list of registered ETW providers. + /// Returns the current set of registered custom ETW providers. /// - public static readonly string GetEtwProvidersApi = "api/etw/providers"; + /// EtwProviders object containing a list of providers, friendly name and GUID + public async Task GetCustomEtwProvidersAsync() + { + return await this.GetAsync(CustomEtwProvidersApi); + } + + /// + /// Returns the current set of registered ETW providers. + /// + /// EtwProviders object containing a list of providers, friendly name and GUID + public async Task GetEtwProvidersAsync() + { + return await this.GetAsync(EtwProvidersApi); + } + + /// + /// Starts listening for ETW events with it being returned via the RealtimeEventsMessageReceived event handler. + /// + /// Task for connecting to the WebSocket but not for listening to it. + public async Task StartListeningForEtwEventsAsync() + { + if (this.realtimeEventsWebSocket == null) + { +#if WINDOWS_UWP + this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection); +#else + this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); +#endif + + this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler; + } + else + { + if (this.realtimeEventsWebSocket.IsListeningForMessages) + { + return; + } + } + + await this.realtimeEventsWebSocket.StartListeningForMessagesAsync(RealtimeEtwSessionApi); + } + + /// + /// Stop listening for ETW events. + /// + /// Task for stop listening for ETW events and disconnecting from the WebSocket. + public async Task StopListeningForEtwEventsAsync() + { + if (this.realtimeEventsWebSocket == null || !this.realtimeEventsWebSocket.IsListeningForMessages) + { + return; + } + + await this.realtimeEventsWebSocket.StopListeningForMessagesAsync(); + } + + /// + /// Handler for the ETW received event that passes the event to the RealtimeEventsMessageReceived handler. + /// + /// The object sending the event. + /// The event data. + private void EtwEventsReceivedHandler( + WebSocket sender, + WebSocketMessageReceivedEventArgs args) + { + if (args.Message != null) + { + this.RealtimeEventsMessageReceived?.Invoke( + this, + args); + } + } + + #region Data contract + + /// + /// ETW Events. + /// + [DataContract] + public class EtwEvents + { + } + + /// + /// ETW Providers. + /// + [DataContract] + public class EtwProviders + { + /// + /// Gets list of ETW providers + /// + [DataMember(Name = "Providers")] + public List Providers { get; private set; } + } + + /// + /// ETW Provider Info. Contains the Name and GUID. + /// + public class EtwProviderInfo + { + /// + /// Gets provider guid. + /// + [DataMember(Name = "GUID")] + public string GUID { get; private set; } + + /// + /// Gets provider name. + /// + [DataMember(Name = "Name")] + public string Name { get; private set; } + } + +#endregion // Data contract } } From 5861be2191284e87151093705277b81f69b0ca5b Mon Sep 17 00:00:00 2001 From: Chris Baudin Date: Thu, 9 Feb 2017 18:46:38 -0800 Subject: [PATCH 2/5] Update ETW API tests to validate the contents of the EtwProviders object. (Issue #31) --- .../UnitTestProject/Core/EtwTests.cs | 8 ++++++-- .../MockData/Defaults/api_etw_customproviders_Default.dat | 2 +- .../MockData/Defaults/api_etw_providers_Default.dat | 2 +- .../WindowsDevicePortalWrapper.Shared/Core/Etw.cs | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs index 7a7f61d2..c168141e 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs @@ -4,6 +4,7 @@ // //---------------------------------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -101,8 +102,11 @@ private static void ValidateEtwEvents(EtwEvents etwEvents) /// The to validate. private static void ValidateEtwProviders(EtwProviders etw) { - EtwProviderInfo providerInfo = etw?.Providers?.FirstOrDefault(); - Assert.IsNotNull(providerInfo); + Guid result; + Assert.IsTrue(etw.Providers.Count > 0); + Assert.IsTrue(etw.Providers.All(etwProvider => + Guid.TryParse(etwProvider.GUID, out result) && + !string.IsNullOrEmpty(etwProvider.Name))); } } } diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat index 0b713c77..d332e2d4 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat @@ -1 +1 @@ -{ "Providers": [ { "GUID": "ProviderGUID", "Name": "ProviderName" } ] } \ No newline at end of file +{"Providers" : [{"GUID" : "3A43D90E-530E-41E5-A897-B555516070E2", "Name" : "Provider.One"},{"GUID" : "52B1715B-5AB6-4B48-A61A-A93EE8D4B5CD", "Name" : "Provider-Two"},{"GUID" : "698F02FF-2B63-4CBB-AB06-FEB88011347E", "Name" : "Provider.Three"}]} \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat index 0b713c77..d332e2d4 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat @@ -1 +1 @@ -{ "Providers": [ { "GUID": "ProviderGUID", "Name": "ProviderName" } ] } \ No newline at end of file +{"Providers" : [{"GUID" : "3A43D90E-530E-41E5-A897-B555516070E2", "Name" : "Provider.One"},{"GUID" : "52B1715B-5AB6-4B48-A61A-A93EE8D4B5CD", "Name" : "Provider-Two"},{"GUID" : "698F02FF-2B63-4CBB-AB06-FEB88011347E", "Name" : "Provider.Three"}]} \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index 64b6ffcb..f249ba9e 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -143,6 +143,7 @@ public class EtwProviders /// /// ETW Provider Info. Contains the Name and GUID. /// + [DataContract] public class EtwProviderInfo { /// From 834b52d1473767f8e547117db241352cf2d04585 Mon Sep 17 00:00:00 2001 From: Chris Baudin Date: Mon, 13 Feb 2017 19:12:06 -0800 Subject: [PATCH 3/5] Refactor the WebSocket class implementations to decouple the Connect, Close, Receive and Send actions. (Issue #31) --- .../WDPMockImplementations/WebSocket.cs | 72 +++++- .../Core/Etw.cs | 67 +++-- .../Core/PerformanceData.cs | 22 +- .../DevicePortal.cs | 7 +- .../HttpRest/WebSocket.cs | 67 +++-- .../IoT/ApplicationManager.cs | 5 +- .../IoT/BluetoothConnectivity.cs | 232 +++++++++--------- .../HttpRest/WebSocket.cs | 107 ++++---- .../HttpRest/WebSocket.cs | 88 ++++--- 9 files changed, 408 insertions(+), 259 deletions(-) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs index 8a499e7c..2973898e 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs @@ -36,6 +36,11 @@ internal partial class WebSocket /// private Func serverCertificateValidationHandler; + /// + /// The connection to the websocket. + /// + private Task webSocketTask; + /// /// Initializes a new instance of the class. /// @@ -50,6 +55,34 @@ public WebSocket(IDevicePortalConnection connection, Func + /// Initialize a connection to the websocket. + /// + /// The uri that the weboscket should connect to. + /// The task of opening a connection to the websocket. + private async Task ConnectInternalAsync(Uri endpoint) + { + await Task.Run(() => + { + webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint); + this.IsConnected = true; + }); + } + + /// + /// Closes the connection to the websocket. + /// + /// The task of closing the websocket connection. + private async Task CloseInternalAsync() + { + await Task.Run(() => + { + webSocketTask.Dispose(); + webSocketTask = null; + this.IsConnected = false; + }); + } + /// /// Stops listneing for messages from the websocket and closes the connection to the websocket. /// @@ -57,35 +90,31 @@ public WebSocket(IDevicePortalConnection connection, Func - /// Connects to the websocket and starts listening for messages from the websocket. + /// Starts listening for messages from the websocket. /// Once they are received they are parsed and the WebSocketMessageReceived event is raised. /// - /// The uri that the weboscket should connect to /// The task of listening for messages from the websocket. - private async Task StartListeningForMessagesInternalAsync(Uri endpoint) + private async Task StartListeningForMessagesInternalAsync() { + this.IsListeningForMessages = true; this.keepListeningForMessages = true; try { while (this.keepListeningForMessages) { - Task webSocketTask = TestHelpers.MockHttpResponder.WebSocketAsync(endpoint); await webSocketTask.ConfigureAwait(false); webSocketTask.Wait(); @@ -121,5 +150,24 @@ private async Task StartListeningForMessagesInternalAsync(Uri endpoint) this.IsListeningForMessages = false; } } + + /// + /// Sends a message to the server. + /// + /// The message to send. + /// The task of sending the message to the websocket + private async Task SendMessageInternalAsync(string message) + { + await webSocketTask.ConfigureAwait(false); + webSocketTask.Wait(); + + using (HttpResponseMessage response = webSocketTask.Result) + { + if (!response.IsSuccessStatusCode) + { + throw new DevicePortalException(response); + } + } + } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index f249ba9e..a0e1ceed 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -32,10 +32,15 @@ public partial class DevicePortal public static readonly string EtwProvidersApi = "api/etw/providers"; /// - /// Web socket to get ETW events.! + /// Web socket to get ETW events. /// private WebSocket realtimeEventsWebSocket; + /// + /// Determines if the event listener has been registered + /// + private bool isListeningForRealtimeEvents = false; + /// /// The ETW event message received handler /// @@ -59,31 +64,36 @@ public async Task GetEtwProvidersAsync() return await this.GetAsync(EtwProvidersApi); } + /// + /// Toggles the listening state of a specific provider on the realtime events WebSocket. + /// + /// The provider to update the listening state of. + /// Determines whether the listening state should be enabled or disabled. + /// Task for toggling the listening state of the specified provider. + public async Task ToggleEtwProviderAsync(EtwProviderInfo etwProvider, bool isEnabled = true) + { + string action = isEnabled ? "enable" : "disable"; + string message = $"provider {etwProvider.GUID} {action}"; + + await this.InitializeRealtimeEventsWebSocket(); + await this.realtimeEventsWebSocket.SendMessageAsync(message); + } + /// /// Starts listening for ETW events with it being returned via the RealtimeEventsMessageReceived event handler. /// /// Task for connecting to the WebSocket but not for listening to it. public async Task StartListeningForEtwEventsAsync() { - if (this.realtimeEventsWebSocket == null) - { -#if WINDOWS_UWP - this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection); -#else - this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); -#endif + await this.InitializeRealtimeEventsWebSocket(); - this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler; - } - else + if (!this.isListeningForRealtimeEvents) { - if (this.realtimeEventsWebSocket.IsListeningForMessages) - { - return; - } + this.isListeningForRealtimeEvents = true; + this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler; } - await this.realtimeEventsWebSocket.StartListeningForMessagesAsync(RealtimeEtwSessionApi); + await this.realtimeEventsWebSocket.ReceiveMessagesAsync(); } /// @@ -92,12 +102,31 @@ public async Task StartListeningForEtwEventsAsync() /// Task for stop listening for ETW events and disconnecting from the WebSocket. public async Task StopListeningForEtwEventsAsync() { - if (this.realtimeEventsWebSocket == null || !this.realtimeEventsWebSocket.IsListeningForMessages) + if (this.isListeningForRealtimeEvents) + { + this.isListeningForRealtimeEvents = false; + this.realtimeEventsWebSocket.WebSocketMessageReceived -= this.EtwEventsReceivedHandler; + } + + await this.realtimeEventsWebSocket.CloseAsync(); + } + + /// + /// Creates a new if it hasn't already been initialized. + /// + /// Task for connecting the ETW realtime event WebSocket. + private async Task InitializeRealtimeEventsWebSocket() + { + if (this.realtimeEventsWebSocket == null) { - return; +#if WINDOWS_UWP + this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection); +#else + this.realtimeEventsWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); +#endif } - await this.realtimeEventsWebSocket.StopListeningForMessagesAsync(); + await this.realtimeEventsWebSocket.ConnectAsync(RealtimeEtwSessionApi); } /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/PerformanceData.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/PerformanceData.cs index 16322971..e14fcf1c 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/PerformanceData.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/PerformanceData.cs @@ -79,7 +79,8 @@ public async Task StartListeningForRunningProcessesAsync() } } - await this.deviceProcessesWebSocket.StartListeningForMessagesAsync(RunningProcessApi); + await this.deviceProcessesWebSocket.ConnectAsync(RunningProcessApi); + await this.deviceProcessesWebSocket.ReceiveMessagesAsync(); } /// @@ -88,12 +89,7 @@ public async Task StartListeningForRunningProcessesAsync() /// Task for stop listening for processes and disconnecting from the websocket . public async Task StopListeningForRunningProcessesAsync() { - if (this.deviceProcessesWebSocket == null || !this.deviceProcessesWebSocket.IsListeningForMessages) - { - return; - } - - await this.deviceProcessesWebSocket.StopListeningForMessagesAsync(); + await this.deviceProcessesWebSocket.CloseAsync(); } /// @@ -129,7 +125,8 @@ public async Task StartListeningForSystemPerfAsync() } } - await this.systemPerfWebSocket.StartListeningForMessagesAsync(SystemPerfApi); + await this.systemPerfWebSocket.ConnectAsync(SystemPerfApi); + await this.systemPerfWebSocket.ReceiveMessagesAsync(); } /// @@ -138,12 +135,7 @@ public async Task StartListeningForSystemPerfAsync() /// Task for stop listening for system perf and disconnecting from the websocket . public async Task StopListeningForSystemPerfAsync() { - if (this.systemPerfWebSocket == null || !this.systemPerfWebSocket.IsListeningForMessages) - { - return; - } - - await this.systemPerfWebSocket.StopListeningForMessagesAsync(); + await this.systemPerfWebSocket.CloseAsync(); } /// @@ -431,6 +423,7 @@ public bool Contains(int processId) break; } } + return found; } @@ -455,6 +448,7 @@ public bool Contains(string packageName, bool caseSensitive = true) break; } } + return found; } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs index 01bfe26a..bff970db 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs @@ -372,12 +372,15 @@ public async Task SaveEndpointResponseToFileAsync( websocket.WebSocketStreamReceived += streamReceivedHandler; - Task startListeningForStreamTask = websocket.StartListeningForMessagesAsync(endpoint); + Task connect = websocket.ConnectAsync(endpoint); + connect.Wait(); + + Task startListeningForStreamTask = websocket.ReceiveMessagesAsync(); startListeningForStreamTask.Wait(); streamReceived.WaitOne(); - Task stopListeningForStreamTask = websocket.StopListeningForMessagesAsync(); + Task stopListeningForStreamTask = websocket.CloseAsync(); stopListeningForStreamTask.Wait(); websocket.WebSocketStreamReceived -= streamReceivedHandler; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs index 93ae900b..1fc98b05 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs @@ -53,6 +53,15 @@ internal partial class WebSocket /// public event WebSocketStreamReceivedEventInternalHandler WebSocketStreamReceived; + /// + /// Gets a value indicating whether the web socket is connected. + /// + public bool IsConnected + { + get; + private set; + } + /// /// Gets a value indicating whether the web socket is listening for messages. /// @@ -62,35 +71,65 @@ public bool IsListeningForMessages private set; } + /// + /// Initialize a connection to the websocket. + /// + /// The relative portion of the uri path that specifies the API to call. + /// The query string portion of the uri path that provides the parameterized data. + /// The task of opening the websocket connection. + internal async Task ConnectAsync(string apiPath, string payload = null) + { + if (this.IsConnected) + { + return; + } + + Uri uri = Utilities.BuildEndpoint( + this.deviceConnection.WebSocketConnection, + apiPath, + payload); + await this.ConnectInternalAsync(uri); + } + /// /// Closes the connection to the websocket and stop listening for messages. /// /// The task of closing the websocket connection. - internal async Task StopListeningForMessagesAsync() + internal async Task CloseAsync() { - if (this.IsListeningForMessages) + if (this.IsConnected) { - await this.StopListeningForMessagesInternalAsync(); + if (this.IsListeningForMessages) + { + await this.StopListeningForMessagesInternalAsync(); + } + + await this.CloseInternalAsync(); } } /// /// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised. /// - /// The relative portion of the uri path that specifies the API to call. - /// The query string portion of the uri path that provides the parameterized data. /// The task of listening for messages from the websocket. - internal async Task StartListeningForMessagesAsync( - string apiPath, - string payload = null) + internal async Task ReceiveMessagesAsync() + { + if (this.IsConnected && !this.IsListeningForMessages) + { + await this.StartListeningForMessagesInternalAsync(); + } + } + + /// + /// Sends a message to the server. + /// + /// The message to send. + /// The task of sending a message to the websocket. + internal async Task SendMessageAsync(string message) { - if (!this.IsListeningForMessages) + if (this.IsConnected) { - Uri uri = Utilities.BuildEndpoint( - this.deviceConnection.WebSocketConnection, - apiPath, - payload); - await this.StartListeningForMessagesInternalAsync(uri); + await this.SendMessageInternalAsync(message); } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ApplicationManager.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ApplicationManager.cs index 2842f4e2..460e8291 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ApplicationManager.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/ApplicationManager.cs @@ -123,11 +123,14 @@ public class AppsListInfo public List AppPackages { get; private set; } } + /// + /// Application package. + /// [DataContract] public class AppPackage { /// - /// Gets an app as the startup app + /// Gets a value indicating whether the app is the startup app /// [DataMember(Name = "IsStartup")] public bool IsStartup { get; private set; } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/BluetoothConnectivity.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/BluetoothConnectivity.cs index 740f7cb2..7e15254b 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/BluetoothConnectivity.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/IoT/BluetoothConnectivity.cs @@ -5,11 +5,11 @@ //---------------------------------------------------------------------------------------------- using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; -using System.Threading.Tasks; using System.Threading; -using System.Collections.Generic; +using System.Threading.Tasks; namespace Microsoft.Tools.WindowsDevicePortal { @@ -41,27 +41,27 @@ public partial class DevicePortal /// /// Web socket to get list of bluetooth devices. /// - private WebSocket BluetoothWebSocket; + private WebSocket bluetoothWebSocket; /// - /// Gets or sets the list of bluetooth devices received handler. + /// Web socket to get list of paired bluetooth devices. /// - public event WebSocketMessageReceivedEventHandler BluetoothDeviceListReceived; + private WebSocket pairedBluetoothWebSocket; /// - /// Web socket to get list of paired bluetooth devices. + /// Web socket to get the results of bluetooth device pairing. /// - private WebSocket PairedBluetoothWebSocket; + private WebSocket pairBluetoothWebSocket; /// - /// Gets or sets the list of paired bluetooth devices received handler. + /// Gets or sets the list of bluetooth devices received handler. /// - public event WebSocketMessageReceivedEventHandler PairedBluetoothDeviceListReceived; + public event WebSocketMessageReceivedEventHandler BluetoothDeviceListReceived; /// - /// Web socket to get the results of bluetooth device pairing. + /// Gets or sets the list of paired bluetooth devices received handler. /// - private WebSocket PairBluetoothWebSocket; + public event WebSocketMessageReceivedEventHandler PairedBluetoothDeviceListReceived; /// /// Gets the results of pairing bluetooth device received handler. @@ -75,14 +75,14 @@ public partial class DevicePortal public AvailableBluetoothDevicesInfo GetAvailableBluetoothDevicesInfo() { AvailableBluetoothDevicesInfo bluetooth = null; - ManualResetEvent BluetoothReceived = new ManualResetEvent(false); + ManualResetEvent bluetoothReceived = new ManualResetEvent(false); WebSocketMessageReceivedEventHandler bluetoothReceivedHandler = - delegate (DevicePortal sender, WebSocketMessageReceivedEventArgs BluetoothArgs) + delegate(DevicePortal sender, WebSocketMessageReceivedEventArgs bluetoothArgs) { - if (BluetoothArgs.Message != null) + if (bluetoothArgs.Message != null) { - bluetooth = BluetoothArgs.Message; - BluetoothReceived.Set(); + bluetooth = bluetoothArgs.Message; + bluetoothReceived.Set(); } }; this.BluetoothDeviceListReceived += bluetoothReceivedHandler; @@ -90,7 +90,7 @@ public AvailableBluetoothDevicesInfo GetAvailableBluetoothDevicesInfo() Task startListeningForBluetooth = this.StartListeningForBluetoothAsync(AvailableBluetoothDevicesApi); startListeningForBluetooth.Wait(); - BluetoothReceived.WaitOne(); + bluetoothReceived.WaitOne(); Task stopListeningForBluetooth = this.StopListeningForBluetoothAsync(); stopListeningForBluetooth.Wait(); @@ -99,7 +99,6 @@ public AvailableBluetoothDevicesInfo GetAvailableBluetoothDevicesInfo() return bluetooth; } - /// /// Gets the paired bluetooth device information. /// @@ -107,14 +106,14 @@ public AvailableBluetoothDevicesInfo GetAvailableBluetoothDevicesInfo() public PairedBluetoothDevicesInfo GetPairedBluetoothDevicesInfo() { PairedBluetoothDevicesInfo bluetooth = null; - ManualResetEvent PairedBluetoothReceived = new ManualResetEvent(false); + ManualResetEvent pairedBluetoothReceived = new ManualResetEvent(false); WebSocketMessageReceivedEventHandler pairedBluetoothReceivedHandler = - delegate (DevicePortal sender, WebSocketMessageReceivedEventArgs BluetoothArgs) + delegate(DevicePortal sender, WebSocketMessageReceivedEventArgs bluetoothArgs) { - if (BluetoothArgs.Message != null) + if (bluetoothArgs.Message != null) { - bluetooth = BluetoothArgs.Message; - PairedBluetoothReceived.Set(); + bluetooth = bluetoothArgs.Message; + pairedBluetoothReceived.Set(); } }; this.PairedBluetoothDeviceListReceived += pairedBluetoothReceivedHandler; @@ -122,7 +121,7 @@ public PairedBluetoothDevicesInfo GetPairedBluetoothDevicesInfo() Task startListeningForPairedBluetooth = this.StartListeningForPairedBluetoothAsync(PairedBluetoothDevicesApi); startListeningForPairedBluetooth.Wait(); - PairedBluetoothReceived.WaitOne(); + pairedBluetoothReceived.WaitOne(); Task stopListeningForPairedBluetooth = this.StopListeningForPairedBluetoothAsync(); stopListeningForPairedBluetooth.Wait(); @@ -134,18 +133,19 @@ public PairedBluetoothDevicesInfo GetPairedBluetoothDevicesInfo() /// /// Gets the results for pairing a bluetooth device. /// + /// Device Id. /// Results of pairing a bluetooth device public PairBluetoothDevicesInfo GetPairBluetoothDevicesInfo(string deviceId) { PairBluetoothDevicesInfo bluetooth = null; - ManualResetEvent PairBluetoothReceived = new ManualResetEvent(false); + ManualResetEvent pairBluetoothReceived = new ManualResetEvent(false); WebSocketMessageReceivedEventHandler pairBluetoothReceivedHandler = - delegate (DevicePortal sender, WebSocketMessageReceivedEventArgs BluetoothArgs) + delegate(DevicePortal sender, WebSocketMessageReceivedEventArgs bluetoothArgs) { - if (BluetoothArgs.Message != null) + if (bluetoothArgs.Message != null) { - bluetooth = BluetoothArgs.Message; - PairBluetoothReceived.Set(); + bluetooth = bluetoothArgs.Message; + pairBluetoothReceived.Set(); } }; this.PairBluetoothDeviceListReceived += pairBluetoothReceivedHandler; @@ -153,7 +153,7 @@ public PairBluetoothDevicesInfo GetPairBluetoothDevicesInfo(string deviceId) Task startListeningForPairBluetooth = this.StartListeningForPairBluetoothAsync(PairBluetoothDevicesApi, string.Format("deviceId={0}", Utilities.Hex64Encode(deviceId))); startListeningForPairBluetooth.Wait(); - PairBluetoothReceived.WaitOne(); + pairBluetoothReceived.WaitOne(); Task stopListeningForPairBluetooth = this.StopListeningForPairBluetoothAsync(); stopListeningForPairBluetooth.Wait(); @@ -165,28 +165,30 @@ public PairBluetoothDevicesInfo GetPairBluetoothDevicesInfo(string deviceId) /// /// Starts listening for bluetooth list of devices returned from the BluetoothDeviceListReceived handler. /// + /// The relative portion of the uri path that specifies the API to call. /// Task for connecting to the websocket but not for listening to it. public async Task StartListeningForBluetoothAsync(string bluetoothApi) { - if (this.BluetoothWebSocket == null) + if (this.bluetoothWebSocket == null) { #if WINDOWS_UWP - this.BluetoothWebSocket = new WebSocket(this.deviceConnection); + this.bluetoothWebSocket = new WebSocket(this.deviceConnection); #else - this.BluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); + this.bluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); #endif - this.BluetoothWebSocket.WebSocketMessageReceived += this.BluetoothReceivedHandler; + this.bluetoothWebSocket.WebSocketMessageReceived += this.BluetoothReceivedHandler; } else { - if (this.BluetoothWebSocket.IsListeningForMessages) + if (this.bluetoothWebSocket.IsListeningForMessages) { return; } } - await this.BluetoothWebSocket.StartListeningForMessagesAsync(bluetoothApi); + await this.bluetoothWebSocket.ConnectAsync(bluetoothApi); + await this.bluetoothWebSocket.ReceiveMessagesAsync(); } /// @@ -195,56 +197,36 @@ public async Task StartListeningForBluetoothAsync(string bluetoothApi) /// Task to stop listening for bluetooth devices and disconnecting from the websocket . public async Task StopListeningForBluetoothAsync() { - if (this.BluetoothWebSocket == null || !this.BluetoothWebSocket.IsListeningForMessages) - { - return; - } - - await this.BluetoothWebSocket.StopListeningForMessagesAsync(); - } - - /// - /// Handler for the list of bluetooth devices received event to pass it on to the BluetoothDeviceListReceived handler. - /// - /// The object sending the event. - /// The event data. - private void BluetoothReceivedHandler( - WebSocket sender, - WebSocketMessageReceivedEventArgs args) - { - if (args.Message != null) - { - this.BluetoothDeviceListReceived?.Invoke( - this, - args); - } + await this.bluetoothWebSocket.CloseAsync(); } /// /// Starts listening for paired bluetooth list of devices returned from the PairedBluetoothDeviceListReceived handler. /// + /// The relative portion of the uri path that specifies the API to call. /// Task for connecting to the websocket but not for listening to it. public async Task StartListeningForPairedBluetoothAsync(string bluetoothApi) { - if (this.PairedBluetoothWebSocket == null) + if (this.pairedBluetoothWebSocket == null) { #if WINDOWS_UWP - this.PairedBluetoothWebSocket = new WebSocket(this.deviceConnection); + this.pairedBluetoothWebSocket = new WebSocket(this.deviceConnection); #else - this.PairedBluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); + this.pairedBluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); #endif - this.PairedBluetoothWebSocket.WebSocketMessageReceived += this.PairedBluetoothReceivedHandler; + this.pairedBluetoothWebSocket.WebSocketMessageReceived += this.PairedBluetoothReceivedHandler; } else { - if (this.PairedBluetoothWebSocket.IsListeningForMessages) + if (this.pairedBluetoothWebSocket.IsListeningForMessages) { return; } } - await this.PairedBluetoothWebSocket.StartListeningForMessagesAsync(bluetoothApi); + await this.pairedBluetoothWebSocket.ConnectAsync(bluetoothApi); + await this.pairedBluetoothWebSocket.ReceiveMessagesAsync(); } /// @@ -253,56 +235,42 @@ public async Task StartListeningForPairedBluetoothAsync(string bluetoothApi) /// Task to stop listening for bluetooth devices and disconnecting from the websocket . public async Task StopListeningForPairedBluetoothAsync() { - if (this.PairedBluetoothWebSocket == null || !this.PairedBluetoothWebSocket.IsListeningForMessages) + if (this.pairedBluetoothWebSocket == null || !this.pairedBluetoothWebSocket.IsListeningForMessages) { return; } - await this.PairedBluetoothWebSocket.StopListeningForMessagesAsync(); - } - - /// - /// Handler for the list of paired bluetooth devices received event to pass it on to the PairedBluetoothDeviceListReceived handler. - /// - /// The object sending the event. - /// The event data. - private void PairedBluetoothReceivedHandler( - WebSocket sender, - WebSocketMessageReceivedEventArgs args) - { - if (args.Message != null) - { - this.PairedBluetoothDeviceListReceived?.Invoke( - this, - args); - } + await this.pairedBluetoothWebSocket.CloseAsync(); } /// /// Starts listening for the result to pair the bluetooth device returned from the PairBluetoothDeviceListReceived handler. /// + /// The relative portion of the uri path that specifies the API to call. + /// The query string portion of the uri path that provides the parameterized data. /// Task for connecting to the websocket but not for listening to it. public async Task StartListeningForPairBluetoothAsync(string bluetoothApi, string payload) { - if (this.PairBluetoothWebSocket == null) + if (this.pairBluetoothWebSocket == null) { #if WINDOWS_UWP - this.PairBluetoothWebSocket = new WebSocket(this.deviceConnection); + this.pairBluetoothWebSocket = new WebSocket(this.deviceConnection); #else - this.PairBluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); + this.pairBluetoothWebSocket = new WebSocket(this.deviceConnection, this.ServerCertificateValidation); #endif - this.PairBluetoothWebSocket.WebSocketMessageReceived += this.PairBluetoothReceivedHandler; + this.pairBluetoothWebSocket.WebSocketMessageReceived += this.PairBluetoothReceivedHandler; } else { - if (this.PairBluetoothWebSocket.IsListeningForMessages) + if (this.pairBluetoothWebSocket.IsListeningForMessages) { return; } } - await this.PairBluetoothWebSocket.StartListeningForMessagesAsync(bluetoothApi, payload); + await this.pairBluetoothWebSocket.ConnectAsync(bluetoothApi, payload); + await this.pairBluetoothWebSocket.ReceiveMessagesAsync(); } /// @@ -311,12 +279,53 @@ public async Task StartListeningForPairBluetoothAsync(string bluetoothApi, strin /// Task to stop listening for the bluetooth device and disconnecting from the websocket . public async Task StopListeningForPairBluetoothAsync() { - if (this.PairBluetoothWebSocket == null || !this.PairBluetoothWebSocket.IsListeningForMessages) + await this.pairBluetoothWebSocket.CloseAsync(); + } + + /// + /// Unpairs the bluetooth device. + /// + /// Device Id. + /// Task tracking completion of the REST call. + public async Task UnPairBluetoothDeviceAsync(string deviceId) + { + return await this.PostAsync( + UnpairBluetoothDevicesApi, + string.Format("deviceId={0}", Utilities.Hex64Encode(deviceId))); + } + + /// + /// Handler for the list of bluetooth devices received event to pass it on to the BluetoothDeviceListReceived handler. + /// + /// The object sending the event. + /// The event data. + private void BluetoothReceivedHandler( + WebSocket sender, + WebSocketMessageReceivedEventArgs args) + { + if (args.Message != null) { - return; + this.BluetoothDeviceListReceived?.Invoke( + this, + args); } + } - await this.PairBluetoothWebSocket.StopListeningForMessagesAsync(); + /// + /// Handler for the list of paired bluetooth devices received event to pass it on to the PairedBluetoothDeviceListReceived handler. + /// + /// The object sending the event. + /// The event data. + private void PairedBluetoothReceivedHandler( + WebSocket sender, + WebSocketMessageReceivedEventArgs args) + { + if (args.Message != null) + { + this.PairedBluetoothDeviceListReceived?.Invoke( + this, + args); + } } /// @@ -336,18 +345,6 @@ private void PairBluetoothReceivedHandler( } } - /// - /// Unpairs the bluetooth device. - /// - /// Device Id. - /// Task tracking completion of the REST call. - public async Task UnPairBluetoothDeviceAsync(string deviceId) - { - return await this.PostAsync( - UnpairBluetoothDevicesApi, - string.Format("deviceId={0}", Utilities.Hex64Encode(deviceId))); - } - #region Data contract /// @@ -357,21 +354,25 @@ public async Task UnPairBluetoothDeviceAsync(string deviceId) public class AvailableBluetoothDevicesInfo { /// - /// Returns a list of available devices + /// Gets a list of available devices /// [DataMember(Name = "AvailableDevices")] public List AvailableDevices { get; private set; } } + + /// + /// Information about a bluetooth device. + /// public class BluetoothDeviceInfo { /// - /// Returns the bluetooth device ID + /// Gets the bluetooth device ID /// [DataMember(Name = "ID")] public string ID { get; private set; } /// - /// Returns the bluetooth device name + /// Gets the bluetooth device name /// [DataMember(Name = "Name")] public string Name { get; private set; } @@ -384,7 +385,7 @@ public class BluetoothDeviceInfo public class PairedBluetoothDevicesInfo { /// - /// Returns a list of paired devices + /// Gets a list of paired devices /// [DataMember(Name = "PairedDevices")] public List PairedDevices { get; private set; } @@ -397,28 +398,31 @@ public class PairedBluetoothDevicesInfo public class PairBluetoothDevicesInfo { /// - /// Returns the pair results + /// Gets the pair results /// [DataMember(Name = "PairResult")] public PairResult PairResult { get; private set; } } + /// + /// Information about device pairing. + /// public class PairResult { /// - /// Returns the result about the device pairing + /// Gets the result about the device pairing /// [DataMember(Name = "Result")] public string Result { get; private set; } /// - /// Returns the deviceId to be paired + /// Gets the deviceId to be paired /// [DataMember(Name = "deviceId")] - public string deviceId { get; private set; } + public string DeviceId { get; private set; } /// - /// Returns the pin + /// Gets the pin /// [DataMember(Name = "Pin")] public string Pin { get; private set; } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs index 52395049..c1603bde 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs @@ -24,6 +24,11 @@ internal partial class WebSocket /// private MessageWebSocket websocket = null; + /// + /// The websocket connection has closed after the request was fulfilled. + /// + private UInt16 WebSocketCloseStatus_NormalClosure = 1000; + /// /// Initializes a new instance of the class. /// @@ -37,12 +42,11 @@ public WebSocket(IDevicePortalConnection connection, bool sendStreams = false) } /// - /// Opens a connection to the specified websocket API and starts listening for messages. + /// Opens a connection to the specified websocket API. /// /// The uri that the weboscket should connect to /// The task of opening a connection to the websocket. -#pragma warning disable 1998 - private async Task StartListeningForMessagesInternalAsync( + private async Task ConnectInternalAsync( Uri endpoint) { this.websocket = new MessageWebSocket(); @@ -72,36 +76,36 @@ private async Task StartListeningForMessagesInternalAsync( this.websocket.SetRequestHeader("Origin", this.deviceConnection.Connection.AbsoluteUri); - // Do not wait on receiving messages. - Task connectTask = this.ConnectAsync(endpoint); + await this.websocket.ConnectAsync(endpoint); + + this.IsConnected = true; } -#pragma warning restore 1998 /// - /// Opens a connection to the specified websocket API. + /// Closes the connection to the websocket. + /// + /// The task of closing the websocket connection. + private async Task CloseInternalAsync() + { + await Task.Run(() => + { + this.websocket.Close(WebSocketCloseStatus_NormalClosure, "Closed due to user request."); + this.websocket.Dispose(); + this.websocket = null; + this.IsConnected = false; + }); + } + + /// + /// Opens a connection to the specified websocket API and starts listening for messages. /// - /// The uri that the weboscket should connect to /// The task of opening a connection to the websocket. - private async Task ConnectAsync( - Uri endpoint) + private async Task StartListeningForMessagesInternalAsync() { - bool connecting = true; - try + await Task.Run(() => { - await this.websocket.ConnectAsync(endpoint); - connecting = false; this.IsListeningForMessages = true; - } - catch (Exception) - { - // Error happened during connect operation. - if (connecting && this.websocket != null) - { - this.websocket.Dispose(); - this.websocket = null; - this.IsListeningForMessages = false; - } - } + }); } /// @@ -111,38 +115,55 @@ private async Task ConnectAsync( /// The message from the web socket. private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args) { - using (IInputStream inputStream = args.GetDataStream()) + if (this.IsListeningForMessages) { - Stream stream = new MemoryStream(); + using (IInputStream inputStream = args.GetDataStream()) + { + Stream stream = new MemoryStream(); - Task copyTask = inputStream.AsStreamForRead().CopyToAsync(stream); - copyTask.Wait(); + Task copyTask = inputStream.AsStreamForRead().CopyToAsync(stream); + copyTask.Wait(); - // Ensure we return with the stream pointed at the origin. - stream.Position = 0; + // Ensure we return with the stream pointed at the origin. + stream.Position = 0; - this.ConvertStreamToMessage(stream); + this.ConvertStreamToMessage(stream); + } } } /// - /// Closes the connection to the websocket. + /// Stops listening for messages from the websocket. /// /// The task of closing the websocket connection. -#pragma warning disable 1998 private async Task StopListeningForMessagesInternalAsync() { - if (this.IsListeningForMessages) + await Task.Run(() => { - if (this.websocket != null) - { - // Code 1000 indicates that the purpose of the connection has been fulfilled and the connection is no longer needed. - this.websocket.Close(1000, "Closed due to user request."); - this.websocket = null; - this.IsListeningForMessages = false; - } + this.IsListeningForMessages = false; + }); + } + + /// + /// Sends a message to the websocket. + /// + /// The message to be sent. + /// The task of sending the specified message to the server. + private async Task SendMessageInternalAsync(string message) + { + using (DataWriter data = new DataWriter(this.websocket.OutputStream)) + { + // Load the content into the data writer. + data.UnicodeEncoding = UnicodeEncoding.Utf8; + data.WriteString(message); + + // Send the content to the output stream. + await data.StoreAsync(); + await data.FlushAsync(); + + // Do not close the output stream when the data writer is disposed. + data.DetachStream(); } } -#pragma warning restore 1998 } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs index 889bf7d1..ad844e32 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs @@ -11,6 +11,7 @@ using System.Net.Sockets; using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -35,7 +36,7 @@ internal partial class WebSocket /// /// The that is being wrapped. /// - private ClientWebSocket websocket = new ClientWebSocket(); + private ClientWebSocket websocket; /// /// for receiving messages. @@ -62,13 +63,14 @@ public WebSocket(IDevicePortalConnection connection, Func - /// Opens a connection to the specified websocket API and starts listening for messages. + /// Opens a connection to the specified websocket API. /// /// The uri that the weboscket should connect to /// The task of opening a connection to the websocket. - private async Task OpenConnectionAsync( + private async Task ConnectInternalAsync( Uri endpoint) { + this.websocket = new ClientWebSocket(); this.websocket.Options.UseDefaultCredentials = false; this.websocket.Options.Credentials = this.deviceConnection.Credentials; this.websocket.Options.SetRequestHeader("Origin", this.deviceConnection.Connection.AbsoluteUri); @@ -80,36 +82,58 @@ private async Task OpenConnectionAsync( }; await this.websocket.ConnectAsync(endpoint, CancellationToken.None); + this.IsConnected = true; } /// - /// Stops listening for messages and closes the connection to the websocket. + /// Closes the connection to the websocket. /// /// The task of closing the websocket connection. - private async Task StopListeningForMessagesInternalAsync() + private async Task CloseInternalAsync() { - if (this.IsListeningForMessages) + await Task.Run(() => { - await this.websocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + this.websocket.Dispose(); + this.websocket = null; + this.IsConnected = false; + }); + } - // Wait for web socket to no longer be receiving messages. - if (this.IsListeningForMessages) - { - await this.receivingMessagesTask; - this.receivingMessagesTask = null; - } + /// + /// Stops listening for messages. + /// + /// The task of closing the websocket connection. + private async Task StopListeningForMessagesInternalAsync() + { + await this.websocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - // Reset websocket as WDP will abort the connection once it receives the close message. - this.websocket = new ClientWebSocket(); + // Wait for web socket to no longer be receiving messages. + if (this.IsListeningForMessages) + { + await this.receivingMessagesTask; + this.receivingMessagesTask = null; } } /// - /// Listen for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised. + /// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised. /// /// The task of listening for messages from the websocket. - private async Task ListenForMessagesInternalAsync() + private async Task StartListeningForMessagesInternalAsync() + { + await Task.Run(() => + { + this.StartListeningForMessagesInternal(); + }); + } + + /// + /// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised. + /// + private void StartListeningForMessagesInternal() { + this.IsListeningForMessages = true; + this.receivingMessagesTask = Task.Run(async () => { try @@ -178,35 +202,19 @@ private async Task ListenForMessagesInternalAsync() this.IsListeningForMessages = false; } }); - - await this.receivingMessagesTask; } /// - /// Starts listening for messages from the websocket. Once they are received they are parsed and the WebSocketMessageReceived event is raised. + /// Sends a message to the server. /// - /// The uri that the weboscket should connect to - /// The task of listening for messages from the websocket. - private async Task StartListeningForMessagesInternalAsync(Uri endpoint) + /// The message to send. + /// The task of sending a message to the websocket. + private async Task SendMessageInternalAsync(string message) { - if (this.IsListeningForMessages) - { - return; - } - - this.IsListeningForMessages = true; - - try - { - await this.OpenConnectionAsync(endpoint); - } - catch - { - this.IsListeningForMessages = false; - } + byte[] bytes = Encoding.UTF8.GetBytes(message); + ArraySegment buffer = new ArraySegment(bytes); - // Do not wait on actually listening for messages. - Task listenForMessagesInternal = this.ListenForMessagesInternalAsync(); + await this.websocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None); } } } From 74b313746635dd5d9b29c70a814cfac480ed2262 Mon Sep 17 00:00:00 2001 From: Chris Baudin Date: Mon, 13 Feb 2017 19:22:36 -0800 Subject: [PATCH 4/5] Implement the Data Contract for the realtime ETW API. (Issue #31) --- .../Core/Etw.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index a0e1ceed..f6aa4146 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -154,6 +154,78 @@ private void EtwEventsReceivedHandler( [DataContract] public class EtwEvents { + /// + /// Gets the event. + /// + [DataMember(Name = "Events")] + public List Event { get; private set; } + + /// + /// Gets the event frequency. + /// + [DataMember(Name = "Frequency")] + public long Frequency { get; private set; } + } + + /// + /// ETW Event Info. + /// + [DataContract] + public class EtwEventInfo + { + /// + /// Gets the event latency. + /// + [DataMember(Name = "EventLatency")] + public string EventLatency { get; private set; } + + /// + /// Gets the event payload. + /// + [DataMember(Name = "EventPayload")] + public string EventPayload { get; private set; } + + /// + /// Gets the event persistence. + /// + [DataMember(Name = "EventPersistence")] + public string EventPersistence { get; private set; } + + /// + /// Gets the event identifer. + /// + [DataMember(Name = "ID")] + public string ID { get; private set; } + + /// + /// Gets the event keyword. + /// + [DataMember(Name = "Keyword")] + public string Keywword { get; private set; } + + /// + /// Gets the event level. + /// + [DataMember(Name = "Level")] + public string Level { get; private set; } + + /// + /// Gets the event provider name. + /// + [DataMember(Name = "ProviderName")] + public string ProviderName { get; private set; } + + /// + /// Gets the event task name. + /// + [DataMember(Name = "TaskName")] + public string TaskName { get; private set; } + + /// + /// Gets the event timestamp. + /// + [DataMember(Name = "Timestamp")] + public string Timestamp { get; private set; } } /// From 42c7ed84ce2626c51f34970539cacc0879ffa32a Mon Sep 17 00:00:00 2001 From: Chris Baudin Date: Mon, 13 Feb 2017 20:09:06 -0800 Subject: [PATCH 5/5] Update the Data Contract for the realtime ETW API to use the correct property types. (Issue #31) --- .../Core/Etw.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index f6aa4146..7a3dfe3a 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs @@ -177,55 +177,55 @@ public class EtwEventInfo /// Gets the event latency. /// [DataMember(Name = "EventLatency")] - public string EventLatency { get; private set; } + public int Latency { get; private set; } /// /// Gets the event payload. /// [DataMember(Name = "EventPayload")] - public string EventPayload { get; private set; } + public string Payload { get; private set; } /// /// Gets the event persistence. /// [DataMember(Name = "EventPersistence")] - public string EventPersistence { get; private set; } + public int Persistence { get; private set; } /// /// Gets the event identifer. /// [DataMember(Name = "ID")] - public string ID { get; private set; } + public ushort ID { get; private set; } /// /// Gets the event keyword. /// [DataMember(Name = "Keyword")] - public string Keywword { get; private set; } + public ulong Keyword { get; private set; } /// /// Gets the event level. /// [DataMember(Name = "Level")] - public string Level { get; private set; } + public uint Level { get; private set; } /// /// Gets the event provider name. /// [DataMember(Name = "ProviderName")] - public string ProviderName { get; private set; } + public string Provider { get; private set; } /// /// Gets the event task name. /// [DataMember(Name = "TaskName")] - public string TaskName { get; private set; } + public string Task { get; private set; } /// /// Gets the event timestamp. /// [DataMember(Name = "Timestamp")] - public string Timestamp { get; private set; } + public ulong Timestamp { get; private set; } } ///