Android如何配置init.rc中的开机启动进程(service)

2023-11-09

开篇:为什么写这篇文章

先说下我自己的情况,我是个普通的学生,之前在学校一直做Android应用开发,找实习的时候也一直想找相关的工作,来到现在这家公司以后,由于业务调整,被领导安排去做底层开发,本来我对底层的东西一无所知,加上其实并不感兴趣,其实一开始感觉还是很难的,不过刚刚工作,只有小孩子才在乎喜欢不喜欢,成年人只在乎是否有利。我本着技多不压身的心态,开始了底层开发学习之旅,做Android底层开发的人其实相对应用开发来说少很多,所以网上相关的文章和学习资料也是非常的有限,我在最近两个月时间总算琢磨出点东西,打算写这篇文章分享给大家,给底层的世界填一分彩,不过本人才疏学浅,尚在学习当中,如果有什么错误,请大家不吝赐教。
 

本文到底是讲什么的?

首先我先来解释一下本文到底讲什么的。用一句话来说:本文讲解的主要内容是,如何通过修改Android操作系统源码,来配置一个自定义的开机启动进程。有些人也许会问,这有什么用?问的好,一项实用的技术必然要有用处才会有价值。首先说明的是,如果你的工作或项目只是做一个应用程序app,那本文确实没有什么用处。但如果你的公司做的是Android系统开发,或者本身就是一家做硬件的公司,那本文可能就会有不少用处了;举个例子:假如你们公司做了一台搭载Android的嵌入式设备,这台设备有某个特殊的传感器是一般手机没有的,传感器属于硬件,那想让硬件工作就必然有驱动程序,现在我们想让这个传感器在设备一开机的时候就立刻启动,那我们就需要知道如何配置一个Android的开机启动进程了。而本文正是讲这部分内容的。如果用最简单的一句话描述本文是讲什么的,那其实只要文章题目的一句话,但是其实涉及到的知识又多又杂,加上本人也处在底层开发的探索阶段,所以,涉及到的知识我只讲到我们能用到的深度为止,如果想深入学习,我会附上其他博主对这些知识深入分析的文章的链接。

需要做哪些准备工作?

首先,在硬件上,我希望你能有一块能搭载Android系统的嵌入式开发板,比如我用的是Friendly-arm的NanoPi M3,也许你会问,一台普通的Android手机行不行,这个说实话我没有试过,但是市面上的手机有很多权限是被厂商限制的,所以如果用手机来测试,可能会发生很多迷之问题,所以我建议还是使用一块开发板,不一定非要和我的型号一样,只要能跑Android就行。其次,在软件上,我希望你准备好了一份Android系统的源码,关于如何获取Android的源码我这里就不讲了,网上有很多文章讲这个,你可以从Google官方的渠道获取,如果你使用开发板,一般开发板的提供商也会提供定制的Android源码,那我们就直接从开发板的提供商的网站获取就行了。最后,我希望你有一台装有mac OS或者Linux操作系统的电脑,本文所开展的工作只支持以上两种系统,不支持Windows,其实我比较推荐unbuntu 14.04,这也是我使用的开发板的官方文档所推荐的,你当然也可以用Mac,但是说实话,我最开始开展工作时用的是搭载mac OS 10.12.02的Macbook pro 2016,各种发生迷之错误,其实按道理来说是可以解决的,但是由于做底层开发的人本来就不多,以及用Mac做底层开发的人就更少了,所以网上关于解决这些问题的学习资料以及文章少之又少,导致问题长时间无法解决,浪费时间,所以还是推荐使用ubuntu 14.04。
最后,阅读本文需要哪些知识?本文尽量讲的通俗且浅显易懂,你不需要有任何底层开发的前置知识,你只需要对Android系统有一个比较全面的认识就行,你说你写App的水平不高?这些都没关系,都不会影响你阅读这篇文章。但是我希望你能了解最起码的Linux命令,不用太复杂,最基本的就行,实在不行,Windows的cmd命令总用过吧,只要会用命令行工具进入硬盘下某个目录就行。
 

大体上有哪些知识点

大体上有哪些知识点,我画了以下一张图来描述:
这么一看,一目了然,我们需要将我们自己写的程序(示例中我用的是C语言)将它配置给init.rc这个文件;然后通过编写Android.mk来对它进行编译描述,什么是编译描述呢?我们可以这么理解,只要我们写的这个程序的所在目录下有Android.mk文件,它就可以和Android源码一样用make命令编译;然后我们要通过SEAndroid给我们的程序授权,否则程序会因为权限问题而无法执行;以上这些都是在Android源码中进行修改的;最后,我们将源码编译,刷机,运行,你亲自编译的Android操作系统就可以运行在你的开发板上,同时,你的C程序也可以跟随Android系统的启动而实现开机启动。
 

