双屏异显开机动画实现

1.整体设计思路

基于原生开机动画流程上,背屏开机动画在bootanim服务起来之后,启动主屏开机动画线程bootanimation时,同时启动一条新增加的背屏开机动画线程BackBootAnimation,然后在背屏开机动画线程BackBootAnimation中, 按照主屏开机动画控制流程一样,依次实现从preload分区中加载背屏开机动画资源,解析动画文件资源,初始化EGL,创建用于背屏绘图的Surface,通过Surface创建EGL Surface,创建EGLContext 上下文,调用eglMakeCurrent绑定eglSurface, context,display,最终调用eglSwapBuffer函数进行绘制.

背屏开机动画结束流程,也与主屏相同,不停的检查service.bootanim.exit属性是否被写值为1,写值为1的时候线程停止.

2.流程图

双屏异显开机动画实现_第1张图片 

对于如上流程图中,针对背屏开机动画主要修改在SurfaceFlinger和bootanim服务中,SurfaceFlinger中主要是增加了一开机就对背屏diaplayState初始化流程,新增了一些bootanim服务中需要用到的接口.

bootanim服务中的修改,主要针对背屏开机动画流程修改.理论上我们是需要新增加一个类似Bootanimation.cpp类播放背屏开机动画,这样改动比较大,我们选择了修改Bootanimation.cpp的构造方法,增加一个参数标记当前线程是背屏还是主屏.

 

3.bootanim服务中的修改点

3.1修改 BootAnimation的构造方法

frameworks/base / cmds/bootanimation/BootAnimation.h

....

 explicit BootAnimation(sp callbacks, bool shuttingDown, bool isBackDisplay);//修改构造方法,isBackDisplay新增参数,标记背屏还是主屏

....

 bool        mBackDisplay; //新增参数,标记背屏还是主屏,会在实现类BootAnimation.cpp中用到

....

 

3.2 增加背屏开机动画线程

frameworks/base/cmds/bootanimation/bootanimation_main.cpp

int main(int argc, char** argv)

{

    ....

        sp boot = new BootAnimation(audioplay::createAnimationCallbacks(true), isShutdown, false);//创建按主屏开机动画线程

        sp backBoot = new BootAnimation(audioplay::createAnimationCallbacks(false), isShutdown, true);//创建背屏开机动画线程

BootAnimation的构造方法的三个参数意义:

//audioplay::createAnimationCallbacks(false)//标记是否开机播放铃音

// isShutdown 标记是开机还是关机

//

        waitForSurfaceFlinger();

 

        boot->run("BootAnimation", PRIORITY_DISPLAY);

        backBoot->run("BackBootAnimation", PRIORITY_DISPLAY);//启动背屏线程

        ALOGV("Boot animation set up. Joining pool.");

 

        IPCThreadState::self()->joinThreadPool();

    }

    return 0;

}

 

3.3 增加是否播放开机铃音控制逻辑

frameworks/base / cmds/bootanimation/audioplay.h

android::sp createAnimationCallbacks(bool needAudioPlay);//修改audioplay构造方法,增加 needAudioPlay参数,标记是否播放铃音,在audioplay.cpp使用

 

3.4 控制开机铃音播放逻辑

frameworks/base / cmds/bootanimation/audioplay.cpp

增加构造方法,增加全局变量 mNeedAudioPlay,

    bool mNeedAudioPlay = true;

    AudioAnimationCallbacks(bool needAudioPlay){

       mNeedAudioPlay = needAudioPlay;

    }

    ~AudioAnimationCallbacks(){}

 

playPart方法播放开机铃声方法.

void playPart(int partNumber, const Animation::Part& part, int playNumber) override {

        // only play audio file the first time we animate the part

        if (playNumber == 0 && part.audioData && playSoundsAllowed() && mNeedAudioPlay)   {//播放铃音时增加判断条件

            audioplay::playClip(part.audioData, part.audioLength);

        }

    };

 

3.5 修改开机动画播放控制逻辑

platform/frameworks/base / cmds/bootanimation/BootAnimation.cpp

这个文件中主要修改有三点:

3.5.1 构造方法中初始化 mBackDisplay,全局用其区分主屏还是背屏线程

BootAnimation::BootAnimation(sp callbacks, bool shuttingDown, bool isBackDisplay)

    mBackDisplay = isBackDisplay;//在构造方法中初始化 mBackDisplay,全局用 mBackDisplay区分是主屏线程还是背屏线程

}

 

