PHLASHNT.SYS 드라이버를 로드할 수 없습니다. 오류 코드: 1275

공대생의 팁 2015. 9. 7. 23:36

 현재 제가 데스크톱으로 활용하고 있는 리퍼비시 PC인 Smasung 매직스테이션 DB-Z70을 사용하다가 최신 BIOS로 업그레이드를 해 보고자 삼성전자 홈페이지에서 해당 데스크톱의 최신 BIOS 버전을 다운로드 받은 후 실행해 보았습니다. 그러자 아래와 같은 경고문이 뜨면서 저를 반기더군요.

 "PHLASHNT.SYS 드라이버를 로드할 수 없습니다. 계정을 확인하십시오. 관리자 권한이 없는 경우에는 다시 로그인하십시오! 이 드라이버가 차단되었기 때문에 로드할 수 없습니다. 오류 코드: 1275"



 위 화면은 Windows 10 64-bit 버전에서 해당 프로그램을 실행하였을 때 위와 같은 현상이 발생합니다. 위와 같은 문제가 발생하는 원인은 아래 중 하나에 해당될 것입니다.


- 현재 자신이 사용하는 운영체제의 버전이 Windows 7 이상(8, 8.1, 10)인 경우

- 자신이 사용하는 운영체제가 64비트 기반일 경우


 구글링을 통해 다른 분들의 사례들을 분석해 본 결과, 해당 BIOS의 버전이 설치되지 않는 이유는 Windows 7에서 부터 실행권한이 Root와 User가 분리되면서 발생하는 것으로 추정됩니다. 현재 해외의 사례에서 가장 좋은 해결 방법은 32비트 기반의 Windows XP 운영체제를 통해 설치하는 것을 권장하고 있습니다. 위 프로그램을 설치하기 위해서는 아래의 방법 중 하나를 선택해 주시면 될 것으로 보입니다.


- 자신의 하드디스크에 임시로 파티션을 분리하여 Windows XP를 설치한 후 BIOS 설치 프로그램을 실행합니다.

- Windows XP가 설치되어 있는 하드디스크 혹은 USB를 통해 Windows XP를 부팅한 후 BIOS 설치 프로그램을 실행합니다. 


 위에서 제시한 방법들 중 하나를 선택하여 BIOS 설치 프로그램을 실행하시면 아래와 같이 정상적으로 설치되는 것을 확인하실 수 있습니다.


 


300x250

안드로이드 프레임워크 프로그래밍(24) [BitTube 클래스]

 안드로이드 Native Framework에 대한 분석을 계속 진행을 하다보니 어느덧 linux 기반의 시스템프로그래밍에 대한 지식이 많이 필요하더군요. 종종 튀어나오는 unix 명령어들을 보면 반가우면서도 한편으로는 동작 원리를 다시 살펴보는 제 모습을 보게 됩니다.

 이번 포스팅에서는 Native Framework 단계에서 프로세스간 통신 기능을 보조해주는 BitTube에 대해 알아보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/EventThread.cpp

1
2
3
4
5
EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
cs

 위 소스코드를 보시면 BitTube라는 이름의 클래스가 새로 만들어지고 있는 것을 보실 수 있습니다. BitTube는 안드로이드 상에서 unix 소켓 프로그래밍을 쓰기 쉽게 만들어진 클래스 입니다. BitTube 클래스의 내부를 살펴보신다면 이전에 시스템 프로그래밍에 대해 공부하신 분들이라면 익숙한 함수들을 만나보실 수 있을 것입니다.


/frameworks/native/include/gui/BitTube.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
class Parcel;
 
class BitTube : public RefBase
{
public:
 
    // creates a BitTube with a default (4KB) send buffer
    BitTube();
 
    // creates a BitTube with a a specified send and receive buffer size
    explicit BitTube(size_t bufsize);
 
    explicit BitTube(const Parcel& data);
    virtual ~BitTube();
 
    // check state after construction
    status_t initCheck() const;
 
    // get receive file-descriptor
    int getFd() const;
 
    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
    template <typename T>
    static ssize_t sendObjects(const sp<BitTube>& tube,
            T const* events, size_t count) {
        return sendObjects(tube, events, count, sizeof(T));
    }
 
    // receive objects (sized blobs). If the receiving buffer isn't large enough,
    // excess messages are silently discarded.
    template <typename T>
    static ssize_t recvObjects(const sp<BitTube>& tube,
            T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }
 
    // parcels this BitTube
    status_t writeToParcel(Parcel* reply) const;
 
private:
    void init(size_t rcvbuf, size_t sndbuf);
 
    // send a message. The write is guaranteed to send the whole message or fail.
    ssize_t write(void const* vaddr, size_t size);
 
