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

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


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

 


 상단의 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

  • 양야잉 2015.06.01 07:42 ADDR 수정/삭제 답글

    안녕하세요, 컴퓨터과학과 학생입니다 ! 올려주신 글 정말 도움이 많이 되었습니다 !
    그런데 에러가 나서 여쭤보고 싶습니다.
    1. SystemServer.java 를 고치던 도중
    if (true) {
    이 부분이 없어서 추가하여 컴파일을 진행하였습니다.
    원래 맞는 방식인가요 ??

    2. 컴파일을 하고 기기에 올리려 이미지파일을 보는데 system.img만 안 나왔더라구요
    혹시 이 부분에 대해 해결한 방법을 알고계신다면 알려주실 수 있나요 ??
    참고로 기기는 nexus4, 안드로이드 4.3입니다.
    감사합니다 !

    • Justin T. 2015.06.01 14:08 신고 수정/삭제

      반갑습니다! 같은 공대생을 이렇게 만나뵙게 되어서 참으로 기쁘군요! 질문하신 내용에 대해 답변드립니다.

      1. 본 예제는 프로그램이 실행되는 과정을 보여주기 위해 진행을 하다보니 if문을 생략했습니다. 프로그램 동작에는 큰 차이는 없을겁니다.

      2. 컴파일이 완료되기 전에 관련 오류들을 살펴보셨으면 합니다. 아마 터미널 창에 에러 내역이 나올텐데 관련 내용을 구글링 하시면 답을 구하실 수 있을 겁니다.

      프레임워크 부분을 공부하는 데 생각보다 많은 시간이 들고 어려울 것입니다. 하지만 조금씩 이해하다보면 어느새 안드로이드 프레임워크의 동작을 이해하실 수 있으리라 생각합니다!

  • 진우 2015.08.23 15:43 ADDR 수정/삭제 답글

    안녕하세요
    시스템 서비스 자료를 찾는중에 몇가지 궁금한게 있는데요

    1. 시스템 서비스는 시스템 전역적인 생명주기를 갖고 일반 서비스보다 더 제약이 없이 만들수 있나요??
    2. 통신사앱 제조사앱 처럼 루트 권한으로 작동하게 할 수 있는지 궁금하네요
    3. 그리고 앱이 루트 권한으로 작동한다는건 매니페이스트에 권한 추가 없이도 제약없이 모든기능을 사용할 수 있다는걸 의미하는건가요?


    검색해보면 일반 서비스에 대한 자료는 많은데 시스템 서비스에 대한 자료는 별로 없는것같아서 문의 드리게되었습니다.

    • Justin T. 2015.08.23 18:40 신고 수정/삭제

      System Service는 흔히 Eclipse로 제작하는 안드로이드 Application과는 다른 개념의 기능입니다.

      SystemService는 Application에서 묻는 권한이라는 개념이 없기 때문에 실행되는 데에는 문제가 되지 않습니다. Application 단계에서는 사용자가 설치시 해당 권한을 Access 하는 것을 허용하는지의 여부를 위해 설정하는 것이라고 보시면 되겠습니다.

      System Service에 대한 자세한 내용은 아래 링크를 참조해 주셨으면 합니다.
      http://elecs.tistory.com/128

  • 진우 2015.08.23 20:51 ADDR 수정/삭제 답글

    아 그렇군요
    빠른 댓글 감사합니다
    링크 잘 참조하겠습니다 ^^

  • jh 2015.09.10 13:15 ADDR 수정/삭제 답글

    아죄송합니다 ! 답변달아주셔서 감사합니다운영체제를 해야겠군요!
    리눅스공부와는 별개인가요 그럼?ㅠ

    • Justin T. 2015.09.10 15:45 신고 수정/삭제

      우선 시스템프로그래밍을 먼저 공부하셨으면 합니다.
      리눅스를 기반으로한 프로그래밍으로서 이를 통해 컴퓨터의 운영체제가 기본적으로 어떻게 돌아가는지를 자세히 이해하셔야 리눅스를 공부하시던 안드로이드를 공부하시던 간에 프로그램의 흐름을 파악하실 수 있습니다.
      리눅스를 공부하신다는 것은 운영체제의 커널을 공부한다는 말씀이신듯 한데 이를 독학으로 공부하기에는 많은 시간이 소요됩니다. 실제 이 분야를 공부하는 대학원 석사 분들도 어려워 하시는 부분이기도 하고요.
      안드로이드의 경우 5단계로 구분되어 있는데 리눅스 커널은 가장 아랫단에 위치하다보니 어떻게 보면 리눅스 자체를 공부하는 것 보다 리눅스 위에서 동작하는 안드로이드 프레임워크 부분을 공부하시는 게 쉬우실 수도 있습니다.

      다만, 가장 중요한 것은 운영체제의 전반적인 이해가 필요하며, 이에 수반하는 기초를 탄탄히 하신 다음에 공부를 하는 것이 좋을 것입니다.

      아래 링크는 제가 이전에 포스팅 하였던 안드로이드 운영체제의 구성도입니다. 도움이 되셨으면 합니다.
      http://elecs.tistory.com/55

  • jh 2015.09.17 08:55 ADDR 수정/삭제 답글

    진짜 너무너무감사드립니다 정말 많은도움이되었습니다!

    시스템프로그래밍이랑 운영체제랑 다른분야인가요?2개다해야하나요 ?~

    • Justin T. 2015.09.17 10:52 신고 수정/삭제

      시스템프로그램을 이해하게 되면 운영체제도 쉽게 이해하실 수 있습니다.
      같이 병행해서 공부하신다면 꽤 도움이 되실 것입니다.

  • jh 2015.09.17 14:49 ADDR 수정/삭제 답글

    감사합니다.너무ㅠㅠㅠ
    리눅스시스템프로그래밍부터해야겠군요!!

  • 지니 2018.01.27 17:47 ADDR 수정/삭제 답글

    안녕하세요 안드로이드 분석중인 초보인데요
    글 내용은 일반적인 SDK 개발이 아니라 AOSP 나 제조사 안드로이드 소스를 받아 수정하는 PDK 같은건가요?
    그리고 AOSP나 Cyanogenmod, 제조사배포소스 중 어떤걸로 하는게 좋은건지 조언 좀 부탁드리겠습니다 ^^;;

    • Justin T. 2018.01.27 17:52 신고 수정/삭제

      보통은 제조사에서 소스코드를 공개하지 않아 AOSP를 사용하는 것으로 알고있습니다