Freescale IMX6 Android (7): Android启动动画死循环 Home界面不出来与pid XXX exit 可能的原因汇总

最后更新于:2022-04-01 11:41:45

或许你某一次编译了整个Android,然后烧进去,结果屏幕中一直显示Android Bootanimation,Home界面一直不出来。对此,可能有以下五类原因,往这几个方向考虑与调查可能可以提供一些线索。前面两类是属于软件的,后面两类属于硬件。最后一类属于软件硬件都相关。 ## 第一类:binder相关 ~~~ <span style="font-family:Microsoft YaHei;font-size:18px;">binder: release 3000:3000 transaction 12769 out, still active binder: 2720:2720 transaction failed 29189, size 4-0 binder: send failed reply for transaction 12769, target dead</span> ~~~ 这一类的问题,一般都是因为某个进程退出了,因此binder消息无法发送过去了,因此和下面第二类比较类似。 ## 第二类:pid XXX exit 这类log会不断输出,表现为某个或者某几个线程不断的重启。为何会不断的重启,因为这个是系统关键的线程,可能是守护进程。 那么就需要调查程序为什么会退出,这一般有三种可能的原因: 1. 进程需要的资源无法获得 1. 进程程序有问题 1. 有其他进程在不断的kill(发信号)这个进程 这几类都遇到过,下面举例说明。 ### 进程需要的资源无法获得 有可能是某个分区坏了,而这个线程一定需要在这个分区创建某个文件并写入数据才能执行。 有一些Service可能需要某个属性的值变成特定的值才能运行。 ### 进程程序有问题 在前面的博客:[Freescale IMX6 Android (6): 向ServerManager中添加Service](http://blog.csdn.net/sy373466062/article/details/50333581),实践中就遇到过这个问题,且最终发现是自己写的问题。 那么如何解决呢? 看log,有时候会出现误解,例如log可能看到的A进程不对重启,但是实际上可能是因为A进程依赖B进程,而B进程有问题自己退出了,所以A进程就一直等不到或者发现B进程死了,也退出了,但是B进程退出的时候居然没有提示。于是我们误认为是A进程有问题,但是实际是B进程的问题。对此,我们可以看Android的tombstone。不同系统的tombstone存放的位置可能不大一样,但是一般默认是放在/data/tombstone下面的。 例如某一次启动后不出现Home画面,且提示mediaServer与btd不同重启,但是查看tombstone却发现是我在SystemServer中添加了代码导致的: ~~~ *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'Freescale/sabresd_6dq/sabresd_6dq:4.3/1.1.0-rc4/20131206:eng/dev-keys' Revision: '405522' pid: 15565, tid: 15565, name: system_server >>> system_server <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadd00d r0 00000000 r1 00000000 r2 00000000 r3 409961a0 r4 deadd00d r5 0000020c r6 00000001 r7 663e7290 r8 663e13b1 r9 00000001 sl 00000000 fp 40991c88 ip 3ccd4001 sp be835258 lr 00000001 pc 4092dad8 cpsr 600b0030 d0 0000000000000000 d1 0000000000000000 d2 0000000000000000 d3 0000000000000000 中间省略 --------- log /dev/log/main 01-02 08:52:24.370 15565 15565 E BatteryService: No charger supplies found 01-02 08:52:24.370 15565 15565 E dalvikvm: ERROR: couldn't find native method 01-02 08:52:24.370 15565 15565 E dalvikvm: Requested: Lcom/android/server/LedService;.native_ledOpen:()I 01-02 08:52:24.370 15565 15565 E dalvikvm: Candidate: Lcom/android/server/LedService;.native_ledOpen:()V 01-02 08:52:24.370 15565 15565 E dalvikvm: JNI posting fatal error: RegisterNatives failed for 'com/android/server/LedService', aborting ~~~ 可以看到其实我native函数的声明与实际的定义类型不匹配导致的。 ### 有其他进程在不断的kill(发信号)这个进程 这个一般出现在资源互斥的情况下,例如只有一个摄像头,但是两个进程都需要,且某一个进程的做法是如果其他进程在使用这个设备,那么kill掉它。尽管这种做法很流氓,但是实际中,有的时候为了快速响应,也是可以理解的。 ## 第三类:某个硬件相关的驱动不停的打印log 例如,我的PC的USB接口有些松动了,如果adb先接到这个端口,就会不断的出现下面的log: ~~~ android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null)) ~~~ 这个是因为硬件接触的问题导致一下子接好马上就松开了,于是USB驱动不断产生中断,内核不断发送event到上层。 对于这种持续不断大量的中断产生,内核大部分的CPU时间都用去处理中断了,因为中断的优先级比非NMI中断外的任务都高,且中断的上半部分会关闭中断与调度,因此系统启动会变得特别的慢,看起来就向一直启动不来似的。 ## 第四类:没有特殊的log输出,但是系统一直卡住 这个也在实际中遇到过,而且几乎可以肯定是内核(硬件)问题,例如产生某个NMI中断硬件一直产生中断,这个最终将会导致CPU无法调度其他任何任务而freeze。 ## 第五类:硬件出现问题,软件等不到这个硬件不断重启 例如某次启动后不断打印如下log: ~~~ ov3640_write_reg:write reg error:reg=3012,val=80 ERROR: v4l2 capture: slave not found! ~~~ 然后一个service依赖这个v4l2_capture(摄像头)设备,于是不断的去检测,不存在就退出,然后又被启动起来又检测发现不存在又退出。对此,我们可以插上这个硬件,也可以直接去掉对这个设备的检测,甚至可以在内核中不要添加这个设备,例如在TQIMX6Q的内核中可以注释掉下面的语句来解决这个问题: ~~~ $ git diff arch/arm/mach-mx6/board-mx6q_sabresd.c diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.c b/arch/arm/mach-mx6/board-mx6q_sabresd.c index fd1f3fd..5ca39e0 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.c +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.c @@ -1328,9 +1328,9 @@ static void __init mx6_sabresd_board_init(void) imx6q_add_mipi_dsi(&mipi_dsi_pdata); imx6q_add_lcdif(&lcdif_data); imx6q_add_ldb(&ldb_data); - imx6q_add_v4l2_output(0); - imx6q_add_v4l2_capture(0, &capture_data[0]); - imx6q_add_v4l2_capture(1, &capture_data[1]); + //imx6q_add_v4l2_output(0); + //imx6q_add_v4l2_capture(0, &capture_data[0]); + //imx6q_add_v4l2_capture(1, &capture_data[1]); imx6q_add_mipi_csi2(&mipi_csi2_pdata); imx6q_add_imx_snvs_rtc(); ~~~ 上面总共给出了五种可能,也有一些其他的情况,例如可能某些库的不匹配导致软件的问题,等不一而足。
';

Freescale IMX6 Android (6): 向ServerManager中添加Service

最后更新于:2022-04-01 11:41:43

