Skip to content

Commit

Permalink
Switch ble adapter listing to use the native bluez interface. (#4839)
Browse files Browse the repository at this point in the history
* Add an adapter iterator class to bluez

* code review updates

* Fix typo in method

* Fix compilation and off-by-one error

* Ensure we unref the adapter during listing

* Remove unused variable

* Fix typos in comments - I clobbered the fixes that Justin pushed

* Fix typos in comments - I clobbered the fixes that Justin pushed

* Add support for native adapter listing

* Restyle fixes

* Update the init logic

* Do not auto-import GetAdapters. ble is a stand alone package for now

* Update typing

* Move iterator values to std::string and fix typo in linux impl

* Switch reinterpret cast to static cast
  • Loading branch information
andy31415 authored Feb 16, 2021
1 parent c2ba4d1 commit aa7c8ec
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 56 deletions.
16 changes: 15 additions & 1 deletion src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import("//build_overrides/pigweed.gni")
import("$dir_pw_build/python.gni")

import("${build_root}/chip/tools.gni")
import("${chip_root}/src/platform/device.gni")
import("${dir_pw_unit_test}/test.gni")

config("controller_wno_deprecate") {
Expand All @@ -44,6 +45,10 @@ shared_library("ChipDeviceCtrl") {
"ChipDeviceController-StorageDelegate.h",
]

if (current_os == "linux" && chip_enable_ble) {
sources += [ "chip/ble/LinuxImpl.cpp" ]
}

public_deps = [
"${chip_root}/src/app",
"${chip_root}/src/controller",
Expand Down Expand Up @@ -75,8 +80,8 @@ pw_python_action("python") {
"chip/ChipStack.py",
"chip/ChipUtility.py",
"chip/__init__.py",
"chip/ble/__init__.py",
"chip/exceptions/__init__.py",
"chip/native/__init__.py",
"chip/tlv/__init__.py",
]
},
Expand All @@ -90,6 +95,15 @@ pw_python_action("python") {
},
]

if (chip_enable_ble) {
_py_manifest_files += [
{
src_dir = "."
sources = [ "chip/ble/__init__.py" ]
},
]
}

_py_manifest_file = "${target_gen_dir}/${target_name}.py_manifest.json"

inputs = []
Expand Down
1 change: 1 addition & 0 deletions src/controller/python/build-chip-wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def finalize_options(self):
'chip',
'chip.ble',
'chip.exceptions',
'chip.native',
'chip.tlv',
],
package_dir={
Expand Down
5 changes: 2 additions & 3 deletions src/controller/python/chip-repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ def main():
######## List available BLE adapters #########
for adapter in chip.GetBleAdapters():
print(adapter)
import chip.ble
print(chip.ble.GetAdapters())
'''.strip())

if __name__ == "__main__":
Expand Down
4 changes: 1 addition & 3 deletions src/controller/python/chip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,4 @@
# Provides Python APIs for CHIP.
#

"""Provides Python APIs for CHIP."""

from chip.ble import GetBleAdapters
"""Provides Python APIs for CHIP."""
51 changes: 51 additions & 0 deletions src/controller/python/chip/ble/LinuxImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

#include <platform/CHIPDeviceLayer.h>
#include <platform/Linux/CHIPBluezHelper.h>
#include <platform/internal/BLEManager.h>
#include <support/CHIPMem.h>
#include <support/ReturnMacros.h>

using namespace chip::DeviceLayer::Internal;

/////////// Listing adapters implementation //////////

extern "C" void * pychip_ble_adapter_list_new()
{
return static_cast<void *>(new chip::DeviceLayer::Internal::AdapterIterator());
}

extern "C" void pychip_ble_adapter_list_delete(void * adapter)
{
delete static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter);
}

extern "C" bool pychip_ble_adapter_list_next(void * adapter)
{
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->Next();
}

extern "C" unsigned pychip_ble_adapter_list_get_index(void * adapter)
{
/// NOTE: returning unsigned because python native has no sized values
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->GetIndex();
}

extern "C" const char * pychip_ble_adapter_list_get_address(void * adapter)
{
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->GetAddress();
}

extern "C" const char * pychip_ble_adapter_list_get_alias(void * adapter)
{
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->GetAlias();
}

extern "C" const char * pychip_ble_adapter_list_get_name(void * adapter)
{
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->GetName();
}

extern "C" bool pychip_ble_adapter_list_is_powered(void * adapter)
{
return static_cast<chip::DeviceLayer::Internal::AdapterIterator *>(adapter)->IsPowered();
}
92 changes: 53 additions & 39 deletions src/controller/python/chip/ble/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,78 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""BLE-related functionality within CHIP"""

from chip import ChipDeviceCtrl
import sys
import platform
import chip.native
import ctypes
from typing import List
from ctypes import c_bool, c_void_p, c_char_p, c_uint
from dataclasses import dataclass


class Adapter:
"""Generic exposed adapter information."""
@dataclass
class AdapterInfo:
index: int
address: str
name: str
alias: str
powered_on: bool

def __init__(self, name, macAddress):
self.name = name
self.macAddress = macAddress

def __str__(self):
return 'chip.ble.Adapter(%s, %s)' % (self.name, self.macAddress)
def _GetBleLibraryHandle() -> ctypes.CDLL:
""" Get the native library handle with BLE method initialization.
if platform.system() == 'Darwin':
from chip.ChipCoreBluetoothMgr import CoreBluetoothManager as BleManager
Retreives the CHIP native library handle and attaches signatures to
native methods.
"""

def ConvertNativeAdapter(nativeAdapter):
raise NotImplementedError('Not implemented for Darwin')
handle = chip.native.GetLibraryHandle()

elif sys.platform.startswith('linux'):
from chip.ChipBluezMgr import BluezManager as BleManager
# Uses one of the type decorators as an indicator for everything being
# initialized. Native methods default to c_int return types
if handle.pychip_ble_adapter_list_new.restype != c_void_p:
setter = chip.native.NativeLibraryHandleMethodArguments(handle)

def ConvertNativeAdapter(nativeAdapter):
return Adapter(name = str(nativeAdapter.path), macAddress = str(nativeAdapter.Address))
setter.Set('pychip_ble_adapter_list_new', c_void_p, [])
setter.Set('pychip_ble_adapter_list_next', c_bool, [c_void_p])
setter.Set('pychip_ble_adapter_list_get_index', c_uint, [c_void_p])
setter.Set('pychip_ble_adapter_list_get_address', c_char_p, [c_void_p])
setter.Set('pychip_ble_adapter_list_get_alias', c_char_p, [c_void_p])
setter.Set('pychip_ble_adapter_list_get_name', c_char_p, [c_void_p])
setter.Set('pychip_ble_adapter_list_is_powered', c_bool, [c_void_p])
setter.Set('pychip_ble_adapter_list_delete', None, [c_void_p])

class LazyHandles(object):
"""Contains a handle to an underlying BLE manager."""
return handle

def __init__(self):
self._deviceController = None
self._bleManager = None

@property
def deviceController(self):
if self._deviceController is None:
self._deviceController = ChipDeviceCtrl.ChipDeviceController()
return self._deviceController
def GetAdapters() -> List[AdapterInfo]:
"""Get a list of BLE adapters available on the system. """
handle = _GetBleLibraryHandle()

@property
def bleManager(self):
if self._bleManager is None:
self._bleManager = BleManager(self.deviceController)
return self._bleManager
result = []
nativeList = handle.pychip_ble_adapter_list_new()
if nativeList == 0:
raise Exception('Failed to get BLE adapter list')

def GetAdapters(self):
"""Return available BLE adapters on this platform."""
return [ConvertNativeAdapter(a) for a in self.bleManager.get_adapters()]
try:
while handle.pychip_ble_adapter_list_next(nativeList):
result.append(
AdapterInfo(
index=handle.pychip_ble_adapter_list_get_index(nativeList),
address=handle.pychip_ble_adapter_list_get_address(nativeList).decode('utf8'),
name=handle.pychip_ble_adapter_list_get_name(nativeList).decode('utf8'),
alias=handle.pychip_ble_adapter_list_get_alias(nativeList).decode('utf8'),
powered_on=handle.pychip_ble_adapter_list_is_powered(nativeList),
))

finally:
handle.pychip_ble_adapter_list_delete(nativeList)

lazyHandles = LazyHandles()
return result

def GetBleAdapters():
return lazyHandles.GetAdapters()

__all__ = [
"GetBleAdapters",
'GetAdapters',
]
79 changes: 79 additions & 0 deletions src/controller/python/chip/native/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import ctypes
import glob
import os
import platform

NATIVE_LIBRARY_BASE_NAME = "_ChipDeviceCtrl.so"


def _AllDirsToRoot(dir):
"""Return all parent paths of a directory."""
dir = os.path.abspath(dir)
while True:
yield dir
parent = os.path.dirname(dir)
if parent == "" or parent == dir:
break
dir = parent


def FindNativeLibraryPath() -> str:
"""Find the native CHIP dll/so path."""

scriptDir = os.path.dirname(os.path.abspath(__file__))

# When properly installed in the chip package, the Chip Device Manager DLL will
# be located in the package root directory, along side the package's
# modules.
dmDLLPath = os.path.join(
os.path.dirname(scriptDir), # file should be inside 'chip'
NATIVE_LIBRARY_BASE_NAME)
if os.path.exists(dmDLLPath):
return dmDLLPath

# For the convenience of developers, search the list of parent paths relative to the
# running script looking for an CHIP build directory containing the Chip Device
# Manager DLL. This makes it possible to import and use the ChipDeviceMgr module
# directly from a built copy of the CHIP source tree.
buildMachineGlob = "%s-*-%s*" % (platform.machine(),
platform.system().lower())
relDMDLLPathGlob = os.path.join(
"build",
buildMachineGlob,
"src/controller/python/.libs",
NATIVE_LIBRARY_BASE_NAME,
)
for dir in _AllDirsToRoot(scriptDir):
dmDLLPathGlob = os.path.join(dir, relDMDLLPathGlob)
for dmDLLPath in glob.glob(dmDLLPathGlob):
if os.path.exists(dmDLLPath):
return dmDLLPath

raise Exception(
"Unable to locate Chip Device Manager DLL (%s); expected location: %s" %
(NATIVE_LIBRARY_BASE_NAME, scriptDir))


class NativeLibraryHandleMethodArguments:
"""Convenience wrapper to set native method argtype and restype for methods."""

def __init__(self, handle):
self.handle = handle

def Set(self, methodName: str, resultType, argumentTypes: list):
method = getattr(self.handle, methodName)
method.restype = resultType
method.argtype = argumentTypes


_nativeLibraryHandle: ctypes.CDLL = None


def GetLibraryHandle() -> ctypes.CDLL:
"""Get a memoized handle to the chip native code dll."""

global _nativeLibraryHandle
if _nativeLibraryHandle is None:
_nativeLibraryHandle = ctypes.CDLL(FindNativeLibraryPath())

return _nativeLibraryHandle
8 changes: 4 additions & 4 deletions src/platform/Linux/CHIPBluezHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2073,10 +2073,10 @@ bool AdapterIterator::Advance()
index = 0;
}

mCurrent.index = index;
CopyString(mCurrent.address, bluez_adapter1_get_address(adapter));
CopyString(mCurrent.alias, bluez_adapter1_get_alias(adapter));
CopyString(mCurrent.name, bluez_adapter1_get_name(adapter));
mCurrent.index = index;
mCurrent.address = bluez_adapter1_get_address(adapter);
mCurrent.alias = bluez_adapter1_get_alias(adapter);
mCurrent.name = bluez_adapter1_get_name(adapter);
mCurrent.powered = bluez_adapter1_get_powered(adapter);

g_object_unref(adapter);
Expand Down
18 changes: 12 additions & 6 deletions src/platform/Linux/CHIPBluezHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <platform/Linux/dbus/bluez/DbusBluez.h>

#include <cstdint>
#include <string>

namespace chip {
namespace DeviceLayer {
Expand Down Expand Up @@ -213,6 +214,11 @@ CHIP_ERROR ConnectDevice(BluezDevice1 * apDevice);
/// while (iterator.Next()) {
/// std::cout << iterator.GetAddress() << std::endl;
/// }
///
/// Data is provided through the bluez dbus interface. You can view
/// this data in the commandline using commands such as:
///
/// busctl introspect org.bluez /org/bluez/hci0
class AdapterIterator
{
public:
Expand All @@ -227,9 +233,9 @@ class AdapterIterator
// Information about the current value. Safe to call only after
// "Next" has returned true.
uint32_t GetIndex() const { return mCurrent.index; }
const char * GetAddress() const { return mCurrent.address; }
const char * GetAlias() const { return mCurrent.alias; }
const char * GetName() const { return mCurrent.name; }
const char * GetAddress() const { return mCurrent.address.c_str(); }
const char * GetAlias() const { return mCurrent.alias.c_str(); }
const char * GetName() const { return mCurrent.name.c_str(); }
bool IsPowered() const { return mCurrent.powered; }

private:
Expand All @@ -253,9 +259,9 @@ class AdapterIterator
struct
{
uint32_t index;
char address[kMaxAddressLength];
char alias[kMaxNameLength];
char name[kMaxNameLength];
std::string address;
std::string alias;
std::string name;
bool powered;
} mCurrent = { 0 };
};
Expand Down

0 comments on commit aa7c8ec

Please sign in to comment.