From 6e56e34c4a6aedd4b89acd6292cfad7ff49028d4 Mon Sep 17 00:00:00 2001 From: asarium Date: Tue, 24 May 2022 19:01:25 +0200 Subject: [PATCH] Do not crash on devices without a LinkKey If a bluetooth device does not have a LinkKey, for example if it does not support pairing, then the current code would crash with an unknown section error. This fixes that by handling that case gracefully and showing those devices in a separate list to make it clear that these are not relevant for this project. --- bt_dualboot/bluetooth_device.py | 3 +++ .../bt_linux/bluetooth_device_factory.py | 2 +- .../bt_sync_manager/bt_sync_manager.py | 20 ++++++++++++++-- bt_dualboot/cli/app.py | 8 +++++++ .../bt_linux/bluetooth_device_factory_test.py | 23 +++++++++++++++++++ .../A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info | 7 ++++++ tests/bt_linux/devices_test.py | 2 ++ 7 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info diff --git a/bt_dualboot/bluetooth_device.py b/bt_dualboot/bluetooth_device.py index ea50f85..f9fe5a3 100644 --- a/bt_dualboot/bluetooth_device.py +++ b/bt_dualboot/bluetooth_device.py @@ -48,3 +48,6 @@ def is_source_linux(self): def is_source_windows(self): return self.source == "Windows" + + def has_pairing_key(self): + return self.pairing_key is not None diff --git a/bt_dualboot/bt_linux/bluetooth_device_factory.py b/bt_dualboot/bt_linux/bluetooth_device_factory.py index 5696064..7b8bdb4 100644 --- a/bt_dualboot/bt_linux/bluetooth_device_factory.py +++ b/bt_dualboot/bt_linux/bluetooth_device_factory.py @@ -36,7 +36,7 @@ def extract_info(device_info_path): return { "name": config.get("General", "Name"), "class": config.get("General", "Class", fallback=None), - "pairing_key": config.get("LinkKey", "Key"), + "pairing_key": config.get("LinkKey", "Key", fallback=None), } # fmt: on diff --git a/bt_dualboot/bt_sync_manager/bt_sync_manager.py b/bt_dualboot/bt_sync_manager/bt_sync_manager.py index a5e0b65..6e30353 100644 --- a/bt_dualboot/bt_sync_manager/bt_sync_manager.py +++ b/bt_dualboot/bt_sync_manager/bt_sync_manager.py @@ -142,17 +142,33 @@ def devices_absent_windows(self): !uses cached Returns: - list: which exist both in Linux and Windows, but have different pairing keys + list: which only exist in Linux and have a pairing key """ index = self._index_devices() single_linux_devices = [ devices[0] for mac, devices in index.items() - if len(devices) == 1 and devices[0].is_source_linux() + if len(devices) == 1 and devices[0].is_source_linux() and devices[0].has_pairing_key() ] return single_linux_devices + def devices_without_pairingkey(self): + """ + !uses cached + + Returns: + list: which only exist in Linux but have no pairing key + """ + + index = self._index_devices() + no_key_devices = [ + devices[0] + for mac, devices in index.items() + if len(devices) == 1 and devices[0].is_source_linux() and not devices[0].has_pairing_key() + ] + return no_key_devices + def _param_get_macs_list(self, device_or_mac_or_list): """Align plural argument to list of devices MACs diff --git a/bt_dualboot/cli/app.py b/bt_dualboot/cli/app.py index ba5513c..030613e 100644 --- a/bt_dualboot/cli/app.py +++ b/bt_dualboot/cli/app.py @@ -191,6 +191,14 @@ def list_devices(self): bot=self.opts.bot, ) + print_devices_list( + "missing_paring_key", + "Missing pairing key", + devices=sync_manager.devices_without_pairingkey(), + annotation="Following devices do not have a pairing key and so cannot be synced", + bot=self.opts.bot, + ) + def backup(self, path): """Backups Hive file to given or default path Args: diff --git a/tests/bt_linux/bluetooth_device_factory_test.py b/tests/bt_linux/bluetooth_device_factory_test.py index 87f6499..e83488c 100644 --- a/tests/bt_linux/bluetooth_device_factory_test.py +++ b/tests/bt_linux/bluetooth_device_factory_test.py @@ -6,6 +6,9 @@ SAMPLE_DEVICE_INFO_PATH = os.path.join( SMPL_BT_SAMPLE_01, "A4:6B:6C:9D:E2:FB", "B6:C2:D3:E5:F2:0D", "info" ) +SAMPLE_DEVICE_INFO_PATH_NO_LINKKEY = os.path.join( + SMPL_BT_SAMPLE_01, "A4:6B:6C:9D:E2:FB", "22:94:90:56:EE:38", "info" +) def test_extract_macs(): @@ -22,6 +25,15 @@ def test_extract_info(): assert extract_info(SAMPLE_DEVICE_INFO_PATH) == expected +def test_extract_info_no_linkkey(): + expected = { + "name": "DEV-1-02-Name", + "class": "0x000540", + "pairing_key": None, + } + assert extract_info(SAMPLE_DEVICE_INFO_PATH_NO_LINKKEY) == expected + + def test_bluetooth_device_factory(): device = bluetooth_device_factory(SAMPLE_DEVICE_INFO_PATH) @@ -32,3 +44,14 @@ def test_bluetooth_device_factory(): assert device.name == "DEV-1-02-Name" assert device.pairing_key == "A515CBE4E8F2E236FF999C0A53369EF6" # fmt: on + +def test_bluetooth_device_factory_no_linkkey(): + device = bluetooth_device_factory(SAMPLE_DEVICE_INFO_PATH_NO_LINKKEY) + + # fmt: off + assert device.__class__.__name__ == "BluetoothDevice" + assert device.klass == "0x000540" + assert device.mac == "22:94:90:56:EE:38" + assert device.name == "DEV-1-02-Name" + assert device.pairing_key == None + # fmt: on diff --git a/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info b/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info new file mode 100644 index 0000000..87a12ca --- /dev/null +++ b/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info @@ -0,0 +1,7 @@ +[General] +Name=DEV-1-02-Name +Class=0x000540 +SupportedTechnologies=BR/EDR; +Trusted=true +Blocked=false +Services=00001101-0000-1000-8000-00805f9b34fb;00001124-0000-1000-8000-00805f9b34fb; diff --git a/tests/bt_linux/devices_test.py b/tests/bt_linux/devices_test.py index cb3ffdc..b7897bb 100644 --- a/tests/bt_linux/devices_test.py +++ b/tests/bt_linux/devices_test.py @@ -25,6 +25,7 @@ def test_get_devices_paths(self, bt_linux_sample_01): "A4:6B:6C:9D:E2:FB/B6:C2:D3:E5:F2:0D", "A4:6B:6C:9D:E2:FB/C2:9E:1D:E2:3D:A5", "A4:6B:6C:9D:E2:FB/D1:8A:4E:71:5D:C1", + "A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38", "B4:6B:6C:9D:E2:FB/A4:80:1D:C5:4F:7E", "B4:6B:6C:9D:E2:FB/B8:94:A5:FD:F1:0A", "B4:6B:6C:9D:E2:FB/C4:72:B3:6F:82:42", @@ -45,6 +46,7 @@ def test_get_devices(self): "A4:80:1D:C5:4F:7E", "B8:94:A5:FD:F1:0A", "C4:72:B3:6F:82:42", + "22:94:90:56:EE:38", ] actual_macs = [device.mac for device in get_devices()] assert sorted(actual_macs) == sorted(expected_macs)