## 简述 前面的博客中,我们直接添加C lib到APK中,然后使用LoadLibrary加载这个库,同时添加一个class来作为中间层,直接使用这个C库中的native函数来控制硬件,这种做法将硬件与APK牢牢绑定,如果有多个APP来访问同一个硬件就会出现问题,代码也会有很多的重复,在Android中,我们使用Android的SystemServer向ServiceManager来将硬件的功能添加为一个服务,这样当一个APP需要使用硬件的时候就向SystemServer发出请求service服务,然后由ServiceMnager统一提供服务,提供统一的接口与硬件控制,即相当于多添加了一层,从而实现解耦。 ## 详细原理 先看下图(图片来源于韦东山的Android视频资料)中的③②①,按照顺序: 1. SystemServer会加载Cpp lib 1. 在JNI_OnLoad中注册各个Service,SystemServer向ServiceManager添加服务 1. 这些service就包括像串口/LED等硬件相关的服务 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9e48dc5.jpg) 而使用的时候,就是7~5步骤: 1. AddService:SystemServer向ServiceManager添加服务addServeice 1. getservice:通过getservice来从SystemServer注册了的service中获取服务所具有的功能,例如ledctrl 1. 使用Service的方法:APP使用一个Interface(即以i开头的对象)来使用service提供的功能,将服务请求到SystemServer去 APP/SystemServer/ServiceManager三者都是通过Bindler来通讯。 ## 添加Service与使用Service的步骤 ### 添加serviceAIDL文件,生成Interface java文件 因为系统中其他都aidl文件都放在frameworks/base/core/java/android/os下,所有我们也参考其他的文件添加一个ILedService.aidl: ~~~ package android.os; /** {@hide} */ interface ILedService { int ledCtrl(int which, int status); } ~~~ 可以看到这个interface前面有个@hide的修饰,表明是个hide class。 同时还需要将此aidl文件添加到Android.mk(Makefile)中: ~~~ $ git diff Android.mk diff --git a/Android.mk b/Android.mk index 151621c..7bde511 100644 --- a/Android.mk +++ b/Android.mk @@ -150,6 +150,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IUpdateLock.aidl \ core/java/android/os/IUserManager.aidl \ core/java/android/os/IVibratorService.aidl \ + core/java/android/os/ILedService.aidl \ core/java/android/service/notification/INotificationListener.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ ~~~ 这个这个Android.mk位于frameworks/base/,编译后就会生成一个ILedService.java ### 添加service的实现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 <hardware_legacy/led.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> namespace android { #define ALOGI printf #define LED_NUM 3 int leds_fd[LED_NUM]; char path_buff[255]; static jint ledCtrl(JNIEnv *env, jobject clazz, jint which, jint status) { int ret = -1; if(status == 1) { ret = write(leds_fd[which], "255", 3); } else { ret = write(leds_fd[which], "0", 1); } if(ret < 0){ return -1; } ALOGI("Native ctrl fd = [%d]\n", which); return 0; } static jint ledOpen(JNIEnv *env, jobject clazz) { int i = 0; for(i=0; i<LED_NUM; i++){ sprintf(path_buff, "/sys/class/leds/led%d/brightness", i); printf("path:%s\n",path_buff); leds_fd[i] = open(path_buff, O_RDWR); if(leds_fd[i] < 0){ ALOGI("led%d: %s, open failed\n", i, path_buff); return -1; } else { ALOGI("led%d: %s, open success\n", i, path_buff); } } return 0; } static void ledClose(JNIEnv *env, jobject clazz) { int i = 0; for(i=0; i< LED_NUM; i++){ close(leds_fd[i]); } } static JNINativeMethod method_table[] = { { "native_ledCtrl", "(II)I", (void*)ledCtrl }, { "native_ledClose", "()V", (void*)ledClose }, { "native_ledOpen", "()I", (void*)ledOpen } }; int register_android_server_LedService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table)); } }; ~~~ 里面定义好了来调用这个native函数的java class名字为com_android_server_LedService: ~~~ jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table)); ~~~ 还需要添加到编译中: ~~~ $ git diff services/jni/Android.mk diff --git a/services/jni/Android.mk b/services/jni/Android.mk index b313d48..fb359cb 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -14,6 +14,7 @@ LOCAL_SRC_FILES:= \ com_android_server_UsbDeviceManager.cpp \ com_android_server_UsbHostManager.cpp \ com_android_server_VibratorService.cpp \ + com_android_server_LedService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ com_android_server_connectivity_Vpn.cpp \ onload.cpp ~~~ ### 添加LedService.java文件 前面有了native c/cpp的实现,接下来就需要用JNI来调用native方法了,因此需要添加LedService.java(frameworks/base/services/java/com/android/server/LedService.java)文件: ~~~ package com.android.server; import android.os.ILedService; /** * Created by hexiongjun on 12/9/15. * Function: * Call Native C function to control hardware */ public class LedService extends ILedService.Stub{ private static final String TAG = "LedService"; public int ledCtrl(int which, int status) throws android.os.RemoteException { return native_ledCtrl(which, status); } public void LedService(){ native_ledOpen(); } // Declare the function public native static int native_ledCtrl(int which, int status); public native static int native_ledOpen(); public native static void native_ledClose(); } ~~~ 内容很简单: - 声明了native函数 - 在构造函数中调用open打开设备 上层的Android.mk会自动将java文件添加到Android编译中,不需要自己添加。 ### 让SystemServer启动的时候加载service ~~~ $ git diff services/jni/onload.cpp diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index 423ebd1..83721fe 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -31,6 +31,8 @@ int register_android_server_SerialService(JNIEnv* env); int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); +// From com_android_server_LedService.cpp +int register_android_server_LedService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); @@ -60,6 +62,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_UsbDeviceManager(env); register_android_server_UsbHostManager(env); register_android_server_VibratorService(env); + register_android_server_LedService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); register_android_server_connectivity_Vpn(env); ~~~ 这个是因为SystemServer进程启动的时候会去调用LoadLibrary去加载各个库,这个加载的过程就在OnLoad.cpp中。 ### 添加Service到ServiceManager中 这个是在SystemServer中完成的: ~~~ $ git diff services/java/com/android/server/SystemServer.java diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9455017..1ccf63a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -129,6 +129,7 @@ class ServerThread extends Thread { PowerManagerService power = null; DisplayManagerService display = null; BatteryService battery = null; + LedService led = null; VibratorService vibrator = null; AlarmManagerService alarm = null; MountService mountService = null; @@ -288,6 +289,11 @@ class ServerThread extends Thread { battery = new BatteryService(context, lights); ServiceManager.addService("battery", battery); + //Add the led service to SystemServer, so others can use + Slog.i(TAG, "Led Service"); + led = new LedService(); + ServiceManager.addService("led", led); + Slog.i(TAG, "Vibrator Service"); vibrator = new VibratorService(context); ServiceManager.addService("vibrator", vibrator); ~~~ ### LED App中使用添加的Service 到了最后就可以使用这些服务了,但是要使用之前还需要添加包含了LedService的模块,这个模块其实是framework,但是因为我们是java,而framework属于dex格式,因此我们需要添加jar格式的包,这个包编译完成后,位于: > out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 因此在APP中添加此模块: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9e69af0.jpg) 并将此模块添加到app的依赖中: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9e88e99.jpg) 然后在代码中导入Interface与ServiceManager,并使用Service: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9ea1aa4.jpg) ## 这种添加Service到ServiceManager方法的问题 现在我们依然将硬件相关的操作放到了一个cpp中,而这个cpp会编译到系统中,因此如果对硬件的操作有变更,我们就需要修改这个文件,修改了这个文件,那么就需要将整个Android系统重新编译,因此图片中还有一个步骤④,这个就是将硬件相关的东西放在一个HAL层,这样子就避免了修改一个文件就需要编译整个系统,同时也可以不放出与硬件相关的源码而仅仅给出一个HAL相关的库(保密)。 ## 遇到的问题 ### multidex问题 因为包含了framework的classes.jar,而这个jar中有超过65K个的方法,因此就需要开启multidex。 ~~~ $ git diff app/build.gradle diff --git a/app/build.gradle b/app/build.gradle index 131397f..82a2a62 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,12 @@ android { targetSdkVersion 22 versionCode 1 versionName "1.0" + + //Enable multidex + multiDexEnabled true + } + dexOptions { + javaMaxHeapSize "4g" } sourceSets{ main { @@ -29,4 +35,6 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:design:22.2.1' + compile project(':classes') + compile 'com.android.support:multidex:1.0.0' } ~~~ 同时还需要更改xml文件: ~~~ $ git diff app/src/main/AndroidManifest.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a77e7a1..1504514 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.hexiongjun.led"> <application + android:name="android.support.multidex.MultiDexApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ~~~ 并对gradle resync。 ### jar不匹配的问题 如果重新编译了Android classes.jar但是在APP中依旧使用的是老的,那么会出现一些奇怪的问题: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9ec5da9.jpg) 此时需要先将老的移除掉然后重新添加,或者直接在app的workspace中替换新的。 ### javaHeap size的配置 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9eebcb2.jpg)
';

