Android Framework 之HelloWorld(三)

       本来是要写一个linux驱动,用于控制led灯的,但考虑到nanopc-T4的内核已经帮我们配置好设备树,已经可以利用/sys/class/gpio操作gpio了,所以没必要再造轮子了!

        在shell里,可以利用下面的命令控制Led灯的亮与灭:

#导出GPIO0_A0管脚
echo 32 > /sys/class/gpio/export
#让GPIO0_A0管脚作为输出使用
echo out > /sys/class/gpio/gpio32/direction
#GPIO0_A0输出高电平
echo 1 > /sys/class/gpio/gpio32/value
#GPIO0_A0输出低电平
echo 0 > /sys/class/gpio/gpio32/value

        接上串口或者adb shell进去shell终端,先来看下export的用户和组:

nanopc-t4:/ $ ls -l /sys/class/gpio/                                           
total 0
--w--w---- 1 root   system 4096 2020-08-22 04:50 export
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpio156 -> ../../devices/platform/pinctrl/gpio/gpio156
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpiochip0 -> ../../devices/platform/pinctrl/gpio/gpiochip0
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpiochip128 -> ../../devices/platform/pinctrl/gpio/gpiochip128
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpiochip32 -> ../../devices/platform/pinctrl/gpio/gpiochip32
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpiochip64 -> ../../devices/platform/pinctrl/gpio/gpiochip64
lrwxrwxrwx 1 root   root      0 2020-08-22 07:48 gpiochip96 -> ../../devices/platform/pinctrl/gpio/gpiochip96
--w--w---- 1 root   system 4096 2020-08-22 04:50 unexport

        可以看出,export是普通文件,拥有者具有“写”权限,组内其他成员具有“写”权限,其他用户则没有任何权限,export属于root用户,所属组为system。

        android8.1定义了很多用户,我们可以在shell里使用“ps -ef”,随机截取了几个就有root,shell,system、u0_a59等用户了。

#随机截取了几个
$ps -ef
...
webview_zygote 660     1 0 04:50:44 ?     00:00:00 webview_zygote32
radio          689   363 0 04:50:44 ?     00:00:00 com.android.phone
system        1120   363 0 04:50:48 ?     00:00:00 android.rockchip.update.service
root          3694     1 0 05:08:17 ?     00:00:03 adbd --root_seclabel=u:r:su:s0
root         13893  3694 0 07:45:05 136:0 00:00:00 logcat -v long -v epoch
u0_a59       13962   363 1 07:45:22 ?     00:00:02 com.demo.helloworld
shell        14478 14087 36 07:53:04 ttyFIQ0 00:00:00 ps -ef
...

        所以我们要操作/sys/class/gpio/export,至少也要将app提升至system用户权限,apk在安装时,会根据AndroidManifest.xml的android:sharedUserId="android.uid.system"属性进行分配用户权限,查了一些资料,如果要让app具有system权限,有两种方法,一是将app放进android源码上使用mm DEX_PREOPT_DEFAULT=nostripping编译,还有就是使用签名,我们这里使用签名的方式:

先设置AndroidManifest.xml的android:sharedUserId="android.uid.system"属性
在编译android源码后,在此处得到如下文件:

./build/make/target/product/security/platform.pk8
./build/make/target/product/security/platform.x509.pem

每次编译android源码这两个文件都不变,可以拷贝出来使用。

而signapk.jar则存在于这两个地方:

./out/host/linux-x86/framework/signapk.jar和./prebuilts/sdk/tools/lib/signapk.jar,这两个signapk.jar的区别我不清楚,不过猜测使用前者会好一点,毕竟是编译输出的。

把上面的三个文件拷贝出来,如果使用./out/host/linux-x86/framework/signapk.jar,则使用如下命令生成签名后的apk安装包:

java -Djava.library.path=../rk3399-android-8.1/out/host/linux-x86/lib64 -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk system.apk

