Android에서 VSync 동작 원리 및 초기화 과정(3)

안드로이드/프레임워크 2015. 8. 31. 12:02

 지난 포스팅에 이어 VSync의 동작 과정을 이어서 살펴보도록 하겠습니다. 지난 포스팅에서 VSync의 동작 설정 여부를 살펴보았고 이어서 해당 동작이 현재 실행중인 Application 단계에 전달하는 과정을 이어서 살펴보겠습니다. 지난 포스팅에서 다루었던 SurfaceFlinger::onVSyncReceived() 함수부터 이어가도록 하겠습니다.


/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;
 
    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }
 
    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}
cs


/frameworks/native/services/surfaceflinger/SurfaceFlinger.h

1
2
3
4
5
6
....
    // these are thread safe
    mutable MessageQueue mEventQueue;
    FrameTracker mAnimFrameTracker;
    DispSync mPrimaryDispSync;
....
cs

 위의 함수에서 쓰인 변수 mPrimaryDispSync는 DispSync 클래스로서 해당 클래스의 함수 addResyncSample() 함수를 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.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
45
46
47
48
49
50
51
52
53
// DispSync maintains a model of the periodic hardware-based vsync events of a
// display and uses that model to execute period callbacks at specific phase
// offsets from the hardware vsync events.  The model is constructed by
// feeding consecutive hardware event timestamps to the DispSync object via
// the addResyncSample method.
//
// The model is validated using timestamps from Fence objects that are passed
// to the DispSync object via the addPresentFence method.  These fence
// timestamps should correspond to a hardware vsync event, but they need not
// be consecutive hardware vsync times.  If this method determines that the
// current model accurately represents the hardware event times it will return
// false to indicate that a resynchronization (via addResyncSample) is not
// needed.
class DispSync {
 
public:
 
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {};
        virtual void onDispSyncEvent(nsecs_t when) = 0;
    };
 
    DispSync();
    ~DispSync();
 
    void reset();
 
....
 
    // The beginResync, addResyncSample, and endResync methods are used to re-
    // synchronize the DispSync's model to the hardware vsync events.  The re-
    // synchronization process involves first calling beginResync, then
    // calling addResyncSample with a sequence of consecutive hardware vsync
    // event timestamps, and finally calling endResync when addResyncSample
    // indicates that no more samples are needed by returning false.
    //
    // This resynchronization process should be performed whenever the display
    // is turned on (i.e. once immediately after it's turned on) and whenever
    // addPresentFence returns true indicating that the model has drifted away
    // from the hardware vsync events.
    void beginResync();
    bool addResyncSample(nsecs_t timestamp);
    void endResync();
 
    // The setPreiod method sets the vsync event model's period to a specific
    // value.  This should be used to prime the model when a display is first
    // turned on.  It should NOT be used after that.
    void setPeriod(nsecs_t period);
 
....
 
};
cs


/frameworks/native/services/surfaceflinger/DispSync.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
bool DispSync::addResyncSample(nsecs_t timestamp) {
    Mutex::Autolock lock(mMutex);
 
    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    mResyncSamples[idx] = timestamp;
 
    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }
 
    updateModelLocked();
 
    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }
 
    if (runningWithoutSyncFramework) {
        // If we don't have the sync framework we will never have
        // addPresentFence called.  This means we have no way to know whether
        // or not we're synchronized with the HW vsyncs, so we just request
        // that the HW vsync events be turned on whenever we need to generate
        // SW vsync events.
        return mThread->hasAnyEventListeners();
    }
 
    return mPeriod == 0 || mError > errorThreshold;
}
cs

addResyncSample()함수는 mThread를 통해 hasAnyEventListeners() 함수를 호출합니다. 여기서 잠시 mThread에 대해 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.h

1
2
    // mThread is the thread from which all the callbacks are called.
    sp<DispSyncThread> mThread;
cs


/frameworks/native/services/surfaceflinger/DispSync.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
class DispSyncThread: public Thread {
public:
 
    DispSyncThread():
            mStop(false),
            mPeriod(0),
            mPhase(0),
            mWakeupLatency(0) {
    }
 
    virtual ~DispSyncThread() {}
 
    void updateModel(nsecs_t period, nsecs_t phase) {
        Mutex::Autolock lock(mMutex);
        mPeriod = period;
        mPhase = phase;
        mCond.signal();
    }
 
....
 
    // This method is only here to handle the runningWithoutSyncFramework
    // case.
    bool hasAnyEventListeners() {
        Mutex::Autolock lock(mMutex);
        return !mEventListeners.empty();
    }
 
private:
 
    struct EventListener {
        nsecs_t mPhase;
        nsecs_t mLastEventTime;
        sp<DispSync::Callback> mCallback;
    };
 
    struct CallbackInvocation {
        sp<DispSync::Callback> mCallback;
        nsecs_t mEventTime;
    };
 
....
 
    Vector<EventListener> mEventListeners;
 
    Mutex mMutex;
    Condition mCond;
};
cs

 이제 DispSync의 Thread에 대해 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.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
DispSync::DispSync() {
    mThread = new DispSyncThread();
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 
    reset();
    beginResync();
 
    if (traceDetailedInfo) {
        // If runningWithoutSyncFramework is true then the ZeroPhaseTracer
        // would prevent HW vsync event from ever being turned off.
        // Furthermore the zero-phase tracing is not needed because any time
        // there is an event registered we will turn on the HW vsync events.
        if (!runningWithoutSyncFramework) {
            addEventListener(0new ZeroPhaseTracer());
        }
    }
}
 
DispSync::~DispSync() {}
 
void DispSync::reset() {
    Mutex::Autolock lock(mMutex);
 
    mNumResyncSamples = 0;
    mFirstResyncSample = 0;
    mNumResyncSamplesSincePresent = 0;
    resetErrorLocked();
}
 
....
 
void DispSync::beginResync() {
    Mutex::Autolock lock(mMutex);
    mNumResyncSamples = 0;
}
cs


/frameworks/native/services/surfaceflinger/DispSync.cpp

1
2
3
4
5
6
status_t DispSync::addEventListener(nsecs_t phase,
        const sp<Callback>& callback) {
 
    Mutex::Autolock lock(mMutex);
    return mThread->addEventListener(phase, callback);
}
cs


 소스코드 상으로 보았을 때 addEventListener()를 통해 ZeroPhaseTracer가 EventListener로 등록되고 있음을 확인하실 수 있습니다. 여기서 ZeroPhaseTracer에 대해  알아보겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.cpp

1
2
3
4
5
6
7
8
9
10
11
12
class ZeroPhaseTracer : public DispSync::Callback {
public:
    ZeroPhaseTracer() : mParity(false) {}
 
    virtual void onDispSyncEvent(nsecs_t when) {
        mParity = !mParity;
        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
    }
 
private:
    bool mParity;
};
cs


 이제 addEventListener() 함수를 살펴보도록 합시다.

/frameworks/native/services/surfaceflinger/DispSync.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
    status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
        Mutex::Autolock lock(mMutex);
 
        for (size_t i = 0; i < mEventListeners.size(); i++) {
            if (mEventListeners[i].mCallback == callback) {
                return BAD_VALUE;
            }
        }
 
        EventListener listener;
        listener.mPhase = phase;
        listener.mCallback = callback;
 
        // We want to allow the firstmost future event to fire without
        // allowing any past events to fire.  Because
        // computeListenerNextEventTimeLocked filters out events within a half
        // a period of the last event time, we need to initialize the last
        // event time to a half a period in the past.
        listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC) - mPeriod / 2;
 
        mEventListeners.push(listener);
 
        mCond.signal();
 
        return NO_ERROR;
    }
cs

 위의 과정을 통해 EventListener가 등록되었고 push() 함수를 통해 등록한 후 signal()함수로 thread를 깨웁니다. 이제 thread의 threadLoop()함수를 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.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
59
60
61
62
63
64
65
66
67
68
69
    virtual bool threadLoop() {
        status_t err;
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        nsecs_t nextEventTime = 0;
 
        while (true) {
            Vector<CallbackInvocation> callbackInvocations;
 
            nsecs_t targetTime = 0;
 
            { // Scope for lock
                Mutex::Autolock lock(mMutex);
 
                if (mStop) {
                    return false;
                }
 
                if (mPeriod == 0) {
                    err = mCond.wait(mMutex);
                    if (err != NO_ERROR) {
                        ALOGE("error waiting for new events: %s (%d)",
                                strerror(-err), err);
                        return false;
                    }
                    continue;
                }
 
                nextEventTime = computeNextEventTimeLocked(now);
                targetTime = nextEventTime;
 
                bool isWakeup = false;
 
                if (now < targetTime) {
                    err = mCond.waitRelative(mMutex, targetTime - now);
 
                    if (err == TIMED_OUT) {
                        isWakeup = true;
                    } else if (err != NO_ERROR) {
                        ALOGE("error waiting for next event: %s (%d)",
                                strerror(-err), err);
                        return false;
                    }
                }
 
                now = systemTime(SYSTEM_TIME_MONOTONIC);
 
                if (isWakeup) {
                    mWakeupLatency = ((mWakeupLatency * 63+
                            (now - targetTime)) / 64;
                    if (mWakeupLatency > 500000) {
                        // Don't correct by more than 500 us
                        mWakeupLatency = 500000;
                    }
                    if (traceDetailedInfo) {
                        ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
                        ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
                    }
                }
 
                callbackInvocations = gatherCallbackInvocationsLocked(now);
            }
 
            if (callbackInvocations.size() > 0) {
                fireCallbackInvocations(callbackInvocations);
            }
        }
 
        return false;
    }
cs



/frameworks/native/services/surfaceflinger/DispSync.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
    Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
        Vector<CallbackInvocation> callbackInvocations;
        nsecs_t ref = now - mPeriod;
 
        for (size_t i = 0; i < mEventListeners.size(); i++) {
            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
                    ref);
 
            if (t < now) {
                CallbackInvocation ci;
                ci.mCallback = mEventListeners[i].mCallback;
                ci.mEventTime = t;
                callbackInvocations.push(ci);
                mEventListeners.editItemAt(i).mLastEventTime = t;
            }
        }
 
        return callbackInvocations;
    }
 
