在Android系统开发过程中,偶尔会遇到显示异常的问题,如画面不再刷新,也就是冻屏,或者屏幕变黑,如果对图形显示系统不熟悉,遇到此类问题还是比较棘手的。如果在工作中遇到类似的问题,或者对图形显示系统感兴趣,可参考清华大学出版社出版的《Android图形显示系统》。 图形显示系统是Android系统中最为核心、复杂的子系统,掌握它对于理解Android系统会有一个很大的提升,是一门进阶课题。
下面主要结合《Android图形显示系统》来分析工作过程中碰到的图形显示异常的问题。该书主要基于Android9,而本文主要基于Android12,其实思路是一样的,只不过在代码上有差异。
假设遇到了一个屏幕突然变黑的问题,首先得确认黑屏的可能原因,比如是否灭屏,如果不是灭屏,那就是应用的图形内容没有正常地传递到屏幕,可以在SurfaceFlinger确定大体原因。
通过《Android图形显示系统》可以指导,应用的图形内容要经过SurfaceFlinger才能传递到显示设备,SurfaceFlinger每隔一定时间(间隔由帧率决定)会向屏幕传递图形数据,但是并不是每个应用的图形都会显示,只有最前面应用的画面才能显示,应用的图形数据一般保存在图层中,因此每次刷新都会收集可见的图层。核心流程如下。
void SurfaceFlinger::onMessageRefresh() {
mCompositionEngine->present(refreshArgs);
}
void CompositionEngine::present(CompositionRefreshArgs& args) {
for (const auto& output : args.outputs) {
output->prepare(args, latchedLayers);
}
}
void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& geomSnapshots) {
rebuildLayerStacks(refreshArgs, geomSnapshots);
}
void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
compositionengine::Output::CoverageState& coverage) {
for (auto layer : reversed(refreshArgs.layers)) {
ensureOutputLayerIfVisible(layer, coverage);
}
}
void Output::ensureOutputLayerIfVisible(sp& layerFE,
compositionengine::Output::CoverageState& coverage) {
auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
layerFE->dumpLayer();
}
流程解析如下:
1)SurfaceFlinger在处理onMessageRefresh时正是要刷新屏幕,调用CompositionEngine的present进行下一步处理。
2)CompositionEngine在处理present时,针对每个显示设备调用prepare先准备好要处理的图层,每个Output对象代表一个显示设备。
3)Output在处理prepare主要重建图层栈,也就是找到可见的图层,如果上面的流程走到ensureOutputLayerIfVisible的ensureOutputLayer,可以确定该图层是可见的,这里可以把该图层及其父图层打印出来,这里layerFE->dumpLayer();是新加的方法,需要在Layer也加上相应的实现,如下。
class LayerFE : public virtual RefBase {
virtual void dumpLayer() = 0;
};
class Layer : public virtual RefBase, compositionengine::LayerFE {
void dumpLayer() override {
sp parent = getParent();
if(parent != nullptr){
parent->dumpLayer();
}
ALOGI("dumpLayer LayerName: %s", mName.c_str());
}
}
这里会把图层及其父图层的名称打印出来,结果如下。
04-12 14:37:57.714 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{7c850a1 android.os.BinderProxy@bd7a6ab}#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: f541552 ScreenDecorOverlayBottom#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: ScreenDecorOverlayBottom#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{6e71384 android.os.BinderProxy@26cb1d8}#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: fdb4f1c ScreenDecorOverlay#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: ScreenDecorOverlay#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Root#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowedMagnification:0:31#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Leaf:24:25#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{4b66858 android.os.BinderProxy@23dd335}#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Surface(name=521d296 NavigationBar0)/@0x8baf6da - animation-leash of insets_animation#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: 521d296 NavigationBar0#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: NavigationBar0#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Root#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowedMagnification:0:31#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: OneHanded:17:17#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: FullscreenMagnification:17:17#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Leaf:17:17#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowToken{4beb8e7 android.os.BinderProxy@afe79e8}#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Surface(name=17ee33d StatusBar)/@0xbfb7639 - animation-leash of insets_animation#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: 17ee33d StatusBar#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: StatusBar#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Root#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowedMagnification:0:31#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: HideDisplayCutout:0:16#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: OneHanded:2:16#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: FullscreenMagnification:2:14#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: DefaultTaskDisplayArea#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Task=14#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: ActivityRecord{73e3548 u0 com.sino.compose/.MainActivity t14}#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Surface(name=b001cb7 Splash Screen com.sino.compose)/@0xf9b3d26 - animation-leash of window_animation#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: b001cb7 Splash Screen com.sino.compose#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Splash Screen com.sino.compose#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Root#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: WindowedMagnification:0:31#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: HideDisplayCutout:0:16#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: OneHanded:2:16#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: FullscreenMagnification:2:14#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: DefaultTaskDisplayArea#0
04-12 14:37:57.715 720 720 I BufferLayer: dumpLayer LayerName: Task=14#0
04-12 14:37:57.716 720 720 I BufferLayer: dumpLayer LayerName: ActivityRecord{73e3548 u0 com.sino.compose/.MainActivity t14}#0
04-12 14:37:57.716 720 720 I BufferLayer: dumpLayer LayerName: 1c021fa com.sino.compose/com.sino.compose.MainActivity#0
在上面的日志中Root#0是第0个屏幕的根图层,com.sino.compose/com.sino.compose.MainActivity才是应用显示内容的图层。从内容图层到根图层有9层。
这里需要根据打印的图层确定异常的位置,如果某个图层突然不显示了,那么就不会打印出来,找到发送变换的位置,找到是哪个图层发生了变化。如果一个图层发生变化,基本上是向它设置了状态,可以把在相关方法加日志,比如设置隐藏状态对于的接口为setFlags,
bool Layer::setFlags(uint32_t flags, uint32_t mask) {
const uint32_t newFlags = (mDrawingState.flags & ~mask) | (flags & mask);
if (mDrawingState.flags == newFlags) return false;
mDrawingState.sequence++;
mDrawingState.flags = newFlags;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
一旦确认是该接口被调用导致图层的状态发生了变化,就到SurfaceControl找到相应的接口,并加日志进一步确认。
public final class SurfaceControl implements Parcelable {
public @Nullable String getName(){
return mName;
}
public static class Transaction implements Closeable, Parcelable {
public Transaction hide(SurfaceControl sc) {
checkPreconditions(sc);
Log.i("SurfaceControl::Transaction", "hide "+sc.getName());
Thread.dumpStack();
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
return this;
}
}
}
在上面的代码中,通过Thread.dumpStack();可以把调用hide方法的调用堆栈打印出来,
04-12 19:32:57.502 1744 1892 I SurfaceControl::Transaction: hide com.sino.compose/com.sino.compose.MainActivity
04-12 19:32:57.502 1744 1892 W System.err: java.lang.Exception: Stack trace
04-12 19:32:57.502 1744 1892 W System.err: at java.lang.Thread.dumpStack(Thread.java:1517)
04-12 19:32:57.502 1744 1892 W System.err: at android.view.SurfaceControl$Transaction.hide(SurfaceControl.java:2760)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowSurfaceController.hideSurface(WindowSurfaceController.java:128)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowSurfaceController.hide(WindowSurfaceController.java:118)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowStateAnimator.hide(WindowStateAnimator.java:228)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowStateAnimator.prepareSurfaceLocked(WindowStateAnimator.java:531)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowState.prepareSurfaces(WindowState.java:5494)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.ActivityRecord.prepareSurfaces(ActivityRecord.java:6682)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.Task.prepareSurfaces(Task.java:3973)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.502 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.503 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.503 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.503 1744 1892 W System.err: at com.android.server.wm.WindowContainer.prepareSurfaces(WindowContainer.java:2432)
04-12 19:32:57.503 1744 1892 W System.err: at com.android.server.wm.DisplayArea$Dimmable.prepareSurfaces(DisplayArea.java:644)
通过堆栈可以分析到调用该接口的地方,这里主要是WindowManagerService的内容,需要根据实际情况分析具体原因。
假设图层没有异常,就需要分析图形数据有没有传递到HAL层,在《Android图形显示系统》介绍过,通过硬件混合渲染器合成的方式主要分为以下3步。
1)createLayer
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::onMessageRefresh() {
mCompositionEngine->present(refreshArgs);
}
//frameworks/native/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
void CompositionEngine::present(CompositionRefreshArgs& args) {
for (const auto& output : args.outputs) {
output->prepare(args, latchedLayers);
}
}
//frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp
void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& geomSnapshots) {
rebuildLayerStacks(refreshArgs, geomSnapshots);
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
collectVisibleLayers(refreshArgs, coverage);
}
void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
compositionengine::Output::CoverageState& coverage) {
for (auto layer : reversed(refreshArgs.layers)) {
// Incrementally process the coverage for each layer
ensureOutputLayerIfVisible(layer, coverage);
}
}
void Output::ensureOutputLayerIfVisible(sp& layerFE,
compositionengine::Output::CoverageState& coverage) {
auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
}
//frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
class Output : public virtual compositionengine::Output {
OutputLayer* ensureOutputLayer(std::optional prevIndex,
const sp& layerFE) {
auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())
? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex])
: BaseOutput::createOutputLayer(layerFE); //here
}
}
//frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cpp
std::unique_ptr Display::createOutputLayer(
const sp& layerFE) const {
auto hwcLayer = hwc.createLayer(*halDisplayId);
}
//frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
std::shared_ptr HWComposer::createLayer(HalDisplayId displayId) {
auto expected = mDisplayData[displayId].hwcDisplay->createLayer();
}
//frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.cpp
base::expected, hal::Error> Display::createLayer() {
HWLayerId layerId = 0;
auto intError = mComposer.createLayer(mId, &layerId);
}
//frameworks/native/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
Error Composer::createLayer(Display display, Layer* outLayer)
{
mClient->createLayer(display, kMaxLayerBufferCount,
[&](const auto& tmpError, const auto& tmpLayer) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outLayer = tmpLayer;
});
return error;
}
createLayer请求HAL创建硬件图层保存图形缓冲。
2) setBuffer
void CompositionEngine::present(CompositionRefreshArgs& args) {
for (const auto& output : args.outputs) {
output->present(args);
}
}
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
writeCompositionState(refreshArgs);
}
void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
}
void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
bool zIsOverridden, bool isPeekingThrough) {
if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); error != hal::Error::NONE) {}
}
//HWC2.cpp
Error Layer::setBuffer(uint32_t slot, const sp& buffer,
const sp& acquireFence)
{
auto intError = mComposer.setLayerBuffer(mDisplay->getId(), mId, slot, buffer, fenceFd);
return static_cast(intError);
}
Error Composer::setLayerBuffer(Display display, Layer layer,
uint32_t slot, const sp& buffer, int acquireFence)
{
mWriter.selectDisplay(display);
mWriter.selectLayer(layer);
const native_handle_t* handle = nullptr;
if (buffer.get()) {
handle = buffer->getNativeBuffer()->handle;
}
mWriter.setLayerBuffer(slot, handle, acquireFence);
return Error::NONE;
}
setBuffer把要显示的图形缓冲从SurfaceFlinger传递到HAL
3) present
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
prepareFrame();
}
void Output::prepareFrame() {
chooseCompositionStrategy();
}
void Display::chooseCompositionStrategy() {
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
getState().earliestPresentTime,
getState().previousPresentFence, &changes);
}
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
std::chrono::steady_clock::time_point earliestPresentTime,
const std::shared_ptr& previousPresentFence,
std::optional* outChanges) {
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
}
HWC2.cpp
Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
sp* outPresentFence, uint32_t* state) {
auto intError = mComposer.presentOrValidateDisplay(
mId, &numTypes, &numRequests, &presentFenceFd, state);
}
Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
mWriter.selectDisplay(display);
mWriter.presentOrvalidateDisplay();
}
present通知HAL把图形数据传递到底层驱动,开始对图形进行合成并显示。
如果上面3步流程也没有问题,那么可以认为是底层显示驱动的问题,需要在显示驱动模块继续分析原因。
小结:
通过上面的分析可以指定,分析图形显示异常的关键主要在SurfaceFlinger,因此要对SurfaceFlinger的关键流程要比较熟悉,同时WindowManagerSurface的内容也很重要,这两部分内容可参考《Android图形显示系统》。