android12-release
推荐 Android 12 开机动画代码与流程详解
一般使用
rle格式图片
;例如放在splash分区
adb reboot bootloader
fastboot flash splash splash.img
fastboot reboot
记录
kernel/drivers/video/msm/msm_fb.c
也是读出根目录下的xx.rle,并显示为开机画面
使用BootAnimation程序显示开机画面,如需修改开机画面,不用修改代码,只需按格式要求做
bootanimation.zip
包,放在系统的/system/media
目录中,或/oem/media
、/product/media
等目录。
代码路径:frameworks/base/cmds/bootanimation
frameworks/base/cmds/bootanimation/BootAnimation.cpp,例如bootanimation.zip
frameworks/base/cmds/bootanimation/Android.bp
frameworks/base/cmds/bootanimation/bootanim.rc
frameworks/base/cmds/bootanimation/bootanimation_main.cpp
frameworks/base/cmds/bootanimation/BootAnimation.cpp
frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp
SurfaceFlinger进程名:bootanim
bin文件:/system/bin/bootanimation
对应启动入口:/frameworks/base/cmds/bootanimation/bootanimation_main.cpp
bootAnimationDisabled()
检测系统属性(debug.sf.nobootanimation、ro.boot.quiescent、ro.bootanim.quiescent.enabled
),noBootAnimation
是否启动开机动画。注意init进程
在启动bootanimation服务是disable的,不会主动将应用程序bootanimation启动起来。启动bootanimation是从surfaceFlinger这边来启动的
frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the boot animation object (may take up to 200ms for 2MB zip)
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
waitForSurfaceFlinger();
boot->run("BootAnimation", PRIORITY_DISPLAY);
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp
bool bootAnimationDisabled() {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0");
if (atoi(value) > 0) {
return true;
}
property_get("ro.boot.quiescent", value, "0");
if (atoi(value) > 0) {
// Only show the bootanimation for quiescent boots if this system property is set to enabled
if (!property_get_bool("ro.bootanim.quiescent.enabled", false)) {
return true;
}
}
return false;
}
SurfaceFlinger启动-Android12 初始化时候
mStartPropertySetThread->Start()
在线程中设置property_set("ctl.start", "bootanim")
启动开机动画
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
#include
#include "StartPropertySetThread.h"
namespace android {
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
status_t StartPropertySetThread::Start() {
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
bool StartPropertySetThread::threadLoop() {
// Set property service.sf.present_timestamp, consumer need check its readiness
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
property_set("service.bootanim.exit", "0");
property_set("service.bootanim.progress", "0");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
return false;
}
} // namespace android
result = android()
android原生动画;result = movie()
自动动画playAnimation(*mAnimation)
播放动画;releaseAnimation(mAnimation)
释放动画资源- 播放动画过程:
initTexture(frame.map, &w, &h)
、drawClock(animation.clockFont, part.clockPosX, part.clockPosY)
、drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY)
、checkExit()
checkExit()
检测属性"service.bootanim.exit"
退出动画
bool BootAnimation::threadLoop() {
bool result;
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
result = android();
} else {
result = movie();
}
mCallbacks->shutdown();
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mDisplay, mContext);
eglDestroySurface(mDisplay, mSurface);
mFlingerSurface.clear();
mFlingerSurfaceControl.clear();
eglTerminate(mDisplay);
eglReleaseThread();
IPCThreadState::self()->stopProcess();
return result;
}
bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
if (mAnimation == nullptr)
return false;
// mCallbacks->init() may get called recursively,
// this loop is needed to get the same results
for (const Animation::Part& part : mAnimation->parts) {
if (part.animation != nullptr) {
mCallbacks->init(part.animation->parts);
}
}
mCallbacks->init(mAnimation->parts);
bool anyPartHasClock = false;
for (size_t i=0; i < mAnimation->parts.size(); i++) {
if(validClock(mAnimation->parts[i])) {
anyPartHasClock = true;
break;
}
}
if (!anyPartHasClock) {
mClockEnabled = false;
}
// Check if npot textures are supported
mUseNpotTextures = false;
String8 gl_extensions;
const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
if (!exts) {
glGetError();
} else {
gl_extensions.setTo(exts);
if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
(gl_extensions.find("GL_OES_texture_npot") != -1)) {
mUseNpotTextures = true;
}
}
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool clockFontInitialized = false;
if (mClockEnabled) {
clockFontInitialized =
(initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
mClockEnabled = clockFontInitialized;
}
initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);
if (mClockEnabled && !updateIsTimeAccurate()) {
mTimeCheckThread = new TimeCheckThread(this);
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
}
playAnimation(*mAnimation);
if (mTimeCheckThread != nullptr) {
mTimeCheckThread->requestExit();
mTimeCheckThread = nullptr;
}
if (clockFontInitialized) {
glDeleteTextures(1, &mAnimation->clockFont.texture.name);
}
releaseAnimation(mAnimation);
mAnimation = nullptr;
return false;
}
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
int fadedFramesCount = 0;
int lastDisplayedProgress = 0;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
if (part.animation != nullptr) {
playAnimation(*part.animation);
if (exitPending())
break;
continue; //to next part
}
// process the part not only while the count allows but also if already fading
for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
mCallbacks->playPart(i, part, r);
glClearColor(
part.backgroundColor[0],
part.backgroundColor[1],
part.backgroundColor[2],
1.0f);
// For the last animation, if we have progress indicator from
// the system, display it.
int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
bool displayProgress = animation.progressEnabled &&
(i == (pcount -1)) && currentProgress != 0;
for (size_t j=0 ; j<fcount ; j++) {
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
processDisplayEvents();
const int animationX = (mWidth - animation.width) / 2;
const int animationY = (mHeight - animation.height) / 2;
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
if (part.count != 1) {
glGenTextures(1, &frame.tid);
glBindTexture(GL_TEXTURE_2D, frame.tid);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
int w, h;
initTexture(frame.map, &w, &h);
}
const int xc = animationX + frame.trimX;
const int yc = animationY + frame.trimY;
Region clearReg(Rect(mWidth, mHeight));
clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
if (!clearReg.isEmpty()) {
Region::const_iterator head(clearReg.begin());
Region::const_iterator tail(clearReg.end());
glEnable(GL_SCISSOR_TEST);
while (head != tail) {
const Rect& r2(*head++);
glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
glClear(GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
}
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
const int frameDrawY = mHeight - (yc + frame.trimHeight);
glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
// if the part hasn't been stopped yet then continue fading if necessary
if (exitPending() && part.hasFadingPhase()) {
fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
++fadedFramesCount);
if (fadedFramesCount >= part.framesToFadeCount) {
fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
}
}
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
if (displayProgress) {
int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
// In case the new progress jumped suddenly, still show an
// increment of 1.
if (lastDisplayedProgress != 100) {
// Artificially sleep 1/10th a second to slow down the animation.
usleep(100000);
if (lastDisplayedProgress < newProgress) {
lastDisplayedProgress++;
}
}
// Put the progress percentage right below the animation.
int posY = animation.height / 3;
int posX = TEXT_CENTER_VALUE;
drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
}
handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
nsecs_t delay = frameDuration - (now - lastFrame);
//SLOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
lastFrame = now;
if (delay > 0) {
struct timespec spec;
spec.tv_sec = (now + delay) / 1000000000;
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
} while (err<0 && errno == EINTR);
}
checkExit();
}
usleep(part.pause * ns2us(frameDuration));
if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
!part.hasFadingPhase()) {
if (lastDisplayedProgress != 0 && lastDisplayedProgress != 100) {
android::base::SetProperty(PROGRESS_PROP_NAME, "100");
continue;
}
break; // exit the infinite non-fading part when it has been played at least once
}
}
}
// Free textures created for looping parts now that the animation is done.
for (const Animation::Part& part : animation.parts) {
if (part.count != 1) {
const size_t fcount = part.frames.size();
for (size_t j = 0; j < fcount; j++) {
const Animation::Frame& frame(part.frames[j]);
glDeleteTextures(1, &frame.tid);
}
}
}
return true;
}
checkExit()
检测属性"service.bootanim.exit"
退出动画;在playAnimation方法和android方法中都有一个checkExit方法
来负责检查是否退出动画- WMS中
performEnableScreen()
设置SystemProperties.set("service.bootanim.exit", "1")
void BootAnimation::checkExit() {
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
requestExit();
}
}
private void performEnableScreen() {
synchronized (mGlobalLock) {
ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
+ " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
+ " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore,
new RuntimeException("here").fillInStackTrace());
if (mDisplayEnabled) {
return;
}
if (!mSystemBooted && !mShowingBootMessages) {
return;
}
if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
return;
}
// Don't enable the screen until all existing windows have been drawn.
if (!mForceDisplayEnabled) {
if (mBootWaitForWindowsStartTime < 0) {
// First time we will start waiting for all windows to be drawn.
mBootWaitForWindowsStartTime = SystemClock.elapsedRealtime();
}
for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) {
return;
}
}
long waitTime = SystemClock.elapsedRealtime() - mBootWaitForWindowsStartTime;
mBootWaitForWindowsStartTime = -1;
if (waitTime > 10) {
ProtoLog.i(WM_DEBUG_BOOT,
"performEnableScreen: Waited %dms for all windows to be drawn",
waitTime);
}
}
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
return;
}
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
data, null, 0);
data.recycle();
}
} catch (RemoteException ex) {
ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
}
EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis());
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
mDisplayEnabled = true;
ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");
// Enable input dispatch.
mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
}
try {
mActivityManager.bootAnimationComplete();
} catch (RemoteException e) {
}
mPolicy.enableScreenAfterBoot();
// Make sure the last requested orientation has been applied.
updateRotationUnchecked(false, false);
}
bootanimation.zip
bootanimation.zip\desc.txt:
1080 2400 5
p 0 5 part0