Sensor Stack


출처 : https://source.android.com/devices/sensors/index.html
이번에는 HAL에서 framework로 어떻게 올라가는지 확인해보려 한다

framework에서 HAL을 어떻게 호출?

이 파일에는 볼게 많다. 실제 휴대폰에 adb로 들어가서 /system/lib/hw 디렉토리에 뭐가 있나 봐보자
hardware/libhardware/hardware.c

int hw_get_module_by_class(const char *class_id, const char *inst,
                            const struct hw_module_t **module)
{
    ...
    return load(classid_, path, module);
}

int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}

#define HAL_MODULE_INFO_SYM                 HMI
#define HAL_MODULE_INFO_SYM_AS_STR         "HMI"
static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    ...
    void *handle = dlopen(path, RTLD_NOW);

    /* Get the address of the struct hal_module_info */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    struct hw_module_t *hmi = (struct hw_module_t *)dlsym(handle, sym);
    if(hmi->dso)
        hmi->dso = handle;
    *pHmi = hmi;
    ...
}

네 그렇습니다. load라는 녀석을 통해 심볼 호출이 되어 HMI가 읽힙니다
hw_get_module_by_class 같은 함수는 밑의 SensorDevice.cpp에서 호출이 됩니다


hardware/libhardware/include/hardware/sensors.h

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, (struct hw_device_t**)device);
}

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

아주 오래전에 open method가 호출 된다고 했던거 기억하는지요
이 함수는 아래의 class에서 호출 됩니다
잘 기억이 나지 않는다면 1편을 참조하시기 바랍니다



frameworks/native/services/sensorservice/SensorDevice.cpp

SensorDevice::SensorDevice()
    :    mSensorDevice(0),
        mSensorModule(0)
{
    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
            (hw_module_t const**)&mSensorModule);            //HMI에서 정의 했던 SENSORS_HARDWARE_MODULE_ID를 여기서 호출

    err = sensors_open_1(&mSensorModule->common, &mSensorDevice);    //HMI open method call & get mSensorDevice
    if (mSensorDevice) {
        sensor_t const* list;
        ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);    //HMI call get_sensors_list
        mActivationCount.setCapacity(count);
        Info model;
        for (size_t i=0 ; i<size_t(count) ; i++) {
            mActivationCount.add(list[i].handle, model);
            mSensorDevice->activate(        //아까 &dev->device.common으로 보냈던거 구조체 형변환 기억하나요?
                    reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
                    list[i].handle, 0); 
        }
}

activate method 처럼 HMI에서 정의한 함수들을 호출한다
activate 호출이 되면 pipe로 wake message가 날아간다 그리고 poll에서 pipe를 확인하고 이벤트를 읽는다

기타 HMI에서 정의 되었던 각종 method들 batch, poll, setDelay 등 모두 SensorDevice.cpp 에서 호출한다
SensorDevice.cpp는 google에서 만드니 우리가 건들 부분은 없고 포팅을 위해서는 Sensors.cpp 이하만 구현 해주면 될 것이다
애초에 디렉토리 이름이 frameworks/native/serivces/services/ 이다

그 이후로 java랑 어떻게 붙냐면 무지막지하게 긴데 그걸 다 묘사할 수도 없고 대충 써보면
SensorService.cpp -> SensorEventQueue.cpp -> android_hardware_SensorManager.cpp -> SystemSensorManager.java


JNI

java와 c++의 교두보 같은 느낌이다
모든 jni 들은 android_hardware_xxxx.cpp 이런식으로 파일명이 붙는다

framework/base/core/jni/android_hardware_SensorManager.cpp

static JNINativeMethod gSystemSensorManagerMethods[] = { 
    {"nativeClassInit",                                //java
            "()V",                                    //argument, return type
            (void*)nativeClassInit },                 //c++

    {"nativeGetNextSensor",
            "(Landroid/hardware/Sensor;I)I",
            (void*)nativeGetNextSensor },
};

static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
    jclass sensorClass = _env->FindClass("android/hardware/Sensor");
    SensorOffsets& sensorOffsets = gSensorOffsets;
    sensorOffsets.name        = _env->GetFieldID(sensorClass, "mName",      "Ljava/lang/String;");
    sensorOffsets.vendor      = _env->GetFieldID(sensorClass, "mVendor",    "Ljava/lang/String;");
    sensorOffsets.version     = _env->GetFieldID(sensorClass, "mVersion",   "I");
    ...
}


JNINativeMethod 형식으로 이렇게 c++의 함수 이름과 java의 framework api 이름을 매핑시켜 놓는다

int register_android_hardware_SensorManager(JNIEnv *env)
{
    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
            gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));        //아까 위에서 매핑했던 이름

    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$BaseEventQueue",
            gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));

    FIND_CLASS(gBaseEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$BaseEventQueue");
    ...
}

extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
{
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

    ALOGV("Registering %s's %d native methods...", className, numMethods);

    scoped_local_ref<jclass> c(env, findClass(env, className));
    if (c.get() == NULL) {
        char* msg;
        asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
        e->FatalError(msg);
    }

    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
        char* msg;
        asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
        e->FatalError(msg);
    }

    return 0;
}

Android RunTime에 서비스 등록

frameworks/base/core/jni/AndroidRuntime.cpp

#define REG_JNI(name)    { name }
struct RegJNIRec {
    int (*mProc)(JNIEnv*);
};

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    ...
    REG_JNI(register_android_hardware_SensorManager),        //아까 java method와 c++ method를 연결했던 그 함수 포인터
    ...
}

