안드로이드 프레임워크 프로그래밍(28) [System Service의 proxy와 native의 Interface 상속 구조]

 안드로이드 프레임워크를 공부하는 데 있어 다루게 되는 내용 중 가장 중요한 것을 꼽아본다면 각 프로세스간의 통신 방식인 Binder의 활용이 아닐까 생각합니다. 실제로 안드로이드 운영체제 내에서 동작하는 System Service가 Android 애플리케이션 프로세스 상호간에 통신하기 위해서는 IPC(Interprocess Communication)통신의 일종인 Binder 통신을 사용합니다. 안드로이드 운영체제 또한 리눅스를 기반으로 만들어졌으므로 프로세스의 구조 또한 리눅스의 그것과 비슷하다고 할 수 있습니다. 다만, 기존 리눅스에서 사용하는 IPC 뿐 아니라 RPC(Remote Process Communication)를 사용해 AIDL(Android Interface Definition Language)를 통해 좀 더 간단하게 프로세스간 통신을 할 수 있게 하고 있습니다.



 위의 그램은 Application에서의 프로세스와 System Service에서의 프로세스가 Binder 드라이버를 통해 커널 상으로 통신하고 있는 모습을 그림으로 나타낸 것입니다. 본 포스팅에서는 위와 같은 Binder를 사용하기 위해 proxy와 native에서 Interface를 구성하는 과정에 대해 간단히 설명드리겠습니다. Android에서 proxy에 해당되는 프로세스는 앞부분에 bp를, native에 해당되는 프로세스는 bn을 붙여줍니다. 아마도 약자는 각각 binder proxy, binder native로 추정됩니다.


 먼저 System Service중 하나인 CameraService를 예를 들어 설명해 보겠습니다. 실제로 CameraService는 System Service의 일부로서 이를 사용하기 위해서는 Interface로 구성된 ICameraService를 통해 Binder를 통한 프로세스 통신을 해야 합니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
class ICameraService : public IInterface
{
public:
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


 ICameraService 클래스는 IInterface 클래스를 상속받고 있으며 내부에는 virtual 함수 connect()가 있습니다. 차후 connect()함수는 ICameraService 클래스를 상속하는 클래스에서 정의될 것입니다.


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

1
2
3
4
5
6
7
8
9
10
11
class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);
 
protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};
cs


 IInterface 클래스가 RefBase 클래스를 상속받고 


/frameworks/rs/cpp/util/RefBase.h

1
2
3
4
5
6
7
class RefBase
{
public:
 
...
 
}
cs


 RefBase 클래스는 안드로이드 native 단계에서의 framework 내에 있는 거의 대다수의 Class의 최상 부모입니다. 안드로이드에서는 이를 통해 각 클래스의 성질을 설정하고 있다고 생각해주시면 되겠습니다.


 그렇다면 위에서 설명해드렸던 ICameraService를 상속받는 클래스는 어떤 것이 있을까요? Application 프로세스가 System Service 프로세스와 통신하기 위해서는 총 2가지의 클래스를 사용하게 됩니다. Application 프로세스에서는 BpCameraService 클래스를, System Service 프로세스에서는 BnCameraService를 통해 Application 프로세스에서 요구하는 명령을 수행하게 됩니다.

 아래 소스코드는 ICameraService 클래스를 상속받는 BpCameraService의 모습을 나타냅니다.


/frameworks/av/camera/ICameraService.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
class BpCameraService: public BpInterface<ICameraService>
{
public:
    BpCameraService(const sp<IBinder>& impl)
        : BpInterface<ICameraService>(impl)
    {
    }
....
    // connect to camera service (android.hardware.Camera)
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                             const String16 &clientPackageName, int clientUid,
                             /*out*/
                             sp<ICamera>& device)
    {
        Parcel data, reply;
        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
        data.writeStrongBinder(cameraClient->asBinder());
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);
 
        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());
        }
        return status;
    }
....
}
cs

 소스코드를 보시면 아시듯이 BpCameraService는 BpInterface 클래스를 수식하는데 BpInterface 클래스는 ICameraService 클래스를 template로 설정하고 있는 것을 볼 수 있습니다. 그렇다면 BpInterface는 어떻게 생겼을까요? 


/frameworks/native/include/binder/IInterface.h
1
2
3
4
5
6
7
8
9
10
template<typename INTERFACE>
class BpInterface : public INTERFACEpublic BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);
 
protected:
    virtual IBinder*            onAsBinder();
};
 
cs

 놀랍게도 BpInterface는 template로 설저하였던 INTERFACE를 그대로 상속받는 것으로 설정하고 있습니다. 즉, 위의 경우 template인 INTERFACE는 ICameraService인 것이지요. 이렇게 BpInterface는 ICameraService와 BpRefBase 클래스를 다중상속 받고 있는 것입니다.

/frameworks/native/include/binder/Binder.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BpRefBase : public virtual RefBase
{
protected:
                            BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
 
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
 
private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);
 
    IBinder* const          mRemote;
    RefBase::weakref_type*  mRefs;
    volatile int32_t        mState;
};
cs


 BpRefBase 클래스 또한 RefBase를 상속받고 있습니다. 결과적으로 BpCameraService는 RefBase를 다이아몬드 상속으로 두 번 이상 상속받고 있는 것입니다. 그 때문인지 RefBase는 virtual 클래스로 상속받는 것으로 이해할 수 있습니다.


 위에서 설명한 소스코드의 상속 구조도를 이미지로 표현하면 다음과 같습니다. native에서의 상속 구조도 나타내 보았으니 여러분들께서 BnCameraService를 직접 확인해보시면 원리를 파악하는데 큰 도움이 되실 겁니다. :)






안드로이드 프레임워크 프로그래밍(27) [Fedora에서 AOSP 안드로이드 운영체제 컴파일하기]

 일반적으로 안드로이드 운영체제를 Build 할 때 거의 대부분의 경우 Ubuntu 환경에서 수행됩니다. AOSP 공식 홈페이지에서도 Ubuntu를 권장하고 있는 바이기도 합니다.

 그래도 혹여나 하는 마음에 Fedora 운영체제에서 안드로이드를 Build 해보는 과정에 대해 포스팅을 해보고자 합니다. 같은 RPM 패키지를 사용하는 Redhat이나 OpenSUSE에서도 이 포스팅의 내용을 적용할 수 있으리라 생각합니다.


Build Version    : Android 6.0.1(Marshmellow)

OS             : Fedora 23 (Twenty Three) 64-bit

JDK Version     : OpenJDK 1.7.0


1. 빌드하고자 하는 AOSP 소스코드를 다운로드 받습니다. 관련 내용에 대해 자세히 알아보고자 하시는 분은 아래 포스팅을 참조해 주기길 바랍니다.

http://elecs.tistory.com/56


$ mkdir ~/bin

$ export PATH=$PATH:~/bin

$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo

$ chmod a+x ~/bin/reop

$ mkdir ~/aosp 

$ cd ~/aosp

$ repo init -u https://android.googlesource.com/platform/manifest -b android-6.0.1_r1

$ repo sync -j4


 위 과정까지 마치셨다면 아래와 같이 aosp 폴더에 소스코드 다운로드 된 것을 확인하실 수 있습니다.


2. 다운로드한 안드로이드 소스코드를 컴파일할 수 있는 환경을 설정합니다. Marshmellow(6.0) 버전의 경우 OpenJDK 1.7.0을 설치해야 합니다. Oracle JDK로 컴파일을 시도하려 해도 시작하기 전에 컴파일이 중단되어버립니다. Fedora의 경우 dnf를 통해서는 최신 버전의 자바만 지원해주기 때문에 사용자가 직접 OpenJDK를 설치해야 합니다. 설치 방법은 아래의 포스팅을 참조해 주시기 바랍니다.


Fedora에 이전 버전의 OpenJDK 설치하기(Install OpenJDK 7 in Fedora 23)

http://elecs.tistory.com/166


만약 설치한 이후에도 해당 버전이 적용되어 있지 않았을 경우 아래의 명령어를 통해 직접 설치해줍시다.