3.5.2修改 readyToRun(),通过 mBackDisplay区分创建不同的Surface,标记背屏Surface的layerStack=4096 往背屏送图

status_t BootAnimation::readyToRun() {

    mAssets.addDefaultAssets();

    if(mBackDisplay){

        mDisplayToken = SurfaceComposerClient::getBackDisplayToken();// getBackDisplayToken()是新增的接口,后面说明

    }else{

        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();

    }

//获取对应屏幕的displayToken,displayToken中封装的有屏幕所有信息,后面会使用 mDisplayToken中的宽高创建对应的Surface

    if (mDisplayToken == nullptr)

        return -1;

    DisplayInfo dinfo;

    status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);//从 mDisplayToken中取出信息存储 dinfo

    // create the native surface

    sp control;

   if(mBackDisplay){

       control = session()->createSurface(String8("BackBootAnimation"),

                       dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);//用dinfo宽高创建SurfaceControl,最终会用SurfaceControl获取Surface,实际上 dinfo.w, dinfo.h最终给了surface,而client端创建一个surface,对应在server端,SurfaceFlinger中创建一个一模一样信息的Layer,SurfaceFlinger中的Layer,可理解为当前surface

 

       SurfaceComposerClient::Transaction t;

           t.setLayer(control, 0x40000000)

             .setLayerStack(control,4096)//这一句标记当前的Layer将会送给哪个显示设备,默认为主屏,所以主屏开机动画中是没有这一句的.

             .apply();

   }else{

       control = session()->createSurface(String8("BootAnimation"),

                       dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

       SurfaceComposerClient::Transaction t;

           t.setLayer(control, 0x40000000)

            .apply();

   }

    sp s = control->getSurface();

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);// EGL_DEFAULT_DISPLAY 值为0,并且EGL库中就只支持一个 EGL_DEFAULT_DISPLAY

    eglInitialize(display, nullptr, nullptr);

    eglChooseConfig(display, attribs, &config, 1, &numConfigs);

 

    surface = eglCreateWindowSurface(display, config, s.get(), nullptr);

 

    context = eglCreateContext(display, config, nullptr, nullptr);

    eglQuerySurface(display, surface, EGL_WIDTH, &w);

    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

 

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)

        return NO_INIT

}

 

3.5.3 通过 mBackDisplay标记,查找不同的资源文件

void BootAnimation::findBootAnimationFile() {

    const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;

    static const char* bootFiles[] =

        {APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,

         OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};

    static const char* shutdownFiles[] =

        {PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""};

 

    static const char* backBootFiles[] =

        {PRODUCT_BACK_BOOTANIMATION_FILE};//背屏开机动画文件

 

    static const char* backShutdownFiles[] =

        {PRODUCT_BACK_SHUTDOWNANIMATION_FILE};//背屏关机动画文件

 

    if(mBackDisplay){

        for (const char* f : (!mShuttingDown ? backBootFiles : backShutdownFiles)) {

            if (access(f, R_OK) == 0) {

                mZipFileName = f;

                return;

            }

        }

    }else{

        for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {

            if (access(f, R_OK) == 0) {

                mZipFileName = f;

                return;

            }

        }

    }

}

 

4.新增接口说明

4.1 新增接口SurfaceComposerClient::getBackDisplayToken(),在Bootanimation.cpp的 readyToRun()方法中会用到,返回DisplayToken

4.1.1 frameworks/native / libs/gui/include/gui/SurfaceComposerClient.h

定义两个接口:

//niexu add for back Screen boot animation 

 static std::optional getBackDisplayId();

    static sp getBackDisplayToken();

 

4.1.2 frameworks/native / libs/gui/SurfaceComposerClient.cpp 接口实现类,调用服务端的对应接口

std::optional SurfaceComposerClient::getBackDisplayId() {//这个接口其实在bootanim服务中没用上

    return ComposerService::getComposerService()->getBackDisplayId();

}

sp SurfaceComposerClient::getBackDisplayToken() {

    return ComposerService::getComposerService()->getBackDisplayToken();//最终会调用到ISurfaceComposer.h中的getBackDisplayToken();

}

 

4.1.3 frameworks/native / libs/gui/include/gui/ISurfaceComposer.h 接口真正的实现类

    std::optional getBackDisplayId() const {

        const auto displayIds = getPhysicalDisplayIds();//系统方法,默认返回数组,背屏back(),主屏front(),

        return displayIds.empty() ? std::nullopt : std::make_optional(displayIds.back());

    }

 

    sp getBackDisplayToken() const {

            const auto displayId = getBackDisplayId();

            return displayId ? getPhysicalDisplayToken(*displayId) : nullptr;

        }

