支持Android机器上开机动画的动态替换,即系统动画bootanimation.zip支持在上层应用中自定义修改,重启开机后生效。
前提
为了保持开机动画效果的正确性,系统动画bootanimation.zip需要满足一定的条件。
- 在同一款机型上,尽量保持图片数目、播放时间与之前一致。
- 命名使用英文,图片尺寸、格式保持与之前一致。
- zip压缩方式必须为存储压缩。
- bootanimation.zip解压后的内部结构和目录如下:
最后在此次需求修改后,开机速度的效果不能发生明显变化。
源码实现
源码位置:frameworks/base/cmds/bootanimation/BootAnimation.cpp
系统默认使用的开机动画路径:/system/media/bootanimation.zip
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
我们添加一个自定义路径:
static const char NOAH_CUSTOM_BOOTANIMATION_FILE[] = "/noah/bootanim/bootanimation.zip";
在BootAnimation::readyToRun()方法中引用了SYSTEM_BOOTANIMATION_FILE定义的路径。
static const char* bootFiles[] =
{
PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
static const char* shutdownFiles[] =
{
PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
把新加的路径添加到bootFiles[]数组中去:若"/noah/bootanim/bootanimation.zip"文件可访问,根据条件就会播放该路径下的bootFiles。
//changed by noah_liuke
bool playNoahCustomBootAnim = (access(NOAH_CUSTOM_BOOTANIMATION_FILE, R_OK) == 0) ? true : false;
ALOGI("noah, playNoahCustomBootAnim = %d", playNoahCustomBootAnim);
static const char* bootFiles[] =
{
PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, playNoahCustomBootAnim ? NOAH_CUSTOM_BOOTANIMATION_FILE : SYSTEM_BOOTANIMATION_FILE};
//end by noah_liuke
验证是否生效时遇到的主要问题是自定义路径的访问权限。当上层应用复制bootanimation.zip到该路径时,系统限制了目录文件的读写权限,以及selinux avc权限配置问题。关于avc权限的修改验证不在此篇讨论访问内。
效果验证
make bootanimation 编译后将生成的产物:system/bin/bootanimation、system/lib/libbootanimation.so、system/lib64/libbootanimation.so,分别push到机器对应的目录。
-
系统验证:
创建bootanim目录,同时赋予权限:mkdir -p -m 777 bootanim;
adb push bootanimation.zip到该自定义路径;
重启开机过程中接Xshell串口日志,若修改不生效,logcat | grep avc查看是否有avc权限是否错误并不断修改。
系统验证无误后刷机再给应用验证。 -
应用验证:
应用代码自行mkdir bootanim,cp bootanimation.zip到自定义路径。
使用如下的Runtime.exec()执行命令,比如cp、chmod文件。
public synchronized static int executeCommand(String command) {
int status = -1;
Process process = null;
DataOutputStream os = null;
Log.d(TAG, "exec_start:" + command);
try {
process = Runtime.getRuntime().exec(command);
status = process.waitFor();
if (status == 0) {
Log.i(TAG, "exec succeed.");
} else {
Log.i(TAG, "exec failed.");
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (process != null) {
process.destroy();
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
Log.d(TAG, "exec_end:" + command);
return status;
}
根据应用反馈是否还有avc权限问题,再由系统修改,最终达成效果正确。