# alternatives --install /usr/bin/java java /usr/lib/jvm/java-1.7.0-openjdk/bin/java 1

# alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-1.7.0-openjdk/bin/javac 1

# alternatives --install /usr/bin/javadoc javadoc /usr/lib/jvm/java-1.7.0-openjdk/bin/javadoc 1


3. dnf를 통해 안드로이드를 빌드하기 위해 필요한 패키지를 설치합니다. 


# dnf install bison gcc xorg-x11-fonts-Type1 libpng15 


4. 이제 다운로드 받은 소스코드를 build 하기 위한 준비과정을 진행해 보도록 하겠습니다. 먼저 build 환경을 초기화합니다.


$ ~/aosp

$ source build/envsetup.sh

$ lunch


 안드로이드 6.0.1 버전 기준으로 build 환경은 다음과 같이 나타납니다.


 여기서 자신이 build 하고자 하는 환경을 선택합니다. 본 포스팅의 경우 Android Studio를 통한 에뮬레이터에서의 실행을 목표로 함으로 1번 혹은 14번을 선택합니다. 혹시 자신이 build 하고자 하는 환경에 대한 설정을 알고 싶으신 분은 아래의 포스팅을 참조해 주시기 바랍니다.


안드로이드 프레임워크 프로그래밍(3) [NEXUS5에 소스 빌드하기]

http://elecs.tistory.com/59


5. 이제 build를 해보도록 합니다.


$ make update-api && make -j4


아래와 같은 결과가 나왔다면 Fedora 운영체제 환경에서 안드로이드 이미지를 빌드하는 데에 성공한 겁니다!


 


Windows 환경에서 fastboot를 통해 Android 이미지 포팅(Fastboot in Windows 7)

 Linux 환경에서 안드로이드 운영체제를 컴파일 하다 보니 안드로이드 이미지 포팅을 Linux로만 하게 되다보니 Windows 환경에서는 어떻게 하면 되는지 궁금했었는데 마침 기회가 되어 포스팅을 하게 되었습니다. 이번 포스팅에서는 Windows 환경에서 안드로이드 커널 이미지를 fastboot를 통해 설치해 보도록 합니다.


안드로이드 기기 : LG NEXUS 5

빌드 버전 : KitKat 4.4.4(r2)

운영체제 : Windows 7 SP1


 포스팅을 시작하기에 앞서 안드로이드 운영체제를 컴파일하여 이미지를 얻어내는 과정은 이전에 설명드렸던 포스팅을 참조해 주시길 바랍니다.


안드로이드 프레임워크 프로그래밍(3) [NEXUS5에 소스 빌드하기]

http://elecs.tistory.com/59


  1. 컴파일을 통해 완셩된 안드로이드 운영체제 이미지를 얻어냅니다. 이는 아래의 경로에서 얻으실 수 있습니다.


~/kitkat/out/target/product/hammerhead/


위에서 확인하실 수 있는 이미지(img) 파일 5개와 android-info.txt를 가져옵니다.


2. Windows 환경에서 Android SDK가 설치되어 있어야 합니다. Android SDK Manager를 실행하여 최하단 메뉴에 있는 Google USB Driver를 설치합니다.



3. 환경번수를 설정해줍니다. 컴퓨터에서 마우스 우측 클릭후 '속성'을 클릭합니다.



4. '고급 시스템 설정'을 클릭합니다.



5. 시스템 속성에서 '고급' 탭을 선택한 후 '환경 변수'를 클릭합니다.



6. 환경변수 창에서 시스템변수(S) 내에 Path를 더블클릭합니다.




7. 변수 값의 뒷부분에 다음 값을 추가합니다.


C:\Program Files\Android\android-sdk\platform-tools;


※주의!!

 절대로 변수 값의 내용을 지우시면 안됩니다! 만약 실수로 변수 값을 덮어쓰셨다면 바로 취소 버튼을 누릅니다. 만약 기존의 환경변수를 덮어쓰기로 모두 삭제할 경우 연결된 프로그램들이 동작을 하지 못하게 되는 참사가 발생합니다!

 만약 실수로 덮어쓰기 후 확인 버튼을 눌러 변수가 지워지신 분들은 절대로 당황하지 마시고 제가 이전에 포스팅한 내용을 따라가며 원래 환경 변수값을 복구하시길 바랍니다.


[윈도7] 환경변수 path 삭제시 복구하는 방법 ← 클릭하시면 새창으로 이동합니다.


========================================================================================================


8. 이번에는 안드로이드 운영체제 이미지가 위치하는 경로를 설정합니다. 아래 화면에서 '새로 만들기(W)'를 클릭합니다.

 

 

9. 새 시스템 변수의 변수 이름은 'ANDROID_PRODUCT_OUT'로, 변수 값은 안드로이드 커널 이미지가 위치한 파일 경로를 입력합니다. 본 포스팅에서는 E:\ 디스크에 안드로이드 커널 이미지를 두었습니다.



10. 이로서 Windows 환경에서 안드로이드 기기에 운영체제 이미지를 포팅할 준비를 마쳤습니다. 자신의 기기를 Fastboot 모드로 설정한 후 USB로 연결합니다.



11. Windows 운영체제가 안드로이드의 Fastboot 상태를 인식하게 되면 '드라이버 소프트웨어 설치'를 진행합니다. 아래와 같이 'Andorid Bootloader Interface'가 사용 준비 완료가 되기를 기다립니다.



12. CMD 창을 열어 다음과 같이 입력합니다.


> fastboot flashall



 위와 같은 화면이 나온다면 여러분들은 Windows 환경에서 fastboot를 통한 이미지 설치를 완료한 것입니다!


  • 세봉아 2015.11.27 10:42 ADDR 수정/삭제 답글

    벽돌된 것을 넥서스 5에 순정롬(6.0) 올리면 잘 동작됩니다

    질문 있습니다
    현재 제가 사용 하는 넥서스 5에 올려져 있는 버전이 안드로이드 6.0(마시멜로) 입니다
    리눅스에서 빌드 한 소스는 안드로이드 5.1.1.r3(Lollipop) 입니다
    넥서스 5에 맞게 빌드하기 위해 필요한 바이너리 파일과 팩토리 이미지는 모두 넥서스 5 기기와 동일한 6.0(마시멜로)으로 받아주었습니다.
    이렇게 하여 위 게시물 대로 진행 하였는데 현재 결과는 구글 무한반복 현상(벽돌만4회) 입니다
    버전을 일관성 있게 모두 맞춰야하나요?
    버전은 상관 없다는 분이 계셔서

    구글링했지만 비슷한 상황은 안보이네요.
    조언 좀 얻을 수 있을까요?

    • Justin T. 2015.11.27 11:00 신고 수정/삭제

      가장 기본적인 것은 자신에게 해당되는 기기와 버전을 모두 일치시키는 것이 기본입니다.
      즉, 롤리팝으로 올리시려면 바이너리 파일과 팩토리 파일 모두 롤리팝으로 맞추어주셔야 합니다. 물론 버전도 일치해야 하고요.

      그리고 자신의 기기보다 낮은 버전의 안드로이드를 설치하게 될 경우 로딩만 무한반복되는 현상이 종종 발견됩니다. 이 경우 공장초기화를 해주시면 정상적으로 동작하는 경우가 있습니다. 이는 아래 포스팅을 참조해주시기 바랍니다.
      http://elecs.tistory.com/63

  • 세봉아 2015.11.27 15:22 ADDR 수정/삭제 답글

    지금 버전을 맞추어서 이미지 포팅을 다시 해보려고 합니다.

    http://elecs.tistory.com/63
    상기 링크 확인하였습니다.
    그리고 구글 무한반복 현상은 저도 공장초기화를 여러 번 해주었는데 저는 해결되지 않았습니다.
    다른 경우 일수도 있겠다는 생각이 듭니다