Android源码目录结构介绍

我相信读者手里已经拿到一份Android源码了,无论它是从哪里获得的(哪怕是同学同事拷贝给你的)。我的源码版本是5.1.1,源码版本对本文的影响不大,只要版本不是太老就行。我们先来对Android源码做个简要的介绍。打开源码的文件夹,我们会发现有很多目录,让我们眼花缭乱,确实,Android系统是一个非常复杂的软件,它必须面面俱到,所以这里面的源码覆盖了方方面面,想要全部把它看完是不可能的,我相信有不少人看过Linux内核源码,光是Linux内核的源码可能就能让一个人一生都无法穷尽,而Linux内核只是Android的最底层,可想而知Android的源码数量有多么庞大,所以我在这里只说几个我们最常用的目录以及本文会涉及到的目录。
不少人做Android都是做应用开发,做应用开发入门以后很多人都想要进阶,这时候就会买一些进阶的书,这些进阶的书很多都涉及Android源码分析,比如比较火的《Android开发艺术探索》,《Andoird源码设计模式解析与实战》等等,我们刚学Android的时候就知道,Android系统分为四层,我们开发的应用处于Application层,而Application层的下面一层是framework层,想做好应用开发,了解一下下层机制是必不可少的,所以我刚才说的源码分析的书籍所讲解的源码都在framework层,大家其实也能发现,这些书里所讲的源码都是java代码。那打开Android源码目录,我们能看到一个framework文件夹,所以framework层的源码就在这个文件夹下,你进去找找,可以找到不少应用开发时常用的API。
上面说的是闲话,现在我来介绍一个等一下会多次使用的一个目录——device,点开这个目录,我们会看到很多手机品牌的名字,比如htc啊,moto啊,samsung啊等等。这是什么意思呢?Android系统会运行在各种品牌的手机上,但是各种品牌的手机在硬件上都会有差异,因此,很多厂商都会对源码进行定制,以修改它的某些部分来配合自家硬件的特性,比如我有某个传感器,别的厂商没有,我的摄像头比较特殊,运行起来比较复杂,这些都属于硬件差异。由于我使用的Friendly-arm提供的源码,所以我可以在这个目录下看到一个friendly-arm的文件夹,里面是针对friendly-arm的开发板定制的一些代码,等会儿我们会多次访问这里。
还有一些等下我们会常用的目录,比如system,external,out等等,这些我们等会儿用到的时候再说。

 

如何在源码中添加自己的可执行文件

我们如果想要一个属于自己的开机启动进程,那首先就要一个我们自己编写的程序了,一般来说,在实际项目中这个程序就是我们想要开机启动的驱动,正如文章开头所说的那样,但是在我们这个例子中,我就不搞那么复杂了,写一个最简单的C语言程序,让它作为我们的开机启动进程。我给这个程序命名为loop,也就是循环的意思,代码如下:
#include<stdio.h>

int main(){

    int i=0;

    for(i;i<100;i++)

    {

      sleep(180);

      printf("I am a process\n");

    }

    return 0;

}

怎么样,是不是非常简单,任何有任何语言编程基础的人都能看懂,我们设置了一个循环,每次在执行循环体的内容前,将主线程休眠180秒,一共循环100次,而循环的内容就是输出一条语句。我为什么要这么做,主要是因为,如果这个程序一下子就执行完成,这个进程就死掉了,那我们就不能在命令行终端中看到这个进程,所以每次循环的时候我都会让它休眠180秒,这样算下来,这个进程理论上可以保活5小时,嗯,够长了,手速再慢的人也能在5小时内用命令行终端登入开发板,然后输入命令查看当前活动的进程了。
现在我们要把这个写好的.c文件放到Android源码目录下,进入Android源码目录,找到vendor文件夹,然后新建一个自己的文件夹,我给它起名叫“while-process”,我们就把它放在这个里面。那么如何才能让它和源码一起编译呢,这时就涉及到了一个知识点——Android.mk。
 

如何编写自己的Android.mk

