实现的效果
客户提供一张格式为 bmp 的开机logo,在 linux 环境下执行制作 logo.bin 脚本文件,
apk 拷贝 logo.bin 文件替换系统原有的bin,然后重启 logo 自动更新。
原理分析
MTK 平台的充电图片和开机logo都是打包到 logo.bin 中的
对应的资源路径在源码 vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo
系统一般预制了很多分辨率文件夹,实际通过 BOOT_LOGO 来确定打包那一个
制作 logo.bin 文件的规则在 rules.mk 中写的很详细
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\rules.mk
1、将 BOOT_LOGO 文件夹中的 bmp 图片通过 bmp_to_raw 工具转化为 .raw
具体定义在 vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\update
2、将上一步转化后所有 raw 文件通过 zpipe 工具压缩成 boot_logo.raw 文件
3、最后调用 mkimage 指令将 boot_logo.raw 文件打包成 logo.bin
弄清楚流程了那就好办了,我们只需要将这几个工具和对应的指令集成到一个 .sh 脚本文件即可。
为什么不直接将这几个工具放到 android 系统里呢???
答案是,不支持,一开始我也是这么想的,那直接将这几个工具指令扔到 /system/bin 目录下,
直接调用不就行了,这样就更方便了。实际尝试后发现,对应的工具必须在 linux 下才能执行。
实现代码
先将 bmp_to_raw、zpipe、mkimage 三个工具找到并拷贝至新建文件夹中,就叫 gccLogoBin 吧
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\tool\bmp_to_raw
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\tool\zpipe
vendor\mediatek\proprietary\bootable\bootloader\lk\scripts\mkimage
除了这三个工具,还需要 img_hdr_logo.cfg 和 BOOT_LOGO 文件夹对应的所有 raw 文件(在out目录下)
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\img_hdr_logo.cfg
out\target\product\tcl98\obj\LK_OBJ\build-tcl98\dev\logo\hdplus1560
img_hdr_logo.cfg 文件用于 mkimage 工具参数,hdplus1560(文件夹名称取决于BOOT_LOGO)用于减少 bmp_to_raw 工具
转换的时间,因为我们只更换开机logo,其它图片资源是不动的,所以只需生成 hdplus1560_uboot.raw
这样 gccLogoBin 文件夹下一共是 6 个文件,因为要加上接下来的 makelogobin.sh
#!/bin/bash
if [ "$1" = "" ]; then
echo "Please input BOOT_LOGO name,"
echo "Such as: hdplus1560, hd720,wqxga"
exit
fi
BASELOGO=$1
BOOT_LOGO_RESOURCE=boot_logo.raw
echo "BASELOGO=${BASELOGO}"
# step 1 make hdplus1560_uboot.raw hdplus1560_kernel.raw
echo "===================step 1 bmp_to_raw make uboot and kernel raw===================="
./bmp_to_raw ./${BASELOGO}/${BASELOGO}_kernel.raw logo.bmp
./bmp_to_raw ./${BASELOGO}/${BASELOGO}_uboot.raw logo.bmp
echo "=========done"
# step 2 zpipe all raw to boot_logo.raw
echo "===================step 2 zpipe all raws get ${BASELOGO}.raw===================="
./zpipe -l 9 ${BOOT_LOGO_RESOURCE} ./${BASELOGO}/${BASELOGO}_uboot.raw ./${BASELOGO}/${BASELOGO}_battery.raw ./${BASELOGO}/${BASELOGO}_low_battery.raw ./${BASELOGO}/${BASELOGO}_charger_ov.raw ./${BASELOGO}/${BASELOGO}_num_0.raw ./${BASELOGO}/${BASELOGO}_num_1.raw ./${BASELOGO}/${BASELOGO}_num_2.raw ./${BASELOGO}/${BASELOGO}_num_3.raw ./${BASELOGO}/${BASELOGO}_num_4.raw ./${BASELOGO}/${BASELOGO}_num_5.raw ./${BASELOGO}/${BASELOGO}_num_6.raw ./${BASELOGO}/${BASELOGO}_num_7.raw ./${BASELOGO}/${BASELOGO}_num_8.raw ./${BASELOGO}/${BASELOGO}_num_9.raw ./${BASELOGO}/${BASELOGO}_num_percent.raw ./${BASELOGO}/${BASELOGO}_bat_animation_01.raw ./${BASELOGO}/${BASELOGO}_bat_animation_02.raw ./${BASELOGO}/${BASELOGO}_bat_animation_03.raw ./${BASELOGO}/${BASELOGO}_bat_animation_04.raw ./${BASELOGO}/${BASELOGO}_bat_animation_05.raw ./${BASELOGO}/${BASELOGO}_bat_animation_06.raw ./${BASELOGO}/${BASELOGO}_bat_animation_07.raw ./${BASELOGO}/${BASELOGO}_bat_animation_08.raw ./${BASELOGO}/${BASELOGO}_bat_animation_09.raw ./${BASELOGO}/${BASELOGO}_bat_animation_10.raw ./${BASELOGO}/${BASELOGO}_bat_10_01.raw ./${BASELOGO}/${BASELOGO}_bat_10_02.raw ./${BASELOGO}/${BASELOGO}_bat_10_03.raw ./${BASELOGO}/${BASELOGO}_bat_10_04.raw ./${BASELOGO}/${BASELOGO}_bat_10_05.raw ./${BASELOGO}/${BASELOGO}_bat_10_06.raw ./${BASELOGO}/${BASELOGO}_bat_10_07.raw ./${BASELOGO}/${BASELOGO}_bat_10_08.raw ./${BASELOGO}/${BASELOGO}_bat_10_09.raw ./${BASELOGO}/${BASELOGO}_bat_10_10.raw ./${BASELOGO}/${BASELOGO}_bat_bg.raw ./${BASELOGO}/${BASELOGO}_bat_img.raw ./${BASELOGO}/${BASELOGO}_bat_100.raw ./${BASELOGO}/${BASELOGO}_kernel.raw
echo "=========done"
# step 3 make logo.bin
echo "===================step 3 make logo.bin===================="
./mkimage ${BOOT_LOGO_RESOURCE} img_hdr_logo.cfg > logo.bin
echo "all=========done"
rm ${BOOT_LOGO_RESOURCE} -rf
注意第二步中的 zpipe 压缩对应的 raw 是有顺序的,具体顺序请参考你的
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\update
cd gccLogoBin
执行 ./makelogobin.sh hdplus1560
这样我们就制作完成 logo.bin 了,如果是工程版本可以直接将 logo.bin push 到 dev/block/platform/bootdevice/by-name/logo
中重启查看即可,或者用烧写工具直接烧录刚刚的 logo.bin 文件。
工作完成了 50% ,接下来我们要在 apk 中将 logo.bin 拷贝至中 dev/block/platform/bootdevice/by-name/logo
apk 拷贝对应代码如下,app 需要配置 android:sharedUserId="android.uid.system" 和加系统签名
int length = 524288;
public void copyLogoBin() {
File srcFile = new File("/sdcard/logo.bin");
String logoPath = "dev/block/platform/bootdevice/by-name/logo";
Log.e("logo","logoPath="+logoPath);
File dstFile = new File(logoPath);
OutputStream os = null;
try {
FileInputStream is = new FileInputStream(srcFile);
os = new BufferedOutputStream(new FileOutputStream(dstFile, false), length);
int curSize = 0;
byte[] data = new byte[length];
for (int len; (len = is.read(data)) != -1; ) {
os.write(data, 0, len);
curSize += len;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
srcFile.delete();
}
}
system app 读写 blk_file 出现 selinux 权限问题,根据 log 增加相应权限
type=1400 audit(0.0:2061): avc: denied { write } for name="mmcblk0p29" dev="tmpfs" ino=578 scontext=u:r:system_app:s0 tcontext=u:object_r:logo_block_device:s0 tclass=blk_file permissive=0
type=1400 audit(0.0:2062): avc: denied { open } for path="/dev/block/mmcblk0p29" dev="tmpfs" ino=578 scontext=u:r:system_app:s0 tcontext=u:object_r:logo_block_device:s0 tclass=blk_file permissive=0
type=1400 audit(0.0:2717): avc: denied { getattr } for path="/dev/block/mmcblk0p29" dev="tmpfs" ino=8299 scontext=u:r:system_app:s0 tcontext=u:object_r:logo_block_device:s0 tclass=blk_file permissive=0
device\mediatek\sepolicy\basic\non_plat\system_app.te
增加 allow system_app logo_block_device:blk_file { getattr open read write };
编译时遇到 neverallow 权限解决
system\sepolicy\public\app.te system\sepolicy\prebuilts\api\30.0\public\app.te
注释 373 行 # Block device access. neverallow appdomain dev_type:blk_file { read write };
重新编译烧写后拷贝时报错
W/System.err: java.io.IOException: write failed: EPERM (Operation not permitted)
2021-02-23 08:24:00.589 6373-6373/? W/System.err: at libcore.io.IoBridge.write(IoBridge.java:544)
2021-02-23 08:24:00.589 6373-6373/? W/System.err: at java.io.FileOutputStream.write(FileOutputStream.java:392)
2021-02-23 08:24:00.589 6373-6373/? W/System.err: at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122)
2021-02-23 08:24:00.589 6373-6373/? W/System.err: at cg.demo.logo.TestActivity.copyLogoBin(TestActivity.java:146)
2021-02-23 08:24:00.589 6373-6373/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.view.View.performClick(View.java:7146)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.view.View.performClickInternal(View.java:7119)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.view.View.access$3500(View.java:803)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.view.View$PerformClick.run(View.java:27533)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.os.Handler.handleCallback(Handler.java:883)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:100)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.os.Looper.loop(Looper.java:214)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7386)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: Caused by: android.system.ErrnoException: write failed: EPERM (Operation not permitted)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.Linux.writeBytes(Native Method)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.Linux.write(Linux.java:294)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.ForwardingOs.write(ForwardingOs.java:241)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.BlockGuardOs.write(BlockGuardOs.java:416)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.ForwardingOs.write(ForwardingOs.java:241)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: at libcore.io.IoBridge.write(IoBridge.java:539)
2021-02-23 08:24:00.590 6373-6373/? W/System.err: ... 16 more
执行 write() 时报错写入失败
修改 vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6771\write_protect.c
注释末尾的 //set_write_protect(); 问题解决