extern "C"
jint Java_LoadClass_registerNatives(JNIEnv* env, jclass clazz) {
    return register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
}

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
    ...
}    //mProc 즉 RegJNIRec에 등록된 function pointer들이 실행 된다


int AndroidRuntime::startReg(JNIEnv* env)
{
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM. (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc)
    if(register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
    ...
}

부팅하면 ART에 의해 startReg가 호출되고 struct gREGJNI에 들어간 각종 native funtion들은 register_jni_procs에 의해 모두 등록 된다


Java Application

public class MainActivity extends Activity implements SensorEventListener{
    private SensorManager mSensorManager;
    private Sensor mSensorAccelerometer;
    private float[] tGravity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

        List<Sensor> lSensor = mSensorManager.getSensorList(Sensor.TYPE_ALL);

        for(int i=0; i<lSensor.size(); i++)
        {
            Log.d("test@debug", String.format("vendor: %s, name: %s, type: %s",
                                lSensor.get(i).getVendor(), lSensor.get(i).getName(), lSensor.get(i).getStringType()));
        }

        mSensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(this, mSensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        switch(event.sensor.getType()) {
            case Sensor.TYPE_ACCELEROMETER:
            default:
                tGravity = event.values.clone();
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

Sensor.java - getType, getVendor, getName등의 method들이 정의 되어 있다



실행 - nexus 5x

vendor: Bosch, name: BMI160 accelerometer, type: android.sensor.accelerometer
vendor: Bosch, name: BMI160 gyroscope, type: android.sensor.gyroscope
vendor: Bosch, name: BMM150 magnetometer, type: android.sensor.magnetic_field
...
vendor: Google, name: Double Tap, type: com.google.sensor.double_tap
vendor: Google, name: Device Orientation, type: android.sensor.device_orientation


framework/base/core/java/android/hardware/SensorManager.java

    public boolean registerListener(SensorEventListener listener, Sensor sensor, ...) {
        return registerListenerImpl(listener, sensor, ...);
    }

onResume(), onPause()에서 호출 되는 registerListener method



frameworks/base/core/java/android/hardware/SystemSensorManager.java

    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,...) {
        ...
        Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
        queue = new SensorEventQueue(listener, looper, this);
        queue.addSensor(...)
        mSensorListeners.put(listener, queue);
        ...
    }

    private static abstract class BaseEventQueue {
        private static native int nativeEnableSensor(long eventQ, int handle ...)
        private long nSensorEventQueue;

        BaseEventQueue(Looper looper, SystemSensorManager manager) {
            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mSractch);
            ...
        }
    }

    static final class SensorEventQueue extends BaseEventQueue {
        public SensorEventQueue(SensorEventListener listener, Looper looper,
            SystemSensorManager manager) {
            super(looper, manager);
            mListener = listener;
            }
    }


looper 없으면 생성하고 SensorEvent Queue 만들고 queue에 센서를 추가한다

    public boolean addSensor(
        ...
        addSensorEvent(sensor);
        if(enableSensor(...))
    )
    private int enableSensor(
        Sensor sensor, ...) {
        return nativeEnableSensor(nSensorEventQueue, ...);
    }
static jint nativeEnableSensor(JNIEnv *env, jclass clazz, ...) {
    sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
    return receiver->getSensorEventQueue()->enableSensor(handle, ...);
}


다시 JNI로 돌아가서 확인해보면
frameworks/base/core/jni/android_hardware_SensorManager.cpp

static JNINativeMethod gBaseEventQueueMethods[] = { 
    {"nativeInitBaseEventQueue",
            "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)J",
            (void*)nativeInitSensorEventQueue },

    {"nativeEnableSensor",
            "(JIIII)I",
            (void*)nativeEnableSensor },

    {"nativeDisableSensor",
            "(JI)I",
            (void*)nativeDisableSensor },

    {"nativeDestroySensorEventQueue",
            "(J)V",
            (void*)nativeDestroySensorEventQueue },

    {"nativeFlushSensor",
            "(J)I",
            (void*)nativeFlushSensor },
}

java application test code
cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HeartRateMonitorTestActivity.java


정리

  • SensorManager = onCreate(), (SensorManager)getSystemService(SENSOR_SERVICE); : java sensor application
  • SensorManager.getSensorList - getType, getVendor, getName등 method 정의 : Sensor.java
  • registerListener - onResume() onPause() : SensorManager.java
  • registerListenerImpl - Looper per SensorEvnentListener : SystemSensorManager.java
  • static final class SensorEventQueue extends BaseEventQueue : SystemSensorManager.java
    addSensor가 호출, 부모의 method가 호출된다 - private static abstract class BaseEventQueue
    -> enableSensor -> nativeEnableSensor
  • static jint nativeEnableSensor - framework/base/core/jni/android_hardware_SensorManager.cpp
  • SensorEventQueue::enableSensor - SensorEventQueue.cpp
  • enableDiasble - frameworks/native/services/sernsorservice/SensorService.cpp
    SensorService::enable
  • SensorDevice& dev(SensorDevice::getInstance());
    singleton 기법으로 instatnce 생성
  • SensorDevice.cpp

기타

input keyevent 62
sendevent
sendevent /dev/input/event
getevent
hex 값으로 데이터 가져옴


Posted by 쵸코케키

블로그 이미지
chocokeki
쵸코케키

공지사항

Yesterday
Today
Total

달력

 « |  » 2024.5
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 달린 댓글

글 보관함