Performance of sensor in HAL layer

Sensors are often used in daily life, such as stair lights in buildings, street lights on roads and so on. So what is the role of sensors in mobile phones in particular? Android phones provide acceleration sensors, magnetic fields, directions, gyroscopes, light, pressure, temperature and other sensors. In Android system. The code distribution information of the sensor is as follows:

1) The Java part of the sensor system, and the implementation file is sensor * java.

Code path: frameworks/base/include/core/java/android/hardware

2) JNI part of sensor system, which demonstrates Android hardware. Sensor. Intrinsic support for the manager class.

Code path: frameworks/base/core/jni/android_hardware_SensorManager.cpp.

3) The HAL layer of sensor system demonstrates that the hardware abstraction layer of sensor system needs specific implementation.

Header file path: Hardware / libhardware / include / hardware / sensor h

4) Driving layer

Code path: kernel/driver/hwmon/$(PROJECT)/sensor

1. Sensor code of HAL layer

1) Android files mk

The code of HAL layer is in c/cpp format, and the general saving path is hardware/$(PROJECT)/sensor / Among them, the file Android The implementation code of MK is as follows;

LOCAL_PATH : = $(call my-dir)

LOCAL_PRELINK_MODULE := false

LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

LOCAL_SHARED_LIBARIES := libcutils libc liblog

LOCAL_SRC_FILES: = adapter file

LOCAL_MODULE := sensors.$(PROJECT)

include $(BUILD_SHARED_LIBRARY)

Note LOCAL_MODULE assignment. The module names here are pre-defined. For details, please refer to hardware / libhardware / hardware c

Here, we can see the loading order. When loading sensors, check whether these so exist once, and then load them to the corresponding adapter.

2) Filled structure

In HAL layer, special attention should be paid to the following filling structures.

(1) The code that defines the sensor module is as follows:

struct  sensor_module_t {

        struct hw_module_t common;

        int (*get_sensors_list) (struct sensors_module_t *module, struct sensor_t const**list);

};

Where, get_sensors_list() is used to represent the list of sensors.

(2) sensor_t represents the description of a sensor, and the specific code is as follows:

struct sensor_t {

const char* name ; / / sensor name

const char* vendor ; / / vendor of sensor

int  version; / / sensor version

int  handle ; / / handle of the sensor

int type ; / / sensor type

float  maxRange; / / maximum range of sensor

float  resolution; / / resolution of the sensor

float power; / / the energy consumption of the sensor is mA

void * reserved[9];    

}

(3) The structure and consortium are defined, and the specific codes are as follows:

typedef struct {

int sensor; / / sensor flag

unio {

sensors_vec_t vector; / / x,y,z} vector

sensors_vec_t orientation; / / direction key, unit: angle

sensors_vec_t acceleration; / / acceleration value, unit: m/s2

sensors_vec_t magnetic ; / / magnetic vector, unit: ut

float temperature; / / temperature, unit. c

float distance; / / distance in cm

float light; / / brightness of light, in lux

}

int64_t time;  //ns

uint32_t reserved;

} sensors_data_t;

3) Adaptation layer function interface

In the HAL layer, you need to pay attention to the following function.

static int  a_device_open(const struct hw_module_t* module,

                const  char* name,

                struct hw_device_t** device)

Special attention should be paid to the following assignment:

if (! strcmp(name,SENSORS_HARDWARE_CONTROL)) {/ / command path

        ···

        dev->device.common.close = dev_control_close;

        dev->device.open_data_source=open_data_source;

        dev->device.activate=activate;

        dev->device.set_delay=set_delay;

        ```

}else if (!strcmp(name, SENSORS_HARDWARE_DATA)) {/ / data path

        ···

        dev->device.common.close=dev_data_close;

        dev->device.data_open=data_open;

        dev->device.data_close=data_close;

        dev->device.poll=poll;

```

}

In the function, you can know according to the name that JNI should use the poll method to obtain data, that is, the tape provided in the driver should implement file_operation.

Note: the value of the Sensor register obtained in the driver code is not necessarily the value we actually report to the application. For example, g-sensor, it should not be greater than 10 in all directions. It must be noted that other factors should also be considered.

