Android exec 执行脚本文件例子

本文介绍了一个Android应用程序如何通过Java代码执行设备上的Shell命令并获取输出的方法。该示例展示了如何创建按钮来触发不同的Shell命令,如列出文件、执行特定脚本等,并将输出显示在界面上。
package weijing.myself;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import android.app.Activity;
import android.app.IntentService;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class AndroidExecActivity extends Activity {
	TextView text; 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        text = (TextView) findViewById(R.id.text);  
        
        Button btn_ls = (Button) findViewById(R.id.btn_ls);  
        btn_ls.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {                 
                do_exec("cat /data/setip/init.usblpmod.sh");
            }             
        });  
        Button btn_cat = (Button) findViewById(R.id.btn_cat);  
        btn_cat.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {                 
              //  do_exec("insmod /system/lib/modules/3.0.8-android-x86-tegav2+/kernel/drivers/usb/class/usblp3.ko"); 
            	//System.SystemProperties.set("ctl.start", "ifconfig_test");
            	

            	System.setProperty("persist.seriver.usblp_test.enable", "1");
            	String ret = System.getProperty("persist.seriver.usblp_test.enable", "");
            	   System.out.println("服务执行成功!!!!!!!!!!!" +ret);
            	
            	if(ret != null && ret.equals("stopped"))
            	  {
            	      System.out.println("服务执行成功!!!!!!!!!!!");
            	     

            	  }
            }             
        });          
        Button btn_rm = (Button) findViewById(R.id.btn_rm);  
        btn_rm.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {                 
                do_exec("start usblp_test");  
                System.out.println("服务执行成功!!!!!!!!!!!");
//            	System.setProperty("ctl.start", "usblp_test");
            }             
        });      
        Button btn_sh = (Button) findViewById(R.id.btn_sh);  
        btn_sh.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {                 
                do_exec("/system/bin/sh /mnt/sdcard/test.sh 123");  
            }             
        });           
    }  
      
   private  String do_exec(String cmd) {  
        String s = "/n";  
        try {  
            Process p = Runtime.getRuntime().exec(cmd);  
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));  
            String line = null;  
            while ((line = in.readLine()) != null) {  
                s += line + "/n";                 
            }  
        } catch (IOException e) {  
            // TODO Auto-generated catch block   
            e.printStackTrace();  
        }  
        text.setText(s);  
        return cmd;       
    }  

   
}

