Linux(우분투)에서 Android Studio 빠르게 실행하는 방법(바로가기 추가하기)

 안드로이드가 주로 애플리케이션 제작 도구로서 사용해오던 Eclipse에 대한 지원을 종료하게 됨에 따라 앞으로 안드로이드 Application을 개발하기 위해서는 안드로이드에서 자체적으로 제공하고 있는 IDE인 안드로이드 스튜디오(Android Studio)를 사용하는 것이 불가피하게 되었습니다.



안드로이드 애플리케이션 제작에 최적화 된 IDE인 Android Studio.

Eclipse에 익숙한 사용자들도 다양한 편의 기능에 개발 환경이 개선되었음을 체감할 수 있다.


 Windows 버전의 경우 필수 설치 프로그램만 설정되어 있다면 바로 설치 후 이용하실 수 있습니다만 Linux의 경우 studio.sh 파일 방식으로 실행을 해야 하기 때문에 Terminal을 사용해야 한다는 불편한 점이 있습니다.



기존 Eclipse에 익숙한 Linux 사용자에게도 Terminal을 통한 실행 방식은 번거롭고 불편하다.

 그렇다면 Linux 환경에서 Android Studio를 좀 더 쉽게 이용할 수 있는 방법은 없는 것일까요? 아래는 Terminal을 사용하지 않고 바로 Android Studio를 실행할 수 있는 방법들에 대해 다룬 내용입니다.


1) Dash Home을 통해 Android Studio 실행하기

 이 방법은 Android Studio에서 직접적으로 제공하는 방식으로서 별도의 설정 없이 바로 적용이 가능합니다.

- Android Studio를 실행한 후 Tools -> Create Desktop Entry... 를 실행합니다.



 실행을 하게 되면 아래와 같은 안내문이 나타납니다. 만약 다른 사용자가 사용할 수 있게 하고 싶다면 박스를 체크하신 후 OK 버튼을 클릭합니다.



 위의 과정을 완료한 후 Dash Home에 'android'를 검색하면 아래와 같이 Android Studio를 바로 실행할 수 있는 아이콘이 나타나는 것을 확인하실 수 있습니다.



2) 바탕화면(Desktop)에 바로가기 추가하기


 이 방법을 사용하면 바탕화면에 있는 아이콘을 더블클릭하여 바로 Android Studio를 실행할 수 있게 합니다.


$ vi android_studio.desktop


 android_studio.desktop 파일을 vi로 아래와 같은 내용을 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
[Desktop Entry]
Version=1.0
Type=Application
Name=Android Studio
Exec="/home/자신의 폴더 경로/android-studio/bin/studio.sh" %f
Icon=/home/자신의 폴더 경로/android-studio/bin/studio.ico
Categories=Developement;IDE;
Terminal=false
StartupNotify=true
StartupWMClass=jetbrains-android-studio
Name[en_G0]=android-studio.desktop
cs

위와 같이 작성한 후 다음과 같은 명령어를 입력해줍니다.


$ chmod +x android_studio.desktop

$ mv ~/Desktop/ 또는 $ mv ~/바탕화면/


 위와 같은 과정을 거치신 후 자신의 바탕화면을 확인하면 Android Studio 바로가기 아이콘이 생성되어 있음을 확인할 수 있습니다. 실행하면 Android Studio가 정상적으로 실행됨을 확인하실 수 있습니다.




300x250