    // receive a message. the passed buffer must be at least as large as the
    // write call used to send the message, excess data is silently discarded.
    ssize_t read(void* vaddr, size_t size);
 
    int mSendFd;
    mutable int mReceiveFd;
 
    static ssize_t sendObjects(const sp<BitTube>& tube,
            void const* events, size_t count, size_t objSize);
 
    static ssize_t recvObjects(const sp<BitTube>& tube,
            void* events, size_t count, size_t objSize);
};
cs

 BitTube의 기능을 전반적으로 보여드리기 위해 BitTube의 헤더파일을 모두 살펴보았습니다. 위 소스코드를 살펴보신다면 BitTube 클래스가 어떠한 동작으로 움직이는 어느 정도 감이 오실 것입니다. 여기서 BitTube의 생성자를 보도록 합시다.


/frameworks/native/libs/gui/BitTube.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
// Socket buffer size.  The default is typically about 128KB, which is much larger than
// we really need.  So we make it smaller.
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
 
BitTube::BitTube()
    : mSendFd(-1), mReceiveFd(-1)
{
    init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
}
 
....
 
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
        // sine we don't use the "return channel", we keep it small...
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
        mReceiveFd = sockets[0];
        mSendFd = sockets[1];
    } else {
        mReceiveFd = -errno;
        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
    }
}
cs

 BitTube 클래스가 생성되면 BitTube를 생성한 해당 프로세스의 File Discriptor가 만들어집니다. 이후 해당 FD를 BitTube 클래스가 관리해주며 프로세스는 BitTube를 통해 다른 프로세서로부터 값을 받아오거나 보낼 수 있게 됩니다. 위의 함수들은 unix 표준 함수이므로 관련 자료를 쉽게 구하실 수 있을 것입니다.


/frameworks/native/libs/gui/BitTube.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
status_t BitTube::initCheck() const
{
    if (mReceiveFd < 0) {
        return status_t(mReceiveFd);
    }
    return NO_ERROR;
}
 
int BitTube::getFd() const
{
    return mReceiveFd;
}
 
ssize_t BitTube::write(void const* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
        // cannot return less than size, since we're using SOCK_SEQPACKET
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    return err == 0 ? len : -err;
}
 
ssize_t BitTube::read(void* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        // EAGAIN means that we have non-blocking I/O but there was
        // no data to be read. Nothing the client should care about.
        return 0;
    }
    return err == 0 ? len : -err;
}
 
status_t BitTube::writeToParcel(Parcel* reply) const
{
    if (mReceiveFd < 0)
        return -EINVAL;
 
    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
    close(mReceiveFd);
    mReceiveFd = -1;
    return result;
}
 
 
ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
        void const* events, size_t count, size_t objSize)
{
    const char* vaddr = reinterpret_cast<const char*>(events);
    ssize_t size = tube->write(vaddr, count*objSize);
 
    // should never happen because of SOCK_SEQPACKET
    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize),
            "BitTube::sendObjects(count=%d, size=%d), res=%d (partial events were sent!)",
            count, objSize, size);
 
    //ALOGE_IF(size<0, "error %d sending %d events", size, count);
    return size < 0 ? size : size / objSize;
}
 
ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
        void* events, size_t count, size_t objSize)
{
    char* vaddr = reinterpret_cast<char*>(events);
    ssize_t size = tube->read(vaddr, count*objSize);
 
    // should never happen because of SOCK_SEQPACKET
    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize),
            "BitTube::recvObjects(count=%d, size=%d), res=%d (partial events were received!)",
            count, objSize, size);
 
    //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
    return size < 0 ? size : size / objSize;
}
cs


300x250

안드로이드 프레임워크 프로그래밍(23) [onFirstRef() 함수 호출시기]

 안드로이드 Native 단계에서의 Framework를 분석해 보던 도중 의문이 들었던 함수가 하나 있었습니다.


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

1
2
3
4
void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);
}
cs

 표면적으로 보았을 때 onFirstRef() 함수를 바로 호출하는 부분을 해당 부분을 실행하는 소스코드 내에서 바로 찾아내기는 어렵습니다. 그렇다면 위 함수 onFirstRef()가 어느 시점에서 호출이 되는지 살펴보도록 하겠습니다.

 먼저 SurfaceFlinger가 처음 생성되는 부분에서부터 추적해 나가도록 해보겠습니다.


/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

1
2
3
4
5
6
7
8
9
10
int main(int argc, char** argv) {
 
....
 
    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();
 
....
 
}
cs

 위 함수 onFirstRef()의 호출 시점을 파악하기 위해 Strong Pointer를 분석해 볼 필요가 있습니다. Strong Pointer에 대해 분석한 이전 포스팅에 대한 내용은 아래 링크를 참조해 주시기 바랍니다.


