안드로이드 프레임워크 프로그래밍(9) [프레임워크에 JNI를 활용해 C/C++ 코드 추가하기]

안드로이드/프레임워크 2015. 2. 17. 01:05

 안드로이드 프레임워크에 자신이 만든 System Service를 추가하여 사용하다 보면 안드로이드 기기의 하드웨어를 제어하는 Native 단계를 제어해야 하는 경우가 있습니다. Native 단계를 다루기 위해서는 불가피하게도 JNI를 통하여 C/C++로 구성된 코드를 작성해야 하는 경우가 거의 대부분입니다.


 물론 AIDL로 Stub 클래스를 통해 Java로 작성된 다른 프로세스에 접근하여 이를 제어하는 방법도 있습니다만, 항상 사용하지 못할 뿐 아니라 해당 Class가 다른 Class를 상속하는 상황이어서 Stub를 상속하지 못할 경우 이를 처리하기가 상당히 어렵습니다. 하지만 JNI를 활용하여 Native 단계에 접근한다면 좀 더 쉽게 Java로 작성된 코드의 Method를 호출할 수 있어 생각보다 유용하게 사용하실 수 있습니다.


 이번 포스팅에서는 안드로이드 프레임워크 단계에서 Java 기반으로 작성된 System Service에서 C/C++로 작성된 코드를 호출하는 방법에 대해 다루어 보도록 하겠습니다.


 혹시 JNI에 대해 자세한 내용과 예제를 참조하고 싶으신 분께서는 이전에 작성된 포스팅을 참조해 주시기 바랍니다.

http://elecs.tistory.com/71



※본 포스팅은 Ubuntu 12.04 데스크톱 에서 NEXUS5 Kitkat 4.4.4 r2 환경의 안드로이드 기기로 실행되었음을 알립니다.


 이전 포스팅에서 작성했던 Java System Service인 TestService.java에 대응하는 Native 소스코드를 작성해 보도록 하겠습니다. 이전에 작성했었던 TestService.java의 전체 코드를 확인하고자 하시는 분은 아래 링크를 참조해 주시길 바랍니다.

http://elecs.tistory.com/64


 그리고 이전에 적용하게 되는 Native 소스코드는 이전에 작성했던 JNI 활용 예제 때 사용했던 소스코드를 사용합니다. 해당 코드에 대해 알고 싶으신 분은 아래 링크를 참조해 주시길 바랍니다.

http://elecs.tistory.com/71



기존에 작성된 Java System Service에 native 소스코드를 연결해주는 코드를 작성해줍니다.


frameworks/base/services/java/com/android/server/TestService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestService extends ITestService.Stub {
 
    ....
 
    public static native String stringFromJNI();
    public static native void nativeCallJava();
 
    ....
 
    public void callJava(){
        nativeCallJava();
    }
 
    public String callString(){
        Log.i(TAG, "callString()");
        return stringFromJNI();
    }
 
    ....
 
}
cs

다음으로 위에 추가한 함수를 불러들일 수 있도록 AIDL을 수정해줍니다.


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

1
2
3
4
5
6
7
8
9
10
package android.os;
interface ITestService {
/**
* {@hide}
*/
    void setValue(int val);
    void showToast(int val);
    void callJava();
    String callString();
}
cs


다음으로 위에 작성된 native 함수에 해당하는 C 기반의 코드를 작성해 줍니다.


/frameworks/base/services/jni/com_android_server_TestService.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "jni.h"
#include "JNIHelp.h"
#include <string.h>
#include <stdio.h>
 
static void android_server_TestService_nativeCallJava(JNIEnv* env, jobject thiz){
 
    jclass jCallJava = (*env)->FindClass(env, "android/view/InputEventReceiver");
    jmethodID callJavaMethod = (*env)->GetStaticMethodID(env, jCallJava, "callJavaMethod""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, callJavaMethod);
 
}
 
static jstring android_server_TestService_stringFromJNI(JNIEnv* env, jobject thiz){
    return (*env)->NewStringUTF(env, "Hello, JNI World!");
}


끝으로 Android.mk에 새로운 소스코드를 추가하여 컴파일이 되도록 설정해줍니다.


/frameworks/base/services/jni/Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:= \
    com_android_server_AlarmManagerService.cpp \
    com_android_server_AssetAtlasService.cpp \
    com_android_server_ConsumerIrService.cpp \
    com_android_server_input_InputApplicationHandle.cpp \
    com_android_server_input_InputManagerService.cpp \
    com_android_server_input_InputWindowHandle.cpp \
    com_android_server_LightsService.cpp \
    com_android_server_power_PowerManagerService.cpp \
    com_android_server_SerialService.cpp \
    com_android_server_SystemServer.cpp \
    com_android_server_TestService.c \
    com_android_server_UsbDeviceManager.cpp \
    com_android_server_UsbHostManager.cpp \
    com_android_server_VibratorService.cpp \
    com_android_server_location_GpsLocationProvider.cpp \
    com_android_server_location_FlpHardwareProvider.cpp \
    com_android_server_connectivity_Vpn.cpp \
    onload.cpp
 
    ....
cs


 이렇게 Application 단계에서 간단하게 작성한 후 바로 실행되었던 것과는 달리 Java System service가 C 코드를 찾지 못하는 상황이 발생합니다. 도대체 무엇이 빠졌길래 이런 결과가 나온 걸까요?


 위 Android.mk 폴더 내에 코드를 보면 다음과 같이 작성된 부분을 보실 수 있습니다.

LOCAL_MODULE:= libandroid_servers


이는 libandroid_servers.so 를 만들겠다는 의미로 보시면 됩니다. 해당 파일은 SystemServer에서 호출됩니다.


/frameworks/base/services/java/com/android/server/SystemServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SystemServer {
 
    ....
 
    private static native void nativeInit();
 
    public static void main(String[] args) {
 
    ....
 
        System.loadLibrary("android_servers");
 
        Slog.i(TAG, "Entered the Android system server!");
 
        // Initialize native services.
        nativeInit();
 
        // This used to be its own separate thread, but now it is
        // just the loop we run on the main thread.
        ServerThread thr = new ServerThread();
        thr.initAndLoop();
    }
}
cs


 위 과정을 통해 Library가 호출되면서 이에 해당되는 Native 함수들이 등록됩니다. 이 때 JNI_OnLoad() 함수가 호출되는데 해당 함수는 onload.cpp 소스 내부에 정의되어 있습니다.


/frameworks/base/services/jni/onload.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
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
 
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
};
 
