diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 021dd98d..2cf8f08f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,6 +71,17 @@ Address any review comments, force push to your topic branch, and post a comment If the pull request review goes well, a project maintainer will merge your changes. Thank you for helping improve the Windows Device Portal Wrapper! +# NuGet release and versioning + +**For maintainers** + +When creating a new NuGet and GitHub release, the following steps should be taken: +1. Bump the version number as appropriate in master (after 1.0, WDP Wrapper will correctly use semver) +2. Merge from Master to Release, with a PR appropriately named ("v1.2.3 release") +3. Squash and merge commits, leaving major feature entries and fixes in the description. +4. Compile release builds of the .NET and UWP libraries, sign them, and upload to NuGet +5. Cut a new release on GitHub using the same version number ("v1.2.3") and attach the signed libraries to the release. +6. Update code documentation. # Updating code documentation diff --git a/Samples/SampleWdpClient.UniversalWindows/MainPage.xaml.cs b/Samples/SampleWdpClient.UniversalWindows/MainPage.xaml.cs index fca8e65a..ad8d8d3e 100644 --- a/Samples/SampleWdpClient.UniversalWindows/MainPage.xaml.cs +++ b/Samples/SampleWdpClient.UniversalWindows/MainPage.xaml.cs @@ -62,7 +62,7 @@ private void ClearOutput() /// /// The caller of this method. /// The arguments associated with this event. - private void ConnectToDevice_Click(object sender, RoutedEventArgs e) + private async void ConnectToDevice_Click(object sender, RoutedEventArgs e) { this.EnableConnectionControls(false); this.EnableDeviceControls(false); @@ -78,61 +78,51 @@ private void ConnectToDevice_Click(object sender, RoutedEventArgs e) this.password.Password)); StringBuilder sb = new StringBuilder(); - Task connectTask = new Task( - async () => - { - sb.Append(this.MarshalGetCommandOutput()); - sb.AppendLine("Connecting..."); - this.MarshalUpdateCommandOutput(sb.ToString()); - portal.ConnectionStatus += (portal, connectArgs) => - { - if (connectArgs.Status == DeviceConnectionStatus.Connected) - { - sb.Append("Connected to: "); - sb.AppendLine(portal.Address); - sb.Append("OS version: "); - sb.AppendLine(portal.OperatingSystemVersion); - sb.Append("Device family: "); - sb.AppendLine(portal.DeviceFamily); - sb.Append("Platform: "); - sb.AppendLine(String.Format("{0} ({1})", - portal.PlatformName, - portal.Platform.ToString())); - } - else if (connectArgs.Status == DeviceConnectionStatus.Failed) - { - sb.AppendLine("Failed to connect to the device."); - sb.AppendLine(connectArgs.Message); - } - }; - - try - { - // If the user wants to allow untrusted connections, make a call to GetRootDeviceCertificate - // with acceptUntrustedCerts set to true. This will enable untrusted connections for the - // remainder of this session. - if (allowUntrusted) - { - this.certificate = await portal.GetRootDeviceCertificateAsync(true); - } - await portal.ConnectAsync(manualCertificate: this.certificate); - } - catch (Exception exception) - { - sb.AppendLine(exception.Message); - } - this.MarshalUpdateCommandOutput(sb.ToString()); - }); + sb.Append(this.commandOutput.Text); + sb.AppendLine("Connecting..."); + this.commandOutput.Text = sb.ToString(); + portal.ConnectionStatus += (portal, connectArgs) => + { + if (connectArgs.Status == DeviceConnectionStatus.Connected) + { + sb.Append("Connected to: "); + sb.AppendLine(portal.Address); + sb.Append("OS version: "); + sb.AppendLine(portal.OperatingSystemVersion); + sb.Append("Device family: "); + sb.AppendLine(portal.DeviceFamily); + sb.Append("Platform: "); + sb.AppendLine(String.Format("{0} ({1})", + portal.PlatformName, + portal.Platform.ToString())); + } + else if (connectArgs.Status == DeviceConnectionStatus.Failed) + { + sb.AppendLine("Failed to connect to the device."); + sb.AppendLine(connectArgs.Message); + } + }; - Task continuationTask = connectTask.ContinueWith( - (t) => + try + { + // If the user wants to allow untrusted connections, make a call to GetRootDeviceCertificate + // with acceptUntrustedCerts set to true. This will enable untrusted connections for the + // remainder of this session. + if (allowUntrusted) { - this.MarshalEnableDeviceControls(true); - this.MarshalEnableConnectionControls(true); - }); + this.certificate = await portal.GetRootDeviceCertificateAsync(true); + } + await portal.ConnectAsync(manualCertificate: this.certificate); + } + catch (Exception exception) + { + sb.AppendLine(exception.Message); + } - connectTask.Start(); + this.commandOutput.Text = sb.ToString(); + EnableDeviceControls(true); + EnableConnectionControls(true); } /// @@ -179,55 +169,45 @@ private void EnableDeviceControls(bool enable) /// /// The caller of this method. /// The arguments associated with this event. - private void GetIPConfig_Click(object sender, RoutedEventArgs e) + private async void GetIPConfig_Click(object sender, RoutedEventArgs e) { this.ClearOutput(); this.EnableConnectionControls(false); this.EnableDeviceControls(false); StringBuilder sb = new StringBuilder(); - Task getTask = new Task( - async () => - { - sb.Append(this.MarshalGetCommandOutput()); - sb.AppendLine("Getting IP configuration..."); - this.MarshalUpdateCommandOutput(sb.ToString()); + sb.Append(commandOutput.Text); + sb.AppendLine("Getting IP configuration..."); + commandOutput.Text = sb.ToString(); - try - { - IpConfiguration ipconfig = await portal.GetIpConfigAsync(); - - foreach (NetworkAdapterInfo adapterInfo in ipconfig.Adapters) - { - sb.Append(" "); - sb.AppendLine(adapterInfo.Description); - sb.Append(" MAC address :"); - sb.AppendLine(adapterInfo.MacAddress); - foreach (IpAddressInfo address in adapterInfo.IpAddresses) - { - sb.Append(" IP address :"); - sb.AppendLine(address.Address); - } - sb.Append(" DHCP address :"); - sb.AppendLine(adapterInfo.Dhcp.Address.Address); - } - } - catch (Exception ex) - { - sb.AppendLine("Failed to get IP config info."); - sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); - } - }); + try + { + IpConfiguration ipconfig = await portal.GetIpConfigAsync(); - Task continuationTask = getTask.ContinueWith( - (t) => + foreach (NetworkAdapterInfo adapterInfo in ipconfig.Adapters) { - this.MarshalUpdateCommandOutput(sb.ToString()); - this.MarshalEnableDeviceControls(true); - this.MarshalEnableConnectionControls(true); - }); + sb.Append(" "); + sb.AppendLine(adapterInfo.Description); + sb.Append(" MAC address :"); + sb.AppendLine(adapterInfo.MacAddress); + foreach (IpAddressInfo address in adapterInfo.IpAddresses) + { + sb.Append(" IP address :"); + sb.AppendLine(address.Address); + } + sb.Append(" DHCP address :"); + sb.AppendLine(adapterInfo.Dhcp.Address.Address); + } + } + catch (Exception ex) + { + sb.AppendLine("Failed to get IP config info."); + sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); + } - getTask.Start(); + commandOutput.Text = sb.ToString(); + EnableDeviceControls(true); + EnableConnectionControls(true); } /// @@ -235,130 +215,57 @@ private void GetIPConfig_Click(object sender, RoutedEventArgs e) /// /// The caller of this method. /// The arguments associated with this event. - private void GetWifiInfo_Click(object sender, RoutedEventArgs e) + private async void GetWifiInfo_Click(object sender, RoutedEventArgs e) { this.ClearOutput(); this.EnableConnectionControls(false); this.EnableDeviceControls(false); StringBuilder sb = new StringBuilder(); - Task getTask = new Task( - async () => - { - sb.Append(this.MarshalGetCommandOutput()); - sb.AppendLine("Getting WiFi interfaces and networks..."); - this.MarshalUpdateCommandOutput(sb.ToString()); - - try - { - WifiInterfaces wifiInterfaces = await portal.GetWifiInterfacesAsync(); - sb.AppendLine("WiFi Interfaces:"); - foreach (WifiInterface wifiInterface in wifiInterfaces.Interfaces) - { - sb.Append(" "); - sb.AppendLine(wifiInterface.Description); - sb.Append(" GUID: "); - sb.AppendLine(wifiInterface.Guid.ToString()); - - WifiNetworks wifiNetworks = await portal.GetWifiNetworksAsync(wifiInterface.Guid); - sb.AppendLine(" Networks:"); - foreach (WifiNetworkInfo network in wifiNetworks.AvailableNetworks) - { - sb.Append(" SSID: "); - sb.AppendLine(network.Ssid); - sb.Append(" Profile name: "); - sb.AppendLine(network.ProfileName); - sb.Append(" is connected: "); - sb.AppendLine(network.IsConnected.ToString()); - sb.Append(" Channel: "); - sb.AppendLine(network.Channel.ToString()); - sb.Append(" Authentication algorithm: "); - sb.AppendLine(network.AuthenticationAlgorithm); - sb.Append(" Signal quality: "); - sb.AppendLine(network.SignalQuality.ToString()); - } - }; - } - catch (Exception ex) - { - sb.AppendLine("Failed to get WiFi info."); - sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); - } - }); - - Task continuationTask = getTask.ContinueWith( - (t) => - { - this.MarshalUpdateCommandOutput(sb.ToString()); - this.MarshalEnableDeviceControls(true); - this.MarshalEnableConnectionControls(true); - }); - getTask.Start(); - } - - /// - /// Executes the EnabledConnectionControls method on the UI thread. - /// - /// True to enable the controls, false to disable them. - private void MarshalEnableConnectionControls(bool enable) - { - Task t = this.Dispatcher.RunAsync( - CoreDispatcherPriority.Normal, - () => - { - this.EnableConnectionControls(enable); - }).AsTask(); - t.Wait(); - } + sb.Append(commandOutput.Text); + sb.AppendLine("Getting WiFi interfaces and networks..."); + commandOutput.Text = sb.ToString(); - /// - /// Executes the EnabledDeviceControls method on the UI thread. - /// - /// True to enable the controls, false to disable them. - private void MarshalEnableDeviceControls(bool enable) - { - Task t = this.Dispatcher.RunAsync( - CoreDispatcherPriority.Normal, - () => - { - this.EnableDeviceControls(enable); - }).AsTask(); - t.Wait(); - } - - /// - /// Executes the fetching of the text displayed in the command output UI element on the UI thread. - /// - /// The contents of the command output UI element. - private string MarshalGetCommandOutput() - { - string output = string.Empty; - - Task t = this.Dispatcher.RunAsync( - CoreDispatcherPriority.Normal, - () => + try + { + WifiInterfaces wifiInterfaces = await portal.GetWifiInterfacesAsync(); + sb.AppendLine("WiFi Interfaces:"); + foreach (WifiInterface wifiInterface in wifiInterfaces.Interfaces) { - output = this.commandOutput.Text; - }).AsTask(); - t.Wait(); - - return output; - } + sb.Append(" "); + sb.AppendLine(wifiInterface.Description); + sb.Append(" GUID: "); + sb.AppendLine(wifiInterface.Guid.ToString()); + + WifiNetworks wifiNetworks = await portal.GetWifiNetworksAsync(wifiInterface.Guid); + sb.AppendLine(" Networks:"); + foreach (WifiNetworkInfo network in wifiNetworks.AvailableNetworks) + { + sb.Append(" SSID: "); + sb.AppendLine(network.Ssid); + sb.Append(" Profile name: "); + sb.AppendLine(network.ProfileName); + sb.Append(" is connected: "); + sb.AppendLine(network.IsConnected.ToString()); + sb.Append(" Channel: "); + sb.AppendLine(network.Channel.ToString()); + sb.Append(" Authentication algorithm: "); + sb.AppendLine(network.AuthenticationAlgorithm); + sb.Append(" Signal quality: "); + sb.AppendLine(network.SignalQuality.ToString()); + } + }; + } + catch (Exception ex) + { + sb.AppendLine("Failed to get WiFi info."); + sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); + } - /// - /// Executes the update of the text displayed in the command output UI element ont he UI thread. - /// - /// The text to display in the command output UI element. - private void MarshalUpdateCommandOutput(string output) - { - Task t = this.Dispatcher.RunAsync( - CoreDispatcherPriority.Normal, - () => - { - this.commandOutput.Text = output; - }).AsTask(); - t.Wait(); + commandOutput.Text = sb.ToString(); + EnableDeviceControls(true); + EnableConnectionControls(true); } /// @@ -376,7 +283,7 @@ private void Password_PasswordChanged(object sender, RoutedEventArgs e) /// /// The caller of this method. /// The arguments associated with this event. - private void RebootDevice_Click(object sender, RoutedEventArgs e) + private async void RebootDevice_Click(object sender, RoutedEventArgs e) { bool reenableDeviceControls = false; @@ -385,34 +292,25 @@ private void RebootDevice_Click(object sender, RoutedEventArgs e) this.EnableDeviceControls(false); StringBuilder sb = new StringBuilder(); - Task rebootTask = new Task( - async () => - { - sb.Append(this.MarshalGetCommandOutput()); - sb.AppendLine("Rebooting the device"); - this.MarshalUpdateCommandOutput(sb.ToString()); - try - { - await portal.RebootAsync(); - } - catch (Exception ex) - { - sb.AppendLine("Failed to reboot the device."); - sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); - reenableDeviceControls = true; - } - }); + sb.Append(commandOutput.Text); + sb.AppendLine("Rebooting the device"); + commandOutput.Text = sb.ToString(); - Task continuationTask = rebootTask.ContinueWith( - (t) => - { - this.MarshalUpdateCommandOutput(sb.ToString()); - this.MarshalEnableDeviceControls(reenableDeviceControls); - this.MarshalEnableConnectionControls(true); - }); + try + { + await portal.RebootAsync(); + } + catch (Exception ex) + { + sb.AppendLine("Failed to reboot the device."); + sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); + reenableDeviceControls = true; + } - rebootTask.Start(); + commandOutput.Text = sb.ToString(); + EnableDeviceControls(reenableDeviceControls); + EnableConnectionControls(true); } /// @@ -420,7 +318,7 @@ private void RebootDevice_Click(object sender, RoutedEventArgs e) /// /// The caller of this method. /// The arguments associated with this event. - private void ShutdownDevice_Click(object sender, RoutedEventArgs e) + private async void ShutdownDevice_Click(object sender, RoutedEventArgs e) { bool reenableDeviceControls = false; @@ -429,34 +327,23 @@ private void ShutdownDevice_Click(object sender, RoutedEventArgs e) this.EnableDeviceControls(false); StringBuilder sb = new StringBuilder(); - Task shutdownTask = new Task( - async () => - { - sb.Append(this.MarshalGetCommandOutput()); - sb.AppendLine("Shutting down the device"); - this.MarshalUpdateCommandOutput(sb.ToString()); - - try - { - await portal.ShutdownAsync(); - } - catch (Exception ex) - { - sb.AppendLine("Failed to shut down the device."); - sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); - reenableDeviceControls = true; - } - }); - - Task continuationTask = shutdownTask.ContinueWith( - (t) => - { - this.MarshalUpdateCommandOutput(sb.ToString()); - this.MarshalEnableDeviceControls(reenableDeviceControls); - this.MarshalEnableConnectionControls(true); - }); + sb.Append(commandOutput.Text); + sb.AppendLine("Shutting down the device"); + commandOutput.Text = sb.ToString(); + try + { + await portal.ShutdownAsync(); + } + catch (Exception ex) + { + sb.AppendLine("Failed to shut down the device."); + sb.AppendLine(ex.GetType().ToString() + " - " + ex.Message); + reenableDeviceControls = true; + } - shutdownTask.Start(); + commandOutput.Text = sb.ToString(); + EnableDeviceControls(reenableDeviceControls); + EnableConnectionControls(true); } /// 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..6774e3da --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/Core/EtwTests.cs @@ -0,0 +1,109 @@ +//---------------------------------------------------------------------------------------------- +// +// Licensed under the MIT License. See LICENSE.TXT in the project root license information. +// +//---------------------------------------------------------------------------------------------- + +using System; +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) + { + Assert.IsTrue(etw.Providers.Count > 0); + Assert.IsTrue(etw.Providers.All(etwProvider => !string.IsNullOrEmpty(etwProvider.Name))); + } + } +} diff --git a/WindowsDevicePortalWrapper/UnitTestProject/Core/WindowsErrorReportingTests.cs b/WindowsDevicePortalWrapper/UnitTestProject/Core/WindowsErrorReportingTests.cs new file mode 100644 index 00000000..9335c9c7 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/Core/WindowsErrorReportingTests.cs @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------------------------------- +// +// Licensed under the MIT License. See LICENSE.TXT in the project root license information. +// +//---------------------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Microsoft.Tools.WindowsDevicePortal.DevicePortal; + +namespace Microsoft.Tools.WindowsDevicePortal.Tests.Core +{ + /// + /// Test class for Windows Error Reporting (WER) APIs. + /// + [TestClass] + public class WindowsErrorReportingTests : BaseTests + { + /// + /// Basic test of GET method for getting a list of Windows Error Reporting (WER) reports. + /// + [TestMethod] + public void GetWindowsErrorReportsTest() + { + TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.WindowsErrorReportsApi, HttpMethods.Get); + + Task getWerReportsTask = TestHelpers.Portal.GetWindowsErrorReportsAsync(); + getWerReportsTask.Wait(); + + Assert.AreEqual(TaskStatus.RanToCompletion, getWerReportsTask.Status); + + List deviceReports = getWerReportsTask.Result.UserReports; + Assert.AreEqual(6, deviceReports.Count); + deviceReports.ForEach(userReport => + { + Assert.IsFalse(string.IsNullOrWhiteSpace(userReport.UserName)); + userReport.Reports.ForEach(report => + { + Assert.AreNotEqual(0, report.CreationTime); + Assert.IsFalse(string.IsNullOrWhiteSpace(report.Name)); + switch (report.Type.ToLower()) + { + case "queue": + case "archive": + break; + default: + Assert.Fail($"Expected the report type to be 'Queue' or 'Archive'. Actual value is '{report.Type}'."); + break; + } + }); + }); + } + + /// + /// Basic test of GET method for getting a list of Windows Error Reporting (WER) report files. + /// + [TestMethod] + public void GetWindowsErrorReportFilesTest() + { + TestHelpers.MockHttpResponder.AddMockResponse(DevicePortal.WindowsErrorReportingFilesApi, HttpMethods.Get); + + Task getWerReportingFilesTask = TestHelpers.Portal.GetWindowsErrorReportingFileListAsync(string.Empty, string.Empty, string.Empty); + getWerReportingFilesTask.Wait(); + + Assert.AreEqual(TaskStatus.RanToCompletion, getWerReportingFilesTask.Status); + + List list = getWerReportingFilesTask.Result.Files; + Assert.AreEqual(2, list.Count); + list.ForEach(file => + { + Assert.IsFalse(string.IsNullOrWhiteSpace(file.Name)); + Assert.AreNotEqual(0, file.Size); + }); + } + } +} 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..d332e2d4 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_customproviders_Default.dat @@ -0,0 +1 @@ +{"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 new file mode 100644 index 00000000..d332e2d4 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_etw_providers_Default.dat @@ -0,0 +1 @@ +{"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_wer_report_files_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_wer_report_files_Default.dat new file mode 100644 index 00000000..5af22c47 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_wer_report_files_Default.dat @@ -0,0 +1 @@ +{"Files" : [{"Name" : "DMI59C3.tmp.log.xml", "Size" : 17490},{"Name" : "Report.wer", "Size" : 2354}]} \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_wer_reports_Default.dat b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_wer_reports_Default.dat new file mode 100644 index 00000000..fd900ce2 --- /dev/null +++ b/WindowsDevicePortalWrapper/UnitTestProject/MockData/Defaults/api_wer_reports_Default.dat @@ -0,0 +1 @@ +{"WerReports" : [{"User" : "All Users", "Reports" : []},{"User" : "Default", "Reports" : []},{"User" : "Default User", "Reports" : []},{"User" : "defaultuser0", "Reports" : []},{"User" : "Public", "Reports" : []},{"User" : "SYSTEM", "Reports" : [{"CreationTime" : 131318340058889142, "Name" : "NonCritical_x64_7929388dba23a244febb598fddbb379b5a87d15_00000000_cab_03f96433", "Type" : "Queue"},{"CreationTime" : 131318340058732887, "Name" : "NonCritical_x64_bf8ef24cbbad37c50f759d68f2292f1f2b0e7df_00000000_cab_03f96423", "Type" : "Queue"},{"CreationTime" : 131318550054539960, "Name" : "NonCritical_x64_f52a5825f740cca45c7b0c8078746ec73beaac_00000000_18add298", "Type" : "Archive"},{"CreationTime" : 131318340063576709, "Name" : "NonCritical_x64_f728cd6f9e9f34997130a4d3dd3d855648a6f_00000000_0ca56608", "Type" : "Archive"}]}]} \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj b/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj index 5f96bccb..f3f1c29f 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj +++ b/WindowsDevicePortalWrapper/UnitTestProject/UnitTestProject.csproj @@ -62,6 +62,8 @@ + + @@ -85,9 +87,24 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestDelete.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestDelete.cs index 773180df..05019cda 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestDelete.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestDelete.cs @@ -43,7 +43,7 @@ private async Task DeleteAsync(Uri uri) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestGet.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestGet.cs index bca5ee88..c3e45d45 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestGet.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestGet.cs @@ -44,7 +44,7 @@ private async Task GetAsync( { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } using (HttpContent content = response.Content) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPost.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPost.cs index 6fa1755e..87423035 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPost.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPost.cs @@ -56,7 +56,7 @@ private async Task PostAsync( { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPut.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPut.cs index d0f89aab..98c38a8d 100644 --- a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPut.cs +++ b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/RestPut.cs @@ -47,7 +47,7 @@ private async Task PutAsync( { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) diff --git a/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs b/WindowsDevicePortalWrapper/UnitTestProject/WDPMockImplementations/WebSocket.cs index 8a499e7c..5439b300 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,52 +90,45 @@ 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(); using (HttpResponseMessage response = webSocketTask.Result) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } using (HttpContent content = response.Content) { MemoryStream dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we return with the stream pointed at the origin. dataStream.Position = 0; @@ -121,5 +147,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 await DevicePortalException.CreateAsync(response); + } + } + } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppCrashDumpCollection.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppCrashDumpCollection.cs index 17acfa3a..99b1eef3 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppCrashDumpCollection.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppCrashDumpCollection.cs @@ -64,7 +64,8 @@ public async Task GetAppCrashDumpAsync(AppCrashDump crashdump) /// Task tracking completion of the request. public async Task DeleteAppCrashDumpAsync(AppCrashDump crashdump) { - await this.DeleteAsync(CrashDumpFileApi, + await this.DeleteAsync( + CrashDumpFileApi, string.Format("packageFullName={0}&fileName={1}", crashdump.PackageFullName, crashdump.Filename)); } @@ -99,13 +100,13 @@ public async Task GetAppCrashDumpSettingsAsync(string pack public async Task SetAppCrashDumpSettingsAsync(AppPackage app, bool enable = true) { string pfn = app.PackageFullName; - await SetAppCrashDumpSettingsAsync(pfn, enable); + await this.SetAppCrashDumpSettingsAsync(pfn, enable); } /// /// Set the crash settings for a sideloaded app. /// - /// The app to set crash settings for. + /// The app to set crash settings for. /// Whether to enable or disable crash collection for the app. /// Task tracking completion of the request. public async Task SetAppCrashDumpSettingsAsync(string packageFullName, bool enable = true) @@ -133,7 +134,7 @@ await this.DeleteAsync( public class AppCrashDumpSettings { /// - /// Gets whether crash dumps are enabled for the app + /// Gets a value indicating whether crash dumps are enabled for the app /// [DataMember(Name = "CrashDumpEnabled")] public bool CrashDumpEnabled @@ -149,7 +150,6 @@ public bool CrashDumpEnabled [DataContract] public class AppCrashDump { - /// /// Gets the timestamp of the crash as a string. /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs index 062dc6a1..be904419 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppDeployment.cs @@ -185,7 +185,7 @@ public async Task InstallApplicationAsync( await Task.Delay(TimeSpan.FromMilliseconds(stateCheckIntervalMs)); - status = await this.GetInstallStatusAsync(); + status = await this.GetInstallStatusAsync().ConfigureAwait(false); } while (status == ApplicationInstallStatus.InProgress); @@ -366,6 +366,28 @@ public class PackageInfo [DataMember(Name = "Version")] public PackageVersion Version { get; private set; } + /// + /// Gets package origin, a measure of how the app was installed. + /// PackageOrigin_Unknown            = 0, + /// PackageOrigin_Unsigned           = 1, + /// PackageOrigin_Inbox              = 2, + /// PackageOrigin_Store              = 3, + /// PackageOrigin_DeveloperUnsigned  = 4, + /// PackageOrigin_DeveloperSigned    = 5, + /// PackageOrigin_LineOfBusiness     = 6 + /// + [DataMember(Name = "PackageOrigin")] + public int PackageOrigin { get; private set; } + + // + /// Helper method to determine if the app was sideloaded and therefore can be used with e.g. GetFolderContentsAsync + /// + /// True if the package is sideloaded. + public bool IsSideloaded() + { + return (this.PackageOrigin == 4 || this.PackageOrigin == 5); + } + /// /// Get a string representation of the package /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs index cc93f29e..5829f175 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/AppFileExplorer.cs @@ -310,6 +310,12 @@ public class FileOrFolderInformation [DataMember(Name = "FileSize")] public long SizeInBytes { get; private set; } + /// + /// Gets whether the current item is a folder by checking for FILE_ATTRIBUTE_DIRECTORY + /// See https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx + /// + public bool IsFolder { get { return (this.Type & 0x10) == 0x10; } } + /// /// Overridden ToString method providing a user readable /// display of a file or folder. Tries to match the formatting @@ -321,7 +327,7 @@ public override string ToString() DateTime timestamp = DateTime.FromFileTime(this.DateCreated); // Check if this is a folder. - if (!string.Equals(this.SubPath, this.CurrentDir)) + if (this.IsFolder) { return string.Format("{0,-24:MM/dd/yyyy HH:mm tt}{1,-14} {2}\n", timestamp, "", this.Name); } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/Etw.cs index 91d6b976..8b131015 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,301 @@ 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; + + /// + /// Determines if the event listener has been registered + /// + private bool isListeningForRealtimeEvents = false; + + /// + /// The ETW event message received handler + /// + public event WebSocketMessageReceivedEventHandler RealtimeEventsMessageReceived; + + /// + /// Returns the current set of registered custom 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); + } + + /// + /// 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. + /// /// Verbosity level - 1 for least, 5 for most verbose. + /// Task for toggling the listening state of the specified provider. + public async Task ToggleEtwProviderAsync(EtwProviderInfo etwProvider, bool isEnabled = true, int level = 5) + { + await this.ToggleEtwProviderAsync(etwProvider.GUID, isEnabled, level); + } + + /// + /// Toggles the listening state of a specific provider on the realtime events WebSocket. + /// + /// The GUID of the provider to update the listening state of. + /// Determines whether the listening state should be enabled or disabled. + /// Verbosity level - 1 for least, 5 for most verbose. + /// Task for toggling the listening state of the specified provider. + public async Task ToggleEtwProviderAsync(Guid etwProvider, bool isEnabled = true, int level = 5) + { + string action = isEnabled ? "enable" : "disable"; + string message = $"provider {etwProvider} {action} {level}"; + + await this.InitializeRealtimeEventsWebSocketAsync(); + 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() + { + await this.InitializeRealtimeEventsWebSocketAsync(); + + if (!this.isListeningForRealtimeEvents) + { + this.isListeningForRealtimeEvents = true; + this.realtimeEventsWebSocket.WebSocketMessageReceived += this.EtwEventsReceivedHandler; + } + + await this.realtimeEventsWebSocket.ReceiveMessagesAsync(); + } + + /// + /// Stop listening for ETW events. + /// + /// Task for stop listening for ETW events and disconnecting from the WebSocket. + public async Task StopListeningForEtwEventsAsync() + { + if (this.isListeningForRealtimeEvents) + { + this.isListeningForRealtimeEvents = false; + this.realtimeEventsWebSocket.WebSocketMessageReceived -= this.EtwEventsReceivedHandler; + } + + await this.realtimeEventsWebSocket.CloseAsync(); + } /// - /// API to getthe list of registered custom ETW providers. + /// Creates a new if it hasn't already been initialized. /// - public static readonly string GetCustomEtwProvidersApi = "api/etw/customproviders"; + /// Task for connecting the ETW realtime event WebSocket. + private async Task InitializeRealtimeEventsWebSocketAsync() + { + 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.realtimeEventsWebSocket.ConnectAsync(RealtimeEtwSessionApi); + } /// - /// API to getthe list of registered ETW providers. + /// Handler for the ETW received event that passes the event to the RealtimeEventsMessageReceived handler. /// - public static readonly string GetEtwProvidersApi = "api/etw/providers"; + /// 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 + { + /// + /// Gets or sets the raw list of events. Not for straight usage, as it's entirely unformatted. + /// + [DataMember(Name = "Events")] + private List> RawEvents { get; set; } + + /// + /// Saves the downconverted list of events + /// + private List stashedList; + + /// + /// Get the list of ETW Events that occured in the last second. + /// + public List Events + { + get + { + if (this.stashedList != null) + { + return this.stashedList; + } + + List events = new List(); + foreach (Dictionary dic in RawEvents ) + { + events.Add(new EtwEventInfo(dic)); + } + + this.stashedList = events; + return this.stashedList; + } + } + + + /// + /// Gets the event frequency. + /// This is always 10 million (10000000) in RS2 devices. + /// + [DataMember(Name = "Frequency")] + public long Frequency { get; private set; } + } + + /// + /// ETW Events Info. Allows strongly typed access to guaranteed fields + /// like ID or Timestamp, and raw (as string) access to all other + /// payload data, like Latency or PID. + /// + public class EtwEventInfo : Dictionary + { + + /// + /// Initializes a new instance of the class. Used by the DataContract at access time. + /// + /// Base dictionary used to populate the object. + internal EtwEventInfo(IDictionary dictionary) : base(dictionary) + { + } + + /// + /// Gets the event identifer. + /// + public ushort ID + { + get + { + return ushort.Parse(this["ID"]); + } + } + + /// + /// Gets the event keyword. + /// + public ulong Keyword + { + get + { + return ulong.Parse(this["Keyword"]); + } + } + + /// + /// Gets the event level. + /// + public uint Level + { + get + { + return uint.Parse(this["Level"]); + } + } + + /// + /// Gets the event provider name. + /// + public string Provider + { + get + { + return this["ProviderName"]; + } + } + + /// + /// Gets the event timestamp. + /// + public ulong Timestamp + { + get + { + return ulong.Parse(this["Timestamp"]); + } + } + } + + /// + /// 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. + /// + [DataContract] + public class EtwProviderInfo + { + /// + /// Gets provider guid. + /// + [DataMember(Name = "GUID")] + public Guid GUID { get; private set; } + + /// + /// Gets provider name. + /// + [DataMember(Name = "Name")] + public string Name { get; private set; } + } + +#endregion // Data contract } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/OsInformation.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/OsInformation.cs index 0530bf9e..17771459 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/OsInformation.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/OsInformation.cs @@ -93,7 +93,7 @@ public enum DevicePortalPlatforms /// String containing the device's family. public async Task GetDeviceFamilyAsync() { - DeviceOsFamily deviceFamily = await this.GetAsync(DeviceFamilyApi); + DeviceOsFamily deviceFamily = await this.GetAsync(DeviceFamilyApi).ConfigureAwait(false); return deviceFamily.Family; } @@ -111,9 +111,9 @@ public async Task GetDeviceNameAsync() /// Gets information about the device's operating system. /// /// OperatingSystemInformation object containing details of the installed operating system. - public async Task GetOperatingSystemInformationAsync() + public Task GetOperatingSystemInformationAsync() { - return await this.GetAsync(OsInfoApi); + return this.GetAsync(OsInfoApi); } /// @@ -122,9 +122,9 @@ public async Task GetOperatingSystemInformationAsync /// The name to assign to the device. /// The new name does not take effect until the device has been restarted. /// Task tracking setting the device name completion. - public async Task SetDeviceNameAsync(string name) + public Task SetDeviceNameAsync(string name) { - await this.PostAsync( + return this.PostAsync( MachineNameApi, string.Format("name={0}", Utilities.Hex64Encode(name))); } 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/Core/WindowsErrorReporting.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs index c2842cbe..cf53ab2e 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/WindowsErrorReporting.cs @@ -6,8 +6,15 @@ namespace Microsoft.Tools.WindowsDevicePortal { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.Serialization; + using System.Threading.Tasks; + /// - /// Wrappers for DNS methods + /// Wrapper for collecting Windows Error Reports from the device. /// public partial class DevicePortal { @@ -25,5 +32,183 @@ public partial class DevicePortal /// API for getting the list of Windows error reports. /// public static readonly string WindowsErrorReportsApi = "api/wer/reports"; + + /// + /// Gets the list of Windows Error Reporting (WER) reports. + /// + /// The list of Windows Error Reporting (WER) reports. + public async Task GetWindowsErrorReportsAsync() + { + this.CheckPlatformSupport(); + + return await this.GetAsync(WindowsErrorReportsApi); + } + + /// + /// Gets the list of files in a Windows Error Reporting (WER) report. + /// + /// The user associated with the report. + /// The type of report. This can be either 'queried' or 'archived'. + /// The name of the report. + /// The list of files. + public async Task GetWindowsErrorReportingFileListAsync(string user, string type, string name) + { + this.CheckPlatformSupport(); + + Dictionary payload = new Dictionary(); + payload.Add("user", user); + payload.Add("type", type); + payload.Add("name", Utilities.Hex64Encode(name)); + + return await this.GetAsync(WindowsErrorReportingFilesApi, Utilities.BuildQueryString(payload)); + } + + /// + /// Gets the specified file from a Windows Error Reporting (WER) report. + /// + /// The user associated with the report. + /// The type of report. This can be either 'queried' or 'archived'. + /// The name of the report. + /// The name of the file to download from the report. + /// Byte array containing the file data + public async Task GetWindowsErrorReportingFileAsync(string user, string type, string name, string file) + { + this.CheckPlatformSupport(); + + Dictionary payload = new Dictionary(); + payload.Add("user", user); + payload.Add("type", type); + payload.Add("name", Utilities.Hex64Encode(name)); + payload.Add("file", Utilities.Hex64Encode(file)); + + Uri uri = Utilities.BuildEndpoint( + this.deviceConnection.Connection, + WindowsErrorReportingFileApi, + Utilities.BuildQueryString(payload)); + + byte[] werFile = null; + using (Stream stream = await this.GetAsync(uri)) + { + werFile = new byte[stream.Length]; + stream.Read(werFile, 0, werFile.Length); + } + + return werFile; + } + + /// + /// Checks if the Windows Error Reporting (WER) APIs are being called on a supported platform. + /// + private void CheckPlatformSupport() + { + switch (this.Platform) + { + case DevicePortalPlatforms.Mobile: + case DevicePortalPlatforms.XboxOne: + throw new NotSupportedException("This method is only supported on Windows Desktop, HoloLens and IoT platforms."); + } + } + + /// + /// A list of all files contained within a Windows Error Reporting (WER) report. + /// + [DataContract] + public class WerFiles + { + /// + /// Gets a list of all files contained within a Windows Error Reporting (WER) report. + /// + [DataMember(Name = "Files")] + public List Files { get; private set; } + } + + /// + /// Information about a Windows Error Reporting (WER) report file. + /// + [DataContract] + public class WerFileInformation + { + /// + /// Gets the name of the file. + /// + [DataMember(Name = "Name")] + public string Name { get; private set; } + + /// + /// Gets the size of the file (in bytes). + /// + [DataMember(Name = "Size")] + public int Size { get; private set; } + } + + /// + /// A list of all Windows Error Reporting (WER) reports on a device. + /// + [DataContract] + public class WerDeviceReports + { + /// + /// Gets a list of all Windows Error Reporting (WER) reports on a + /// device. The SYSTEM user account usually holds the bulk of the + /// error reports. + /// + [DataMember(Name = "WerReports")] + public List UserReports { get; private set; } + + /// + /// Convenience accessor for the System error reports - this is + /// where most error reports end up. + /// + public WerUserReports SystemErrorReports { + get + { + return UserReports.First(x => x.UserName == "SYSTEM"); + } + } + } + + /// + /// A list of Windows Error Reporting (WER) reports for a specific user. + /// + [DataContract] + public class WerUserReports + { + /// + /// Gets the user name. + /// + [DataMember(Name = "User")] + public string UserName { get; private set; } + + /// + /// Gets a list of Windows Error Reporting (WER) reports + /// + [DataMember(Name = "Reports")] + public List Reports { get; private set; } + } + + /// + /// Information about a Windows Error Reporting (WER) report. + /// + [DataContract] + public class WerReportInformation + { + /// + /// Gets the creation time. + /// + [DataMember(Name = "CreationTime")] + public ulong CreationTime { get; private set; } + + /// + /// Gets the report name (not base64 encoded). + /// + [DataMember(Name = "Name")] + public string Name { get; private set; } + + /// + /// Gets the report type ("Queue" or "Archive"). + /// + [DataMember(Name = "Type")] + public string Type { get; private set; } + } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs index e58eae5f..ed6278c7 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/DevicePortal.cs @@ -189,7 +189,7 @@ public string PlatformName public async Task ConnectAsync( string ssid = null, string ssidKey = null, - bool updateConnection = true, + bool updateConnection = false, #if WINDOWS_UWP Certificate manualCertificate = null) #else @@ -216,8 +216,8 @@ public async Task ConnectAsync( DeviceConnectionStatus.Connecting, DeviceConnectionPhase.RequestingOperatingSystemInformation, connectionPhaseDescription); - this.deviceConnection.Family = await this.GetDeviceFamilyAsync(); - this.deviceConnection.OsInfo = await this.GetOperatingSystemInformationAsync(); + this.deviceConnection.Family = await this.GetDeviceFamilyAsync().ConfigureAwait(false); + this.deviceConnection.OsInfo = await this.GetOperatingSystemInformationAsync().ConfigureAwait(false); // Default to using whatever was specified in the connection. bool requiresHttps = this.IsUsingHttps(); @@ -231,7 +231,7 @@ public async Task ConnectAsync( DeviceConnectionStatus.Connecting, DeviceConnectionPhase.DeterminingConnectionRequirements, connectionPhaseDescription); - requiresHttps = await this.GetIsHttpsRequiredAsync(); + requiresHttps = await this.GetIsHttpsRequiredAsync().ConfigureAwait(false); } // Connect the device to the specified network. @@ -242,10 +242,10 @@ public async Task ConnectAsync( DeviceConnectionStatus.Connecting, DeviceConnectionPhase.ConnectingToTargetNetwork, connectionPhaseDescription); - WifiInterfaces wifiInterfaces = await this.GetWifiInterfacesAsync(); + WifiInterfaces wifiInterfaces = await this.GetWifiInterfacesAsync().ConfigureAwait(false); // TODO - consider what to do if there is more than one wifi interface on a device - await this.ConnectToWifiNetworkAsync(wifiInterfaces.Interfaces[0].Guid, ssid, ssidKey); + await this.ConnectToWifiNetworkAsync(wifiInterfaces.Interfaces[0].Guid, ssid, ssidKey).ConfigureAwait(false); } // Get the device's IP configuration and update the connection as appropriate. @@ -268,7 +268,7 @@ public async Task ConnectAsync( } this.deviceConnection.UpdateConnection( - await this.GetIpConfigAsync(), + await this.GetIpConfigAsync().ConfigureAwait(false), requiresHttps, preservePort); } @@ -296,6 +296,7 @@ await this.GetIpConfigAsync(), while (innermostException.InnerException != null) { innermostException = innermostException.InnerException; + await Task.Yield(); } this.ConnectionFailedDescription = innermostException.Message; @@ -372,13 +373,13 @@ public async Task SaveEndpointResponseToFileAsync( websocket.WebSocketStreamReceived += streamReceivedHandler; - Task startListeningForStreamTask = websocket.StartListeningForMessagesAsync(endpoint); - startListeningForStreamTask.Wait(); + await websocket.ConnectAsync(endpoint); + + await websocket.ReceiveMessagesAsync(); streamReceived.WaitOne(); - Task stopListeningForStreamTask = websocket.StopListeningForMessagesAsync(); - stopListeningForStreamTask.Wait(); + await websocket.CloseAsync(); websocket.WebSocketStreamReceived -= streamReceivedHandler; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs index 6abfd68a..6fb6f5c7 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Exceptions/DevicePortalException.cs @@ -72,16 +72,16 @@ public DevicePortalException( /// Http response message. /// Optional exception message. /// Optional inner exception. - public DevicePortalException( + public static async Task CreateAsync( HttpResponseMessage responseMessage, string message = "", - Exception innerException = null) : this( - responseMessage.StatusCode, + Exception innerException = null) + { + DevicePortalException error = new DevicePortalException(responseMessage.StatusCode, responseMessage.ReasonPhrase, responseMessage.RequestMessage != null ? responseMessage.RequestMessage.RequestUri : null, message, - innerException) - { + innerException); try { if (responseMessage.Content != null) @@ -92,9 +92,7 @@ public DevicePortalException( { dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we point the stream at the origin. dataStream.Position = 0; @@ -103,12 +101,7 @@ public DevicePortalException( IBuffer dataBuffer = null; using (IHttpContent messageContent = responseMessage.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); if (dataBuffer != null) { @@ -123,19 +116,21 @@ public DevicePortalException( HttpErrorResponse errorResponse = (HttpErrorResponse)serializer.ReadObject(dataStream); - this.HResult = errorResponse.ErrorCode; - this.Reason = errorResponse.ErrorMessage; + error.HResult = errorResponse.ErrorCode; + error.Reason = errorResponse.ErrorMessage; // If we didn't get the Hresult and reason from these properties, try the other ones. - if (this.HResult == 0) + if (error.HResult == 0) { - this.HResult = errorResponse.Code; + error.HResult = errorResponse.Code; } - if (string.IsNullOrEmpty(this.Reason)) + if (string.IsNullOrEmpty(error.Reason)) { - this.Reason = errorResponse.Reason; + error.Reason = errorResponse.Reason; } + + dataStream.Dispose(); } } } @@ -143,6 +138,7 @@ public DevicePortalException( { // Do nothing if we fail to get additional error details from the response body. } + return error; } /// diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/RestGet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/RestGet.cs index 0a027444..8f5b4c31 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/RestGet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/RestGet.cs @@ -83,7 +83,7 @@ private async Task GetAsync( DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); - using (Stream dataStream = await this.GetAsync(uri)) + using (Stream dataStream = await this.GetAsync(uri).ConfigureAwait(false)) { if ((dataStream != null) && (dataStream.Length != 0)) diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/WebSocket.cs index 93ae900b..05f16a7b 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.IsListeningForMessages) + if (this.IsConnected && !this.IsListeningForMessages) { - Uri uri = Utilities.BuildEndpoint( - this.deviceConnection.WebSocketConnection, - apiPath, - payload); - await this.StartListeningForMessagesInternalAsync(uri); + 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.IsConnected) + { + await this.SendMessageInternalAsync(message); } } @@ -114,7 +153,13 @@ private void ConvertStreamToMessage(Stream stream) { using (stream) { - DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); + DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings() + { + UseSimpleDictionaryFormat = true + }; + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings); + + T message = (T)serializer.ReadObject(stream); this.WebSocketMessageReceived?.Invoke( 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..7a27ba55 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. @@ -72,60 +72,55 @@ public partial class DevicePortal /// Gets the available bluetooth device information. /// /// List of Available bluetooth devices - public AvailableBluetoothDevicesInfo GetAvailableBluetoothDevicesInfo() + public async Task GetAvailableBluetoothDevicesInfoAsync() { 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; - Task startListeningForBluetooth = this.StartListeningForBluetoothAsync(AvailableBluetoothDevicesApi); - startListeningForBluetooth.Wait(); + await this.StartListeningForBluetoothAsync(AvailableBluetoothDevicesApi); - BluetoothReceived.WaitOne(); + bluetoothReceived.WaitOne(); - Task stopListeningForBluetooth = this.StopListeningForBluetoothAsync(); - stopListeningForBluetooth.Wait(); + await this.StopListeningForBluetoothAsync(); this.BluetoothDeviceListReceived -= bluetoothReceivedHandler; return bluetooth; } - /// /// Gets the paired bluetooth device information. /// /// List of paired bluetooth devices - public PairedBluetoothDevicesInfo GetPairedBluetoothDevicesInfo() + public async Task GetPairedBluetoothDevicesInfoAsync() { 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; - Task startListeningForPairedBluetooth = this.StartListeningForPairedBluetoothAsync(PairedBluetoothDevicesApi); - startListeningForPairedBluetooth.Wait(); + await this.StartListeningForPairedBluetoothAsync(PairedBluetoothDevicesApi); - PairedBluetoothReceived.WaitOne(); + pairedBluetoothReceived.WaitOne(); - Task stopListeningForPairedBluetooth = this.StopListeningForPairedBluetoothAsync(); - stopListeningForPairedBluetooth.Wait(); + await this.StopListeningForPairedBluetoothAsync(); this.PairedBluetoothDeviceListReceived -= pairedBluetoothReceivedHandler; return bluetooth; @@ -134,29 +129,28 @@ public PairedBluetoothDevicesInfo GetPairedBluetoothDevicesInfo() /// /// Gets the results for pairing a bluetooth device. /// + /// Device Id. /// Results of pairing a bluetooth device - public PairBluetoothDevicesInfo GetPairBluetoothDevicesInfo(string deviceId) + public async Task GetPairBluetoothDevicesInfoAsync(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; - Task startListeningForPairBluetooth = this.StartListeningForPairBluetoothAsync(PairBluetoothDevicesApi, string.Format("deviceId={0}", Utilities.Hex64Encode(deviceId))); - startListeningForPairBluetooth.Wait(); + await this.StartListeningForPairBluetoothAsync(PairBluetoothDevicesApi, string.Format("deviceId={0}", Utilities.Hex64Encode(deviceId))); - PairBluetoothReceived.WaitOne(); + pairBluetoothReceived.WaitOne(); - Task stopListeningForPairBluetooth = this.StopListeningForPairBluetoothAsync(); - stopListeningForPairBluetooth.Wait(); + await this.StopListeningForPairBluetoothAsync(); this.PairBluetoothDeviceListReceived -= pairBluetoothReceivedHandler; return bluetooth; @@ -165,28 +159,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 +191,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 +229,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 +273,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 +339,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 +348,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 +379,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 +392,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.Shared/Xbox/XboxAppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Xbox/XboxAppDeployment.cs index 923f4381..074e9b51 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Xbox/XboxAppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Xbox/XboxAppDeployment.cs @@ -40,15 +40,15 @@ public async Task RegisterApplicationAsync(string folderName) await this.PostAsync( RegisterPackageApi, - string.Format("folder={0}", Utilities.Hex64Encode(folderName))); + string.Format("folder={0}", Utilities.Hex64Encode(folderName))).ConfigureAwait(false); // Poll the status until complete. ApplicationInstallStatus status = ApplicationInstallStatus.InProgress; do { - await Task.Delay(TimeSpan.FromMilliseconds(500)); + await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); - status = await this.GetInstallStatusAsync(); + status = await this.GetInstallStatusAsync().ConfigureAwait(false); } while (status == ApplicationInstallStatus.InProgress); } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/CertificateHandling.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/CertificateHandling.cs index 35a6261f..5c140920 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/CertificateHandling.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/CertificateHandling.cs @@ -43,24 +43,11 @@ public async Task GetRootDeviceCertificateAsync(bool acceptUntruste using (HttpClient client = new HttpClient(requestSettings)) { this.ApplyHttpHeaders(client, HttpMethods.Get); - - IAsyncOperationWithProgress responseOperation = client.GetAsync(uri); - TaskAwaiter responseAwaiter = responseOperation.GetAwaiter(); - while (!responseAwaiter.IsCompleted) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.GetAsync(uri)) { using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - TaskAwaiter readBufferAwaiter = bufferOperation.GetAwaiter(); - while (!readBufferAwaiter.IsCompleted) - { - } - - certificate = new Certificate(bufferOperation.GetResults()); + certificate = new Certificate(await messageContent.ReadAsBufferAsync()); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs index 61328091..f8959235 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Core/AppDeployment.cs @@ -53,12 +53,7 @@ public async Task GetInstallStatusAsync() { this.ApplyHttpHeaders(client, HttpMethods.Get); - IAsyncOperationWithProgress responseOperation = client.GetAsync(uri); - while (responseOperation.Status != AsyncStatus.Completed) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.GetAsync(uri)) { if (response.IsSuccessStatusCode) { @@ -72,12 +67,7 @@ public async Task GetInstallStatusAsync() IBuffer dataBuffer = null; using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); if (dataBuffer != null) { @@ -114,7 +104,7 @@ public async Task GetInstallStatusAsync() } else { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs index ed8aa767..8ae60a44 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestDelete.cs @@ -45,30 +45,18 @@ private async Task DeleteAsync(Uri uri) using (HttpClient client = new HttpClient(httpFilter)) { this.ApplyHttpHeaders(client, HttpMethods.Delete); - - IAsyncOperationWithProgress responseOperation = client.DeleteAsync(uri); - TaskAwaiter responseAwaiter = responseOperation.GetAwaiter(); - while (!responseAwaiter.IsCompleted) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.DeleteAsync(uri)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) { using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs index 38593c45..10e3952b 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestGet.cs @@ -45,28 +45,16 @@ private async Task GetAsync(Uri uri) using (HttpClient client = new HttpClient(requestSettings)) { this.ApplyHttpHeaders(client, HttpMethods.Get); - - IAsyncOperationWithProgress responseOperation = client.GetAsync(uri); - TaskAwaiter responseAwaiter = responseOperation.GetAwaiter(); - while (!responseAwaiter.IsCompleted) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.GetAsync(uri)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs index 3a634322..b8eabb84 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPost.cs @@ -58,30 +58,18 @@ private async Task PostAsync( using (HttpClient client = new HttpClient(httpFilter)) { this.ApplyHttpHeaders(client, HttpMethods.Post); - - IAsyncOperationWithProgress responseOperation = client.PostAsync(uri, requestContent); - TaskAwaiter responseAwaiter = responseOperation.GetAwaiter(); - while (!responseAwaiter.IsCompleted) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.PostAsync(uri, requestContent)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) { using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs index 36548ec0..18661ab6 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/RestPut.cs @@ -50,29 +50,18 @@ private async Task PutAsync( this.ApplyHttpHeaders(client, HttpMethods.Put); // Send the request - IAsyncOperationWithProgress responseOperation = client.PutAsync(uri, null); - TaskAwaiter responseAwaiter = responseOperation.GetAwaiter(); - while (!responseAwaiter.IsCompleted) - { - } - - using (HttpResponseMessage response = responseOperation.GetResults()) + using (HttpResponseMessage response = await client.PutAsync(uri, null)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) { using (IHttpContent messageContent = response.Content) { - IAsyncOperationWithProgress bufferOperation = messageContent.ReadAsBufferAsync(); - while (bufferOperation.Status != AsyncStatus.Completed) - { - } - - dataBuffer = bufferOperation.GetResults(); + dataBuffer = await messageContent.ReadAsBufferAsync(); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/HttpRest/WebSocket.cs index 52395049..36e77b04 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; - } - } + }); } /// @@ -109,40 +113,56 @@ private async Task ConnectAsync( /// /// The that sent the message. /// The message from the web socket. - private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args) + private async 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(); + await inputStream.AsStreamForRead().CopyToAsync(stream); - // 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.UniversalWindows/Properties/AssemblyInfo.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Properties/AssemblyInfo.cs index 903fff1b..041695ec 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Properties/AssemblyInfo.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.UniversalWindows/Properties/AssemblyInfo.cs @@ -29,6 +29,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.2.0")] -[assembly: AssemblyFileVersion("0.9.2.0")] +[assembly: AssemblyVersion("0.9.3.0")] +[assembly: AssemblyFileVersion("0.9.3.0")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Core/AppDeployment.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Core/AppDeployment.cs index 8457c36e..c410b35a 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Core/AppDeployment.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Core/AppDeployment.cs @@ -39,12 +39,7 @@ public async Task GetInstallStatusAsync() using (HttpClient client = new HttpClient(handler)) { this.ApplyHttpHeaders(client, HttpMethods.Get); - - Task getTask = client.GetAsync(uri); - await getTask.ConfigureAwait(false); - getTask.Wait(); - - using (HttpResponseMessage response = getTask.Result) + using (HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { @@ -64,9 +59,7 @@ public async Task GetInstallStatusAsync() { dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we point the stream at the origin. dataStream.Position = 0; @@ -101,7 +94,7 @@ public async Task GetInstallStatusAsync() } else { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs index 29b87268..428c65c5 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestDelete.cs @@ -35,15 +35,11 @@ private async Task DeleteAsync(Uri uri) { this.ApplyHttpHeaders(client, HttpMethods.Delete); - Task deleteTask = client.DeleteAsync(uri); - await deleteTask.ConfigureAwait(false); - deleteTask.Wait(); - - using (HttpResponseMessage response = deleteTask.Result) + using (HttpResponseMessage response = await client.DeleteAsync(uri).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) @@ -52,9 +48,7 @@ private async Task DeleteAsync(Uri uri) { dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we return with the stream pointed at the origin. dataStream.Position = 0; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs index 77f1dafa..e6c0d170 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestGet.cs @@ -35,24 +35,18 @@ private async Task GetAsync( { this.ApplyHttpHeaders(client, HttpMethods.Get); - Task getTask = client.GetAsync(uri); - await getTask.ConfigureAwait(false); - getTask.Wait(); - - using (HttpResponseMessage response = getTask.Result) + using (HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } using (HttpContent content = response.Content) { dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we return with the stream pointed at the origin. dataStream.Position = 0; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs index 898288c6..13f5a015 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPost.cs @@ -48,15 +48,11 @@ private async Task PostAsync( { this.ApplyHttpHeaders(client, HttpMethods.Post); - Task postTask = client.PostAsync(uri, requestContent); - await postTask.ConfigureAwait(false); - postTask.Wait(); - - using (HttpResponseMessage response = postTask.Result) + using (HttpResponseMessage response = await client.PostAsync(uri, requestContent).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) @@ -65,9 +61,7 @@ private async Task PostAsync( { responseDataStream = new MemoryStream(); - Task copyTask = responseContent.CopyToAsync(responseDataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await responseContent.CopyToAsync(responseDataStream).ConfigureAwait(false); // Ensure we return with the stream pointed at the origin. responseDataStream.Position = 0; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs index 4e9372a8..7097c753 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/RestPut.cs @@ -39,15 +39,11 @@ private async Task PutAsync( this.ApplyHttpHeaders(client, HttpMethods.Put); // Send the request - Task putTask = client.PutAsync(uri, body); - await putTask.ConfigureAwait(false); - putTask.Wait(); - - using (HttpResponseMessage response = putTask.Result) + using (HttpResponseMessage response = await client.PutAsync(uri, body).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { - throw new DevicePortalException(response); + throw await DevicePortalException.CreateAsync(response); } if (response.Content != null) @@ -56,9 +52,7 @@ private async Task PutAsync( { dataStream = new MemoryStream(); - Task copyTask = content.CopyToAsync(dataStream); - await copyTask.ConfigureAwait(false); - copyTask.Wait(); + await content.CopyToAsync(dataStream).ConfigureAwait(false); // Ensure we return with the stream pointed at the origin. dataStream.Position = 0; diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/HttpRest/WebSocket.cs index 889bf7d1..14de4cd4 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 @@ -126,7 +150,7 @@ private async Task ListenForMessagesInternalAsync() do { - result = await this.websocket.ReceiveAsync(buffer, CancellationToken.None); + result = await this.websocket.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false); if (result.MessageType == WebSocketMessageType.Close) { @@ -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); } } } diff --git a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Properties/AssemblyInfo.cs b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Properties/AssemblyInfo.cs index 020c92ca..0eb2f712 100644 --- a/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Properties/AssemblyInfo.cs +++ b/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper/Properties/AssemblyInfo.cs @@ -37,5 +37,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.9.2.0")] -[assembly: AssemblyFileVersion("0.9.2.0")] +[assembly: AssemblyVersion("0.9.3.0")] +[assembly: AssemblyFileVersion("0.9.3.0")] \ No newline at end of file