Java JNI 코드 분석 : GetObjectClass()

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

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


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

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

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


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


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


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


http://elecs.tistory.com/71


hello.c

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


 결론적으로 말해서


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


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


300x250

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

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

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

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


/frameworks/base/core/jni/android_view_Surface.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int register_android_view_Surface(JNIEnv* env)
{
    int err = AndroidRuntime::registerNativeMethods(env, "android/view/Surface",
            gSurfaceMethods, NELEM(gSurfaceMethods));
 
    jclass clazz = env->FindClass("android/view/Surface");
    gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
    gSurfaceClassInfo.mNativeObject =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject""I");
    gSurfaceClassInfo.mLock =
            env->GetFieldID(gSurfaceClassInfo.clazz, "mLock""Ljava/lang/Object;");
    gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>""(I)V");
 
    clazz = env->FindClass("android/graphics/Canvas");
    gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer""Landroid/graphics/Canvas$CanvasFinalizer;");
    gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
    gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat""I");
 
    clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
    gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas""I");
 
    clazz = env->FindClass("android/graphics/Rect");
    gRectClassInfo.left = env->GetFieldID(clazz, "left""I");
    gRectClassInfo.top = env->GetFieldID(clazz, "top""I");
    gRectClassInfo.right = env->GetFieldID(clazz, "right""I");
    gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom""I");
 
    return err;
}
cs


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



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

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

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


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

http://elecs.tistory.com/71



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

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

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


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

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


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

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

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

http://elecs.tistory.com/71


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

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

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

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


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

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

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

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


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

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

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


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

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

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

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

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

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

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

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

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


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


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


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


MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
 
 
package com.example.calljava;
 
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
 
public class MainActivity extends Activity {
    private TextView tv;
    private Button bt;
    private static Context mContext;
    public int a = 11;
    public int b = 23;
    
    static {
        System.loadLibrary("hello");
    }
    
    public native String stringFromJNI();
    public native void callJava();
    public native int nativeSum();
    public native TestClass nativeConst();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        tv = (TextView) findViewById(R.id.textview);
        tv.setText(this.stringFromJNI());
        mContext = this;
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
    public void OnClick(View v){
        switch(v.getId()){
            case R.id.button1:
                this.callJava();
                break;
            case R.id.button2:
                Toast.makeText(thisthis.nativeSum()+"", Toast.LENGTH_SHORT).show();
                break;
            case R.id.button3:
                Toast.makeText(thisthis.nativeConst().toString(), Toast.LENGTH_SHORT).show();
        }
    }
    
    public static void testToast(){
        Toast.makeText(mContext, "Hello, Toast!", Toast.LENGTH_SHORT).show();
    }
    
}
 
class TestClass{
    public TestClass(){
        
    }
    
    public String toString(){
        return "JNI를 통해 생성된 클래스입니다!";
        
    }
}
cs


Activity_Main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.calljava.MainActivity" >
 
    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
 
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textview"
        android:layout_below="@+id/textview"
        android:layout_marginTop="14dp"
        android:onClick="OnClick"
        android:text="Button1" />
 
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button1"
        android:layout_alignBottom="@+id/button1"
        android:layout_marginLeft="14dp"
        android:layout_toRightOf="@+id/button1"
        android:onClick="OnClick"
        android:text="Button2" />
 
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button2"
        android:layout_alignBottom="@+id/button2"
        android:layout_marginLeft="20dp"
        android:layout_toRightOf="@+id/button2"
        android:onClick="OnClick"
        android:text="Button3" />
 
</RelativeLayout>
cs


hello.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <string.h>
#include <jni.h>
#include <stdio.h>
 
void Java_com_example_calljava_MainActivity_callJava(JNIEnv* env, jobject thiz){
    jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
    //jclass jCallJava = (*env)->GetObjectClass(env, thiz);
 
    jmethodID testToast = (*env)->GetStaticMethodID(env, jCallJava, "testToast""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, testToast);
}
 
jstring Java_com_example_calljava_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz){
    return (*env)->NewStringUTF(env, "Hello, JNI World!");
}
 
jint Java_com_example_calljava_MainActivity_nativeSum(JNIEnv* env, jobject thiz){
    jclass jCallJava = (*env)->FindClass(env, "com/example/calljava/MainActivity");
 
    jfieldID ja = (*env)->GetFieldID(env, jCallJava, "a""I");
    int a = (*env)->GetIntField(env, thiz, ja);
    jfieldID jb = (*env)->GetFieldID(env, jCallJava, "b""I");
    int b = (*env)->GetIntField(env, thiz, jb);
 
    int c = a+b;
 
    return c;
}
 
jobject Java_com_example_calljava_MainActivity_nativeConst(JNIEnv* env, jobject thiz){
 
    jclass jTestClass = (*env)->FindClass(env, "com/example/calljava/TestClass");
 
    jmethodID jmid = (*env)->GetMethodID(env, jTestClass, "<init>""()V");
    jobject obj = (*env)->NewObject(env, jTestClass, jmid);
    
    
    return obj;
    
}
cs







300x250

안드로이드 Framework에서 Camera 동작 원리 분석(3)

안드로이드/카메라 2015. 4. 27. 00:04

 안드로이드 카메라 기능을 꾸준히 분석해 보고 있습니다만 생각보다 복잡한 구조에 놀라우면서도 한 편으로는 안드로이드의 심오함을 동시에 느끼고 있습니다. 이번 포스팅은 지난시간에 이어 계속 이어가도록 하겠습니다.


 지난 포스팅까지 Camera의 Connect() 함수가 동작하는 과정에 대해 살펴보았었습니다. 이번 포스팅에서는 바로 그 다음부터 이어가도록 하겠습니다.


/frameworks/base/core/jni/android_hardware_Camera.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
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jstring clientPackageName)
{
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);
 
    sp<Camera> camera = Camera::connect(cameraId, clientName,
            Camera::USE_CALLING_UID);
 
    if (camera == NULL) {
        jniThrowRuntimeException(env, "Fail to connect to camera service");
        return;
    }
 
    // make sure camera hardware is alive
    if (camera->getStatus() != NO_ERROR) {
        jniThrowRuntimeException(env, "Camera initialization failed");
        return;
    }
 
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
        return;
    }
 
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong((void*)android_hardware_Camera_native_setup);
    camera->setListener(context);
 
    // save context in opaque field
    env->SetIntField(thiz, fields.context, (int)context.get());
}
cs


이제 한 줄씩 살펴보도록 하겠습니다.


sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);


JNICameraContext 클래스를 선언하는 모습입니다. JNICameraContext 클래스의 내용을 자세히 살펴보도록 합시다.


/frameworks/base/core/jni/android_hardware_Camera.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
// provides persistent context for calls from native code to Java
class JNICameraContext: public CameraListener
{
public:
    JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
    ~JNICameraContext() { release(); }
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata);
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
    void postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata);
    void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType);
    void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
    sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
    bool isRawImageCallbackBufferAvailable() const;
    void release();
 