3) Summary of Sensor programming process

The process of Sensor programming is as follows:

(1) The process of Sensor programming is as follows:

The SENSOR_SERVICE returns a Sensormanager object.

sensormanager = (SensoerManager) getSystemSeriver (SENSOR_SERVICE);

(2) Get the corresponding Sensor type object through the SensorManager object.

sensorObject= sensormanager.getDefaultSensor(sensor Type);

(3) Declare a SensorEventListener object to listen for Sensor events and overload the onSensorChanged method.

SensorEventListener sensorListener = new SensorEventListener() {};

(4) Register the corresponding SensorService

sensormanager.registerListener(sensorListener, sensorobject,Sensor TYPE);

(5) Destroy the corresponding SensorService

sensormanager.unregisterListener(sensorListener,sensorObject);

The SensorListener interface here is the core of the whole sensor application. It includes the following two necessary methods.

  • onSensorChanged(int sensor,float values[]); This method is called when the sensor changes and is only called on sensors monitored by this application. This method contains the following two parameters:

An integer indicating the changed sensor.

An array of floating point numbers representing the sensor data itself.

  • onAccuracyChanged(int sensor,int accuracy); This function is called when the exact value of the sensor changes. This method contains two integers, one represents the sensor and the other represents the exact value of the sensor.

2. Analyze the connection between Sensor source code API layer and hardware platform

Next, let's take the gravity Sensor as an example to see how the gravity Sensor interacts with applications and application framework.

1) First, in the file platform / hardware / libardware / include / sensors H defines the operation of the gravity sensor on the driver, and the code is as follows:

/hardware/libhardware/include/hardware/sensors.h

/**
 * The id of this module
 */
#define SENSORS_HARDWARE_MODULE_ID "sensors"

/**
 * Name of the sensors device to open
 */
#define SENSORS_HARDWARE_POLL       "poll"