Freescale IMX6 Android (4): 基于TQIMX6 给Toolbox添加LED控制程序

最后更新于:2022-04-01 11:41:41

本篇博文是为了后面的Android HAL层使用LED而做的准备,板子基于TQIMX6Q,Android 4.3,Android已经编译完成了,如果还没有可以参考前面我的博文:[Freescale IMX6 Android: 使用HDMI作为Android显示输出的配置](http://blog.csdn.net/sy373466062/article/details/50184041),以及TQ提供的开发者手册。 ## LED的硬件连接 LED4~6是给用户使用的,如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9ccf55d.jpg) 可以看到是GPIO3_21~23。而且是GPIO给高电平的时候接通。 ## LED软件方面的配置 ### 直接导出使用 看到前面的原理图,在用户态直接将gpio导出来操作(gpiolib)是最容易的了,但是要在/sys/class/gpio中导出来需要知道各组gpio的base number,直接使用cat查看,结果如下: ~~~ #for i in gpiochip* ; do echo `cat $i/label`: `cat $i/base` ; done gpio-0: 0 gpio-4: 128 gpio-5: 160 gpio-6: 192 gpio-1: 32 gpio-2: 64 gpio-3: 96</span></span> ~~~ 可以知道gpio3是从64开始的,gpio3-21为64+21=85,于是直接操作: ~~~ #cd /sys/class/gpio echo 85 > export echo out > gpio85/direction echo 0 > gpio85/value ~~~ 但是发现不成功,因为gpio已经被使用了,无法导出来,尽管操作的时候没有出现问题log提示。 ### 使用Gpio-led 内核使用的是3.0.35版本的内核,尽管也有dts,但是Freescale在最开始的Linuxkernel中并没有使用,因此都是hard code在board文件中的,例如这里的LED的配置就是在arch/arm/mach-mx6/board-mx6q_sabresd.c中: ~~~ #define SABRESD_GPIO_LED0 IMX_GPIO_NR(3, 21) //home #define SABRESD_GPIO_LED1 IMX_GPIO_NR(3, 22) //enter #define SABRESD_GPIO_LED2 IMX_GPIO_NR(3, 23) //esc ~~~ 忽略注释,然后接下来定义了一个Platform device: ~~~ static struct gpio_led imx6q_gpio_leds[] = { GPIO_LED(SABRESD_GPIO_LED0, "led0", 0, 1, "charger-charging"), GPIO_LED(SABRESD_GPIO_LED1, "led1", 0, 1, "charger-charging"), GPIO_LED(SABRESD_GPIO_LED2, "led2", 0, 1, "charger-charging"), /* For the latest B4 board, this GPIO_1 is connected to POR_B, which will reset the whole board if this pin's level is changed, so, for the latest board, we have to avoid using this pin as GPIO. GPIO_LED(SABRESD_CHARGE_DONE, "chg_done_led", 0, 1, "charger-full"), */ }; static struct gpio_led_platform_data imx6q_gpio_leds_data = { .leds = imx6q_gpio_leds, .num_leds = ARRAY_SIZE(imx6q_gpio_leds), }; static struct platform_device imx6q_gpio_led_device = { .name = "leds-gpio", .id = -1, .num_resources = 0, .dev = { .platform_data = &imx6q_gpio_leds_data, } }; ~~~ 直接在代码中HardCode Device信息,这是老内核的通常做法。从上面的代码我们知道注册了leds-gpio设备,这个设备可以在/sys/class/leds中找到,启动的时候probe时候也会打印出设备信息: ~~~ <span style="font-family:Microsoft YaHei;">Registered led device: led0 Registered led device: led1 Registered led device: led2</span> ~~~ 从前面的imx6q_gpio_leds结构体以及最前面的定义中可以知道LED的对应关系为: ~~~ HW ------- SW led4 led0 led5 led1 led6 led2 ~~~ 在对应的目录中我们可以看到device设备信息: ~~~ root@sabresd_6dq:/sys/class/leds # ls -l lrwxrwxrwx root root 1970-01-02 09:36 led0 -> ../../devices/platform/leds-gpio/leds/led0 lrwxrwxrwx root root 1970-01-02 09:36 led1 -> ../../devices/platform/leds-gpio/leds/led1 lrwxrwxrwx root root 1970-01-02 09:36 led2 -> ../../devices/platform/leds-gpio/leds/led2 ~~~ ## GPIO-LED设备的控制 gpio-led设备的子目录中有一个brightness文件,操作这个文件就可以操作led灯的亮灭,例如下面是点亮: ~~~ echo 255 > /class/gpio/leds/led0/brightness ~~~ 如果写入0,那么就是熄灭。 因为Android中基础小工具都是由Toolbox提供,因此我们可以往toolbox添加一个ledctrl工具来控制灯的亮灭: ~~~ #include <sys/cdefs.h> #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)ledctrl.c 8.5 (Berkeley) 5/4/95"; #else __RCSID("$NetBSD: ledctrl.c,v 1.33 2008/07/30 22:03:40 dsl Exp $"); #endif #endif /* not lint */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> void ledClose(); int ledctrl( int which, int status); int ledOpen(); #define ALOGI printf #define LED_NUM 3 int leds_fd[LED_NUM]; char path_buff[255]; int ledctrl( int which, int status) { int ret = -1; if(status == 1) { ret = write(leds_fd[which], "255", 3); } else { ret = write(leds_fd[which], "0", 1); } if(ret < 0){ return -1; } ALOGI("Native ctrl fd = [%d]\n", which); return 0; } int ledOpen(void) { int i = 0; for(i=0; i<LED_NUM; i++){ sprintf(path_buff, "/sys/class/leds/led%d/brightness", i); printf("path:%s\n",path_buff); leds_fd[i] = open(path_buff, O_RDWR); if(leds_fd[i] < 0){ ALOGI("led%d: %s, open failed\n", i, path_buff); return -1; } else { ALOGI("led%d: %s, open success\n", i, path_buff); } } return 0; } void ledClose(void) { int i = 0; for(i=0; i< LED_NUM; i++){ close(leds_fd[i]); } } int ledctrl_main(int argc, char * argv[]) { int i = 0; int ret = ledOpen(); if (ret < 0){ printf("Open failed\n"); return -1; } for(i=0; i< LED_NUM; i++){ ledctrl(i,1); sleep(1); ledctrl(i,0); } ledClose(); return 0; } ~~~ 我们在main函数中对每一个LED点亮1秒,然后就熄灭。将这个C代码保存为ledctrl.c,放在system/core/toolbox下面,然后更改toolbox目录下的Android.mk将其添加到toolbox中: ~~~ diff --git a/core/toolbox/Android.mk b/core/toolbox/Android.mk index c764690..a19338e 100644 --- a/core/toolbox/Android.mk +++ b/core/toolbox/Android.mk @@ -57,6 +57,7 @@ TOOLS := \ touch \ lsof \ du \ + ledctrl \ md5 \ clear \ getenforce \ ~~~ 直接在toolbox目录下面使用mm命令编译: ~~~ $ mm ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=4.3 TARGET_PRODUCT=sabresd_6dq TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm TARGET_ARCH_VARIANT=armv7-a-neon TARGET_CPU_VARIANT=cortex-a9 HOST_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=Linux-3.13.0-39-generic-x86_64-with-Ubuntu-12.04-precise HOST_BUILD_TYPE=release BUILD_ID=1.1.0-rc4 OUT_DIR=out ============================================ PRODUCT_COPY_FILES device/fsl/common/input/HannStar_P1003_Touchscreen.idc:system/usr/idc/HannStar_P1003_Touchscreen.idc ignored. PRODUCT_COPY_FILES device/fsl/common/input/Novatek_NT11003_Touch_Screen.idc:system/usr/idc/Novatek_NT11003_Touch_Screen.idc ignored. PRODUCT_COPY_FILES device/fsl/common/input/qwerty.idc:system/usr/idc/qwerty.idc ignored. PRODUCT_COPY_FILES device/fsl/common/input/qwerty2.idc:system/usr/idc/qwerty2.idc ignored. No private recovery resources for TARGET_DEVICE sabresd_6dq make: Entering directory `/home/hexiongjun/iMX6Q/TQIMX6_android-4.3' target thumb C: toolbox <= system/core/toolbox/ledctrl.c target Executable: toolbox (out/target/product/sabresd_6dq/obj/EXECUTABLES/toolbox_intermediates/LINKED/toolbox) target Symbolic: toolbox (out/target/product/sabresd_6dq/symbols/system/bin/toolbox) target Strip: toolbox (out/target/product/sabresd_6dq/obj/EXECUTABLES/toolbox_intermediates/toolbox) Install: out/target/product/sabresd_6dq/system/bin/toolbox make: Leaving directory `/home/hexiongjun/iMX6Q/TQIMX6_android-4.3' ~~~ 编译完成后toolbox,我们可以重新将system目录的文件拷贝到SD开对应的system分区中,也可以直接将toolbox push到android机器中,在push之前需要先remount system为rw,因此在串口中,或者有root权限的adb shell中输入下面命令: ~~~ # mount -t ext4 -r -w -o remount /system EXT4-fs (mmcblk1p2): re-mounted. Opts: (null) ~~~ 然后push文件到机器中: ~~~ $ adb push $OUT/system/bin/toolbox /system/bin/ 1309 KB/s (139096 bytes in 0.103s) ~~~ 然后在机器的console中测试: ~~~ # toolbox ledctrl path:/sys/class/leds/led0/brightness led0: /sys/class/leds/led0/brightness, open success path:/sys/class/leds/led1/brightness led1: /sys/class/leds/led1/brightness, open success path:/sys/class/leds/led2/brightness led2: /sys/class/leds/led2/brightness, open success Native ctrl fd = [0] Native ctrl fd = [0] Native ctrl fd = [1] Native ctrl fd = [1] Native ctrl fd = [2] Native ctrl fd = [2] ~~~ 如果看到LED点亮1秒然后熄灭,那么说明代码无误。
';

Freescale IMX6 Android (5): APP通过JNI控制LED

最后更新于:2022-04-01 11:41:38

本篇博客接上一篇的:[Freescale IMX6 Android: 基于TQIMX6 给Toolbox添加LED控制程序](http://blog.csdn.net/sy373466062/article/details/50252123),上一篇是直接将控制程序放到了Toolbox,本篇则是通过JNI调用C库来完成LED的控制,即APP--> C --> HW。 ## APK的编写(源码见末尾) ### 编写主要需要注意下面个事项与步骤 1. 更改xml在layout中添加控件 2. Checkbox与Button的添加及其click与checked事件的handler 3. Toast的使用 4. API版本问题 5. 文档的查询 UI的layout如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9aaaf32.jpg) 然后添加checkbox与button的callback。 ### 添加调用C语言native方法的java库 直接在App下的java中添加一个package,然后添加一个class,如下图的libledctrl中的HWLedCtrl: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9ac9c16.jpg) 图一 其中的内容也很简单,就是建立一个static代码块,同时将native函数声明为static函数,这样子因为static代码块是存在class中的,不需要new一个对象来操作,其中HWLedCtrl.java的内容如上图: ~~~ package libledctrl; /** * Created by hexiongjun on 12/11/15. */ public class HWLedCtrl { public static native int ledctrl(int which, int status); public static native int ledopen(); public static native void ledclose(); static { try { System.loadLibrary("ledctrl"); } catch (Exception e) { e.printStackTrace(); } } } ~~~ 可以看到这里面加载了libledctrl.so这个库,这个就是下面的内容。 有了native方法与class之后,我们就可以在APP更改checkbox与button的时候更改led状态了: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9aeee5e.jpg) 这里面首先是Open,然后是进行亮灭状态切换。 ### C库的制作 在前面博客中的C语言代码基础上,我们去掉main函数,将其改成符合JNI接口的C源码: ~~~ #include "jni.h" //#include "JNIHelp.h" //#include "android_runtime/AndroidRuntime.h" //#include <utils/misc.h> //#include <utils/Log.h> //#include <hardware_legacy/led.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #define ALOGI printf #define LED_NUM 3 int leds_fd[LED_NUM]; char path_buff[255]; static jint ledctrl(JNIEnv *env, jobject clazz, jint which, jint status) { int ret = -1; if(status == 1) { ret = write(leds_fd[which], "255", 3); } else { ret = write(leds_fd[which], "0", 1); } if(ret < 0){ return -1; } ALOGI("Native ctrl fd = [%d]\n", which); return 0; } static jint ledopen(JNIEnv *env, jobject clazz) { int i = 0; printf("Native Open\n"); for(i=0; i<LED_NUM; i++){ sprintf(path_buff, "/sys/class/leds/led%d/brightness", i); printf("path:%s\n",path_buff); leds_fd[i] = open(path_buff, O_RDWR); if(leds_fd[i] < 0){ ALOGI("led%d: %s, open failed\n", i, path_buff); return -1; } else { ALOGI("led%d: %s, open success\n", i, path_buff); } } return 0; } static void ledclose(JNIEnv *env, jobject clazz) { int i = 0; for(i=0; i< LED_NUM; i++){ close(leds_fd[i]); } } static JNINativeMethod method_table[] = { { "ledctrl", "(II)I", (void*)ledctrl }, { "ledclose", "()V", (void*)ledclose }, { "ledopen", "()I", (void*)ledopen } }; #if 0 int register_android_server_LedService(JNIEnv *env) { return jniRegisterNativeMethods(env, "libledctrl", method_table, NELEM(method_table)); } #endif JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; jclass cls; printf("Enter the JNI Onload\n"); if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { printf("Version Error in JNI OnLoad\n"); return JNI_ERR; /* JNI version not supported */ } // Package/ClassName // The package is define in the java first line cls = (*env)->FindClass(env, "libledctrl/HWLedCtrl"); if (cls == NULL) { return JNI_ERR; } /* 2. map java hello <-->c c_hello */ if ((*env)->RegisterNatives(env, cls, method_table, sizeof(method_table)/sizeof(JNINativeMethod)) < 0) return JNI_ERR; return JNI_VERSION_1_4; } ~~~ 需要注意的是: ~~~ cls = (*env)->FindClass(env, "libledctrl/HWLedCtrl"); ~~~ 这里面需要填写正确的来调用的class,这个路径按照Package/Class来写。 然后进行编译链接成动态库,要使用Android提供的ToolChain需要先lunch target,免去写长长的路径Prefix: ~~~ source build/ensetup.sh lunch sabresd_6dq-eng ~~~ 第一步是compile,将C源码(ledctrl_jni.c)编译成object(ledctrl_jni.o)文件: ~~~ arm-linux-androideabi-gcc -I bionic/libc/bionic -I libnativehelper/include/nativehelper -isystem system/core/include -isystem hardware/libhardware/include -isystem hardware/libhardware_legacy/include -isystem hardware/ril/include -isystem libnativehelper/include -isystem frameworks/native/include -isystem frameworks/native/opengl/include -isystem frameworks/av/include -isystem frameworks/base/include -isystem external/skia/include -isystem out/target/product/sabresd_6dq/obj/include -isystem bionic/libc/arch-arm/include -isystem bionic/libc/include -isystem bionic/libstdc++/include -isystem bionic/libc/kernel/common -isystem bionic/libc/kernel/arch-arm -isystem bionic/libm/include -isystem bionic/libm/include/arm -isystem bionic/libthread_db/include -fno-exceptions -Wno-multichar -msoft-float -fpic -fPIE -ffunction-sections -fdata-sections -funwind-tables -fstack-protector -Wa,--noexecstack -Werror=format-security -D_FORTIFY_SOURCE=1 -fno-short-enums -march=armv7-a -mfloat-abi=softfp -mfpu=neon -include build/core/combo/include/arch/linux-arm/AndroidConfig.h -I build/core/combo/include/arch/linux-arm/ -Wno-unused-but-set-variable -fno-builtin-sin -fno-strict-volatile-bitfields -Wno-psabi -mthumb-interwork -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -DNDEBUG -g -Wstrict-aliasing=2 -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -c ledctrl_jni.c -o ledctrl_jni.o ~~~ 第二步则是完成链接ledctrl_jni.o --> libledctrl.so: ~~~ arm-linux-androideabi-g++ -nostdlib -Bdynamic -fPIE -pie -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -Lout/target/product/sabresd_6dq/obj/lib -Wl,-rpath-link=out/target/product/sabresd_6dq/obj/lib ledctrl_jni.o -Wl,--whole-archive -Wl,--no-whole-archive -lc -lstdc++ -lm -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--icf=safe -Wl,--fix-cortex-a8 -Wl,--no-undefined -shared -fPIC -o libledctrl.so ~~~ ### 链接时候需要的注意 需要注意的是链接的时候需要写明-lc,即要链接libc,否则在加载的时候后出现问题,例如没有加入的话运行的时候会出现下面的提示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9b1ae26.jpg) 这里提示的是找不到符号__sprintf_chk,而这个符号我们可以确定其来源于libc,操作命令如下: ~~~ readelf --syms libc.so ~~~ 结果中可以看到这个函数的定义: ~~~ File: out/target/product/sabresd_6dq/system/lib/libc.so Symbol table '.dynsym' contains 1243 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000c9d8 88 FUNC GLOBAL DEFAULT 8 __init_tls 289: 000138bd 30 FUNC GLOBAL DEFAULT 8 __sprintf_chk ~~~ 这个是查找一个symbol的方法。 ### 编译用的ToolChain的注意 另外,看到很多人直接使用了arm-linux-gnueabi-gcc来编译链接,这样子做并不合理,因为我们是需要运行在Android上面,而Android系统编译使用的是arm-linux-androideabi-gcc来编译的,所以最后Android系统打包的libc等各种库都是用这个toolchain里面的。否则容易出现问题。 添加Clib库到APK项目中 在app/libs中添加armeabi目录,并将libledctrl.so拷贝进来,然后project的structure如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9b3b00d.jpg) 添加完成后build project,并generate APK,然后我们可以解压APK看到lib库已经打包进去了: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9b58813.jpg) 然后就可以运行查看效果了。 ## class名字的一致性 前面提到C代码中需要查找(FindFlass)对应来调用其的java class,如果名字不对应,那么就会提示找不到对应的class,例如下面在C中要查找的是LedCtrl class,自然找不到: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9b7cb80.jpg) ## native函数名字的一致性 如果java中native的函数声明与实际的C中的函数不一致也会报错,而且要注意函数的返回类型与传入的参数类型,例如下面这个就是不匹配的结果: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc9b9bff7.jpg) ## APK调试方法 ### 查看so是否加载 这个可以通过adb中的log信息来查看,同时也可以查看加载器是否加载了so,例如我们可以去cat /proc/PROCESS/maps文件查看,也可以使用pmap命令来查看: ~~~ root@sabresd_6dq:/ # pmap 9196 | grep led 415a7000 16K r--s /data/app/com.hexiongjun.led-1.apk 65c6f000 16K r--s /data/app/com.hexiongjun.led-1.apk 6644b000 156K r--s /data/app/com.hexiongjun.led-1.apk 664a7000 4K r-xp /data/app-lib/com.hexiongjun.led-1/libledctrl.so 664a8000 4K r--p /data/app-lib/com.hexiongjun.led-1/libledctrl.so 664a9000 4K rw-p /data/app-lib/com.hexiongjun.led-1/libledctrl.so ~~~ 我们可以看到后面so被加载了。 ## logcat查看某个进程的log 一条命令,后面的30585就是process id: ~~~ logcat -v process | grep 30585 ~~~ ## 代码位置 [https://github.com/tonyho/ApkJNI01](https://github.com/tonyho/ApkJNI01.git)
';

Freescale IMX6 Android (3): 手动制作Android启动用SD卡 省去MFGTOOLS烧写

最后更新于:2022-04-01 11:41:36

Freescale IMX6的烧写方式在Windows下面一般使用MFGTools,但是TQ提供的MFGTools在Windows 10下面无法工作,USB的驱动不正常。于是想到Linux下面的烧写方式,结果一样出现问题,且因为TQ没有提供一些烧写程序的源码,因此无法更改与调试。因此TQ提供的两种烧写Android的方式都无法使用了,加之前面博文中,我也尝试使用NFS来启动,也没有达到需要的效果,因此这篇文章说一下自己原创的直接制作SD卡来启动Android。 本文默认大家很熟悉Linux与Android的启动流程,如果不是很熟悉可以先看看我的分享:[Linux启动流程 关于initrd与initramfs的区分及其发展历程](http://blog.csdn.net/sy373466062/article/details/50325047) Linux系统下面使用 SD卡烧写Android的尝试:这个是TQ给出的方法不可行,可以跳过,直接到下面的分区mount开始看 因为这个方案是TQ给出的,所以我尝试的去使用了一下,结果无法使用,因此也写下来,希望其他人不用会去浪费时间,或者帮忙指出我的操作哪里不对。 按照官方的说法制作好了烧写用的SD卡,并且从SD卡启动之后,rcS中会自动去mount SD卡,然后解析EmbedSky.ini配置文件,但是解析的结果似乎不正确,以下是启动之后看到了rcS内容: ~~~ # cat /etc/init.d/rcS #!/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel # # Trap CTRL-C &c only in this shell so we can interrupt subprocesses. # mount -a mkdir -p /dev/pts mount -t devpts devpts /dev/pts mount -n -t usbfs none /proc/bus/usb echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s mkdir -p /var/lock #modprobe s5pv210_wm8960 #modprobe ds18b20 #modprobe rt5370sta hwclock -s EmbedSky_wdg & ifconfig lo 127.0.0.1 net_set & /etc/rc.d/init.d/netd start /etc/rc.d/init.d/httpd start #InputAdapter #pda & /bin/hostname -F /etc/sysconfig/HOSTNAME autoDownload [root@EmbedSky /]# ~~~ 步骤非常直接,配置好各种需要的材料之后,直接调用一个可执行的ELF程序autoDownload,然后就没有反应了,使用ps命令也无法看到有此进程在工作。 ## 分区mount 前面的博客([Freescale IMX6 Android NFS启动问题汇总](http://blog.csdn.net/sy373466062/article/details/50208247))中提到了Android的启动过程,其中有一个步骤是mount各个分区,这个分区的mount list有一个和Linux 发行版类似的配置文件,IMX6的这个文件是fstab.freescale,其内容如下: ~~~ # Android fstab file. #<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> # The filesystem that contains the filesystem checker binary (typically /system) cannot # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK /devices/platform/sdhci-esdhc-imx.1/mmc_host/mmc1 /mnt/extsd vfat defaults voldmanaged=sdcard:auto /devices/platform/fsl-ehci /mnt/udisk vfat defaults voldmanaged=sdcard:auto /dev/block/mmcblk0p5 /system ext4 rw wait /dev/block/mmcblk0p4 /data ext4 nosuid,nodev,nodiratime,noatime,nomblk_io_submit,noauto_da_alloc,errors=panic wait,encryptable=footer /dev/block/mmcblk0p6 /cache ext4 nosuid,nodev,nomblk_io_submit wait /dev/block/mmcblk0p7 /device ext4 rw,nosuid,nodev wait /dev/block/mmcblk0p1 /boot emmc defaults defaults /dev/block/mmcblk0p2 /recovery emmc defaults defaults /dev/block/mmcblk0p8 /misc emmc defaults defaults ~~~ 其中各个分区的信息可以参考这篇文章:[安卓系统分区介绍](http://cn.club.vmall.com/thread-985489-1-1.html),下面是我给出自己的理解,不保证准确: - /system是system.img对应的partition - /boot存放的应该是boot.img文件 - /recovery放recovery.img文件,手机进入recovery模式的时候使用 - /data分区,存放的是用户配置信息,以及安装的用户程序 - /cache分区,就是我们平常刷手机的时候双清中的一个分区,放一些程序运行过程中的cache - /device分区,一些设备特有文件的配置或者资源文件,例如可能是fireware,这个在标准的Android中没有 对于我们而已,要制作一个启动用的SD卡,我们需要创建所有Android系统必须的分区,同时将对应的文件放进去,以上分区只有recovery分区可以不需要,因为我们不会进入到recovery模式中去,因此,我们第一步就是对SD卡分区。 ## 对SD卡分区 可以使用Gparted GUI图形化软件来进行分区,我使用的是4GB的SD卡,分区如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc992274f.jpg) 注意后面的扩展分区,在新建分区的时候需要选择扩展分区类型而不是主分区,因为MBR格式的分区表只能有4ge主分区,但是我们需要的分区数目远大于4个。注意第一个分区(label为init的分区)是我额外添加的一个分区,原生中没有。 ## 拷贝文件到分区 文件拷贝如下: - root下面的文件 -->  init分区 - system下面的文件 --> system分区 更改fstab配置文件 因为TQ提供的Android编译出来之后,默认是烧写到eMMC中的,而我们使用的是SD卡,因此mmc的设备号是不一样的,同时根据: - eMMC与SD卡的特性,eMMC的初始化比SD卡快 - 在Makefile中更靠前,因此init段在更前面,因此会更快的运行init 因此,eMMC在TQIMX6Q中是mmc0,而SD卡是mmc1。 另外也可以在启动之后,拔插SD卡来确认,拔掉的时候会有类似下面的提示,也可以确定SD卡的名称: ~~~ mmc1 removed ~~~ 然后我们就可以更改fstab(就是在out/target/XXX/init中的fstab.freescale)了,将里面的mmc0改成mmc1,同时将各个分区变更一下,例如SD卡挂载到了/media/init下面,那么可以使用 gvim /media/init/fstab.freescale命令来修改,如下: ~~~ root@sabresd_6dq:/ # cat /fstab.freescale # Android fstab file. #<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> # The filesystem that contains the filesystem checker binary (typically /system) cannot # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK /devices/platform/sdhci-esdhc-imx.1/mmc_host/mmc1 /mnt/extsd vfat defaults voldmanaged=sdcard:auto /devices/platform/fsl-ehci /mnt/udisk vfat defaults voldmanaged=sdcard:auto /dev/block/mmcblk1p2 /system ext4 rw wait /dev/block/mmcblk1p3 /data ext4 nosuid,nodev,nodiratime,noatime,nomblk_io_submit,noauto_da_alloc,errors=panic wait,encryptable=footer /dev/block/mmcblk1p5 /cache ext4 nosuid,nodev,nomblk_io_submit wait /dev/block/mmcblk1p6 /device ext4 rw,nosuid,nodev wait root@sabresd_6dq:/ # ~~~ 注意结合前面的GParted分区图示,根据自己的实际情况填写。这里配置了/system,/data,/chace与/device分区; 这些就够了。 修改完成后,保存并sync,然后将SD卡查到机器上面。 如果没有更改这个fstab,那么将会在下面的log后没有任何输出: ~~~ Freeing init memory: 236K ~~~ 将这些拷贝操作,与fstab文件打包放到了csdn,供参考,下载链接:[imx6 手动制作Android启动用SD卡脚本与fstab](http://download.csdn.net/detail/sy373466062/9343265)   ## 使用SD卡启动Android 我们已经制作好了启动Android的SD卡,还记得前面博客说道的Android启动顺序吗? 由这个顺序,我们可以使用下面这些命令从uboot来启动kenrel与Android: ~~~ set serverip 192.168.2.100;set ipaddr 192.168.2.111; set bootargs 'rootwait console=ttySAC0,115200n8 root=/dev/mmcblk1p1 debug ignore_loglevel init=/init vmalloc=400M androidboot.console=ttySAC0 androidboot.hardware=freescale video=mxcfb0:dev=hdmi,1280x720MM@60,if=RGB24,bpp=32 video=mxcfb1:off video=mxcfb2:off fbmem=48M' tftp 0x10800000 192.168.2.100:imx6/uImage;bootm 0x10800000 ~~~ 其中需要注意的是'root='参数,这个是以前不一样的地方,同时添加了rootwait,等待SD卡Probe。 启动之后,输入mount可以查看mount信息: ~~~ root@sabresd_6dq:/ # mount rootfs / rootfs rw 0 0 /dev/root / ext4 ro,relatime,user_xattr,barrier=1,data=ordered 0 0 tmpfs /dev tmpfs rw,nosuid,relatime,mode=755 0 0 devpts /dev/pts devpts rw,relatime,mode=600 0 0 proc /proc proc rw,relatime 0 0 sysfs /sys sysfs rw,relatime 0 0 none /acct cgroup rw,relatime,cpuacct 0 0 tmpfs /mnt/secure tmpfs rw,relatime,mode=700 0 0 tmpfs /mnt/asec tmpfs rw,relatime,mode=755,gid=1000 0 0 tmpfs /mnt/obb tmpfs rw,relatime,mode=755,gid=1000 0 0 tmpfs /mnt/shm tmpfs rw,relatime,size=1024k,mode=775,uid=1000,gid=1003 0 0 none /dev/cpuctl cgroup rw,relatime,cpu 0 0 /dev/block/mmcblk1p2 /system ext4 ro,relatime,user_xattr,barrier=1,data=ordered 0 0 /dev/block/mmcblk1p3 /data ext4 rw,nosuid,nodev,noatime,nodiratime,errors=panic,user_xattr,barrier=1,nomblk_io_submit,data=ordered,noauto_da_alloc 0 0 /dev/block/mmcblk1p5 /cache ext4 rw,nosuid,nodev,relatime,user_xattr,barrier=1,nomblk_io_submit,data=ordered 0 0 /dev/block/mmcblk1p6 /device ext4 ro,relatime,user_xattr,barrier=1,data=ordered 0 0 none /sys/kernel/debug debugfs rw,relatime 0 0 /dev/fuse /mnt/shell/emulated fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0 ~~~ 下面则是这些分区的size信息: ~~~ 1|root@sabresd_6dq:/ # df Filesystem Size Used Free Blksize / 96.8M 6.0M 90.8M 1024 / 96.8M 6.0M 90.8M 1024 /dev 381.6M 48.0K 381.6M 4096 /mnt/secure 381.6M 0.0K 381.6M 4096 /mnt/asec 381.6M 0.0K 381.6M 4096 /mnt/obb 381.6M 0.0K 381.6M 4096 /mnt/shm 1024.0K 0.0K 1024.0K 4096 /system 503.9M 234.5M 269.5M 4096 /data 1007.9M 48.8M 959.1M 4096 /cache 503.9M 16.4M 487.6M 4096 /device 503.9M 16.4M 487.6M 4096 /mnt/shell/emulated 1007.9M 48.8M 959.1M 4096 ~~~ ## APK的测试 和前面一样,我们使用Android Studio编译一个HelloWorld程序,并选择这个设备运行,那么就会自动push apk并安装这个apk到设备中,如果没有出现问题,那么就OK了,如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc994839a.jpg) 连接完成后,可以在logcat中看到信息: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc995b8b8.jpg) 新建Android Project的时候需要注意API版本的匹配,不要用比设备更高的Android版本。 同时需要注意打开开发者模式,使能USB调试。 ## 开发过程中遇到的其他问题 OTG线连接不稳定,会不断的出现如下log: ~~~ <span style="font-family:Microsoft YaHei;font-size:18px;">android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null)) android_work: did not send uevent (0 0 (null))</span> ~~~ 正常连接的应该是这样的log: ~~~ android_work: sent uevent USB_STATE=DISCONNECTED android_work: sent uevent USB_STATE=CONNECTED android_usb gadget: high speed config #1: android android_work: sent uevent USB_STATE=CONFIGURED mtp_open ~~~ 同时也可以在PC中使用下面命令来查看dmesg,从而确定是否打开了调试,并确认OTG adb连接是否有问题: ~~~ dmesg | tail ~~~
';

Freescale IMX6 Android (2): Android NFS启动问题汇总

最后更新于:2022-04-01 11:41:34

前面的一篇博文中,提到了要使用自己编译出来的Android来启动,且使用NFS的方式来启动Android,但是在今天的尝试中却遇到了问题。且最终没有解决,但是找到了替换方案,替换方案见下一篇博文。遇到的问题汇总如下,希望可以帮助遇到同样问题的人。板子用的还是TQIMX6Q(见以前的博文)。 ## Android NFS启动的rootfs制作与启动 要制作Android NFS rootfs,需要对Android的启动有一个基本的了解,推荐参考相关书籍。简单而已,Android的启动过程如下: uboot --> kernel --> Android Init in ramdisk(boot.img) --> Init 解析 init.rc --> Init 解析 init.HARDWARE.rc --> 根据initrc中的不同section,执行对应的操作 这里面执行的操作包括: 1. 创建目录/配置目录文件的权限/创建symbol link 1. mount文件系统,包括pesudo(例如debufs/proc)与实际的文件系统(例如system分区) 1. 安装内核模块等 1. 启动各个service,例如vold,bootanmition,让系统拥有软件硬件服务 其中ramdisk(可能位于boot.img中),属于第一阶段的rootfs,init.rc与init.HARDWARE.rc都在这里面,而且这个是一个page cache而非initrd形式的文件系统。 对此,我们可以如下建立一个Android NFS rootfs: 1. 拷贝root(即未打包前的uramdisk中的内容)目录中的文件到Rootfs更目录下 1. 在Rootfs中创建system目录,并将Andriod编译生成目录下面的system目录中的文件拷贝进去 1. 添加这个rootfs到nfs server的配置文件中,例如/etc/exports 最后就是启动了,这个时候只需要机器中有可以启动的uboot即可,在uboot中使用前一篇博文的启动args启动。 ## 问题1:NFS启动Android之后特别卡 这个问题表现为,在NFS启动之后输入ps查看进程,会卡很久才出结果,同时在串口中交互也明显感受到很卡,这个问题是因为NFS的连接不稳定所致,一般NFS不稳定的时候会出现如下log提示: ~~~ nfs: server 192.168.2.100 not responding ~~~ 恢复连接之后: ~~~ nfs: server 192.168.2.100 OK ~~~ 但是有的时候会出现nfs 连接重试等待,这个时候就会卡住。 这个问题的解决方法: 1. 确认PC Linux下网络配置是否变更,网络是否繁忙 1. 系统负荷是否过大而导致nfs server无法被及时的调度到 ## 问题2:Init启动各种service之后,Service会exit 问题表现为启动之后,会有Service开始退出,然后这个Service又启动,然后此service又退出,如此反复。这个问题在[TQIMX6的论坛中也有人遇到了这个问题](http://www.armbbs.net/forum.php?mod=viewthread&tid=21353&extra=page%3D1),但是没有人回复。 我这里的log是如下: ~~~ init: untracked pid 2395 exited nfs: server 192.168.2.100 OK binder: release 2593:2614 transaction 31 in, still active binder: send failed reply for transaction 31 to 2591:2617 init: untracked pid 2592 exited binder: release 2633:2640 transaction 56 in, still active binder: send failed reply for transaction 56 to 2642:2653 init: untracked pid 2632 exited ~~~ service退出后,导致bindler发消息无法被回复,这个service居然还是app_process(PID=2395): ~~~ root 2386 2 0 0 c0145ef8 00000000 S flush-0:12 root 2389 1 832 460 c004c9f0 401bce1c S /system/bin/sh system 2390 1 904 172 c0523a28 40106324 S /system/bin/servicemanager root 2391 1 4032 752 ffffffff 4015fbfc S /system/bin/vold root 2392 1 2128 996 c0132c80 400de4d0 S /system/bin/netd root 2393 1 940 236 c056c5ec 400fff8c S /system/bin/debuggerd system 2394 1 5000 1556 ffffffff 400b6614 S /system/bin/surfaceflinger root 2395 1 5552 872 c00eb200 40105050 D /system/bin/app_process drm 2396 1 4648 1316 c00eb200 40150f72 D /system/bin/drmserver media 2397 1 5052 1408 c00eb200 4015af72 D /system/bin/mediaserver install 2398 1 900 216 c056c5ec 401faf8c S /system/bin/installd keystore 2400 1 3268 996 c0523a28 40172324 S /system/bin/keystore radio 2401 1 5556 708 ffffffff 4010fbfc S /system/bin/rild root 2426 2 0 0 c009d3e8 00000000 S kworker/0:2 root 2441 2389 1156 236 00000000 40188060 R ps root 2473 2392 764 72 c06a2aa8 400b9b80 D /system/bin/iptables root@android:/ # ~~~ 遇到这个问题的解决步骤如下: 1. 查看tomb文件,定位Service退出的信息,如果没有记录下来,就按下面步骤来 1. 在bindler中添加打印log,看看是什么消息没有回复,确定Service在哪个位置退出的 1. 确定Service大概退出的问题位置之后,添加log输出,使用二分法确定位置 其中在Bindler中添加输出调试log的示例如下: ~~~ printk(KERN_INFO "binder: %d:%d transaction failed %d, size" "%zd-%zd\n", proc->pid, thread->pid, return_error, tr->data_size, tr->offsets_size); ~~~ bindler实现位于kenrel代码中的:drivers/staging/android/binder.c ## 问题3:NFS启动后的Android无法安装程序 本来以为解决了前面两个问题,就开始使用了,于是在Android Studio中写了一个helloworld测试程序,结果,push到机器后,无法安装: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc974f113.jpg) 提示的是空间不够,但是实际上,使用的是NFS(几十GB),拥有足够的空间。 想了想,发现我们使用NFS启动与正常冲eMMC启动Android有一个很重要的区别: > system/data分区在nfs环境下面启动不会mount实际的设备(即不会mount某个flash的分区),我们的data/system其实是NFS mount了的rootfs下面的一个普通目录。 鉴于此,我想提示的空间不足其实是因为读取的是这个目录mount设备的空间大小,而我们没有设备mount到这个目录下,当我们要安装一个apk的时候,会到cache与data下面创建文件,此时就会出现错误。因此对于这个问题,我们可以按照如下方法解决: > 将data/system/cache分区mount到实际的设备分区中。 ## 总结 尽管在经过多个问题的解决与折腾之后,我们可以正常的使用NFS启动Android并安装与调试程序,但是实际上与我们最初的目标有些违背了,这样子变得更为麻烦了。因此我决定使用可拔插的SD卡来启动Android。这个是下一篇博文的内容。
';

Freescale IMX6 Android (1): 使用HDMI作为Android显示输出的配置

最后更新于:2022-04-01 11:41:32

## 环境准备与编译 个人使用的是Ubuntu 12.04 X64,针对环境的配置,百度即可。板子使用的是TQIMX6Q。 ### lunch目标 但是,编译的时候使用的是TQ提供的脚本build_sh,根据这个脚本,可以看到lunch的目标是sabresd_6dq: ~~~ $ echo $TARGET_PRODUCT sabresd_6dq ~~~ 即,TQIMX6的Android lunch目标其实使用的是sabred板子的目标,并没有自己添加lunch target,编译完成后,也没有userdata.img: ~~~ 10:50 hexiongjun:TQIMX6_android-4.3 $ echo $OUT /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq 10:51 hexiongjun:TQIMX6_android-4.3 $ ls -l $OUT/*.img -rw-r--r-- 1 hexiongjun hexiongjun 5314560 Dec 4 18:37 /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq/boot.img -rw-rw-r-- 1 hexiongjun hexiongjun 242287 Dec 4 18:37 /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq/ramdisk.img -rw-rw-r-- 1 hexiongjun hexiongjun 794534 Dec 4 18:37 /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq/ramdisk-recovery.img -rw-r--r-- 1 hexiongjun hexiongjun 5865472 Dec 4 18:37 /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq/recovery.img -rw-r--r-- 1 hexiongjun hexiongjun 293601280 Dec 4 18:37 /home/hexiongjun/iMX6Q/TQIMX6_android-4.3/out/target/product/sabresd_6dq/system.img 10:53 hexiongjun:TQIMX6_android-4.3 $ ~~~ 同时也编译了uboot与uImage。 ### 编译可能出现的问题 Android编译使用的是自带的ToolChain,自然不需要再自己配置。但是Host PC中的GCC也需要注意版本,否则会出现问题: ~~~ host C: libcrypto_static <= external/openssl/crypto/bio/b_sock.c external/openssl/crypto/bio/b_print.c:842:2: internal compiler error: Segmentation fault Please submit a full bug report, with preprocessed source if appropriate. See <http://gcc.gnu.org/bugs.html> for instructions. make: *** [out/host/linux-x86/obj/STATIC_LIBRARIES/libcrypto_static_intermediates/crypto/bio/b_print.o] Error 1 ~~~ 看log输出,还以为是Host GCC的Bug,换了多个版本的GCC之后,问题依旧,后来发现make时指定submake过多,将make -j8改成了make libcrypto就好了,比较怪异。 HDMI硬件配置 因为没有TFT屏幕,所以使用了以前用过的改装过的笔记本屏幕,参考我以前的博文:[废物利用: 动手将笔记本的显示器改成多功能显示器 可用于电视盒子以及嵌入式开发](http://blog.csdn.net/sy373466062/article/details/50184341) HDMI显示输出的配置 因为我们使用的是HDMI显示输出,因此需要配置参数,这个参数通过uboot的bootargs env传递到内核中去,使用的笔记本屏幕分配率是1366X768,找了一个接近的1280X720,显示部分的参数如下: ~~~ video=mxcfb0:dev=hdmi,1366x768MM@60,if=RGB24,bpp=32 video=mxcfb1:off video=mxcfb2:off fbmem=48M ~~~ 按需更改。 ## Android的NFS启动 配置好NFS Server与TFTP Server,可以参考我以前的博文:[Ubuntu下nfs server与tftp server的搭建](http://www.hexiongjun.com/?p=75) Android的NFS可以先使用TQ提供的文件系统。如果需要使用自己编译出来的,请看下一篇博文。 然后就是启动板子,在uboot中配置环境变量与启动: ~~~ set serverip 192.168.2.100 set ipaddr 192.168.2.111 set bootargs 'console=ttySAC0,115200n8 root=/dev/nfs nfsroot=192.168.2.100:/home/hexiongjun/nfs-imx6/Android ip=192.168.2.120 debug ignore_loglevel init=/init vmalloc=400M androidboot.console=ttySAC0 androidboot.hardware=freescale video=mxcfb0:dev=hdmi,1280x720MM@60,if=RGB24,bpp=32 video=mxcfb1:off video=mxcfb2:off fbmem=48M' tftp 0x10800000 192.168.2.100:imx6/uImage;bootm 0x10800000 ~~~ 启动后的效果图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572afc960944a.jpg) ADB的配置 在TQIMX6上面开启开发者选项,并启用USB调试,然后在出现的运行USB调试选项中确定。 在Linux端中,添加vendor ID到android adb配置中: ~~~ echo "0x0451" > ~/.android/adb_usb.ini ~~~ 然后用USB线连接板子与PC,使用adb 查看是否连接成功: ~~~ 17:51 hexiongjun:Con $ adb devices List of devices attached emulator-5554 device 0123456789ABCDEF device ~~~ 其中第二项,就是我们的设备。
';

前言

最后更新于:2022-04-01 11:41:29

> 原文出处:[i.MX6 Android HAL实战](http://blog.csdn.net/column/details/imx6-android.html) 作者:[sy373466062](http://blog.csdn.net/sy373466062) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # i.MX6 Android HAL实战 > 对i.MX6 Android HAL的实战,配合APP。
';