android jni调用ioctl实现

系统源码版本:android5.1
ndk版本:android-ndk-r17
Android Studio版本: 3.2
硬件:核心板为64bit

需求:屏幕供应商提供升级程序cpp文件源码,操作/dev/i2c-1,调用ioctl读写数据。需要编写App,调用cpp源码相关接口,App目标平台为Android P,cpp源码与Android平台无关。由于cpp源码平台无关,因此可以采用android 5.1源码的头文件来支持jni so的编译。这样App运行到Android P上运行就不会出问题。
注意:如果cpp源码中引用了某些头文件中定义的宏,而Android P的定义与Android 5.1如果宏定义值不一致,这将会出现问题,因为宏定义在编译时就会进行替换。

方案一:NDK直接编译:

失败方案,cpp源码引用的头文件较少时可以使用。详细的编写过程,参见方案二。
1. Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := libScreenUpdateJni
LOCAL_SRC_FILES := screen_update.cpp \
                   core.cpp \
                   update.cpp \
                   i2c.cpp
LOCAL_LDLIBS := -lm -llog
LOCAL_CFLAGS += -I/home/xxx/sourcecode/frameworks/native/include \
                -I/home/xxx/sourcecode/system/core/include
#include下添加其他的.h文件
#LOCAL_C_INCLUDES += 与LOCAL_CFLAGS += -I效果类似
LOCAL_C_INCLUDES += \
	$(LOCAL_PATH) \
	$(LOCAL_PATH)/include

#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)

2.Application.mk:

APP_MODULES := libScreenUpdateJni
APP_ABI := armeabi-v7a
APP_PLATFORM := android-21

3.build.gradle:

  defaultConfig {  
        ndk {
            abiFilters "armeabi-v7a"
        }
    }

    // ndk-build模式
    externalNativeBuild {
        ndkBuild {
            // Provides a relative path to your ndkBuild script.
            path file("src/main/jni/Android.mk")
        }
}

这种便捷方式编译失败,原因为system/core/include下的头文件与ndk中的头文件有冲突,导致某些struct变量定义冲突。解决这些冲突,需要从源码中提取出所有cpp源码include的头文件,由于源码引用的头文件非常多,因此这个工作比较繁琐,放弃此方案。
直接采用方案二。

方案二:
Jni so由源码base中编译出,导入Android Studio中load调用。

1.编写java文件:
ScreenUpdateImpl.java:

package com.neu.screenupdate;

public class ScreenUpdateImpl {
    public native int SynapticsRmi4Init();
    public native int SynapticsRmi4FwuUpdater(String PathFwImage,int ImageFwId);
    static {
        System.loadLibrary("ScreenUpdateJni");
    }
}

2.生成jni头文件:
在终端中生成jni头文件:

cd screenupdate/src/main/java/com/neu/screenupdate
javac ScreenUpdateImpl.java  //这将生成对应的ScreenUpdateImpl.class
cd screenupdate/src/main
javah -d jni -classpath ./java com.neu.screenupdate.ScreenUpdateImp
//这将生成com_neu_screenupdate_ScreenUpdateImpl.h

3.编写cpp文件
将com_neu_screenupdate_ScreenUpdateImpl.h文件内容拷贝至screenupdate.cpp:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include "synaptics_tp_i2c.h"
#include "synaptics_rmi4_fw_update.h"


/* Header for class com_neu_screenupdate_ScreenUpdateImpl */

#ifndef _Included_com_neu_screenupdate_ScreenUpdateImpl
#define _Included_com_neu_screenupdate_ScreenUpdateImpl
#ifdef __cplusplus
#define TAG "screenupdatejni" // 这个是自定义的LOG的标识  
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型   
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型  
extern "C" {
#endif

char* jstringToChar(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}


/*
 * Class:     com_neu_screenupdate_ScreenUpdateImpl
 * Method:    SynapticsRmi4Init
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_neu_screenupdate_ScreenUpdateImpl_SynapticsRmi4Init
        (JNIEnv * env, jobject object){
    return 0;
}
/*
 * Class:     com_neu_screenupdate_ScreenUpdateImpl
 * Method:    SynapticsRmi4FwuUpdater
 * Signature: (Ljava/lang/String;I)I
 */
JNIEXPORT jint JNICALL Java_com_neu_screenupdate_ScreenUpdateImpl_SynapticsRmi4FwuUpdater
        (JNIEnv * env, jobject object, jstring path, jint id){
    LOGD("SynapticsRmi4FwuUpdater jni called");
   
    return 0;
}


#ifdef __cplusplus
}
#endif