private:
    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
    void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers);
    void clearCallbackBuffers_l(JNIEnv *env);
    jbyteArray getCallbackBuffer(JNIEnv *env, Vector<jbyteArray> *buffers, size_t bufferSize);
 
    jobject     mCameraJObjectWeak;     // weak reference to java object
    jclass      mCameraJClass;          // strong reference to java class
    sp<Camera>  mCamera;                // strong reference to native object
    jclass      mFaceClass;  // strong reference to Face class
    jclass      mRectClass;  // strong reference to Rect class
    Mutex       mLock;
 
    /*
     * Global reference application-managed raw image buffer queue.
     *
     * Manual-only mode is supported for raw image callbacks, which is
     * set whenever method addCallbackBuffer() with msgType =
     * CAMERA_MSG_RAW_IMAGE is called; otherwise, null is returned
     * with raw image callbacks.
     */
    Vector<jbyteArray> mRawImageCallbackBuffers;
 
    /*
     * Application-managed preview buffer queue and the flags
     * associated with the usage of the preview buffer callback.
     */
    Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[]
    bool mManualBufferMode;              // Whether to use application managed buffers.
    bool mManualCameraCallbackSet;       // Whether the callback has been set, used to
                                         // reduce unnecessary calls to set the callback.
};
cs


 이 클래스는 Camera 관련 호출이 Native에서 Java로 전송해야 될 때 주로 쓰이는 클래스로 추측할 수 있습니다. 일단 자세한 사항은 다음에 기회가 될 때 살명하고 넘어가도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
{
    mCameraJObjectWeak = env->NewGlobalRef(weak_this);
    mCameraJClass = (jclass)env->NewGlobalRef(clazz);
    mCamera = camera;
 
    jclass faceClazz = env->FindClass("android/hardware/Camera$Face");
    mFaceClass = (jclass) env->NewGlobalRef(faceClazz);
 
    jclass rectClazz = env->FindClass("android/graphics/Rect");
    mRectClass = (jclass) env->NewGlobalRef(rectClazz);
 
    mManualBufferMode = false;
    mManualCameraCallbackSet = false;
}
cs


 위에서 보시는 바와 같이 JNICameraContext 클래스 내에 Camera와 관련된 Java 클래스값들을 선언하고 있는 것을 확인하실 수 있습니다.



context->incStrong((void*)android_hardware_Camera_native_setup);


함수 android_hardware_Camera_native_setup() 함수의 참조계수를 1 증가시킵니다.


camera->setListener(context);


위에서 선언하였던 JNICameraContext의 변수값을 camera 클래스 변수 내에 Listerner로 설정해줍니다.


/frameworks/av/camera/Camera.cpp

1
2
3
4
5
void Camera::setListener(const sp<CameraListener>& listener)
{
    Mutex::Autolock _l(mLock);
    mListener = listener;
}
cs


 이로서 대망의 Camera.open() 함수의 동작과정을 (1)~(3) 포스팅을 통해 모두 살펴보았습니다. Camera의 구현과정을 처음 보시는 분들이시라면 상당히 큰 어려움이 있으실 것이라 생각합니다. 막히더라도 일단 망설이지 마시고 대략 이러한 기능을 한다는 것을 기억하신 후 다음 단계로 넘어가신다면 이후 넘어갔던 부분이 이해가 되실 날이 오리라 저는 생각합니다!

300x250

안드로이드 Framework에서 Camera 동작 원리 분석(2)

안드로이드/카메라 2015. 4. 26. 09:00

 지난 시간에 이어 Framework 단계에서 Camera가 CameraService와 어떻게 연결되는지 이어서 진행해 보도록 하겠습니다.

 Application 단계에서 Camera 클래스의 open() 매소드를 실행한 후 이를 지속적으로 추적하여 CameraBase 까지 접근하였고 이 곳에서 CameraService와의 접점을 찾는 과정까지 진행하였습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
 
    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}
cs

 이제 여기서부터 한 줄씩 분석을 하며 내려가 보도록 하겠습니다.


sp<TCam> c = new TCam(cameraId);


먼저 저 Templete인 TCam의 정체를 확인해 보도록 합시다. TCam은 위 함수가 호출되기 전 미리 정의가 되어 있을 것입니다.


/frameworks/av/camera/Camera.cpp

1
2
3
4
5
6
7
#include <camera/Camera.h>
 
sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
        int clientUid)
{
    return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}
cs


TCam을 알아보려 함수를 거슬러 올라왔더니 이번에는 CameraBaseT가 떡하니 있군요. 이 녀석은 무엇을 하는 녀석일까요?


/frameworks/av/include/camera/Camera.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <camera/CameraBase.h>
 
class Camera :
    public CameraBase<Camera>,
    public BnCameraClient
{
public:
    enum {
        USE_CALLING_UID = ICameraService::USE_CALLING_UID
    };
 
            // construct a camera client from an existing remote
    static  sp<Camera>  create(const sp<ICamera>& camera);
    static  sp<Camera>  connect(int cameraId,
                                const String16& clientPackageName,
                                int clientUid);
}
cs

/frameworks/av/include/camera/CameraBase.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public:
    typedef typename TCamTraits::TCamListener       TCamListener;
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
 
    static sp<TCam>      connect(int cameraId,
                                 const String16& clientPackageName,
                                 int clientUid);
 
    typedef CameraBase<TCam>         CameraBaseT;
}
cs

 함수 하나를 설명하려다 보니 생각보다 많은 코드들이 추가되었군요. 일단 위 함수들을 근거로 하나씩 단서를 찾아보도록 합시다. 우선 CameraBaseT의 정체를 알아봅시다.


return CameraBaseT::connect(cameraId, clientPackageName, clientUid);


 바로 위의 CameraBase.h 파일 내에서 CameraBaseT가 typedef로 정의되어 있는 것을 확인하실 수 있습니다. 이를 변환해 보면 다음과 같군요.


return CameraBase<TCam>::connect(cameraId, clientPackageName, clientUid);

 그래도 아직 TCam의 정체는 풀리지 않았군요 그러나 이는 Camera.h 에 정의된 Camera의 상속 클래스를 확인하면 바로 풀리는 것을 보실 수 있습니다.


class Camera : public CameraBase<Camera>, public BnCameraClient


이로서 TCam의 정체는 Camera라는 것을 알게 되었습니다. 이를 다시 정리하면 다음과 같이 변환될 수 있음을 알 수 있습니다.


sp<Camera> c = new Camera(cameraId);


/frameworks/av/camera/Camera.cpp

1
2
3
4
Camera::Camera(int cameraId)
    : CameraBase(cameraId)
{
}
cs


/frameworks/av/include/camera/CameraBase.h

1
2
3
4
5
6
7
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
protected:
    CameraBase(int cameraId);
    const int                        mCameraId;
}
cs


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
template <typename TCam, typename TCamTraits>
CameraBase<TCam, TCamTraits>::CameraBase(int cameraId) :
    mStatus(UNKNOWN_ERROR),
    mCameraId(cameraId)
{
}
cs

이러한 과정을 통해 Camera 객체가 생성되었음을 확인하였습니다. 이제 다음줄을 보도록 합니다.


sp<TCamCallbacks> cl = c;


자... 이번에는 TCamCallbacks가 뿅 하고 나타나 우리들을 괴롭히고 있군요! 이 녀석의 정체는 과연 누구일까요?

/frameworks/av/include/camera/Camera.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <camera/CameraBase.h>
 
template <>
struct CameraTraits<Camera>
{
    typedef CameraListener        TCamListener;
    typedef ICamera               TCamUser;
    typedef ICameraClient         TCamCallbacks;
    typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,
                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);
    static TCamConnectService     fnConnectService;
};
cs


/frameworks/av/include/camera/CameraBase.h

1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename TCam>
struct CameraTraits {
};
 
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public:
    typedef typename TCamTraits::TCamListener       TCamListener;
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
}
cs

위 코드를 처음 볼 땐 그냥 정신이 멍해질 겁니다. 하지만 다시 정신 바짝 차리시고 코드를 상세하게 살펴보도록 합니다!


/frameworks/av/include/camera/CameraBase.h
template <typename TCam>
struct CameraTraits {
};

template <typename TCam, typename TCamTraits = CameraTraits<TCam> >

우선 CameraBase.h 헤더에서 다음과 같이 CameraTraits<Tcam> 이 정의되어 있고 이는 struct 내의 CameraTraits에 적용이 됩니다.