안드로이드 프레임워크 프로그래밍(26) [System Service에서 Activity 함수 호출하기]

 안드로이드 AIDL을 사용하여 RPC통신을 통해 Application 단계에서 Framework 단계의 method를 호출하는 방법에 대해 알아본 바 있습니다. 이번 포스팅에서는 Framework 단계에서 Application의 Activity에 존재하는 method를 호출하는 방법에 대해 알아보도록 하겠습니다.


 포스팅을 시작하기에 앞서 이전에 사용하였던 Application 단계에서 Framework 단계의 기능을 사용한 바 있습니다. 이에 대한 자세한 내용은 아래 포스팅을 참조해주시길 바랍니다. 또한 본 포스팅의 예제 또한 아래 포스팅의 것을 사용하였음을 알립니다.


안드로이드 프레임워크 프로그래밍(4) [시스템서비스 추가하기]

http://elecs.tistory.com/64


 안드로이드 Application을 실행하였을 때 Framework 단계에서 실행하고자 하는 method를 onCreate() 단계에서 등록을 합니다. 그 이후 System Service에서 특정 부분을 호출 받았을 때 Application의 Activity 내에 있는 함수를 실행하는 과정을 구현할 것입니다.


1. AIDL을 통해 Callback 함수 등록 및 해제 함수를 만들어줍니다.


/frameworks/base/core/java/android/os/ITestService.aidl

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
/*
* aidl file : frameworks/base/core/java/android/os/ITestService.aidl
* This file contains definitions of functions which are exposed by service
*/
package android.os;
 
import android.os.IActivityServiceCallback;
 
interface ITestService {
/**
* {@hide}
*/
    void setValue(int val);
    void execCallback(int val);
 
    /**
     * Often you want to allow a service to call back to its clients.
     * This shows how to do so, by registering a callback interface with
     * the service.
     */
    boolean registerCallback(IActivityServiceCallback cb);
    
    /**
     * Remove a previously registered callback interface.
     */
    boolean unregisterCallback(IActivityServiceCallback cb);
 
}
cs

 Activity에서 실행하고자 하는 method는 registerCallback() method를 통해 System Service에 등록할 수 있습니다. execCallback()함수는 이후 Application에서 RPC를 통해 호출을 받은 후 Callback 기능을 구현하기 위해 만든 함수입니다.


2. Activity로부터 호출하고자 하는 함수를 구현할 IActivityServiceCallback.aidl을 생성합니다.


/frameworks/base/core/java/android/os/IActivityServiceCallback.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
package android.os;
 
/**
 * Example of a callback interface used by IActivityService to send
 * synchronous notifications back to its clients.  Note that this is a
 * one-way interface so the server does not block waiting for the client.
 */
oneway interface IActivityServiceCallback {
    /**
     * Called when the service has a new value for you.
     */
    void callActivityMethod(int value);
}
cs

 위의 과정에서 만들어진 callActivityMethod() method는 이후 Activity에서 Callback 방식으로 구현될 것입니다. 다음으로 System Serivce에서 위의 새로 생성한 함수들을 구현해 보도록 하겠습니다.


/frameworks/base/services/java/com/android/server/TestService.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
/*TestService.java */
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.IActivityServiceCallback;
import android.os.ITestService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
 
public class TestService extends ITestService.Stub {
    private static final String TAG = "TestService";
    private TestWorkerThread mWorker;
    private TestWorkerHandler mHandler;
    private Context mContext;
 
    RemoteCallbackList<IActivityServiceCallback> callbacks =
                 new RemoteCallbackList<IActivityServiceCallback>();
 
    public TestService(Context context) {
        super();
        mContext = context;
        mWorker = new TestWorkerThread("TestServiceWorker");
        mWorker.start();
        Log.i(TAG, "Spawned worker thread");
    }
 
    public boolean registerCallback(IActivityServiceCallback cb) throws RemoteException{
        boolean flag = false;
        if(cb != null){
            flag = callbacks.register(cb);
        }
        return flag;
    }
 