http://elecs.tistory.com/79


 여기서 바로 StrongPointer에 SurfaceFlinger가 적용되는 코드를 보도록 하겠습니다.


 /system/core/include/utils/StrongPointer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}
 
template<typename T>
sp<T>::sp(const sp<T>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}
cs

 Strong Pointer가 생성될 때 해당 포인터를 받게 되면 해당 포인터의 incStrong() 함수를 실행하게 됩니다. 여기서 incStrong() 함수를 찾아보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/main_surfaceflinger.h

1
2
3
4
5
6
7
8
class SurfaceFlinger : public BnSurfaceComposer,
                       private IBinder::DeathRecipient,
                       private HWComposer::EventHandler
{
 
....
 
};
cs


/frameworks/native/include/gui/ISurfaceComposer.h

1
2
3
4
5
6
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
 
....
 
};
cs


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
8
9
10
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
    virtual const String16&     getInterfaceDescriptor() const;
 
protected:
    virtual IBinder*            onAsBinder();
};
cs


/frameworks/native/include/binder/Binder.h

1
2
3
4
5
6
class BBinder : public IBinder
{
 
....
 
};
cs


/frameworks/native/include/binder/IBinder.h

1
2
3
4
5
6
class IBinder : public virtual RefBase
{
 
....
 
};
cs


/system/core/include/utils/RefBase.h

1
2
3
4
5
6
7
8
class RefBase
{
public:
            void            incStrong(const void* id) const;
 
....
 
}
cs


 안드로이드 Native 클래스들의 토대라 할 수 있는 RefBase 클래스 내에 incStrong()함수가 존재하는 것을 확인하였습니다. 이제 해당 함수의 소스코드를 살펴보도록 합시다.


/system/core/libutils/RefBase.cpp

1
2
3
4
RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
}
cs


/system/core/libutils/RefBase.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
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile int32_t    mFlags;
 
....
 
    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
        , mStrongRefs(NULL)
        , mWeakRefs(NULL)
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        , mRetain(false)
    {
    }
 
....
 
}
cs


/system/core/libutils/RefBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <utils/RefBase.h>
 
...
 
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0"incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n"this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }
 
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}
cs

 위의 RefBase 단계에서의 incStrong() 함수가 호출될 때 onFirstRef()함수가 호출되는 것을 확인하실 수 있습니다.

300x250

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

 이번 포스팅에서는 Java Framework 단계에서 초기화 되는 과정에서 VSync 관련 동작이 Native 단계와 연결되는 과정을 살펴보도록 하겠습니다.


 Java 단계에서 안드로이드 기반 디바이스의 화면을 관리하는 클래스로 Choreographer가 있습니다. 이 클래스는 화면의 변화나 입력등을 통해 그려지는 타이밍을 관리합니다. Choreographer는 디스플레이의 부속시스템으로부터 전달되는 VSync(Vertical Synchronization)와 같은 신호를 수신하고, 다음 디스플레이 프레임을 렌더링 하기 위해 작업을 스케줄링 합니다.


 그렇다면 이제 Choreographer가 초기화 되는 과정을 살펴보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper);
        }
    };
cs

 ThreadLocal을 통하여 Choreographer 값을 thread에 저장합니다. static으로 선언되어 있으므로 sThreadInstance의 값이 초기화 되면서 return 값인 new Choreographer()가 실행됩니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // Enable/disable vsync for animations and drawing.
    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync"true);
 
....
 
    private Choreographer(Looper looper) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;
 
        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
 
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }
cs

 Choreographer의 Constructor가 생성되고 있습니다. VSync를 사용하고 있으므로 FrameDisplayEventReceiver()의 Constructor가 새로 생성됨을 확인하실 수 있습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
 
        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
 
....
 
    }
cs


/frameworks/base/core/java/android/view/DisplayEventReceiver.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
/**
 * Provides a low-level mechanism for an application to receive display events
 * such as vertical sync.
 *
 * The display event receive is NOT thread safe.  Moreover, its methods must only
 * be called on the Looper thread to which it is attached.
 *
 * @hide
 */
public abstract class DisplayEventReceiver {
    private static final String TAG = "DisplayEventReceiver";
 
    private final CloseGuard mCloseGuard = CloseGuard.get();
 
    private int mReceiverPtr;
 
    // We keep a reference message queue object here so that it is not
    // GC'd while the native peer of the receiver is using them.
    private MessageQueue mMessageQueue;
 