```````


/** convenience API for opening and closing a device */

static inline int sensors_open(const struct hw_module_t* module,
        struct sensors_poll_device_t** device) {
    return module->methods->open(module,
            SENSORS_HARDWARE_POLL, TO_HW_DEVICE_T_OPEN(device));
}

static inline int sensors_close(struct sensors_poll_device_t* device) {
    return device->common.close(&device->common);
}

static 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, TO_HW_DEVICE_T_OPEN(device));
}

static inline int sensors_close_1(sensors_poll_device_1_t* device) {
    return device->common.close(&device->common);
}

(2) File framework / JNI / onload CPP is used to load the access program of the driver. The specific code is as follows:

/frameworks/base/services/core/jni/onload.cpp

 int register_android_server_SerialService(JNIEnv* env);

(3) File framework / JNI / COM_ androidserver_ SensorService. CPP is used to provide an interface to the Application Framework. The specific codes are as follows:

 /frameworks/base/services/core/jni/com_android_server_sensor_SensorService.cpp

/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * 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.
 */

#define LOG_TAG "NativeSensorService"

#include <android-base/properties.h>
#include <android_runtime/AndroidRuntime.h>
#include <core_jni_helpers.h>
#include <cutils/properties.h>
#include <jni.h>
#include <sensorservice/SensorService.h>
#include <utils/Log.h>
#include <utils/misc.h>

#include <mutex>

#define PROXIMITY_ACTIVE_CLASS \
    "com/android/server/sensors/SensorManagerInternal$ProximityActiveListener"

namespace android {

static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnProximityActive;

class NativeSensorService {
public:
    NativeSensorService(JNIEnv* env, jobject listener);

    void registerProximityActiveListener();
    void unregisterProximityActiveListener();

private:
    sp<SensorService> mService;

    class ProximityActiveListenerDelegate : public SensorService::ProximityActiveListener {
    public:
        ProximityActiveListenerDelegate(JNIEnv* env, jobject listener);
        ~ProximityActiveListenerDelegate();

        void onProximityActive(bool isActive) override;

    private:
        jobject mListener;
    };
    sp<ProximityActiveListenerDelegate> mProximityActiveListenerDelegate;
};

NativeSensorService::NativeSensorService(JNIEnv* env, jobject listener)
      : mProximityActiveListenerDelegate(new ProximityActiveListenerDelegate(env, listener)) {
    if (base::GetBoolProperty("system_init.startsensorservice", true)) {
        sp<IServiceManager> sm(defaultServiceManager());
        mService = new SensorService();
        sm->addService(String16(SensorService::getServiceName()), mService,
                       false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
    }
}

void NativeSensorService::registerProximityActiveListener() {
    if (mService == nullptr) {
        ALOGD("Dropping registerProximityActiveListener, sensor service not available.");
        return;
    }
    mService->addProximityActiveListener(mProximityActiveListenerDelegate);
}

void NativeSensorService::unregisterProximityActiveListener() {
    if (mService == nullptr) {
        ALOGD("Dropping unregisterProximityActiveListener, sensor service not available.");
        return;
    }

    mService->removeProximityActiveListener(mProximityActiveListenerDelegate);
}

NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
        JNIEnv* env, jobject listener)
      : mListener(env->NewGlobalRef(listener)) {}

NativeSensorService::ProximityActiveListenerDelegate::~ProximityActiveListenerDelegate() {
    AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mListener);
}

void NativeSensorService::ProximityActiveListenerDelegate::onProximityActive(bool isActive) {
    auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
    jniEnv->CallVoidMethod(mListener, sMethodIdOnProximityActive, static_cast<jboolean>(isActive));
}

static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) {
    NativeSensorService* service = new NativeSensorService(env, listener);
    return reinterpret_cast<jlong>(service);
}

static void registerProximityActiveListenerNative(JNIEnv* env, jclass, jlong ptr) {
    auto* service = reinterpret_cast<NativeSensorService*>(ptr);
    service->registerProximityActiveListener();
}

static void unregisterProximityActiveListenerNative(JNIEnv* env, jclass, jlong ptr) {
    auto* service = reinterpret_cast<NativeSensorService*>(ptr);
    service->unregisterProximityActiveListener();
}

static const JNINativeMethod methods[] = {
        {
                "startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
                reinterpret_cast<void*>(startSensorServiceNative)
        },
        {
                "registerProximityActiveListenerNative", "(J)V",
                reinterpret_cast<void*>(registerProximityActiveListenerNative)
        },
        {
                "unregisterProximityActiveListenerNative", "(J)V",
                reinterpret_cast<void*>(unregisterProximityActiveListenerNative)
         },

};

int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
    sJvm = vm;
    jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS);
    sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V");
    return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
                                    NELEM(methods));
}

}; // namespace android

so far, the underlying functions in the file system have been completed. It can be seen that for the underlying class library, the bridge between Android API and hardware device driver can be established through HAL. For different hardware platforms, it is necessary to write the implementation of the above functions, and control the hardware behavior through the driver in the Android kernel. For the upper layer, it can be seen as providing an access interface for the top-level Java implementation of Android API. Because the file is a *. Compiled into the system so library file, which is the same as loading a *. For the system in DK so and its similarity.

(4) Listen to the physical data of the Sensor. Next, see how the Application Framework layer listens to the physical data of the Sensor. At this time, you can use system Load ("*. So") to get access to a library and use the functions in it to do the operations we want. In order to facilitate the operation of programmers, Google uses the Java language to provide a convenient access Application Framework. They encapsulate the drivers or other details of the underlying C/C + + implementation, which is actually the prototype of the API.

Now go back to the Android API layer and the file/ frameworks/base/services/core/java/com/android/server/sensors/SensorService.java The implementation code is as follows:

/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * 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.
 */

package com.android.server.sensors;

import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;

import android.annotation.NonNull;
import android.content.Context;
import android.util.ArrayMap;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ConcurrentUtils;
import com.android.server.LocalServices;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.utils.TimingsTraceAndSlog;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;

/*
Class for managing sensor devices
 Customer registration is required to activate the sensor, and the sensor event itself does not play this service
 Instead, a file descriptor is provided to each client
*/

public class SensorService extends SystemService {
    private static final String START_NATIVE_SENSOR_SERVICE = "StartNativeSensorService";
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final ArrayMap<ProximityActiveListener, ProximityListenerProxy> mProximityListeners =
            new ArrayMap<>();
    @GuardedBy("mLock")
    private Future<?> mSensorServiceStart;
    @GuardedBy("mLock")
    private long mPtr;


    /** Start the sensor service. This is a blocking call and can take time. */
    private static native long startSensorServiceNative(ProximityActiveListener listener);

    private static native void registerProximityActiveListenerNative(long ptr);
    private static native void unregisterProximityActiveListenerNative(long ptr);


    public SensorService(Context ctx) {
        super(ctx);
        synchronized (mLock) {
            mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
                TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
                traceLog.traceBegin(START_NATIVE_SENSOR_SERVICE);
                long ptr = startSensorServiceNative(new ProximityListenerDelegate());
                synchronized (mLock) {
                    mPtr = ptr;
                }
                traceLog.traceEnd();
            }, START_NATIVE_SENSOR_SERVICE);
        }
    }

    @Override
    public void onStart() {
        LocalServices.addService(SensorManagerInternal.class, new LocalService());
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE) {
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart,
                    START_NATIVE_SENSOR_SERVICE);
            synchronized (mLock) {
                mSensorServiceStart = null;
            }
        }
    }

    class LocalService extends SensorManagerInternal {
        @Override
        public void addProximityActiveListener(@NonNull Executor executor,
                @NonNull ProximityActiveListener listener) {
            Objects.requireNonNull(executor, "executor must not be null");
            Objects.requireNonNull(listener, "listener must not be null");
            ProximityListenerProxy proxy = new ProximityListenerProxy(executor, listener);
            synchronized (mLock) {
                if (mProximityListeners.containsKey(listener)) {
                    throw new IllegalArgumentException("listener already registered");
                }
                mProximityListeners.put(listener, proxy);
                if (mProximityListeners.size() == 1) {
                    registerProximityActiveListenerNative(mPtr);
                }
            }
        }

        @Override
        public void removeProximityActiveListener(@NonNull ProximityActiveListener listener) {
            Objects.requireNonNull(listener, "listener must not be null");
            synchronized (mLock) {
                ProximityListenerProxy proxy = mProximityListeners.remove(listener);
                if (proxy == null) {
                    throw new IllegalArgumentException(
                            "listener was not registered with sensor service");
                }
                if (mProximityListeners.isEmpty()) {
                    unregisterProximityActiveListenerNative(mPtr);
                }
            }
        }
    }

    private static class ProximityListenerProxy implements ProximityActiveListener {
        private final Executor mExecutor;
        private final ProximityActiveListener mListener;

        ProximityListenerProxy(Executor executor, ProximityActiveListener listener) {
            mExecutor = executor;
            mListener = listener;
        }

        @Override
        public void onProximityActive(boolean isActive) {
            mExecutor.execute(() -> mListener.onProximityActive(isActive));
        }
    }

    private class ProximityListenerDelegate implements ProximityActiveListener {
        @Override
        public void onProximityActive(boolean isActive) {
            final ProximityListenerProxy[] listeners;
            // We can't call out while holding the lock because clients might be calling into us
            // while holding their own  locks (e.g. when registering / unregistering their
            // listeners).This would break lock ordering and create deadlocks. Instead, we need to
            // copy the listeners out and then only invoke them once we've dropped the lock.
            synchronized (mLock) {
                listeners = mProximityListeners.values().toArray(new ProximityListenerProxy[0]);
            }
            for (ProximityListenerProxy listener : listeners) {
                listener.onProximityActive(isActive);
            }
        }
    }
}

The final code is the API provided by the Core for us to use, which is not listed in detail here.

In fact, a driver can also be called directly in Android. Of course, this is for relatively simple subsystems. These systems do not have a hardware abstraction layer, that is, the functional part of realizing hardware abstraction is not in a separate code. For example, the device node of the driver directly called by JNi code or using SYS file system.

Article from:

Detailed information of android underlying interface driveable development technology

Keywords: Single-Chip Microcomputer stm32 Autonomous vehicles

Added by beckjoh on Wed, 16 Feb 2022 16:15:54 +0200