/frameworks/av/include/camera/Camera.h

#include <camera/CameraBase.h>
 
template <>
struct CameraTraits<Camera>
{
....
    typedef ICameraClient         TCamCallbacks;
....
};

 위의 CameraTraits<Camera> 로 선언된 struct로 인해 TCam이 Camera임을 알 수 있습니다. 또한 ICameraClient 클래스가 TCamCallbacks로 선언되어 있는 모습을 보실 수 있습니다.


이제 문제는 CameraBase.h에 정의된 아래의 코드가 뜻을 의미하기 난해다다는 점이지요.

typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;

 저 위에 쓰인 typename은 template 내에서 쓰이는건 많이 보았는데 여기서는 클래스 내에까지 정의되어 있는 것을 보실 수 있습니다. 이는 위에 보이시는 TCamTraits라는 것이 class임을 컴파일에게 알려주기 위해 부득이하게 typename을 적어준 것이라고 이해해 주시면 되겠습니다. 이와 관련해서 좀 더 자세한 정보를 알고 싶으신 분께서는 아래 링크를 참조해 주시기 바랍니다.

http://ikpil.com/540

위의 코드를 우리들이 읽기 쉽게 변환하면 다음과 같다고 보실 수 있겠습니다.


typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;

typedef CameraTraits<Camera>::TCamCallbacks      TCamCallbacks;

typedef CameraTraits<Camera>::ICameraClient      TCamCallbacks;
CameraTraits<Camera>::ICameraClient


결국 TCamCallbacks는 ICameraClient와 같음을 확인하실 수 있습니다.

sp<ICameraClient> cl = c;


이제 다음 코드를 확인해 보도록 하겠습니다.


const sp<ICameraService>& cs = getCameraService();


ICameraService 클래스를 불러오라는 듯한 의미의 코드로 해석됩니다. 여기서 getCameraService() 함수를 살펴보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}

cs

 위 코드는 BpCameraService와 BpBinder를 불러들이는 코드입니다. 위 코드에 대해 자세한 내용은 아래 포스팅을 참고해주시기 바랍니다.

http://elecs.tistory.com/93


 위 과정까지 진행하셨다면 CameraBase와 CameraService가 Binder를 통해 연결되었음을 확인하실 수 있습니다. 이제 다음으로 이후의 코드들을 살펴보도록 하겠습니다.

/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
 
    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}
cs

이제 이후의 코드들을 한 줄씩 분석해 보도록 하겠습니다.

TCamConnectService fnConnectService = TCamTraits::fnConnectService;


 이 코드는 typedef로 정의된 함수 포인터 변수를 선언하고 이것에 실행하고자 하는 함수를 저장하고 있습니다. fnConnectService 내에는 다음과 같은 내용의 함수가 저장됩니다.


/frameworks/av/camera/Camera.cpp

1
2
CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService =
        &ICameraService::connect;
cs

 fnConnectService 함수 포인터는 ICamaerService의 connect 함수를 저장함을 확인할 수 있습니다. typedef 함수 포인터에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조하시기 바랍니다.


http://elecs.tistory.com/97

 

status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,

                                             /*out*/ c->mCamera);


 위에서 설정하였던 함수 포인터를 실행하는 코드입니다. sp<T> 클래스에서 get() 함수를 호출하면 자신이 가지고 있는 객체값의 포인터 값을 불러옵니다. 이 때 리턴값의 자료형은 T입니다. 위의 코드에서 변수 cs는 ICameraService를 리턴합니다. 이 때 cs에는 BpCameraService를 저장하고 있으므로 BpCameraService::connect 함수를 실행하게 된다.


/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


 Proxy 측인 BpCameraService 클래스에서 실행된 Parcel에 대한 transact() 함수는 이후 Native 측인 BnCameraService 클래스로 Parcel을 통해 값이 넘어오면서 실행되기 시작합니다.


/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
status_t BnCameraService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
    ....
        case CONNECT: {
            CHECK_INTERFACE(ICameraService, data, reply);
            sp<ICameraClient> cameraClient =
                    interface_cast<ICameraClient>(data.readStrongBinder());
            int32_t cameraId = data.readInt32();
            const String16 clientName = data.readString16();
            int32_t clientUid = data.readInt32();
            sp<ICamera> camera;
            status_t status = connect(cameraClient, cameraId,
                    clientName, clientUid, /*out*/ camera);
            reply->writeNoException();
            reply->writeInt32(status);
            if (camera != NULL) {
                reply->writeInt32(1);
                reply->writeStrongBinder(camera->asBinder());
            } else {
                reply->writeInt32(0);
            }
            return NO_ERROR;
        } break;
    ....
    }
 
}
cs


 위의 코드를 보았을 때 마치 connect() 함수가 이전에 실행했던 BpCameraService 클래스의 connect() 함수를 실행하는 듯한 모습을 보이고 있습니다. 하지만 사실은 Native 프로세스의 CameraService 내의 connect() 함수가 실행되고 있다는 것을 기억하시면 되겠습니다. 해당 코드를 분석해보도록 합시다.


/frameworks/av/services/camera/libcameraservice/CameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
class CameraService :
    public BinderService<CameraService>,
    public BnCameraService,
    public IBinder::DeathRecipient,
    public camera_module_callbacks_t
{
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
            const String16& clientPackageName, int clientUid,
            /*out*/
            sp<ICamera>& device);
....
}
cs


 먼저 CameraService 클래스의 구조를 보겠습니다. 위에서 보시면 아시듯이 BnCameraService 클래스를 상속받고 있는 것을 보실 수 있으며 CameraService 클래스 내에 connect() 함수가 virtual로 선언되어 있는 모습을 확인하실 수 있습니다.


/frameworks/av/include/camera/ICameraService.h
1
2
3
4
5
6
7
8
9
10
class BnCameraService: public BnInterface<ICameraService>
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};
 
}; // namespace android
cs


 BnCameraService 클래스는 보시듯이 BnInterface를 상속받고 있으며 BnInterface는 ICameraService 상속하는 상황임을 보실 수 있습니다.


/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


