Tiny4412——Android访问硬件的方法

版权声明:本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,欢迎转载,但转载请注明来自hceng blog(www.hceng.cn),并保持转载后文章内容的完整。本人保留所有版权相关权利。 https://blog.csdn.net/hceng_linux/article/details/89944985

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2019/03/15/Tiny4412——Android访问硬件的方法/#more

2019年,正式学习Android驱动。
以LED为切入点,学习Android访问硬件的方法。

之前写第一个Linux下的LED驱动时候,提到:Linux驱动 = 裸机 + 框架
这个思维在写Linux驱动的过程中,得到了充分验证。
如今,学习Android驱动,开始验证:Android驱动 = Linux驱动 + 框架

1. 环境搭建

开发环境主要包含:
1.一个Linux主机(虚拟机),在该主机里编译Linux内核和Android源码等;
2.一个Windows主机,在该主机里编写Android APP,并连接开发板调试;

1.1 搭建Ubuntu虚拟机

前面在学习Java时,已经安装了Ubuntu18.04的虚拟机,这里继续使用该虚拟机。

值得一提的是,虚拟机的内存给多一点,虚拟机参考配置:内存6G,硬盘100G,CPU 8核
在编译内核源码的过程中,发现编译太慢,一般挂一晚上都能编译好,之前内存给小了,编译了一天一夜也没好,后来改大了内存后,一会就编译完了。

1.2 编译Kernel和Android

以下所有安装包都是从百问网云盘下载

1、安装交叉编译工具链
下载arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz并解压。
修改/etc/environment,将解压的交叉编译工具链加在末尾:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/work/tools/toolschain/4.5.1/bin"

执行source /etc/environment更新环境变量。
此时执行arm-linux-gcc -v,打印出gcc version 4.5.1 (ctng-1.8.1-FA)即OK。

2、编译Kernel
下载linux-3.0.86-20150324.tgz并解压。
进入内核源码根目录执行:

cp tiny4412_android_defconfig .config
make zImage

最后生成/work/linux_source/linux-3.0.86/arch/arm/boot/zImage

3、编译Android
下载android-5.0.2-fs-20150325.tar.gz并解压。
进入Android源码根目录执行:

. setenv
lunch

在选择界面:

You're building on Linux

Lunch menu... pick a combo:
     1. aosp_arm-eng
     2. aosp_arm64-eng
     3. aosp_mips-eng
     4. aosp_mips64-eng
     5. aosp_x86-eng
     6. aosp_x86_64-eng
     7. mini_emulator_x86-userdebug
     8. m_e_arm-userdebug
     9. mini_emulator_arm64-userdebug
     10. mini_emulator_mips-userdebug
     11. mini_emulator_x86_64-userdebug
     12. aosp_hammerhead-userdebug
     13. aosp_mako-userdebug
     14. full_tiny4412-userdebug
     15. full_tiny4412-eng
     16. aosp_shamu-userdebug
     17. aosp_deb-userdebug
     18. aosp_flo-userdebug
     19. aosp_grouper-userdebug
     20. full_fugu-userdebug
     21. aosp_fugu-userdebug
     22. aosp_tilapia-userdebug
     23. aosp_manta-userdebug

Which would you like? [aosp_arm-eng] 

输入15,即选择full_tiny4412-eng
再执行make –j8,等待几个小时,编译完成后执行./gen-img.sh生成/work/android_source/android-5.0.2/system.img.

当然编译过程不可能是那么一帆风顺的,实际编译Android源码过程中遇到的问题有:
①JDK版本过高,重新安装openjdk-7-jdk参考解决链接
②编译报错aidl_language_l参考解决链接
③编译报错error: unsupported reloc 43参考解决链接
④制作映像文件出错mkimage: command not found,执行sudo apt-get install u-boot-tools安装即可。

4、烧写
使用厂家提供的MiniTools
zImagesystem.img分别烧写到Android KernelAndroid RooFs/System Image

1.3 安装JDK(Windows)

因为要在Windows上使用Android Studio编写APP,所以在Windows上也需要安装Java JDK(Java Development Kit)。
通过Oracle官网下载最新的JDK:

安装好后,将JDK安装路径加入Windows的环境变量:

最后在CMD里验证:

1.4 安装Android Studio

首先从官网下载Android Studio。

点击安装,会弹出首次运行没有Android SDK,点击“Cancel”。

然后选择"Custom"以自定义SDK安装路径。在弹出的界面勾选上"Android Virtual Device",再指定SDK的安装路径。

此时会下载Android SDK和AVD,两个文件都很大,为了C盘节省空间,应用界面只指定了SDK的安装路径在D盘,还需手动指定AVD的安装路径,这个需要修改环境变量设置。

然后,等待一段时间就安装完了。

1.5 Android Studio的常用快捷键

参考博客:Android Studio2.0 教程从入门到精通MAC版 - 提高篇

2. Android应用程序

我的目的是写一个Android应用程序,通过该应用程序控制开发板上的LED灯。
因此首先得设计一个APP,该APP有一个总开关控制所有灯的亮灭,每个灯又有单独的开关控制亮灭。