这里简单介绍下如何编译Android源码,等下还会详细介绍。编译Android源码简单来说,就是做一大堆准备工作,然后在命令行工具中使用make命令进行编译,make命令会在源码目录中遍历所有目录,找到里面的Android.mk文件,然后根据Android.mk文件的内容编译当前目录下的代码。所以说我们可以理解Android.mk配置文件是一个编译指南。关于Android.mk文件所能扩展出来的知识点也是非常的多,这里我也只介绍一些我们最需要和最基本的东西。首先你需要随便找一个Android.mk文件然后用记事本之类的软件打开它。
首先,这行代码必须有:“LOCAL_PATH:= $(call my-dir)”,这行表示所要编译的内容在本Android.mk文件所在的目录下。
然后,Android.mk文件中可以指定多个编译模块,每个编译模块都是以include $(CLEAR_VARS)开始,以include $(BUILD_XXX)结束。然后在这两代码中间我们找到LOCAL_SRC_FILES:= \xxx,后面的xxx就表示你要编译的源文件,比如我刚才写的程序叫loop,这里就写loop.c;我们还会看到LOCAL_MODULE:= xxx,这里表示编译的模块的名字,在这里我们把xxx换成loop。由于我们这个程序非常简单,所以更多的属性在这里就不介绍了,感兴趣的可以去下面这篇文章去查阅:
http://blog.chinaunix.net/uid-25838286-id-3204120.html
你如果不知道怎么创建Android.mk文件,最简单的方法就是随便找一个Android.mk文件拷贝过来,然后删掉些对你没用的语句,增加些对你有用的语句,然后改改某些语句的值或者变量名,就可以用了,如果编译失败,就按照提示进行修改,多试几次就可以了。
 

开始编译Android源码

由于讲解了Android.mk,所以我这里就先讲一下Android源码的编译,不过其实这是最后几步才要做的事情,但是先编译也是有好处的,因为第一次编译的时候时间会非常的长,你可以一边让它编译着,一边了解下面的知识点。当你后面的工作全部完成以后再次编译的时候,只要你不清空之前的编译结果,它就会进行增量编译,也就是只编译修改过的地方,这样只需要几分钟就能完成了。
Android源码怎么在ubuntu下编译,这个网上的文章有很多,我这里简要的说明一下。如果你使用的是开发板,请找到开发板对应的官方文档,然后严格按照文档中的教程一步一步来一般不会遇到什么问题。比如说我用的开发板的文档中给出的步骤大致如下:
1.搭建编译环境。这里需要一堆支持包,你可以先不用管这些支持包是什么,按照如下命令安装即可:
sudo apt-get install bison g++-multilib git gperf libxml2-utils make python-networkx zip
sudo apt-get install flex libncurses5-dev zlib1g-dev gawk minicom
 
2.使用命令行工具的cd命令进入Android源码目录,然后依次执行以下三个命令:
source build/envsetup.sh
lunch aosp_nanopi3-userdebug
make -j8
这三行命令第一行是设置编译环境,第二行是选择编译方案,也就是我刚才所说的选择厂商定制的方案,可以看到nanopi3是我的开发板的型号,所以就选择这个方案,第三行是开始编译,后面的-j8代表的是使用8线程同时编译,使用几个线程同时编译要看你的电脑配置,一般来说和你电脑的处理器有关。例如你的电脑装有四核处理器,每个核有两个线程,那你就可以使用j8,也就是4*2。选对同时编译的线程数量合适,编译的速度就能成倍增长。如果你是第一次编译源码,最慢的话时间可能长达一晚上之久,如果你之前成功编译过,那这次编译就是增量编译,系统只会编译你修改过的地方,很人性化,只需要几分钟。
编译过程中也许会遇到一些问题从而停止编译,比如你无权操作某些文件等等,这些都会在命令行工具中有英文提示,看着提示改就行了,这里的提示的英文也不复杂。建议在执行命令前先进入root用户,也就是先使用su命令。
编译成功以后,我们先进入Android源码目录,然后进入以下这个目录:out/target/product/nanopi3;还是那句话,根据你选择的编译方案,即你使用的开发板型号不同目录会有区别。进入这个目录下面以后我们能看到很多个img镜像文件。
 
3.现在我们把我们编译好的系统刷到我们的开发板上,在这之前先准备一张足够大的SD卡,然后用读卡器把SD卡连接到电脑上。
我使用的方式是使用刷机脚本来刷。首先执行以下两条命令:
git clone https://github.com/friendlyarm/sd-fuse_s5p6818.git
cd sd-fuse_s5p6818
意思就是时候用git下载这个刷机工具,然后进入这个刷机工具的目录。执行以下两条命令:
su
./fusing.sh /dev/sdx
这两条命令的意思是,首先进入root用户,如果你刚才已经进入root用户了,可以不用执行su这条命令,下面一行就是开始刷写,其中sdx是你的sd卡的设备名,请把sdx换成你的设备名,比如我的sd卡叫做sdb,就把sdx换成sdb。
刷写成功后,我们把卡插入开发板,然后用USB线把开发板和电脑连接,然后执行命令:
adb remount
adb shell
这时我们就已经使用命令行工具登入到开发板了,现在我们要找到我们写的loop程序。比如我把它放在了system/bin目录下,使用cd命令进入这个目录,然后使用ls命令就可以看到它存在,如果想要执行它,就使用./命令,loop这个程序就执行了,执行以后我们能看到每三分钟就可以在命令行终端中看到它输出一行语句,但是我们需要确保这个loop这个进程确实存在,那我们就需要用ps -Z命令查看它,但是当前这个命令行工具窗口正在运行程序,无法使用命令,所以我们就新建一个命令行终端窗口,像刚才一样使用adb shell命令登入开发板,然后使用命令ps -Z。就能看到它正在运行了。
编译Android源码这一块不是我们本讲的重点,这一块没有什么概念需要理解,就是一些操作步骤的流水账,我写的比较简单,中间难免有疏漏会造成一些小问题,如果读者出现了问题,可以根据具体问题去网上搜索或者给我留言,网上关于如何编译Android源码如何编译的文章是非常多的,不过我还是建议你参照你使用的开发板的官方文档,严格按照上面的步骤一步一步来,这样发生不必要的问题的概率会小一些。
 

