Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch ble adapter listing to use the native bluez interface. #4839

Merged
merged 16 commits into from
Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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