项目中需要处理Android的原生开机动画,一定条件下还需要做到静默重启(android系统启动进入到桌面前,屏幕保持完全没有亮度的状态)。因为项目是基于Android Q做的,顺势探索了一波开机动画的细节。
搜了一些资料,推荐一篇博客,原理讲的很细,汇总开机动画一共有三个:
Android系统的开机画面显示过程分析
总结下:
1、Linux内核的启动画面,这个动画一般不显示。
2、第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。以小米手机为例,开机出现的第一个白色的 “MI” logo。
3、第三个开机动画一般各手机厂商会自行定制,也就是开机后的动画特效。
但是基于Q测试时,还发现一个“Phone is starting”的对话框页面,这个页面后才是桌面。于是引入了第四个开机动画:
4、启动Launcher之前会先启动一个FallbackHome,这个FallbackHome就是“Phone is starting”的对话框页面,这个页面一般厂商也会自行定制。
综上:目前最新的Android源码一共有四个开机动画。
这个基本没有需求,我没研究,有需求的同学可以参考Android系统的开机画面显示过程分析学习研究。
Android系统默认的开机动画是由两张图片android-logo-mask.png和android-logo-shine.png中。这两张图片保存在frameworks/base/core/res/assets/images目录中。一般厂商会自行定制此动画,谷歌也提供了替换的方法:
#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
替换两个目录下的任何一个即可。一般会在rom源码去替换这个文件,示例代码:
需要修改device.mk(在rom根目录的device目录中)文件,同时把自定义的bootanimation.zip放到device.mk所在git库根目录
PRODUCT_COPY_FILES += $(LOCAL_PATH)/bootanimation.zip:system/media/bootanimation.zip
注: 如果需要隐藏此动画,有个取巧的方法,可以bootanimation.zip内放不合适的image图片,就不会显示此动画了。相比改代码处理更简洁。
也就是改动src/com/android/settings/FallbackHome.java这个类。这个类是在系统应用Settings中
项目路径是:rom根目录/packages/apps/Settings,改动很简单,直接把显示dialog的代码注掉(或者自行定制)
@Override
protected void onResume() {
super.onResume();
if (mProvisioned) {
// mHandler.postDelayed(mProgressTimeoutRunnable, PROGRESS_TIMEOUT);
}
如果系统已经做过开机引导,不会显示此动画。
注:验收过程中,mtk平台的rom自行定制了Settings项目,改动/packages/apps/Settings无效,MTK平台下的rom,需要改动rom根目录/vendor/mediatek/proprietary/packages/apps/MtkSettings下的FallbackHome.java**
分析完上边的动画,如果需要在设备启动中变更屏幕亮度。一共需要处理两大块。
1、修改第二个动画的屏幕背光。
2、修改第三个动画的屏幕背光。
因为第二个开机动画,整个android的文件系统还没启动,所以无法使用文件系统记录。所以使用misc分区记录。
这里大概解释下:
android上主要有六个分区:cache,dumisc,recovery,boot,system,userdata。
后四种分区不做解释了,主要在于misc和cache两个分区的作用。cache是缓存空间,程序和系统用到的缓存数据和指令就存放在这里;CPU在调用和执行命令是会优先调用这里的数据;misc分区中有Bootloader Control Block(BCB),主要是用于存放Recovery引导信息。第二个开机动画(静态logo)就需要借用此分区。
1、从misc分区读亮度值,并设置。
设置背光值的源码位置:
vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mt…(型号代号)/mt_leds.c
void mt65xx_backlight_on(void)
{
// backlight_level增加逻辑,从misc分区读出背光值,在这里设置即可
enum led_brightness backlight_level = get_cust_led_default_level();
LEDS_INFO("[LEDS]LK: mt65xx_backlight_on:level = %d\n\r",backlight_level);
mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, backlight_level);
LEDS_INFO("[LEDS]LK: mt65xx_leds_brightness_set is done\n");
}
2、暴露给app可以操作misc分区此值的工具。
app如果想修改misc分区数据,需要提供c程序,app可以使用命令调用。c程序保密,不过原理大概这样。
1、因为从这里开始文件系统已经加载,所以使用文件记录是否设置背光为0。
2、本以为第三个开机动画到桌面显示,也就一个地方设置了背光,代码尝试改动了
frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private void updatePowerState() {
......
if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
// 这里使用/data/backlight_0文件是否存在标记
File file = new File("/data/backlight_0");
brightness = file.exists() ? 0 : mPowerRequest.screenBrightnessOverride;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
mAppliedScreenBrightnessOverride = true;
} else {
mAppliedScreenBrightnessOverride = false;
}
......
}
改动后发现并没有把整个流程的背光设置为0。
3、后来发现整个过程多个地方操作了背光,总不能所有的地方都去变动。于是找到了设置背光的源头:
vendor/mediatek/proprietary/hardware/liblights/lights.c
增加判断:
static int
write_int(char const* path, int value)
{
int fd;
#ifdef LIGHTS_INFO_ON
ALOGD("write %d to %s", value, path);
#endif
// 此处增加了逻辑,如果文件存在直接返回。
if ((access("/data/backlight_0", F_OK)) != -1) {
return 0;
}
fd = open(path, O_RDWR);
ALOGD("write_int open fd=%d\n", fd);
if (fd >= 0) {
char buffer[20];
int bytes = sprintf(buffer, "%d\n", value);
int amt = write(fd, buffer, bytes);
close(fd);
return amt == -1 ? -errno : 0;
} else {
return -errno;
}
}
注:这里解释下此处设置背光的原理。是通过write写/sys/class/leds/lcd-backlight/brightness文件设置背光的。
这里可能很多人想在桌面去改动。其实有个更好的地方!!!就是第四个动画的FallbackHome.java。这个要比桌面启动早。且更独立。并且还有最重要的一点,这个app是systemUid,可以操作更广的文件系统,权限也更高。
代码变动:
@Override
protected void onPause() {
super.onPause();
mHandler.removeCallbacks(mProgressTimeoutRunnable);
clearMyBackLight();
}
private void clearMyBackLight() {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 1、需要执行提供的c程序,清掉第二个动画,也就是misc分区存储的值
Runtime.getRuntime().exec("/system/bin/提供的程序 clear");
// 2、清掉backlight_0,恢复第三个动画往后的正常屏幕背光
Runtime.getRuntime().exec("rm /data/backlight_0");
// 3、恢复重启前的用户设置的背光值,这里用了Settings.Secure存储
int value = Settings.Secure.getInt(getApplicationContext().getContentResolver(), "pref_key_back_light");
String[] args = {"echo", String.valueOf(value), ">", "/sys/class/leds/lcd-backlight/brightness"};
Process process = Runtime.getRuntime().exec("sh");
OutputStream outputStream = process.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
String cmd = "";
for (String arg : args) {
cmd += arg + " ";
}
cmd += "\n";
dataOutputStream.writeBytes(cmd);
dataOutputStream.flush();
dataOutputStream.writeBytes("exit\n");
dataOutputStream.flush();
} catch (Exception e) {
// ignore
}
}
}).start();
}
一次比较神奇和艰难的探索之旅。
参考资料声明:Android系统的开机画面显示过程分析