init.rc介绍

我先来做个名词解释,什么是init.rc,那就要从什么是init说起。init是由Android的Linux内核启动的第一个第一个进程,这个进程非常特殊,它的PID永远是1,并且这个进程是不会死亡的,如果它死亡,内核就会崩溃。init进程启动后会fork出很多及其重要的系统进程,比如我们做应用开发的时候都耳熟能详的zygote进程,我们所有的应用程序的进程都由zygote拉起。解释完了init进程,我们再说init.rc,init.rc是一个规定init进程行为和动作的配置文件。init进程可以做哪些事情,都由它规定。关于init.rc的详细介绍,大家可以参考这篇文章:

http://qiangbo.space/2017-01-28/AndroidAnatomy_Init/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io。

我们这里只对init.rc做一个简单的介绍,init.rc文件中只包含两种声明,on和service,我们可以把on称为行为,把service称为服务(这里的服务和应用开发中四大组件中的服务以及通过context.getSystemService()所得到的系统服务都不是一个东西,我一直不知道该怎么给它起名,姑且叫它init服务)。service声明了服务以及服务的各种行为。我们标题中说的开机启动进程就是这里的init服务。service只定义服务,但不能让服务做任何事情,如果你需要服务能够产生启动或者停止等相关动作,你就需要on,每个on下面的有各种命令,其中就包括很多对init服务的操作。这里要提到的是,我们要修改的init.rc文件在device/friendly-arm/nanopi3目录下,也就是厂商定制的版本,如果你使用的是别的开发板,可以去相应的目录找找。我们来看看init.rc中on和service两个典型的定义:

 

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000


    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0


    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    setcon u:r:init:s0
    
    # Set the security context of /adb_keys if present.
    restorecon /adb_keys


    start ueventd
    # create mountpoints
    mkdir /mnt 0775 root system


    ......
    ......
    ......


service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

#后面的一行是注释,这个不用我多说。我们看到里面有很多东西感觉很晕,现在我们只重点关注几行,我们看到on下面有一行是“start ueventd”,而下面service的名字也是ueventd,这表明什么,我想大家都猜到了,那就是在early-init这个on启动了ueventd这个service。对,on就是这样启动sercice的,当然,on还有例如restart以及stop等等其它对service的操作,分别是让service停止并重新启动以及停止,不过on并不只是为service而生的,它还有许多其它的命令,在这里我就不详细介绍了,大家可以去网上搜索相关文章或者看这本书《构建嵌入式Android系统》。

 

我们看完了on再看看service,service我也只是简单介绍一下,service关键字声明了你要定义一个service,而ueventd就是这个service的名字,至于后面的目录则是这个service对应的可执行文件在系统中的位置。注意:这里是说在系统中的位置,也就是在开发板运行你的Android源码编译的系统后的目录,而不是源码的目录,至于Android源码的编译,等下再讲。接下来,我们可以看到service下面也有很多东西,这里我们不叫它们命令了,叫属性或者参数也许会比较好,其中比较重要的是class core,表示这个service属于core这个class,class我们不需要深入去管它,只要把它理解成一组service的集合就行,至于后面的属性,等下我们开始配置service的时候再说。

 

正式开始在init.rc中配置service

一上午已经过去了,不知不觉我已经写了这么多,但是重点才刚刚要开始。
我们先来理一理,我们现在通过上文的讲解已经得到了什么。我们已经编写了一个自己的C语言程序loop,并且把它放在了Android的源码中,Android的源码也编译好了,如何把编译好的Android系统刷到开发板上并启动我们也已经学会了。但是最重要的目的还没实现,那就是让loop这个可执行文件开机就可以被启动。而现在我们正要做的就是这件事。
首先打开Android源码目录,进入device/friendly-arm/nanopi3文件夹下,然后打开inti.rc文件,我们正式开始配置。
首先定义一个service,还记得service是怎么定义的吗,我这里定义的语句如下:service qya system/bin/loop。相信不难理解吧,我们定义了一个服务叫qya,它对应的程序是system/bin目录下的loop。这些上面都讲了。然后我在这个service下面增加几个配置属性:
service qya /system/bin/loop
    class main
    console
    oneshot