4.Android.mk:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := libScreenUpdateJni
LOCAL_SRC_FILES := screen_update.cpp \
                   core.cpp \
                   update.cpp \
                   i2c.cpp
LOCAL_LDLIBS := -llog
#下面注释掉,因为源码base的编译环境中已经有这些include path。
#LOCAL_CFLAGS += -I/home/z/framework/frameworks/native/include \
                -I/home/z/framework/system/core/include
LOCAL_C_INCLUDES += \
        $(LOCAL_PATH) \
        $(LOCAL_PATH)/include

#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)

5.Android Studio配置
第4步中会生成/system/lib/libScreenUpdateJni.so
/system/lib64/libScreenUpdateJni.so
在App src的同级目录建立libs文件夹,建立arm64-v8a及armeabi文件夹,分别拷贝64bit及32bit的对应so至对应的文件夹。
Build.gradle:

 //默认为jniLibs文件夹,其他路径需要指定。
    sourceSets.main{
        jniLibs.srcDirs = ['libs']
    }

MainActivity.java:

package com.neu.screenupdate;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

    private static final String TAG = "screenupdate";
    private ScreenUpdateImpl mScreenUpdate;
    private String mImagePath = "screen_update.img";
    private static final int SCREEN_UPDATE = 1;

    HandlerThread handlerThread = new HandlerThread("screenupdate");
    Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mScreenUpdate = new ScreenUpdateImpl();
        //mScreenUpdate.SynapticsRmi4Init();
        Button updateBtn = (Button) findViewById(R.id.screenupdate);
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case SCREEN_UPDATE:
                        int ret = mScreenUpdate.SynapticsRmi4FwuUpdater(mImagePath,0);
                        Log.d(TAG, "SynapticsRmi4FwuUpdater ret:" + ret);
                        break;
                    default:
                        break;
                }
            }
        };
        updateBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mHandler.sendEmptyMessage(SCREEN_UPDATE);
            }
        });
    }
}

升级过程耗时,需要handlerthread支持。
App添加shareduserid,platform签名,保证system权限。
至此已经完成调用。

6.调试问题
权限:
/dev/i2c-1设备文件owner为root,权限600,system权限的app也无法操作。
open时的errno为-13,权限拒绝。
正常需要写有root权限的service端,客户端跨进程调用ioctl才可以。
chmod 777 /dev/i2c-1暂且解决

ioctl失败:
第一次生成app时测试,在权限设置777后,open能够成功,但是ioctl失败,原因为那次app只是使用了32bit的so,而核心板及板载系统均为64bit。正常的情况下,对Ioctl的调用,会走unlock_ioctl,但在32位系统64位的内核上面会走compat_ioctl接口,很可能是这个原因导致ioctl失败。
通过showmap pid查看App进程调用的so,发现确实是32bit so的问题。将arm64的so添加重新生成App,此时showmap可以看到调用的so的dex都是arm64的。此时调用成功。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android JNI是一种可用于在Java代码中调用C/C++代码的技术,驱动ko则是指内核模块文件,通常用于实现与硬件设备的交互。在Android应用中,我们可以使用JNI调用驱动ko,实现与底层硬件的交互。 要在Android应用中调用驱动ko,首先需要在应用中使用JNI技术编写C/C++代码,实现与驱动ko的交互逻辑。在C/C++代码中,我们可以通过使用Linux系统提供的API来访问并且控制驱动ko。通过JNI,我们可以将C/C++代码嵌入到Java代码中,并在Android应用中调用这些代码。 具体来说,调用驱动ko的过程如下: 1. 在JNI层编写与驱动ko交互的C/C++代码。这些代码可以使用Linux系统提供的ioctl函数等API来与驱动ko进行通信,并发送相应的指令和数据。 2. 在Java层编写对应的JNI接口和方法。这些方法将与JNI层的C/C++代码进行绑定,以便在Java代码中调用这些方法来间接调用驱动ko。 3. 在Java代码中加载JNI库,并调用JNI接口方法。加载JNI库可以使用System.loadLibrary方法,然后在Java代码中就可以调用JNI接口方法,进而间接调用驱动ko。 通过这种方式,我们可以在Android应用中通过JNI技术调用驱动ko,实现与底层硬件的交互。注意,使用JNI调用驱动ko需要确保设备具备相应的权限,否则可能会出现权限不足的错误。同时,也需要确保驱动ko文件存在于系统中,并且可以被应用访问到。 总之,使用Android JNI可以实现在应用中调用驱动ko文件的功能,通过编写C/C++代码并与Java代码进行绑定,实现与底层硬件交互的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值