using namespace android;
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
 
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");
 
    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_location_FlpHardwareProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_AssetAtlasService(env);
    register_android_server_ConsumerIrService(env);
 
 
    return JNI_VERSION_1_4;
}
 


 위에서 보시는 바와 같이 jni 폴더 내부에 있던 Native 소스코드들이 호출되며 자신이 가지고 있는 함수들이 등록되고 있는 과정을 보실 수 있습니다. 자신이 작성한 Native 소스코드 내에는 자신의 함수를 등록하는 과정을 거치지 않았기 때문에 Java System Service에서는 해당 Native 함수를 부를 수 없었던 것입니다. 이번에는 자신의 Native 함수를 등록하는 과정을 보도록 하겠습니다.


 이후부터는 자신의 소스코드가 C++로 작성되었다는 것을 전재로 진행하게 됩니다. 만약 자신의 코드가 C로 작성된 분일 경우 아래 포스팅을 통해 진행해주시면 되겠습니다.


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


 우선 자신의 Native 함수를 등록할 수 있도록 onload.cpp 소스를 수정합니다.


/frameworks/base/services/jni/onload.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
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
 
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_TestService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
};
 
using namespace android;
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
 
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");
 
    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_TestService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_location_FlpHardwareProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_AssetAtlasService(env);
    register_android_server_ConsumerIrService(env);
 
 
    return JNI_VERSION_1_4;
}
cs


다음으로 자신의 소스코드에 다음 내용을 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * JNI registration.
 */
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "nativeCallJava""()V", (void*)android_server_TestService_nativeCallJava },
    { "stringFromJNI""()Ljava/lang/String;", (void*)android_server_TestService_stringFromJNI },
};
 
int register_android_server_TestService(JNIEnv* env)
{
    int res = jniRegisterNativeMethods(env, "com/android/server/TestService",
            gMethods, NELEM(gMethods));
 
    return 0;
}
cs


여기서 각 코드를 입력시 주의할 점이 있습니다.

static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "Java 코드 내에 정의한 native method의 이름""Signature", (void*)Native_소스_코드_내에_있는_함수의_이름 },
};


 위 코드에서 중간에 들어가는 Signature는 Java의 인자와 return값을 표현한 것으로 해당 함수의 특성을 간단하게 나타냈다고 볼 수 있겠습니다. 이에 대한 자세한 설명은 아래 사이트를 참조해 주시기 바랍니다.

http://forum.falinux.com/zbxe/index.php?document_srl=573117&mid=lecture_tip


 signature를 처음 접하시는 분이라면 도통 뭔소린지 도저히 이해하지 못할 분들이 많으실 겁니다. Java에서는 해당 함수의 signature를 확인할 수 있는 방법이 있습니다. 이는 linux의 Terminal에서 다음과 같은 명령어를 통해 알아볼 수 있습니다.


$ javap -s '클래스명'


자신이 알아보고자 하는 Class를 알아내기 위해서는 먼저 Native 소스를 포함하기 이전에 Java로 작성된 소스를 Build 해주셔야 합니다.


$ cd ~/kitkat

$ make


 소스코드를 Build 하시면 /out/target/common/obj/JAVA_LIBRARIES/ 폴더 내부에 소스코드의 Class들이 생성되어 있는 것을 확인하실 수 있습니다. 현재 우리가 작성하고 있는 해당 Class가 있는 폴더로 이동하면 다음과 같이 해당 Class가 있음을 확인하실 수 있습니다.

/out/target/common/obj/JAVA_LIBRARIES/services_intermediates/classes/com/android/server





이제 Terminal을 실행하신 후 해당 폴더로 이동하신 후 명령어를 입력하시면 자신이 원하는 함수의 signature가 나타나는 것을 확인하실 수 있습니다.


위에서 확인한 대로 해당 함수에 해당되는 signature를 입력해주시면 되겠습니다!

※주의!!

인자나 return 값에 class가 있을 경우 반드시 해당 클래스명의 끝에 ;(세미콜론)을 붙여주셔야 합니다!

예) String의 경우 'Ljava/lang/String;'


int register_android_server_TestService(JNIEnv* env)
{
    int res = jniRegisterNativeMethods(env, "Native 함수와 연결되는 Java 소스코드의 경로",
            gMethods, NELEM(gMethods));
 
    return 0;
}


 위에서 작성된 gMethods가 해당 함수에서 jniRegisterNativeMethods를 호출함으로서 Native 소스 내에 있는 함수들이 Java의 native로 선언된 함수와 연결됩니다. 여기까지 진행하셨다면 Framework 단계에서 Java 코드와 C 소스코드가 JNI와 연결되는 과정이 끝납니다. 다시 소스코드를 Build 하신 후 Application을 작성해 보도록 하겠습니다.


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
package com.example.test;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.ITestService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.util.Log;
 
 
public class MainActivity extends Activity {
    private static final String DTAG = "HelloServer";
    ITestService om;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
        try{
            Log.d(DTAG, "Going to call service");
            om.setValue(20);
            om.showToast(30);
            Log.d(DTAG, "Service called successfully");
        }catch(Exception e){
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
        
    }
    
    public void OnClick(View v) throws RemoteException{
        switch(v.getId()){
            case R.id.button1:
                ((TextView)this.findViewById(R.id.textview1)).setText(om.callString());
                break;
        }
    }
}
 
 
cs



참고자료 : http://blog.daum.net/baramjin/16011086

300x250

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

안드로이드/프레임워크 2015. 2. 16. 23:31

 안드로이드 소스코드를 추가하는 과정에서 기존에 작성된 소스코드를 수정하거나 새로 작성된 소스코드를 추가하는 등의 작업을 하는 경우가 있습니다. 기존에 작성된 코드를 수정하는 경우 약간의 응용만 거치면 되니 상당히 쉬운 일입니다만 새로 작성된 소스코드를 추가하는 경우 예상치 못한 경우들이 발생하는 사태가 벌어집니다. 특히 C언어에 익숙하신 분이 C로 구성된 JNI 코드를 안드로이드 프레임워크의 Native System에 적용하려 했더니 C++로 작성된 코드들 사이에 끼워 넣는 과정이 상당히 복잡할 겁니다.


 이번 포스팅에서는 안드로이드 Native 프레임워크에 작성된 C++ 코드들 사이에 C로 작성된 새로운 System Service의 Native 코드를 작성한 후 이  C코드를 기존에 C++로 작성된 코드에서 호출할 수 있는 환경을 구성해 보도록 하겠습니다.


※본 포스팅은 이전에 작성된 JNI 코드를 활용한 예제를 확장하여 다루어 볼 예정입니다. 안드로이드 JNI에 대해 자세히 알고 싶으신 분이나 이전에 작성된 코드에 대해 알고 싶으신 분은 아래 링크를 참조해 주시기 바랍니다.

http://elecs.tistory.com/71


 

 Framework에 Java로 작성된 System Service에 대응하는 Native 코드를 작성한 후 이를 JNI 폴더에 저장만 한다고 해서 해당 Native 코드를 곧바로 사용할 수 없습니다. 해당 폴더 내에 있는 onload.cpp에 해당 코드 내에 있는 함수를 등록해야 사용할 수 있는데 만약 자신이 작성한 코드가 C일 경우 바로 연동할 수 없어 조금은 난감한 일이 벌어집니다.