여기까지 확인하셨다면 일단 CameraService 클래스의 상속상황이 어떻게 되어있는지 어느정도 감을 잡으셨으리라 생각합니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ICameraService : public IInterface
{
....
 
public:
    /**
     * clientPackageName and clientUid are used for permissions checking.  if
     * clientUid == USE_CALLING_UID, then the calling UID is used instead. Only
     * trusted callers can set a clientUid other than USE_CALLING_UID.
     */
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


 자, 이제 저 connect() 함수는 어느 부분에서 선언되어 있는 것인지 확인해 보도록 합시다. C++에서 virtual 함수는 Java에서 Dynamic 함수로 정의하는 것으로 감을 잡으시면 이해가 쉬울 것입니다.

 이에 대한 힌트는 이전에 작성하였던 포스팅 중에 있습니다. 바로 CameraService가 등록되는 과정입니다.


http://elecs.tistory.com/83


/frameworks/native/include/binder/BinderService.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
#ifndef ANDROID_BINDER_SERVICE_H
#define ANDROID_BINDER_SERVICE_H
 
#include <stdint.h>
 
#include <utils/Errors.h>
#include <utils/String16.h>
 
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
 
// ---------------------------------------------------------------------------
namespace android {
 
template<typename SERVICE>
class BinderService
{
public:
    static status_t publish(bool allowIsolated = false) {
        sp<IServiceManager> sm(defaultServiceManager());
        return sm->addService(
                String16(SERVICE::getServiceName()),
                new SERVICE(), allowIsolated);
    }
 
    static void publishAndJoinThreadPool(bool allowIsolated = false) {
        publish(allowIsolated);
        joinThreadPool();
    }
 
    static void instantiate() { publish(); }
 
    static status_t shutdown() { return NO_ERROR; }
 
private:
    static void joinThreadPool() {
        sp<ProcessState> ps(ProcessState::self());
        ps->startThreadPool();
        ps->giveThreadPoolName();
        IPCThreadState::self()->joinThreadPool();
    }
};
 
 
}; // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_BINDER_SERVICE_H
 
 
cs

 

 내용이 다소 생략되어 있습니다만 위의 addService() 과정에서 CameraService 클래스가 new를 통해 생성되고 있음을 보실 수 있습니다. 즉, CameraService가 SystemService에 등록될 때 순수 CameraService 클래스가 선언되어 있으므로 virtual 함수의 dynamic 성질을 생각한다면 CameraService 내의 함수가 호출되어야 함이 맞음을 알 수 있을 것입니다.


/frameworks/av/services/camera/libcameraservice/CameraService.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
status_t CameraService::connect(
        const sp<ICameraClient>& cameraClient,
        int cameraId,
        const String16& clientPackageName,
        int clientUid,
        /*out*/
        sp<ICamera>& device) {
 
    String8 clientName8(clientPackageName);
    int callingPid = getCallingPid();
 
    LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
            clientName8.string(), cameraId);
 
    status_t status = validateConnect(cameraId, /*inout*/clientUid);
    if (status != OK) {
        return status;
    }
 
 
    sp<Client> client;
    {
        Mutex::Autolock lock(mServiceLock);
        sp<BasicClient> clientTmp;
        if (!canConnectUnsafe(cameraId, clientPackageName,
                              cameraClient->asBinder(),
                              /*out*/clientTmp)) {
            return -EBUSY;
        } else if (client.get() != NULL) {
            device = static_cast<Client*>(clientTmp.get());
            return OK;
        }
 
        int facing = -1;
        int deviceVersion = getDeviceVersion(cameraId, &facing);
 
        // If there are other non-exclusive users of the camera,
        //  this will tear them down before we can reuse the camera
        if (isValidCameraId(cameraId)) {
            // transition from PRESENT -> NOT_AVAILABLE
            updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
                         cameraId);
        }
 
        switch(deviceVersion) {
          case CAMERA_DEVICE_API_VERSION_1_0:
            client = new CameraClient(this, cameraClient,
                    clientPackageName, cameraId,
                    facing, callingPid, clientUid, getpid());
            break;
          case CAMERA_DEVICE_API_VERSION_2_0:
          case CAMERA_DEVICE_API_VERSION_2_1:
          case CAMERA_DEVICE_API_VERSION_3_0:
            client = new Camera2Client(this, cameraClient,
                    clientPackageName, cameraId,
                    facing, callingPid, clientUid, getpid(),
                    deviceVersion);
            break;
          case -1:
            ALOGE("Invalid camera id %d", cameraId);
            return BAD_VALUE;
          default:
            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
            return INVALID_OPERATION;
        }
 
        status_t status = connectFinishUnsafe(client, client->getRemote());
        if (status != OK) {
            // this is probably not recoverable.. maybe the client can try again
            // OK: we can only get here if we were originally in PRESENT state
            updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
            return status;
        }
 
        mClient[cameraId] = client;
        LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
             getpid());
    }
    // important: release the mutex here so the client can call back
    //    into the service from its destructor (can be at the end of the call)
 
    device = client;
    return OK;
}
 
cs

 현재 과정에서 보시는 바와 같이 client 에는 CameraClient() 혹은 Camera2Client() 함수가 저장되었고 이는 Camera 클래스 내의 mCamera에 저장됩니다.


 위 과정까지 마치게 된다면 카메라가 CameraService와 연결이 완료가 되면서 Camera 클래스 객체를 android_hardware_Camera.cpp 에서 실행하고 있던 초기화 함수로 return하게 됩니다.


 다음 포스팅에서는 android_hardware_Camera.cpp 에서 진행하고 있던 초기화 함수 부분에서 이어서 진행하도록 하겠습니다.



300x250

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

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

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


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

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


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


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


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

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


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

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

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


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

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

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


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


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


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

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


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


    bs = binder_open(128*1024);


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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct binder_state
{
    int fd;
    void *mapped;
    unsigned mapsize;
};
 
struct binder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;
 
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }
 
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
 
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
 
        /* TODO: check version */
 
    return bs;
 
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
}
cs

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


binder_become_context_manager(bs)


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

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

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

binder.h

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

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


binder_loop(bs, svcmgr_handler);


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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];
 
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0= BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));
 
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;
 
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
 
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
 
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
 
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
 
 
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)
{
    int r = 1;
    uint32_t *end = ptr + (size / 4);
 
    while (ptr < end) {
        uint32_t cmd = *ptr++;
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
#if TRACE
            fprintf(stderr,"  %08x %08x\n", ptr[0], ptr[1]);
#endif
            ptr += 2;
            break;
        case BR_TRANSACTION: {
            struct binder_txn *txn = (void *) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
 
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data, res);
            }
            ptr += sizeof(*txn) / sizeof(uint32_t);
            break;
        }
        case BR_REPLY: {
            struct binder_txn *txn = (void*) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                    /* todo FREE BUFFER */
            }
            ptr += (sizeof(*txn) / sizeof(uint32_t));
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            struct binder_death *death = (void**ptr++;
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }
 
    return r;
}
cs


binder.h

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



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


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

300x250

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

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

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


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


/frameworks/av/camera/CameraBase.cpp

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

/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
 
    virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs

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

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


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

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

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


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


Parcel data, reply;

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


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

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

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


http://elecs.tistory.com/87


data.writeString16(name);

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


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

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


return reply.readStrongBinder();

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

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


/frameworks/native/libs/binder/Parcel.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
 
....
 
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);
    
    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}
 
....
 
inline static status_t finish_unflatten_binder(
    BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
{
    return NO_ERROR;
}
cs

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


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

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

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


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

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

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

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


http://elecs.tistory.com/89


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
 
 
cs

return finish_unflatten_binder(

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

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



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


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs


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


binder->linkToDeath(gDeathNotifier);

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


/frameworks/av/camera/CameraBase.cpp

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


gCameraService = interface_cast<ICameraService>(binder);

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


http://elecs.tistory.com/87

http://elecs.tistory.com/88


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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
....
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
 
 
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
....
 
cs


/frameworks/av/camera/ICameraService.cpp

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


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


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

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

300x250

안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정]

 안드로이드 Binder 부분을 연구하는데 상당히 많은 지식이 필요하군요. 이제는 안드로이드 Application 단계에서는 상상도 못했던 linux 시스템 프로그래밍 단계까지 내려와보니 분석 난이도도 많이 높아졌음을 실감하고 있습니다.


 이번 포스팅에서는 안드로이드 IPC 통신의 핵심이라 할 수 있는 Binder 드라이버가 호출되는 과정을 살펴보겠습니다. 본 포스팅을 이해하기 위해서는 linux 단계에서의 시스템 프로그래밍을 이해하셔야 이해가 수월하실 듯 합니다. 그럼 분석을 시작해 보도록 하겠습니다.


 Binder 드라이버는 PrecessState가 생성될 때 함께 생성되는 구조로 되어 있습니다. 아래 코드를 보면서 이해해보도록 합시다.


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }
 
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0"Binder driver could not be opened.  Terminating.");
}
cs

  현재 ProcessSate가 생성되면서 Binder 드라이버와 System Service를 등록하기 위한 mVMStart 부분을 보고 계십니다. 여기서 우리는 바인더를 호출하는 open_driver() 부분을 살펴볼 것입니다.