2.1 创建APP工程

打开Android Studio,选择"Start a new Android Studio project"创建一个新APP工程。
然后选择"Empty Activity"空主界面,最后设置APP信息、保存路径、兼容API版本。

等待自动创建完成后,会自动生成工程文件,其中有两个文件后面常用:

  • app\src\main\res\layout\activity_main.xml,界面控件布局文件,既可通过图形界面设计控件,也可直接编辑xml;
  • app\src\main\java\cn\hceng\led\MainActivity.java,实现控件各种具体功能,逻辑关系。

2.2 常用控件介绍

2.2.1 TextView

TextView是文本视图,用来显示文字的。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.标签<TextView />代表着要在Activity中添加一个TextView,标签中可以设置一些属性;
2.**android:id代表TextViewID,也就是TextView的唯一标识;在java代码中我们可以通过findViewById()方法来通过ID获取控件;上述控件的唯一IDTEXT
3.
android:layout_width代表控件的宽度,该属性的值是match_parent,表示该控件的宽度与父视图的宽度相同;
4.
android:layout_height代表控件的高度,该属性的值是wrap_content,表示控件的高度根据内容的高度进行改变;
5.
android:gravity代表文字对齐方式,该属性的值是center,表示居中显示;
6.
android:text代表文字内容,该属性的值是hceng first APP!,表示显示这串字符;
7.
android:textColor代表文字的颜色,该属性的值是#008577,表示16进制色值的绿色;
7.
android:textSize代表文字的大小,该属性的值是25sp,长度宽度的数值用dp,字体的大小用sp
7.
android:textStyle**代表文字的样,normalbolditalic分别为正常,加粗、斜体,默认为normal

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.TextView;

      TextView myTextView = (TextView) findViewById(R.id.TEXT);
      String myText = myTextView.getText().toString();
      myTextView.setText(myText+"  Add String");
    

{% endcodeblock %}

2.2.2 Button

Button是按钮,用来控制某个目的的开关。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.Button标签里面的属性和前面TextView基本类似,这里就不赘述了;

  • 代码控制:
    Activity的类中也是使用findViewById()来通过ID获取该按钮。
    获取按钮后需要给按钮绑定点击事件,也就是点击按钮要做的事情,常用的方法有四种:布局文件指定响应函数直接匿名内部类新建子类实现在当前类实现接口

布局文件指定响应函数

在布局文件的<Button />标签里,添加android:onClick="onClick"MainActivity.java里就只需要实现onClick()方法即可。

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
}

public void onClick(View v) {
    //Todo
}

}
{% endcodeblock %}

直接匿名内部类:

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);

    button.setOnClickListener(new View.OnClickListener() {
        public void onClick (View v){
            //Todo
        }
    });
}

}
{% endcodeblock %}

新建子类实现:

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(new MyButtonListener());
}

class MyButtonListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
        //Todo
    }
}

}
{% endcodeblock %}

在当前类实现接口

{% codeblock lang:java %}
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;

public class MainActivity extends AppCompatActivity implements OnClickListener { //注意这里实现接口

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    //Todo
}

}
{% endcodeblock %}

参考博客:Android代码规范----按钮单击事件的四种写法

2.2.3 CheckBox

CheckBox是复选框,用来多项选择。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

  • 分析:

1.CheckBox标签里面的属性和前面也基本类似;
2.android:onClick代表控件的响应函数,该属性的值是onCheckboxClicked,表示操作该控件将调用onCheckboxClicked()方法;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.CheckBox;
    import android.widget.Toast;

    CheckBox checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);

    public void onCheckboxClicked(View v) {
    boolean checked = ((CheckBox) v).isChecked();

      switch (v.getId()) {
          case R.id.LED1:
              if (checked) {
                  Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
              } else {
                  Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
              }
              break;
          case R.id.LED2:
              //Todo
              break;
      }
    

    }
    {% endcodeblock %}

在XML里,多个CheckBoxandroid:onClick都是onCheckboxClicked,也就是任意一个CheckBox被点击,都会调用到onCheckboxClicked()方法。
因此,需要使用getId()获取是按的哪一个CheckBox,再根据isChecked()的状态,进行相应操作。

2.2.4 EditText

EditText是输入框,用来接收用户输入的数据。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**android:hint代表占提示字符串,用来提示用户输入内容;
2.
android:macLines**表示显示的最多行数;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.EditText;
    import android.widget.Toast;

      EditText myEditText = (EditText) findViewById(R.id.EDIT_TEXT);
      String inputText = myEditText.getText().toString();
      Toast.makeText(MainActivity.this, inputText, Toast.LENGTH_SHORT).show();
    

{% endcodeblock %}

一般EditText还需要监控键盘回车键,或者和Button搭配,从而判断用户输完了数据,再获取数据。

2.2.5 ImageView

ImageView是图片显示框,用来在界面上显示贴图。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**android:src**代表图片资源的,使用@xx来引用,图片一般放在res/drawable里面;

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.ImageView;

      ImageView imageView = (ImageView) findViewById(R.id.IMAGEVIEW);
      imageView.setImageResource(R.drawable.p2);
    