....
 
    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
        for (size_t i = 0; i < callbacks.size(); i++) {
            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
        }
    }
cs


300x250

Android에서 VSync 동작 원리 및 초기화 과정(2)

안드로이드/프레임워크 2015. 8. 30. 21:39

 지난 포스팅에서 Hardware에서 VSync를 담당하는 Thread가 생성되는 것을 확인하였습니다. 이번 포스팅에서는 지난 시간에 이어 VSync의 동작 원리를 좀 더 살펴보도록 하겠습니다.


/hardware/qcom/display/msm8960/libhwcomposer/hwc_vsync.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
static void *vsync_loop(void *param)
{
....
    do {
        if (LIKELY(!ctx->vstate.fakevsync)) {
            len = pread(fd_timestamp, vdata, MAX_DATA, 0);
            if (len < 0) {
                // If the read was just interrupted - it is not a fatal error
                // In either case, just continue.
                if (errno != EAGAIN &&
                    errno != EINTR  &&
                    errno != EBUSY) {
                    ALOGE ("FATAL:%s:not able to read file:%s, %s",
                           __FUNCTION__,
                           vsync_timestamp_fb0, strerror(errno));
                }
                continue;
            }
            // extract timestamp
            const char *str = vdata;
            if (!strncmp(str, "VSYNC=", strlen("VSYNC="))) {
                cur_timestamp = strtoull(str + strlen("VSYNC="), NULL, 0);
            }
        } else {
            usleep(16666);
            cur_timestamp = systemTime();
        }
        // send timestamp to HAL
        if(ctx->vstate.enable) {
            ALOGD_IF (logvsync, "%s: timestamp %llu sent to HWC for %s",
                      __FUNCTION__, cur_timestamp, "fb0");
            ctx->proc->vsync(ctx->proc, dpy, cur_timestamp);
        }
 
    } while (true);
    if(fd_timestamp >= 0)
        close (fd_timestamp);
 
    return NULL;
}
cs


 VSync를 담당하는 thread는 초당 60번 VSync 신호를 보냅니다. 물론 임의로 설정된 시간 이후에 VSync가 동작하는 경우도 있습니다. VSync를 담당하는 thread는 HWComposer 클래스에 VSync 함수를 호출하게 됩니다. 해당 함수는 앞서 Callback 함수를 등록할 당시 설정되어 있습니다.


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp

1
2
3
4
5
6
7
8
9
10
11
        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            mCBContext->procs.invalidate = &hook_invalidate;
            mCBContext->procs.vsync = &hook_vsync;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }
cs

Hardware에서 Framewokr를 호출하여 아래와 같이 함수가 실행됩니다.


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp

1
2
3
4
5
6
void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
        int64_t timestamp) {
    cb_context* ctx = reinterpret_cast<cb_context*>(
            const_cast<hwc_procs_t*>(procs));
    ctx->hwc->vsync(disp, timestamp);
}
cs


다음으로 vsync 함수를 보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.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
void HWComposer::vsync(int disp, int64_t timestamp) {
    if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
        {
            Mutex::Autolock _l(mLock);
 
            // There have been reports of HWCs that signal several vsync events
            // with the same timestamp when turning the display off and on. This
            // is a bug in the HWC implementation, but filter the extra events
            // out here so they don't cause havoc downstream.
            if (timestamp == mLastHwVSync[disp]) {
                ALOGW("Ignoring duplicate VSYNC event from HWC (t=%lld)",
                        timestamp);
                return;
            }
 
            mLastHwVSync[disp] = timestamp;
        }
 
        char tag[16];
        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
        ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
        mEventHandler.onVSyncReceived(disp, timestamp);
    }
}
cs


 여기서 mEventHandler는 SurfaceFlinger가 상속하고 있으며 실제로 SurfaceFlinger을 지칭합니다. mEventHandler에 의해 SurfaceFlinger가 실행됩니다.


/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
void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;
 
    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }
 
    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}
 
....
 
void SurfaceFlinger::enableHardwareVsync() {
    Mutex::Autolock _l(mHWVsyncLock);
    if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
        mPrimaryDispSync.beginResync();
        //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
        mEventControlThread->setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}
cs


 SurfaceFlinger가 mEventControlThread를 호출하고 있는 모습입니다. 이어서 소스코드를 보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/EventControlThread.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
void EventControlThread::setVsyncEnabled(bool enabled) {
    Mutex::Autolock lock(mMutex);
    mVsyncEnabled = enabled;
    mCond.signal();
}
 
bool EventControlThread::threadLoop() {
    Mutex::Autolock lock(mMutex);
 
    bool vsyncEnabled = mVsyncEnabled;
 
    mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC,
            mVsyncEnabled);
 
    while (true) {
        status_t err = mCond.wait(mMutex);
        if (err != NO_ERROR) {
            ALOGE("error waiting for new events: %s (%d)",
                strerror(-err), err);
            return false;
        }
 
        if (vsyncEnabled != mVsyncEnabled) {
            mFlinger->eventControl(HWC_DISPLAY_PRIMARY,
                    SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled);
            vsyncEnabled = mVsyncEnabled;
        }
    }
 
    return false;
}
cs

 여기서 thread를 통해 SurfaceFlinger의 eventControl() 함수가 호출됨을 보실 수 있습니다.


/frameworks/native/services/surfaceflinger/SurfaceFlinger.h

1
2
3
4
5
    /* ------------------------------------------------------------------------
     * H/W composer
     */
 
    HWComposer& getHwComposer() const { return *mHwc; }
cs


/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
1
2
3
4
5
6
void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
    ATRACE_CALL();
    getHwComposer().eventControl(disp, event, enabled);
}
 
 
cs


 이전에 새로 생성하였던 HWComposer 클래스의 eventControl() 함수가 호출된다.


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.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
void HWComposer::eventControl(int disp, int event, int enabled) {
    if (uint32_t(disp)>31 || !mAllocatedDisplayIDs.hasBit(disp)) {
        ALOGD("eventControl ignoring event %d on unallocated disp %d (en=%d)",
              event, disp, enabled);
        return;
    }
    if (event != EVENT_VSYNC) {
        ALOGW("eventControl got unexpected event %d (disp=%d en=%d)",
              event, disp, enabled);
        return;
    }
    status_t err = NO_ERROR;
    if (mHwc && !mDebugForceFakeVSync) {
        // NOTE: we use our own internal lock here because we have to call
        // into the HWC with the lock held, and we want to make sure
        // that even if HWC blocks (which it shouldn't), it won't
        // affect other threads.
        Mutex::Autolock _l(mEventControlLock);
        const int32_t eventBit = 1UL << event;
        const int32_t newValue = enabled ? eventBit : 0;
        const int32_t oldValue = mDisplayData[disp].events & eventBit;
        if (newValue != oldValue) {
            ATRACE_CALL();
            err = mHwc->eventControl(mHwc, disp, event, enabled);
            if (!err) {
                int32_t& events(mDisplayData[disp].events);
                events = (events & ~eventBit) | newValue;
            }
        }
        // error here should not happen -- not sure what we should
        // do if it does.
        ALOGE_IF(err, "eventControl(%d, %d) failed %s",
                event, enabled, strerror(-err));
    }
 
    if (err == NO_ERROR && mVSyncThread != NULL) {
        mVSyncThread->setEnabled(enabled);
    }
}
cs

여기서 다시 hardware library 단계에서 함수가 호출되고 있는 것을 보실 수 있습니다.


/hardware/qcom/display/msm8960/libhwcomposer/hwc.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
static int hwc_eventControl(struct hwc_composer_device_1* dev, int dpy,
                             int event, int enable)
{
    int ret = 0;
    hwc_context_t* ctx = (hwc_context_t*)(dev);
    if(!ctx->dpyAttr[dpy].isActive) {
        ALOGE("Display is blanked - Cannot %s vsync",
              enable ? "enable" : "disable");
        return -EINVAL;
    }
 
    switch(event) {
        case HWC_EVENT_VSYNC:
            if (ctx->vstate.enable == enable)
                break;
            ret = hwc_vsync_control(ctx, dpy, enable);
            if(ret == 0)
                ctx->vstate.enable = !!enable;
            ALOGD_IF (VSYNC_DEBUG, "VSYNC state changed to %s",
                      (enable)?"ENABLED":"DISABLED");
            break;
        default:
            ret = -EINVAL;
    }
    return ret;
}
cs


/hardware/qcom/display/msm8960/libhwcomposer/hwc_vsync.cpp

1
2
3
4
5
6
7
8
9
10
11
12
int hwc_vsync_control(hwc_context_t* ctx, int dpy, int enable)
{
    int ret = 0;
    if(!ctx->vstate.fakevsync &&
       ioctl(ctx->dpyAttr[dpy].fd, MSMFB_OVERLAY_VSYNC_CTRL,
             &enable) < 0) {
        ALOGE("%s: vsync control failed. Dpy=%d, enable=%d : %s",
              __FUNCTION__, dpy, enable, strerror(errno));
        ret = -errno;
    }
    return ret;
}
cs



출저 : http://blog.csdn.net/fuyajun01/article/details/41411715

300x250

Android에서 VSync 동작 원리 및 초기화 과정(1)

안드로이드/프레임워크 2015. 8. 29. 18:51


 안드로이드 기반 디바이스에서 화면의 출력되는 정보가 변경될 경우 이를 지속적으로 갱신해 줄 필요가 있습니다. 안드로이드에서 이 기능을 수행하는 것으로 VSync가 있습니다. VSync란 안드로이드 기기에 표출되는 화면의 정보가 변경되었을 때 이를 호출하는 신호로 이는 Choreographer 클래스에서 정의하고 있습니다. 자세한 설명은 아래 그림을 통해 해 보도록 하겠습니다.


 VSync는 주기적으로 신호를 발생하여 디스플레이의 화면을 갱신시키는 함수를 호출합니다. 하드웨어의 설정대로 VSync의 발생 주기는 60Hz입니다. VSync는 HWComposer에서 설정되며 설정 과정을 소스코드를 통해 분석해 보도록 합니다. HWComposer는 SurfaceFlinger가 초기화될 때 동시에 진행됩니다. SurfaceFlinger가 생성되는 과정에 대해서는 아래 링크를 참조해 주시기 바랍니다.