같은 공유기에 접속중인 안드로이드 기기 탐색 및 통신하기

 안드로이드 디바이스와 같이 휴대용 기기의 경우 Wi-Fi를 사용할 때 장소 및 환경에 따라 항상 같은 공유기를 사용할 수 만은 없지요. 또한 무선공유기(라우터)에 접속할 때 마다 할당 받는 IP 주소 또한 달라질 수 있기 때문에 Socket 통신을 하게 될 때 IP 주소를 고정 시킬 수 없어 변경된 IP 주소를 일일이 변경하여야만 합니다.


 그렇다면 번거롭게 IP 주소를 확인해야만 하는 걸까요? JAVA를 기반으로 하는 서버를 통한 통신 프로그래밍을 설계한다면 UDP 방식을 활용한 Broadcast 방식으로 공유기에 접속한 안드로이드 기기의 IP 주소를 알아낸 후 해당 IP를 통해 TCP 소켓 통신을 하는 방법이 있습니다.


 이번 포스팅에서는 같은 공유기(라우터)에 접속중인 서버가 UDP를 통해 현재 접속 중인 안드로이드 기기들을 탐색한 후 이에 응답을 한 안드로이드 기기의 IP 주소를 통해 TCP 방식의 Socket 통신이 이루어지는 과정에 대해 알아보도록 하겠습니다.


 본 포스팅의 소스코드는 이전에 작성하였던 아래 포스팅의 소스코드를 사용하였음을 밝힙니다. 혹시 서버에서의 UDP 통신에 대해 좀 더 알고 싶으신 분은 아래의 포스팅을 참조해 주시면 도움이 될 것입니다.


[JAVA] 같은 공유기에 접속중인 기기의 IP 주소 확인하는방법

http://elecs.tistory.com/153