{% endcodeblock %}

2.2.6 ProgressBar

ProgressBar是进度条,用来展示当前进度。

  • xml创建:
    {% codeblock lang:xml %}

    {% endcodeblock %}

1.**style用来指定进度条样式,默认是转动的圆,这里是水平的一根横线表示进度条;
2.
android:max表示进度条的最大值;
3.
android:visibility表示是否可见进度条,visible是可见,invisible是不可见,gone是隐藏,即不保留控件所占空间;
4.
android:progress**表示进度条的当前值(初始值);

  • 代码控制:
    {% codeblock lang:java %}
    import android.widget.ProgressBar;

      ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.PROGRESSBAR);
      myProgressBar.setProgress(myProgressBar.getProgress()+10);
    
      if (myProgressBar.getProgress() == myProgressBar.getMax()) {
          //myProgressBar.setVisibility(View.GONE);
          myProgressBar.setProgress(0);
      } else {
          myProgressBar.setVisibility(View.VISIBLE);
      }
    

{% endcodeblock %}

1、通过findViewById()方法找到指定ProgressBar
2、通过getProgress()方法获取当前进度值,setProgress()方法设置当前进度值;
3、通过getMax()方法获取xml设置的最大值;
4、通过setVisibility()方法设置进度条是否可见,VISIBLEINVISIBLEGONE含义同上;

2.2.7 AlterDialog

AlterDialog是警告对话框,用来展示警告信息和简单的交互。

  • 代码创建:
    {% codeblock lang:java %}
    import android.widget.Toast;
    import android.app.AlertDialog;
    import android.content.DialogInterface;

      AlertDialog.Builder alterDialog = new AlertDialog.Builder(MainActivity.this);
      alterDialog.setTitle("Warning");
      alterDialog.setMessage("Warning content");
      alterDialog.setCancelable(false);
      alterDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              Toast.makeText(MainActivity.this, "yes", Toast.LENGTH_SHORT).show();
          }
      });
    
      alterDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              Toast.makeText(MainActivity.this, "no", Toast.LENGTH_SHORT).show();
          }
      });
    
      alterDialog.show();
    

{% endcodeblock %}

1、AlterDialog是通过Builder进行创建,在创建的时候会指定该AlterDialog在那个Activity上进行显示;
2、通过setTitle()方法设置标题,通过setMessage()设置内容;
3、setCancelable()方法,表示弹出的AlterDialog在用户点击返回键是否消失,默认是true,这里设置为false,表示点击返回键不消失;
4、setPositiveButton()方法是设置点击“确定”按钮时的事件,setNegativeButton是设置点击“取消”按钮的事件;
5、show()方法弹出AlterDialog

2.2.8 ProgressDialog

ProgressDialog是进度提示框,即在AlterDialog上添加Progress

  • 代码创建:
    {% codeblock lang:java %}
    import android.app.ProgressDialog;

      ProgressDialog myProgressDialog = new ProgressDialog(MainActivity.this);
    
      myProgressDialog.setTitle("ProgressDialog");
      myProgressDialog.setMessage("Loading……");
      myProgressDialog.setCancelable(true);
    
      myProgressDialog.show();
    

{% endcodeblock %}

2.3 四种布局介绍

Android中的布局方式有四种:线性布局(LinearLayout)、相对布局(RelativeLayout)、帧布局(FrameLayout)、表格布局(TableLayout)。

2.3.1 线性布局

线性布局是最常用的布局之一,如名字一样,将控件在线性方向上依次排列。
既然是线性,那么就可能是水平线性horizontal,也可以是垂直线性vertical

  • xml示例:
    {% codeblock lang:xml %}
    <LinearLayout
    android:layout_width=“match_parent”
    android:layout_height=“match_parent”
    android:orientation=“vertical” >

      <LinearLayout <!--LinearLayout 1.1-->
          android:layout_width="match_parent"
          android:layout_height="960px"
          android:background="#005A5B" >
    
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="A"
              android:textSize="100dp"/>
      </LinearLayout>
    
      <LinearLayout <!--LinearLayout 1.2-->
          android:layout_width="match_parent"
          android:layout_height="960px"
          android:orientation="horizontal" >
    
          <LinearLayout <!--LinearLayout 1.2.1-->
              android:layout_width="540px"
              android:layout_height="match_parent"
              android:background="#008C72" >
    
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="B"
                  android:textSize="100dp"/>
          </LinearLayout>
    
          <LinearLayout <!--LinearLayout 1.2.2-->
              android:layout_width="540px"
              android:layout_height="match_parent"
              android:background="#02A676" >
    
              <TextView
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="C"
                  android:textSize="100dp"/>
          </LinearLayout>
      </LinearLayout>
    

{% endcodeblock %}

  • 效果:

示例实现了三个色块,最外层的LinearLayout 1是整个区域,它指定内部按垂直排列。
然后里面的两个LinearLayout 1.1LinearLayout 1.2就按垂直的上下排列。
再在LinearLayout 1.2里又指定水平排列,里面的LinearLayout 1.2.1LinearLayout 1.2.2就左右排列。