http://elecs.tistory.com/131


/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
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
....
 
void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");
 
    status_t err;
    Mutex::Autolock _l(mStateLock);
 
....
 
    // 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");
    }
 
....
 
    // 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


 위의 소스코드에서 보시는 바와 같이 HWComposer가 새롭게 생성되고 있는 것을 확인하실 수 있습니다. 이번에는 HWCompose가 생성되는 과정을 보도록 하겠습니다.


/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
class HWComposer
{
....
    sp<SurfaceFlinger>              mFlinger;
    framebuffer_device_t*           mFbDev;
    struct hwc_composer_device_1*   mHwc;
    // invariant: mLists[0] != NULL iff mHwc != NULL
    // mLists[i>0] can be NULL. that display is to be ignored
    struct hwc_display_contents_1*  mLists[MAX_HWC_DISPLAYS];
    DisplayData                     mDisplayData[MAX_HWC_DISPLAYS];
    size_t                          mNumDisplays;
 
    cb_context*                     mCBContext;
    EventHandler&                   mEventHandler;
    size_t                          mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES];
    sp<VSyncThread>                 mVSyncThread;
    bool                            mDebugForceFakeVSync;
    BitSet32                        mAllocatedDisplayIDs;
....
}
cs


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.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
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
....
 
HWComposer::HWComposer(
        const sp<SurfaceFlinger>& flinger,
        EventHandler& handler)
    : mFlinger(flinger),
      mFbDev(0), mHwc(0), mNumDisplays(1),
      mCBContext(new cb_context),
      mEventHandler(handler),
      mDebugForceFakeVSync(false)
{
 
....
 
    // Note: some devices may insist that the FB HAL be opened before HWC.
    int fberr = loadFbHalModule();
    loadHwcModule();
 
    if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
        // close FB HAL if we don't needed it.
        // FIXME: this is temporary until we're not forced to open FB HAL
        // before HWC.
        framebuffer_close(mFbDev);
        mFbDev = NULL;
    }
 
    // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory.
    if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
            && !mFbDev) {
        ALOGE("ERROR: failed to open framebuffer (%s), aborting",
                strerror(-fberr));
        abort();
    }
 
    // these display IDs are always reserved
    for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
        mAllocatedDisplayIDs.markBit(i);
    }
 
    if (mHwc) {
        ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
              (hwcApiVersion(mHwc) >> 24) & 0xff,
              (hwcApiVersion(mHwc) >> 16) & 0xff);
        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            mCBContext->procs.invalidate = &hook_invalidate;
            mCBContext->procs.vsync = &hook_vsync;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }
 
        // don't need a vsync thread if we have a hardware composer
        needVSyncThread = false;
        // always turn vsync off when we start
        eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
 
        // the number of displays we actually have depends on the
        // hw composer version
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
            // 1.3 adds support for virtual displays
            mNumDisplays = MAX_HWC_DISPLAYS;
        } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // 1.1 adds support for multiple displays
            mNumDisplays = NUM_BUILTIN_DISPLAYS;
        } else {
            mNumDisplays = 1;
        }
    }
 
    if (mFbDev) {
        ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
                "should only have fbdev if no hwc or hwc is 1.0");
 
        DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
        disp.connected = true;
        disp.width = mFbDev->width;
        disp.height = mFbDev->height;
        disp.format = mFbDev->format;
        disp.xdpi = mFbDev->xdpi;
        disp.ydpi = mFbDev->ydpi;
        if (disp.refresh == 0) {
            disp.refresh = nsecs_t(1e9 / mFbDev->fps);
            ALOGW("getting VSYNC period from fb HAL: %lld", disp.refresh);
        }
        if (disp.refresh == 0) {
            disp.refresh = nsecs_t(1e9 / 60.0);
            ALOGW("getting VSYNC period from thin air: %lld",
                    mDisplayData[HWC_DISPLAY_PRIMARY].refresh);
        }
    } else if (mHwc) {
        // here we're guaranteed to have at least HWC 1.1
        for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
            queryDisplayProperties(i);
        }
    }
 
    if (needVSyncThread) {
        // we don't have VSYNC support, we need to fake it
        mVSyncThread = new VSyncThread(*this);
    }
}
 
....
cs

 HWComposer가 생성될 때 실행되는 loadHwcModule() 함수를 살펴보도록 합니다. 먼저 해당 함수를 이해하기 위해 해당 함수에서 사용하는 헤더파일의 내용 중 일부를 살펴보겠습니다.


/hardware/libhardware/include/hardware/hardware.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
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
....
 
/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;
 
    /**
     * The API version of the implemented module. The module owner is
     * responsible for updating the version when a module interface has
     * changed.
     *
     * The derived modules such as gralloc and audio own and manage this field.
     * The module user must interpret the version field to decide whether or
     * not to inter-operate with the supplied module implementation.
     * For example, SurfaceFlinger is responsible for making sure that
     * it knows how to manage different versions of the gralloc-module API,
     * and AudioFlinger must know how to do the same for audio-module API.
     *
     * The module API version should include a major and a minor component.
     * For example, version 1.0 could be represented as 0x0100. This format
     * implies that versions 0x0100-0x01ff are all API-compatible.
     *
     * In the future, libhardware will expose a hw_get_module_version()
     * (or equivalent) function that will take minimum/maximum supported
     * versions as arguments and would be able to reject modules with
     * versions outside of the supplied range.
     */
    uint16_t module_api_version;
#define version_major module_api_version
    /**
     * version_major/version_minor defines are supplied here for temporary
     * source code compatibility. They will be removed in the next version.
     * ALL clients must convert to the new version format.
     */
 
    /**
     * The API version of the HAL module interface. This is meant to
     * version the hw_module_t, hw_module_methods_t, and hw_device_t
     * structures and definitions.
     *
     * The HAL interface owns this field. Module users/implementations
     * must NOT rely on this value for version information.
     *
     * Presently, 0 is the only valid value.
     */
    uint16_t hal_api_version;
#define version_minor hal_api_version
 
    /** Identifier of module */
    const char *id;
 
    /** Name of this module */
    const char *name;
 
    /** Author/owner/implementor of the module */
    const char *author;
 
    /** Modules methods */
    struct hw_module_methods_t* methods;
 
    /** module's dso */
    void* dso;
 
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
 
} hw_module_t;
 
 
typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);
 
hw_module_methods_t;
 
/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 */
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;
 
    /**
     * Version of the module-specific device API. This value is used by
     * the derived-module user to manage different device implementations.
     *
     * The module user is responsible for checking the module_api_version
     * and device version fields to ensure that the user is capable of
     * communicating with the specific module implementation.
     *
     * One module can support multiple devices with different versions. This
     * can be useful when a device interface changes in an incompatible way
     * but it is still necessary to support older implementations at the same
     * time. One such example is the Camera 2.0 API.
     *
     * This field is interpreted by the module user and is ignored by the
     * HAL interface itself.
     */
    uint32_t version;
 
    /** reference to the module this device belongs to */
    struct hw_module_t* module;
 
    /** padding reserved for future use */
    uint32_t reserved[12];
 
    /** Close this device */
    int (*close)(struct hw_device_t* device);
 
} hw_device_t;
 
....
cs


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.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
....
 
// Load and prepare the hardware composer module.  Sets mHwc.
void HWComposer::loadHwcModule()
{
    hw_module_t const* module;
    //Hardware로부터 module을 받아옵니다.
    if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
        ALOGE("%s module not found", HWC_HARDWARE_MODULE_ID);
        return;
    }
    //위의 과정으로 부터 얻어온 module을 통해 HWComposer에 적용합니다.
    int err = hwc_open_1(module, &mHwc);
    if (err) {
        ALOGE("%s device failed to initialize (%s)",
              HWC_HARDWARE_COMPOSER, strerror(-err));
        return;
    }
    if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_0) ||
            hwcHeaderVersion(mHwc) < MIN_HWC_HEADER_VERSION ||
            hwcHeaderVersion(mHwc) > HWC_HEADER_VERSION) {
        ALOGE("%s device version %#x unsupported, will not be used",
              HWC_HARDWARE_COMPOSER, mHwc->common.version);
        hwc_close_1(mHwc);
        mHwc = NULL;
        return;
    }
}
 
....
cs

 hw_get_module을 통해 하드웨어의 모듈 정보를 받아옵니다. hw_get_module의 함수는 아래와 같이 구성되어 있습니다.


/hardware/libhardware/hardware.c

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
....
 
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int status;
    int i;
    const struct hw_module_t *hmi = NULL;
    char prop[PATH_MAX];
    char path[PATH_MAX];
    char name[PATH_MAX];
 
    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);
 
    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */
 
    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
        if (i < HAL_VARIANT_KEYS_COUNT) {
            if (property_get(variant_keys[i], prop, NULL) == 0) {
                continue;
            }
            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH2, name, prop);
            if (access(path, R_OK) == 0break;
 
            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH1, name, prop);
            if (access(path, R_OK) == 0break;
        } else {
            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH2, name);
            if (access(path, R_OK) == 0break;
 
            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH1, name);
            if (access(path, R_OK) == 0break;
        }
    }
 
    status = -ENOENT;
    if (i < HAL_VARIANT_KEYS_COUNT+1) {
        /* load the module, if this fails, we're doomed, and we should not try
         * to load a different variant. */
        status = load(class_id, path, module);
    }
 
    return status;
}
 
int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}
cs


 hw_get_modlue() 함수는 이어서 hw_get_module_by_class()함수를 호출하게 되며 load()함수가 호출되면서 module 변수의 값을 설정하게 됩니다.


/hardware/libhardware/hardware.c
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
/**
 * Load the file defined by the variant and if successful
 * return the dlopen handle and the hmi.
 * @return 0 = success, !0 = failure.
 */
static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status;
    void *handle;
    struct hw_module_t *hmi;
 
    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }
 
    /* Get the address of the struct hal_module_info. */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }
 
    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }
 
    hmi->dso = handle;
 
    /* success */
    status = 0;
 
    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }
 
    *pHmi = hmi;
 
    return status;
}
cs