아래 소스코드는 기존에 JNI로 만든 안드로이드 Native Service 코드입니다.


/frameworks/base/services/jni/com_android_server_TestService.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "jni.h"
#include "JNIHelp.h"
#include <string.h>
#include <stdio.h>
 
static void android_server_TestService_nativeCallJava(JNIEnv* env, jobject thiz){
 
    jclass jCallJava = (*env)->FindClass(env, "android/view/InputEventReceiver");
    jmethodID callJavaMethod = (*env)->GetStaticMethodID(env, jCallJava, "callJavaMethod""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, callJavaMethod);
 
}
 
static jstring android_server_TestService_stringFromJNI(JNIEnv* env, jobject thiz){
    return (*env)->NewStringUTF(env, "Hello, JNI World!");
}
cs



아래 소스코드는 Native의 함수를 시스템에 등록하게 해주는 소스코드인 onload.cpp입니다.
/frameworks/base/services/jni/onload.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
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
 
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
};
 
using namespace android;
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
 
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");
 
    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_location_FlpHardwareProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_AssetAtlasService(env);
    register_android_server_ConsumerIrService(env);
 
 
    return JNI_VERSION_1_4;
}
 
cs


 위 두 코드를 통해 현재 C와 C++ 소스코드의 차이를 확연히 아실 수 있을 겁니다. C로 작성되었다면 extern을 활용해 외부의 함수를 호출할 수 있는 방법이 있겠습니다만, C++의 경우 namespace 라는 개념까지 가지고 있기 때문에 C++ 코드를 C코드 짜듯이 작성했다간 error가 작렬하는 상황이 벌어집니다.


 C++에서 C코드를 호출하는 것을 구현하기 위해 우선 두 소스코드가 공유하는 header 파일을 작성합니다.


/frameworks/base/services/jni/com_android_server_TestService.h

1
2
3
4
5
6
7
8
9
#include "jni.h"
#include "JNIHelp.h"
 
#ifndef _ANDROID_SERVER_TESTSERVICE_H
#define _ANDROID_SERVER_TESTSERVICE_H
 
int register_android_server_TestService(JNIEnv* env);
 
#endif
cs


작성된 header 파일을 각 소스코드에 include 해 주신 후 C++ 코드로부터 호출될 C 코드를 구현해 봅니다.


/frameworks/base/services/jni/onload.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
#include "jni.h"
#include "JNIHelp.h"
#include "com_android_server_TestService.h"
#include <string.h>
#include <stdio.h>
#include <utils/Log.h>
 
static void android_server_TestService_nativeCallJava(JNIEnv* env, jobject thiz){
    jclass jCallJava = (*env)->FindClass(env, "android/view/InputEventReceiver");
    jmethodID callJavaMethod = (*env)->GetStaticMethodID(env, jCallJava, "callJavaMethod""()V");
    (*env)->CallStaticVoidMethod(env, jCallJava, callJavaMethod);
 
}
 
static jstring android_server_TestService_stringFromJNI(JNIEnv* env, jobject thiz){
    return (*env)->NewStringUTF(env, "Hello, JNI World!");
}
 
/*
 * JNI registration.
 */
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "nativeCallJava""()V", (void*)android_server_TestService_nativeCallJava },
    { "stringFromJNI""()Ljava/lang/String;", (void*)android_server_TestService_stringFromJNI },
};
 
int register_android_server_TestService(JNIEnv* env)
{
    int res = jniRegisterNativeMethods(env, "com/android/server/TestService",
            gMethods, NELEM(gMethods));
 
    return 0;
}
cs

C 코드 내부에서 C++ 코드에서 호출될 int register_android_server_TestService(JNIEnv* env) 함수를 구현하였습니다. 이제 해당 함수를 C++에서 불러들이는 방법을 알아보도록 하겠습니다.


 C와 C++이 서로의 함수를 바로 사용할 수 없는 가장 큰 이유는 코드를 컴파일 할 때 C와 C++이 자신의 함수를 Linking 하는 방식이 다르기 때문입니다. C++의 경우 Overriding이 가능하여 같은 이름을 가진 함수 여러 개를 만들 수 있으나 C의 경우 단 하나의 유일한 이름의 함수를 가질 수 밖에 없습니다. 이렇게 기본적인 개념에 대한 차이가 크기 때문에 C++에서 C로 작성된 코드를 사용하기 위해서는 extern "C"를 사용해야 합니다.


1
2
3
extern "C" {
#include "com_android_server_TestService.h"
}
cs

 위의 경우 헤더파일이 C로 작성되어 있는데 해당 헤더파일이 C++에 적용되면 C++ 문법에 기반하여 컴파일이 동작하기 때문에 C로 작성된 함수를 호출할 수 없습니다. C++ 코드 내에 extern "C"라 작성된 부분은 해당 부분은 C언어에서 처럼 동작시키겠다는 의미로 동작하게 되므로 코드 내부에 해당 Header 파일을 위와 같은 방식으로 적용해 주시면 되겠습니다.


아래는 위에 작성된 코드를 적용한 후 C 소스코드를 추가하여 호출이 가능하도록 짜여진 코드입니다.

/frameworks/base/services/jni/com_android_server_TestService.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
#include "JNIHelp.h"
#include "jni.h"
extern "C" {
#include "com_android_server_TestService.h"
}
 
#include "utils/Log.h"
#include "utils/misc.h"
 
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
};
 
using namespace android;
 
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
 
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");
 
    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_TestService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_location_FlpHardwareProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_AssetAtlasService(env);
    register_android_server_ConsumerIrService(env);
 
 
    return JNI_VERSION_1_4;
}
cs




위와 같이 코드를 작성하시면 C++에서 C로 작성된 코드를 손쉽게 사용할 수 있도록 구성하실 수 있습니다.

300x250

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

 안드로이드 프레임워크를 다루다 보면 Java로 작성된 서비스 부분 뿐 아니라 C/C++로 작성된 Native 서비스 부분을 건드려야 하는 경우가 있습니다. 이를 위해서는 C/C++과 Java 사이의 매개체인 JNI를 알아야 할 필요가 있습니다. Java를 활용한 프로그래밍이 상당히 편하기는 합니다만 Java는 가상머신 위에서 동작하기 때문에 하드웨어를 직접 컨트롤 하기에는 적합하지 못하다는 단점이 있습니다. JNI는 Java 소스코드로 C/C++로 작성된 코드를 호출할 수 있기 때문에 NDK에서 JNI는 필수요소라 보아도 될 겁니다.


 아래의 예제는 Java에서 C로 작성된 코드의 String을 받아 TextView에 띄우는 Java -> C 호출 방법과 C에서 Java로 작성된 코드를 호출하여 Toast를 작동시키는 C -> Java 호출 방법에 대해 알아보겠습니다.