2.3.2 相对布局

相对布局可以根据已经固定的控件来确定其它新加控件的位置。

  • xml示例:
    {% codeblock lang:xml %}
<?xml version="1.0" encoding="utf-8"?>

<Button
    android:id="@+id/button_center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="center" />

<Button
    android:id="@+id/button_above"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/button_center"
    android:layout_centerInParent="true"
    android:text="above" />

<Button
    android:id="@+id/button_below"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/button_center"
    android:layout_centerInParent="true"
    android:text="below" />

<Button
    android:id="@+id/button_left"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_toLeftOf="@+id/button_center"
    android:text="left" />

<Button
    android:id="@+id/button_right"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_toRightOf="@+id/button_center"
    android:text="right" />
{% endcodeblock %}
  • 效果:

示例实现了五个按键,先确定最中间button_center
然后使用layout_abovelayout_belowlayout_toLeftOflayout_toRightOf分别在其上下左右。
其中layout_centerVertical作用是将控件置于父控件的中心位置。

2.3.3 帧布局

帧布局就是以屏幕左上角为坐标原点,指定每个控件的大小,后加进来的控件覆盖前面的控件。

  • xml示例:
    {% codeblock lang:xml %}






    {% endcodeblock %}

  • 效果:

示例实现了五个色块,都以左上角为原点,设置不同的大小,实现层级覆盖的效果。

2.3.4 表格布局

在表格布局中,整个页面就相当于一张大的表格,控件就放在每个表格中。

  • xml示例:
    {% codeblock lang:xml %}

      <TableRow>
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#00BF80">
          </LinearLayout>
    
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#298A6A">
          </LinearLayout>
      </TableRow>
    
      <TableRow>
          <LinearLayout
              android:layout_width="75pt"
              android:layout_height="75pt"
              android:background="#00734D"
              android:layout_span="2">
          </LinearLayout>
      </TableRow>
    

{% endcodeblock %}

  • 效果:

示例实现了三个色块,先用TableLayout圈出整个表格,stretchColumns表示第N列自动扩充,可以看到上面的两个色块虽然设置的大小一样,但第0列的比第1列的大,就是因为第0列的自动扩充占据了两个剩余的部分。
TableRow代表一行,在标签里可以根据需求设置多个控件,
再用TableRow,表示继续下一行,layout_span属性表示该控件占两个表格。

参考博客:Android开发之基本控件和详解四种布局方式

2.4 设计的APP

有了上面的基础知识,基本就可以编写APP了。

2.4.1 布局设计

布局比较简单,采用线性布局,里面有6个控件:1个TextView显示字符、1个Button作为所有LED总开关、4个CheckBox分别控制每一个LED。
{% codeblock lang:xml %}

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android
xmlns:app=“http://schemas.android.com/apk/res-auto
xmlns:tools=“http://schemas.android.com/tools
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=".MainActivity">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/TEXT"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginTop="30dp"
        android:text="Android access hardware"
        android:textColor="#008577"
        android:textSize="25sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/BUTTON"
        android:layout_width="50pt"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_gravity="center"
        android:text="ALL ON" />

    <CheckBox
        android:id="@+id/LED1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED1" />

    <CheckBox
        android:id="@+id/LED2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED2" />

    <CheckBox
        android:id="@+id/LED3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED3" />

    <CheckBox
        android:id="@+id/LED4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onCheckboxClicked"
        android:text="LED4" />

</LinearLayout>

</android.support.constraint.ConstraintLayout>
{% endcodeblock %}

2.4.2 代码操作

代码的操作逻辑也很简单,先是获取每个按键的ID,然后绑定button按键监听和chekbox按键监听。
在监听的函数里,进行相应的逻辑控制。这里展示屏蔽了所有的硬件操作,硬件操作在后面再添加。

{% codeblock lang:java %}
package cn.hceng.led;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;

import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

private boolean ledStatus = false;

private Button button = null;
private CheckBox checkBoxLed1 = null;
private CheckBox checkBoxLed2 = null;
private CheckBox checkBoxLed3 = null;
private CheckBox checkBoxLed4 = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initView();
}

private void initView() {
    checkBoxLed1 = (CheckBox) findViewById(R.id.LED1);
    checkBoxLed2 = (CheckBox) findViewById(R.id.LED2);
    checkBoxLed3 = (CheckBox) findViewById(R.id.LED3);
    checkBoxLed4 = (CheckBox) findViewById(R.id.LED4);

    button = (Button) findViewById(R.id.BUTTON);
    button.setOnClickListener(new MyButtonListener());
}

class MyButtonListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
        ledStatus = !ledStatus;

        if (ledStatus) {
            button.setText("ALL OFF");
            checkBoxLed1.setChecked(true);
            checkBoxLed2.setChecked(true);
            checkBoxLed3.setChecked(true);
            checkBoxLed4.setChecked(true);
        } else {
            button.setText("ALL  ON");
            checkBoxLed1.setChecked(false);
            checkBoxLed2.setChecked(false);
            checkBoxLed3.setChecked(false);
            checkBoxLed4.setChecked(false);
        }
    }
}