위 과정을 통해 pHmi가 설정되는 것을 보실 수 있습니다.이 때 dlsym()함수는 동적 적제 라이브러리로 해당 기기의 Library와 연결되게 됩니다.


 다음으로 hwc_open_1() 함수와 hwc_close_1() 함수를 살펴보도록 하겠습니다.


/hardware/libhardware/include/hardware/hwcomposer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
....
/** convenience API for opening and closing a device */
 
static inline int hwc_open_1(const struct hw_module_t* module,
        hwc_composer_device_1_t** device) {
    return module->methods->open(module,
            HWC_HARDWARE_COMPOSER, (struct hw_device_t**)device);
}
 
static inline int hwc_close_1(hwc_composer_device_1_t* device) {
    return device->common.close(&device->common);
}
....
cs


 hwc_open_1() 함수는 위의 과정에서 동적 라이브러리와 연결되어 있는 method 내의 open()함수를 통해 hwc_device_open()함수를 호출하게 됩니다.


/hardware/qcom/display/msm8960/libhwcomposer/hwc.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
static int hwc_device_open(const struct hw_module_t* module, const char* name,
                           struct hw_device_t** device)
{
    int status = -EINVAL;
 
    if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
        struct hwc_context_t *dev;
        dev = (hwc_context_t*)malloc(sizeof(*dev));
        memset(dev, 0sizeof(*dev));
 
        //Initialize hwc context
        initContext(dev);
 
        //Setup HWC methods
        dev->device.common.tag          = HARDWARE_DEVICE_TAG;
        dev->device.common.version      = HWC_DEVICE_API_VERSION_1_2;
        dev->device.common.module       = const_cast<hw_module_t*>(module);
        dev->device.common.close        = hwc_device_close;
        dev->device.prepare             = hwc_prepare;
        dev->device.set                 = hwc_set;
        dev->device.eventControl        = hwc_eventControl;
        dev->device.blank               = hwc_blank;
        dev->device.query               = hwc_query;
        dev->device.registerProcs       = hwc_registerProcs;
        dev->device.dump                = hwc_dump;
        dev->device.getDisplayConfigs   = hwc_getDisplayConfigs;
        dev->device.getDisplayAttributes = hwc_getDisplayAttributes;
        *device = &dev->device.common;
        status = 0;
    }
    return status;
}
cs


 위의 과정을 mhwc이 모두 정의되면 되면 다음으로 callback으로  각 함수들이 적용됩니다.


/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.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
....
 
struct HWComposer::cb_context {
    struct callbacks : public hwc_procs_t {
        // these are here to facilitate the transition when adding
        // new callbacks (an implementation can check for NULL before
        // calling a new callback).
        void (*zero[4])(void);
    };
    callbacks procs;
    HWComposer* hwc;
};
 
....
 
if (mHwc) {
        ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
              (hwcApiVersion(mHwc) >> 24) & 0xff,
              (hwcApiVersion(mHwc) >> 16) & 0xff);
        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            mCBContext->procs.invalidate = &hook_invalidate;
            mCBContext->procs.vsync = &hook_vsync;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }
 
        // don't need a vsync thread if we have a hardware composer
        needVSyncThread = false;
        // always turn vsync off when we start
        eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
 
        // the number of displays we actually have depends on the
        // hw composer version
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
            // 1.3 adds support for virtual displays
            mNumDisplays = MAX_HWC_DISPLAYS;
        } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // 1.1 adds support for multiple displays
            mNumDisplays = NUM_BUILTIN_DISPLAYS;
        } else {
            mNumDisplays = 1;
        }
    }
 
....
cs

 위에서 보시는 바와 같이 Callback 함수로 vsync 함수가 등록되어 지는 것을 확인하실 수 있습니다. 설정이 완료된 후 registerProcs()함수가 실행되여 Callback 함수를 등록하게 됩니다. 이는 이전에 hwc_device_open()함수에서 등록되어 있는 것이 실행됩니다.


/hardware/qcom/display/msm8960/libhwcomposer/hwc.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Save callback functions registered to HWC
 */
static void hwc_registerProcs(struct hwc_composer_device_1* dev,
                              hwc_procs_t const* procs)
{
    ALOGI("%s", __FUNCTION__);
    hwc_context_t* ctx = (hwc_context_t*)(dev);
    if(!ctx) {
        ALOGE("%s: Invalid context", __FUNCTION__);
        return;
    }
    ctx->proc = procs;
 
    // Now that we have the functions needed, kick off
    // the uevent & vsync threads
    init_uevent_thread(ctx);
    init_vsync_thread(ctx);
}
cs


 위 hwc_registerProcs() 함수가 호출되면서 init_vsync_thread()를 호출함으로서 vsync thread가 만들어집니다.


/hardware/qcom/display/msm8960/libhwcomposer/hwc_vsync.cpp

1
2
3
4
5
6
7
8
9
10
11
void init_vsync_thread(hwc_context_t* ctx)
{
    int ret;
    pthread_t vsync_thread;
    ALOGI("Initializing VSYNC Thread");
    ret = pthread_create(&vsync_thread, NULL, vsync_loop, (void*) ctx);
    if (ret) {
        ALOGE("%s: failed to create %s: %s", __FUNCTION__,
              HWC_VSYNC_THREAD_NAME, strerror(ret));
    }
}
cs


 위의 과정을 통해 VSync를 담당하는 thread가 생성되었음을 확인하였습니다. 여기서 VSync Thread의 소스코드를 확인해 보는 것으로 이번 포스팅을 마치도록 하겠습니다. 다음 포스팅에서 계속 이어서 소스코드 설명을 진행하도록 하겠습니다.



/hardware/qcom/display/msm8960/libhwcomposer/hwc_vsync.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
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
static void *vsync_loop(void *param)
{
    const char* vsync_timestamp_fb0 = "/sys/class/graphics/fb0/vsync_event";
    const char* vsync_timestamp_fb1 = "/sys/class/graphics/fb1/vsync_event";
    int dpy = HWC_DISPLAY_PRIMARY;
 
    hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);
 
    char thread_name[64= HWC_VSYNC_THREAD_NAME;
    prctl(PR_SET_NAME, (unsigned long) &thread_name, 000);
    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY +
                android::PRIORITY_MORE_FAVORABLE);
 
    const int MAX_DATA = 64;
    static char vdata[MAX_DATA];
 
    uint64_t cur_timestamp=0;
    ssize_t len = -1;
    int fd_timestamp = -1;
    int ret = 0;
    bool fb1_vsync = false;
    bool logvsync = false;
 
    char property[PROPERTY_VALUE_MAX];
    if(property_get("debug.hwc.fakevsync", property, NULL) > 0) {
        if(atoi(property) == 1)
            ctx->vstate.fakevsync = true;
    }
 
    if(property_get("debug.hwc.logvsync", property, 0> 0) {
        if(atoi(property) == 1)
            logvsync = true;
    }
 
    /* Currently read vsync timestamp from drivers
       e.g. VSYNC=41800875994
       */
    fd_timestamp = open(vsync_timestamp_fb0, O_RDONLY);
    if (fd_timestamp < 0) {
        // Make sure fb device is opened before starting this thread so this
        // never happens.
        ALOGE ("FATAL:%s:not able to open file:%s, %s",  __FUNCTION__,
               (fb1_vsync) ? vsync_timestamp_fb1 : vsync_timestamp_fb0,
               strerror(errno));
        ctx->vstate.fakevsync = true;
    }
 
    do {
        if (LIKELY(!ctx->vstate.fakevsync)) {
            len = pread(fd_timestamp, vdata, MAX_DATA, 0);
            if (len < 0) {
                // If the read was just interrupted - it is not a fatal error
                // In either case, just continue.
                if (errno != EAGAIN &&
                    errno != EINTR  &&
                    errno != EBUSY) {
                    ALOGE ("FATAL:%s:not able to read file:%s, %s",
                           __FUNCTION__,
                           vsync_timestamp_fb0, strerror(errno));
                }
                continue;
            }
            // extract timestamp
            const char *str = vdata;
            if (!strncmp(str, "VSYNC=", strlen("VSYNC="))) {
                cur_timestamp = strtoull(str + strlen("VSYNC="), NULL, 0);
            }
        } else {
            usleep(16666);
            cur_timestamp = systemTime();
        }
        // send timestamp to HAL
        if(ctx->vstate.enable) {
            ALOGD_IF (logvsync, "%s: timestamp %llu sent to HWC for %s",
                      __FUNCTION__, cur_timestamp, "fb0");
            ctx->proc->vsync(ctx->proc, dpy, cur_timestamp);
        }
 
    } while (true);
    if(fd_timestamp >= 0)
        close (fd_timestamp);
 
    return NULL;
}
cs


다음 포스팅에서 내용을 이어서 진행하도록 하겠습니다.


http://elecs.tistory.com/133

300x250

안드로이드 프레임워크 프로그래밍(22) [SurfaceFlinger(시스템 서비스) 등록 및 초기화 과정]

안드로이드/프레임워크 2015. 8. 28. 02:37

※ 본 포스팅은 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을 초기화 시킵니다. 소스코드는 아래와 같습니다.


/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
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





300x250

안드로이드 프레임워크 프로그래밍(21) [System Service란?]

안드로이드/프레임워크 2015. 8. 23. 18:36

 지금까지 블로그를 통해 안드로이드 프레임워크에 대해 꾸준이 다루어 왔습니다. 그러다가 문득 이런 생각을 해보게 되었습니다.


"지금까지 프레임워크에 대한 이론을 정확히 다루었던 포스팅이 있었던가?"


 프로그래밍을 공부하면서 문제의 솔루션을 찾는 것만 생각을 하다보니 막상 솔루션을 설명하는 것에 대한 공부를 소흘히 해왔던 듯한 생각이 들게 되었습니다. 지금까지 개념들에 대해 간단한 설명만 하고 소스코드를 확인하고 넘어가는 과정만 보였지 실질적으로 해당 개념에 대해 심도있는 고찰을 하는 경우가 드물었음을 알게 되었습니다. 비록 지금 시점에서 포스팅하기엔 다소 늦은 감이 있습니다만 이후에 안드로이드 프레임워크에 대해 연구하실 분들을 위해 조금씩 생각나는 중요한 내용들을 정리해볼까 합니다. 그 첫번째로 안드로이드 프레임워크의 가장 기초적인 상식이라 할 수 있는 System Service에 대해 알아보고자 합니다.