    private static native int nativeInit(DisplayEventReceiver receiver,
            MessageQueue messageQueue);
    private static native void nativeDispose(int receiverPtr);
    private static native void nativeScheduleVsync(int receiverPtr);
 
    /**
     * Creates a display event receiver.
     *
     * @param looper The looper to use when invoking callbacks.
     */
    public DisplayEventReceiver(Looper looper) {
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }
 
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(this, mMessageQueue);
 
        mCloseGuard.open("dispose");
    }
 
....
 
}
cs

 위 소스코드를 통해 DisplayEventReceiver 클래스에서 nativeInit를 통해 JNI로 연결되는 것을 보실 수 있습니다.


/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
        jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverObj, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }
 
    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jint>(receiver.get());
}
cs

 NativeDisplayEventReceiver클래스의 Constructor가 생성된 후 initialize() 함수가 호출되고 있는 모습입니다.


/frameworks/base/core/jni/android_view_DisplayEventReceiver.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
class NativeDisplayEventReceiver : public LooperCallback {
public:
    NativeDisplayEventReceiver(JNIEnv* env,
            jobject receiverObj, const sp<MessageQueue>& messageQueue);
 
    status_t initialize();
    void dispose();
    status_t scheduleVsync();
 
protected:
    virtual ~NativeDisplayEventReceiver();
 
private:
    jobject mReceiverObjGlobal;
    sp<MessageQueue> mMessageQueue;
    DisplayEventReceiver mReceiver;
    bool mWaitingForVsync;
 
    virtual int handleEvent(int receiveFd, int events, void* data);
    bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
    void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
    void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};
 
 
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
        jobject receiverObj, const sp<MessageQueue>& messageQueue) :
        mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
        mMessageQueue(messageQueue), mWaitingForVsync(false) {
    ALOGV("receiver %p ~ Initializing input event receiver."this);
}
 
NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->DeleteGlobalRef(mReceiverObjGlobal);
}
 
status_t NativeDisplayEventReceiver::initialize() {
    status_t result = mReceiver.initCheck();
    if (result) {
        ALOGW("Failed to initialize display event receiver, status=%d", result);
        return result;
    }
 
    int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
            this, NULL);
    if (rc < 0) {
        return UNKNOWN_ERROR;
    }
    return OK;
}
cs


 위의 과정을 보았을 때 MessageQueue 내에 들어있는 Looper를 얻어낸 후 addFd() 함수를 통해 DisplayEventReceiver의 File Description이 등록됩니다. NativeDisplayEventReceiver 내에 있는 mReceiver는 DisplayEventReceiver의 클래스 변스로 해당 클래스의 소스코드를 살펴보도록 하겠습니다.


/frameworks/native/include/gui/DisplayEventReceiver.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
class DisplayEventReceiver {
public:
    enum {
        DISPLAY_EVENT_VSYNC = 'vsyn',
        DISPLAY_EVENT_HOTPLUG = 'plug'
    };
 
    struct Event {
 
        struct Header {
            uint32_t type;
            uint32_t id;
            nsecs_t timestamp;
        };
 
        struct VSync {
            uint32_t count;
        };
 
        struct Hotplug {
            bool connected;
        };
 
        Header header;
        union {
            VSync vsync;
            Hotplug hotplug;
        };
    };
 
public:
    /*
     * DisplayEventReceiver creates and registers an event connection with
     * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
     * or requestNextVsync to receive them.
     * Other events start being delivered immediately.
     */
    DisplayEventReceiver();
 
    /*
     * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
     * stop being delivered immediately. Note that the queue could have
     * some events pending. These will be delivered.
     */
    ~DisplayEventReceiver();
 
    /*
     * initCheck returns the state of DisplayEventReceiver after construction.
     */
    status_t initCheck() const;
 
    /*
     * getFd returns the file descriptor to use to receive events.
     * OWNERSHIP IS RETAINED by DisplayEventReceiver. DO NOT CLOSE this
     * file-descriptor.
     */
    int getFd() const;
 
    /*
     * getEvents reads events from the queue and returns how many events were
     * read. Returns 0 if there are no more events or a negative error code.
     * If NOT_ENOUGH_DATA is returned, the object has become invalid forever, it
     * should be destroyed and getEvents() shouldn't be called again.
     */
    ssize_t getEvents(Event* events, size_t count);
    static ssize_t getEvents(const sp<BitTube>& dataChannel,
            Event* events, size_t count);
 
    /*
     * sendEvents write events to the queue and returns how many events were
     * written.
     */
    static ssize_t sendEvents(const sp<BitTube>& dataChannel,
            Event const* events, size_t count);
 