public void onCheckboxClicked(View view) {
    boolean checked = ((CheckBox) view).isChecked();

    switch (view.getId()) {
        case R.id.LED1:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED1 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED1 off", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.LED2:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED2 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED2 off", Toast.LENGTH_SHORT).show();
            }
            break;

        case R.id.LED3:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED3 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED3 off", Toast.LENGTH_SHORT).show();
            }
            break;

        case R.id.LED4:
            if (checked) {
                Toast.makeText(getApplicationContext(), "LED4 on", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "LED4 off", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            break;
    }
}

}
{% endcodeblock %}

3. Android访问硬件的方法

Android APP采用Java编写的,Java不能直接访问硬件,因此Android APP访问硬件只能两种方式:通过JNI直接访问Android硬件访问服务

3.1 通过JNI直接访问

驱动层
和Linux驱动完全一样,编写驱动,向上提供/dev/leds节点;
C库
使用C/C++操作/dev/leds,将C库函数名和Java函数名建立映射关系,注册java本地方法;
应用层
APP加载C库(so文件),加载后便可以调用C库提供的java本地方法:open()ioctl()close()实现对硬件的操作;

3.1.1 编写LED驱动

LED的驱动很简单,就不多废话了。

{% codeblock lang:c %}
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static int led_gpios[] = {
EXYNOS4212_GPM4(0),
EXYNOS4212_GPM4(1),
EXYNOS4212_GPM4(2),
EXYNOS4212_GPM4(3),
};

static int led_open(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++)
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

return 0;

}

/* app : ioctl(fd, cmd, arg) */
static long led_ioctl(struct file filp, unsigned int cmd, unsigned long arg)
{
/
cmd : 0-off, 1-on /
/
arg : 0-3, which led */

if ((cmd != 0) && (cmd != 1))
    return -EINVAL;

if (arg > 4)
    return -EINVAL;

gpio_set_value(led_gpios[arg], !cmd);

return 0;

}

static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,

};

static int major;
static struct class *cls;

int leds_init(void)
{
major = register_chrdev(0, “leds”, &leds_ops);

cls = class_create(THIS_MODULE, "leds");
device_create(cls, NULL, MKDEV(major, 0), NULL, "leds"); /* /dev/leds */

return 0;

}

void leds_exit(void)
{
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, “leds”);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Tiny4412 leds driver.”);
MODULE_VERSION(“v1.0”);
{% endcodeblock %}

编写好后,放在内核目录drivers/char/里,并在Makefile里添加obj-y += leds_drv.o
重新编译make zImage,将内核重新烧写到开发板里。

3.1.2 编写C库文件

参考前面Java的JNI,编写hardcontrol.c,生成libhardcontrol.so
hardcontrol.c里,操作/dev/leds,将操作函数与Java方法绑定,并注册。

{% codeblock lang:c %}
#include <stdio.h>
#include <jni.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <android/log.h>

#define DEBUG 1

#if(DEBUG==1)
#define LOG_TAG “JNI”
#define LOGV(…) __android_log_print(ANDROID_LOG_VERBOSE,TAG,VA_ARGS)
#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, VA_ARGS)
#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, VA_ARGS)
#define LOGE(…) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, VA_ARGS)
#else
#define LOGV(…) NULL
#define LOGD(…) NULL
#define LOGI(…) NULL
#define LOGE(…) NULL
#endif

static jint fd;

jint ledOpen(JNIEnv *env, jobject cls)
{
LOGD(“hardcontrol ledOpen\n”);

fd = open("/dev/leds", O_RDWR);
if (fd >= 0)
    return 0;
else
    return -1;

}

void ledClose(JNIEnv *env, jobject cls)
{
LOGD(“hardcontrol ledClose\n”);

close(fd);

return;

}

jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
LOGD(“hardcontrol ledCtrl number=%d status=%d\n”, number, status);

int ret = ioctl(fd, status, number);

return ret;

}

static const JNINativeMethod methods[] = {
{“ledOpen”, “()I”, (void *)ledOpen},
{“ledClose”, “()V”, (void *)ledClose},
{“ledCtrl”, “(II)I”, (void *)ledCtrl},
};

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;

if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) 
    return JNI_ERR; 

cls = (*env)->FindClass(env, "cn/hceng/hardlibrary/HardControl"); 
if (cls == NULL) 
    return JNI_ERR;

if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
    return JNI_ERR;

return JNI_VERSION_1_4;

}
{% endcodeblock %}

编译:

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so

这里的-I指定头文件jni.h的路径;-nostdlib是为了不使用标准的libc库,因为后面libhardcontrol.so依赖libc.so.6,需要重新构建系统得到libc.so.6,但源码中有很多libc.so也能用,为了方便找了一个版本较高的libc.so

最后把生成的libhardcontrol.so放在APP的D:\Android\APP\LED\app\libs\armeabi\路径下。