-Server측(PC)

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
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
 
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        RecvMesage rm = new RecvMessage();
        rm.start();
 
        for (int i = 0; i < 100; i++) {
            try {
                new SearchDevice("255.255.255.255"8200).start();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        rm.closeServer();
 
    }
 
    static class SearchDevice extends Thread {
        InetAddress ia;
        int port;
 
        SearchDevice(String IPaddr, int Port) {
            try {
                ia = InetAddress.getByName(IPaddr);
                port = Port;
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            String msg = "Hello, ELECS!";
            try {
                DatagramSocket ds = new DatagramSocket();
                int length = msg.length();
                byte[] msgbyte = msg.getBytes();
                DatagramPacket dp = new DatagramPacket(msgbyte, length, ia, port);
                //UDP 방식으로 공유기에 접속중인 모든 기기에 Packet을 전송합니다.
                ds.send(dp);
                ds.close();
 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
    static class RecvMessage extends Thread {
        boolean ready = true;
        Socket socket;
        InputStream is;
        ObjectInputStream ois;
        String clientIp;
        ServerSocket sockserver;
        Object lock = new Object();
 
        public void closeServer() {
            try {
                synchronized (lock) {
                    sockserver.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            try {
                sockserver = new ServerSocket(8000);
                while (ready) {
                    //안드로이드 디바이스로부터 TCP 소켓 통신을 시작합니다.
                    socket = sockserver.accept();
                    synchronized (lock) {
                        is = socket.getInputStream();
                        ois = new ObjectInputStream(is);
 
                        //안드로이드 기기로부터 IP주소를 받아옵니다.
                        clientIp = (String) ois.readObject();
 
                        System.out.println("Client IP : " + clientIp);
                        ois.close();
                        is.close();
                        socket.close();
                    }
                }
 
            } catch (SocketException e) {
                System.out.println("ServerSocket is closed!");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
cs


-Client측(안드로이드 디바이스)

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
public class MainActivity extends AppCompatActivity {
    TextView tv1;
    Handler mHandler = new Handler();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        tv1 = (TextView)findViewById(R.id.textview1);
        (new RecvUDP()).start();
    }
 
    class RecvUDP extends Thread{
        String text;
        String serverIp;
        DatagramPacket dp;
 
        public void run(){
            int port = 8200;
            byte[] message = new byte[1000];
            dp = new DatagramPacket(message, message.length);
            try {
                
                DatagramSocket ds = new DatagramSocket(port);
                //서버로부터 전달되는 Packet을 수신합니다.
                ds.receive(dp);
                text = new String(message, 0, dp.getLength());
                ds.close();
                //수신된 Packet으로부터 서버의 IP를 저장합니다.
                serverIp = dp.getAddress().getHostAddress();
 
                mHandler.post(new Runnable(){
 
                    @Override
                    public void run() {
                        tv1.setText(text);
                    }
                });
 
                WifiManager wifiMgr = (WifiManager) getSystemService(WIFI_SERVICE);
                WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
                int ip = wifiInfo.getIpAddress();
 
                String androidIp = Formatter.formatIpAddress(ip);
 
                //서버의 IP를 통하여 Socket 통신을 시작합니다.
                Socket sock = new Socket(serverIp, 8000);
                DataOutputStream os = new DataOutputStream(sock.getOutputStream());
                ObjectOutputStream oos = new ObjectOutputStream(os);
                oos.writeObject(androidIp);
                oos.close();
                os.close();
                sock.close();
 
            } catch (Exception e) {
                e.printStackTrace();
            }
 
        }
    }
}
cs


-결과



300x250

Error: The SDK Build Tools revision is too low for project

 Eclipse에서 사용하던 프로젝트를 Android Studio로 가져왔을 때 아래와 같은 상황의 에러가 발생하였습니다.


Error: The SDK Build Tools revision (19.0.0) is too low for project. Minimum required is 19.1.0


 위 에러는 프로젝트가 기존 Eclipse에서 사용하던 Build Tools가 Android Studio에 설치된 Build Tools보다 버전이 낮아 발생하는 에러 입니다. 위 에러를 해결하기 위해서는 아래와 같은 과정을 수행합니다.


1.에러가 발생한 프로젝트의 폴더 -> build.grade 를 실행합니다.

2.android 부분에서 BuildToolsVersion 부분을 최소 요구 버전으로 설정합니다. 본 예제의 경우 버전을 19.1.0으로 수정합니다.



 위와 같이 적용한 후 Android Studio를 종료한 후 다시 실행하면 제대로 적용되어 있는 것을 확인하실 수 있습니다.




300x250

저장된 사진및 파일이 보이지 않을 때 미디어스캐닝(Media Scanning) 방법 [Kitkat 이후의 버전에서 적용방법]

 Socket을 활용하여 안드로이드 프로그래밍을 하는 분들이라면 누구나 한 번 즈음은 난관에 부딪치는 경우가 하나 있습니다. 그 중 하나가 분명 소켓 통신을 통해 받은 이미지나 동영상 파일을 저장하였는데 갤러리를 통해 확인해 보려 하면 보이지 않는 경우이지요! 희한하게도 안드로이드 기기의 전원을 끈 후 다시 확인해보면 안보이던 사진이 버젓이 보인다는 사실!


갤러리에 내가 다운로드 받은 파일이 누락되어 있다면 상당히 당황스러울 것이다.


 실제로 안드로이드 기기는 파일을 바로 저장한 상태로는 이를 기기가 바로 인식을 하지 못합니다. 그렇다면 기껏 다운로드 받은 파일을 보기 위해서 안드로이드  기기를 리셋 하는 방법밖에 없는 걸까요?



 안드로이드 기기를 쓰다가 위와 같이 미디어스캐닝(Media scanning)이 진행중인 것을 종종 보실 수 있습니다. 이것이 바로 종적을 감추어버린 파일들의 위치를 다시 찾는 기능을 하는 녀석입니다. 종종 개발자들이 애플리케이션을 개발하던 도중 다운로드가 완료된 후 미디어스캐닝을 해주지 않게 될 경우 파일을 볼 수 없는 상황이 벌어지는 것입니다.


 이번 포스팅에서는 소스코드를 통하여 미디어스캐닝을 수행하는 방법에 대해 알아볼 것입니다. 또한 본 포스팅은 Kitkat에서부터 변경된 미디어 저장 방식을 반영한 방법에 대해 다루어 보도록 할 것입니다.


 아래의 소스코드는 카메라로부터 촬영된 사진 데이터를 저장하는 과정을 나타낸 소스코드입니다.

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
        private Camera.PictureCallback picb = new Camera.PictureCallback() {
 
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                long time = System.currentTimeMillis();
                SimpleDateFormat day = new SimpleDateFormat("yyyymmddhhmmssSSS");
                String output = day.format(new Date(time));
 
                String folder = Environment.getExternalStorageDirectory().getAbsolutePath()
                        + "/DCIM/FrameworkTest";
                String file = folder + File.separator + output + ".jpg";
 
                File FolderPath = new File(folder);
                if (!FolderPath.exists()) {
                    FolderPath.mkdirs();
                    Log.d("MKDIR", folder);
                }
 
                try {
                    FileOutputStream out = new FileOutputStream(file);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 50out);
                    out.close();
                    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, 
                            Uri.parse("file://"+file)));
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
 
                camera.startPreview();
            }
        };
cs

  위 소스코드의 내용대로 byte[] 형식으로 들어온 사진 데이터가 FileOutputStream 클래스를 통해 저장되는 과정을 나타내고 있습니다. 이를 위해 위의 소스코드 몇 줄을 확인해 보도록 하겠습니다.


String folder = Environment.getExternalStorageDirectory().getAbsolutePath();


 저장할 파일의 폴더명을 설정해 줍니다. 위의 소스코드를 통해 안드로이드 기기의 외부저장소의 최상단에서의 절대주소를 얻을 수 있습니다.


sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+file)));


 FileOutputStream을 통해 저장된 파일 하나를 나타내기 위해 해당 파일에 대해 Media Scanning을 수행합니다. Uri.parse() 의 인자값으로 해당 파일의 절대주소를 입력하며, Intent의 첫번째 인자인 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE에 주의하시면 해당 소스코드를 사용하는 데에 큰 문제는 없을 것입니다. 위와 같이 설정된 Intent 클래스를 sendBroadcast() 매서드를 통해 전달하면 저장된 파일에 대한 Media Scanning이 정상적으로 동작되에 갤러리를 통해 해당 파일을 확인하실 수 있을 것입니다.

300x250

byte[] 바이트 배열을 socket을 통해 쉽게 전송하는 방법

 C/C++을 통해 파일을 socket 통신으로 전송하는 경우 데이터를 char 배열을 통해 buffer의 크기를 고려하면서 전송을 해야 하기 때문에 프로그램을 설계할 때 상당히 많은 부분을 고려해야 되어 골치가 아프지요. 그러한 면에서 보았을 때 Java에서 제공하는 Socket 통신 기능들이 상당히 편해서 프로그래머들에게도 상당히 큰 부담을 줄여주는 점이 맘에 들곤 합니다. 이번 포스팅에서는 C/C++에서는 다루는 것이 다소 번거로운 byte 배열을 손쉽게 전송하는 방법에 대해 알아보도록 하겠습니다.


 안드로이드 프로그래밍을 하다 보면 이미지나 파일을 Socket을 통해 전송해야 되는 경우들이 많습니다. 만약 수신측이 C/C++로 짜여져 있으면 정해진 buffer로 나누어서 전송을 해야 하기 때문에 파일을 byte[] 배열 형식으로 변환한 후 socket에 실어서 전송해야 합니다. 프로그램을 설계할 때 도중에 자료가 누락되는 경우 원본의 손실 또한 발생하기 때문에 신중하게 프로그래밍을 해야 합니다.

 안드로이드 카메라의 경우 takePicture() 함수에 callback 함수를 통해 카메라에 찍힌 이미지를 아래와 같은 방식으로 byte[] 배열로 제공합니다. 프로그래머는 이를 통해 파일로 저장하거나 화면에 띄우는 등의 작업을 수행하게 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
        private Camera.PictureCallback picb_remote = new Camera.PictureCallback() {
 
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
 
                ....
 
                camera.startPreview();
            }
        };
cs

 위의 이미지 형식의 데이터인 byte[] 배열을 Java 기반의 서버와 socket을 통해 어떤 방식으로 통신을 하면 가장 간편할까요? Java에서는 직렬화 된 Object 혹은 byte[] 배열을 손쉽게 전송할 수 있는 ObjectOutputStreamObjectInputStream을 제공합니다. 이를 사용하는 방식에 대해 좀 더 자세히 알아보겠습니다. 소스코드에서 각 중요한 내용을 주석을 통해 설명하였습니다.

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
        private Camera.PictureCallback picb_remote = new Camera.PictureCallback() {
 
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                // TODO Auto-generated method stub
                try {
                    //IP주소와 포트번호를 입력하여 Socket 통신을 시작합니다.
                    Socket sock = new Socket("127.0.0.1"8200);
                    //Socket으로부터 outputStream을 얻습니다.
                    OutputStream os = sock.getOutputStream();
                    //등록한 OutputStream을 ObjectOutputStream 방식으로 사용합니다.
                    ObjectOutputStream oos = new ObjectOutputStream(os);
 
                    //byte[] 파일을 object 방식으로 통째로 전송합니다.
                    oos.writeObject(data);
                
                    oos.close();
                    os.close();
                    sock.close();
                        
                } catch (UnknownHostException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
 
                camera.startPreview();
            }
        };
cs

 위의 소스코드에서 보이는 바와 같이 생성된 byte[] 배열을 그대로 writeObject() 매서드의 인자값에 등록을 하면 Java에서는 이를 그대로 Server 쪽으로 전송해줍니다. 아래는 byte[]를  수신하는 Server 측의 소스코드입니다. 중요한 부분은 주석으로 설명합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
            int port = 8200;
            //Server측에서 사용할 포트번호를 설정한 후 Socket 서버를 개방합니다.
            ServerSocket sock = new ServerSocket(port);
            //Client로부터 소켓 신호를 기다립니다.
            Socket socket = sock.accept();
            //Socket로부터 InputStream을 등록합니다.
            InputStream is = socket.getInputStream();
            //등록한 InputStream을 ObjectInputStream방식으로 사용합니다.
            final ObjectInputStream ois = new ObjectInputStream(is);
            
            //전송된 byte[] 데이터를 수신합니다.
            byte[] data = (byte[])ois.readObject();
            
            System.out.println("data size : " + data.length);
            
            ois.close();
            is.close();
            socket.close();
            sock.close();
cs


 위와 같은 방식으로 Socket 프로그램을 설계하시면 byte[] 배열 방식으로 되어있는 데이터 값을 Java 상에서 손쉽게 전송할 수 있습니다.

300x250

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

안드로이드/프레임워크 2015. 9. 20. 17:37

 이전 포스팅을 통해 안드로이드의 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




300x250

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

안드로이드/프레임워크 2015. 9. 16. 23:01

 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 함수가 실행됨을 확인할 수 있습니다.

300x250

SurfaceFlinger에서 DispSyncSource의 초기화 과정

안드로이드/프레임워크 2015. 9. 12. 23:40

 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() 함수를 호출하게 됩니다.






300x250

SurfaceFlinger의 초기화 과정 흐름도

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




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

300x250

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


300x250

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

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

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


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

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

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


/frameworks/native/include/gui/BitTube.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Parcel;
 
class BitTube : public RefBase
{
public:
 
    // creates a BitTube with a default (4KB) send buffer
    BitTube();
 
    // creates a BitTube with a a specified send and receive buffer size
    explicit BitTube(size_t bufsize);
 
    explicit BitTube(const Parcel& data);
    virtual ~BitTube();
 
    // check state after construction
    status_t initCheck() const;
 
    // get receive file-descriptor
    int getFd() const;
 
    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
    template <typename T>
    static ssize_t sendObjects(const sp<BitTube>& tube,
            T const* events, size_t count) {
        return sendObjects(tube, events, count, sizeof(T));
    }
 
    // receive objects (sized blobs). If the receiving buffer isn't large enough,
    // excess messages are silently discarded.
    template <typename T>
    static ssize_t recvObjects(const sp<BitTube>& tube,
            T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }
 
    // parcels this BitTube
    status_t writeToParcel(Parcel* reply) const;
 
private:
    void init(size_t rcvbuf, size_t sndbuf);
 
    // send a message. The write is guaranteed to send the whole message or fail.
    ssize_t write(void const* vaddr, size_t size);
 
    // receive a message. the passed buffer must be at least as large as the
    // write call used to send the message, excess data is silently discarded.
    ssize_t read(void* vaddr, size_t size);
 
    int mSendFd;
    mutable int mReceiveFd;
 
    static ssize_t sendObjects(const sp<BitTube>& tube,
            void const* events, size_t count, size_t objSize);
 
    static ssize_t recvObjects(const sp<BitTube>& tube,
            void* events, size_t count, size_t objSize);
};
cs

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Socket buffer size.  The default is typically about 128KB, which is much larger than
// we really need.  So we make it smaller.
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
 
BitTube::BitTube()
    : mSendFd(-1), mReceiveFd(-1)
{
    init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
}
 
....
 
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
        // sine we don't use the "return channel", we keep it small...
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
        mReceiveFd = sockets[0];
        mSendFd = sockets[1];
    } else {
        mReceiveFd = -errno;
        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
    }
}
cs

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
status_t BitTube::initCheck() const
{
    if (mReceiveFd < 0) {
        return status_t(mReceiveFd);
    }
    return NO_ERROR;
}
 