CallJava.zip

위 코드는 안드로이드 4.4(API 19)를 기준으로 작성되었으며 적용된 기기는 LG Nexus7 Kitkat 4.4.4임을 알립니다.


먼저 MainActivity를 다음과 같이 작성합니다.


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
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;
    
    static {
        System.loadLibrary("hello");
    }
    
    public native String stringFromJNI();
    public native void callJava();
 
    @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();
        }
    }
    
    public static void testToast(){
        Toast.makeText(mContext, "Hello, Toast!", Toast.LENGTH_SHORT).show();
    }
    
}
 
 
 
cs

코드를 작성하실 때 주의하실 점은

안드로이드에서 C -> Java를 호출할 때 Java에서 호출될 함수는 static으로 설정하여야 합니다.


다음으로 Project 폴더에 jni 폴더를 추가합니다.



다음으로 jni 폴더 내에 Android.mk 파일과 C 소스코드를 생성한 후 다음과 같이 입력합니다.


Android.mk

1
2
3
4
5
6
7
8
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello
LOCAL_SRC_FILES    := hello.c

include $(BUILD_SHARED_LIBRARY)
cs


다음으로 C 코드를 작성해 보도록 합니다.

진행하기에 앞서 자신이 호출하고자 하는 Java 함수의 Signature를 알 필요가 있습니다. 이를 확인하기 위해 Terminal을 여신 후 자신의 Class가 있는 폴더로 이동하신 후 다음과 같은 명령어를 입력합니다.


$ javap -s MainActivity


아래에서 보시는 바와 같이 해당 함수의 Signature를 확인하실 수 있습니다. 이를 자신의 코드에 적용하시면 되겠습니다.




hello.c

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


다음으로 Eclipse에서 JNI를 NDK-BUILD할 수 있는 환경을 구축합니다.

이는 아래의 포스팅을 참조해 주시길 바랍니다.

http://dsnight.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Eclipse%EC%97%90%EC%84%9C-NDKbuild-%ED%95%98%EA%B8%B0



위의 과정까지 진행하셨다면 다음과 같은 결과를 얻으실 수 있습니다.





300x250

안드로이드 프레임워크 프로그래밍(6) [Wi-Fi 접속 확인 Toast 만들기]

안드로이드/프레임워크 2015. 1. 30. 23:30

 최근 안드로이드 프레임워크 공부를 해보던 중 한 번 도전하고 싶었던 것이 있었습니다. 바로 갤럭시 S2에서 지원하던 Wi-Fi 접속 알림창이었습니다.


 ※갤럭시 S2 JellyBean 4.1에서의 모습입니다.

 위에서 보는 바와 같이 안드로이드 기기가 Wi-Fi에 접속하면 Framework를 통해 Toast가 이를 알려주는 모습을 보고 이를 Nexus 폰에도 적용해보자는 마음으로 도전하게 되었습니다.


※본 포스팅은 Nexus5 KitKat 4.4.4 환경에서 제작되었음을 알립니다.


1.  아래 굵은 표시로 추가된 소스코드를 안드로이드 소스에 추가합니다.

/frameworks/base/services/java/com/android/server/ConnectivityService.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
        @Override
        public void handleMessage(Message msg) {
            NetworkInfo info;
            switch (msg.what) {
                case NetworkStateTracker.EVENT_STATE_CHANGED: {
                    info = (NetworkInfo) msg.obj;
                    NetworkInfo.State state = info.getState();
 
                    if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
                            (state == NetworkInfo.State.DISCONNECTED) ||
                            (state == NetworkInfo.State.SUSPENDED)) {
                        log("ConnectivityChange for " +
                            info.getTypeName() + ": " +
                            state + "/" + info.getDetailedState());
        //APPEND
 
        if ( state == NetworkInfo.State.CONNECTED ){
            Toast.makeText(mContext, info.getExtraInfo().
                substring(1,info.getExtraInfo().length() - 1)
                + "에 연결되었습니다.", Toast.LENGTH_SHORT).show();
        }
 
        //END
                    }
 
                    // Since mobile has the notion of a network/apn that can be used for
                    // provisioning we need to check every time we're connected as
                    // CaptiveProtalTracker won't detected it because DCT doesn't report it
                    // as connected as ACTION_ANY_DATA_CONNECTION_STATE_CHANGED instead its
                    // reported as ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN. Which
                    // is received by MDST and sent here as EVENT_STATE_CHANGED.
                    if (ConnectivityManager.isNetworkTypeMobile(info.getType())
                            && (0 != Settings.Global.getInt(mContext.getContentResolver(),
                                        Settings.Global.DEVICE_PROVISIONED, 0))
                            && (((state == NetworkInfo.State.CONNECTED)
                                    && (info.getType() == ConnectivityManager.TYPE_MOBILE))
                                || info.isConnectedToProvisioningNetwork())) {
                        log("ConnectivityChange checkMobileProvisioning for"
                                + " TYPE_MOBILE or ProvisioningNetwork");
                        checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS);
                    }
    }
 
 
cs


2. 적용된 소스코드를 빌드하여 이미지를 새로 만듭니다.

$ make -j4


3. 안드로이드 기기에 이미지를 올린 후 동작을 확인합니다.




300x250

안드로이드 프레임워크 프로그래밍(5) [Toast 구현하기]

안드로이드/프레임워크 2015. 1. 25. 01:43

 지난 포스팅에서 새로 추가한 System service를 적용하여 Log를 통해 등록한 System service가 제대로 동작하는지에 대해 알아보았습니다. 이번에는 Framework 자체에서 Toast를 구현하는 작업을 해보도록 하겠습니다.


※본 포스팅은 지난 포스팅에서 사용하였던 예제를 수정하여 적용하였습니다. 그러므로 본 포스팅에서는 수정된 코드에 대해서만 다룰 것이며 상세한 코드는 이전 포스팅을 통해 확인해 주시기 바랍니다.


http://elecs.tistory.com/64


1. System service에 Toast 관련 소스를 입력합니다.

frameworks/base/services/java/com/android/server/TestService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.ITestService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.widget.Toast;
public class TestService extends ITestService.Stub {
    private static final String TAG = "TestService";
    private TestWorkerThread mWorker;
    private TestWorkerHandler mHandler;
    private Context mContext;
    public TestService(Context context) {
        super();
        mContext = context;
        mWorker = new TestWorkerThread("TestServiceWorker");
        mWorker.start();
        Log.i(TAG, "Spawned worker thread");
    }
 