如何在C语言中打印log到android控制台
1.修改app\build.gradle,在Android里添加ndk:

        //hceng add for c language printing
        ndk {
            moduleName "printing"
            abiFilters "armeabi","armeabi-v7a","x86"

            ldLibs "log"
        }

2.C文件中添加:

#include <android/log.h>
#define DEBUG 1 

#if(DEBUG==1)
#define LOG_TAG "JNI"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#else
#define LOGV(...) NULL
#define LOGD(...) NULL
#define LOGI(...) NULL
#define LOGE(...) NULL
#endif

3.和printf一样使用:

  //打印logcat
  LOGD("name=%s age=%f\n",name,age);

4.编译C文件,需指定android/log.h路径和添加liblog.so库:

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -I /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include/ -nostdlib /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so /work/android_source/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so

参考博客:C语言中打印log到android控制台

3.1.2 修改APP文件

在APP目录app\src\main\java\cn\hceng\hardlibrary\下创建HardControl.java来加载C库和声明Native方法:
{% codeblock lang:java %}
package cn.hceng.hardlibrary;

public class HardControl {
//1.load
static {
try {
System.loadLibrary(“hardcontrol”); //Call libhardcontrol.so from C.
} catch (Exception e) {
e.printStackTrace();
}
}
//2.Function declaration
public static native int ledCtrl(int number, int status);
public static native int ledOpen();
public static native void ledClose();
}
{% endcodeblock %}

在APP里,即可通过JNI调用本地方法:
{% codeblock lang:java %}
import cn.hceng.hardlibrary.*; //硬件库

HardControl.ledOpen(); //硬件初始化
HardControl.ledCtrl(0, 1); //硬件操作
HardControl.ledCtrl(0, 0); //硬件操作

{% endcodeblock %}
在APP的适当位置,根据操作逻辑,调用本地方法。

最后还要修改app\build.gradle,在Android里添加:

    //hceng add for led
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs'];
        }
    }

此时修改后,将不能在AVD里仿真了,必须在开发板上测试。

最后,编译APP,插上USB线,连接开发板,运行测试,通过APP实现对LED的控制。

3.2 Android硬件访问服务

前面直接通过JNI访问硬件,操作比较简单,同时也存在一些问题。
比如,当多个APP同时访问某个硬件时,就可能发生硬件冲突。
为了解决这个问题,Android提供了硬件访问服务,APP不能直接访问硬件,而是都给System Server申请操作硬件,System Server决策后,再去访问硬件,解决多个APP同时访问硬件冲突的问题。

驱动层
和Linux驱动完全一样,编写驱动,向上提供/dev/leds节点;
C库
HAL层操作/dev/leds,JNI层加载HAL文件和向上注册java本地方法;
系统服务
通过注册android服务的方式加载C库,再将服务加入service_manager里面。
应用层
service_manager里获取相关服务,再通过接口调用,接口里实现对本地方法的调用。

3.2.1 编写AIDL

AIDL(Android Interface Definition Language),即Android接口定义语言,顾名思义就是定义接口,提供给APP使用。
LED对APP而言,就一个控制方法,因此ILedService.aidl如下:
{% codeblock lang:aidl %}
package android.os;

interface ILedService {
int ledCtrl(int number, int status);
}
{% endcodeblock %}

ILedService.aidl并不能直接被APP调用,需要生成ILedService.java才能被APP使用。
ILedService.aidl放在frameworks/base/core/java/android/os/里,修改frameworks/base/Android.mk,添加:

core/java/android/os/ILedService.aidl \

然后执行mmm frameworks/base/,将自动生成out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java

3.2.2 编写LedService.java

前面生成了ILedService.java,需要实现ILedService接口的成员函数。
{% codeblock lang:java %}
package com.android.server;
import android.os.ILedService;

public class LedService extends ILedService.Stub {
private static final String TAG = “ILedService”;

//Call native c function to access hardware
public int ledCtrl(int number, int status) throws android.os.RemoteException 
{
	return native_ledCtrl(number, status);
}

public LedService() {
	native_ledOpen();
}

//Function declaration
public static native int native_ledCtrl(int number, int status);
public static native int native_ledOpen();
public static native void native_ledClose();

}
{% endcodeblock %}

将其放在frameworks/base/services/core/java/com/android/server/路径下。

可以看到LedService.java继承于ILedService,并调用本地方法实现了成员函数。并在构造函数里调用native_ledOpen()

3.2.3 修改SystemServer.java

SystemServer.java主要做两件事,一是加载C库,二是使用addService将LED服务加入service_manager里面。
加载C库这个是调用onload.cpp,这个后面只修改onload.cpp,这里就不需要改了。
修改frameworks/base/services/java/com/android/server/SystemServer.java,添加:
{% codeblock lang:java %}
Slog.i(TAG, “Led Service”);
ServiceManager.addService(“led”, new LedService());
{% endcodeblock %}

3.2.4 修改onload.cpp