<think> 我们正在讨论如何向Android启动脚本中添加可执行的后台程序。根据之前的引用和讨论,我们可以通过修改init.rc或类似的启动脚本来添加一个service,然后让这个service运行我们的可执行程序或脚本。 关键点: 1. 在init.rc中定义一个service,指定可执行程序的路径。 2. 设置service的属性,比如user、group、oneshot(如果只运行一次)或者设为后台运行(通常不设置oneshot,并设置class main等)。 3. 设置disabled,然后通过触发器(trigger)在合适的时机启动,比如在boot完成时。 注意:后台程序通常需要持续运行,所以不应该使用oneshot(oneshot表示服务退出后不会重新启动)。因此,我们可以不设置oneshot,这样如果程序意外退出,init会重新启动它(除非我们设置了其他选项来避免重启)。但如果我们希望程序一直运行,且退出后自动重启,那么就不使用oneshot。 步骤: 1. 将可执行程序或脚本放在系统的某个位置,比如/system/bin/ 或 /vendor/bin/。 2. 在init.rc(或设备特定的init.xxx.rc)中添加一个service段。 3. 设置该service为自启动(通过触发器)或者通过属性触发。 例如,如果我们有一个后台程序叫做my_daemon,我们希望它在开机时启动并一直运行在后台,可以这样写: 在init.rc(或者更好的做法是在设备特定的init文件中,比如init.mydevice.rc)中添加: ``` service my_daemon /system/bin/my_daemon class main user root group root oneshot # 注意:如果程序是守护进程(daemon),通常会自己进入后台并且不会退出,那么我们可以使用oneshot吗? # 实际上,对于守护进程,我们通常不设置oneshot,因为守护进程会自己fork并退出父进程,然后init会认为服务已经退出。所以我们需要让init知道这个服务应该作为守护进程运行。 # 更好的做法是:不使用oneshot,而是让守护进程自己实现两次fork(即成为守护进程的标准做法),这样init就会把它当作一个长期运行的服务。 # 但是,如果我们的程序是一个一直运行在前台的进程(比如一个循环),那么就不需要oneshot,因为进程不会退出,如果退出init会重新启动。 # 因此,这里有两种情况: # a) 程序自己会变成守护进程(后台运行),那么init启动它之后,这个程序的父进程(即init)会等待它退出。但是守护进程会fork然后父进程(init启动的那个进程)退出,子进程由init接管(init会收养孤儿进程)。此时,init会认为服务已经退出(因为父进程退出了),如果设置了oneshot,那么init不会重启,但是如果没有oneshot,init会重启它(这通常不是我们想要的,因为守护进程已经在运行了)。 # b) 程序不fork,而是一直在前台运行。这时,如果程序不退出的情况下,init会一直管理它。如果程序退出,init会根据设置重启(如果没有oneshot的话)。 # 所以,对于后台守护进程,我们通常这样处理: # 1. 让程序自己按照守护进程的方式编写,即两次fork,这样init启动的进程会退出,然后init会认为服务已经退出(返回0表示成功退出,非0表示失败,init可能会重启)。但是这样就需要设置oneshot,因为服务已经成功启动并转为了后台进程,不需要init再重启。但是这样init就失去了对守护进程的监控(因为守护进程已经脱离init启动的进程树)。 # 2. 另一种做法是:让程序不要fork,而是直接在前台运行,但是这样会占用一个终端。但是init启动的服务是后台运行的,所以没关系。这种情况下,程序退出后,init会重启它(如果没有oneshot)。但是如果我们希望程序退出后不再重启,那么可以设置oneshot。 # 实际上,在Android中,许多守护进程都是在前台运行的,由init监控。例如,zygote就是在前台运行,如果它退出,系统会重启。所以,我们通常的做法是: # 不使用oneshot,这样如果程序退出(非正常退出),init会重新启动它。但是,如果程序正常退出(exit(0))且不需要重启,那么我们可以设置oneshot。 # 因此,如果我们希望程序一直运行(即使崩溃也重新启动),那么不使用oneshot,并确保程序在前台运行(即不fork成守护进程,而是直接运行主循环)。 # 另外,我们可以通过设置`restart`选项来控制重启行为。 # 总结:对于后台服务,我们通常这样写: service my_daemon /system/bin/my_daemon class main user root group root # 如果希望服务退出后重启(除非是init停止它),则不要oneshot,而是使用: # restart # 可以设置restart的条件,比如always或者on-failure等,但一般默认就会重启 # 但是,如果程序是守护进程(即启动后立即fork并退出父进程),那么父进程退出后,init会认为服务已经退出,然后重启(如果我们没有设置oneshot)。这会导致不停地重启,因为每次重启都会fork一个子进程然后父进程退出。所以,这种情况必须设置oneshot。 # 所以,正确的做法是: # - 如果程序自己会守护进程化(即daemonize),那么设置oneshot。 # - 如果程序不会守护进程化(即在前台运行),那么不设置oneshot,这样如果程序崩溃退出,init会自动重启。 所以,根据我们的需求,如果我们希望程序作为后台守护进程运行,并且一旦启动就一直在后台运行(直到系统关闭),那么有两种选择: 选择1:让程序自己daemonize,然后设置oneshot。 选择2:不让程序daemonize,而是写一个循环保持前台运行,然后不设置oneshot(这样如果程序崩溃退出,init会重启它)。 通常,推荐选择2,因为这样init可以监控服务,并在服务异常退出时重新启动。 因此,如果我们编写一个不会退出的前台程序(例如一个无限循环),那么我们可以这样写: ``` service my_daemon /system/bin/my_daemon class main user root group root # 不设置oneshot,这样如果my_daemon退出,init会重新启动它 ``` 但是,如果我们无法修改程序(程序本身是守护进程,会fork并退出父进程),那么我们必须设置oneshot: ``` service my_daemon /system/bin/my_daemon class main user root group root oneshot ``` 4. 设置启动时机:我们可以设置为在boot完成时启动,或者更早。例如,我们可以设置触发器: 在init.rc中,通常有: ``` on property:sys.boot_completed=1 start my_daemon ``` 或者,我们可以将服务添加到某个class,然后在启动某个class时启动。例如,class main是核心服务,通常在boot早期启动。我们可以将服务加入class main,然后它就会在boot早期启动: ``` service my_daemon /system/bin/my_daemon class main user root group root ``` 这样,在init启动class main时,就会启动这个服务。 但是,如果我们希望在所有核心服务启动完成后再启动,我们可以使用boot_completed属性。 5. 另外,还需要注意SELinux策略,可能需要为这个服务添加相应的策略。 根据引用[5]中的例子: ``` service loadsetmix /system/bin/loadsetmix.sh disabled oneshot seclabel u:r:shell:s0 on property:dev.bootcomplete=1 start loadsetmix ``` 这里使用了disabled,表示不会随着class启动,而是通过start命令显式启动。然后通过属性触发。 所以,综合以上,我们可以这样写: 步骤: a) 将可执行程序放到/system/bin/,并设置权限(在编译系统中确保被编译进去)。 b) 在init.rc(或者设备特定的init.rc)中添加: ``` service my_daemon /system/bin/my_daemon class main user root group root # 如果程序会自己守护进程化,则加上oneshot;否则不加。 # oneshot # 或者,如果我们希望它被init监控并自动重启,且程序不会守护进程化,则不加oneshot,并确保程序不会退出(或者退出后需要重启)。 # 这里假设我们不使用oneshot,程序在前台运行(不退出) # 设置seclabel(如果需要,根据SELinux策略) on property:sys.boot_completed=1 start my_daemon # 如果希望等到boot完成再启动,可以这样。如果希望更早启动,可以将其加入class main并去掉disabled,然后去掉这个触发器。 ``` 但是,如果我们将其加入class main,那么就不需要这个触发器,因为class main会在boot早期启动。所以,如果我们希望服务在boot早期启动,可以这样: ``` service my_daemon /system/bin/my_daemon class main user root group root ``` 这样,在init启动class main的时候就会启动这个服务,而不需要等到boot_completed。 6. 关于SELinux:可能需要为这个服务编写SELinux策略,否则在强制模式下可能无法启动。可以参考引用[3]中的方式,添加.te文件,定义domain和权限。 7. 最后,确保可执行程序有可执行权限。 总结步骤: 1. 将可执行程序放入系统镜像的/system/bin/(或/vendor/bin/)目录。 2. 在init.rc(或设备特定的init.rc)中定义service。 3. 根据程序特性(是否守护进程化)决定是否使用oneshot。 4. 设置启动时机(通过class或属性触发器)。 5. 处理SELinux策略(如果需要)。 注意:修改init.rc需要重新编译系统镜像,并刷入设备。 示例: 假设我们有一个可执行程序`mydaemon`,它会在前台运行(即不会退出),我们希望它在系统启动时(class main)启动,并且如果崩溃则重启。 在init.rc中添加: ``` service mydaemon /system/bin/mydaemon class main user root group root # 不设置oneshot,这样如果进程退出,init会重启它 ``` 如果使用属性触发(比如等到boot完成): ``` service mydaemon /system/bin/mydaemon class late_start user root group root disabled # 不随class启动 # 然后通过属性触发 on property:sys.boot_completed=1 start mydaemon ``` 以上两种方式根据需求选择。 另外,如果程序需要一些系统服务已经启动,那么可能需要等待boot_completed。否则,可以尽早启动。 最后,关于后台程序的标准输出和错误:默认情况下,init会重定向服务的输出到/dev/null。如果我们希望记录日志,可以使用`logwrapper`: ``` service mydaemon /system/bin/logwrapper /system/bin/mydaemon ... ``` 这样,输出会到logcat。 相关问题: 1. 如何为Android启动脚本中的服务添加SELinux策略? 2. 如何编写一个在Android init中运行的前台守护进程? 3. 如何调试Android启动脚本中添加的服务? </think> 在 Android 启动脚本中添加可执行的后台程序,需通过修改 `init.rc` 或相关初始化文件实现。以下是具体步骤和示例: ### 一、添加后台程序的核心步骤 1. **创建可执行程序或脚本** 将编译好的二进制程序或脚本(如 `my_daemon`)放入系统镜像的 `/system/bin/` 或 `/vendor/bin/` 目录,并确保有可执行权限: ```bash adb push my_daemon /system/bin/ adb shell chmod 755 /system/bin/my_daemon ``` 2. **在 `init.rc` 中声明服务** 编辑设备的 `init.rc` 文件(路径通常为 `/system/etc/init/` 或 `/vendor/etc/init/`),添加 `service` 定义: ```rc service my_daemon /system/bin/my_daemon # 服务名 + 可执行路径 class main # 归属到main类(随系统启动) user root # 以root身份运行 group root oneshot # 程序退出后不重启(适用于自管理守护进程) disabled # 默认不启动,需触发器激活 seclabel u:r:init:s0 # SELinux上下文(按需调整) ``` 3. **添加启动触发器** 在同一个 `init.rc` 文件中,定义触发条件(如系统启动完成时): ```rc on property:sys.boot_completed=1 # 系统启动完成时触发 start my_daemon # 启动服务 ``` --- ### 二、关键配置说明 1. **服务属性** - `class`:指定服务分类(`main`/`core`/`late_start`) - `user`/`group`:运行身份(通常为 `root`) - `oneshot`:程序退出后不重启(适合自守护进程) - `disabled`:不随 `class` 自动启动,需显式触发 2. **SELinux 策略**(必需步骤) 在设备源码中添加策略文件(如 `my_daemon.te`): ```te type my_daemon, domain; type my_daemon_exec, exec_type, file_type; init_daemon_domain(my_daemon) # 允许init启动 allow my_daemon device:dir rw; # 按需添加权限 ``` --- ### 三、完整示例 #### 1. 后台程序源码(C语言) ```c // my_daemon.c #include <unistd.h> int main() { daemon(0, 0); // 自我守护进程化 while(1) { sleep(5); // 后台持续运行 } return 0; } ``` 编译:`aarch64-linux-android-gcc my_daemon.c -o my_daemon` #### 2. `init.rc` 配置 ```rc # /vendor/etc/init/my_daemon.rc service my_daemon /vendor/bin/my_daemon class late_start user root group root oneshot disabled on property:sys.boot_completed=1 start my_daemon ``` #### 3. SELinux 策略 ```te # /sepolicy/my_daemon.te type my_daemon, domain; type my_daemon_exec, exec_type, file_type; init_daemon_domain(my_daemon) ``` --- ### 四、调试与验证 1. **检查服务状态** ```bash adb shell getprop | grep boot_completed # 确认启动完成 adb shell ps -A | grep my_daemon # 查看进程是否存在 ``` 2. **查看日志** ```bash adb logcat | grep my_daemon ``` 3. **常见错误** - 权限问题:检查文件权限和 SELinux 策略 - 路径错误:确保二进制路径与 `init.rc` 声明一致 - 触发器未激活:检查 `sys.boot_completed` 是否设置为 `1`[^4][^5] --- ### 相关问题 1. 如何为 Android 后台服务配置 SELinux 策略? 2. `init.rc` 中的 `class` 分类(main/core/late_start)有何区别? 3. Android 系统启动各阶段(bootloader/kernel/init)如何协调? 4. 如何调试 Android 启动脚本中的服务启动失败问题? [^1]: Android 服务定义语法与权限配置规范 [^2]: 后台守护进程的 Linux 实现原理 [^3]: SELinux 在 Android 中的策略编写方法 [^4]: Android 属性触发器(property-based trigger)的工作机制 [^5]: 系统启动状态属性(如 `sys.boot_completed`)的监控与
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值