    /*
     * setVsyncRate() sets the Event::VSync delivery rate. A value of
     * 1 returns every Event::VSync. A value of 2 returns every other event,
     * etc... a value of 0 returns no event unless  requestNextVsync() has
     * been called.
     */
    status_t setVsyncRate(uint32_t count);
 
    /*
     * requestNextVsync() schedules the next Event::VSync. It has no effect
     * if the vsync rate is > 0.
     */
    status_t requestNextVsync();
 
private:
    sp<IDisplayEventConnection> mEventConnection;
    sp<BitTube> mDataChannel;
};
cs


/frameworks/native/libs/gui/DisplayEventReceiver.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
DisplayEventReceiver::DisplayEventReceiver() {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != NULL) {
        mEventConnection = sf->createDisplayEventConnection();
        if (mEventConnection != NULL) {
            mDataChannel = mEventConnection->getDataChannel();
        }
    }
}
 
DisplayEventReceiver::~DisplayEventReceiver() {
}
 
status_t DisplayEventReceiver::initCheck() const {
    if (mDataChannel != NULL)
        return NO_ERROR;
    return NO_INIT;
}
 
int DisplayEventReceiver::getFd() const {
    if (mDataChannel == NULL)
        return NO_INIT;
 
    return mDataChannel->getFd();
}
cs


 DisplayEventReceiver의 소스코드를 하나씩 살펴보도록 하겠습니다.


sp<ISurfaceComposer> sf(ComposerService::getComposerService());

 SurfaceFlinger로부터 ComposerService의 Binder Proxy를 얻어옵니다.


/frameworks/native/libs/gui/SurfaceComposerClient.cpp

1
2
3
4
5
6
7
8
9
10
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
    ComposerService& instance = ComposerService::getInstance();
    Mutex::Autolock _l(instance.mLock);
    if (instance.mComposerService == NULL) {
        ComposerService::getInstance().connectLocked();
        assert(instance.mComposerService != NULL);
        ALOGD("ComposerService reconnected");
    }
    return instance.mComposerService;
}
cs


/frameworks/native/include/private/gui/ComposerService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// This holds our connection to the composer service (i.e. SurfaceFlinger).
// If the remote side goes away, we will re-establish the connection.
// Users of this class should not retain the value from
// getComposerService() for an extended period.
//
// (It's not clear that using Singleton is useful here anymore.)
class ComposerService : public Singleton<ComposerService>
{
    sp<ISurfaceComposer> mComposerService;
    sp<IBinder::DeathRecipient> mDeathObserver;
    Mutex mLock;
 
    ComposerService();
    void connectLocked();
    void composerServiceDied();
    friend class Singleton<ComposerService>;
public:
 
    // Get a connection to the Composer Service.  This will block until
    // a connection is established.
    static sp<ISurfaceComposer> getComposerService();
};
cs

mEventConnection = sf->createDisplayEventConnection();

 SurfaceFlinger로 부터 디스플레이와 관련된 이벤트를 수신할 수 있는 EventConnection 클래스를 받습니다.


/frameworks/native/libs/gui/ISurfaceComposer.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
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
    BpSurfaceComposer(const sp<IBinder>& impl)
        : BpInterface<ISurfaceComposer>(impl)
    {
    }
 
....
 
    virtual sp<IDisplayEventConnection> createDisplayEventConnection()
    {
        Parcel data, reply;
        sp<IDisplayEventConnection> result;
        int err = data.writeInterfaceToken(
                ISurfaceComposer::getInterfaceDescriptor());
        if (err != NO_ERROR) {
            return result;
        }
        err = remote()->transact(
                BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
                data, &reply);
        if (err != NO_ERROR) {
            ALOGE("ISurfaceComposer::createDisplayEventConnection: error performing "
                    "transaction: %s (%d)", strerror(-err), -err);
            return result;
        }
        result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());
        return result;
    }
 
....
 
}
 
status_t BnSurfaceComposer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
 
....
 
        case CREATE_DISPLAY_EVENT_CONNECTION: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            sp<IDisplayEventConnection> connection(createDisplayEventConnection());
            reply->writeStrongBinder(connection->asBinder());
            return NO_ERROR;
        }
 
....
 
}
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
void SurfaceFlinger::init() {
 
....
 
    // 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);
 
....
 
}
 
....
 
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
    return mEventThread->createEventConnection();
}
cs

 EventThread를 통해 createEventConnection() 함수를 실행하게 됩니다. EventThread가 SurfaceFlinger에 적용되는 과정에 대해 알고 싶으신 분은 이전 포스팅의 내용을 참조해 주시기 바랍니다.


SurfaceFlinger에서 EventThread의 초기화 과정

http://elecs.tistory.com/140


