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 @@
-