Android System Service

 System Srevice란 안드로이드에서 제공하는 Framework API와 Hardware 사이를 연결해주는 기능을 담당하는 서비스입니다. System Service는 아래 빨간 네모로 강조한 부분을 일컫는 말입니다.


출저 : Android Open Source Project


  실제로 System Service의 일부 기능들은 안드로이드 Application과 같이 순수 Java로 구성되어 프로그램을 관리하는 기능을 하는가 하면 Camera와 같이 하드웨어를 사용하여 JNI를 통해 C++혹은 C와 상호작용 할 수 있도록 설계되어 있는 경우가 있습니다. 안드로이드 Application과는 달리 System Service는 Application이 종료된 후에도 계속 동작하여 이후 다른 Application이 실행되었을 때에도 계속 동작할 수 있도록 구성되어 있습니다.


 System Service는 위의 그림에서 보시는 바와 같이 특정한 기능에 집중되어 있습니다. 이러한 System Service의 기능은 Media Server와 System Server로 나누어서 볼 수 있습니다. 전체적인 기능을 보았을 때 Media Server는 Hardware Abstraction Library(HAL)에 의존적이다 보니 C와 C++과 같은 Native Library를 좀 더 적극적으로 사용하고 있다고 보셔도 될 듯 합니다.


 System Service는 수십개의 기능들로 구성되어 있으며 애플리케이션 프로그래밍을 할 때 필요로 하는 기능들을 제공합니다. 각 서비스들은 Binder를 통해 통신을 수행합니다. Application 또한 Binder를 통해 기능들이 초기화 됩니다. Binder에 대한 자세한 내용은 아래 링크를 참조해 주시기 바랍니다.


http://d2.naver.com/helloworld/47656


 


300x250

SurfaceView가 다른 View 클래스와의 차이(updateWindow() method in SurfaceView class)

안드로이드/프레임워크 2015. 8. 17. 20:13

 안드로이드 UI를 설계할 때 주로 사용되는 View의 경우 화면에 한 번 표출되면 특별한 이펙트를 적용하지 않는 한 Activity에서 생성된 모습 그대로의 모습을 유지하는 경우가 대부분입니다.

 반면 SurfaceView의 경우 화면의 변화가 다른 View들에 비해 거의 실시간으로 화면에 변화를 주어야 하는 특성을 가지고 있기 때문에 이를 실시간으로 적용시킬 수 있는 기능이 필요로 하는데 그 중 하나가 updateWindow() 함수입니다.


 아래 간단한 예제를 통해 알아보도록 하겠습니다.


1
surfaceView.setVisibility(View.VISIBLE);
cs


 Application 단계에서 SurfaceView를 화면상에서 보이도록 설정하게 되는 순간 SurfaceView는 화면에 비추어지기 시작합니다.


/frameworks/base/core/java/android/view/SurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        mViewVisibility = visibility == VISIBLE;
        boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
        if (newRequestedVisible != mRequestedVisible) {
            // our base class (View) invalidates the layout only when
            // we go from/to the GONE state. However, SurfaceView needs
            // to request a re-layout when the visibility changes at all.
            // This is needed because the transparent region is computed
            // as part of the layout phase, and it changes (obviously) when
            // the visibility changes.
            requestLayout();
        }
        mRequestedVisible = newRequestedVisible;
        updateWindow(falsefalse);
    }
cs


 보시는 바와 같이 SurfaceView를 화면에 비추도록 설정하였을 때 함수 내에 updateWindow() 함수가 호출되고 있는 것을 보실 수 있습니다. 해당 함수는 SurfaceHolder에서 설정한 기능들을 수행하는 데에 사용되며 SurfaceHolder에 대한 자세한 사항은 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/78


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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    private void updateWindow(boolean force, boolean redrawNeeded) {
 
        if (!mHaveFrame) {
            return;
        }
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null) {
            mTranslator = viewRoot.mTranslator;
        }
 
        if (mTranslator != null) {
            mSurface.setCompatibilityTranslator(mTranslator);
        }
 
        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
 
        getLocationInWindow(mLocation);
        final boolean creating = mWindow == null;
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
        final boolean visibleChanged = mVisible != mRequestedVisible;
 
        if (force || creating || formatChanged || sizeChanged || visibleChanged
            || mLeft != mLocation[0|| mTop != mLocation[1]
            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
 
            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mLeft != mLocation[0])
                    + " top=" + (mTop != mLocation[1]));
 
            try {
                final boolean visible = mVisible = mRequestedVisible;
                mLeft = mLocation[0];
                mTop = mLocation[1];
                mWidth = myWidth;
                mHeight = myHeight;
                mFormat = mRequestedFormat;
 
                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
 
                // Places the window relative
                mLayout.x = mLeft;
                mLayout.y = mTop;
                mLayout.width = getWidth();
                mLayout.height = getHeight();
                if (mTranslator != null) {
                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
                }
 
                mLayout.format = mRequestedFormat;
                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                              | WindowManager.LayoutParams.FLAG_SCALED
                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                              ;
                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
                    mLayout.privateFlags |=
                            WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
                }
                mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
                if (mWindow == null) {
                    Display display = getDisplay();
                    mWindow = new MyWindow(this);
                    mLayout.type = mWindowType;
                    mLayout.gravity = Gravity.START|Gravity.TOP;
                    mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
                            mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets);
                }
 
                boolean realSizeChanged;
                boolean reportDrawNeeded;
 
                int relayoutResult;
 
                mSurfaceLock.lock();
                try {
                    mUpdateWindowNeeded = false;
                    reportDrawNeeded = mReportDrawNeeded;
                    mReportDrawNeeded = false;
                    mDrawingStopped = !visible;
 
                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
 
                    relayoutResult = mSession.relayout(
                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                            visible ? VISIBLE : GONE,
                            WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                            mWinFrame, mOverscanInsets, mContentInsets,
                            mVisibleInsets, mConfiguration, mNewSurface);
                    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                        mReportDrawNeeded = true;
                    }
 
                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
                            + ", vis=" + visible + ", frame=" + mWinFrame);
 
                    mSurfaceFrame.left = 0;
                    mSurfaceFrame.top = 0;
                    if (mTranslator == null) {
                        mSurfaceFrame.right = mWinFrame.width();
                        mSurfaceFrame.bottom = mWinFrame.height();
                    } else {
                        float appInvertedScale = mTranslator.applicationInvertedScale;
                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
                    }
 
                    final int surfaceWidth = mSurfaceFrame.right;
                    final int surfaceHeight = mSurfaceFrame.bottom;
                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
                            || mLastSurfaceHeight != surfaceHeight;
                    mLastSurfaceWidth = surfaceWidth;
                    mLastSurfaceHeight = surfaceHeight;
                } finally {
                    mSurfaceLock.unlock();
                }
 
                try {
                    redrawNeeded |= creating | reportDrawNeeded;
 
                    SurfaceHolder.Callback callbacks[] = null;
 
                    final boolean surfaceChanged = (relayoutResult
                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                        mSurfaceCreated = false;
                        if (mSurface.isValid()) {
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
                            callbacks = getSurfaceCallbacks();
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceDestroyed(mSurfaceHolder);
                            }
                        }
                    }
 
                    mSurface.transferFrom(mNewSurface);
 
                    if (visible && mSurface.isValid()) {
                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                            mSurfaceCreated = true;
                            mIsCreating = true;
                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceCreated(mSurfaceHolder);
                            }
                        }
                        if (creating || formatChanged || sizeChanged
                                || visibleChanged || realSizeChanged) {
                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
                                    + " w=" + myWidth + " h=" + myHeight);
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                            }
                        }
                        if (redrawNeeded) {
                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
                            if (callbacks == null) {
                                callbacks = getSurfaceCallbacks();
                            }
                            for (SurfaceHolder.Callback c : callbacks) {
                                if (c instanceof SurfaceHolder.Callback2) {
                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                            mSurfaceHolder);
                                }
                            }
                        }
                    }
                } finally {
                    mIsCreating = false;
                    if (redrawNeeded) {
                        if (DEBUG) Log.i(TAG, "finishedDrawing");
                        mSession.finishDrawing(mWindow);
                    }
                    mSession.performDeferredDestroy(mWindow);
                }
            } catch (RemoteException ex) {
            }
            if (DEBUG) Log.v(
                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                " w=" + mLayout.width + " h=" + mLayout.height +
                ", frame=" + mSurfaceFrame);
        }
    }
cs


300x250

[Android Developers] SurfaceHolder

안드로이드/프레임워크 2015. 8. 13. 00:38

  SurfaceHolder란 말 그대로 하나의 display surface를 잡고 있는 추상 Interface를 말합니다.  SurfaceHolder는 개발자가 직접 surface의 사이즈나 형식을 조절할 수 있고 surface의 pixel을 수정할 수 있습니다. 그리고 surface의 변화를 실시간으로 확인할 수 있습니다. SurfaceHolder는 왠만해서는 SurfaceView 클래스를 통해 사용할 수 있습니다.


 Surface의 상태 변경에 대한 정보를 다루기 위해서는 SurfaceHolder.Callback 인터페이스를 사용합니다. SurfaceView에서 사용될 때 SurfaceHolder.Callback 내에 있는 surfaceCreated(), surfaceDistroyed() 함수를 설정하는 것으로 Surface를 통제할 수 있게 합니다. SurfaceHolder.Callback는 SurfaceHolder.addCallback() 함수를 통해 등록할 수 있습니다.


SurfaceHolder가 SurfaceView에 적용되는 과정은 아래 포스팅을 참조해 주시길 바랍니다.

http://elecs.tistory.com/78


출저 : http://developer.android.com/reference/android/view/SurfaceHolder.html

300x250

안드로이드 Framework 단계에서 Surface 생성과정