    public void setValue(int val) {
        Log.i(TAG, "setValue " + val);
        Message msg = Message.obtain();
        msg.what = TestWorkerHandler.MESSAGE_SET;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    public void showToast(int val){
        Log.i(TAG, "showToast " + val);
        Message msg = Message.obtain();
        msg.what = 2;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    private class TestWorkerThread extends Thread {
        public TestWorkerThread(String name) {
            super(name);
        }
        public void run() {
            Looper.prepare();
            mHandler = new TestWorkerHandler();
            Looper.loop();
        }
    }
 
    private class TestWorkerHandler extends Handler {
        private static final int MESSAGE_SET = 0;
        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
        if (msg.what == 2){
            Log.i(TAG, "Show toast!!");
            Toast.makeText(mContext, "Toast message : "+msg.arg1, Toast.LENGTH_SHORT).show();
        }
        
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in TestWorkerHandler.handleMessage:", e);
            }
        }
    }
}
cs


2. AIDL 또한 변경된 System service 프로그램에 맟게 method를 추가합니다.

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

1
2
3
4
5
6
7
8
package android.os;
interface ITestService {
/**
* {@hide}
*/
    void setValue(int val);
    void showToast(int val);
}
cs



3. 여기까지 진행하셨다면 작성한 소스를 Build 해줍니다.

$ make -j4


4. 애플리케이션 프로젝트를 통해 Framework에서 Toast를 구현하는 프로그램을 작성합니다.

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
package com.example.test;
 
import android.app.Activity;
import android.app.ActivityThread;
import android.os.Bundle;
import android.os.ITestService;
import android.os.ServiceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import android.util.Log;
 
 
public class MainActivity extends Activity {
    private static final String DTAG = "HelloServer";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ITestService om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
        try{
            Log.d(DTAG, "Going to call service");
            om.setValue(20);
            om.showToast(30);
            Log.d(DTAG, "Service called successfully");
        }catch(Exception e){
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
        
    }
 
 
    @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);
    }
 
}
cs



이제 어플리케이션을 실행하면 다음과 같은 결과를 확인하실 수 있습니다.





300x250

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

안드로이드/프레임워크 2015. 1. 24. 04:13

 안드로이드 시스템 서비스란 운영체제가 동작하는 동안 수행할 수 있도록 구성된 어플리케이션 구성요소로 메시지 표시와 같이 시스템의 기본적인 기능들을 제공한다. 이러한 기능들을 활용하여 안드로이드 기기에서 동작하는 서비스를 개발하기 위한 클래스의 집합을 프레임워크라 한다. 본 포스팅은 이러한 시스템서비스를 추가하여 프레임워크의 동작 원리를 이해하는 것을 목표로 한다.


 다음은 서비스 레이어의 구조를 나타낸 것이다.

 


 상단의 Application에서 시스템서비스를 호출하면 해당 기능을 System Server에서 실행한다. 이 때 각 프로세스 간의 통신이 이루어져야 하는데 이는 IPC 또는 RPC 방식으로 이루어진다. Application과 System Server 사이에 있는 AIDL은 Application이 System Server로부터 해당 기능을 실행하는 프로세스를 호출할 수 있는 Interface를 제공하는 역할을 한다.


다음은 예제를 다루어 보도록 한다.


1. 시스템 서비스를 추가한다. 아래의 코드를 다음 경로에 생성한다.

frameworks/base/services/java/com/android/server/TestService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*TestService.java */
package com.android.server;
import android.content.Context;
import android.os.Handler;
import android.os.ITestService;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
public class TestService extends ITestService.Stub {
    private static final String TAG = "TestService";
    private TestWorkerThread mWorker;
    private TestWorkerHandler mHandler;
    private Context mContext;
    public TestService(Context context) {
        super();
        mContext = context;
        mWorker = new TestWorkerThread("TestServiceWorker");
        mWorker.start();
        Log.i(TAG, "Spawned worker thread");
    }
 
    public void setValue(int val) {
        Log.i(TAG, "setValue " + val);
        Message msg = Message.obtain();
        msg.what = TestWorkerHandler.MESSAGE_SET;
        msg.arg1 = val;
        mHandler.sendMessage(msg);
    }
 
    private class TestWorkerThread extends Thread {
        public TestWorkerThread(String name) {
            super(name);
        }
        public void run() {
            Looper.prepare();
            mHandler = new TestWorkerHandler();
            Looper.loop();
        }
    }
 
    private class TestWorkerHandler extends Handler {
        private static final int MESSAGE_SET = 0;
        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in TestWorkerHandler.handleMessage:", e);
            }
        }
    }
}
 
cs


2. 추가로 만든 서비스를 등록한다.

frameworks/base/services/java/com/android/server/SystemServer.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
 
......
 
    if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
        //Added code(363 line)
        try{
            Slog.i(TAG, "Test Service");
            ServiceManager.addService("Test"new TestService(context));
        } catch (Throwable e){
            Slog.e(TAG, "Failure starting TestService Service", e);
        }
 
            //if (!disableNonCoreServices) { // TODO: View depends on these; mock them?
            if (true) {
                try {
                    Slog.i(TAG, "Input Method Service");
                    imm = new InputMethodManagerService(context, wm);
                    ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
                } catch (Throwable e) {
                    reportWtf("starting Input Manager Service", e);
                }
 
                try {
                    Slog.i(TAG, "Accessibility Manager");
                    ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
                            new AccessibilityManagerService(context));
                } catch (Throwable e) {
                    reportWtf("starting Accessibility Manager", e);
                }
            }
        }
......
cs

3. aidl을 추가하여 애플리케이션에서 추가된 서비스에 접근할 수 있는 인터페이스를 만든다.

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

1
2
3
4
5
6
7
8
9
10
11
/*
* aidl file : frameworks/base/core/java/android/os/ITestService.aidl
* This file contains definitions of functions which are exposed by service
*/
package android.os;
interface ITestService {
/**
* {@hide}
*/
    void setValue(int val);
}
cs

4. 추가한 aidl 파일을 등록한다.

frameworks/base/Android.mk

1
2
3
4
5
6
7
8
/*
 * open frameworks/base/Android.mk and add following line
 */
......
core/java/android/os/IPowerManager.aidl \
core/java/android/os/ITestService.aidl \
core/java/android/os/IRemoteCallback.aidl \
......
cs


5. 변경사항을 적용하기 위해 소스코드를 Build 한다. 빌드 방법은 아래 블로그를 참조한다.

http://elecs.tistory.com/59


6. Build가 완료되면 새로 수정된 프레임워크 라이브러리를 추출한다. 라이브러리는 다음 경로에서 구할 수 있다.

out/target/common/obj/JAVA_LIBRARIES

자바 라이브러리들을 확인할 수 있다. 우리들이 새로 추가한 시스템 서비스는 framework_intermediate 폴더 내에 있다.


폴더 내에 classes.jar 파일이 있는데 이 안에 우리들이 만든 서비스가 들어있다. 이를 직접 확인해보면


class 파일이 내부에 있는 것을 확인할 수 있다. 이것이 안드로이드 기기 전원이 ON 상태인 동안 백그라운드에서 기기가 OFF될 때까지 계속 실행된다.