SystemServer.java会加载C库,调用到onload.cpp,需要在onload.cpp注册LED服务。
修改frameworks/base/services/core/jni/onload.cpp,添加:
{% codeblock lang:c %}
int register_android_server_LedService(JNIEnv* env);

……

register_android_server_LedService(env);
{% endcodeblock %}

3.2.5 编写com_android_server_LedService.cpp

前面用到了register_android_server_LedService(),是在com_android_server_LedService.cpp里实现的。
com_android_server_LedService.cpp理论上可以直接操作节点/dev/leds,但一般不这样做。
通常的做法是,向上提供本地方法(native_ledOpen),向下加载hal文件(led_hal.c),并调用HAL的函数。
这样操作有两个好处:
1.方便修改;
如果需要修改硬件部分的操作,只需要修改led_hal.c,生成so文件,放入系统即可,而不需要编译整个Android系统;
2.保密代码;
因为Linux的GPL协议,一旦使用的内核代码,自己的代码也得开源出去,硬件厂商为了保密硬件的具体细节,只在内核实现操作寄存器的接口,具体的操作逻辑放在HAL文件里,而Android采用Apache协议,修改了代码而无需开源,这样就实现了保密代码;

Google的这一"骚"操作就不评论了,积极的看确实加快的Linux的传播,但也伤害了Linux的开源社区。

编写步骤如下:
1、定义JNINativeMethod,建立Java本地方法与C库函数名的映射关系;
2、使用jniRegisterNativeMethods注册本地方法,将在onload.cpp被调用;
3、在open()里:
 3.1、使用hw_get_module获得hw_module_t结构体;
 3.2、使用module->methods->open获得hw_device_t结构体;
 3.3、将hw_device_t转换为led_device_t,调用对应open
4、完成其它函数ctrlclose的调用;

{% codeblock lang:cpp %}
#define LOG_TAG “LedService”

#include “jni.h”
#include “JNIHelp.h”
#include “android_runtime/AndroidRuntime.h”

#include <utils/misc.h>
#include <utils/Log.h>

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <hardware/led_hal.h>

namespace android
{
static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
    jint err;
    hw_module_t* module;
    hw_device_t* device;

    ALOGI("native ledOpen");

    //1. hw_get_module for get module
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
        //2. module->methods->open for get device 
        err = module->methods->open(module, NULL, &device);
        
        if (err == 0) {
            //3. conversion, call led_open
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
        } else {
            return -1;
        }
    }
    return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
    ALOGI("nativeled Close");
    
    return;
}

jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
    ALOGI("native ledCtrl %d, %d", number, status);
    
    return led_device->led_ctrl(led_device, number, status);
}

static const JNINativeMethod method_table[] = {
    {"native_ledOpen",  "()I",   (void *)ledOpen}, 
    {"native_ledClose", "()V",   (void *)ledClose}, 
    {"native_ledCtrl",  "(II)I", (void *)ledCtrl}, 
};

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            method_table, NELEM(method_table));
}

};
{% endcodeblock %}

将其放到./frameworks/base/services/core/jni/路径下。

3.2.6 编写HAL文件led_hal.c

接着前面,HAL负责访问驱动程序执行硬件操作。

编写步骤如下:
1、创建一个名为HMI(HAL_MODULE_INFO_SYM)的hw_module_t结构体;
2、创建一个open函数, 它返回led_device_t结构体;
3、创建led_device_t结构体,里面包含hw_device_t结构体和提供的函数;
4、操作设备节点实现提供的函数;

{% codeblock lang:h [led_hal.h] %}
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

struct led_device_t {
struct hw_device_t common;

int (*led_open) (struct led_device_t* dev);
int (*led_ctrl) (struct led_device_t* dev, int number, int status);

};

__END_DECLS

#endif
{% endcodeblock %}

{% codeblock lang:c [led_hal.c] %}
#include <hardware/hardware.h>

#include <cutils/log.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>

#include <hardware/led_hal.h>

#define LOG_TAG “LED_HAL”

static int fd;

static int led_open(struct led_device_t* dev __unused)
{
fd = open("/dev/leds", O_RDWR);

ALOGI("led_open : %d", fd);

if (fd >= 0)
    return 0;
else
    return -1;

}

static int led_ctrl(struct led_device_t* dev __unused, int number, int status)
{
int ret = ioctl(fd, status, number);

ALOGI("led_ctrl : %d, %d, %d", number, status, ret);

return ret;

}

static int led_close(struct hw_device_t* device __unused)
{
ALOGI(“led_close : %d”, fd);

close(fd);

return 0;

}

static struct led_device_t led_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
.close = led_close,
},
.led_open = led_open,
.led_ctrl = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module __unused, const char* id __unused,
struct hw_device_t** device)
{
//return by id
*device = &led_dev;

return 0;

}

static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.id = “led”,
.methods = &led_module_methods,
};
{% endcodeblock %}

led_hal.h放在./hardware/libhardware/include/hardware/目录下;
led_hal.c上放在./hardware/libhardware/modules/led/目录下;

3.2.7 编译和重新烧写system.img