5.SurfaceFlinger中的修改

SurfaceFlinger中主要修改点,在SurfaceFlinger的init初始化方法中,原生代码,只是在此处初始化了主屏的displayState,背屏的displayState初始化流程貌似从Frameworks中的DisplayManagerService往下初始化的(这个猜测,还未仔细研究),这就导致了背屏开机动画播放开始播放的时间点比主屏开机动画播放的时间点晚(实际上背屏开机线程已经起来,送图已经开始了一段时间,但dispalyState未初始化完成,导致背屏开机动画part0中的图片未显示).为了解决这个问题,在SurfaceFlinger的init方法中,初始化主屏的displayState时,同时初始化背屏的displayState,即可实现同步播放.

5.1 SurfaceFlinger中初始化背屏displayState

frameworks/native / services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::onInitializeDisplays() {

    const auto display = getDefaultDisplayDeviceLocked();

    if (!display) return;

    const sp token = display->getDisplayToken().promote();

    LOG_ALWAYS_FATAL_IF(token == nullptr);

 

    // reset screen orientation and use primary layer stack

    Vector state;

    Vector displays;

    DisplayState d;

    d.what = DisplayState::eDisplayProjectionChanged |

             DisplayState::eLayerStackChanged;

    d.token = token;

    d.layerStack = 0;//主屏display id

    d.orientation = DisplayState::eOrientationDefault;

    d.frame.makeInvalid();

    d.viewport.makeInvalid();

    d.width = 0;

    d.height = 0;

    displays.add(d);//add 到 DisplayState中

 

    //bug  add for sub screen boot animation 

    const auto backDisplay = getExternalDisplayDeviceLocked();//新增接口,通过hw层的背屏物理id,获取hw层的PhysicalDisplayToken,再通过PhysicalDisplayToken获取displayToken

    SLOGD("SurfaceFlinger backDisplay: %d", backDisplay? 1 : 0 );

    if(backDisplay){

        const sp backToken = backDisplay->getDisplayToken().promote();//拿到背屏的displayToken,使用其封装的屏幕信息,初始化背屏displayState

 

        LOG_ALWAYS_FATAL_IF(backToken == nullptr);

 

        DisplayState backDisplayState;

        backDisplayState.what = DisplayState::eDisplayProjectionChanged |

                 DisplayState::eLayerStackChanged;

        backDisplayState.token = backToken;

        backDisplayState.layerStack = 4096;//背屏的display id

        backDisplayState.orientation = DisplayState::eOrientation90;//强制转90度

        backDisplayState.frame.makeInvalid();

        backDisplayState.viewport.makeInvalid();

        backDisplayState.width = 0;

        backDisplayState.height = 0;

        displays.add(backDisplayState);//将背屏DisplayState add到 displays中

        setPowerModeInternal(backDisplay, HWC_POWER_MODE_NORMAL);

     }

    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});

    setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);

    const nsecs_t vsyncPeriod = getVsyncPeriod();

    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);

 

    // Use phase of 0 since phase is not known.

    // Use latency of 0, which will snap to the ideal latency.

    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};

    setCompositorTimingSnapped(stats, 0);

}

 

5.2 SurfaceFlinger中使用到的新增接口说明

frameworks/native / services/surfaceflinger/SurfaceFlinger.h

    sp getExternalDisplayDeviceLocked() const {//SurfaceFlinger.cpp中调用该方法,接着会往下调用自身的 getExternalDisplayDeviceLocked

            return const_cast(this)->getExternalDisplayDeviceLocked();

        }

 

    sp getExternalDisplayDeviceLocked() {

         if (const auto token = getExternalDisplayTokenLocked()) {

             return getDisplayDeviceLocked(token);//返回供上层使用的displayToken

         }

         return nullptr;

    }

    sp getExternalDisplayTokenLocked() const {

    //返回背屏hw层的PhysicalDisplayToken

            const auto displayId = getExternalDisplayIdLocked();

            return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;

        }

    std::optional getExternalDisplayIdLocked() const {

     //返回hw层背屏物理id

            const auto hwcExternalDisplayId = getHwComposer().getExternalHwcDisplayId();

            return hwcExternalDisplayId ?     getHwComposer().toPhysicalDisplayId(*hwcExternalDisplayId) : std::nullopt;

        }

你可能感兴趣的:(项目经验积累,android)