mDriverFD(open_driver())

 open_driver() 함수는 binder의 file descriptor을 반환해줍니다. 이 함수를 통해 Binder가 호출됩니다.


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            ALOGE("Binder driver protocol does not match user space protocol!");
            close(fd);
            fd = -1;
        }
        size_t maxThreads = 15;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}
cs

 오늘 제가 주로 설명하게 될 코드입니다. 중요한 부분마다 자세하게 설명을 해 보도록 하겠습니다.


int fd = open("/dev/binder", O_RDWR);

 open() 함수를 통해 binder 파일을 생성합니다. 보시면 아시듯이 binder는 하나의 파일에 불과했다는 것을 아실 수 있습니다.


fcntl(fd, F_SETFD, FD_CLOEXEC);

 fd에 설정된 Binder를 Control 하는 함수 입니다. F_SETFD는 Binder인 fd에 FD_CLOEXEC 플래그를 설정하라는 의미입니다. 이를 설정해주었을 경우 exec를 통해 프로그램이 실행될 때 Binder가 close 되도록 해주는 역할을 합니다.


 FD_CLOEXEC 설정에 대해 좀 더 자세히 알고 싶으신 분은 아래 링크를 통해 확인바랍니다.(영문)

http://stackoverflow.com/questions/6125068/what-does-the-fd-cloexec-fcntl-flag-do


status_t result = ioctl(fd, BINDER_VERSION, &vers);

 Binder 의 입출력 설정을 바꾸어주는 역할을 합니다. BINDER_VERSION은 요청 코드 번호를 말하며, &vers는 포인터를 보내 값을 얻기 위해 사용합니다.


/bionic/libc/kernel/common/linux/binder.h

1
2
3
4
5
6
struct binder_version {
 signed long protocol_version;
};
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define BINDER_VERSION _IOWR('b'9struct binder_version)
cs


/bionic/libc/kernel/common/asm-generic/ioctl.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
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size)   (((dir) << _IOC_DIRSHIFT) |   ((type) << _IOC_TYPESHIFT) |   ((nr) << _IOC_NRSHIFT) |   ((size) << _IOC_SIZESHIFT))
extern unsigned int __invalid_size_argument_for_IOC;
#define _IOC_TYPECHECK(t)   ((sizeof(t) == sizeof(t[1]) &&   sizeof(t) < (1 << _IOC_SIZEBITS)) ?   sizeof(t) : __invalid_size_argument_for_IOC)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
cs

size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
 Binder의 최대 쓰레드 수를 15개로 한 후 이를 ioctl을 통해 설정해주는 모습입니다.

return fd;

return을 통해 Binder의 File Description을 반환해 줍니다.

300x250

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

안드로이드/프레임워크 2015. 3. 31. 00:05

  지난 포스팅에서 Native 단계에서 Parcel 클래스가 전송되는 과정에 대하여 다루어 보았었습니다. 이번 포스팅은 전송된 Parcel을 수신하고 해당 Parcel을 풀어보는 과정에 대해 분석해 보도록 하겠습니다.

 Parcel이 Native 단계에서 전송되는 과정에 대한 자세한 사항은 이전 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/90


지난 포스팅에서 Parcel이 전송되는 과정에 이어 Parcel이 Binder를 통해 수신되는 과정을 살펴보겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
 
    while (1) {
        if ((err=talkWithDriver()< NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0continue;
        
        cmd = mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
 
        switch (cmd) {    
....
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;
 
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t),
                            freeBuffer, this);
                    } else {
                        err = *static_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t), this);
                    continue;
                }
            }
            goto finish;
 
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
 
finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
cs

 위에서 작성된 코드들 중 중요한 부분들에 대해 살펴보겠습니다.


err=talkWithDriver()

바인더 IPC 데이터가 저장된 mOut을 Binder에 전송하며 또한 수신된 데이터를 mIn에 저장하는 과정입니다.


cmd = mIn.readInt32();

 수신된 Binder를 확인하는 부분입니다. min 내에 저장된 값을 불러내어 Parcel의 상태값을 얻어냅니다. 이후 switch()문을 통해 수신된 Parcel를 처리하는 과정을 거치게 됩니다.


 cmd에는 바인더 드라이버로부터 온 Parcel 클래스를 읽어 오게 되며 해당 부분에는 BR_REPLY가 저장되 있습니다. switch() 문을 통해 BR_REPLY가 실행되며 binder_transaction_data의 값을 수신하게 됩니다.


reply->ipcSetDataReference()

포인터로 선언되었던 reply Parcel에 데이터를 저장하는 과정입니다. 해당 함수에 대해 자세히 들여다보도록 합니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
{
    size_t minOffset = 0;
    freeDataNoInit();
    mError = NO_ERROR;
    mData = const_cast<uint8_t*>(data);
    mDataSize = mDataCapacity = dataSize;
    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid());
    mDataPos = 0;
    ALOGV("setDataReference Setting data pos of %p to %d\n"this, mDataPos);
    mObjects = const_cast<size_t*>(objects);
    mObjectsSize = mObjectsCapacity = objectsCount;
    mNextObjectHint = 0;
    mOwner = relFunc;
    mOwnerCookie = relCookie;
    for (size_t i = 0; i < mObjectsSize; i++) {
        size_t offset = mObjects[i];
        if (offset < minOffset) {
            ALOGE("%s: bad object offset %zu < %zu\n",
                  __func__, offset, minOffset);
            mObjectsSize = 0;
            break;
        }
        minOffset = offset + sizeof(flat_binder_object);
    }
    scanForFds();
}
 
 
cs


위 과정을 통해 reply Parcel의 값이 Binder에 의해 저장이 되었음을 확인하실 수 있습니다.

여기까지 실행하게 되었다면 이제 우리는 IServiceManager에서 transact() 함수를 호출했던 시점으로 다시 돌아가봅니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
....
 
}
cs

transact() 함수가 실행이 왼료되면 마지막으로 return 과정을 거치게 됩니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int32_t Parcel::readExceptionCode() const
{
  int32_t exception_code = readAligned<int32_t>();
  if (exception_code == EX_HAS_REPLY_HEADER) {
    int32_t header_start = dataPosition();
    int32_t header_size = readAligned<int32_t>();
    // Skip over fat responses headers.  Not used (or propagated) in
    // native code
    setDataPosition(header_start + header_size);
    // And fat response headers are currently only used when there are no
    // exceptions, so return no error:
    return 0;
  }
  return exception_code;
}
cs


 이로서 addService()가 Parcel을 마침으로서 하나의 Native System Service가 등록되는 과정을 살펴보았습니다. Parcel은 안드로이드 IPC통신의 핵심인 Binder에서 필히 사용되는 부분이므로 공부하시는 분들께서는 잘 참고해주셨으면 하네요.


 본 포스팅은 사실상 코드만 소개하다 보니 해당 내용이 상당히 복잡하게 보이실 겁니다. 해당 코드에 관련된 이미지 및 자세한 설명은 아래 출저의 책을 참조해주셨으면 합니다.


참고문헌 : 인사이드 안드로이드, 송형주 외 4인 저, 위키북스, 2010



300x250

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