안드로이드/프레임워크 2015. 4. 30. 19:12

 안드로이드 기기에 있어 가장 중요한 부분이라 할 수 있는 것 중 하나가 바로 기기의 화면에 나오는 View들을 제대로 처리하는 것이라 생각합니다. 특히 역동적인 안드로이드 화면을 출력하기 위해서는 SurfaceView를 사용하게 됩니다. 이번 포스팅에서는 안드로이드 Framework 단계에서 Surface가 생성돠는 과정에 대해 살펴보도록 하겠습니다.


※본 예제는 Android의 Camera 동작 과정을 토대로 Surface가 동작하는 과정에 대해 다루었습니다.


/frameworks/base/core/java/android/hardware/Camera.java

1
2
3
4
5
6
7
    public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
        if (holder != null) {
            setPreviewDisplay(holder.getSurface());
        } else {
            setPreviewDisplay((Surface)null);
        }
    }
cs


 안드로이드의 카메라 Preview를 설정하는 과정에서 setPreviewDisplay() 함수를 통해 SurfaceView 클래스가 적용되고 있는 상황을 나타내고 있습니다. holder 내의 SurfaceView 클래스는 다음과 같습니다.


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
public class SurfaceView extends View {
    static private final String TAG = "SurfaceView";
    static private final boolean DEBUG = false;
 
    final ArrayList<SurfaceHolder.Callback> mCallbacks
            = new ArrayList<SurfaceHolder.Callback>();
 
    final int[] mLocation = new int[2];
 
    final ReentrantLock mSurfaceLock = new ReentrantLock();
    final Surface mSurface = new Surface();       // Current surface in use
    final Surface mNewSurface = new Surface();    // New surface we are switching to
....
    /**
     * Return the SurfaceHolder providing access and control over this
     * SurfaceView's underlying surface.
     *
     * @return SurfaceHolder The holder of the surface.
     */
    public SurfaceHolder getHolder() {
        return mSurfaceHolder;
    }
....
 
    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
 
        private static final String LOG_TAG = "SurfaceHolder";
....
        @Override
        public Surface getSurface() {
            return mSurface;
        }
 
        @Override
        public Rect getSurfaceFrame() {
            return mSurfaceFrame;
        }
    };
}
cs


 위의 소스코드를 통해 holder가 mSurface를 return 하고 있고 mSurface는 new Surface()를 통해 클래스를 생성하고 있는 것을 확인하실 수 있습니다. 그렇다면 이번에는 Surface 클래스가 생성되는 과정을 살펴보도록 합시다.


/frameworks/base/core/java/android/view/Surface.java

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
public class Surface implements Parcelable {
    private static final String TAG = "Surface";
....
    // Guarded state.
    final Object mLock = new Object(); // protects the native state
    private String mName;
    int mNativeObject; // package scope only for SurfaceControl access
....
    /**
     * Create an empty surface, which will later be filled in by readFromParcel().
     * @hide
     */
    public Surface() {
    }
....
    public void readFromParcel(Parcel source) {
        if (source == null) {
            throw new IllegalArgumentException("source must not be null");
        }
 
        synchronized (mLock) {
            // nativeReadFromParcel() will either return mNativeObject, or
            // create a new native Surface and return it after reducing
            // the reference count on mNativeObject.  Either way, it is
            // not necessary to call nativeRelease() here.
            mName = source.readString();
            setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
        }
    }
....
    private void setNativeObjectLocked(int ptr) {
        if (mNativeObject != ptr) {
            if (mNativeObject == 0 && ptr != 0) {
                mCloseGuard.open("release");
            } else if (mNativeObject != 0 && ptr == 0) {
                mCloseGuard.close();
            }
            mNativeObject = ptr;
            mGenerationId += 1;
        }
    }
....
}
 
cs


 맨 처음에 surface가 생성될 때에는 빈 Class 하나가 생성됩니다. 이후 readFromParcel()을 통하여 Surface 클래스 내에 mNativeObject의 값이 채워지게 되며 nativeReadFromParcel() 함수는 다음과 같다.


/frameworks/base/core/jni/android_view_Surface.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
static jint nativeReadFromParcel(JNIEnv* env, jclass clazz,
        jint nativeObject, jobject parcelObj) {
    Parcel* parcel = parcelForJavaObject(env, parcelObj);
    if (parcel == NULL) {
        doThrowNPE(env);
        return 0;
    }
 
    sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
    sp<IBinder> binder(parcel->readStrongBinder());
 
    // update the Surface only if the underlying IGraphicBufferProducer
    // has changed.
    if (self != NULL
            && (self->getIGraphicBufferProducer()->asBinder() == binder)) {
        // same IGraphicBufferProducer, return ourselves
        return int(self.get());
    }
 
    sp<Surface> sur;
    sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder));
    if (gbp != NULL) {
        // we have a new IGraphicBufferProducer, create a new Surface for it
        sur = new Surface(gbp, true);
        // and keep a reference before passing to java
        sur->incStrong(&sRefBaseOwner);
    }
 
    if (self != NULL) {
        // and loose the java reference to ourselves
        self->decStrong(&sRefBaseOwner);
    }
 
    return int(sur.get());
}
cs


 일단 지금은 이러한 방식으로 Surface가 생성되고 있구나 라고 감을 잡으시고 이해를 하고 넘어가셨으면 합니다. 위 코드를 확인해보고 예상컨데 Surface로 Parcel을 보내는 주체는 IGraphicBufferProduce를 넘겨주고 있는 듯 하다는 생각으로 진행하면 될 것으로 보입니다.



300x250

Java JNI 코드 분석 : GetObjectClass()

안드로이드/프레임워크 2015. 4. 29. 00:12

 안드로이드 프레임워크 프로그래밍을 진행하시다 보면 JNI 부분이 생각보다 많은 지식이 필요함을 느끼고 있습니다. 오늘 분석하게 되는 코드 또한 안드로이드는 물론이고 Java 에서 JNI를 사용할 때 자주 사용하는 함수들인데요. 오늘부터 각 코드들에 대한 설명을 적어내려가볼까 생각합니다.


jclass    (*env)->GetObjectClass(env, jobject);
jclass    env->GetObjectClass(jobject);

 윗부분운 C로 JNI를 코딩하실 때, 아래부분은 C++로 JNI를 코딩하실 때 사용하는 함수입니다. 이 함수는 jobject 변수로부터 해당 변수의 jclass 값을 얻기 위해 사용합니다.

 이게 무슨 말이냐 하면, jobject는 java 클래스 객체 자체라고 이해하시면 되겠습니다. 그리고 jclass는 해당 java 클래스의 경로를 저장한다는 역할을 하고 있다는 식으로 이해하시면 감이 오실 것이라 생각합니다. 위의 함수의 결과값인 return은 아래의 함수를 실행할 때에도 같은 값을 나타냅니다.


jclass    (*env)->FindClass(env, "Java 클래스의 경로명");


 위와 같이 해당 Java 클래스의 경로명을 직접 적어주는 방식으로로도 jclass 값을 return 받으실 수 있습니다.


아래는 위의 소스코드를 적용한 예제입니다. 해당 예제에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/71


hello.c

1
2
3
4
5
6
7
8
9
10
11
#include <string.h>
#include <jni.h>
#include <stdio.h>
 