7. 이제 이를 활용한 애플리케이션을 제작해 보도록 하자. 새로운 프로젝트를 생성한 후 위에서 찾은 라이브러리를 추가한다.

Eclipse에서 Project -> Properties를 실행한다.


Java Build Path -> Libraries에 들어가면 현재 안드로이드 프로젝트 내에 있는 라이브러리들을 볼 수 있다. 위에서 확인하였던 라이브러리를 추가하기 위해 오른쪽 메뉴에 있는 "Add External JARs..."를 클릭한다.



해당 경로로 이동한 후 Classes.jar 파일을 선택한 후 확인 버튼을 누르면



위에서 보는 바와 같이 classes.jar이 추가된 것을 확인할 수 있다. OK버튼을 눌러 설정을 적용한다.


8. 이제 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
package com.example.test;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.ITestService;
import android.os.ServiceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.util.Log;
 
 
public class MainActivity extends Activity {
    private static final String DTAG = "HelloServer";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ITestService om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
        try{
            Log.d(DTAG, "Going to call service");
            om.setValue(20);
            Log.d(DTAG, "Service called successfully");
        }catch(Exception e){
            Log.d(DTAG, "FAILED to call service");
            e.printStackTrace();
        }
        
    }
 
 
    @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);
    }
}
cs


9. 안드로이드 기기에 어플리케이션을 설치한 후 실행하면 LogCat에 다음과 같은 결과가 나타나는 것을 확인할 수 있다.




출저 : Android-Adding SystemService

http://processors.wiki.ti.com/index.php/Android-Adding_SystemService

300x250

리눅스로 Nexus 기기의 안드로이드 버전 다운그레이드 하기

안드로이드/프레임워크 2015. 1. 22. 19:52

 안드로이드 프레임워크에 대해 공부하게 되면서 안드로이드의 다양한 버전들을 기기에 설치하면서 동작시켜보는 일을 하고 있습니다. 지금 시점에서 가장 최신 버전인 Lollipop까지 실행을 해보면서 말이지요.

 물론 Lollipop를 사용한 프레임워크 작업도 참으로 할 만 하다고 생각합니다만 그래도 아직은 최신버전이다 보니 구글링을 해서 구할 수 있는 정보가 한정되어 있다 보니 바로 이전 버전인 Kitkat을 사용해서 작업을 해보기로 하였습니다. 그런데 여기서 문제가 발생하고 말았습니다.



 보시는 바와 같이 Lollipop에서 하위 버전인 Kitkat을 새로 설치해서 실행해보니 이렇게 로딩 화면만 주구장창 뜨기만 하고 다음 다음 화면으로 넘어갈 생각조차 하고 있지 않는 겁니다. 혹시나 해서 다시 이미지를 씌어서 입력해도 전혀 작동이 되지 않더군요. 이거 휴대폰 하나 날려먹는거 아닌가 하고 걱정했는데 다행히도 방법을 알아낼 수 있었습니다.


그렇다면 이제 안드로이드 버전의 다운그레이드 방법에 대해 살펴보도록 하겠습니다.


1. fastboot를 이용해 자신이 설치하고자 하는 버전의 이전 버전을 휴대폰에 설치합니다.

이 과정은 이전에 작성한 포스팅을 참고해 주시기 바랍니다.


http://elecs.tistory.com/59


위 링크에서 "fastboot flashall" 까지 진행하신 후 돌아오시면 되겠습니다.


2. fastboot 모드에서 Recovery Mode로 진입합니다.



fastboot 모드에 진입하신 후 휴대폰으 음량 버튼을 조작하시면 모드를 선택하실 수 있습니다.

자신이 원하는 모드를 선택하신 후 전원 버튼을 눌러 Recovery mode에 진입합니다.



 처음 진입하시면 다음과 같이 안드로이드가 배째고 있는 모습 위에 느낌표가 써진 붉은 삼각형만 덩그러니 보이실 겁니다. 여기서 당황하지 마시고 바로 음량 Up 버튼을 누르시면



 보시는 바와 같이 설정 메뉴들이 나타납니다.


 


 여기서 볼륨 Down 버튼을 눌러 "wipe data/factory reset"를 선택한 후 전원 버튼을 누릅니다.

 이제 기기가 스스로 공장 초기화에 들어간 후 리셋되는데 다시 다운그레이드된 버전이 정상적으로 동작하는 것을 확인하실 수 있을 겁니다.

300x250

error: neither -p product specified nor ANDROID_PRODUCT_OUT set

안드로이드/프레임워크 2015. 1. 21. 23:44

안드로이드를 빌드한 후 이를 안드로이드 기기에 적용하려 할 때 이러한 오류가 발생하는 경우가 있습니다.


# fastboot flashall
error: neither -p product specified nor ANDROID_PRODUCT_OUT set


이는 아직 root 의 bash 파일에 환경변수를 새로 적용하지 않을 경우 발생합니다. 따라서 root의 bashrc를 수정함으로서 이를 해결합니다.


# vi ~/.bashrc


다음으로 아래 내용을 추가합니다.



export ANDROID_TOOLS=안드로이드 소스코드가 설치된 폴더/out/host/linux-x86
export PATH=$ANDROID_TOOLS/bin:$JAVA_HOME/bin:$PATH
export ANDROID_PRODUCT_OUT=안드로이드 소스코드가 설치된 폴더/out/target/product/컴파일 형식(에뮬의 경우 generic)





안드로이드 컴파일 형식에 관해 알고 싶으신 분은 이전에 작성된 포스팅을 참고해 주시기 바랍니다.

http://elecs.tistory.com/59


위 과정까지 완료하셨다면 새로 수정한 bash를 적용한 후 실행해봅니다.

# source ~/.bashrc

# fastboot flashall


실행시 컴파일된 이미지가 안드로이드 기기에 적용되는 것을 확인하실 수 있습니다.

300x250

make: *** [out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.jar] error 41

안드로이드/프레임워크 2015. 1. 20. 00:58

안드로이드 코드를 빌드하던 도중 다음과 같은 오류를 맞닥트리는 경우가 있습니다.


Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors
6 warnings
make: *** [out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.jar] 오류 41
make: *** 끝나지 않은 작업을 기다리고 있습니다....


이 경우 자신의 컴퓨터 환경이 다음에 해당되는지 확인합니다.


1.자신이 Build 하는 JDK의 버전이 해당 안드로이드 버전에 맞는지 확인합니다.

(GingerBread에서 Kitkat 사이에 해당되는 버전은 JDK 6을, lollipop 이후의 버전은 JDK 7을 사용해야 합니다.)


2. 컴파일 옵션을 사용하였을 경우 컴파일 도중 오작동으로 인해 위와 같은 오류가 발생하는 경우가 있습니다. 이 경우 컴파일 시간이 느려지는 것을 감수하고 옵션 없이 컴파일을 수행합니다.


$ make