/frameworks/native/services/surfaceflinger/EventThread.cpp

1
2
3
4
5
6
7
8
9
10
11
sp<EventThread::Connection> EventThread::createEventConnection() const {
    return new Connection(const_cast<EventThread*>(this));
}
 
....
 
EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
cs


mDataChannel = mEventConnection->getDataChannel();

 위 Connection 클래스를 생성하면서 만들어진 FD인 BitTube 클래스를 mDataChannel에 저장합니다.


/frameworks/native/services/surfaceflinger/EventThread.cpp

1
2
3
sp<BitTube> EventThread::Connection::getDataChannel() const {
    return mChannel;
}
cs


 이후 등록된 FD가 호출될 경우 VSync 관련 이벤트가 발생하게 됩니다.

/system/core/libutils/Looper.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
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p"this, fd, ident,
            events, callback.get(), data);
#endif
 
    if (!callback.get()) {
        if (! mAllowNonCallbacks) {
            ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
            return -1;
        }
 
        if (ident < 0) {
            ALOGE("Invalid attempt to set NULL callback with ident < 0.");
            return -1;
        }
    } else {
        ident = ALOOPER_POLL_CALLBACK;
    }
 
    int epollEvents = 0;
    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
 
    { // acquire lock
        AutoMutex _l(mLock);
 
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;
 
        struct epoll_event eventItem;
        memset(& eventItem, 0sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;
 
        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}
 
....
 
int Looper::pollInner(int timeoutMillis) {
 
....
 
    // Release lock.
    mLock.unlock();
 
    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == ALOOPER_POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    return result;
}
cs



300x250

[C/C++]thread 조건변수 다루기 - pthread_cond_broadcast()

프로그래밍 팁 2015. 9. 1. 01:34

 멀티코어를 기반으로 한 프로그래밍을 수행할 때 가장 중요한 요소라면 여러 개의 thread를 통제할 수 있는 방법에 대한 정확한 이해라고 생각합니다. 다수의 thread가 하나의 변수를 동시에 접근하려 하는 것을 통제하기 위해 주로 mutex와 semaphore가 사용됩니다.

 이번 포스팅에서는 C/C++ 언어를 기반으로 thread를 좀 더 섬세하게 다룰 수 있는 조건변수(Condition Variable)에 대해 살펴보도록 합니다.


 조건변수를 사용하게 되면 기존의 mutex로 통제하는 방식에서 좀 더 발전한 방식을 사용합니다. 이에 사용되는 thread 함수들을 간단하게 살펴보겠습니다.


int pthread_cond_wait( pthread_cond_t* cond, pthread_mutex_t* mutex );

 동작중인 thread를 잠시 중단시킵니다 condition과 mutex인자를 모두 적용합니다. 다른 thread로부터 signal이나 broadcast를 받았을 경우 해당 함수는 아래의 함수와 같은 동작을 하게 됩니다.

int pthread_mutex_lock(pthread_mutex_t *mutex);


int pthread_cond_signal(pthread_cond_t *cond);

 cond 인자를 가지고 pthread_cond_wait() 함수를 실행중인 하나의 thread를 깨웁니다. 만약 cond 인자를 잡고있는 thread가 다수일 경우 단 하나의 thread만 깨어납니다.


int pthread_cond_broadcast(pthread_cond_t *cond);

 cond 인자를 가지고 pthread_cond_wait() 함수를 실행중인 모든 thread를 깨웁니다. 만약 cond 인자를 잡고 있는 thread가 다수일 경우 mutex를 먼저 잡은 thread가 먼저 동작되며 나머지 thread는 mutex를 받을 때 까지 대기상태를 유지합니다.


int pthread_mutex_unlock(pthread_mutex_t *mutex);

 pthread_cond_signal()함수 혹은 pthread_cond_broadcast() 함수에 의해 깨어난 thread가 실행을 끝내고 종료하기 전에 자신이 가지고 있는 mutex를 풀어줍니다. 만약 해당 thread가 이를 실행하지 않고 넘어가게 되면 다른 thread는 mutex가 풀릴 때까지 대기해야 합니다.


다음으로 간단한 예제를 통해 동작을 확인해 보도록 하겠습니다.


 - pthread_cond_signal()을 사용한 예제

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
#include <iostream>
#include <pthread.h>
using namespace std;
 
void *thread(void *);
pthread_t    tid[4];
pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t    cond = PTHREAD_COND_INITIALIZER;
 
int main() {
    int i;
    int id[4];
 
    for(i = 0; i < 4; i++){
        id[i] = i;
        pthread_create(&tid[i],NULL,thread,(void*)&id[i]);
    }
 
    sleep(2);
    pthread_cond_signal(&cond);
 
    for(i = 0; i < 4 ; i++){
        pthread_join(tid[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
 
    return 0;
}
 
void *thread(void *arg){
    //pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    cout << "hello, world! from " <<  *((int*)arg) << endl;
    pthread_mutex_unlock(&mutex);
}
cs


 결과 :


 pthread_cond_signal() 함수가 실행되어 하나의 thread만 깨어나 수행을 종료하였습니다. 함수가 끝나기 전 pthread_mutex_unlock() 함수를 실행하였으나 다른 thread들은 깨어나지 못하여 프로그램은 계속 대기상태에 머무르게 됩니다.


 - pthread_cond_broadcast()를 사용한 예제

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
#include <iostream>
#include <pthread.h>
using namespace std;
 
void *thread(void *);
pthread_t    tid[4];
pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t    cond = PTHREAD_COND_INITIALIZER;
 
int main() {
    int i;
    int id[4];
 
    for(i = 0; i < 4; i++){
        id[i] = i;
        pthread_create(&tid[i],NULL,thread,(void*)&id[i]);
    }
 
    sleep(2);
    pthread_cond_broadcast(&cond);
 
    for(i = 0; i < 4 ; i++){
        pthread_join(tid[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
 
    return 0;
}
 
void *thread(void *arg){
    //pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    cout << "hello, world! from " <<  *((int*)arg) << endl;
    pthread_mutex_unlock(&mutex);
}
cs

결과 :


 pthread_cond_broadcast() 함수가 실행되어 모든 thread가 깨어났음을 확인할 수 있습니다. 깨어난 thread들은 마치 pthread_mutex_lock() 함수가 실행되는 것과 같은 상황이 되어 먼저 mutex를 얻은 thread가 동작을 수행한 후 pthread_mutex_unlock() 함수를 실행하여 깨어난 다른 thread들이 mutex를 잡고 동작을 수행할 수 있도록 해주는 것을 확인하실 수 있습니다.

300x250

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

안드로이드 Camera의 Framework 구조

안드로이드/카메라 2015. 8. 27. 19:13

 안드로이드의 카메라는 HAL과 Framework와 연결되어 있습니다. Camera를 활용하여 애플리케이션을 제작하는 분이라면 Framework 단계에서 만들어진 API를 활용하여 안드로이드의 Camera를 제어합니다. 안드로이드 카메라의 동작 원리를 안드로이드 운영체제의 구조를 통해 각 단계에서의 역할을 확인해 보도록 하겠습니다. 본 포스팅에서는 안드로이드 OS의 구조를 각 부분으로 나누어진 구조도로 살펴보겠습니다. 각 기능에 대한 자세한 내용은 본 포스팅에 일부 다루어진 부분이 있으니 참조해 주시길 바랍니다.





 위의 그림에서 붉은 글씨로 작성된 부분들에 대해 설명해 드리도록 하겠습니다.


Java Application

 우리들이 실제 사용하는 애플리케이션들이 작동되는 위치 입니다. 안드로이드 메인화면에서 카메라 애플리케이션 실행시 동작합니다. Java Framework에서 제공하는 API들을 활용하여 프로그램을 제작합니다.


Java Framework

  Application에게 카메라의 동작을 위한 API를 제공합니다. 주로 사용되는 클래스는 android.hardware.Camera API입니다. 이 API는 Camera의 실제 하드웨어와 연결됩니다. 소스코드가 Java로 구성되어 있어 Application 제작자들도 Java에 대한 이해가 깊다면 기능들을 쉽게 이해할 수 있습니다.


JNI(Java Native Interface)

 Java로 작성된 소스코드를 C/C++로 작성된 소스코드와 연결해주는 역할을 합니다. android.hardware.Camera의 JNI는 아래 경로에 있는 것을 확인하실 수 있습니다.


/frameworks/base/core/jni/android_hardware_Camera.cpp


 위 코드는 실제 카메라와의 연결된 low level의 native code를 호출하여 기능을 구현합니다. 해당 return 값을 Java Framework 단계에 있는 android.hardware.Camera에 전달합니다.


Native Framework

 해당 단계에서는 Java Framework 단계에 있는 android.hardware.Camera 클래스에게 native단계에서 구현된 기능을 제공합니다. 소스코드는 C++로 구성되어 있으며 Binder를 통해 camera service를 호출합니다. 해당 소스코드는 아래 경로에 있는 것을 확인하실 수 있습니다.


/frameworks/av/camera/Camera.cpp


HAL(Hardware Abstraction Layer)

 실제 안드로이드의 Camera 하드웨어와 C를 통해 긴밀하게 연결되어 있는 하드웨어 추상 단계입니다. 해당 단계에서는 Native Framework 단계에서의 camera service 클래스와 연결되어 있습니다. camera와 driver는 디바이스 기기의 화면을 통한 preview와 동영상 녹화 기능을 구현하기 위해 YV12와 NV21 이미지 포멧을 제공하니다.



출저 : http://source.android.com/devices/camera/

300x250

Handler와 Message를 활용하여 콜백함수 구현하기

 안드로이드 프레임워크를 분석하던 도중 흥미로운 부분을 발견하게 되어 이를 소개하고자 합니다. 물론 이는 안드로이드의 Application 단계에서도 쉽게 구현될 수 있는 기능이기에 안드로이드 애프리케이션 제작에 어느 정도 경험이 있으신 분들이라면 쉽게 이해하실 수 있으시리라 생각합니다.


 이번에 다루고자 하는 핵심적인 개념은 바로 Callback 입니다. 그럼 여기서 Callback 이란 무엇인지 간단하게 설명하도록 하겠습니다.


Callback이란?

 일반적으로 우리들이 프로그래밍을 설계할 때 Method와 같은 함수를 구현합니다. 특히 API와 같이 원하는 기능이 미리 구현되어 있어 해당 함수를 호출하는 것으로  원하는 기능을 실행하기도 하지요. Callback 또한 일반적인 함수들과 비슷하게 구성되어 있습니다. Android의 경우 프로그래머가 구현하고자 하는 기능을 Listener Interface를 통해 Callback 기능을 등록해줍니다.


 Callback 함수가 일반 함수와 가장 큰 차이점으로 호출되는 시점에 있습니다. 일반적인 함수의 경우 프로세스가 해당 함수를 호출하면 호출되는 즉시 해당 기능을 수행하게 됩니다. 반면 Callback 함수의 경우 프로세서가 호출을 요청할 경우 일반 함수처럼 즉시 호출될 수도 있지만 프로세스의 동작과는 독립적으로 동작하는 것이 가능하여 해당 프로세스가 수행을 종료한 후에 Callback 함수를 실행시킬 수 있습니다.


 안드로이드 Framework에서 Runnable Interface를 통해 Callback 기능을 구현한 방법이 있어 해당 기능을 분석해 보았습니다. 특이하게도 안드로이드에서 지원하는 Looper와 Message를 활용해서 구현하였다는 점인데요 쉽게 설명을 드리자면 실행하고자 하는 함수를 Runnable Interface를 통해 구현한 후 이를 Mssage를 통해 해당 함수를 예약해 두었다가 이후 프로세스가 작업을 종료하게 되었을 때 해당 함수를 호출하는 방식입니다.


 자세한 구현 내용을 실제 소스코드를 통해 확인해보도록 합시다.



/framework/base/core/java/android/view/Choreographer.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public final class Choreographer {
 
    ....
    // Choreographer를 초기와 합니다. 인자로 Choreographer의 Looper를 넘겨줍니다.
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper);
        }
    };
 
    ....
 
    private final Looper mLooper;
    private final FrameHandler mHandler;
 
    // The display event receiver can only be accessed by the looper thread to which
    // it is attached.  We take care to ensure that we post message to the looper
    // if appropriate when interacting with the display event receiver.
    private final FrameDisplayEventReceiver mDisplayEventReceiver;
 
    ....
 
    private Choreographer(Looper looper) {
    //Constructor를 통해 Looper를 받는다.
        mLooper = looper;
    //Constructor를 통하여 얻게 된 Looper를 FrameHadler에 등록한다.
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
 
        ....
 
        }
    }
 
    ....
 
}
 
 
//Callback 기능을 수행할 Hndler입니다.
//Callback 기능 구현시 handlerMessage(Message msg) 함수는 실행되지 않습니다.
private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
}
 
//Callback 기능을 구현하고자 하는 부분입니다.
//Runnable Interface로 구현하고자 하는 Callback 함수를 run() 함수로 구성합니다.
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
 
        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
 
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
 
        ....
 
            mTimestampNanos = timestampNanos;
            mFrame = frame;
 
            //Message를 통해 Callback 기능 구현
 
            //Callback 함수를 Message에 등록합니다.
            //mHandler    : Message를 받을 Looper를 갖고 있는 Handler
        //this    : 등록하고자 하는 함수. 해당 기능은 Runnable Interface를 통하여 run() 함수로 구성됨
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //Message를 Handler에 전송합니다. Handler는 Message를 받은 후 현재 프로세스가 작업을
            // 종료하면 이후 해당 Callback 기능을 실행합니다.
            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
        }
 
        //실행하고자 하는 Callback 함수
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
}
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