使用Android NDK和Java测试Linux驱动

简介:

在Android模拟器和Ubuntu上测试Linux驱动

三、使用AndroidNDK测试Linux驱动

     在 Android系统中Linux驱动主要的使用者是APK程序。因此,Linux驱动做完后必须要用APK程序进行测试才能说明Linux驱动可以正常使 用。由于上一节在Android虚拟机上使用C语言编写的可执行程序测试了Linux驱动,因此很容易想到可以利用Android NDK来测试Linux驱动,

由于Android NDK也使用C/C++来编写程序,因此可以利用上一节的C语言代码,当然,还得加上一些AndroidNDK特有的代码。在使用AndroidNDK测试Linux驱动之前需要做如下两件事。

1. 由于Linux驱动模块不会随Android系统启动而装载,因此必须执行build.sh脚本文件安装word_count驱动。

2. 不能使用默认方式启动Android模拟器,而要使用我们自己编译的Linux内核启动Android模拟器,启动模拟器的命令如下:

#  emulator-avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage

为了方便,读者也可以在随书光盘带的Ubuntu Linux虚拟环境中直接执行如下的命令来异步启动Android模拟器。其中emulator.sh文件在/root/drivers目录中。

# sh emulator.sh &

本节的例子已经包含在随书光盘和虚拟环境中,路径如下:

随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_ndk

虚拟环境:/root/drivers/ch06/word_count/word_count_ndk

word_count_ndk工程的代码部分由WordCountNDKTestMain.java和ndk_test_word_count.c文件组成。工程结构如图6-17所示。

       ndk_test_word_count.c文件用于访问word_count驱动。该文件包含两个供Java访问的函数,分别用来读取/dev /wordcount设备文件中的单词数和向/dev/wordcount设备文件写入字符串。下面先看看ndk_test_word_count.c文 件的完整代码。

#include <string.h> 
#include <jni.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <stdlib.h> 
//  JNI函数:readWordCountFromDev 
//  用于从/dev/wordcount设备文件读取单词数 
jint Java_mobile_android_word_count_ndk_WordCountNDKTestMain_readWordCountFromDev( 
         JNIEnv* env, jobject thiz) 
   
     int  dev;                //  open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 
     jint wordcount = 0;     //  单词数 
     unsigned char  buf[4];   //  以4个字节形式存储的单词数 
   
         //  以只读方式打开/dev/wordcount设备文件 
     dev = open( "/dev/wordcount" , O_RDONLY); 
   
     //  从dev/wordcount设备文件中读取单词数 
     read(dev, buf, 4); 
   
     int  n = 0;              //  存储单词数的int类型变量 
   
     //  将由4个字节表示的单词数转换成int类型的值 
     n = (( int ) buf[0]) << 24 | (( int ) buf[1]) << 16 | (( int ) buf[2]) << 8 | (( int ) buf[3]); 
          //  将int类型的单词数转换成jint类型的单词数 
     wordcount = (jint) n; 
     //  关闭/dev/wordcount设备文件 
     close(dev); 
     //  返回单词数 
     return  wordcount; 
//  将jstring类型的值转换成char *类型的值 
char * jstring_to_pchar(JNIEnv* env, jstring str) 
     char * pstr = NULL; 
     //  下面的代码会调用Java中的String.getBytes方法获取字符串的字节数 
   
     //  获取java.lang.String类 
     jclass clsstring = (*env)->FindClass(env, "java/lang/String" ); 
     //  将字符串“utf-8”转换成jstring类型的值 
     jstring strencode = (*env)->NewStringUTF(env, "utf-8" ); 
     //  获取java.lang.String.getBytes方法 
     jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes" , "(Ljava/lang/String;)[B" ); 
     //  调用String.getBytes方法将str变量的值转换成jbytearray类型的值 
     jbyteArray byteArray = (jbyteArray)( (*env)->CallObjectMethod(env, str, mid, strencode)); 
     //  获取字节长度 
     jsize size = (*env)->GetArrayLength(env, byteArray); 
     //  将jbytearray类型的值转换成jbyte*类型的值 
     jbyte* pbyte = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE); 
     if  (size > 0) 
    
         //  为char*类型变量pstr分配空间 
         pstr = ( char *) malloc (size); 
         //  将pbyte变量中的值复制到pstr变量中 
         memcpy (pstr, pbyte, size); 
    
     //  返回转换后的值 
     return  pstr; 