만약 다음 과정을 수행해도 같은 오류가 발생시 컴파일을 처음부터 다시 수행해야 합니다.


$ make clean

$ make

300x250

make: *** [out/target/common/obj/PACKAGING/checkapi-last-timestamp] error 38

안드로이드/프레임워크 2015. 1. 18. 02:16

 안드로이드 소스를 빌드하던 도중 다음과 같은 버그를 맞닥뜨리는 경우가 있습니다.


out/target/common/obj/PACKAGING/public_api.txt:23512: error 12: Class android.telephony.gsm.SmsManager changed static qualifier
prebuilts/sdk/api/19.txt:23496: error 9: Removed public method android.telephony.gsm.SmsManager.divideMessage
prebuilts/sdk/api/19.txt:23497: error 9: Removed public method android.telephony.gsm.SmsManager.getDefault
prebuilts/sdk/api/19.txt:23498: error 9: Removed public method android.telephony.gsm.SmsManager.sendDataMessage
prebuilts/sdk/api/19.txt:23499: error 9: Removed public method android.telephony.gsm.SmsManager.sendMultipartTextMessage
prebuilts/sdk/api/19.txt:23500: error 9: Removed public method android.telephony.gsm.SmsManager.sendTextMessage
prebuilts/sdk/api/19.txt:23501: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_GENERIC_FAILURE
prebuilts/sdk/api/19.txt:23502: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_NO_SERVICE
prebuilts/sdk/api/19.txt:23503: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_NULL_PDU
prebuilts/sdk/api/19.txt:23504: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_RADIO_OFF
prebuilts/sdk/api/19.txt:23505: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_FREE
prebuilts/sdk/api/19.txt:23506: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_READ
prebuilts/sdk/api/19.txt:23507: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_SENT
prebuilts/sdk/api/19.txt:23508: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_UNREAD
prebuilts/sdk/api/19.txt:23509: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_UNSENT

******************************
You have tried to change the API from what has been previously released in
an SDK.  Please fix the errors listed above.
******************************


make: *** [out/target/common/obj/PACKAGING/checkapi-last-timestamp] 오류 38
make: *** 끝나지 않은 작업을 기다리고 있습니다....
frameworks/base/api/current.txt:23496: error 9: Removed public method android.telephony.gsm.SmsManager.divideMessage
frameworks/base/api/current.txt:23497: error 9: Removed public method android.telephony.gsm.SmsManager.getDefault
frameworks/base/api/current.txt:23498: error 9: Removed public method android.telephony.gsm.SmsManager.sendDataMessage
frameworks/base/api/current.txt:23499: error 9: Removed public method android.telephony.gsm.SmsManager.sendMultipartTextMessage
frameworks/base/api/current.txt:23500: error 9: Removed public method android.telephony.gsm.SmsManager.sendTextMessage
frameworks/base/api/current.txt:23501: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_GENERIC_FAILURE
frameworks/base/api/current.txt:23502: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_NO_SERVICE
frameworks/base/api/current.txt:23503: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_NULL_PDU
frameworks/base/api/current.txt:23504: error 10: Removed field android.telephony.gsm.SmsManager.RESULT_ERROR_RADIO_OFF
frameworks/base/api/current.txt:23505: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_FREE
frameworks/base/api/current.txt:23506: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_READ
frameworks/base/api/current.txt:23507: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_SENT
frameworks/base/api/current.txt:23508: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_UNREAD
frameworks/base/api/current.txt:23509: error 10: Removed field android.telephony.gsm.SmsManager.STATUS_ON_SIM_UNSENT
out/target/common/obj/PACKAGING/public_api.txt:23512: error 12: Class android.telephony.gsm.SmsManager changed static qualifier
out/target/common/obj/PACKAGING/public_api.txt:23512: error 27: Class android.telephony.gsm.SmsManager removed final qualifier

******************************
You have tried to change the API from what has been previously approved.

To make these errors go away, you have two choices:
   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
      errors above.

   2) You can update current.txt by executing the following command:
         make update-api

      To submit the revised current.txt to the main Android repository,
      you will need approval.
******************************


이는 다음과 같이 해결할 수 있습니다.


1. 특정 안드로이드 기기를 타겟으로 설정한 후 Build를 하실 경우 다음과 같이 멈추어버리는 경우가 있습니다. 이는 소스코드에 Binary와 Factory Image를 추가한 후 Build를 하여야 합니다. Binary와 Factory Image 추가법은 아래 블로그랠 참조해 주시기 바랍니다.

http://elecs.tistory.com/59


2. 다음과 같은 명령어를 입력합니다.


# make update-api

# make


※make 실행시 추가 옵선 (예를들어 -j4)을 걸고 Build를 하면 도중에 컴파일 오류를 뿜어내는 경우가 있습니다. 옵션 없이 make를 활용하시면 컴파일 시간은 늘어나지만 에러를 최대한으로 줄이실 수 있습니다.

300x250

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

안드로이드/프레임워크 2015. 1. 17. 23:45

 지난 포스팅에서 안드로이드 소스코드를 다운 받은 후 이를 컴파일하는 방법에 대해 알아보았습니다. 이번 포스팅에서는 소스코드를 직접 기기에 빌드하는 과정에 대해 알아보도록 하겠습니다. 작업 환경은 다음과 같습니다.


안드로이드 기기    : LG NEXUS5

빌드 버전               : Android 4.4.4(r2)

컴퓨터 운영체제    : Ubuntu 12.04(64비트)

JDK Version        : Oracle JDK 1.6


1. 자신의 안드로이드 기기에 빌드하고자 하는 안드로이드 소스코드를 다운로드 받습니다. 이 과정은 이전 포스팅을 통해 진행하실 수 있습니다.

http://elecs.tistory.com/56

본 포스팅에서 안드로이드 소스코드를 설치한 폴더는 아래와 같습니다.

~/kitkat


2. 다운로드가 완료되었다면 소스코드 빌드 환경을 초기화 합니다.

$ cd ~/kitkat

$ source build/envsetup.sh


3. 빌드 모드를 설정합니다.

$ lunch


 위에서 보시는 바와 같이 자신의 안드로이드 기기에 올리기 위한 빌드 종류를 선택해야 합니다. 다음 페이지를 참고하시면 자신의 기기에 맞는 종류를 확인하실 수 있습니다.

https://source.android.com/source/building-devices.html




 위의 사이트를 통하여 Nexus 5의 경우 aosp_hammerhead-userdebug를 사용함을 알 수 있습니다. 이를 설정하신 후 다음 단계로 넘어가시면 되겠습니다.


4. 다음으로 기기에 빌드를 하기 위해서는 해당 기기의 Binary와 Factory Image를 소스코드 폴더에 설치해야 합니다.


Binary 다운받기

https://developers.google.com/android/nexus/drivers



 자신의 기기에 해당되는 Binary를 모두 다운로드 받습니다.


Factory Image 다운받기