안드로이드/프레임워크 2015. 3. 30. 21:41


 지난 포스팅에서 ServiceManager에 등록이 되는 과정에 대해 살펴보았습니다. 이번 포스팅에서는 프로세스간에 데이터를 전송하는 기능인 Parcel에 대해서 알아보도록 하겠습니다.


 시작하기에 앞서 아래 링크를 클릭하여 Java 단계에서의 Parcel 전송에 대해 이해하신 후 본 포스팅을 보신다면 이해가 좀 더 쉬워질 것입니다.


 http://arsviator.blogspot.kr/2010/10/parcelable%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EC%A0%84%EB%8B%AC-object.html


 Parcel은 안드로이드에서 사용되는 프로세스간 데이터 전송 방식으로 자신의 프로세스에서 제작된 Parcel을 상대방의 프로세스에 전송한 후 전송받은 프로세스에서 받은 Parcel을 풀어서 안에 들어있는 데이터를 사용하는 방식이라 할 수 있습니다.

 

 A프로세스에서 Parcel을 통해 자신이 보내고자 하는 데이터를 만든 후 이를 IPC통신을 통해 B프로세스에서 Parcel 데이터를 읽을 수 있게 하고 이를 받은 B프로세서에서는 Parcel을 해체하는 작업을 거치게 됩니다. 이 때 주의할 점은 A프로세스에서 Parcel을 묶었을 때의 순서에 따라 진행해야 한다는 점입니다.


 대략적인 개념은 이 정도 한 후 실제 코드를 보면서 부연 설명을 덧붙여보도록 하겠습니다.


 지난 포스팅에서 우리는 ServiceManager에 등록되는 과정에서 Parcel이 사용되는 것을 보셨을 것입니다. 이제 Parcel이 해당 코드에서는 어떤 방식으로 사용되고 있는지 분석해봅니다.


/frameworks/native/libs/binder/IServiceManager.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
....
 
}
cs

 본 코드의 addService()는 안드로이드에 사용되는 서비스를 다른 프로세스에서 사용할 수 있게 Service Manager에 등록을 하는 과정을 나타냅니다. 이 과정을 거치면 Binder를 통해 등록된 Service를 사용할 수 있게 됩니다.


 위의 코드들을 대뜸 보면 아시듯이 data 라는 명의 Parcel 클래스에 write를 통하여 정보를 순서대로 집어넣고 있는 상황을 보고 계십니다. 여기서 각각의 줄마다 어떤 기능이 사용되고 있는지 확인해 보도록 합시다.


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

ServiceManager에 등록될 서비스의 RPC 헤더를 기록하는 부분입니다.

이 때 쓰이는 IServiceManager::getInterfaceDescriptor()은 해당 소스코드 내에서는 확인하기 어렵습니다. 그 이유는 IInterface.h 헤더 파일에 #define 매크로를 통해 정의되어 있기 때문인데요 이 부분에 대해서는 아래 포스팅을 참조해 주셨으면 합니다.


http://elecs.tistory.com/87


위에서 사용된 writeInterfaceToken() 함수는 Parcel 클래스에서 ServiceManager의 RPC 헤더를 저장하게 됩니다.

/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
// Write RPC headers.  (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
               STRICT_MODE_PENALTY_GATHER);
    // currently the interface identification token is just its name as a string
    return writeString16(interface);
}
cs


data.writeString16(name);

해당 Service의 이름이 이 과정을 통해 등록됩니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
status_t Parcel::writeString16(const String16& str)
{
    return writeString16(str.string(), str.size());
}
 
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    if (str == NULL) return writeInt32(-1);
    
    status_t err = writeInt32(len);
    if (err == NO_ERROR) {
        len *= sizeof(char16_t);
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
            memcpy(data, str, len);
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}
cs

data.writeStrongBinder(service);

Service Manager에 바인더를 등록하는 과정입니다.


/frameworks/native/libs/binder/Parcel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}
 
status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }
    
    return finish_flatten_binder(binder, obj, out);
}
 
inline static status_t finish_flatten_binder(
    const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}
 
 
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;
    if (enoughData && enoughObjects) {
restart_write:
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
        
        // Need to write meta-data?
        if (nullMetaData || val.binder != NULL) {
            mObjects[mObjectsSize] = mDataPos;
            acquire_object(ProcessState::self(), val, this);
            mObjectsSize++;
        }
        
        // remember if it's a file descriptor
        if (val.type == BINDER_TYPE_FD) {
            if (!mAllowFds) {
                return FDS_NOT_ALLOWED;
            }
            mHasFds = mFdsKnown = true;
        }
 
        return finishWrite(sizeof(flat_binder_object));
    }
 
    if (!enoughData) {
        const status_t err = growData(sizeof(val));
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) {
        size_t newSize = ((mObjectsSize+2)*3)/2;
        size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize;
    }
    
    goto restart_write;
}
cs


data.writeInt32(allowIsolated ? 1 : 0);

allowIsolated 변수의 초기값은 False로 설정되어 있습니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
 
template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
 
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }
 
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
cs


status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

 transact() 함수를 실행함으로서 Parcel을 전송하게 됩니다. 전송보내게 될 data와 전송받게 될 reply 인 Parcel 클래스를 설정하신 후 실행하게 되면 시스템 서비스에 등록이 성공합니다.


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

1
2
3
4
5
6
7
8
class BpRefBase : public virtual RefBase
{
protected:
....
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
....
};
cs


위 과정을 통해 mRemote에 저장되어 있던 Binder가 반환됩니다.

여기서 ServiceManager는 BpBinder를 반환하게 됩니다.

해당 과정에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/89


BpBinder 클래스에서 transact() 함수가 동작되는 과정을 살펴보도록 하겠습니다.


/frameworks/native/include/binder/BpBinder.h
1
2
3
4
5
6
7
8
9
10
11
class BpBinder : public IBinder
{
public:
                        BpBinder(int32_t handle);
....
    virtual status_t    transact(   uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
....
}
cs


/frameworks/native/libs/binder/BpBinder.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
BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n"this, mHandle);
 
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    IPCThreadState::self()->incWeakHandle(handle);
}
 
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
 
    return DEAD_OBJECT;
}
cs

 위에서 보시게 되는 Parcel 클래스인 data는 정보를 받는 역할을, reply는 정보를 보내는 역할로 보시면 되겠습니다. 이 곳에서는 이번엔 IPCThreadState 클래스를 통해 Transact() 함수가 실행되고 있는 모습을 보고 계십니다. 해당 함수를 추적해 봅시다.


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();
 
    flags |= TF_ACCEPT_FDS;
 
    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }
    
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif
        
        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}
cs


위 코드에서 Parcel과 연관된 기능에 대해 살펴보도록 하겠습니다.

writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

바인더를 통해 Parcel을 전송할 준비를 해주는 함수로 보시면 되겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
 
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = statusBuffer;
        tr.offsets_size = 0;
        tr.data.ptr.offsets = NULL;
    } else {
        return (mLastError = err);
    }
    
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}
cs

 위의 코드에서 보시는 대로 본 함수에 전달된 인자값의 내용들이 binder_transaction_data 구조체 안에 저장되는 모습을 보실 수 있습니다.


 30번째 줄에 등장하는 mOut은 Parcel 클래스로 해당 클래스는 송신 용도로 쓰이는 Parcel 클래스로 이와 반대 역할을 하는 수신 용도로 쓰이는 Parcel 클래스인 mIn 클래스가 존재합니다.


/frameworks/native/include/binder/IPCThreadState.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class IPCThreadState
{
public:
    static  IPCThreadState*     self();
    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
....
private:
                                IPCThreadState();
                                ~IPCThreadState();
....            
            Parcel              mIn;
            Parcel              mOut;
            status_t            mLastError;
            pid_t               mCallingPid;
            uid_t               mCallingUid;
            int32_t             mStrictModePolicy;
            int32_t             mLastTransactionBinderFlags;
....
};
 
 
cs


waitForResponse(NULL, NULL);

본 함수가 실행됨으로서 바인더를 통해 Parcel이 전송됩니다!


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
 
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0continue;
        
        cmd = mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
 
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;
 
        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;
 
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t),
                            freeBuffer, this);
                    } else {
                        err = *static_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t), this);
                    continue;
                }
            }
            goto finish;
 
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
 
finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
cs

 이번 코드는 전체적인 맥락에서 상당히 중요하다 판단하여 모두 기재해 보았습니다. 해당 함수에서 중요한 부분에 대해 자세히 설명해 드리도록 하겠습니다.