void Java_com_example_calljava_MainActivity_callJava(JNIEnv* env, jobject thiz){
    //jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
    jclass jCallJava = (*env)->GetObjectClass(env, thiz);
 
    jmethodID testToast = (*env)->GetStaticMethodID(env, jCallJava, "testToast""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, testToast);
}
cs


 결론적으로 말해서


jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
jclass jCallJava = (*env)->GetObjectClass(env, thiz);


이 두 함수의 return 값이 같다는 의미로 기억해 주시면 되겠습니다!!


300x250

안드로이드 프레임워크 프로그래밍(20) [JNI를 통해 Native에서 JAVA 변수 및 클래스 객체 다루기]

안드로이드/프레임워크 2015. 4. 28. 13:19

 안드로이드 Native 단계의 Framework에서는 종종 Java 단계에서 사용되는 Framework의 값들을 사용해야 할 때가 종종 있습니다. 이를 위해 Java 단계에서 native 함수 호출을 통해 Native에 값들을 인자값을 통해 전달하는 방법이 있습니다만 Native 단계에서 실행중인 프로세스가 Java로부터 데이터를 받아올 수 있는 방법은 없을까요?

 이러한 의문은 아래의 Native 소스코드를 통해 어떤 방식으로 진행되는지 대략적인 감을 잡으실 수 있을겁니다.


/frameworks/base/core/jni/android_view_Surface.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
int register_android_view_Surface(JNIEnv* env)
{
    int err = AndroidRuntime::registerNativeMethods(env, "android/view/Surface",
            gSurfaceMethods, NELEM(gSurfaceMethods));
 
    jclass clazz = env->FindClass("android/view/Surface");
    gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
    gSurfaceClassInfo.mNativeObject =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject""I");
    gSurfaceClassInfo.mLock =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mLock""Ljava/lang/Object;");
    gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>""(I)V");
 
    clazz = env->FindClass("android/graphics/Canvas");
    gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer""Landroid/graphics/Canvas$CanvasFinalizer;");
    gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
    gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat""I");
 
    clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
    gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
 
    clazz = env->FindClass("android/graphics/Rect");
    gRectClassInfo.left = env->GetFieldID(clazz, "left""I");
    gRectClassInfo.top = env->GetFieldID(clazz, "top""I");
    gRectClassInfo.right = env->GetFieldID(clazz, "right""I");
    gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom""I");
 
    return err;
}
cs


 위의 소스코드는 우리들이 앞으로 예제를 통해 사용하게 될 소스코드의 일부입니다. 이 코드를 처음 보시는 분이시라면 상당히 복잡한 구조의 코드를 단번에 이해하시기 힘드실 것이라 생각합니다. 진행하기에 앞서 Android 환경에서 JNI의 활용에 대해 자세히 알고 싶으신 분은 이전에 작성하였던 포스팅을 참조해 주시기 바랍니다.



안드로이드 프레임워크 프로그래밍(7) [NDK 활용을 위한 JNI로 JAVA와 C 상호 호출]

안드로이드 프레임워크 프로그래밍(8) [JNI에서 작성된 C++ 코드에서 C 코드 함수 호출하기]

안드로이드 프레임워크 프로그래밍(10) [Native C/C++ 코드에서 Java 호출]


※본 포스팅의 예제는 이전에 작성하였던 예제를 토대로 진행할 예정입니다. 예제가 필요하신 분께서는 아래의 포스팅을 통해 예제를 다운로드 받으시길 바랍니다.

http://elecs.tistory.com/71



 이번 예제에서 사용되는 소스코드들중 일부를 미리 설명을 하고 넘어가도록 하겠습니다. 예제는 C언어를 기본으로 작성되었으며 C++에서는 약간의 수정이 필요함을 밝힙니다.

jclass (*env)->FindClass(env, "Java/Class/Path");    //C

jclass env->FindClass("Java/Class/Path");                //C++


jclass (*env)->FindClass(env, "Java/Class/Path")

 Native 소스코드에서 Java의 클래스의 경로를 저장하는 함수입니다.


jfieldID (*env)->GetFieldID(env, jclass, "변수명", "Signature값")

 FindClass를 통해 jclass에서 설정한 Class 내에 존재하는 변수의 이름과 해당 변수의 Signature를 설정하는 함수입니다.

 Signature에 대해 자세한 내용은 아래 포스팅을 참조해 주시길 바랍니다.

http://elecs.tistory.com/71


int (*env)->GetIntField(env, thiz, jfieldID);

char (*env)->GetCharField(env, thiz, jfieldID);

float (*env)->GetFloatField(env, thiz, jfieldID);

 이전 위에서 설정한 jclass와 jfieldID를 이용하여 Class 내에 있는 변수값을 가져옵니다. 이를 실행히면 Class에 저장된 현재값이 JNI를 통해 Native로 넘어오게 됩니다.


int (*env)->SetIntField(env, thiz, jfieldID, Class에 설정하고자 하는 값);

char (*env)->SetCharField(env, thiz, jfieldID, Class에 설정하고자 하는 값);

float (*env)->SetFloatField(env, thiz, jfieldID, Class에 설정하고자 하는 값);

 이를 통해 Java Class 내에 있는 변수의 값은 Native 단계에서 변경할 수 있습니다.


jmethodID(*env)->GetMethodID(env, jclass, "Method명", "Signature값");

jmethodID(*env)->GetStaticMethodID(env, jclass, "Method명", "Signature값");

 jclass에 설정된 Class 내에 있는 Method(함수)를 설정해 줍니다. 이를 통해 Native 단계에서 Java 내의 Method를 실행할 수 있는 준비가 완료됩니다.


(*env)->CallVoidMethod(env, jclass, jmethodID, ... );

(*env)->CallIntMethod(env, jclass, jmethodID, ... );

(*env)->CallCharMethod(env, jclass, jmethodID, ... );

(*env)->CallFloatMethod(env, jclass, jmethodID, ... );

(*env)->CallStaticVoidMethod(env, jCallJava, jmethodID, ...);

(*env)->CallStaticIntMethod(env, jCallJava, jmethodID, ...);

(*env)->CallStaticCharMethod(env, jCallJava, jmethodID, ...);

(*env)->CallStaticFloatMethod(env, jCallJava, jmethodID, ...);

 위에서 설정한 jclass와 jmethodID 값을 통해 Java Class 내의 Method를 실행합니다. 위의 ... 표시로 되어 있는 부분에 해당 호출하고자 하는 함수의 인자값을 넣어 실행하며 인자값이 없을 경우 빈칸으로 두시면 되겠습니다.


jmethodID (*env)->GetMethodID(env, jclass, "<init>", "Signature값");
jobject (*env)->NewObject(env, jclass, jmethodID);


 Native 단계에서 특정 Class를 생성하고자 할 때 사용하는 함수입니다. Native 단계 내에서 jmethodID를 통해 해당 클래스의 Constructor(생성자)를 함수명으로 "<init>"를 생성하며 NewObject() 함수를 통해 객체를 생성해 냅니다.


 아래는 이전 소스코드를 응용한 예제입니다.


MainActivity.java

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
 
 
package com.example.calljava;
 
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
 
public class MainActivity extends Activity {
    private TextView tv;
    private Button bt;
    private static Context mContext;
    public int a = 11;
    public int b = 23;
    
    static {
        System.loadLibrary("hello");
    }
    
    public native String stringFromJNI();
    public native void callJava();
    public native int nativeSum();
    public native TestClass nativeConst();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        tv = (TextView) findViewById(R.id.textview);
        tv.setText(this.stringFromJNI());
        mContext = this;
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
    public void OnClick(View v){
        switch(v.getId()){
            case R.id.button1:
                this.callJava();
                break;
            case R.id.button2:
                Toast.makeText(thisthis.nativeSum()+"", Toast.LENGTH_SHORT).show();
                break;
            case R.id.button3:
                Toast.makeText(thisthis.nativeConst().toString(), Toast.LENGTH_SHORT).show();
        }
    }
    
    public static void testToast(){
        Toast.makeText(mContext, "Hello, Toast!", Toast.LENGTH_SHORT).show();
    }
    
}
 
class TestClass{
    public TestClass(){
        
    }
    
    public String toString(){
        return "JNI를 통해 생성된 클래스입니다!";
        
    }
}
cs


Activity_Main.xml

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
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.calljava.MainActivity" >
 
    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
 
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textview"
        android:layout_below="@+id/textview"
        android:layout_marginTop="14dp"
        android:onClick="OnClick"
        android:text="Button1" />
 
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button1"
        android:layout_alignBottom="@+id/button1"
        android:layout_marginLeft="14dp"
        android:layout_toRightOf="@+id/button1"
        android:onClick="OnClick"
        android:text="Button2" />
 
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button2"
        android:layout_alignBottom="@+id/button2"
        android:layout_marginLeft="20dp"
        android:layout_toRightOf="@+id/button2"
        android:onClick="OnClick"
        android:text="Button3" />
 
</RelativeLayout>
cs


hello.c

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
#include <string.h>
#include <jni.h>
#include <stdio.h>
 
void Java_com_example_calljava_MainActivity_callJava(JNIEnv* env, jobject thiz){
    jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
    //jclass jCallJava = (*env)->GetObjectClass(env, thiz);
 
    jmethodID testToast = (*env)->GetStaticMethodID(env, jCallJava, "testToast""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, testToast);
}
 
jstring Java_com_example_calljava_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz){
    return (*env)->NewStringUTF(env, "Hello, JNI World!");
}
 
jint Java_com_example_calljava_MainActivity_nativeSum(JNIEnv* env, jobject thiz){
    jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
 
    jfieldID ja = (*env)->GetFieldID(env, jCallJava, "a""I");
    int a = (*env)->GetIntField(env, thiz, ja);
    jfieldID jb = (*env)->GetFieldID(env, jCallJava, "b""I");
    int b = (*env)->GetIntField(env, thiz, jb);
 
    int c = a+b;
 
    return c;
}
 
jobject Java_com_example_calljava_MainActivity_nativeConst(JNIEnv* env, jobject thiz){
 
    jclass jTestClass = (*env)->FindClass(env, "com/example/calljava/TestClass");
 
    jmethodID jmid = (*env)->GetMethodID(env, jTestClass, "<init>""()V");
    jobject obj = (*env)->NewObject(env, jTestClass, jmid);
    
    
    return obj;
    
}
cs







300x250

안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리]

안드로이드/프레임워크 2015. 4. 10. 00:33

 지금까지 우리는 안드로이드 내의 System service가 ServiceManager에 의해 관리되고 있음을 알 수 있었습니다. IServiceManager를 통해 BpServiceManager를 생성하여 Binder를 통해 서비스를 등록하거나 찾는 과정 또한 확인했습니다.


/frameworks/native/libs/binder/IServiceManager.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android::sp<IServiceManager> IServiceManager::asInterface(                           
    const android::sp<android::IBinder>& obj)                       
    {
        android::sp<IServiceManager> intr;
        if (obj != NULL) {
            intr = static_cast<IServiceManager*>(
                obj->queryLocalInterface(
                    IServiceManager::descriptor).get());
        if (intr == NULL) {
            intr = new BpServiceManager(obj);
        }
    }
    return intr;
cs


이러한 과정을 거치는 한 편으로는 이런 생각을 가지신 분들도 계시리라 생각합니다.


"BpServiceManager와 BnServiceManager가 존재한다면 ServiceManager 클래스도 존재하지 않을까?"


 대부분의 분들이라면 분명 있으리라 생각하실 겁니다. 그러나 놀랍게도 ServiceManager는 Java 단계에서는 클래스가 존재합니다만 Native 단계에서는 ServiceManager 클래스를 확인하실 수 없습니다. 그렇다면 Native 단계에서는 ServiceManager가 사용되지 않는걸까요?

 사실 Native 단계에서 ServiceManager는 daemon 프로세스와 같이 백그라운드에서 지속적으로 동작하는 프로세스로 존재합니다. 비록 ServiceManager라는 이름은 아니지만 Binder를 등록하거나 검색을 할 수 있는 기능을 갖추어 놓고 있습니다. ServiceManager는 다음과 같은 파일들로 구성되어 있습니다.


/frameworks/native/cmds/servicemanager/binder.h

/frameworks/native/cmds/servicemanager/binder.c

/frameworks/native/cmds/servicemanager/service_manager.c


 시작하기에 앞서 Android.mk에 설정된 모습을 보도록 하겠습니다.

/frameworks/native/cmds/servicemanager/Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
LOCAL_PATH:= $(call my-dir)
 
#include $(CLEAR_VARS)
#LOCAL_SRC_FILES := bctest.c binder.c
#LOCAL_MODULE := bctest
#include $(BUILD_EXECUTABLE)
 
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := service_manager.c binder.c
LOCAL_MODULE := servicemanager
include $(BUILD_EXECUTABLE)
cs


 이제 ServiceManager가 실행되는 모습을 보도록 합시다.


/frameworks/native/cmds/servicemanager/service_manager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void *svcmgr_handle;
 
int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
 
    bs = binder_open(128*1024);
 
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
 
    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}
cs


/frameworks/native/cmds/servicemanager/binder.h