    public boolean unregisterCallback(IActivityServiceCallback cb) throws RemoteException{
        boolean flag = false;
        if(cb != null){
            flag = callbacks.unregister(cb);
        }
        return flag;
    }
 
 
    public void setValue(int val) {
        Log.i(TAG, "setValue " + val);
        Message msg = Message.obtain();
        msg.what = TestWorkerHandler.MESSAGE_SET;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    public void execCallback(int val){
     Message msg = Message.obtain();
        msg.what = TestWorkerHandler.CALLBACK;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    private class TestWorkerThread extends Thread {
        public TestWorkerThread(String name) {
            super(name);
        }
        public void run() {
            Looper.prepare();
            mHandler = new TestWorkerHandler();
            Looper.loop();
        }
    }
 
    private class TestWorkerHandler extends Handler {
        private static final int MESSAGE_SET = 0;
        private static final int CALLBACK = 1;
 
        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
                if (msg.what == CALLBACK) {
                    int n = callbacks.beginBroadcast();
                    for(int i = 0; i < n ; i++){
                        try {
                            Log.i(TAG, "callbacks.beginBroadcast()");
                            callbacks.getBroadcastItem(i).callActivityMethod(msg.arg1);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    callbacks.finishBroadcast();
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in TestWorkerHandler.handleMessage:", e);
            }
        }
    }
}
cs

 RemoteCallbackList 클래스를 통해 Activity로부터 callback을 등록한 후 execCallback() method가 Application으로부터 호출되면 곧 System Service에서 등록한 callback을 실행하게 됩니다. 아래는 이를 구현한 Application 소스코드입니다.


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
package com.example.test;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.IActivityServiceCallback;
import android.os.ITestService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.util.Log;
 
 
public class MainActivity extends Activity {
    private static final String DTAG = "HelloServer";
    ITestService om;
 
    IActivityServiceCallback mCallback = new IActivityServiceCallback.Stub() {
        
        @Override
        public void callActivityMethod(int arg0) throws RemoteException {
            // TODO Auto-generated method stub
            final int value = arg0;
            mHandler.post(new Runnable() {
 
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    setToast("callActivityMethod : " + value);
                }
            });
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
        try{
            Log.d(DTAG, "Going to call service");
            om.setValue(20);
            om.registerCallback(mCallback);
            Log.d(DTAG, "Service called successfully");
        }catch(Exception e){
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
        
    }
    
    public void OnClick(View v) throws RemoteException{
        switch(v.getId()){
            case R.id.button1:
                om.execCallback(100);
                break;
        }
    }
 
    void setToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
 
    protected void onDestroy(){
        super.onDestroy();
        try {
            om.unregisterCallback(mCallback);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
 
}
cs

 위 함수를 실행하면 다음과 같은 결과를 얻으실 수 있습니다.






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

 이전 포스팅을 통해 안드로이드의 VSync( Vertical Synchronization, 수직동기화)가 초기화 되어가는 과정을 차근차근 살펴보았습니다. 이번 시간에는 C++ 단계에서 Java Framework 단계로 VSync가 동작하는 과정을 살펴보도록 하겠습니다. 본 포스팅의 이해를 위해 이전에 작성해 왔던 포스팅들을 참조해 주시길 바랍니다.


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

http://elecs.tistory.com/134


SurfaceFlinger에서 DispSyncSource의 초기화 과정`

http://elecs.tistory.com/142


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

1
2
3
4
5
6
7
8
9
10
11
12
13
class DispSyncThread: public Thread {
 
....
 
    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
        for (size_t i = 0; i < callbacks.size(); i++) {
            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
        }
    }
 
....
    
};
cs

 DispSyncSource를 통해 설정하였던 Callback 함수인 onDispSyncEvent()를 호출합니다.


/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
class DispSyncSource : public VSyncSource, private DispSync::Callback {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) :
            mValue(0),
            mPhaseOffset(phaseOffset),
            mTraceVsync(traceVsync),
            mDispSync(dispSync) {}
 
    virtual ~DispSyncSource() {}
 
....
 
    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
        Mutex::Autolock lock(mMutex);
        mCallback = callback;
    }
 
private:
    virtual void onDispSyncEvent(nsecs_t when) {
        sp<VSyncSource::Callback> callback;
        {
            Mutex::Autolock lock(mMutex);
            callback = mCallback;
 
            if (mTraceVsync) {
                mValue = (mValue + 1) % 2;
                ATRACE_INT("VSYNC", mValue);
            }
        }
 
        if (callback != NULL) {
            callback->onVSyncEvent(when);
        }
    }
 
....
 
    DispSync* mDispSync;
    sp<VSyncSource::Callback> mCallback;
    Mutex mMutex;
};
 
 
cs

등록된 Callback 함수 onVSyncEvent()를 통해 EventThread 가 실행됩니다.


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

1
2
3
4
5
6
7
8
void EventThread::onVSyncEvent(nsecs_t timestamp) {
    Mutex::Autolock _l(mLock);
    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
    mVSyncEvent[0].header.id = 0;
    mVSyncEvent[0].header.timestamp = timestamp;
    mVSyncEvent[0].vsync.count++;
    mCondition.broadcast();
}
cs

EventThread의 Condition 클래스를 통해 broadcast()가 호출되어 wait() 상태에 있던 동작이 다시 재개됩니다.


/frameworks/native/services/surfaceflinger/EventThread.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
// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;
 
    do {
 
    ....
 
        // find out connections waiting for events
        size_t count = mDisplayEventConnections.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<Connection> connection(mDisplayEventConnections[i].promote());
            if (connection != NULL) {
                bool added = false;
                if (connection->count >= 0) {
                    // we need vsync events because at least
                    // one connection is waiting for it
                    waitForVSync = true;
                    if (timestamp) {
                        // we consume the event only if it's time
                        // (ie: we received a vsync event)
                        if (connection->count == 0) {
                            // fired this time around
                            connection->count = -1;
                            signalConnections.add(connection);
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            // continuous event, and time to report it
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }
 
                if (eventPending && !timestamp && !added) {
                    // we don't have a vsync event to process
                    // (timestamp==0), but we have some pending
                    // messages.
                    signalConnections.add(connection);
                }
            } else {
                // we couldn't promote this reference, the connection has
                // died, so clean-up!
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }
 
        ....
 
        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
 
            ....
 
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
                mCondition.wait(mLock);
            }
        }
    } while (signalConnections.isEmpty());
 
    // here we're guaranteed to have a timestamp and some connections to signal
    // (The connections might have dropped out of mDisplayEventConnections
    // while we were asleep, but we'll still have strong references to them.)
    return signalConnections;
}
cs

wait 상태에서 멈추어있던 waitForEvent() 함수가 broadcast 함수의 신호를 받고 다시 동작하며 waitForEvent() 함수의 실행을 종료시킵니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    signalConnections = waitForEvent(&event);
 
    // dispatch events to listeners...
    const size_t count = signalConnections.size();
    for (size_t i=0 ; i<count ; i++) {
        const sp<Connection>& conn(signalConnections[i]);
        // now see if we still need to report this event
        status_t err = conn->postEvent(event);
 
    ....
 
    }
    return true;
}
cs

waitForEvent() 함수가 동작되는 동안 수신된 이벤트에 대해 postEvent() 함수를 호출합니다.


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

1
2
3
4
5
status_t EventThread::Connection::postEvent(
        const DisplayEventReceiver::Event& event) {
    ssize_t size = DisplayEventReceiver::sendEvents(mChannel, &event, 1);
    return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
cs

DisplayEventReceiver 클래스 내에 있는 sendEvent() 함수를 호출합니다.


/frameworks/native/libs/gui/DisplayEventReceiver.cpp

1
2
3
4
5
ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
        Event const* events, size_t count)
{
    return BitTube::sendObjects(dataChannel, events, count);
}
cs

sendObjects() 함수를 통해 BitTube 클래스가 Pipe로 Looper와 연결한 File Descriptor에 값을 입력합니다.


/frameworks/native/libs/gui/DisplayEventReceiver.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
cs

write() 함수를 통해 Looper에 값을 입력합니다.


/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
26
27
28
29
30
31
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));
    }
}
 
....
 
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;
}
cs

 File Descriptor를 통해 데이터를 전송합니다. 이는 이후 Looper에서 감지하여 이벤트를 처리하게 됩니다. Looper에 DisplayEventReceiver에서 wirte를 시도하였을 때 이를 읽어들이는 File Descriptor는 아래의 방법으로 등록됩니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


/frameworks/native/libs/gui/DisplayEventReceiver.cpp

1
2
3
4
5
6
int DisplayEventReceiver::getFd() const {
    if (mDataChannel == NULL)
        return NO_INIT;
 
    return mDataChannel->getFd();
}
cs


/frameworks/native/libs/gui/BitTube.cpp

1
2
3
4
int BitTube::getFd() const
{
    return mReceiveFd;
}
cs

위의 과정을 통해 Looper에 File Descriptor가 등록되며 이는 Looper 내에서 epoll_wait()함수에 의해 감지되어 이벤트를 등록합니다.


/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
int Looper::pollInner(int timeoutMillis) {
 
....
 
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
....
 
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
 
....
 
}
cs

epoll_wait()에 의해 감지된 File Descriptor는 이후 등록된 이벤트와 함께 pushResponse() 함수를 통해 등록됩니다.


/system/core/libutils/Looper.cpp

1
2
3
4
5
6
void Looper::pushResponse(int events, const Request& request) {
    Response response;
    response.events = events;
    response.request = request;
    mResponses.push(response);
}
cs

pushResponse() 함수에 의해 등록된 이벤트는 이후 가지고 있는 Callback 함수를 수행합니다.


/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
int Looper::pollInner(int timeoutMillis) {
 
....
 
    // 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

DisplayEventReceiver 클래스를 통해 등록하였던 Callback 함수인 handleEvent() 함수를 실행합니다.


/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
int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        ALOGE("Display event receiver pipe was closed or an error occurred.  "
                "events=0x%x", events);
        return 0// remove the callback
    }
 
    if (!(events & ALOOPER_EVENT_INPUT)) {
        ALOGW("Received spurious callback for unhandled poll event.  "
                "events=0x%x", events);
        return 1// keep the callback
    }
 
    // Drain all pending events, keep the last vsync.
    nsecs_t vsyncTimestamp;
    int32_t vsyncDisplayId;
    uint32_t vsyncCount;
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, id=%d, count=%d",
                this, vsyncTimestamp, vsyncDisplayId, vsyncCount);
        mWaitingForVsync = false;
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }
 
    return 1// keep the callback
}
cs

Callback 함수로서 handleEvent()함수가 실행되며 이는 Java Framework에 있는 함수를 호출합니다.


/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
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
 
    ALOGV("receiver %p ~ Invoking vsync handler."this);
    env->CallVoidMethod(mReceiverObjGlobal,
            gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
    ALOGV("receiver %p ~ Returned from vsync handler."this);
 
    mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}
 
....
 
int register_android_view_DisplayEventReceiver(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver",
            gMethods, NELEM(gMethods));
    LOG_FATAL_IF(res < 0"Unable to register native methods.");
 
    FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver");
 
    GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync,
            gDisplayEventReceiverClassInfo.clazz,
            "dispatchVsync""(JII)V");
    GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchHotplug,
            gDisplayEventReceiverClassInfo.clazz,
            "dispatchHotplug""(JIZ)V");
    return 0;
}
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
    /**
     * Called when a vertical sync pulse is received.
     * The recipient should render a frame and then call {@link #scheduleVsync}
     * to schedule the next vertical sync pulse.
     *
     * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
     * timebase.
     * @param builtInDisplayId The surface flinger built-in display id such as
     * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
     * @param frame The frame number.  Increases by one for each vertical sync interval.
     */
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
    }
 
....
 
    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
        onVsync(timestampNanos, builtInDisplayId, frame);
    }
cs




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

 SurfaceFlinger의 동작 원리에 대해 좀 더 정확히 이해하기 위해서는 Native 단계에서 Looper의 동작 방식을 파악할 필요가 있습니다. 비록 Looper는 안드로이드의 System Library에 있지만 Native 단계에서의 Framework에서 상당히 중요하므로 간단하게 짚고 넘어가도록 하겠습니다.


 Looper란 폴링이 이루어지고 있는 루프(loop)로서 File Discriptor 이벤트를 관찰하는 역할을 하고, 등록된 Callback 함수를 호출하는 역할을 수행하기도 합니다. 실제로 Native 단계에서의 Looper는 UNIX 명령어 집합 epoll() 함수를 사용합니다.


/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
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0"Could not create wake pipe.  errno=%d", errno);
 
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
 
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0"Could not make wake read pipe non-blocking.  errno=%d",
            errno);
 
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0"Could not make wake write pipe non-blocking.  errno=%d",
            errno);
 
    mIdling = false;
 
    // Allocate the epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0"Could not create epoll instance.  errno=%d", errno);
 
    struct epoll_event eventItem;
    memset(& eventItem, 0sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0"Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
}
cs


 Looper 클래스의 생성자 소스코드를 살펴보시면 IPC 함수들이 눈에 띄는 것을 보실 수 있습니다. Looper의 Pipe File Descriptor를 Non-block로 설정되고 있는 모습 또한 확인하실 수 있습니다.


mEpollFd = epoll_create(EPOLL_SIZE_HINT);

 Looper에 등록된 File Descriptor를 관리할 epoll 인스턴스를 생성합니다. 인자로 인스턴스의 크기를 설정하며 return 값으로 해당 인스턴스의 FD를 돌려받습니다.


result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);

  epoll 인스턴스인 mEpollFd에 제어하고자 하는 File Descriptor인 mWakeReadPipeFd를 등록합니다.


 위의 과정을 거쳐 등록된 Looper는 이후 외부 이벤트를 통해 File Descriptor를 제어하게 됩니다. Looper가 생성된 이후 부터는 addFd()함수를 통해 File Descriptor가 등록됩니다.


/system/core/include/utils/Looper.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
/**
 * A looper callback.
 */
class LooperCallback : public virtual RefBase {
protected:
    virtual ~LooperCallback() { }
 
public:
    /**
     * Handles a poll event for the given file descriptor.
     * It is given the file descriptor it is associated with,
     * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT),
     * and the data pointer that was originally supplied.
     *
     * Implementations should return 1 to continue receiving callbacks, or 0
     * to have this file descriptor and callback unregistered from the looper.
     */
    virtual int handleEvent(int fd, int events, void* data) = 0;
};
 
....
 
/**
 * A polling loop that supports monitoring file descriptor events, optionally
 * using callbacks.  The implementation uses epoll() internally.
 *
 * A looper can be associated with a thread although there is no requirement that it must be.
 */
class Looper : public ALooper, public RefBase {
protected:
    virtual ~Looper();
 
....
 
private:
    struct Request {
        int fd;
        int ident;
        sp<LooperCallback> callback;
        void* data;
    };
 
    struct Response {
        int events;
        Request request;
    };
 
    struct MessageEnvelope {
        MessageEnvelope() : uptime(0) { }
 
        MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
                const Message& message) : uptime(uptime), handler(handler), message(message) {
        }
 
        nsecs_t uptime;
        sp<MessageHandler> handler;
        Message message;
    };
 
    const bool mAllowNonCallbacks; // immutable
 
    int mWakeReadPipeFd;  // immutable
    int mWakeWritePipeFd; // immutable
    Mutex mLock;
 
    Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
    bool mSendingMessage; // guarded by mLock
 
    // Whether we are currently waiting for work.  Not protected by a lock,
    // any use of it is racy anyway.
    volatile bool mIdling;
 
    int mEpollFd; // immutable
 
    // Locked list of file descriptor monitoring requests.
    KeyedVector<int, Request> mRequests;  // guarded by mLock
 
    // This state is only used privately by pollOnce and does not require a lock since
    // it runs on a single thread.
    Vector<Response> mResponses;
    size_t mResponseIndex;
    nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
 
    int pollInner(int timeoutMillis);
    void awoken();
    void pushResponse(int events, const Request& request);
 
    static void initTLSKey();
    static void threadDestructor(void *st);
};
cs


/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
int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
 
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;
}
cs

 Looper 이외의 클래스로부터 File Descriptor를 등록받게 되변 epoll을 통해 해당 File Descriptor를 등록하게 되고 해당 File Descriptor가 epoll에 의해 실행이 요청되었을 때, callback 함수가 동작할 수 있도록 Vector 변수인 mRequests에 등록됩니다.


/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
int Looper::pollInner(int timeoutMillis) {
 
....
 
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
    // No longer idling.
    mIdling = false;
 
    // Acquire lock.
    mLock.lock();
 
    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
        result = ALOOPER_POLL_ERROR;
        goto Done;
    }
 
    // Check for poll timeout.
    if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - timeout"this);
#endif
        result = ALOOPER_POLL_TIMEOUT;
        goto Done;
    }
 
    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - handling events from %d fds"this, eventCount);
#endif
 
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
 
....
 
}
cs

 Looper에서 pollInner()함수가 호출되면 epoll을 통해 등록되었던 File Descriptor를 통해 이벤트가 들어왔는지 확인한 후 이를 처리하도록 합니다.


int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

 epoll에 등록된 File Descriptor 중 파일 읽기가 준비된 FD의 갯수를 return 합니다. 이후 준비된 이벤트를 처리하는 과정을 거치게 됩니다.


awoken();

 만약 준비된 File Descriptor가 Looper의 것일 경우 위 함수를 실행합니다.


/system/core/libutils/Looper.cpp

1
2
3
4
5
6
7
8
9
10
11
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ awoken"this);
#endif
 
    char buffer[16];
    ssize_t nRead;
    do {
        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
cs


pushResponse(events, mRequests.valueAt(requestIndex));

 만약 준비된 File Descriptor가 addFd에 의해 등록된 것의 경우 위 함수를 실행합니다.


/system/core/libutils/Looper.cpp

1
2
3
4
5
6
void Looper::pushResponse(int events, const Request& request) {
    Response response;
    response.events = events;
    response.request = request;
    mResponses.push(response);
}
cs


 위에서 보시는 바와 같이 mResponses에 이벤트가 등록되는 것을 확인하였습니다. 이제 해당 이벤트의 Callback이 호출되는 과정을 살펴보도록 하겠습니다.


/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
int Looper::pollInner(int timeoutMillis) {
 
....
 
    // 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


 위 함수를 통해 Looper에 등록된 Callback 함수가 실행됨을 확인할 수 있습니다.

SurfaceFlinger에서 DispSyncSource의 초기화 과정

 DisplaySyncSource 클래스는 SurfaceFlinger.cpp 내에 소스코드가 존재합니다. 이는 이후 안드로이드의 VSync(수직동기화)를 수행하기 위해 따로 만들어지는 EventThread에서 적합하게 사용될 수 있도록 설계되어 있습니다. 전반적인 소스코드를 분석해 보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
    // 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);
cs

 위의 DispSyncSource 생성자 내의 mPrimaryDispSync 변수는 DispSync 클래스의 변수입니다. DispSync 클래스에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.


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

http://elecs.tistory.com/134


 위의 소스코드를 통해 DisySyncSource 클래스가 VSync를 수행하기 위한 Thread를 위해 실행되고 있는 것을 알 수 있습니다. 위에서 생성되고 있는 DispSyncSource 클래스를 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/EventThread.h
1
2
3
4
5
6
7
8
9
10
11
12
class VSyncSource : public virtual RefBase {
public:
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {}
        virtual void onVSyncEvent(nsecs_t when) = 0;
    };
 
    virtual ~VSyncSource() {}
    virtual void setVSyncEnabled(bool enable) = 0;
    virtual void setCallback(const sp<Callback>& callback) = 0;
};
cs


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

1
2
3
4
5
6
7
8
9
10
11
12
13
class DispSync {
 
public:
 
    class Callbackpublic virtual RefBase {
    public:
        virtual ~Callback() {};
        virtual void onDispSyncEvent(nsecs_t when) = 0;
    };
 
....
 
}
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
52
53
54
55
56
57
58
59
60
61
62
63
64
class DispSyncSource : public VSyncSourceprivate DispSync::Callback {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) :
            mValue(0),
            mPhaseOffset(phaseOffset),
            mTraceVsync(traceVsync),
            mDispSync(dispSync) {}
 
    virtual ~DispSyncSource() {}
 
    virtual void setVSyncEnabled(bool enable) {
        // Do NOT lock the mutex here so as to avoid any mutex ordering issues
        // with locking it in the onDispSyncEvent callback.
        if (enable) {
            status_t err = mDispSync->addEventListener(mPhaseOffset,
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error registering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            ATRACE_INT("VsyncOn"1);
        } else {
            status_t err = mDispSync->removeEventListener(
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error unregistering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            ATRACE_INT("VsyncOn"0);
        }
    }
 
    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
        Mutex::Autolock lock(mMutex);
        mCallback = callback;
    }
 
private:
    virtual void onDispSyncEvent(nsecs_t when) {
        sp<VSyncSource::Callback> callback;
        {
            Mutex::Autolock lock(mMutex);
            callback = mCallback;
 
            if (mTraceVsync) {
                mValue = (mValue + 1) % 2;
                ATRACE_INT("VSYNC", mValue);
            }
        }
 
        if (callback != NULL) {
            callback->onVSyncEvent(when);
        }
    }
 
    int mValue;
 
    const nsecs_t mPhaseOffset;
    const bool mTraceVsync;
 
    DispSync* mDispSync;
    sp<VSyncSource::Callback> mCallback;
    Mutex mMutex;
};
cs

 위 과정을 거쳐 이후 EventThread를 통해 해당 내용들이 등록되게 됩니다. EventThread에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.

SurfaceFlinger에서 EventThread의 초기화 과정

http://elecs.tistory.com/140


 이후 EventThread가 초기화가 이루어진 후 waitForEvent() 함수가 호출됩니다. 이 때, 해당 함수 내에 있는 enableVSyncLocked() 함수가 실행되면 DispSyncSource에 Callback이 등록됩니다.

 

/frameworks/native/services/surfaceflinger/EventThread.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
// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;
 
    do {
 
....
 
    // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            // we received a VSYNC but we have no clients
            // don't report it, and disable VSYNC events
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            // we have at least one client, so we want vsync enabled
            // (TODO: this function is called right after we finish
            // notifying clients of a vsync, so this call will be made
            // at the vsync rate, e.g. 60fps.  If we can accurately
            // track the current state we could avoid making this call
            // so often.)
            enableVSyncLocked();
 
....
 
}
 
....
 
void EventThread::enableVSyncLocked() {
    if (!mUseSoftwareVSync) {
        // never enable h/w VSYNC when screen is off
        if (!mVsyncEnabled) {
            mVsyncEnabled = true;
            mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
            mVSyncSource->setVSyncEnabled(true);
            mPowerHAL.vsyncHint(true);
        }
    }
    mDebugVsyncEnabled = true;
}
cs


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

1
2
3
4
    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
        Mutex::Autolock lock(mMutex);
        mCallback = callback;
    }
cs


 위 과정을 통하여 VSyncSource에 callback 함수가 등록됩니다. 해당 callback 함수는 아래와 같습니다.


/frameworks/native/services/surfaceflinger/EventThread.h

1
2
3
4
5
6
7
8
9
10
11
12
class VSyncSource : public virtual RefBase {
public:
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {}
        virtual void onVSyncEvent(nsecs_t when) = 0;
    };
 
    virtual ~VSyncSource() {}
    virtual void setVSyncEnabled(bool enable) = 0;
    virtual void setCallback(const sp<Callback>& callback) = 0;
};
cs

 

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

1
2
3
4
5
6
7
8
void EventThread::onVSyncEvent(nsecs_t timestamp) {
    Mutex::Autolock _l(mLock);
    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
    mVSyncEvent[0].header.id = 0;
    mVSyncEvent[0].header.timestamp = timestamp;
    mVSyncEvent[0].vsync.count++;
    mCondition.broadcast();
}
cs

 Callback이 등록된 후 setVSyncEnable() 함수를 통해 DispSync Thread에 Listener를 등록합니다.


/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
    virtual void setVSyncEnabled(bool enable) {
        // Do NOT lock the mutex here so as to avoid any mutex ordering issues
        // with locking it in the onDispSyncEvent callback.
        if (enable) {
            status_t err = mDispSync->addEventListener(mPhaseOffset,
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error registering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            ATRACE_INT("VsyncOn"1);
        } else {
            status_t err = mDispSync->removeEventListener(
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error unregistering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            ATRACE_INT("VsyncOn"0);
        }
    }
cs

 위의 과정을 통해 DisySync 클래스의 addEventListener()함수를 통해 DisySync의 callback 함수가 등록되는 것을 보실 수 있습니다. 소스코드는 다음과 같습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    virtual void onDispSyncEvent(nsecs_t when) {
        sp<VSyncSource::Callback> callback;
        {
            Mutex::Autolock lock(mMutex);
            callback = mCallback;
 
            if (mTraceVsync) {
                mValue = (mValue + 1) % 2;
                ATRACE_INT("VSYNC", mValue);
            }
        }
 
        if (callback != NULL) {
            callback->onVSyncEvent(when);
        }
    }
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

 위의 소스코드를 통해 DispSyncThread에 Event가 등록되는 것을 확인하실 수 있습니다.


/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
class DispSyncThread: public Thread {
 
....
 
    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

DispSync에 등록된 이벤트들은 이후 DispSyncThread에서 VSync 이벤트 Callback 발생시 실행됩니다.


/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
class DispSyncThread: public Thread {
 
....
 
    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

 위의 과정을 거쳐 EventThread의 Callback 함수를 수행하게 되면 broadcast()함수에 의해 EventThread 내의 Thread들이 모두 깨어나게 됩니다. 이는 이후 postEvent() 함수를 호출하게 됩니다.






SurfaceFlinger의 초기화 과정 흐름도

 ※이 흐름도는 Android 4.4.4 r2 kitkat을 기준으로 만들어진 흐름도입니다.




출저 : http://www.programering.com/a/MDO1EzMwATc.html

SurfaceFlinger에서 EventThread의 초기화 과정

 SurfaceFlinger가 수직동기화(VSync)를 수행하기 위해 이를 담당하는 별도의 Thread를 만들어 줄 필요가 있습니다. EventThread 클래스가 바로 그러한 역할을 담당하고 있다고 보면 되겠습니다. 이번 포스팅에서 EventThread의 초기화 과정을 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/EventThread.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
class VSyncSource : public virtual RefBase {
public:
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {}
        virtual void onVSyncEvent(nsecs_t when) = 0;
    };
 
    virtual ~VSyncSource() {}
    virtual void setVSyncEnabled(bool enable) = 0;
    virtual void setCallback(const sp<Callback>& callback) = 0;
};
 
class EventThread : public Thread, private VSyncSource::Callback {
    class Connection : public BnDisplayEventConnection {
    public:
        Connection(const sp<EventThread>& eventThread);
        status_t postEvent(const DisplayEventReceiver::Event& event);
 
        // count >= 1 : continuous event. count is the vsync rate
        // count == 0 : one-shot event that has not fired
        // count ==-1 : one-shot event that fired this round / disabled
        int32_t count;
 
    private:
        virtual ~Connection();
        virtual void onFirstRef();
        virtual sp<BitTube> getDataChannel() const;
        virtual void setVsyncRate(uint32_t count);
        virtual void requestNextVsync();    // asynchronous
        sp<EventThread> const mEventThread;
        sp<BitTube> const mChannel;
    };
 
public:
 
    EventThread(const sp<VSyncSource>& src);
 
    sp<Connection> createEventConnection() const;
    status_t registerDisplayEventConnection(const sp<Connection>& connection);
 
    void setVsyncRate(uint32_t count, const sp<Connection>& connection);
    void requestNextVsync(const sp<Connection>& connection);
 
    // called before the screen is turned off from main thread
    void onScreenReleased();
 
    // called after the screen is turned on from main thread
    void onScreenAcquired();
 
    // called when receiving a hotplug event
    void onHotplugReceived(int type, bool connected);
 
    Vector< sp<EventThread::Connection> > waitForEvent(
            DisplayEventReceiver::Event* event);
 
    void dump(String8& result) const;
 
private:
    virtual bool        threadLoop();
    virtual void        onFirstRef();
 
    virtual void onVSyncEvent(nsecs_t timestamp);
 
    void removeDisplayEventConnection(const wp<Connection>& connection);
    void enableVSyncLocked();
    void disableVSyncLocked();
 
    // constants
    sp<VSyncSource> mVSyncSource;
    PowerHAL mPowerHAL;
 
    mutable Mutex mLock;
    mutable Condition mCondition;
 
    // protected by mLock
    SortedVector< wp<Connection> > mDisplayEventConnections;
    Vector< DisplayEventReceiver::Event > mPendingEvents;
    DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
    bool mUseSoftwareVSync;
    bool mVsyncEnabled;
 
    // for debugging
    bool mDebugVsyncEnabled;
};
cs


/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
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;
        };
    };
 
....
 
}
cs


 전체적인 맥락을 파악하기 위해 EventThread의 전체 헤더 소스코드를 보았습니다. 이번에는 EventThread가 초기화 되는 과정을 살펴보도록 하겠습니다.


/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
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);
 
....
 
}
cs

 SurfaceFlinger에서 EventThread가 초기화 되는 부분입니다. 여기서 mEventThread 클래스 변수가 초기화 되는 과정을 살펴보도록 하겠습니다.


sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,

            vsyncPhaseOffsetNs, true);


 수직동기화의 Source를 설정하는 부분입니다. 해당 부분은 다음과 같이 구성되어 있습니다.


/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
class DispSyncSource : public VSyncSource, private DispSync::Callback {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) :
            mValue(0),
            mPhaseOffset(phaseOffset),
            mTraceVsync(traceVsync),
            mDispSync(dispSync) {}
 
    virtual ~DispSyncSource() {}
 
....
 
private:
 
....
 
    int mValue;
 
    const nsecs_t mPhaseOffset;
    const bool mTraceVsync;
 
    DispSync* mDispSync;
    sp<VSyncSource::Callback> mCallback;
    Mutex mMutex;
};
cs


mEventThread = new EventThread(vsyncSrc);

 이전 줄에서 설정하였던 수직동기화 정보를 기반으로 EventThread 생성자를 실행합니다.


/frameworks/native/services/surfaceflinger/EventThread.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
EventThread::EventThread(const sp<VSyncSource>& src)
    : mVSyncSource(src),
      mUseSoftwareVSync(false),
      mVsyncEnabled(false),
      mDebugVsyncEnabled(false) {
 
    for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
        mVSyncEvent[i].header.id = 0;
        mVSyncEvent[i].header.timestamp = 0;
        mVSyncEvent[i].vsync.count =  0;
    }
}
 
void EventThread::onFirstRef() {
    run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
}
 
....
 
bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    signalConnections = waitForEvent(&event);
 
    // dispatch events to listeners...
    const size_t count = signalConnections.size();
    for (size_t i=0 ; i<count ; i++) {
        const sp<Connection>& conn(signalConnections[i]);
        // now see if we still need to report this event
        status_t err = conn->postEvent(event);
        if (err == -EAGAIN || err == -EWOULDBLOCK) {
            // The destination doesn't accept events anymore, it's probably
            // full. For now, we just drop the events on the floor.
            // FIXME: Note that some events cannot be dropped and would have
            // to be re-sent later.
            // Right-now we don't have the ability to do this.
            ALOGW("EventThread: dropping event (%08x) for connection %p",
                    event.header.type, conn.get());
        } else if (err < 0) {
            // handle any other error on the pipe as fatal. the only
            // reasonable thing to do is to clean-up this connection.
            // The most common error we'll get here is -EPIPE.
            removeDisplayEventConnection(signalConnections[i]);
        }
    }
    return true;
}
cs


 위의 소스코드에서 waitForEvent() 소스코드를 통해 EventThread의 연결과정이 드러나고 있습니다. 해당 부분을 살펴보면 다음과 같습니다.


/frameworks/native/services/surfaceflinger/EventThread.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
122
123
124
125
126
127
128
129
130
131
132
133
134
// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;
 
    do {
        bool eventPending = false;
        bool waitForVSync = false;
 
        size_t vsyncCount = 0;
        nsecs_t timestamp = 0;
        for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
            timestamp = mVSyncEvent[i].header.timestamp;
            if (timestamp) {
                // we have a vsync event to dispatch
                *event = mVSyncEvent[i];
                mVSyncEvent[i].header.timestamp = 0;
                vsyncCount = mVSyncEvent[i].vsync.count;
                break;
            }
        }
 
        if (!timestamp) {
            // no vsync event, see if there are some other event
            eventPending = !mPendingEvents.isEmpty();
            if (eventPending) {
                // we have some other event to dispatch
                *event = mPendingEvents[0];
                mPendingEvents.removeAt(0);
            }
        }
 
        // find out connections waiting for events
        size_t count = mDisplayEventConnections.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<Connection> connection(mDisplayEventConnections[i].promote());
            if (connection != NULL) {
                bool added = false;
                if (connection->count >= 0) {
                    // we need vsync events because at least
                    // one connection is waiting for it
                    waitForVSync = true;
                    if (timestamp) {
                        // we consume the event only if it's time
                        // (ie: we received a vsync event)
                        if (connection->count == 0) {
                            // fired this time around
                            connection->count = -1;
                            signalConnections.add(connection);
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            // continuous event, and time to report it
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }
 
                if (eventPending && !timestamp && !added) {
                    // we don't have a vsync event to process
                    // (timestamp==0), but we have some pending
                    // messages.
                    signalConnections.add(connection);
                }
            } else {
                // we couldn't promote this reference, the connection has
                // died, so clean-up!
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }
 
        // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            // we received a VSYNC but we have no clients
            // don't report it, and disable VSYNC events
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            // we have at least one client, so we want vsync enabled
            // (TODO: this function is called right after we finish
            // notifying clients of a vsync, so this call will be made
            // at the vsync rate, e.g. 60fps.  If we can accurately
            // track the current state we could avoid making this call
            // so often.)
            enableVSyncLocked();
        }
 
        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
                // This is where we spend most of our time, waiting
                // for vsync events and new client registrations.
                //
                // If the screen is off, we can't use h/w vsync, so we
                // use a 16ms timeout instead.  It doesn't need to be
                // precise, we just need to keep feeding our clients.
                //
                // We don't want to stall if there's a driver bug, so we
                // use a (long) timeout when waiting for h/w vsync, and
                // generate fake events when necessary.
                bool softwareSync = mUseSoftwareVSync;
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    if (!softwareSync) {
                        ALOGW("Timed out waiting for hw vsync; faking it");
                    }
                    // FIXME: how do we decide which display id the fake
                    // vsync came from ?
                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
                    mVSyncEvent[0].vsync.count++;
                }
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
                mCondition.wait(mLock);
            }
        }
    } while (signalConnections.isEmpty());
 
    // here we're guaranteed to have a timestamp and some connections to signal
    // (The connections might have dropped out of mDisplayEventConnections
    // while we were asleep, but we'll still have strong references to them.)
    return signalConnections;
}
cs

 위의 waitForEvent() 함수의 실행이 종료되면 이후에 Connection 클래스가 생성됩니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
 
EventThread::Connection::~Connection() {
    // do nothing here -- clean-up will happen automatically
    // when the main thread wakes up
}
 
void EventThread::Connection::onFirstRef() {
    // NOTE: mEventThread doesn't hold a strong reference on us
    mEventThread->registerDisplayEventConnection(this);
}
cs

 위의 Connection 클래스가 생성되면서 mChannel을 통해 File Descriptor을 통해 프로세스간 통신을 수행하는 BitTube 클래스가 새로 생성됩니다. BitTube에 대한 자세한 내용은 이전의 포스팅을 참조해주시기 바랍니다.


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

http://elecs.tistory.com/138


 Connection 클래스의 onFirstRef()함수에 의해 EventThread 클래스의 registerDisplayEventConnection()함수가 호출됩니다.


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

1
2
3
4
5
6
7
8
status_t EventThread::registerDisplayEventConnection(
        const sp<EventThread::Connection>& connection) {
    Mutex::Autolock _l(mLock);
    mDisplayEventConnections.add(connection);
    mCondition.broadcast();
    return NO_ERROR;
}
 
cs

 EventThread에 EventThread의 Connection 클래스를 벡터에 등록한 후 멈추어 있던 Thread들을 모두 깨웁니다. 위의 과정을 통해 생성된 Connection 클래스는 이후 postEvent() 함수를 수행하게 됩니다.


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

1
2
3
4
5
status_t EventThread::Connection::postEvent(
        const DisplayEventReceiver::Event& event) {
    ssize_t size = DisplayEventReceiver::sendEvents(mChannel, &event, 1);
    return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
cs


/frameworks/native/libs/gui/DisplayEventReceiver.cpp

1
2
3
4
5
ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
        Event const* events, size_t count)
{
    return BitTube::sendObjects(dataChannel, events, count);
}
cs


  • 제트블랙 2015.11.14 14:34 ADDR 수정/삭제 답글

    임베디드 기기 포팅중인데요.
    화면이 안나와서 보니
    EventThread::threadLoop 에서 EventThread: dropping event 로그가 계속 납니다.
    이런 경우 뭐가 문제일까요. 혹시 해결 방법아시면 알려주세요..

안드로이드 프레임워크 프로그래밍(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


  • 김도우 2015.09.08 08:17 ADDR 수정/삭제 답글

    안녕하세요 고생이 많으십니다~
    안드로이드 프레임워크 빌드시 다른 패키지에 있는 클래스를 불러와 사용하려고합니다
    즉,com.android.phone.PhoneGlobals.java 클래스를 다른 패키지 클래스에서 쓰려고하는데
    임포트는 에러 없이 되었는데 컴파일시 존재하지않는다고 에러가 발생해서요~~해결방법이 없을까요??

    • Justin T. 2015.09.08 09:37 신고 수정/삭제

      안녕하세요. 해당 문제는 에러가 떴을 때의 로그를 확인해보아야 원인을 알 수 있을 듯 합니다.
      그리고 해당글과 관련성이 떨어지는 댓글은 원칙적으로 답변을 하지 않으며 문의사항은 방명록을 이용해주시기 바랍니다.

안드로이드 프레임워크 프로그래밍(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()함수가 호출되는 것을 확인하실 수 있습니다.

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



  • 낙은헤 2016.02.02 17:42 ADDR 수정/삭제 답글

    와... 안드로이드 프레임웍을 이렇게 깊이있게 다룬 글은 처음 보네요
    좋은자료 정말 감사드립니다. 지금 수준으로는 이해가 어려운 부분이 많지만 틈틈이 찾아와서 공부해야 겠네요^^

    이 게시글과 직접적인 관련이 있는 것은 아니지만, 질문 하나 드려도 될까요?
    View 의 invalidate() 메소드를 호출해서 다시 그리기를 구현하고 있는데,
    사용자의 입력이 유지되면 invalidate() 가 연속적으로 호출되기 때문에,
    초당 60번 이상 호출이 되지 않도록 코드를 수정하고 있었습니다.
    그런데 지인의 이야기로는, 어차피 Choreographer 클래스가 VSync 신호에 맞춰서 화면을 갱신하기 때문에 그런 작업이
    불필요 하다고 하더라고요.
    하지만 invalidate() 내부에서 어떤 작업을 하는지 구체적으로 잘 모르기 때문에 찜찜하네요.
    화면 갱신을 요청하는 작업 하나뿐이라면야 여러번 호출하는 것이 크게 상관은 없겠지만,
    그외에 부가적인 작업이 동반된다면 한번의 VSync 주기 내에서 불필요한 오버헤드가 발생하게 될테니까요.

    저는 원래 어떤 프로그래밍을 하든간에 함수 호출도 오버헤드라는 생각을 하면서 가능한한 함수호출은 최소한으로
    하자는 철학(?)이 있어서 무엇이 옳은 것인지 확신이 서지 않습니다.
    사실 시간 간격을 확인하려고 해도 어차피 시간 확인을 위한 메소드를 호출해야하기 때문에 이번의 경우는 더더욱
    애매합니다.
    invalidate() 반복 호출에서 부하가 발생하지 않는다면, 코드 가독성 측면에서도 그냥 invalidate() 를 사용하는 것이
    맞을 것 같기도 합니다.
    어떻게 생각하세요???

  • Justin T. 2016.02.04 02:03 신고 ADDR 수정/삭제 답글

    안드로이드 기기의 화면과 수직동기화, 즉 VSync를 하는 이유는 하드웨어적인 측면이 강합니다.
    애초에 우리가 보는 스마트폰의 화면은 정지된 화면이더라도 초당 60회의 변화가 일어나고 있습니다. Vsync 또한 이러한 하드웨어의 특성에 맞게 설계된 것이고요.
    질문자께서 말씀하신대로 화면에 변화가 없는 상태에서 VSync가 발생하면 불필요한 오버헤드가 발생한다는 것은 사실입니다.
    그러나 VSync 신호 자체는 하드웨어 단계에서의 Interrupt가 발생하고 있는 것과 같은 상황이기 때문에 이를 소프트웨어 단계에서 제어하기에는 상당한 어려움이 있습니다. 설령 되는 방법을 찾았다 하더라도 가장 쉬운 방법은 하드웨어를 직접 다시 디자인하는 방법이라 생각됩니다 ㅎㅎ
    수직동기화를 해주어야 하는 이유에 대해 자세히 찾아보시면 답은 나올것이라 생각됩니다.

  • 낙은헤 2016.02.05 21:40 ADDR 수정/삭제 답글

    답변 감사드립니다~ 좋은 하루 되세요:)

  • 2016.09.22 22:46 ADDR 수정/삭제 답글

    안녕하세요. 몇달전 글이지만, 혹시 보실까 싶어 글 남깁니다.
    저는 app정도만 개발해보았는데요. 최근에 성능테스트 때문에 vsync 를 끄고 싶은데, 방법을 찾다가 이곳까지 왔습니다.
    처음엔 openGL 함수로 제어가능할거라 생각했었는데, 그게 아닌것 같아서요.
    윗글에서 말씀하신것처럼 하드웨어문제로 볼수 있어서, 안드로이드 함수를 통해 꺼야할것 같은데, 가능할까요?
    혹시 방법을 아시거나 reference할만한 곳 알려주시면 감사하겠습니다.

    조금 더 말씀드리자면, 최대 frame rate(fps)를 계산하고 싶은 상황입니다. opengl의 rendering mode를 RENDERMODE_CONTINUOUSLY 로 설정해도 자체적으로 60fps를 지키는 것같습니다. 아주 심플한 rendering을 해도 60fps보다 높아지지 않더라고요. 제생각에는 vsync때문에 더 많이 자주 그릴 수 있어도 딱 60번만 하는 것같습니다.
    그래서 vsync를 끄고 최대한 얼마나 자주 그리나 알아보고 싶은 상황입니다.
    잘 전달이 됐나 모르겠네요.
    그럼 좋은하루 되세요.

    • Justin T. 2016.09.23 00:49 신고 수정/삭제

      반갑습니다.
      단도직입적으로 안드로이드의 vsync 기능은 운영체제 내 프레임워크 단계를 수정해야 하기 때문에 대단히 어려운 작업입니다. 이를 수정하려면 관련된 소스코드의 동작과정을 모두 이해하셔야 겨우 수정이 가능하며 다른 알고리즘들과 엮여서 동작하기 때문에 vsync 기능만 따로 설정하는 것은 상당히 어려운 작업이 될 것이라는 것이 제 의견입니다.
      속시원한 답변을 드리지 못한 점 죄송합니다.