err=talkWithDriver()

바인더 IPC 데이터가 저장된 mOut을 Binder에 전송하며 또한 수신된 데이터를 mIn에 저장하는 과정입니다.

아래는 talkWithDriver() 함수가 구현된 모습입니다. 상당히 복잡하게 구성되어 있으므로 전체적인 기능만 보시고 넘어가시면 되겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    
    binder_write_read bwr;
    
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();
 
    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
 
    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
            const void* cmds = (const void*)bwr.write_buffer;
            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
            alog << HexDump(cmds, bwr.write_size) << endl;
            while (cmds < end) cmds = printCommand(alog, cmds);
            alog << dedent;
        }
        alog << "Size of receive buffer: " << bwr.read_size
            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
    }
    
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
 
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(HAVE_ANDROID_OS)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
        }
    } while (err == -EINTR);
 
    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)err << ", write consumed: "
            << bwr.write_consumed << " (of " << mOut.dataSize()
                        << "), read consumed: " << bwr.read_consumed << endl;
    }
 
    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        IF_LOG_COMMANDS() {
            TextOutput::Bundle _b(alog);
            alog << "Remaining data size: " << mOut.dataSize() << endl;
            alog << "Received commands from driver: " << indent;
            const void* cmds = mIn.data();
            const void* end = mIn.data() + mIn.dataSize();
            alog << HexDump(cmds, mIn.dataSize()) << endl;
            while (cmds < end) cmds = printReturnCommand(alog, cmds);
            alog << dedent;
        }
        return NO_ERROR;
    }
    
    return err;
}
cs


cmd = mIn.readInt32();

 수신된 Binder를 확인하는 부분입니다. min 내에 저장된 값을 불러내어 Parcel의 상태값을 얻어냅니다. 이후 switch()문을 통해 수신된 Parcel를 처리하는 과정을 거치게 됩니다.


=====

 이번 포스팅을 작성하다보니 내용이 상당히 길어져 버렸군요. 수신 과정만 분석하는데만 무려 1주일이라는 시간이 지나버렸군요. 사실 이 부분까지 접근을 하다보면 필연적으로 안드로이드 내의 linux 커널 부분까지 접근하게 되는데요 커널을 다루는 것은 본 포스팅에서 다루지 않도록 하겠습니다. 안드로이드 커널과 관련된 부분은 전문 서적등을 통해 정보를 얻으시면 좀 더 정확하고 빠른 이해가 가능하실 것이라 생각합니다.


다음포스팅에서는 전송된 Parcel을 수신한 후 처리하는 과정에 대해 알아보도록 하겠습니다.

http://elecs.tistory.com/91

300x250

안드로이드 프레임워크 프로그래밍(14) [서비스 매니저 생성 및 등록과정]

안드로이드/프레임워크 2015. 3. 29. 22:57

 이번 포스팅에서는 CameraService와 같이 중요한 Native System Service를 담당하는 ServiceManager가 생성되어 동작되는 과정에 대해 살펴보도록 하겠습니다.

 

 Native 단계에서 동작하는 ServiceManager가 초기에 생성되는 과정을 살펴보던 도중 이러한 코드를 발견하게 되었습니다.


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

1
2
3
4
5
6
7
8
9
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
}
cs

 위에서 보시는 바와 같이 BpServiceManager가 public으로 Constructor가 선언되어 있는 것을 보실 수 있습니다. 이로 보아 어딘가에서 이를 통해 ServiceManager를 생성할 것으로 추정하였으나 좀처럼 쉽지가 않았습니다.


 그렇다면 ServiceManager는 어떠한 방식으로 생성이 되는지 차근차근 살펴보도록 하겠습니다.


 먼저 우리는 이전 포스팅에서 CameraService 클래스가 ServiceManager에 등록되어지는 과정을 확인한 바 있습니다.


http://elecs.tistory.com/83 - 안드로이드 프레임워크 프로그래밍(12) [IServiceManager 등록과정]


사실 위 코드에서는 ServiceManager의 생성과정이 포함되어 있음을 확인할 수 있었습니다.

/frameworks/av/media/mediaserver/main_mediaserver.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(int argc, char** argv)
{
....
    if (doLog && (childPid = fork()) != 0) {
....
    } else {
        // all other services
        if (doLog) {
            prctl(PR_SET_PDEATHSIG, SIGKILL);   
            setpgid(00);                      
        }
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }
}
cs

위에서 중요한 코드 별로 하나씩 살펴 보도록 하겠습니다.


sp<ProcessState> proc(ProcessState::self());

 ProcessState 클래스 변수를 선언합니다. 이 때 ProcessState의 Constructor의 선언은 public이 아니기 때문에 따로 마련된 함수 self()를 통해 선언합니다.


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}
cs

 위의 방식으로 ProcessState가 존재하면 바로 return으로 값을 넘기고 없을 경우 Constructor를 새로 생성해냅니다.


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }
 
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0"Binder driver could not be opened.  Terminating.");
}
cs

 위 코드를 살펴보았을 때 mmap() 를 통하여 메모리를 할당하고 있는 모습을 보실 수 있는데, 이 과정을 통해 바인더 드라이버가 프로세스에게 바인더 RPC 데이터를 저장할 때 사용하는 영역을 확보하실 수 있습니다.


sp<IServiceManager> sm = defaultServiceManager();

 위 코드를 통하여 BpServiceManager를 생성하게 됩니다. 해당 과정을 좀 더 자세히 살펴보면 다음과 같습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}
cs

 위에서 볼 수 있는 gDefaultServiceManager는 ServiceManager의 값을 가지는 변수입니다. 이 변수에 BpServiceManager가 저장되는데 해당 코드가 구현되는 과정을 면밀히 살펴보도록 하겠습니다.


ProcessState::self()->getContextObject(NULL)

/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
    return getStrongProxyForHandle(0);
}
cs

 내부를 살펴보니 또다시 return 값으로 함수가 주어져 있는 것을 보실 수 있습니다.


getStrongProxyForHandle(0);

/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
cs

/frameworks/native/include/binder/ProcessState.h

1
2
3
4
5
6
7
8
9
10
11
12
13
            struct handle_entry {
                IBinder* binder;
                RefBase::weakref_type* refs;
            };
            
            handle_entry*       lookupHandleLocked(int32_t handle);
 
            int                 mDriverFD;
            void*               mVMStart;
            
    mutable Mutex               mLock;  // protects everything below.
            
            Vector<handle_entry>mHandleToObject;
cs


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}
cs

 위 함수는 인자값으로 서비스의 handle값을 전달해 호출하는 역할을 하고 있습니다. 해당 코드에서 사용된 핸들값인 0은 ServiceManager에 고정된 서비스의 핸들입니다. 이 함수를 통해 ServiceManager의 Service Handle을 가진 BpBinder의 instance를 생성한 후에 포인터를 반환하는 작업이 이루어집니다. 즉, 해당 함수를 호출함으로서 mHandle 변수의 값이 0인 BpBinder 객체가 생성되는 것이지요.


 일단 위 과정을 통해 ProcessState::self()->getContextObject(NULL)이 BpBinder가 생성되었음을 직접 확인할 수 있었습니다. 이번에는 BpServiceManager가 생성되는 과정에 대해 알아보도록 하겠습니다.


gDefaultServiceManager = interface_cast<IServiceManager>(

                ProcessState::self()->getContextObject(NULL));


 이전에 보았던 IServiceManager에서 작성되었던 코드입니다. 이번에는 이 곳에서 interface_cast() 코드 부분을 살펴보도록 하겠습니다.

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