1
2
/* the one magic object */
#define BINDER_SERVICE_MANAGER ((void*0)
cs


 처음엔 binder_state 구조체 변수와 void 변수 svcmgr이 선언됩니다. binder_state에는 이름 그대로 바인더의 상태를 저장하기 위해 사용되는 구조체임을 알 수 있습니다. 여기서 main() 함수의 내용을 하나씩 살펴보겠습니다.


    bs = binder_open(128*1024);


 binder_open() 함수가 선언되어 있고 이를 통해 binder_state 구조체를 return 하는 모습을 보이고 있습니다. binder_open() 함수를 통해 binder가 설정되며 인자로 메모리에 할당할 용량을 설정합니다.


/frameworks/native/cmds/servicemanager/binder.c

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
struct binder_state
{
    int fd;
    void *mapped;
    unsigned mapsize;
};
 
struct binder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;
 
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }
 
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
 
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
 
        /* TODO: check version */
 
    return bs;
 
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
}
cs

 binder_open() 함수의 내부를 살펴보도록 합시다. open() 함수가 호출됨으로서 바딘더의 File descriptor를 얻어낸 후 mmap() 함수를 호출하여 실제 메모리에 바인더를 할당하는 작업을 진행합니다. 모든 것이 완료되면 binder_state를 저장한 구조체 변수의 포인터를 return 합니다.


binder_become_context_manager(bs)


binder_state의 값을 통하여 컨텍스트 매니저(ServiceManager)을 설정해줍니다.

/frameworks/native/cmds/servicemanager/binder.c

1
2
3
4
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
cs

binder.h

1
#define BINDER_SET_CONTEXT_MGR _IOW('b'7int)
cs

 해당 함수는 ioctl() 함수를 사용하여 바인더의 입출력을 제어합니다.


binder_loop(bs, svcmgr_handler);


 이 함수가 실행됨으로서 ServiceManager의 실행은 loop에 들어갑니다. 즉, 시스템에 특별한 이상이 발생하지 않는 한 작동이 계속 되는 것으로 이해하시면 좋을 듯 합니다.


/frameworks/native/cmds/servicemanager/binder.c

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];
 
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0= BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));
 
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;
 
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
 
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
 
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
 
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
 
 
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)
{
    int r = 1;
    uint32_t *end = ptr + (size / 4);
 
    while (ptr < end) {
        uint32_t cmd = *ptr++;
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
#if TRACE
            fprintf(stderr,"  %08x %08x\n", ptr[0], ptr[1]);
#endif
            ptr += 2;
            break;
        case BR_TRANSACTION: {
            struct binder_txn *txn = (void *) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
 
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data, res);
            }
            ptr += sizeof(*txn) / sizeof(uint32_t);
            break;
        }
        case BR_REPLY: {
            struct binder_txn *txn = (void*) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                    /* todo FREE BUFFER */
            }
            ptr += (sizeof(*txn) / sizeof(uint32_t));
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            struct binder_death *death = (void**ptr++;
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }
 
    return r;
}
cs


binder.h

1
2
3
4
5
6
7
8
9
10
struct binder_write_read {
 signed long write_size;
 signed long write_consumed;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 unsigned long write_buffer;
 signed long read_size;
 signed long read_consumed;
 unsigned long read_buffer;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
};
cs



 제 이전 포스팅에서 Native 단계에서의 Parcel의 전송과정에 대해 다루었던 과정을 보셨던 분이라면 위의 소스코드를 읽었을 때 '앗!'이라는 생각을 하시는 분들이 계시리라 생각합니다. 그렇습니다. Parcel에서 파일이 전송되려 할 때 transact() 함수를 통해 전송되어 오는 Parcel의 값들을 처리하고 있는 것임을 본 포스팅을 통해 확실히 알게 되셨으리라 생각합니다.


 사실 위의 과정에서 좀 더 많은 설명을 해드리고 싶습니다만 글을 더 진행하기엔 포스팅의 분량도 많아질 뿐더러 내용 또한 어려워지기 때문에 이후 시간이 된다면 Parcel이 Binder와 어떻게 동작하는 지에 대해 자세히 다루어 보는 시간을 가져보도록 하겠습니다.

300x250

안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기]

 요즘 안드로이드 Framework를 공부하면서 드는 생각이 있습니다. 정말 구글의 안드로이드 제작자들이 경이롭다고 생각될 정도로 안드로이드 운영체제는 심오하면서도 동작원리가 매우 신기하다는 생각이 듭니다.

 이번 포스팅에서는 Native에서 등록하였던 Service를 ServiceManager에서 불러들이는 과정에 대해 살펴보도록 하겠습니다.


 먼저 예제로 CameraService를 불러들이는 예제를 살펴보도록 하겠습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs

/frameworks/av/camera/CameraBase.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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs

 위에서 보시는 바와 같이 ServiceManager를 통해 getService() 함수로 CameraService의 바인더를 불러오고 있는 모습을 보실 수 있습니다. 그렇다면 위의 바인더가 어떠한 방식으로 호출이 되는지 자세히 알아보도록 하겠습니다.


/frameworks/native/libs/binder/IServiceManager.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
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
 
    virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs

 IServiceManager.cpp 내에 있는 BpServiceManager를 통해 Binder를 받는 과정을 나타내고 있습니다. 위 과정을 마치게 되면 Binder를 CameraService의 Binder를 얻을 수 있게 됩니다.

 getService() 함수를 통해 부르고자 하는 Service의 명을 전달 받은 후 그 안에 있는 checkService()함수를 호출하여 Parcel을 통해 원하는 Service의 Binder를 얻게 됩니다.


혹시 위에서 Binder를 받기 위해 활용되는 Parcel에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.

안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)]

안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)]


여기서 chechService() 함수의 동작 과정에 대해 살펴보도록 하겠습니다.


Parcel data, reply;

전송보낼 data와 전송을 받을 reply인 Parcel 클래스 변수를 선언합니다.


data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

data Parcel에 인터페이스 토큰을 작성합니다. 여기서 인자값으로 들어오는 값은 "android.os.IServiceManager"입니다.

위 함수에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/87


data.writeString16(name);

찾고자 하는 System Service의 이름을 입력합니다. 여기에서는 "media.camera"가 되겠군요.


remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);

지금까지 data에 기록하였던 내용을 Binder로 전송합니다. 이 과정을 통해 reply로  data의 내용에 대한 응답인 Parcel을 전송받게 됩니다.


return reply.readStrongBinder();

전달 받은 Parcel인 reply를 통해 Service의 BpBinder를 반환합니다.

여기서 수신된 Parcel로부터 binder를 얻는 과정을 살펴보도록 합니다.


/frameworks/native/libs/binder/Parcel.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
sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
 
....
 
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);
    
    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}
 
....
 
inline static status_t finish_unflatten_binder(
    BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
{
    return NO_ERROR;
}
cs

 위 코드의 주요 내용들을 하나씩 살펴보도록 하겠습니다.


const flat_binder_object* flat = in.readObject(false);

수신된 Parcel로부터 값을 읽어들입니다. 이 과정에서 flat_binder_object 구조체값이 넘어오게 됩니다.

이렇게 해서 넘어온 구조체값의 type는 BINDER_TYPE_HANDLE이 설정되어 있습니다.


*out = proc->getStrongProxyForHandle(flat->handle);

flat_binder_object에 저장된 서비스 핸들을 전달하여 BpBinder를 얻습니다. 위 함수의 구조는 다음과 같이 되어 있음을 확인하실 수 있습니다.

아래는 해당 함수가 동작하는 과정에 대해 확인해 보고자 하는 목적으로 첨부합니다.

위 함수에 대해 좀 더 자세한 내용을 참조하고 싶으신 분께서는 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/89


/frameworks/native/libs/binder/ProcessState.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
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
 
 
cs

return finish_unflatten_binder(

                    static_cast<BpBinder*>(out->get()), *flat, in);

위 과정을 통해 이 함수의 수행이 무사히 수행되었음을 NO_ERROR을 반환함으로서 종료합니다.



이제 이 즈음에서 우리들이 처음부터 연구하였던 소스 CameraBase.cpp의 getService() 이후의 동작들에 대해 파악해 보도록 합시다.


/frameworks/av/camera/CameraBase.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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs


이제 위에서 남은 두 함수들에 대해 분석을 해 보도록 하겠습니다.


binder->linkToDeath(gDeathNotifier);

 getService() 함수를 통해 얻은 binder에 gDeathNotifier를 틍록합니다. linkToDeath() 함수는 해당 binder가 의도치 않은 상황에서 종료가 되었을 때 해당 클래스를 실행하라는 의도로 생각하시면 되겠습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs


gCameraService = interface_cast<ICameraService>(binder);

 위 코드를 통해 CameraService가 binder를 통해 등록됩니다. 이 함수가 어떻게 동작하는지 확인해 보도록 하겠습니다. 해당 함수에서 사용되는 기술에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/87

http://elecs.tistory.com/88


/frameworks/av/include/camera/ICameraService.h

1
2
3
4
5
6
7
8
9
#include <binder/IInterface.h>
 
class ICameraService : public IInterface
{
....
public:
    DECLARE_META_INTERFACE(CameraService);
....
}
cs

/frameworks/native/include/binder/IInterface.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
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
....
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
 
 
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
....
 
cs


/frameworks/av/camera/ICameraService.cpp

1
2
3
#include <binder/IInterface.h>
 
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
cs


 위 내용을 종합하면 다음과 같은 내용임을 알아낼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    static android::sp<ICameraService> asInterface(
            const android::sp<android::IBinder>& obj);
 
    const android::String16 ICameraService::descriptor("android.hardware.ICameraService");
    android::sp<ICameraService> ICameraService::asInterface(
            const android::sp<android::IBinder>& obj)
    {
        android::sp<ICameraService> intr;
        if (obj != NULL) {
            intr = static_cast<ICameraService*>(
                obj->queryLocalInterface(
                        ICameraService::descriptor).get());
            if (intr == NULL) {
                intr = new BpCameraService(obj);
            }
        }
        return intr;
    } 
cs

위 코드를 통해 gCameraService 내에 BpCameraService 클래스가 선언되는 것을 확인할 수 있다.

300x250