前面添加了LedService.java,其Android.mk自动包含了所有java文件,不需要修改Android.mk
前面添加了com_android_server_LedService.cpp,不会自动包含cpp文件,其Android.mk需要修改。
打开frameworks/base/services/core/jni/Android.mk,添加:

$(LOCAL_REL_DIR)/com_android_server_LedService.cpp \

前面添加了led_hal.c,需要创建hardware/libhardware/modules/led/Android.mk,内容如下:
{% codeblock lang:mk %}
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := led.default

LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng

include $(BUILD_SHARED_LIBRARY)
{% endcodeblock %}

然后执行:

mmm ./frameworks/base/services/             //保证com_android_server_LedService.cpp和onload.cpp都重新编译
mmm ./hardware/libhardware/modules/led/     //保证led_hal.c编译
make snod                                   //重新生成./out/target/product/tiny4412/system.img
./gen-img.sh                                //脚本,生成system.img并移动到源码根目录,且生成其他用户数据镜像等

将生成的system.img重新烧写,重新启动后界面卡在Android logo,串口一直打印request_suspend_state: wakeup (0->0)
解决方法:
修改android源码的./device/friendly-arm/tiny4412/conf/init.rc
mount rootfs rootfs / ro remount改为mount rootfs rootfs / rw remount,重新编译,烧写即可。

3.2.8 修改APP文件

在APP里,会用到ILedService类,这个类是我们自己定义的,直接在APP里使用,连编译都过不了。
因此,就需要把包含ILedService类的文件导入工程,或者使用JAVA的反射机制。

1、使用导入的方法
①首先还是修改MainActivity.java,修改其中对硬件的调用,参考如下:
{% codeblock lang:c %}
import android.os.ILedService;
import android.os.ServiceManager;
import static android.os.ServiceManager.getService;
……
ILedService iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
……
iLedService.ledCtrl(0, 1);
{% endcodeblock %}
注意getService()的名字和SystemServer.javaaddServer添加的名字要一致。

②将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar放在Windows里。
在Android Studio里,选择File->Project Structure,此时弹出配置界面,点击左上角的+,选择Import .JAR/.AAR Package,选择刚才的classes.jar
再选中app,切换到Dependencies选项卡,点击右边的+号,选择3 Module dependency,在弹出的界面选择刚才添加的classes,最后点击OK

以上操作后,会在APP工程根目录下生成`classes`文件夹,里面就包含了`classes.jar`。

为了减小生成的APP体积,还需要设置JAR包只在编译时使用,而不打包到APP里面。如下图,选中classes,在右边的选项卡选择Compile only

③此时,再次编译可能遇到INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113等错误,在app\build.gradle添加:

    //hceng add for solved:INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113.
    splits {
        abi {
            enable true
            reset()
            include 'x86', 'armeabi-v7a', 'x86_64'
            universalApk true
        }
    }

2、使用反射机制

JAVA的反射机制原理参考之前的JAVA学习笔记——4.2反射

使用放射机制就不用导入classes.jar,通过Class.forName()获取类、getMethod()获取方法,最终得到ledCtrl()方法。
{% codeblock lang:java %}
Object proxy = null;
Method ledCtrl = null;

……

try {
//iLedService = ILedService.Stub.asInterface(ServiceManager.getService(“led”));
Method getService = Class.forName(“android.os.ServiceManager”).getMethod(“getService”, String.class);
Object ledService = getService.invoke(null, “led”);
Method asInterface = Class.forName(“android.os.ILedService S t u b &quot; ) . g e t M e t h o d ( &quot; a s I n t e r f a c e &quot; , I B i n d e r . c l a s s ) ; p r o x y = a s I n t e r f a c e . i n v o k e ( n u l l , l e d S e r v i c e ) ; l e d C t r l = C l a s s . f o r N a m e ( &quot; a n d r o i d . o s . I L e d S e r v i c e Stub&quot;).getMethod(&quot;asInterface&quot;, IBinder.class); proxy = asInterface.invoke(null, ledService); ledCtrl = Class.forName(&quot;android.os.ILedService Stub$Proxy”).getMethod(“ledCtrl”, int.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
{% endcodeblock %}

使用:
{% codeblock lang:java %}
try {
ledCtrl.invoke(proxy, i, 1);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
{% endcodeblock %}

4.总结

如何实现一个硬件访问服务:
①编写Linux驱动led_drv.c,操作硬件;
②编写HAL层led_hal.c,访问Linux设备节点;
③编写JNI层com_android_server_LedService.cpp,调用HAL层,注册本地方法;
④修改onload.cpp注册服务,修改SystemServer.java加入服务;
⑤编写LedService.java调用本地方法;
⑥编写ILedService.aidl生成ILedService.java,提供接口;
⑦编写APP,实现UI、操作逻辑,调用接口访问硬件;

参考资料:
韦东山第四期Android驱动_Android系统
Android系统源代码情景分析_罗升阳
第一行代码:Android

涉及示例源码:
Github

猜你喜欢

转载自blog.csdn.net/hceng_linux/article/details/89944985