int BitTube::getFd() const
{
    return mReceiveFd;
}
 
ssize_t BitTube::write(void const* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
        // cannot return less than size, since we're using SOCK_SEQPACKET
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    return err == 0 ? len : -err;
}
 
ssize_t BitTube::read(void* vaddr, size_t size)
{
    ssize_t err, len;
    do {
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        // EAGAIN means that we have non-blocking I/O but there was
        // no data to be read. Nothing the client should care about.
        return 0;
    }
    return err == 0 ? len : -err;
}
 
status_t BitTube::writeToParcel(Parcel* reply) const
{
    if (mReceiveFd < 0)
        return -EINVAL;
 
    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
    close(mReceiveFd);
    mReceiveFd = -1;
    return result;
}
 
 
ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
        void const* events, size_t count, size_t objSize)
{
    const char* vaddr = reinterpret_cast<const char*>(events);
    ssize_t size = tube->write(vaddr, count*objSize);
 
    // should never happen because of SOCK_SEQPACKET
    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize),
            "BitTube::sendObjects(count=%d, size=%d), res=%d (partial events were sent!)",
            count, objSize, size);
 
    //ALOGE_IF(size<0, "error %d sending %d events", size, count);
    return size < 0 ? size : size / objSize;
}
 
ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
        void* events, size_t count, size_t objSize)
{
    char* vaddr = reinterpret_cast<char*>(events);
    ssize_t size = tube->read(vaddr, count*objSize);
 
    // should never happen because of SOCK_SEQPACKET
    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % objSize),
            "BitTube::recvObjects(count=%d, size=%d), res=%d (partial events were received!)",
            count, objSize, size);
 
    //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
    return size < 0 ? size : size / objSize;
}
cs


300x250

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

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


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

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

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

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


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

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

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


http://elecs.tistory.com/79


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


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

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

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


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

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


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

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


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

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


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

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


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

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


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

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


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


/system/core/libutils/RefBase.cpp

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


/system/core/libutils/RefBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile int32_t    mFlags;
 
....
 
    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
        , mStrongRefs(NULL)
        , mWeakRefs(NULL)
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        , mRetain(false)
    {
    }
 
....
 
}
cs


/system/core/libutils/RefBase.cpp

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

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

300x250