From 396aa56bd5497d9b47977120bc21de533299a912 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Mon, 13 Nov 2017 10:26:30 +0100 Subject: [PATCH] Working on the wireless network component (IMPORTANT: The commited code is not yet working, I have to find a way to check whether the Wi-Fi is turned off via hard- or software!). --- .../SafeExamBrowser.Contracts.csproj | 3 + .../SystemComponents/IWirelessNetwork.cs | 35 ++++ .../SystemComponents/WirelessNetworkStatus.cs | 17 ++ .../UserInterface/IUserInterfaceFactory.cs | 5 + .../Taskbar/ISystemWirelessNetworkControl.cs | 38 ++++ .../Operations/TaskbarOperationTests.cs | 8 +- .../Behaviour/Operations/TaskbarOperation.cs | 21 ++ .../SafeExamBrowser.SystemComponents.csproj | 9 + .../WirelessNetwork.cs | 186 ++++++++++++++++++ .../WirelessNetworkDefinition.cs | 26 +++ .../packages.config | 4 + .../Controls/PowerSupplyControl.xaml | 2 +- .../Controls/PowerSupplyControl.xaml.cs | 2 + .../Controls/WirelessNetworkButton.xaml | 30 +++ .../Controls/WirelessNetworkButton.xaml.cs | 40 ++++ .../Controls/WirelessNetworkControl.xaml | 32 +++ .../Controls/WirelessNetworkControl.xaml.cs | 109 ++++++++++ ...feExamBrowser.UserInterface.Classic.csproj | 14 ++ .../UserInterfaceFactory.cs | 5 + .../UserInterfaceFactory.cs | 6 + SafeExamBrowser/CompositionRoot.cs | 4 +- 21 files changed, 593 insertions(+), 3 deletions(-) create mode 100644 SafeExamBrowser.Contracts/SystemComponents/IWirelessNetwork.cs create mode 100644 SafeExamBrowser.Contracts/SystemComponents/WirelessNetworkStatus.cs create mode 100644 SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs create mode 100644 SafeExamBrowser.SystemComponents/WirelessNetwork.cs create mode 100644 SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs create mode 100644 SafeExamBrowser.SystemComponents/packages.config create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml.cs create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml create mode 100644 SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml.cs diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 7b2d5ed4..99df9a33 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -90,6 +90,8 @@ + + @@ -98,6 +100,7 @@ + diff --git a/SafeExamBrowser.Contracts/SystemComponents/IWirelessNetwork.cs b/SafeExamBrowser.Contracts/SystemComponents/IWirelessNetwork.cs new file mode 100644 index 00000000..968858e1 --- /dev/null +++ b/SafeExamBrowser.Contracts/SystemComponents/IWirelessNetwork.cs @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; + +namespace SafeExamBrowser.Contracts.SystemComponents +{ + public interface IWirelessNetwork + { + /// + /// The unique identifier of the network. + /// + Guid Id { get; } + + /// + /// The network name. + /// + string Name { get; } + + /// + /// The signal strength of this network, as percentage. + /// + int SignalStrength { get; } + + /// + /// The connection status of this network. + /// + WirelessNetworkStatus Status { get; } + } +} diff --git a/SafeExamBrowser.Contracts/SystemComponents/WirelessNetworkStatus.cs b/SafeExamBrowser.Contracts/SystemComponents/WirelessNetworkStatus.cs new file mode 100644 index 00000000..c16b088b --- /dev/null +++ b/SafeExamBrowser.Contracts/SystemComponents/WirelessNetworkStatus.cs @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +namespace SafeExamBrowser.Contracts.SystemComponents +{ + public enum WirelessNetworkStatus + { + Undefined = 0, + Connected, + Disconnected + } +} diff --git a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs index 531405f2..6466f7a0 100644 --- a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs @@ -55,5 +55,10 @@ namespace SafeExamBrowser.Contracts.UserInterface /// Creates a new splash screen which runs on its own thread. /// ISplashScreen CreateSplashScreen(ISettings settings, IText text); + + /// + /// Creates a system control which allows to change the wireless network connection of the computer. + /// + ISystemWirelessNetworkControl CreateWirelessNetworkControl(); } } diff --git a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs new file mode 100644 index 00000000..e6e9ffce --- /dev/null +++ b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System.Collections.Generic; +using SafeExamBrowser.Contracts.SystemComponents; + +namespace SafeExamBrowser.Contracts.UserInterface.Taskbar +{ + public delegate void WirelessNetworkSelectedEventHandler(IWirelessNetwork network); + + public interface ISystemWirelessNetworkControl : ISystemControl + { + /// + /// Defines whether the computer has a wireless network adapter. + /// + bool HasWirelessNetworkAdapter { set; } + + /// + /// Sets the current wireless network status. + /// + WirelessNetworkStatus NetworkStatus { set; } + + /// + /// Event fired when the user selected a wireless network. + /// + event WirelessNetworkSelectedEventHandler NetworkSelected; + + /// + /// Updates the list of available networks. + /// + void Update(IEnumerable networks); + } +} diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/TaskbarOperationTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/TaskbarOperationTests.cs index cef3dd39..491c67f8 100644 --- a/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/TaskbarOperationTests.cs +++ b/SafeExamBrowser.Core.UnitTests/Behaviour/Operations/TaskbarOperationTests.cs @@ -27,6 +27,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations private Mock splashScreenMock; private Mock> keyboardLayoutMock; private Mock> powerSupplyMock; + private Mock> wirelessNetworkMock; private Mock systemInfoMock; private Mock taskbarMock; private Mock textMock; @@ -42,6 +43,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations splashScreenMock = new Mock(); keyboardLayoutMock = new Mock>(); powerSupplyMock = new Mock>(); + wirelessNetworkMock = new Mock>(); systemInfoMock = new Mock(); taskbarMock = new Mock(); textMock = new Mock(); @@ -49,6 +51,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations settingsMock.SetupGet(s => s.AllowApplicationLog).Returns(true); settingsMock.SetupGet(s => s.AllowKeyboardLayout).Returns(true); + settingsMock.SetupGet(s => s.AllowWirelessNetwork).Returns(true); systemInfoMock.SetupGet(s => s.HasBattery).Returns(true); uiFactoryMock.Setup(u => u.CreateNotification(It.IsAny())).Returns(new Mock().Object); @@ -57,6 +60,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations settingsMock.Object, keyboardLayoutMock.Object, powerSupplyMock.Object, + wirelessNetworkMock.Object, systemInfoMock.Object, taskbarMock.Object, textMock.Object, @@ -73,7 +77,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations keyboardLayoutMock.Verify(k => k.Initialize(It.IsAny()), Times.Once); powerSupplyMock.Verify(p => p.Initialize(It.IsAny()), Times.Once); - taskbarMock.Verify(t => t.AddSystemControl(It.IsAny()), Times.Exactly(2)); + wirelessNetworkMock.Verify(w => w.Initialize(It.IsAny()), Times.Once); + taskbarMock.Verify(t => t.AddSystemControl(It.IsAny()), Times.Exactly(3)); taskbarMock.Verify(t => t.AddNotification(It.IsAny()), Times.Once); } @@ -84,6 +89,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations keyboardLayoutMock.Verify(k => k.Terminate(), Times.Once); powerSupplyMock.Verify(p => p.Terminate(), Times.Once); + wirelessNetworkMock.Verify(w => w.Terminate(), Times.Once); } } } diff --git a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs index 2753f309..85743639 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs @@ -25,6 +25,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations private ITaskbarSettings settings; private ISystemComponent keyboardLayout; private ISystemComponent powerSupply; + private ISystemComponent wirelessNetwork; private ISystemInfo systemInfo; private ITaskbar taskbar; private IUserInterfaceFactory uiFactory; @@ -37,6 +38,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations ITaskbarSettings settings, ISystemComponent keyboardLayout, ISystemComponent powerSupply, + ISystemComponent wirelessNetwork, ISystemInfo systemInfo, ITaskbar taskbar, IText text, @@ -50,6 +52,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations this.taskbar = taskbar; this.text = text; this.uiFactory = uiFactory; + this.wirelessNetwork = wirelessNetwork; } public void Perform() @@ -71,6 +74,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations { AddPowerSupplyControl(); } + + if (settings.AllowWirelessNetwork) + { + AddWirelessNetworkControl(); + } } public void Revert() @@ -92,6 +100,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations { powerSupply.Terminate(); } + + if (settings.AllowWirelessNetwork) + { + wirelessNetwork.Terminate(); + } } private void AddKeyboardLayoutControl() @@ -110,6 +123,14 @@ namespace SafeExamBrowser.Core.Behaviour.Operations taskbar.AddSystemControl(control); } + private void AddWirelessNetworkControl() + { + var control = uiFactory.CreateWirelessNetworkControl(); + + wirelessNetwork.Initialize(control); + taskbar.AddSystemControl(control); + } + private void CreateLogNotification() { var logInfo = new LogNotificationInfo(text); diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index 549b1e6b..5ea322a3 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -48,8 +48,12 @@ MinimumRecommendedRules.ruleset + + ..\packages\SimpleWifi.1.0.0.0\lib\net40\SimpleWifi.dll + + @@ -57,6 +61,8 @@ + + @@ -64,5 +70,8 @@ SafeExamBrowser.Contracts + + + \ No newline at end of file diff --git a/SafeExamBrowser.SystemComponents/WirelessNetwork.cs b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs new file mode 100644 index 00000000..62544b2a --- /dev/null +++ b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Net.NetworkInformation; +using System.Timers; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.SystemComponents; +using SafeExamBrowser.Contracts.UserInterface.Taskbar; +using SimpleWifi; +using SimpleWifi.Win32; + +namespace SafeExamBrowser.SystemComponents +{ + public class WirelessNetwork : ISystemComponent + { + private const int TWO_SECONDS = 2000; + + private ILogger logger; + private ISystemWirelessNetworkControl control; + private Timer timer; + private Wifi wifi; + + public WirelessNetwork(ILogger logger) + { + this.logger = logger; + } + + public void Initialize(ISystemWirelessNetworkControl control) + { + this.control = control; + this.wifi = new Wifi(); + + if (wifi.NoWifiAvailable || IsTurnedOff()) + { + control.HasWirelessNetworkAdapter = false; + logger.Info("Wireless networks cannot be monitored, as there is no hardware adapter available or it is turned off."); + } + else + { + control.HasWirelessNetworkAdapter = true; + control.NetworkSelected += Control_NetworkSelected; + wifi.ConnectionStatusChanged += Wifi_ConnectionStatusChanged; + + UpdateControl(); + + timer = new Timer(TWO_SECONDS); + timer.Elapsed += Timer_Elapsed; + timer.AutoReset = true; + timer.Start(); + + logger.Info("Started monitoring the wireless network adapter."); + } + } + + public void Terminate() + { + timer?.Stop(); + control?.Close(); + + if (timer != null) + { + logger.Info("Stopped monitoring the wireless network adapter."); + } + } + + private void Control_NetworkSelected(IWirelessNetwork network) + { + throw new NotImplementedException(); + } + + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + UpdateControl(); + } + + private void Wifi_ConnectionStatusChanged(object sender, WifiStatusEventArgs e) + { + control.NetworkStatus = ToStatus(e.NewStatus); + } + + private bool IsTurnedOff() + { + try + { + // See https://msdn.microsoft.com/en-us/library/aa394216(v=vs.85).aspx + string query = @" + SELECT * + FROM Win32_NetworkAdapter"; + var searcher = new ManagementObjectSearcher(query); + var adapters = searcher.Get(); + var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(i => i.NetworkInterfaceType == NetworkInterfaceType.Wireless80211).ToList(); + + logger.Info("Interface count: " + interfaces.Count); + + foreach (var i in interfaces) + { + logger.Info(i.Description); + logger.Info(i.Id); + logger.Info(i.Name); + logger.Info(i.NetworkInterfaceType.ToString()); + logger.Info(i.OperationalStatus.ToString()); + logger.Info("-----"); + } + + foreach (var adapter in adapters) + { + logger.Info("-------"); + + foreach (var property in adapter.Properties) + { + logger.Info($"{property.Name}: {property.Value} ({property.Type})"); + } + } + + logger.Info("Adapter count: " + adapters.Count); + + return true; + + using (var client = new WlanClient()) + { + foreach (var @interface in client.Interfaces) + { + Trace.WriteLine($"[{@interface.InterfaceName}]"); + + foreach (var state in @interface.RadioState.PhyRadioState) + { + Trace.WriteLine($"PhyIndex: {state.dwPhyIndex}"); + Trace.WriteLine($"SoftwareRadioState: {state.dot11SoftwareRadioState}"); + Trace.WriteLine($"HardwareRadioState: {state.dot11HardwareRadioState}"); + } + } + } + } + catch (Exception e) + { + logger.Error("Fail!", e); + + return true; + } + } + + private void UpdateControl() + { + var networks = new List(); + + control.NetworkStatus = ToStatus(wifi.ConnectionStatus); + + try + { + foreach (var accessPoint in wifi.GetAccessPoints()) + { + // The user may only connect to an already configured wireless network! + if (accessPoint.HasProfile) + { + networks.Add(new WirelessNetworkDefinition + { + Name = accessPoint.Name, + SignalStrength = Convert.ToInt32(accessPoint.SignalStrength), + Status = accessPoint.IsConnected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected + }); + } + } + } + catch (Exception e) + { + logger.Error("Failed to update the wireless network adapter status!", e); + } + + control.Update(networks); + } + + private WirelessNetworkStatus ToStatus(WifiStatus status) + { + return status == WifiStatus.Connected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected; + } + } +} diff --git a/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs b/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs new file mode 100644 index 00000000..93fcc8f4 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using SafeExamBrowser.Contracts.SystemComponents; + +namespace SafeExamBrowser.SystemComponents +{ + internal class WirelessNetworkDefinition : IWirelessNetwork + { + public Guid Id { get; } + public string Name { get; set; } + public int SignalStrength { get; set; } + public WirelessNetworkStatus Status { get; set; } + + public WirelessNetworkDefinition() + { + Id = Guid.NewGuid(); + } + } +} diff --git a/SafeExamBrowser.SystemComponents/packages.config b/SafeExamBrowser.SystemComponents/packages.config new file mode 100644 index 00000000..f8a15217 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/PowerSupplyControl.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/PowerSupplyControl.xaml index 727c1b26..8ed32770 100644 --- a/SafeExamBrowser.UserInterface.Classic/Controls/PowerSupplyControl.xaml +++ b/SafeExamBrowser.UserInterface.Classic/Controls/PowerSupplyControl.xaml @@ -33,7 +33,7 @@ - + + diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml.cs b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml.cs new file mode 100644 index 00000000..fb1c6f36 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System.Windows; +using System.Windows.Controls; + +namespace SafeExamBrowser.UserInterface.Classic.Controls +{ + public partial class WirelessNetworkButton : UserControl + { + public event RoutedEventHandler Click; + + public bool IsCurrent + { + set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; } + } + + public string NetworkName + { + set { NetworkNameTextBlock.Text = value; } + } + + public int SignalStrength + { + set { SignalStrengthTextBlock.Text = $"{value}%"; } + } + + public WirelessNetworkButton() + { + InitializeComponent(); + + Button.Click += (o, args) => Click?.Invoke(o, args); + } + } +} diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml new file mode 100644 index 00000000..2f9be690 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + 5 + + + + + +