Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
[5491] Do not enable background discovery for sysfs-based USB-Serial …
Browse files Browse the repository at this point in the history
…discovery if scanned directories are not accessible (#5521)

* [5491] Do not enable background discovery for sysfs-based USB-Serial
discovery if scanned directories are not accessible

On non-Linux systems, the directories used by the sysfs-based USB-Serial
discovery are not available. This commit disables the polling of the
directory scanning for systems where the directories are not available
or not readable.

Bug: #5491
Signed-off-by: Henning Sudbrock <henning.sudbrock@telekom.de>
  • Loading branch information
hsudbrock authored and kaikreuzer committed May 3, 2018
1 parent 191f824 commit 644ecb2
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 14 deletions.
Expand Up @@ -70,6 +70,7 @@ public void testSingleScanReportsResultsCorrectAfterOneScan() throws IOException
UsbSerialDeviceInformation usb3 = usbDeviceInfoGenerator.generate();

when(usbSerialScanner.scan()).thenReturn(new HashSet<>(asList(usb1, usb2)));
when(usbSerialScanner.canPerformScans()).thenReturn(true);

pollingScanner.doSingleScan();

Expand All @@ -91,6 +92,7 @@ public void testSingleScanReportsResultsCorrectlyAfterTwoScans() throws IOExcept

when(usbSerialScanner.scan()).thenReturn(new HashSet<>(asList(usb1, usb2)))
.thenReturn(new HashSet<>(asList(usb2, usb3)));
when(usbSerialScanner.canPerformScans()).thenReturn(true);

pollingScanner.unregisterDiscoveryListener(discoveryListener);
pollingScanner.doSingleScan();
Expand Down Expand Up @@ -118,6 +120,7 @@ public void testBackgroundScanning() throws IOException, InterruptedException {

when(usbSerialScanner.scan()).thenReturn(new HashSet<>(asList(usb1, usb2)))
.thenReturn(new HashSet<>(asList(usb2, usb3)));
when(usbSerialScanner.canPerformScans()).thenReturn(true);

pollingScanner.startBackgroundScanning();

Expand All @@ -137,4 +140,20 @@ public void testBackgroundScanning() throws IOException, InterruptedException {
verify(discoveryListener, never()).usbSerialDeviceRemoved(usb3);
}

@Test
public void testNoBackgroundScanningWhenNoScansPossible() throws IOException, InterruptedException {
when(usbSerialScanner.scan()).thenReturn(new HashSet<>(asList(usbDeviceInfoGenerator.generate())));
when(usbSerialScanner.canPerformScans()).thenReturn(false);

pollingScanner.startBackgroundScanning();

Thread.sleep(1500);

pollingScanner.stopBackgroundScanning();

// Expectation: discovery listener never called, as usbSerialScanner indicates that no scans possible

verify(discoveryListener, never()).usbSerialDeviceDiscovered(any(UsbSerialDeviceInformation.class));
}

}
Expand Up @@ -153,6 +153,32 @@ public void testNonUsbDeviceIsSkipped() throws IOException {
assertThat(scanner.scan(), is(empty()));
}

@Test
public void testCanPerformScans() {
// with given test setup, scans can be performed
assertThat(scanner.canPerformScans(), is(true));
}

@Test
public void testCannotPerformScansWithNonexistingSysDir() {
Map<String, Object> config = new HashMap<>();
config.put(SYSFS_TTY_DEVICES_DIRECTORY_ATTRIBUTE, rootPath.resolve("someNonexistingDir"));
config.put(DEV_DIRECTORY_ATTRIBUTE, rootPath.resolve(DEV_DIR));
scanner.modified(config);

assertThat(scanner.canPerformScans(), is(false));
}

@Test
public void testCannotPerformScansWithNonexistingDevDir() {
Map<String, Object> config = new HashMap<>();
config.put(SYSFS_TTY_DEVICES_DIRECTORY_ATTRIBUTE, rootPath.resolve(SYSFS_TTY_DEVICES_DIR));
config.put(DEV_DIRECTORY_ATTRIBUTE, rootPath.resolve("someNonexistingDir"));
scanner.modified(config);

assertThat(scanner.canPerformScans(), is(false));
}

private void createDevice(String serialPortName, int vendorId, int productId, String manufacturer, String product,
String serialNumber, int interfaceNumber, String interfaceDescription,
DeviceCreationOption... deviceCreationOptions) throws IOException {
Expand Down
Expand Up @@ -58,6 +58,14 @@ public synchronized Delta<UsbSerialDeviceInformation> scan() throws IOException
return new Delta<>(added, removed, unchanged);
}

/**
* @return <code>true</code> if the used {@link UsbSerialScanner} can perform scans on this system,
* <code>false</code> otherwise.
*/
public boolean canPerformScans() {
return usbSerialScanner.canPerformScans();
}

private <T> Set<T> setDifference(Set<T> set1, Set<T> set2) {
Set<T> result = new HashSet<>(set1);
result.removeAll(set2);
Expand Down
Expand Up @@ -103,18 +103,24 @@ public void doSingleScan() {
}

/**
* Starts repeatedly scanning for newly added and removed USB devices in the usbserial devices folder (where the
* duration between two subsequent scans is configurable).
* Starts repeatedly scanning for newly added and removed USB devices using the configured {@link UsbSerialScanner}
* (where the duration between two subsequent scans is configurable).
* <p/>
* This repeated scanning can be stopped using {@link #stopBackgroundScanning()}.
*/
@Override
public synchronized void startBackgroundScanning() {
if (backgroundScanningJob == null) {
backgroundScanningJob = scheduler.scheduleWithFixedDelay(() -> {
singleScanInternal(false);
}, 0, pauseBetweenScans.getSeconds(), TimeUnit.SECONDS);
logger.debug("Scheduled USB-Serial background discovery every {} seconds", pauseBetweenScans.getSeconds());
if (deltaUsbSerialScanner.canPerformScans()) {
backgroundScanningJob = scheduler.scheduleWithFixedDelay(() -> {
singleScanInternal(false);
}, 0, pauseBetweenScans.getSeconds(), TimeUnit.SECONDS);
logger.debug("Scheduled USB-Serial background discovery every {} seconds",
pauseBetweenScans.getSeconds());
} else {
logger.info(
"Do not start background scanning, as the configured USB-Serial scanner cannot perform scans on this system");
}
}
}

Expand Down Expand Up @@ -153,7 +159,7 @@ private void singleScanInternal(boolean announceUnchangedDevices) {
announceAddedDevices(delta.getUnchanged());
}
} catch (IOException e) {
logger.warn("A {} prevented a scan for USB serial devices: {}", e.getClass().getSimpleName(),
logger.debug("A {} prevented a scan for USB serial devices: {}", e.getClass().getSimpleName(),
e.getMessage());
}
}
Expand Down
Expand Up @@ -119,6 +119,11 @@ public Set<UsbSerialDeviceInformation> scan() throws IOException {
return result;
}

@Override
public boolean canPerformScans() {
return isReadable(Paths.get(sysfsTtyDevicesDirectory)) && isReadable(Paths.get(devDirectory));
}

/**
* Gets the set of all found serial ports, by searching through the tty devices directory in the sysfs and
* checking for each found serial port if the device file in the devices folder is both readable and writable.
Expand All @@ -133,7 +138,7 @@ private Set<SerialPortInfo> getSerialPortInfos() throws IOException {
String serialPortName = sysfsTtyPath.getFileName().toString();
Path devicePath = Paths.get(devDirectory).resolve(serialPortName);
Path sysfsDevicePath = getSysfsDevicePath(sysfsTtyPath);
if (sysfsDevicePath != null && isAccessible(devicePath)) {
if (sysfsDevicePath != null && isReadable(devicePath) && isWritable(devicePath)) {
result.add(new SerialPortInfo(devicePath, sysfsDevicePath));
}
}
Expand All @@ -142,10 +147,6 @@ private Set<SerialPortInfo> getSerialPortInfos() throws IOException {
return result;
}

private boolean isAccessible(Path devicePath) {
return exists(devicePath) && isWritable(devicePath) && isReadable(devicePath);
}

/**
* In the sysfs, the directory 'class/tty' contains a symbolic link for every serial port style device, i.e., also
* for serial devices. This symbolic link points to the directory for that device within the sysfs device tree. This
Expand Down Expand Up @@ -225,8 +226,8 @@ private UsbSerialDeviceInformation createUsbSerialDeviceInformation(Path usbDevi
String manufacturer = getContentIfFileExists(usbDevicePath.resolve(SYSFS_FILENAME_USB_MANUFACTURER));
String product = getContentIfFileExists(usbDevicePath.resolve(SYSFS_FILENAME_USB_PRODUCT));

int interfaceNumber = Integer.parseInt(getContent(usbInterfacePath.resolve(SYSFS_FILENAME_USB_INTERFACE_NUMBER)),
16);
int interfaceNumber = Integer
.parseInt(getContent(usbInterfacePath.resolve(SYSFS_FILENAME_USB_INTERFACE_NUMBER)), 16);
String interfaceDescription = getContentIfFileExists(usbInterfacePath.resolve(SYSFS_FILENAME_USB_INTERFACE));

return new UsbSerialDeviceInformation(vendorId, productId, serialNumber, manufacturer, product, interfaceNumber,
Expand All @@ -251,6 +252,11 @@ private void extractConfiguration(Map<String, Object> config) {
sysfsTtyDevicesDirectory = config
.getOrDefault(SYSFS_TTY_DEVICES_DIRECTORY_ATTRIBUTE, SYSFS_TTY_DEVICES_DIRECTORY_DEFAULT).toString();
devDirectory = config.getOrDefault(DEV_DIRECTORY_ATTRIBUTE, DEV_DIRECTORY_DEFAULT).toString();

if (!canPerformScans()) {
logger.info("Cannot perform scans with this configuration: sysfsTtyDevicesDirectory: {}, devDirectory: {}",
sysfsTtyDevicesDirectory, devDirectory);
}
}

private static class SerialPortInfo {
Expand Down
Expand Up @@ -37,4 +37,12 @@ public interface UsbSerialScanner {
*/
Set<UsbSerialDeviceInformation> scan() throws IOException;

/**
* {@link UsbSerialScanner}s might be able to perform scans only on certain platforms, or with proper configuration.
* {@link UsbSerialScanner}s can indicate whether they are able to perform scans using this method.
*
* @return <code>true</code> if able to perform scans, and <code>false</code> otherwise.
*/
boolean canPerformScans();

}

0 comments on commit 644ecb2

Please sign in to comment.