//  JNI函数:writeStringToDev 
//  用于向/dev/wordcount设备文件写入字符串 
void  Java_mobile_android_word_count_ndk_WordCountNDKTestMain_writeStringToDev( 
         JNIEnv* env, jobject thiz, jstring str) 
   
     int  dev;   //  open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 
     //  以只写方式打开/dev/wordcount设备文件 
     dev = open( "/dev/wordcount" , O_WRONLY); 
     //  将jstring类型字符串转换成char* 类型的值 
     char * pstr = jstring_to_pchar(env, str); 
     if  (pstr != NULL) 
    
         //  向/dev/wordcount设备文件写入字符串 
         write(dev,pstr, strlen (pstr)); 
    
     //  关闭/dev/wordcount设备文件 
     close(dev); 

 编写上面的代码有一个重点就是jstring_to_pchar函数。该函数可以将jstring类型的数据转换成char*类型的数据。转换的基 本思想就是调用Java方法String.getBytes,获取字符串对应的字节数组(jbyteArray)。由于write函数需要的是char *类型的数据,因此,还必须将jbyteArray类型的数据转换成char *类型的数据。采用的方法是先将jbyteArray类型的数据转换成jbyte类型的数据,然后调用memcpy函数将jbyte类型的数据复制到使用 malloc函数分配的char *指针空间中。在jstring_to_pchar函数中有如下的一行代码。

jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"];

     看到getMethodID方法最后一个参数的值是"(Ljava/lang/String;)[B",可能Android NDK初学者会对此感到困惑,以为是写错了。实际上这是JNI(Android NDK程序实际上就是遵循JNI规则的程序)对方法参数和返回类型的描述。在JNI程序中为了方便描述Java数据类型,将简单类型使用了一个大写英文字 母表示,如表6-1所示。

    除了表6-1所示的Java简单类型外,还有一些数据类型需要在JNI代码中与其对应。表6-2是这些数据类型在JNI中的描述符。

     从表6-2所示的数据类型对照关系很容易想到本例中的"(Ljava/lang/String;)[B"是什么意思。jstring_to_pchar函数调用的是如下的getBytes方法的重载形式。

public byte[] getBytes(String charsetName) throwsUnsupportedEncodingException

在JNI中调用Java方法需要指定方法参数和返回值的数据类型。在JNI中的格式如下:

"(参数类型)返回值类型"

     getBytes方法的参数类型是String,根据表6-2的描述,String类型中JNI在的描述符是"Ljava/lang/String; "。getBytes方法的返回值类型是byte[]。这里就涉及到一个数组的表示法。在JNI中数组使用左中括号([]表示,后面是数组中元素的类型。 每一维需要使用一个“[”。byte[]是一维字节数组,所以使用"[B"表示。如果是byte[][][],应使用"[[[B"表示。如果Java方法 未返回任何值(返回值类型是void),则用V表示。如void mymethod(int value)的参数和返回值类型可表示为"(I)V"。

Android NDK程序还需要一个Android.mk文件,代码如下:

LOCAL_PATH := $(call my-dir) 
   
include $(CLEAR_VARS) 
   
LOCAL_MODULE    := ndk_test_word_count 
LOCAL_SRC_FILES := ndk_test_word_count.c 
   
include $(BUILD_SHARED_LIBRARY) 

 

在编写Java代码调用JNI函数之前,先看一下本例的界面,如图6-18所示。

       读者需要先在PC上运行build.sh脚本文件安装word_count驱动。然后单击“从/dev/wordcount读取单词数”按钮,会在按钮下 方输出当前/dev/wordcount设备文件中统计出的单词数。读者也可以在输入框中输入一个由空格分隔的字符串,然后单击“向/dev /wordcount写入字符串”按钮,再单击“从/dev/wordcount读取单词数”按钮,就会统计出字符串中包含的单词数,效果如图6-19所 示。

下面看一下本例中Java部分(WordCountNDKTestMain.java)的完整代码。

package  mobile.android.word.count.ndk; 
   
import  android.app.Activity; 
import  android.os.Bundle; 
import  android.view.View; 
import  android.widget.EditText; 
import  android.widget.TextView; 
import  android.widget.Toast; 
   
public  class  WordCountNDKTestMain extends  Activity 
     private  TextView tvWordCount; 
     private  EditText etString; 
   
     @Override 
     public  void  onCreate(Bundle savedInstanceState) 
    
         super .onCreate(savedInstanceState); 
         setContentView(R.layout.main); 
         tvWordCount = (TextView) findViewById(R.id.textview_wordcount); 
         etString = (EditText) findViewById(R.id.edittext_string); 
    
     //  “从/dev/wordcount读取单词数”按钮的执行代码 
     public  void  onClick_ReadWordCountFromDev(View view) 
    
         //  显示单词数 
         tvWordCount.setText( "单词数:"  + String.valueOf(readWordCountFromDev())); 
    
// “向/dev/wordcount写入字符串”按钮的执行代码 
     public  void  onClick_WriteStringToDev(View view) 
    
         //  向/dev/wordcount设备文件写入字符串 
         writeStringToDev(etString.getText().toString()); 
         Toast.makeText( this , "已向/dev/wordcount写入字符串" , Toast.LENGTH_LONG).show(); 
    
   
     // native方法 
     public  native  int  readWordCountFromDev(); 
     public  native  void  writeStringToDev(String str); 
     static 
    
         System.loadLibrary( "ndk_test_word_count" ); 
    

 

WordCountNDKTestMain.java中的代码只是简单地调用了JNI函数来操作/dev/wordcount文件。其他的代码都是常规的Android应用级别的代码。如果读者对这部分不熟悉,可以参阅笔者所著的《Android开发权威指南》。

四、使用Java代码直接操作设备文件来测试Linux驱动

      如果Android拥有root权限,完全可以直接使用Java代码操作/dev/wordcount设备文件(没有root权限,Linux驱动模块是 无法安装的)。本节将介绍如何使用Java代码来测试Linux驱动(测试程序不使用一行C/C++代码)。本节示例的路径如下:

随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_java

虚拟环境:/root/drivers/ch06/word_count/word_count_java

word_count_java工程中只有一个源代码文件WordCountJavaTestMain.java。该文件的内容如下:

package  mobile.android.word.count.java; 
   
import  java.io.FileInputStream; 
import  java.io.FileOutputStream; 
import  android.app.Activity; 
import  android.os.Bundle; 
import  android.view.View; 
import  android.widget.EditText; 
import  android.widget.TextView; 
import  android.widget.Toast; 
   
public  class  WordCountJavaTestMain extends  Activity 
     private  TextView tvWordCount; 
     private  EditText etString; 
     @Override 
     public  void  onCreate(Bundle savedInstanceState) 
    
         super .onCreate(savedInstanceState); 
         setContentView(R.layout.main); 
         tvWordCount = (TextView) findViewById(R.id.textview_wordcount); 
         etString = (EditText) findViewById(R.id.edittext_string); 
    
     //  “从/dev/wordcount读取单词数”按钮的执行代码 
     public  void  onClick_ReadWordCountFromDev(View view) 
    
         //  显示单词数 
         tvWordCount.setText( "单词数:"  + String.valueOf(readWordCountFromDev())); 
    
// “向/dev/wordcount写入字符串”按钮的执行代码 
     public  void  onClick_WriteStringToDev(View view) 
    
         //  向/dev/wordcount设备文件写入字符串 
         writeStringToDev(etString.getText().toString()); 
         Toast.makeText( this , "已向/dev/wordcount写入字符串" , Toast.LENGTH_LONG).show(); 
    
     //  下面是用Java实现的操作/dev/wordcount设备文件的代码 
   
     //  读取/dev/wordcount设备文件中的单词数 
     private  int  readWordCountFromDev() 
    
         int  n = 0
         byte [] buffer = new  byte [ 4 ]; 
         try 
        
             //  打开/dev/wordcount设备文件 
             FileInputStream fis = new  FileInputStream( "/dev/wordcount" ); 
             //  从设备文件中读取4个字节  
             fis.read(buffer); 
             //  将4个字节转换成int类型的值 
             n = (( int ) buffer[ 0 ]) << 24  | (( int ) buffer[ 1 ]) << 16 
                     | (( int ) buffer[ 2 ]) << 8  | (( int ) buffer[ 3 ]);  
             fis.close(); 
        
         catch  (Exception e) 
        
        
         return  n; 
    
    //  向/dev/wordcount设备文件中写入字符串 
     private  void  writeStringToDev(String str) 
    
         try 
        
             //  打开/dev/wordcount设备文件 
             FileOutputStream fos = new  FileOutputStream( "/dev/wordcount" ); 
             //  写入字符串 
             fos.write(str.getBytes( "iso-8859-1" )); 
             fos.close(); 
        
         catch  (Exception e) 
        
        
    

本例的运行效果和使用方法与上一节的例子类似。读者可以运行随书光盘或虚拟环境中的例子与上一节的例子进行比较。


本文节选至《Android深度探索(卷1):HAL与驱动开发》, 接下来几篇文章将详细阐述如何开发ARM架构的Linux驱动,并分别利用android程序、NDK、可执行文件测试Linux驱动。可在ubuntu Linux、Android模拟器和S3C6410开发板(可以选购OK6410-A开发板,需要刷Android)

本文转自银河使者博客园博客,原文链接http://www.cnblogs.com/nokiaguy/archive/2013/03/18/2966996.html如需转载请自行联系原作者


银河使者

相关文章
|
19小时前
|
iOS开发 MacOS Windows
|
19小时前
|
Linux 芯片 Ubuntu
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯
|
19小时前
|
Ubuntu Linux
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-2
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-2
|
19小时前
|
Linux 芯片
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-1
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯
Linux驱动入门 —— 利用寄存器操作GPIO进行LED点灯-1
|
19小时前
|
Linux C语言 Ubuntu
Linux驱动入门——编写第一个驱动
Linux驱动入门——编写第一个驱动
Linux驱动入门——编写第一个驱动
|
19小时前
|
Linux 测试技术 Windows
LabVIEW对NI Linux RT应用程序性能进行基准测试
LabVIEW对NI Linux RT应用程序性能进行基准测试
|
19小时前
|
消息中间件 测试技术 Linux
linux实时操作系统xenomai x86平台基准测试(benchmark)
本文是关于Xenomai实时操作系统的基准测试,旨在评估其在低端x86平台上的性能。测试模仿了VxWorks的方法,关注CPU结构、指令集等因素对系统服务耗时的影响。测试项目包括信号量、互斥量、消息队列、任务切换等,通过比较操作前后的时戳来测量耗时,并排除中断和上下文切换的干扰。测试结果显示了各项操作的最小、平均和最大耗时,为程序优化提供参考。注意,所有数据基于特定硬件环境,测试用例使用Alchemy API编写。
13 0
linux实时操作系统xenomai x86平台基准测试(benchmark)
|
19小时前
|
Java API 开发工具
java与Android开发入门指南
java与Android开发入门指南
15 0
|
19小时前
|
Linux
linux驱动层输出dev_dbg打印信息
linux驱动层输出dev_dbg打印信息
18 0
|
19小时前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
28 3
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库