如果使用./prebuilts/sdk/tools/lib/signapk.jar,则使用如下命令生成签名后的apk安装包:

java -Djava.library.path=../rk3399-android-8.1/prebuilts/sdk/tools/linux/lib64/ -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk system.apk

        我在前文的libledctrl.so里加入WriteFileValue()函数用于操作gpio:

#include 
#include 

static int WriteFileValue(const char *pPath, char *pValue, int len)
{	
	int fd = -1;

	if(!pPath || !pValue)
	{
		return -1;
	}

	PRINT_DEBUG("open %s value = %s, len = %d", pPath, pValue, len);
		
	fd = open(pPath, O_RDWR);
	if (fd < 0)
	{
		PRINT_DEBUG("open %s error: %d,%s", pPath, fd, strerror(errno));
		return fd;
	}
	
	if (len != write(fd, pValue, len))
	{
		PRINT_DEBUG("write error: %d,%s", fd, strerror(errno));
		close(fd);
		return -1;
	}
	close(fd);
	
	return 0;
}

jint LedInit(JNIEnv *env, jobject cls)
{
	PRINT_DEBUG("Entry LedInit ...");
    char *export = "32";
	char *direction = "out";
	WriteFileValue("/sys/class/gpio/export", export, strlen(export)+1);
	WriteFileValue("/sys/class/gpio/gpio32/direction", direction, strlen(direction)+1);
    return 0;
}

         可惜的是,即使app以system用户运行也没权限操控/sys/class/gpio/export,使用chown system:system /sys/class/gpio/export使之处于同一“owner”也不够权限。可能是SElinux的权限管理问题,android源码中允许对某些驱动文件节点进行更细化的权限管理,譬如uevent,在我们NanoPC-T4开发板的ueventd.rk30board.rc文件就指定了各种文件路径的权限值。跟权限相关的我得好好学习才行!因为我使用下面的命令"whoami”或者“dumpsys activity top”获取到当前界面所处用户确实是system用户了,这说明使用签名的方式提升到system权限是没问题的:

import android.util.Log;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
private void Run(String cmd)
    {
        try {
            Process process = Runtime.getRuntime().exec("sh"); //su
            // 获取输出流
            OutputStream outputStream = process.getOutputStream();
            InputStream is = process.getInputStream();
            InputStream es = process.getErrorStream();
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeBytes(cmd);
            dataOutputStream.flush();
            dataOutputStream.close();
            outputStream.close();
            int code = process.waitFor();
            Log.d("TAG", "Run:\"" + cmd +"\", "+"process.waitFor() = " + code);
            String line;
            BufferedReader br;
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            while ((line = br.readLine()) != null) {
                Log.d("TAG", line);
            }

            br = new BufferedReader(new InputStreamReader(es, "UTF-8"));
            while ((line = br.readLine()) != null) {
                Log.e("TAG", line);
            }

        } catch (Throwable t) {
            Log.e("TAG", "Throwable = " + t.getMessage());
            t.printStackTrace();
        }
    }

...
...
Run("whoami"); //获取当前进程是属于哪个用户
//或者在shell中使用dumpsys

          我发现如果在root用户下预先运行chmod 777 /sys/class/gpio/export和chown system:system /sys/class/gpio/export命令后,再重启app,就能操控led灯了,这我就不理解了。

          先来看uevent相关的配置文件:

default@default:~/work/other/rk3399-android-8.1$ find . -name "ueventd*"
./device/rockchip/common/ueventd.rockchip.rc
./device/rockchip/common/sepolicy/ueventd.te
./system/sepolicy/private/ueventd.te
./system/sepolicy/prebuilts/api/26.0/private/ueventd.te
./system/sepolicy/prebuilts/api/26.0/public/ueventd.te
./system/sepolicy/public/ueventd.te
./system/core/init/ueventd.cpp
./system/core/init/ueventd.h
./system/core/init/ueventd_parser.h
./system/core/init/ueventd_test.cpp
./system/core/init/ueventd_parser.cpp
./system/core/rootdir/ueventd.rc

        可以看到相关的应该是./device/rockchip/common/ueventd.rockchip.rc和./system/core/rootdir/ueventd.rc这两个文件,而*.te是SElinux相关的权限配置,截取ueventd.rockchip.rc的与gpio相关的部分:

...
...
/dev/cpu_state 0666 system system
/dev/chip_state 0666 system system
/dev/i2c-1 0660 system system
/dev/i2c-2 0660 system system
/dev/rtc0  0660 system system
/dev/spidev1.0  0660 system system
/dev/watchdog   0660 system system

#for ovr
/dev/ovr0          0664   system          system

#for gpio
/sys/devices/platform/pinctrl/gpio/gpio* active_low 0660 system system
/sys/devices/platform/pinctrl/gpio/gpio* direction  0660 system system
/sys/devices/platform/pinctrl/gpio/gpio* edge       0660 system system
/sys/devices/platform/pinctrl/gpio/gpio* value      0660 system system

#for pwm
...
...

        它并没有指定export的权限,反而在rk3399-android-8.1/device/rockchip/rk3399/init.rk3399.rc上有指定:

    chmod 0220 /sys/class/gpio/export
    chown root system /sys/class/gpio/export
    chmod 0220 /sys/class/gpio/unexport
    chown root system /sys/class/gpio/unexport

        猜想如果在ueventd.rockchip.rc上显式指定/sys/class/gpio/export  0660 system system和/sys/class/gpio/unexport 0660 system system,或者在init.rk3399.rc上修改该export/unexport文件的权限应该可以解决这个问题,不过修改了文件系统,需要重新编译android8.1源码才能验证。

        如你所见,这个HelloWorld是失败的,没能达到要求,同时在android的权限管理方面没能解释清楚!

 

 

 




        终于发现问题所在了,不需要修改ueventd.rockchip.rc和init.rk3399.rc,用回NanoPC-T4原本的配置即可,因为原本export的权限配置是022-root-system(-w--w----  root system),只要我们的app提升至system用户权限即可操作这个文件。那为什么liblledctrl.so的WriteFileValue()->open()还会返回“Permission denied”不够权限呢?原来我在WriteFileValue()的open()使用了O_RDWR标志了,022权限是只写的,改为fd = open(pPath, O_WRONLY);就不报错了。

        另外,uevent跟我们熟悉的linux底下的udev类似,都是用于热插拔控制的,写好规则(/etc/udev/rules.d/)就能控制权限(如指定某个usb设备的PID/VID才具有读写权限)和指定固定节点(如规定任何不同厂商的鼠标插入都是识别成/dev/input/event0节点,或者插入U盘后自动挂载到/mnt/sdcard下等)等。而我们的 android用ueventd.rc作为规则文件,指定了权限,我们的ueventd.rockchip.rc有指定“/sys/devices/platform/pinctrl/gpio/gpio* direction  0660 system system”,即在system用户能读写,但echo 32 >/sys/class/gpio/export到生成 /sys/class/gpio/gpio32/direction等文件以及uevent按照ueventd.rockchip.rc规则修改direction等文件的权限需要一定时间,所以在两条命令之间加入延时,open就不会再报“Permission denied”了:

jint ledInit(JNIEnv *env, jobject cls)
{
    char *export = "32";
    char *direction = "out";
    WriteFileValue("/sys/class/gpio/export", export, strlen(export) + 1);
    usleep(1000*10);
    WriteFileValue("/sys/class/gpio/gpio32/direction", direction, strlen(direction) + 1);
    return 0;
}

        基于上面两条这就能解释前面说的“在root用户下预先运行chmod 777 /sys/class/gpio/export和chown system:system /sys/class/gpio/export命令后,还要重启app才能操控led灯”了。

        以上,就是一个完整的Android  HelloWorld APP了。

你可能感兴趣的:(Android,Framework,android权限)