其中console表示服务需要并运行在控制台,oneshot表示服务只运行一次,在退出时将服务设置为禁用。你可以根据你的需要来增加这些属性,我写的两个属性并不一定都是必须的。《构建嵌入式Android系统》这本书的第六章对这些属性参数介绍的非常详细,如果网上找不到相应的文章可以拿这本书看看。
也许有人会发现,class main这是个什么东西,好像没讲,然而我并不是忘了,我在这里再详细讲讲。我之前讲过start命令是在on中启动serice,但是通览整个init.rc文件,我们会发现,直接使用start命令启动service的情况非常少,我之前也用这种方式试过几次,但都未能成功。所以我在仔细阅读和查询资料后发现,大多数inti进程fork出来的开机启动进程都是用另外一种方式来启动。我们来看看下面一个on行为的定义:
on property:vold.decrypt=trigger_restart_min_framework
    class_start main
我在这里再简单说一下on,on分为两种,第一种一共有7个,它们是一定会随inti进程的启动而执行的,比如我们上面介绍init.rc的时候展示的early-init正是这7个on中的第一个。而第二种on则是在满足某些特定条件时才会启动的,比如我们这里的这个on就是第二种。我们看到它下面有一条class_start main命令,而我们的service下面第一个属性正是class main。所以可以理解了,这个class_start main命令是启动main下面所有service。通览init.rc文件,我们会发现属于main的service非常多,所以我们的service也就搭一趟便车,挤上main的队伍。
 

什么是SELinux/SEAndroid?

看起来我们最后一步已经做了,把我们的loop程序成功添加到init.rc的文件中。你以为这样你就成功了吗?图样图森破!我们将会面临本文从开头到现在为止最大的挑战。我们的进程会被SEAndroid这个东西禁止掉,从而你无论怎么ps -Z你也不会看到它的存在。那么,SEAndroid到底是个什么东西?如果你想深入研究它,那可得好好花上一段时日了,我在这里给出两位大神的系列文章,专门分析什么是SEAndroid。这两位大神分别是罗升阳前辈和阿拉神农前辈,两人讲解SEAndroid的文章地址如下:
http://blog.csdn.net/luoshengyang/article/details/35392905
http://blog.csdn.net/innost/article/details/19299937/
其中,罗升阳老师的文章比较长,分析的比较理论,有助于你全面而细致的了解SEAndroid。而阿拉神农的文章则更加实用,让你快速能看懂这个东西,但是理论上并没有罗老师的深,大家可以各取所需。
我在这里越俎代庖的稍微讲一下什么是SEAndroid。老习惯,一句话:Android的系统安全机制。它来源于Linux系统中的SELinux。关于它们的历史我这里也都不讲了,总之SEAndroid是这样管理权限的:凡是任何想要运行的进程,想要做任何事情,都必须在安全策略配置文件中赋予权限,如果没有声明某个权限,那它就没有这个权限。要理解其实很容易,做应用开发的时候,我们常常需要在AndroidManifest.xml文件中赋予应用权限。比如,如果你的应用想要读写磁盘数据,那你就要写permission语句,赋予它读写磁盘的语句,如果你的应用想要访问网络,那你就需要写一条关于网络的permission语句,以准许它访问网络。SEAndroid也是类似的东西,你的进程想要干什么,你就得给它写一个.te文件,然后在文件中使用allow语句赋予它权限,不同的是SEAndroid的安全策略文件.te比AndroidManifest.xml可是难写多了。
我这里对编写.te文件和上面Android.mk一样,不做深入介绍。想详细了解如何编写.te文件,就去参考阿拉神农前辈的文章。我这里只做一个简单的介绍。
我们随便来找一条.te文件中的allow语句:allow netd proc:file write。这条语句是阿拉神农的文章中给出的示例,他在文章详细分析了这条语句每个词是干什么的,但我在这里也不多说,还是老惯例,一句话:允许netd进程对proc type的file进行写(write)操作。
我们这里把这条语句换一下:写成:allow a b:c d。那意思就是允许a进程,对c这个b类型的objec class进行d操作。是不是一下子就理解了?不过这里的类型(type)是SEAndroid中的概念,很难三言两语说清楚,所以请看上面的文章,而file也不是我们通常理解的文件,而是一种object class,表示一种可以被操作的对象,比如,除了file以外还有Dir,Socket等等,由系统规定,这里也不展开了。
到这里我再废话两句,SEAndroid把操作系统中的东西分为两种,能发起动作的进程,以及只能被进程操作的文件,而allow语句则就是规定允许谁对谁做什么的。其实SEAndroid的知识远不止这些,还有例如MLS分级系统等等,不过这里就不讨论了,再提示一下,想深入了解的,看上面两篇文章吧!
好的,现在我们要开始真正动手了。首先进入Android源码目录,然后进入:external/sepolicy。还是和上面一样,我也不知道怎么创建一个.te文件,我们直接随便找个.te文件,不过说是随便,我们不能真的随便,我们也要想一想,怎么做成功率高,记得我说过我们自定义的service是属于main这个class的吧,那我们就找一个同样在main这个class下面的其他服务的.te文件来修改,因为在同一个class中的进程,所需的权限应该是相近的。
好,我们找到一个,然后把它的名字改成loop.te。注意啊,我们的service的名字叫qya,但是我们要执行的程序叫loop,SEAndroid是赋予进程权限而不是赋予service权限,所以我们的.te名字叫loop.te而不是qya.te。.te文件中除了allow语句以外还有一些其它语句,比如type等等,它们是什么意思,大家就自己去查一查吧,
我这里给出大家一个投机取巧的办法,如果你不想现在了解SEAndroid,那我就教你一个办法,比如我的.te文件是复制uncrypt的,那进到这个文件里面以后,就把文件中所有写uncrypt的地方全部改成loop,一般来说就可以了。如果编译的时候不通过,你就仔细看错误提示,我当时不通过的原因是有两行allow语句和domain.te文件中的neverallow语句相冲突,看字面意思也能明白,neverallow的意思是从不允许,如果这里允许了一件事情,那里又不允许这件事情,势必发生冲突,那我们只需要把我们的.te文件中的相关allow语句注释掉或者删掉就行了。
 