https://developers.google.com/android/nexus/images



 우리는 현재 4.4.4 r2를 사용하고 있으므로 해당 버전을 선택하여 다운로드 받습니다. 다운로드 받은 압축파일들의 압축을 모두 해제하신 후 해제된 파일들을 자신의 안드로이드 소스코드 폴더 상단에 다음과 같이 이동해 주시면 되겠습니다.



 5. 각 .sh 파일을 실행하여 파일을 설치합니다. 설치를 하기 위해서는 라이센스에 동의를 해야 하는데 이 과정이 살짝 골치 아픕니다.



 각 sh 파일을 실행하시면 보시는 바와 같이 라이센스 내용들이 출력됩니다. 이를 모두 읽고 동의한다만 하면 되겠지만 이 긴 글들을 일일히 읽기 귀찮아서 Enter 키를 계속 누르고 있으면 설치가 취소되는 비극(!)이 벌어집니다. 조금은 귀찮더라도 엔터키를 천천히 누르면서 동의 여부 확인이 나올 때까지 기다립니다.


동의문을 모두 읽게 되면 다음과 같이 동의 여부를 묻는 커맨드가 나옵니다. 여기서 무심코 Enter 버튼을 누르게 되면 처음부터 다시 해야 하므로 다음 커멘드가 나오면 잠시 동작을 멈춘 후 "I ACCEPT"를 입력합니다. 그러면 sh 파일이 설치되는 광경을 확인하실 수 있습니다. 다음과 같은 과정을 나머지 sh파일에서도 진행합니다.


6. 이제 빌드 조건은 모두 구축되었습니다. Make를 실행하여 본격적으로 Build 작업에 들어갑니다.


$ make update-api

$ make -j2


※make 진행시 상당히 많은 시간을 소모합니다. 만약 자신이 사용하고 있는 컴퓨터가 멀티코어를 지원할 경우 해당 코어의 갯수만큼의 옵션을 설정하시면 보다 빠르게 컴파일을 진행하실 수 있습니다. 만약 자신의 컴퓨터가 쿼드코어의 경우 다음과 같이 진행하실 수 있습니다.


$ make update-api

$ make -j4


※종종 위의 컴파일 옵션의 숫자를 너무 크게 하면 컴파일이 진행되는 도중 오류가 생기는 경우가 종종 발생합니다. 가장 안정적인 컴파일을 원하시는 분의 경우 어떠한 옵션을 설정하지 마시고 컴파일을 진행합니다.


컴파일을 무사히 끝마치실 경우 다음과 같은 화면을 보실 수 있습니다.




7. 이제 빌드한 소스코드를 안드로이드 기기에 적용해 보도록 하겠습니다. 우선 자신의 기기가 Fastboot 모드에 진입한 상태로 컴퓨터와 USB로 연결되어야 합니다. 자신이 가지고 있는 안드로이드 기기의 Fastboot 모드 진입 방법은 기기를 제공하는 회사의 웹사이트를 확인하시거나 검색 등을 통해 확인하실 수 있습니다. Nexus 5의 경우 왼쪽 음량조절 버튼의 아래버튼 + 전원버튼을 계속 누르고 있으면 Fastboot 모드에 진입하실 수 있습니다.



위에 붉은 네모로 표시한 부분을 누르시면 됩니다.

8. 위와 같은 화면이 나온다면 Fastboot 모드에 진입이 성공한 것입니다. 이제 다시 Ubuntu의 Terminal을 켜신 후 root 모드로 접근합니다.


$ su root


위의 커맨드를 입력하시면 root의 비밀번호를 묻습니다. 초기 비밀번호는 자신의 Ubuntu 로그인 아이디 혹은 0000으로 설정되어 있습니다. root 권한을 획득하셨다면 다음으로 bashrc를 수정합니다.


# vi ~/.bashrc


다음 내용을 추가합니다.


export ANDROID_TOOLS=(자신의 안드로이드 소스코드 폴더)/out/host/linux-x86
export PATH=$ANDROID_TOOLS/bin:$JAVA_HOME/bin:$PATH
export ANDROID_PRODUCT_OUT=(자신의 안드로이드 소스코드 폴더)/out/target/product/(자신의 빌드 환경)


:wq 를 입력하신 후 저장 합니다.


9. bashrc를 바로 적용합니다.


#source ~/.bashrc


10. 기기가 제대로 연결되었는지 확인합니다.


#fastboot devices



위에서 보시는 바와 같은 상황이 나온다면 안도르이드 기기가 성공적으로 연결된 것입니다. 이제 기기에 빌드를 시도해봅니다.


#fastboot oem unlock

#fastboot flashall



빌드가 완료된 안드로이드 기기의 휴대전화 정보를 확인하시면 다음과 같이 자신이 설정한 버전과 빌드 정보들을 직접 확인하실 수 있습니다.


여기까지 진행하셨다면 여러분은 드디어 안드로이드 빌드에 입문하신 겁니다!

지금까지 잘 따라오신 분들께 진심으로 축하드립니다!

중간에 에러로 인해 결과가 좋지 못하신 분들도 좀 더 자세히 들여다 보신다면 성공하실 수 있을 것입니다.




300x250

make: *** [out/target/product/generic/obj/SHARED_LIBRARIES/libwebviewchromium_intermediates/LINKED/libwebviewchromium.so] error 1

  안드로이드 커널을 다운로드한 후 열심히 빌드를 하다 보면 다음과 같은 문구가 발생합니다.


make: *** [out/target/product/generic/obj/SHARED_LIBRARIES/libwebviewchromium_intermediates/LINKED/libwebviewchromium.so] 오류 1


 이 현상은 거의 대부분이 빌드를 하는 컴퓨터의 메모리 구성에서 문제가 발생해서 생기는 것으로 보고 있습니다. 이를 해결하기 위해 다음과 같은 사항을 확인합니다.


 1. 메모리 용량이 얼마나 확보되어 있는지 확인합니다. 가상머신을 사용하시는 분의 경우 가상머신에 설정된 메모리 용량을 확인합니다.

 2. 우분투를 설치하였을 때 스왑 용량을 확인합니다. 대개 기본 설정으로 설치시 스왑 영역이 약 1~2기가 사이로 설정되어 있습니다. 이 경우 자신의 컴퓨터의 메모리가 4기가 이하의 경우 컴파일이 진행되던 도중 메모리 부족으로 프로세스가 멋대로 종료되어 버리는 경우가 발생합니다. 이를 해결하기 위해서는 처음 설치시 부터 스왑 영역을 최대한 확보하거나 설치 디스크로 부팅을 한 후 파티션을 조절하여 스왑영역을 확보합니다.


위 사항대로 수정한다면 오류 없이 컴파일이 되는 것을 확인하실 수 있습니다.


 You should have virtual memory less than 4GB. If you have the problem above, You should increase your memory size or linux-swap size.

300x250