From 0742fcd82093eeb5f4f062acc8b2a829e1f53b09 Mon Sep 17 00:00:00 2001 From: LIBERADO Date: Tue, 29 Dec 2020 22:28:49 +0100 Subject: [PATCH] #12 - Process device connection permission result asynchronously use an android broadcast receiver & events to handle permission result. --- MobileApplication/IHM.sln | 2 +- .../IHM.Android/Interfaces/DroidPandaVcom.cs | 84 +++++++-- .../IHM.Android/Interfaces/DroidUsbManager.cs | 22 ++- .../IHM/IHM.Android/MainActivity.cs | 4 +- MobileApplication/IHM/IHM/MainPage.xaml.cs | 175 +++++++++++------- MobileApplication/IHM/IHM/interfaces.cs | 18 +- 6 files changed, 209 insertions(+), 96 deletions(-) diff --git a/MobileApplication/IHM.sln b/MobileApplication/IHM.sln index f1a6828..fa61a79 100644 --- a/MobileApplication/IHM.sln +++ b/MobileApplication/IHM.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.28307.1082 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IHM.Android", "IHM\IHM.Android\IHM.Android.csproj", "{BE5A8A0A-5A57-486F-82C2-E51124B94857}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IHM", "IHM\IHM\IHM.csproj", "{BD2E3759-DFF1-4368-B8ED-6F78F9692D16}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IHM", "IHM\IHM\IHM.csproj", "{BD2E3759-DFF1-4368-B8ED-6F78F9692D16}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/MobileApplication/IHM/IHM.Android/Interfaces/DroidPandaVcom.cs b/MobileApplication/IHM/IHM.Android/Interfaces/DroidPandaVcom.cs index f84011f..011dea8 100644 --- a/MobileApplication/IHM/IHM.Android/Interfaces/DroidPandaVcom.cs +++ b/MobileApplication/IHM/IHM.Android/Interfaces/DroidPandaVcom.cs @@ -26,6 +26,15 @@ public class USBBroadCastReceiver : BroadcastReceiver /// public static String ACTION_USB_PERMISSION = "com.cio.USB_PERMISSION"; + /// + /// Use an event to notify that the permission completed + /// + public event EventHandler NotifyUsbPermissionCompleted; + + /// + /// We complete the opening device process in the brodcast receive + /// FIXME - don't do that, just send event and let the task that triggered the permission request complete its process + /// private DroidPandaVcom _droidPandaVcom; public USBBroadCastReceiver(DroidPandaVcom droidPandaVcom) @@ -44,20 +53,14 @@ public override void OnReceive(Context context, Intent intent) if (ACTION_USB_PERMISSION.Equals(action)) { UsbDevice device = (UsbDevice)intent.GetParcelableExtra(UsbManager.ExtraDevice); - - if (intent.GetBooleanExtra(UsbManager.ExtraPermissionGranted, false)) - { - if (device != null) - { - _droidPandaVcom.OpenDevice(); - } - } - else + bool bPermissionGranted = false; + if (device != null) { - Log.Debug(DroidPandaVcom.LOG_TAG, "permission denied for device " + device); + bPermissionGranted = intent.GetBooleanExtra(UsbManager.ExtraPermissionGranted, false); } + EventHandler handler = NotifyUsbPermissionCompleted; + handler(this, bPermissionGranted); } - } } @@ -92,14 +95,20 @@ public DroidDevHandle() // everything else is set to null } } + + /// + /// The Event declared in the interface + /// + public event EventHandler NotifyPermRequestCompleted; //////////////////////////// // CONFIG SECTION private const bool bConfUseUsbManagerOnly = true; // Select method to install interface (usbserial package, or android API only) public const int BULK_XFER_TOUT_MS = 1000; - // END public const int iVendorId = 0x0483; // Id for the STM32Nucleo public const int iProductID = 0x5740; // Id for the STM32Nucleo + // END + // usb-to-serial stuff private IList _availableDrivers; // Manage all available driver from package @@ -118,11 +127,26 @@ public void Init(Object context) { _context = (ContextWrapper)context; _devHandle.usbManager = (UsbManager)_context.GetSystemService(Context.UsbService); - _usbPermissionIntent = PendingIntent.GetBroadcast(_context, 0, new Intent(USBBroadCastReceiver.ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(USBBroadCastReceiver.ACTION_USB_PERMISSION); - _context.RegisterReceiver(_USBBroadCastReceiver, filter); _USBBroadCastReceiver = new USBBroadCastReceiver(this); + _USBBroadCastReceiver.NotifyUsbPermissionCompleted += _USBBroadCastReceiver_NotifyUsbPermissionCompleted; + _context.RegisterReceiver(_USBBroadCastReceiver, filter); + } + + private void _USBBroadCastReceiver_NotifyUsbPermissionCompleted(object sender, bool bPermissionGranted) + { + if (bPermissionGranted) + { + OpenDevice(); + // https://docs.microsoft.com/fr-fr/dotnet/api/system.eventhandler-1?view=net-5.0 + EventHandler handler = NotifyPermRequestCompleted; + handler(this, true); + } + else + { + Log.Debug(LOG_TAG, "permission denied for device " + _devHandle.usbdev.DeviceName); + } } // Retreive the physically connected devices. @@ -243,20 +267,36 @@ protected int OpenInterface(UsbDevice usbdev, UsbDeviceConnection connection, re return ret; } + public bool CheckPermStatusAsync(string szDevName) + { + var usbDevice = _devHandle.usbManager.DeviceList[szDevName]; + // Ask for permission to access the created device + return _devHandle.usbManager.HasPermission(usbDevice); + } + + public void RequestPermAsync(string szDevName) + { + var usbDevice = _devHandle.usbManager.DeviceList[szDevName]; + _devHandle.usbManager.RequestPermission(usbDevice, _usbPermissionIntent); + } + public void selectDevice(string name) { if (bConfUseUsbManagerOnly) { _devHandle.usbdev = _devHandle.usbManager.DeviceList[name]; - // Ask for permission to access the created device - if (!_devHandle.usbManager.HasPermission(_devHandle.usbdev)) - { - _devHandle.usbManager.RequestPermission(_devHandle.usbdev, _usbPermissionIntent); + // Check and Ask for permission to access the created device + if (_devHandle.usbManager.HasPermission(_devHandle.usbdev)) + { OpenDevice(); + // https://docs.microsoft.com/fr-fr/dotnet/api/system.eventhandler-1?view=net-5.0 + EventHandler handler = NotifyPermRequestCompleted; + handler(this, true); } else { + _devHandle.usbManager.RequestPermission(_devHandle.usbdev, _usbPermissionIntent); // The wapp don't wait for user input and crash when asking permission 1st time // We must Wait for user input for opening connection. } @@ -295,7 +335,7 @@ public void selectDevice(string name) public void OpenDevice() { // At this stage Persmission must Have been granted - if (!_devHandle.usbManager.HasPermission(_devHandle.usbdev)) + if (_devHandle.usbManager.HasPermission(_devHandle.usbdev)) { // Now open the port, with the USB Manager : we get the fd/enpoints and pass it to library, no more _devHandle.connection = _devHandle.usbManager.OpenDevice(_devHandle.usbdev); @@ -311,7 +351,11 @@ public void OpenDevice() Log.Debug(DroidPandaVcom.LOG_TAG, "FAILED : open device endpoint" + _devHandle.usbdev.DeviceName); } } - } + } + else + { + throw new NotSupportedException("We shall have permission to device"); + } } /// diff --git a/MobileApplication/IHM/IHM.Android/Interfaces/DroidUsbManager.cs b/MobileApplication/IHM/IHM.Android/Interfaces/DroidUsbManager.cs index 059212a..621b179 100644 --- a/MobileApplication/IHM/IHM.Android/Interfaces/DroidUsbManager.cs +++ b/MobileApplication/IHM/IHM.Android/Interfaces/DroidUsbManager.cs @@ -28,6 +28,7 @@ public class DroidUsbManager : IUsbManager { private UsbManager usbManager_; private string selectedDevice; + public event EventHandler NotifyPermRequestCompleted; public DroidUsbManager() {} @@ -77,24 +78,35 @@ public int Close() public int ReadRegisterFromDevice(byte uiRegister, ref byte value) { // not implemented - return -1; + throw new NotImplementedException(); } public int WriteRegisterToDevice(byte uiRegister, byte value) { // not implemented - return -1; + throw new NotImplementedException(); } public int WriteToDevice(byte[] data) { // not implemented - return -1; + throw new NotImplementedException(); } public int ReadFromDevice(byte[] data, int len) { // not implemented - return -1; + throw new NotImplementedException(); } -} + + public bool CheckPermStatusAsync(string szDevName) + { + throw new NotImplementedException(); + } + + public void RequestPermAsync(string szDevName) + { + throw new NotImplementedException(); + } + + } } \ No newline at end of file diff --git a/MobileApplication/IHM/IHM.Android/MainActivity.cs b/MobileApplication/IHM/IHM.Android/MainActivity.cs index 48e4925..d6ec408 100644 --- a/MobileApplication/IHM/IHM.Android/MainActivity.cs +++ b/MobileApplication/IHM/IHM.Android/MainActivity.cs @@ -18,9 +18,8 @@ public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivit { protected override void OnCreate(Bundle savedInstanceState) { - //Pour l'utilisation du service de l'USBManager + // Setup the USB Android Interface we want to use. Xamarin.Forms.DependencyService.Register(); - TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; @@ -39,7 +38,6 @@ protected override void OnCreate(Bundle savedInstanceState) Xamarin.Forms.Forms.Init(this, savedInstanceState); Xamarin.Forms.DependencyService.Get().Init(this); LoadApplication(new App()); - } } } diff --git a/MobileApplication/IHM/IHM/MainPage.xaml.cs b/MobileApplication/IHM/IHM/MainPage.xaml.cs index 492f751..9864b1d 100644 --- a/MobileApplication/IHM/IHM/MainPage.xaml.cs +++ b/MobileApplication/IHM/IHM/MainPage.xaml.cs @@ -38,7 +38,7 @@ public enum DllDeviceType DllDeviceType _eConfDllDevice = DllDeviceType.devtype_libusb; // Select dll device bool _bConfUseAndroidforIOaccess = false; // select dll for R/W Operation OR the android usb hardware API ushort _usConfProxyPort = 5000; - + string _szDevName; /* USB device Name */ //////////////////////////// /// Handle du dll_if dll_if _dll_if; @@ -84,13 +84,14 @@ public MainPage() }; _iusbManager = Xamarin.Forms.DependencyService.Get(); + _iusbManager.NotifyPermRequestCompleted += OnPermReqCompleted; // Pour le log _Logfilename = "FileLog.log"; string path = _logfile.GetLocalStoragePath(); _Logfilename = Path.Combine(path, _Logfilename); // fill log view with the log file contact - fillLog(); + LogViewInit(); listLog.ItemsSource = _lisLogs; switchUseApiAndroidXfer.IsToggled = _bConfUseAndroidforIOaccess; @@ -263,7 +264,13 @@ void OnButtonValidateClicked(object sender, EventArgs e) // Valider la selection { popupView.IsVisible = false; connect((string)usbList.SelectedItem); + } + /// + /// Refresh the GUI state according the device connection state + /// + void OnDeviceconnected() + { // Gestion de l'affichage if (_IsConnected) { @@ -283,82 +290,111 @@ void OnButtonValidateClicked(object sender, EventArgs e) // Valider la selection _logfile.Error(msgFailCo, ""); // Pour le stockage dans le fichier _lisLogs.Insert(0, DateTime.Now.ToString(" HH:mm ") + " " + msgFailCo); //Pour l'affichage en temps réelle dans la dialogue } - + // FIXME - application crashes if the dll device was not created + // In the dll : check stuff was created and return an error otherwise PopDllLogs(); } /// - /// Performs de the connecion : open the protocol and set parameters if needed + /// Performs de the connection first step : ask for permission /// /// void connect(string szUsbDevName) - { + { + _szDevName = szUsbDevName; /* We call the underlying USB connection expect for emulsave*/ - if (szUsbDevName == _ilist_dllDev[(int)DllDeviceType.devtype_emulslave]) + if (_szDevName == _ilist_dllDev[(int)DllDeviceType.devtype_emulslave]) { - // So if we did not selected with the USB type selector we force it now + // So if we did not selected with the emulsave type selector we force it now + // FIXME - maybe we need to update the picker to the right value _eConfDllDevice = DllDeviceType.devtype_emulslave; _IsConnected = (_dll_if.Open(_dll_if.CreateEmulslave(), "") ==0); } else { + // We assynchronously run the permission // everything else is a true USB device _iusbManager.selectDevice(szUsbDevName); } - SWIGTYPE_p_proto_Device_t dev; - int ret = 0; - switch (_eConfDllDevice) + } + + private void OnPermReqCompleted(object sender, bool bPermissionGranted) + { + // Permission has been granted so ask for connection + if (bPermissionGranted == true) { - case DllDeviceType.devtype_emulslave: - dev = _dll_if.CreateEmulslave(); - _IsConnected = (_dll_if.Open(dev, "") == 0); - break; - /* May be we shall just passe the device type we wish to the dll so that it creates the device it self */ - case DllDeviceType.devtype_serial: - // On demande a la dll de s'initialiser sans essayer d'ouvrir un port, car on va s'en occuper - dev = _dll_if.CreateDevSerial(); - _IsConnected = (0 == _dll_if.Open(dev, "")); - if (_IsConnected) - { - // Récupére notre FD avec l'USBManager pour l'affecter à la lib - ret = _dll_if.SerialSetFd(dev, _iusbManager.getDeviceConnection()); - }; - break; - case DllDeviceType.devtype_usbdev: - dev = _dll_if.CreateDevUsbDev(); - _IsConnected = (0 == _dll_if.Open(dev, "")); - if (_IsConnected) - { - ret = _dll_if.UsbDevSetFd(dev, _iusbManager.getDeviceConnection()); - } - break; - case DllDeviceType.devtype_libusb: - dev = _dll_if.CreateDevLibUsb(); - ret = _dll_if.LibUsbSetFd(dev, _iusbManager.getDeviceConnection()); - if (ret==0) - { - _IsConnected = (0 == _dll_if.Open(dev, szUsbDevName)); - } - break; - case DllDeviceType.devtype_proxy: - dev = _dll_if.CreateDevProxy(); - _iusbProxy = new UsbProxy(); - _iusbProxy.SetIUsbManager(ref _iusbManager); - if (_iusbProxy.Start(_usConfProxyPort)) - { - string szProxyUrl = _iusbProxy.GetListenIpAddr() + protocomm.PROXY_URL_SEP + _usConfProxyPort.ToString(); - _IsConnected = (0 == _dll_if.Open(dev, szProxyUrl)); - if (!_IsConnected) + SWIGTYPE_p_proto_Device_t dev; + int ret = 0; + switch (_eConfDllDevice) + { + case DllDeviceType.devtype_emulslave: + dev = _dll_if.CreateEmulslave(); + _IsConnected = (_dll_if.Open(dev, "") == 0); + break; + /* May be we shall just passe the device type we wish to the dll so that it creates the device it self */ + case DllDeviceType.devtype_serial: + // On demande a la dll de s'initialiser sans essayer d'ouvrir un port, car on va s'en occuper + dev = _dll_if.CreateDevSerial(); + _IsConnected = (0 == _dll_if.Open(dev, "")); + if (_IsConnected) + { + // Récupére notre FD avec l'USBManager pour l'affecter à la lib + ret = _dll_if.SerialSetFd(dev, _iusbManager.getDeviceConnection()); + }; + break; + case DllDeviceType.devtype_usbdev: + dev = _dll_if.CreateDevUsbDev(); + _IsConnected = (0 == _dll_if.Open(dev, "")); + if (_IsConnected) + { + ret = _dll_if.UsbDevSetFd(dev, _iusbManager.getDeviceConnection()); + } + break; + case DllDeviceType.devtype_libusb: + dev = _dll_if.CreateDevLibUsb(); + ret = _dll_if.LibUsbSetFd(dev, _iusbManager.getDeviceConnection()); + if (ret == 0) { - _iusbProxy.Stop(); + _IsConnected = (0 == _dll_if.Open(dev, _szDevName)); } - } - break; - default: - break; + break; + case DllDeviceType.devtype_proxy: + dev = _dll_if.CreateDevProxy(); + _iusbProxy = new UsbProxy(); + _iusbProxy.SetIUsbManager(ref _iusbManager); + if (_iusbProxy.Start(_usConfProxyPort)) + { + string szProxyUrl = _iusbProxy.GetListenIpAddr() + protocomm.PROXY_URL_SEP + _usConfProxyPort.ToString(); + _IsConnected = (0 == _dll_if.Open(dev, szProxyUrl)); + if (!_IsConnected) + { + _iusbProxy.Stop(); + } + } + break; + default: + break; + } } + else + { + // OUPS ! We did not have the permission + // Cancel connection + string szLog = "Permission to access USB device was not granted , cancelling"; + _logfile.Info(szLog, ""); // Pour le stockage dans le fichier + _lisLogs.Insert(0, DateTime.Now.ToString(" HH:mm ") + " " + szLog); //Pour l'affichage en temps réelle dans la dialogue + } + + // Update GUI state + OnDeviceconnected(); } + /// + /// This Handler will process hall GUI switch toggle Event. + /// You must always use this handler when creating a switch, and check the sender object + /// + /// + /// void OnToggled(object sender, ToggledEventArgs e) { // Perform an action after examining e.Value @@ -369,6 +405,19 @@ void OnToggled(object sender, ToggledEventArgs e) } } + /// + /// This Handler will process hall GUI Picker Index Event. + /// You must always use this handler when creating a Picker, and check the sender object + /// + /// + /// + private void Picker_SelectedIndexChanged(object sender, EventArgs e) + { + if (sender == PickerDllDevice) + { + _eConfDllDevice = (DllDeviceType)PickerDllDevice.SelectedIndex; + } + } // ******************* Pour le log *************************** private void onClickedLog(object sender, EventArgs e) { @@ -377,18 +426,19 @@ private void onClickedLog(object sender, EventArgs e) shareService.Share(_Logfilename, "Log"); } - private void fillLog() + /// + /// Update Log view from the log file content + /// + private void LogViewInit() { FileStream fileLog; _lisLogs.Clear(); - - //Création du fichier + // Create file if it does not exist if (!File.Exists(_Logfilename)) { fileLog = File.Create(_Logfilename); fileLog.Close(); } - foreach (string line in File.ReadAllLines(_Logfilename)) { _lisLogs.Insert(0, line); @@ -403,13 +453,6 @@ private void onClickedresetLog(object sender, EventArgs e) File.WriteAllText(_Logfilename, ""); } - private void Picker_SelectedIndexChanged(object sender, EventArgs e) - { - if (sender == PickerDllDevice) - { - _eConfDllDevice = (DllDeviceType) PickerDllDevice.SelectedIndex; - } - } // ************************************************************* } } diff --git a/MobileApplication/IHM/IHM/interfaces.cs b/MobileApplication/IHM/IHM/interfaces.cs index a2c7c45..6c8de58 100644 --- a/MobileApplication/IHM/IHM/interfaces.cs +++ b/MobileApplication/IHM/IHM/interfaces.cs @@ -31,6 +31,10 @@ public DevHandle(int fd, int ep_in, int ep_out, int max_pkt_size) /// public interface IUsbManager { + /// + /// This event will be sent when the requested permission has been completed : granted or not + /// + event EventHandler NotifyPermRequestCompleted; /// /// Some custom Initialization @@ -50,11 +54,23 @@ public interface IUsbManager /// Task> getListOfConnectionsAsync(); + /// + /// Request USB permission for device + /// + /// true is permission granted otherwise false + bool CheckPermStatusAsync(string szDevName); + + /// + /// Request USB permission for device + /// + /// + void RequestPermAsync(string szDevName); + /// /// Select device from the the connected list. After this call the device is ready for communication /// /// - void selectDevice(string name); + void selectDevice(string szDevName); /// /// Returns a system File descriptor for the opened connection