1
2
3
4
5
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
cs

 현재 interface_cast() 함수가 template로 선언되어 있는 모습을 보고 계십니다. 여기서 INTERFACE는 정황상 IServiceManager이고, 즉 이 곳에서는 IServiceManager의 asInterface() 함수가 실행되고 있는 모습을 보실 수 있습니다. asInterface() 함수가 실행되는 과정에 대해서는 아래 링크를 참조해 주시길 바랍니다.


http://elecs.tistory.com/87

http://elecs.tistory.com/88


/frameworks/native/include/binder/IServiceManager.h

 static android::sp<IServiceManager> asInterface(                      

            const android::sp<android::IBinder>& obj);

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

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

위 과정을 통해 ServiceManager가 BpServiceManager의 상태로 서비스에 등록되고 있는 과정을 확인하였습니다.

여기서 잠시 맨 처음에 제시하였던 코드로 다시 돌아가 보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
}
cs

 위에서 선언하였던 BpServiceManager() 생성자가 new 를 통해 생성되고 있는데 이 이후의 과정을 살펴보도록 합시다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);
 
protected:
    virtual IBinder*            onAsBinder();
};
 
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    : BpRefBase(remote)
{
}
cs

/frameworks/native/libs/binder/Binder.cpp
1
2
3
4
5
6
7
8
9
10
BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
 
    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}
cs


위 BpRefBase에서 mRemote가 초기화 되고 있는 모습을 보실 수 있습니다. 이 과정을 통해 BpBinder가 mRemote에 저장됩니다. 이때

mRemote(o.get())

 에서 get() 함수는 sp<>인 Smart Pointer입니다. Smart Pointer에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/79


/frameworks/rs/cpp/util/StrongPointer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
class sp
{
public:
    inline sp() : m_ptr(0) { }
....
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
....
private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
 
 
cs

 위 Smart Pointer인 sp 클래스 내의 함수 get()의 정의 대로 Smart Pointer 내에 저장되어 있는 BpBinder의 포인터를 mRemote에 저장하고 있는 상황인 것을 아실 수 있습니다.


참고문헌 : 인사이드 안드로이드, 송형주 외 4인 저, 위키북스, 2010

300x250

안드로이드 Native 코드 분석 : DECLARE_META_INTERFACE()

안드로이드/프레임워크 2015. 3. 28. 02:29

 안드로이드 Binder 부분에 대해 공부를 하시다 보면 다음과 같은 함수를 만나게 되는 경우가 있습니다.

/frameworks/native/include/binder/IServiceManager.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
class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager);
 
    /**
     * Retrieve an existing service, blocking for a few seconds
     * if it doesn't yet exist.
     */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
 
    /**
     * Retrieve an existing service, non-blocking.
     */
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
 
    /**
     * Register a service.
     */
    virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false= 0;
 
    /**
     * Return list of all existing services.
     */
    virtual Vector<String16>    listServices() = 0;
 
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
cs

 위 함수를 처음 만났을 때 많은 분들께서 처음에는 상당히 의야해 하실 것으로 예상합니다. 사실 저것은 함수가 아니라 #define으로 정의된 매크로 함수입니다.

 해당 부분에서 MACRO 함수를 사용하는 이유는 Native의 System Service들의 Binder를 설계하다 보면 각 서비스 별로 바인더를 설계해야 하는 경우가 발생하게 됩니다. 즉, 위에 사용된 매크로 함수는 해당 서비스들의 코드를 작성하기엔 양이 많기 때문에 위와 같은 방법을 사용하여 코드 용량을 줄인 것이라고 이해하셔도 될 듯 합니다.

 이와 같은 용도로 IMPLEMENT_META_INTERFACE() 함수도 안드로이드에서는 쓰이고 있습니다. 그렇다면 DECLARE_META_INTERFACE() 함수가 어떻게 선언되어 있는지 살펴보도록 하겠습니다.


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

1
2
3
4
5
6
7
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
cs


위 Macro 코드가 위 코드에서는 다음과 같이 적용됨을 알 수 있습니다.

/frameworks/native/include/binder/IServiceManager.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
class IServiceManager : public IInterface
{
public:
    static const android::String16 descriptor;                         
    static android::sp<IServiceManager> asInterface(                      
            const android::sp<android::IBinder>& obj);                 
    virtual const android::String16& getInterfaceDescriptor() const;   
    IServiceManager();                                                    
    virtual ~IServiceManager();                                          
 
    /**
     * Retrieve an existing service, blocking for a few seconds
     * if it doesn't yet exist.
     */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
 
    /**
     * Retrieve an existing service, non-blocking.
     */
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
 
    /**
     * Register a service.
     */
    virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false= 0;
 
    /**
     * Return list of all existing services.
     */
    virtual Vector<String16>    listServices() = 0;
 
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
cs


 위와 같은 원리로 안드로이드 Binder 관련 MACRO 함수로 IMPLEMENT_META_INTERFACE() 함수가 있습니다. 해당 매크로함수에 대한 정리는 아래 포스팅을 확인해주시기 바랍니다.

http://elecs.tistory.com/87

300x250

안드로이드 Native 코드 분석 : IMPLEMENT_META_INTERFACE()

안드로이드/프레임워크 2015. 3. 27. 00:46

 안드로이드 프레임워크를 분석하는 과정에서 시스템 서비스와 관련된 부분에 대해 분석을 하다보면 다음과 같은 이름의 함수를 만나보실 수 있을 것입니다.


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

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");


 위 함수는 겉으로 보기에는 뭔가 거창한 듯한 함수처럼 보입니다만 사실은 #define으로 정의된 매크로입니다. 이 매크로는 header 파일인 IInterface.h에 선언되어 있음을 확인하실 수 있습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
 
cs

 보시는 바와 같이 #define을 통해 정의되어 있는 것을 확인하실 수 있습니다. 이 매크로에서 맨 오른쪽 부분에 BackSlash가 처리되어 있는 것은 해당 매크로 #define이 한 줄에서 끝나지 않고 있음을 의미합니다. 그리고 내용을 확인하시다 보면 다음과 같은 내용이 있습니다.


I##INTERFACELLI##INTERFACE() { }


 여기서 '##'이란 해당 기호 앞뒤에 있는 글자를 곧바로 붙여쓰라는 의미로 이해해 주신다면 쉽게 넘어가실 수 있으리라 생각합니다.

 INTERFACE가 'ServiceManager', Name이 '"android.os.IServiceManager"'로 정의되었다는 가정하에 Macro의 결과물은 다음과 같이 나타낼 수 있습니다.


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

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

Macro를 해독하면 다음과 같은 코드가 적용되고 있음을 알 수 있습니다.

 이 함수가 어떤 부분에서 중요하다는 의미인지 지금까지 설명드린 것 만으로는 충분히 이해하실 수 없으실 겁니다. 하지만 아래의 코드를 확인해 보신다면 단박에 위 매크로의 용도를 눈치채실 수 있을 겁니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs


 처음에는 저 IServiceManager 내에 있는 getInterfaceDescriptor() 함수를 찾아보았는데 전혀 찾아낼 수가 없어서 다른 곳에서 이리저리 찾아봤건만 저렇게 Macro를 통해 구현되어 있다는 것은 꿈에도 생각하지 못했습니다! 참으로 안드로이드의 세계는 알거 같으면서도 어려운 구조로군요.

 안드로이드 프레임워크 소스 내에는 위에서 소개드렸던 매크로 외에도 다음과 같이 2종류의 Macro가 존재합니다.


#define DECLARE_META_INTERFACE(INTERFACE) 

#define CHECK_INTERFACE(interface, data, reply)


 위 두 매크로 또한 제가 위에서 설명드린 바와 같이 해석을 해두신다면 원리를 쉽게 파악하실 수 있을 것입니다!

300x250