검색결과 리스트
글
안드로이드 프레임워크 프로그래밍(22) [SurfaceFlinger(시스템 서비스) 등록 및 초기화 과정]
※ 본 포스팅은 Android KitKat 4.4.4 를 기준으로 작성되었습니다. Kitkat 이전의 버전에서는 소스코드의 구조가 다름을 알립니다.
안드로이드 기반 디바이스에 있어 화면을 출력하는 데 가장 중요한 기능으로 SurfaceFlinger가 있습니다. 본 포스팅에서는 시스템 서비스 중 하나인SurfaceFlinger에 대한 간단한 설명과 Framework 소스코드를 통해 SurfaceFlinger가 생성되는 과정을 살펴보도록 하겠습니다.
안드로이드 운영체제가 실행되는 동안 내부에는 수많은 Application들이 실행되고 있습니다. 이러한 Application 중 화면에 정보를 출력하는 애플리케이션이 있는데 이는 Surface를 통해 화면의 정보를 구성합니다. Surface는 화면에 출력하는 그림의 단위로서 하나의 Application이 2개 이상의 Surface를 가지고 있을 수 있습니다.
Application들이 가지고 있는 각 Surface들은 각각 BufferQueue에 연결되어 있으며 Surface에서 생성되는 화면의 정보가 BufferQueue에 축적되게 되면 이후 SurfaceFlinger에서 BufferQueue에 있는 Surface의 정보를 수신하게 됩니다.
각 Surface로부터 화면을 받은 SurfaceFlinger는 수신된 화면의 정보를 합성하게 되고 이를 디스플레이를 담당하는 HAL에 정보를 전달하게 되면 해당 화면이 안드로이드 기반 디바이스의 Display에 출력됩니다.
SurfaceFlinger의 기능에 대해 그림을 통해 간단하게 살펴보았습니다. 다음으로는 Framework를 통해 SurfaceFlinger가 어떻게 생성되는지 살펴보도록 하겠습니다.
/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if defined(HAVE_PTHREADS) #include <sys/resource.h> #endif #include <cutils/sched_policy.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include "SurfaceFlinger.h" using namespace android; int main(int argc, char** argv) { // When SF is launched in its own process, limit the number of // binder threads to 4. ProcessState::self()->setThreadPoolMaxThreadCount(4); // start the thread pool sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); // instantiate surfaceflinger sp<SurfaceFlinger> flinger = new SurfaceFlinger(); #if defined(HAVE_PTHREADS) setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY); #endif set_sched_policy(0, SP_FOREGROUND); // initialize before clients can connect flinger->init(); // publish surface flinger sp<IServiceManager> sm(defaultServiceManager()); sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false); // run in this thread flinger->run(); return 0; } | cs |
C++에 대해 어느 정도 이해하시는 분이라면 위의 소스코드를 보신다면 각 기능의 기능에 대해 바로 이해 하시리라 생각합니다. 각 소스코드를 한 줄씩 분석해 보도록 하겠습니다.
sp<SurfaceFlinger> flinger = new SurfaceFlinger()
SurfaceFlinger를 생성합니다. 생성시 소스코드는 아래와 같습니다.
/frameworks/native/services/surfaceflinger/SurfaceFlinger.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | .... class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, private HWComposer::EventHandler { public: static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } SurfaceFlinger() ANDROID_API; // must be called before clients can connect void init() ANDROID_API; // starts SurfaceFlinger main loop in the current thread void run() ANDROID_API; enum { EVENT_VSYNC = HWC_EVENT_VSYNC }; // post an asynchronous message to the main thread status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); // post a synchronous message to the main thread status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); .... } | cs |
SurfaceFlinger는 총 3개의 클래스를 상속하고 있는 것을 알 수 있습니다. SurfaceFlinger 생성자가 생성되는 동안 중요한 부분들을 소스코드를 통해 확인해 보도록 합시다.
/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | .... class HWComposer { public: class EventHandler { friend class HWComposer; virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0; virtual void onHotplugReceived(int disp, bool connected) = 0; protected: virtual ~EventHandler() {} }; enum { NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES, MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES, VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL, }; HWComposer( const sp<SurfaceFlinger>& flinger, EventHandler& handler); .... } | cs |
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | .... SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), mTransactionFlags(0), mTransactionPending(false), mAnimTransactionPending(false), mLayersRemoved(false), mRepaintEverything(0), mRenderEngine(NULL), mBootTime(systemTime()), mVisibleRegionsDirty(false), mHwWorkListDirty(false), mAnimCompositionPending(false), mDebugRegion(0), mDebugDDMS(0), mDebugDisableHWC(0), mDebugDisableTransformHint(0), mDebugInSwapBuffers(0), mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), mBootFinished(false), mPrimaryHWVsyncEnabled(false), mHWVsyncAvailable(false), mDaltonize(false) { ALOGI("SurfaceFlinger is starting"); // debugging stuff... char value[PROPERTY_VALUE_MAX]; property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); mGpuToCpuSupported = !atoi(value); property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); property_get("debug.sf.ddms", value, "0"); mDebugDDMS = atoi(value); if (mDebugDDMS) { if (!startDdmConnection()) { // start failed, and DDMS debugging not enabled mDebugDDMS = 0; } } ALOGI_IF(mDebugRegion, "showupdates enabled"); ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); } .... | cs |
SurfaceFlinger가 초기화가 이루어지는 동안 각 변수에 특정한 값이 저장되는 것이 보일 것입니다. 각 변수는 SurfaceFlinger.h에 정의되어 있습니다.
/frameworks/native/services/surfaceflinger/SurfaceFlinger.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | .... enum { eTransactionNeeded = 0x01, eTraversalNeeded = 0x02, eDisplayTransactionNeeded = 0x04, eTransactionMask = 0x07 }; class SurfaceFlinger : public BnSurfaceComposer, private IBinder::DeathRecipient, private HWComposer::EventHandler { .... // constant members (no synchronization needed for access) HWComposer* mHwc; RenderEngine* mRenderEngine; nsecs_t mBootTime; bool mGpuToCpuSupported; sp<EventThread> mEventThread; sp<EventThread> mSFEventThread; sp<EventControlThread> mEventControlThread; EGLContext mEGLContext; EGLConfig mEGLConfig; EGLDisplay mEGLDisplay; EGLint mEGLNativeVisualId; sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; .... // don't use a lock for these, we don't care int mDebugRegion; int mDebugDDMS; int mDebugDisableHWC; int mDebugDisableTransformHint; volatile nsecs_t mDebugInSwapBuffers; nsecs_t mLastSwapBufferTime; volatile nsecs_t mDebugInTransaction; nsecs_t mLastTransactionTime; bool mBootFinished; .... } | cs |
property란 안드로이드 system에서 각 프로세서들이 system의 설정값을 공유하는 저장소입니다. 설정값들은 Hash 테이블로 구성되어 있으며 각 key와 value는 string으로 구성되어 있습니다.
property_get(key, value) 함수에서 key는 우리들이 찾고자 하는 해당 system의 property의 key값을 나타내며 value는 key를 통해 찾아낸 value값을 포인터를 통해 SurfaceFlinger에 사용할 수 있게 저장합니다. 아래 소스코드는 property_get() 함수가 정의된 모습입니다.
/frameworks/rs/rsCompatibilityLib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include "rsCompatibilityLib.h" #include <string.h> #include <sys/system_properties.h> // Implementation of property_get from libcutils int property_get(const char *key, char *value, const char *default_value) { int len; len = __system_property_get(key, value); if (len > 0) { return len; } if (default_value) { len = strlen(default_value); memcpy(value, default_value, len + 1); } return len; } | cs |
flinger->init();
새로 생성된 SurfaceFlinger을 초기화 시킵니다. 소스코드는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); status_t err; Mutex::Autolock _l(mStateLock); // initialize EGL for the default display mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(mEGLDisplay, NULL, NULL); // Initialize the H/W composer object. There may or may not be an // actual hardware composer underneath. mHwc = new HWComposer(this, *static_cast<HWComposer::EventHandler *>(this)); // First try to get an ES2 config err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), EGL_OPENGL_ES2_BIT, &mEGLConfig); if (err != NO_ERROR) { // If ES2 fails, try ES1 err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), EGL_OPENGL_ES_BIT, &mEGLConfig); } if (err != NO_ERROR) { // still didn't work, probably because we're on the emulator... // try a simplified query ALOGW("no suitable EGLConfig found, trying a simpler query"); err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), 0, &mEGLConfig); } if (err != NO_ERROR) { // this EGL is too lame for android LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); } // print some debugging info EGLint r,g,b,a; eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_RED_SIZE, &r); eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_GREEN_SIZE, &g); eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_BLUE_SIZE, &b); eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_ALPHA_SIZE, &a); ALOGI("EGL informations:"); ALOGI("vendor : %s", eglQueryString(mEGLDisplay, EGL_VENDOR)); ALOGI("version : %s", eglQueryString(mEGLDisplay, EGL_VERSION)); ALOGI("extensions: %s", eglQueryString(mEGLDisplay, EGL_EXTENSIONS)); ALOGI("Client API: %s", eglQueryString(mEGLDisplay, EGL_CLIENT_APIS)?:"Not Supported"); ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, mEGLConfig); // get a RenderEngine for the given display / config (can't fail) mRenderEngine = RenderEngine::create(mEGLDisplay, mEGLConfig); // retrieve the EGL context that was selected/created mEGLContext = mRenderEngine->getEGLContext(); // figure out which format we got eglGetConfigAttrib(mEGLDisplay, mEGLConfig, EGL_NATIVE_VISUAL_ID, &mEGLNativeVisualId); LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT, "couldn't create EGLContext"); // initialize our non-virtual displays for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i); // set-up the displays that are already connected if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) { // All non-virtual displays are currently considered secure. bool isSecure = true; createBuiltinDisplayLocked(type); wp<IBinder> token = mBuiltinDisplays[i]; sp<BufferQueue> bq = new BufferQueue(new GraphicBufferAlloc()); sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i, bq); sp<DisplayDevice> hw = new DisplayDevice(this, type, allocateHwcDisplayId(type), isSecure, token, fbs, bq, mEGLConfig); if (i > DisplayDevice::DISPLAY_PRIMARY) { // FIXME: currently we don't get blank/unblank requests // for displays other than the main display, so we always // assume a connected display is unblanked. ALOGD("marking display %d as acquired/unblanked", i); hw->acquireScreen(); } mDisplays.add(token, hw); } } // make the GLContext current so that we can create textures when creating Layers // (which may happens before we render something) getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); // start the EventThread sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, vsyncPhaseOffsetNs, true); mEventThread = new EventThread(vsyncSrc); sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, sfVsyncPhaseOffsetNs, false); mSFEventThread = new EventThread(sfVsyncSrc); mEventQueue.setEventThread(mSFEventThread); mEventControlThread = new EventControlThread(this); mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY); // set a fake vsync period if there is no HWComposer if (mHwc->initCheck() != NO_ERROR) { mPrimaryDispSync.setPeriod(16666667); } // initialize our drawing state mDrawingState = mCurrentState; // set initial conditions (e.g. unblank default device) initializeDisplays(); // start boot animation startBootAnim(); } | cs |
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
SurfaceFlinger를 SystemService에 등록합니다. SystemService에 등록됨과 동시에 Surface에서 BaseRef로부터 overriding한 onFirstRef() 함수가 실행됩니다.
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
1 2 3 4 | void SurfaceFlinger::onFirstRef() { mEventQueue.init(this); } | cs |
flinger->run();
SurfaceFlinger의 Thread를 실행합니다.
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
1 2 3 4 5 6 7 8 9 10 11 | void SurfaceFlinger::waitForEvent() { mEventQueue.waitMessage(); } .... void SurfaceFlinger::run() { do { waitForEvent(); } while (true); } | cs |
'안드로이드 > 프레임워크' 카테고리의 다른 글
Android에서 VSync 동작 원리 및 초기화 과정(2) (0) | 2015.08.30 |
---|---|
Android에서 VSync 동작 원리 및 초기화 과정(1) (0) | 2015.08.29 |
안드로이드 프레임워크 프로그래밍(21) [System Service란?] (0) | 2015.08.23 |
SurfaceView가 다른 View 클래스와의 차이(updateWindow() method in SurfaceView class) (0) | 2015.08.17 |
[Android Developers] SurfaceHolder (0) | 2015.08.13 |