最后一步:重新编译刷机并运行

现在我们该修改的东西终于全部改完了,那我们要做的就是重新编译,怎么编译上面已经介绍过了,由于这次只是增量编译,所以只需几分钟就OK,我们把它刷到开发板上,然后给开发板插上USB线和电脑连接,然后在命令行工具中执行两条命令:
adb remount
adb shell
现在我们的命令行工具已经登入到开发板上了,直接使用命令ps -Z,我们就能在窗口中看到我们的进程正在后台运行了,我们没有用./命令去执行loop,所以它就是被init进程fork出来的。命令行窗口的截图如下所示:
 
我们可以看到最下面的我们最熟悉的进程zygote,它的上面就是我们自定义的loop进程,自此,自定义int.rc开机启动service成功,我们大功告成。
 

总结:

按照惯例,还是得总结一下。我们来理一理本文所涉及到的知识点,有:编译Android源码,Android.mk文件的编写,修改Android源码,init.rc介绍,SEAndroid。每个知识点都是为了我们能成功运行自定义service而讲解,所以讲解的深度其实都是不够的,我在文中不少地方都已经贴出了详细介绍每个知识点的博文的地址供大家后续学习。加上本文篇幅也算比较长的了,加上介绍的知识又多又杂,难免有疏漏之处,如果你按照本文的方法在配置过程中出现了失败的地方,可以根据具体问题上网搜索,只要坚持,一定可以解决。Android底层的知识的特点就是多而杂,所以做底层开发最需要的就是耐心,祝所有走在底层开发的小伙伴都能学习顺利!

 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android如何配置init.rc中的开机启动进程(service) 的相关文章

  • 如何对这个字符串进行子串化

    我想得到这个字符串的 4 个部分 String string 10 trillion 896 billion 45 million 56873 我需要的4个部分是 10万亿 8960亿 4500万 和 56873 我所做的是删除所有空格 然
  • 在包“android”中找不到属性“backgroundTint”的资源标识符

    我发现了一些视图 xml 属性 例如backgroundTint backgroundTintMode 但是当我使用它作为视图属性定义时 Eclipse 显示错误 No resource identifier found for attri
  • StrictMode 策略违规:我的应用程序中存在 android.os.strictmode.LeakedClosableViolation?

    Android 开发新手 第一次在我的应用程序上尝试 StrictMode 我注意到以下内容 并想知道这是否是我的应用程序或库中的问题 我不太清楚 谢谢你 D StrictMode StrictMode policy violation a
  • 在 ViewPager Fragments 中使用 Master/Detail 模板(下载链接)

    工作代码 https github com lukeallison ViewPagerMasterDetail https github com lukeallison ViewPagerMasterDetail Android 主 详细流
  • Android 后退按钮无法与 Flutter 选项卡内的导航器配合使用

    我需要在每个选项卡内有一个导航器 因此当我推送新的小部件时 选项卡栏会保留在屏幕上 代码运行得很好 但是 android 后退按钮正在关闭应用程序而不是运行 Navigator pop import package flutter mate
  • 如何以编程方式检查 AndroidManifest.xml 中是否声明了服务?

    我正在编写一个库 该库提供了一项服务 其他开发人员可以通过将其包含在他们的项目中来使用该服务 因此 我无法控制 AndroidManifest xml 我在文档中解释了要做什么 但一个常见的问题是人们忽略了将适当的 标记添加到其清单中 或者
  • 如何在android中获取Camera2 API的当前曝光

    In android hardware Camera旧的 我使用下面的代码获取当前曝光并获取它Camera Camera Parameters param mCamera getParameters currentExposure para
  • Android 中 Kotlin 协程的正确使用方式

    我正在尝试使用异步更新适配器内的列表 我可以看到有太多的样板 这是使用 Kotlin 协程的正确方法吗 这个可以进一步优化吗 fun loadListOfMediaInAsync async CommonPool try Long runn
  • Android SIP 来电使用带有广播接收器的服务

    大家好 其实我正在尝试创建一个应用程序 支持基于 SIP 通过互联网进行音频呼叫 这里使用本机 sip 我遇到了来电问题 我已经完成了服务的注册部分 但是在接听电话时我无法接听电话 请帮助我 Service file package exa
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • 发布android后更改应用内购买项目的价格

    在 Google Play 上发布后 是否可以更改应用内购买商品的价格 我假设该应用程序也已发布 完整的在线文档位于http developer android com http developer android com也http sup
  • 如何使用 IF 检查 TextView 可见性

    我有一个 onCheckedChangeListener 来根据选择的单选按钮显示文本视图 我有 1 个疑问和 1 个难题 想知道是否有人可以帮助我 问题 您能否将单选组默认检查值设置为 否 单选按钮 以便一开始就不会检查任何内容 问题 如
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何确定对手机号码的呼叫是本地呼叫还是 STD 或 ISD

    我正在为 Android 开发某种应用程序 但不知道如何获取被叫号码是本地或 STD 的号码的数据 即手机号码检查器等应用程序从哪里获取数据 注意 我说的是手机号码 而不是固定电话 固定电话号码 你得到的数字是字符串类型 因此 您可以获取号
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • android sdk 的位置尚未在 Windows 操作系统的首选项中设置

    在 Eclipse 上 我转到 windows gt Android SDK 和 AVD Manager 然后弹出此消息 Android sdk 的位置尚未在首选项中设置 进入首选项 在侧边栏找到 Android 然后会出现一个 SDK 位
  • Crashlytics 出现 Android Studio 构建错误

    我正在尝试将 CrashLytics 与 Android Studio 和 gradle 一起使用 但出现一个令人困惑的错误 java lang NoSuchMethodError 我的 build gradle 是 buildscript
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview
  • 强制 Listview 不重复使用视图(复选框)

    我做了一个定制Listview 没有覆盖getView 方法 Listview 中的每个项目都具有以下布局 联系布局 xml

