App Registration sensor monitoring
The overall architecture of the Android Sensor framework is as follows:
The previous several sensor related articles introduced the knowledge of the sensor HAL, to press_sensor real-time display of air pressure coordinates to analyze, the application layer data acquisition process, in fact, the implementation of data monitoring is very simple, mainly divided into the following three steps:
- Get sensor service: Getsystemservice;
- Get specific Sensor object: Getdefaultsensor;
- Register data listener: Registerlistener;
Sensorservice start
When the system server starts up, the sensor service is initialized, that is, she has been running in the background after the boot, the client part, direct connect on the line. As for how to connect, it's all encapsulated in the Sensormanager.
After the Sensorservice service is started, its onfirstref is called when it is strongly referenced for the first time, and then it gets our Sensordevice instance:
void SensorService::onFirstRef() { ALOGD("nuSensorService starting..."); SensorDevice& dev(SensorDevice::getInstance()); sHmacGlobalKeyIsValid = initializeHmacKey(); if (dev.initCheck() == NO_ERROR) { sensor_t const* list; ssize_t count = dev.getSensorList(&list); if (count > 0) {
Attach this part of the process
Sensordevice as the last file native in the sensor architecture, communicates with the HAL layer, so in Sensordevice's construction we can see the famous hw_get_module and Sensors_open_ 1 methods are:
Sensordevice::sensordevice (): Msensordevice (0), Msensormodule (0) {status_t err = Hw_get_module (sensors_hard ware_module_id, (hw_module_t const**) &msensormodule); Aloge_if (Err, "couldn ' t load%s module (%s)", sensors_hardware_module_id, Strerror (-err)); if (msensormodule) {err = sensors_open_1 (&msensormodule->common, &msensordevice); Aloge_if (Err, "couldn ' t open device for module%s (%s)", sensors_hardware_module_id, Strerror (-err)); if (Msensordevice) {if (msensordevice->common.version = = Sensors_device_api_version_1_1 | | Msensordevice->common.version = = sensors_device_api_version_1_2) {aloge (">>>> WARNING &L t;<< Upgrade sensor HAL to version 1_3 "); } sensor_t const* list; ssize_t count = Msensormodule->get_sensors_list (Msensormodule, &list); Mactivationcount.setcapacity (count); Info model; for (size_t i=0; i<size_t (count); i++) {Mactivationcount.add (list[i].handle, model); Msensordevice->activate (reinterpret_cast<struct sensors_poll_device_t *> (MSensorDevice), List[i].handle, 0); } } }}
These SENSORS_HARDWARE_MODULE_ID
are the module names defined in Hardware/sensors.h:
/** * The id of this module */#define SENSORS_HARDWARE_MODULE_ID "sensors"
And Msensormodule is our sensors_module_t
structure, which is defined in the HAL layer Sensors.h:
/** * Every hardware module must has a data structure named Hal_module_info_sym * and the fields of This data structure must begin with hw_module_t * followed by module specific information. */struct sensors_module_t {struct hw_module_t common; /** * Enumerate all available sensors. The list is returned in "list". * @return number of sensors in the list */int (*get_sensors_list) (struct sensors_module_t* module, Stru CT sensor_t const** list); /** * Place the module in a specific mode. The following modes is defined * * 0-normal operation. Default State of the module. * 1-loopback mode. Data is injected for the supported * sensors by the sensor service in this mode. * @return 0 On Success *-einval If requested mode are not supported *-eperm if operation are not a llowed */Int (*set_operation_mode) (unsigned int mode);};
You can see that the sensors_module_t structure expands the hw_module_t, which provides an additional Get_sensor_list method to obtain a list of sensor configurations supported by the system and a schema setting method.
Next, we follow the Hw_get_module method and see what it does.
Hw_get_module
This function is specifically implemented in HARDWARE/LIBHARDWARE/HARDWARE.C.
int hw_get_module(const char *id, const struct hw_module_t **module){ return hw_get_module_by_class(id, NULL, module);}
int Hw_get_module_by_class (const char *class_id, const char *inst, const struct hw_module_t **mo Dule) {int i = 0; Char Prop[path_max] = {0}; Char Path[path_max] = {0}; Char Name[path_max] = {0}; Char Prop_name[path_max] = {0}; if (inst) snprintf (name, Path_max, "%s.%s", class_id, Inst); else strlcpy (name, class_id, Path_max); /* Here we rely on the fact that calling Dlopen multiple times on * the same. So would simply increment a Refcoun T (and not load * A new copy of the library). * We also assume that Dlopen () is Thread-safe. */* First try a property specific to the class and possibly instance */snprintf (prop_name, sizeof (Prop_name), "R o.hardware.%s ", name); if (Property_get (Prop_name, prop, NULL) > 0) {if (hw_module_exists (path, sizeof (path), name, prop) = = 0) { Goto found; }}/* Loop through the configuration variants looking for a module */For (i=0; i
We mainly look at Hw_get_module_by_class, where the parameters passed are "sensors", NULL, and our msensormodule structure.
First copy the string to name:
strlcpy(name, class_id, PATH_MAX);
Then splicing prop_name for Ro.hardware.name, that is prop_name=ro.hardware.sensors
The Property_get method does not give the definition of this value (because it is not defined in the system), so the Next loop is entered:
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; } }
/** * There are a set of variant filename for modules. The form of the filename * is "<MODULE_ID>.variant.so" so for the led module the Dream variants * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be: * * led.trout.so * led.msm7k.so * led.ARMV6.so * led.default.so */ static const char *variant_keys[] = { "ro.hardware", /* This goes first so that it can pick up a different file on the emulator. */ "ro.product.board", "ro.board.platform", "ro.arch"};
According to the above parsing I can also see that will be looked up separately, and, sensors.variant.so
sensors.product.so
sensors.platform.so
sensors.default.so
Finally, we will /system/lib/hw/
find it in the path, sensors.msm8909.so
and then load it into memory through the load method to run. As a result, I have analyzed the Qualcomm 8909 platform.
Small details: When we implement our own HAL Layer module and write an application to test whether the module is working properly, the following parameters should be written when compiling:
LOCAL_MODULE := moduleName.default
Or
LOCAL_MODULE := moduleName.$(TARGET_BOARD_PLATFORM)
Because of the source of the above reasons, if the module name does not correspond to, your modules will not be normal load in, and therefore will not work properly.
We then analyze the implementation of load.
static int load (const char *id, const char *path, const struct hw_module_t **phmi) {int status =-einval; void *handle = NULL; struct hw_module_t *hmi = NULL; /* * Load the symbols resolving undefined symbols before * Dlopen returns. Since Rtld_global is no or ' d in with * Rtld_now the external symbols would not be GLOBAL */handle = Dlopen (pat h, Rtld_now); if (handle = = NULL) {Char const *ERR_STR = Dlerror (); Aloge ("load:module=%s\n%s", Path, Err_str?err_str: "Unknown"); status =-einval; Goto done; }/* Get the address of the struct hal_module_info. */const char *sym = HAL_MODULE_INFO_SYM_AS_STR; HMI = (struct hw_module_t *) dlsym (handle, SYM); if (HMI = = NULL) {aloge ("load:couldn ' t Find Symbol%s", sym); status =-einval; Goto done; }/* Check that the ID matches */if (strcmp (ID, hmi->id)! = 0) {aloge ("load:id=%s! = hmi->id=%s", I D, Hmi->id); status =-einval; Goto done; } HMI->DSO = handle; /* Success * * status = 0; Done:if (Status! = 0) {hmi = NULL; if (handle! = NULL) {dlclose (handle); handle = NULL; }} else {ALOGV ("loaded HAL id=%s path=%s hmi=%p handle=%p", id, path, *PHMI, handle); } *phmi = HMI; return status;}
- First, open the Sensors.xxx.so module with Dlopen and get its handle handle
- Call Dlsym to get the address of the struct hw_module_t struct, note that the string passed in here is HAL_MODULE_INFO_SYM_AS_STR, defined in the Hardware.h header file
/** * Name of the hal_module_info */#define HAL_MODULE_INFO_SYM HMI /** * Name of the hal_module_info as a string */#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
Here's why to get the name of the HMI address, I guess it should be the entrance to the HAL module.
Elf file format:
ELF = executable and linkable format, executable connection format, is the UNIX System Lab (USL) developed and published as an application binary interface (application binary Interface,abi), The extension is elf. An elf head at the beginning of the file, a roadmap (road map) was saved, describing the organization of the document. Sections holds the information of the object file, from the connection point of view: including instructions, data, symbol table, relocation information and so on. With the file command we know that sensors.xx.so is an elf file format
tiny.hui@build-server:~$ file sensors.msm8909.sosensors.msm8909.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[md5/uuid]=0x25812b01ab4700281b41f61327075611, not stripped
Therefore, the Linux readelf command allows us to view information such as the internal layout of the file and the symbol table.
tiny.hui@build-server:~$ readelf-s sensors.msm8909.so Symbol table '. Dynsym ' contains 157 entries:Num:Value Size Type Bind Vis Ndx Name 0:00000000 0 notype LOCAL DEFAULT UND 1:00000000 0 FUNC GLOBAL DE FAULT UND __cxa_finalize@libc (2) 2:00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@libc (2) 3:00000000 0 Func Global default UND __register_atfork@libc (2) 4:00000000 0 FUNC Global default UND PTHREAD_MU TEX_LOCK@LIBC (2) ..............................//omitted irrelevant information 179:0000c179 FUNC GLOBAL Default _zn19nativesensormanager1 180:0000bd21 392 FUNC GLOBAL default _zn19nativesensormanager2 181: 0000a45b func Global default _zn24inputeventcircularre 182:000064d9 148 FUNC Global Default _z N22sensors_poll_context 183:0000d889 6 func GLOBAL DEFAULT _zn11sensors_xmlc1ev 184:0000663d 156 FUNC GLOBAL DEFAULT 13 _Zn10sensorbasec2epkcs1_p 185:000086d5 248 func GLOBAL DEFAULT _zn11accelsensorc1ev 186:000088DD 248 FUNC Global default _zn11accelsensorc2ep13sen 187:00014220 4 OBJECT GLOBAL default _zn7android9singletoni 11s 188:0000a53b-func global DEFAULT _zn18calibrationmanager10 189:00007775. Func Global Defau LT-_zn15proximitysensord1ev 190:00014008 136 OBJECT GLOBAL DEFAULT-HMI 191:0000721d-FUNC Globa L default _znk11accelsensor16haspen 192:0000d475-FUNC WEAK default 193 _znk7android12sortedvecto : 00006dd9 func Global default _ZN11LIGHTSENSORC2EPC 194:00006181 func global default _zn22 Sensors_poll_context 195:0000d4fd func GLOBAL DEFAULT _zn13virtualsensord1ev 196:0000aa15-func Global default _zn18calibrationmanagerd2 197:000087CD 272 FUNC Global Default _zn11accelsensorc1epc
By the symbol table, the address of the HMI is 00014008, get the function address, of course, you can execute the corresponding code.
QualComm Sensor HAL So we went on to look at the Sensor_hal layer, the high-pass sensor realizes its own HAL, its source code in the hardware\qcom\sensors
path, through the android.mk we can also be sure that he is indeed our previous Load method open dynamic link library, which will be generated after compiling sensor.msm8909.so
:
Ifneq ($ (Filter msm8960 msm8610 msm8916 msm8909,$ (target_board_platform)),) # Exclude SSC targetsifneq ($ (target_uses_ SSC), True) # Disable temporarily for Compilling Errorifneq ($ (build_tiny_android), true) Local_path: = $ (call my-dir) # HAL M Odule implemenation stored ininclude $ (clear_vars) ifeq ($ (Use_sensor_multi_hal), true) Local_module: = Sensors.nativeelse Ifneq ($ (Filter msm8610,$ (target_board_platform)), Local_module: = sensors.$ (target_board_ PLATFORM) Local_cflags: =-dtarget_8610 Else Ifneq ($ (Filter msm8916 msm8909,$ (target_board_platform)),) LOCAL _module: = sensors.$ (target_board_platform) Else local_module: = sensors.msm8960 endif endif ifdef target_2nd _arch Local_module_relative_path: = HW Else Local_module_path: = $ (target_out_shared_libraries)/HW EndifendifLOCAL _module_tags: = optionallocal_cflags + =-dlog_tag=\ "Sensors\" ifeq ($ (call is-board-platform,msm8960), true) LOCAL_ CFLAGS + =-dtarget_8930endiflocal_c_includes: = $ (target_out_intermediates)/kernel_obj/usr/includelocal_additional_dependencies: = $ (target_out_intermediates)/KERNEL_OBJ/usr# Export Calibration Library Needed dependency headerslocal_copy_headers_to: = sensors/inclocal_copy_headers: = \ CalibrationModule.h \ sensors_extension.h \ sensors.hlocal_src_files: = \ Sensors.cpp \ SensorBase.cpp \ LightSensor.cpp \ ProximitySensor.cpp \ CompassSensor.cpp \ Accelerometer.cpp \ Gyroscope.cpp \ Bmp180.cpp \ InputEventReader.cpp \ Calibrationmanage R.cpp \ NativeSensorManager.cpp \ VirtualSensor.cpp \ sensors_xml.cpp \ Significantmotion.cpPlocal_c_includes + = External/libxml2/include \ifeq ($ (call is-platform-sdk-version-at-least,20), true) LOCAL_C_INCLU DES + = External/icu/icu4c/source/commonelse Local_c_includes + = external/icu4c/commonendiflocal_shared_libraries: = Liblog libcutils LIBDL libxml2 libutilsinclude $ (build_shared_library) include $ (clear_vars) Local_module: = Libcalmodule_commonlocal_src_files: = \ algo/common/common_wrapper.c \ algo/common/com PASS/AKFS_AOC.C \ algo/common/compass/akfs_device.c \ Algo/common/compass/akfs_directi ON.C \ algo/common/compass/akfs_vnorm.clocal_shared_libraries: = Liblog libcutilslocal_module_tags: = Op Tionalifdef target_2nd_archlocal_module_path_32: = $ (Target_out_vendor)/liblocal_module_path_64: = $ (TARGET_OUT_ VENDOR)/lib64elselocal_module_path: = $ (target_out_vendor_shared_libraries) Endifinclude $ (build_shared_library) Include $ (clear_vars) Local_module: = Calmodule.cfglocal_module_TAGS: = Optionallocal_module_class: = Etclocal_module_path: = $ (target_out_vendor_etc) Local_src_files: = Calmodule.cfginclude $ (build_prebuilt) endif #BUILD_TINY_ANDROIDendif #TARGET_USES_SSCendif #TARGET_BOARD_PLATFORM
So what exactly is the portal of the HMI defined in the file here?
Kung Fu is not a conscientious, in sensors.cpp
, we finally found the HMI's entrance, that is, the following structural body:
static struct hw_module_methods_t sensors_module_methods = { .open = sensors_open}; struct sensors_module_t HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = (uint16_t)SENSORS_DEVICE_API_VERSION_1_3, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = SENSORS_HARDWARE_MODULE_ID, .name = "QTI Sensors Module", .author = "Qualcomm Technologies, Inc.", .methods = &sensors_module_methods, .dso = NULL, .reserved = {0}, }, .get_sensors_list = sensors_get_sensors_list, .set_operation_mode = sensors_set_operation_mode};
Hal_module_info_sym is the HMI variable mentioned above, congratulations, here we open the gate of Qualcomm Sensor HAL.
Finally, the hw_module_t structure handle is returned to our Sensordevice constructor:
SensorDevice::SensorDevice() : mSensorDevice(0), mSensorModule(0){ status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&mSensorModule); ALOGE_IF(err, "couldn't load %s module (%s)", SENSORS_HARDWARE_MODULE_ID, strerror(-err)); if (mSensorModule) { err = sensors_open_1(&mSensorModule->common, &mSensorDevice);
Next, the Module->common is passed through the Sensors_open_1 method to turn on our sensor driver.
// hardware/libhardware/include/hardware/sensors.hstatic inline int sensors_open_1(const struct hw_module_t* module, sensors_poll_device_1_t** device) { return module->methods->open(module, SENSORS_HARDWARE_POLL, (struct hw_device_t**)device);} static inline int sensors_close_1(sensors_poll_device_1_t* device) { return device->common.close(&device->common);}
Looking back at the structure definition of the HMI, where Module->common->open is assigned a value of sensors_module_methods, there is only one open method, so,module->methods-> Open will eventually call the Sensors_open method to open the driver.
The logic of native to the HAL layer here has been basically analyzed.