随机推荐

  • qt 中lineEdit->setText()输出double

    在qt中需要将获取到的double 值在ui界面上显示出来 便于观察 但是lineEdit控件的setText 要求的参数是string 所以我们先要进行转化 将double 转化为string QString QString number
  • 计算方法实验(二):龙贝格积分法

    Romberg积分法数学原理 利用复化梯形求积公式 复化辛普生求积公式 复化柯特斯求积公式的误差估计式计算积分 a b f x
  • 有效的数独

    LeetCode 之 有效的数独 判断一个 9x9 的数独是否有效 只需要根据以下规则 验证已经填入的数字是否有效即可 数字 1 9 在每一行只能出现一次 数字 1 9 在每一列只能出现一次 数字 1 9 在每一个以粗实线分隔的 3x3 宫
  • python机器学习-乳腺癌细胞挖掘(基于真实美国临床数据)

    随着人们生活水平提高 大家不仅关注如何生活 而且关注如何生活得更好 在这个背景下 精准治疗和预测诊断成为当今热门话题 据权威医学资料统计 全球大约每13分钟就有一人死于乳腺癌 乳腺癌已成为威胁当代人健康的主要疾病之一 并且随着发病率的增加
  • Error mounting /dev/sr0 at /media/ VBox

    重新安装Linux映像 sudo apt get install reinstall linux image uname r
  • IBM WAS简介

    IBM WAS简介 IBM WAS 的全称是 IBM WebSphere Application Server 和 Weblogic 一样 是 当前主流的 App Server 应用服务器 之一 App Server 是运行 Java 企
  • DataWhale集成学习(下)——Task14 案例分析1幸福感预测

    目 录 背景介绍 数据信息 评价指标 案例 背景介绍 数据来源于国家官方的 中国综合社会调查 CGSS 文件中的调查结果中的数据 数据来源可靠可依赖 用139维的信息来预测其对幸福感的影响 数据信息 139维 8000余组 幸福感预测值为1
  • 【indemind双目惯性相机调试】sudo后找不到命令问题,环境变量问题

    问题1 报错 kanhao100 ubuntu x86 roslaunch imsee ros wrapper start launch RLException start launch is neither a launch file i
  • 深度学习_调参经验

    面对一个图像分类问题 可以有以下步骤 1 建立一个简单的CNN模型 一方面能够快速地run一个模型 以了解这个任务的难度 卷积层1 卷积核大小3 3 卷积核移动步长1 卷积核个数64 池化大小2 2 池化步长2 池化类型为最大池化 激活函数
  • leetCode热题46-51 解题代码,调试代码和思路

    前言 本文属于特定的六道题目题解和调试代码 正所谓磨刀不误砍柴功 下面我做这几篇文档对于涉及的题型或者数据结构的分析都很有帮助 贴出来仅供参考 1 69 x 的平方根 Easy 2022 12 28 101 2 22 括号生成 Medium
  • JSP核心标签库

    1 一般标签 在JSTL中 一般标签主要用在输出 设置变量值和错误处理等 这些是JSTL中使用最多的标签 1 计算一个表达式的值 然后把计算的结果输出到当前的JspWriter对象 调用的结果和Servlet中out println 语句效
  • linux 删除指定文件夹包含指定字符串的文件会把所有子文件夹包含的都删除

    需求就是在linux服务器上面删除 指定文件夹里面所有包含 delete 内容的文件 并且所有此文件夹里面的子文件夹查出来也要删除掉 使用以下脚本可以进行实现 grep r l delete data xargs rm rf 脚本说明 gr
  • 机器学习-fp16相乘

    1位符号位 5位指数位 10位尾数位 共16位 内存占2个字节 具体fp16表示法可以参照 机器学习 fp16表示 运算步骤 检查操作数中是否有0 Inf NaN NaN a Nan Inf 0 Nan Inf 0 Nan Inf a In
  • UTSim:无人机空中交通集成、控制和通信的框架和模拟器

    计算机仿真资源的可用性有利于无人机的开发和在现实应用中的集成 因为它们降低了成本 风险 开发和测试时间 并提高了安全水平 一些研究人员为自己的定制特殊无人机和应用开发了自己的模拟环境 然而 使用多个无人机仿真环境很难建立可持续的研究 因为很
  • CSAPP第三版运行时打桩Segmentation fault

    CSAPP第三版7 13 3节提到了运行时打桩机制 它可以在运行时将程序中对共享库函数的调用进行截获 替换为执行自己的代码 这个机制基于动态链接器的LD PRELOAD环境变量 如果LD PRELOAD环境变量被设置为一个共享路径名的列表
  • docker搭建registry私有仓库(centos7)

    搭建环境 master节点 registry端 192 168 1 10 node节点 客户端 192 168 1 20 关闭防火墙和selinux master和node节点都要关闭 root master systemctl stop
  • 【夜莺监控方案】04-k8s集群监控(下)(kube-state-metrics+cadvisor+prometheus+n9e及FAQ)

    文章目录 前言 4 接入prometheus 5 接入n9e 5 1 手动接入 方法一 5 2 导入模板 方法二 6 FAQ 6 1 集群数据时常取不到 现象 解决 前言 相关文档如下 03 k8s集群监控 上 4 接入prometheus
  • 【Navicat11安装教程】

    1 下载安装包 网盘地址 链接 提取码 bbqp 下载之后会得到这样的一个文件夹 2 安装 点击navicat111 mysql cs x64 exe安装软件 安装完成后 将PatchNavicat exe文件复制到安装路径下 如图 操作完
  • idea 工程目录横向变纵向【亲测可用】

    idea 目录横向变纵向往上搜好多都没啥用 下面亲测可用三步走 1 删除项目文件夹下的 idea文件夹 横向时点击 project 然后在 idea 下右击 Delete 就好了 2 关闭IDEA 3 重新用IDEA工具打开项目 然后就 O
  • Android如何配置init.rc中的开机启动进程(service)

    开篇 为什么写这篇文章 先说下我自己的情况 我是个普通的学生 之前在学校一直做Android应用开发 找实习的时候也一直想找相关的工作 来到现在这家公司以后 由于业务调整 被领导安排去做底层开发 本来我对底层的东西一无所知 加上其实并不感兴