10 자바 서비스 프레임워크

5 minute read

10.1 자바 서비스 프레임워크

자바 서비스 프레임워크는 자바 기반의 애플리케이션 프레임워크에서 동작하는 자바 시스템 서비스를 개발할 때 이용하는 클래스의 집합.

자바 서비스 프레임워크는 JNI를 통해 네이티브 서비스 프레임워크를 재사용 함으로써 자바 레이어의 서비스 사용자가 자바로 작성된 서비스뿐만 아니라 C++로 작성된 서비스도 이용할 수 있게됨.

자바 서비스 프레임워크와 네이티브 서비스 프레임워크의 차이점 은 아래와 같음.

  • 서비스 생성: 자바 서비스 프레임워크에서 자바 서비스를 개발하는 방법은 두가지. 첫 번째 방법은 Binder 클래스를 상속받아 개발하는 방식, 두 번째 방법은 Service 클래스를 상속받아 개발하는 방식.

  • 바인더 IPC 처리: 자바 서비스 프레임워크에서는 바인더 IPC를 지원하기 위해 JNI를 통해 연결된 네이티브 서비스 프레임워크의 구성요소를 재사용.

자바 서비스를 관리하는 방법은 1) 컨텍스트 매니저에 서비스를 등록 후 서비스 매니저를 통해 서비스 사용 , 2) 컨텍스트 매니저가 아닌 핵심 자바 시스템 서비스인 액티미니 매니저 서비스에서 관리.

10.1.1 자바 서비스 프레임워크의 계층별 요소

Scanner_IMG_2019-05-24 10-53-16

  • FooService 서비스를 자바 서비스 프레임워크를 이용해서 구현했다고 가정.

  • 서비스 사용자의 서비스 레이어 에 매니저 클래스 위치.

  • RPC 레이어 에 AIDL 도구로 자동 생성된 스텁(Stub)과 프록시(Proxy) 클래스가 위치.

  • IPC 레이어 에 위치한 구성요소가 JNI를 통해 네이티브 서비스 프레임워크의 구성요소와 연결. 즉, 네이티브 서비스 프레임워크의 바인더 IPC를 재사용.

서비스 레이어에서 FooManager 클래스를 구현하는 이유는 SDK에 ServiceManager 클래스가 포함되지 않아 이를 이용하여 시스템 서비스를 등록하거나 또는 시스템 서비스를 검색할 수 없기 때문.

Scanner_IMG_2019-05-24 10-53-44

10.1.2 자바 서비스 프레임워크의 클래스별 상호작용

자바 서비스 프레임워크는 바인더 RPC를 지원하기 위해 JNI를 통해 네이티브 서비스 프레임워크의 기능을 재사용하므로 서비스 클라이언트와 서비스 서버 내부의 구성요소 간에 수직 방향 으로 상호작용이 이루어짐.

Scanner_IMG_2019-05-24 10-54-05

Scanner_IMG_2019-05-24 10-54-33

FooService 서비스를 시스템에 등록하고 사용하는 과정을 통해 자바 서비스 프레임워크 구성요소가 상호작용하는 과정을 보면 아래와 같음.

Scanner_IMG_2019-05-24 10-55-04

(1) 서비스 등록 요청(서비스): 자바 서비스 매니저인 ServiceManager 클래스를 이용해 처리. ServiceManager 클래스의 addService() 메서드를 호출하여 등록.

(2) 서비스 등록(서비스 매니저): ServiceManagerProxy 서비스 프록시는 addService() 메서드의 호출 정보를 RPC 데이터로 변환. 이 데이터는 Parcel 클래스에 저장되어 BinderProxy -> BpBinder -> 컨텍스트 매니저 순으로 전달되어 FooService 서비스가 시스템에 등록.

(3) 서비스 검색 요청(서비스 사용자): SDK에서 제공하는 getSystemService() 메서드를 호출해서 서비스를 검색.

(4) 서비스 검색(서비스 매니저): getSystemService()는 ServiceManager의 getService() 메서드를 호출해 시스템에서 FooService 서비스를 검색.

(5) foo() 서비스 프록시 메서드 호출(서비스 사용자)

(6) foo() 서비스 스텁 메서드 실행(서비스)

자바 서비스 프레임워크의 가장 중요한 특징은 JNI를 통해 네이티브 서비스 프레임워크의 기능을 재사용한다는 점.

특히 IPC 레이어에 위치한 바인더 IPC 처리를 위해 BinderProxy와 Binder 클래스가 JNI를 통해 BpBinder와 BBinder 클래스의 기능을 재사용한다는 점을 주목.


10.2 동작 메커니즘

10.2.1 자바 서비스 프레임워크 초기화

register_android_os_Binder() 함수를 호출해서 등록되는 JNI 네이티브 함수들이 자바 서비스 프레임워크와 관련 있는 함수들.

10.2.2 Binder

Binder 클래스의 JNI 설정

Binder 클래스를 사용하려면 달빅 가상 머신에 Binder의 네이티브 메서드를 위한 JNI 네이티브 함수를 등록해주어야 함.

int register_android_os_Binder() 함수가 호출되면 Binder 클래스의 일부 정보를 전역 변수인 gBinderOffsets에 저장하고, Binder 클래스의 네이티브 메서드와 JNI 네이티브 함수를 매핑.

Scanner_IMG_2019-05-24 10-55-34

Binder 객체 생성

Binder 클래스는 바인더 IPC를 위해 BBinder의 기능을 사용하기 때문에 Binder 객체가 생성될 때 BBinder가 함께 생성되어야 함.

Binder는 생성자에서 init() 네이티브 메서드를 호출하며, 이는 JNI의 android_os_Binder_init() 함수와 연결.

Scanner_IMG_2019-05-24 10-56-10

  • JavaBBinderHolder 클래스의 객체 생성 후 SetIntField() JNI 함수를 이용해 Binder의 mObject 변수에 생성된 JavaBBinderHolder 인스턴스의 주소를 저장.

JavaBBinder 객체 생성

Scanner_IMG_2019-05-24 10-56-44

Scanner_IMG_2019-05-24 10-57-43

실제로 JavaBBinder의 인스턴스는 JavaBBinderHolder의 get() 함수에서 생성됨.

JavaBBinder는 BBinder를 상속받아 구현한 클래스이므로 JavaBBinder 객체를 생성하면 BBinder 객체도 생성.

Scanner_IMG_2019-05-24 10-58-17

Binder 클래스와 JavaBBinder 서비스 스텁 클래스의 상호작용

Binder 클래스를 상속한 JavaBBinder 서비스 스텁 클래스는 onTransact() 함수에서 Binder의 execTransact() 메서드를 호출.

Binder 클래스의 execTransact() 메서드가 호출되면 그 메서드 안에서 onTransact() 메서드를 호출.

10.2.3 BinderProxy

BinderProxy 클래스를 위한 JNI 설정

BinderProxy 클래스의 사용을 위해 먼저 달빅 가상 머신에 BinderProxy의 네이티브 메서드를 위한 JNI 네이티브 함수를 등록.

BinderProxy 클래스의 일부 정보는 gBinderProxyOffsets 전역변수에 저장하고, BinderProxy 클래스의 네이티브 메서드와 JNI 네이티브 함수를 매핑.

BinderProxy의 생성자 메서드 ID를 mConstructor 변수에 저장하는데, 이 생성자 메서드 ID는 JNI 네이티브 함수에서 BinderProxy 객체를 생성할 때 사용.

BinderProxy 객체 생성

Scanner_IMG_2019-05-24 10-58-42

앞서 언급한 생성자 메서드 ID를 통해 NewObject() JNI 함수를 호출하여 BinderProxy 객체를 생성한 다음 BinderProxy mObject 변수에 BpBinder 객체를 저장.

BinderProxy 클래스와 BpBinder 클래스의 상호작용

BinderProxy의 transact() 네이티브 메서드가 호출되면 BpBinder의 transact() 메서드를 호출.

10.2.4 Parcel

Parcel 클래스는 바인더 IPC가 진행되는 동안 송신측에서 수신측으로 전달되는 데이터를 저장하는 데 사용.

이는 내부 버퍼 안에 IBinder 객체 레퍼런스를 가지고 있어 프로세스를 가로질러 이동할 때도 레퍼런스 값을 유지해야 함.

이런 기능을 자바 서비스 프레임워크에서도 제공하기 위해 JNI를 통해 Parcel 클래스의 기능을 재사용.

Parcel 클래스의 JNI 설정

Parcel 클래스의 네이티브 메서드는 JNI 함수를 통해 Parcel 클래스에 포함된 이름이 동일한 멤버 함수를 대부분 호출.

Parcel 객체 생성

Parcel은 new Parcel()의 형태로 Parcel 클래스의 인스턴스 생성 불가. Parcel의 obtain() 매서드드를 사용하여 생성.

obtain() 메서드에서는 Parcel의 생성자가 호출되고, 생성자 내부에서는 init() 네이티브 메서드가 호출되어 JNI로 매핑된 android_os_Parcel_init() 함수가 실행되면 C++ Parcel 클래스의 인스턴스를 생성함.

C++ Parcel 클래스와 Java Parcel 클래스간의 상호작용

Scanner_IMG_2019-05-24 10-59-01


10.3 자바 시스템 서비스 구현

10.3.1 알람 매니저 서비스의 구조 분석

Scanner_IMG_2019-05-24 10-59-38

  • AlarmManagerService 서비스 클래스는 IAlarmManager.Stub 서비스 스텀 클래스를 상속하여 알람 매니저 서비스의 실질적인 기능을 구현.

알람 매니저 서비스는 AIDL을 이용해 해당 클래스를 자동으로 생성. 알람 매니저 서비스의 서비스 인터페이스, 서비스 프록시, 서비스 스텁 클래스를 자동으로 생성.

Scanner_IMG_2019-05-24 11-14-27

  • Binder 클래스의 onTransact() 메서드를 재정의하여 시스템의 바인더 RPC 기능을 추가하므로 알람 매니저 서비스의 서비스 스텁 클래스도 onTransact() 메서드를 재정의하여 IAlarmManager 인터페이스에 정의된 5개의 메서드 관련 코드를 추가.

알람 매니저 서비스 사용

애플리케이션 개발자가 시스템 서비스를 사용하려면 SDK의 getSystemService() 메서드를 이용해야 함.

알람 매니저는 Context 클래스의 ALARM_SERVICE 변수를 인자로 getSystemService() 메서드를 호출하면 애플리케이션에서 사용 가능.

10.3.2 HelloWorldService 시스템 서비스의 구현

Scanner_IMG_2019-05-24 11-14-53

HelloWorldService 구현

Scanner_IMG_2019-05-24 11-15-43

  • 서비스 인터페이스, 서비스 프록시, 서비스 스텁을 AIDL을 이용해 자동 구현.

Scanner_IMG_2019-05-24 11-45-59

  • ServerThread의 run() 메서드에서 HelloWorldService의 인스턴스를 생성하고 자바 서비스 매니저의 addService() 메서드를 통해 시스템에 등록.

Context 클래스에 HELLO_SERVICE 문자열 상수 추가.

10.3.3 HelloWorldService 시스템 서비스의 이용

HelloWorldManager 구현

Scanner_IMG_2019-05-24 11-46-17

HelloWorldManager 획득

Scanner_IMG_2019-05-24 11-44-31

(1) 서비스 사용자는 HelloWorldService 서비스를 이용하기 위해 getSystemService() 메소드를 호출. getSystemService() 메소드 내부에서 getHelloWorldManager() 메소드를 호출.

(2) getHelloWorldManager() 메소드는 서비스 매니저에게 HelloWorldService 서비스의 검색을 요청. 서비스 매니저는 서비스 검색에 성공하면 HelloWorldService 서비스를 가리키는 BinderProxy 객체를 반환.

(3) getHelloWorldManager() 메소드는 반환받은 BinderProxy 객체를 HelloWorld.Stub 서비스 스텁의 asInterface() 메소드에 넘겨주어 IHelloWorld.Stub.Proxy 서비스 프록시 객체를 생성.

(4) getHelloWorldManager() 메소드는 생성한 IHelloWorld.Stub.Proxy 서비스 프록시 객체를 HelloWorldManager 생성자의 인자로 넘겨주어 객체를 생성. HelloWorldManager는 mService 변수에 넘겨 받은 IHelloWorld.Stub.Proxy 서비스 프록시 객체를 저장.

(5) 최종적으로 HelloWorldService 서비스를 사용하기 위해 getSystemService() 메소드를 호출한 곳에서는 HelloWorldManager의 객체를 반환받음. 어플리케이션은 HelloWorldService 서비스를 이용하기 위해 HelloWorldManager의 메소드를 호출하면 IHelloWorld.Stub.Proxy 서비스 프록시의 메소드가 호출되어 HelloWorldService 서비스와 바인더 RPC를 사용하여 상호작용.

HelloWorldService를 이용하기 위해 getSystemService() 메소드를 Context.HELLO_SERVICE 인자값과 함께 호출한 후 반환값을 HelloWorldManager 타입으로 형변환하면 서비스를 이용 가능.

10.3.4 HelloWorldService 시스템 서비스 빌드

(1) make 명령어로 안드로이드 플랫폼 컴파일.

(2) HelloWorldManager 클래스에 “@hide” 주석을 추가하거나 make update-api로 SDK의 공개 API 정보를 갱신.

(3) 컴파일이 정상적으로 끝나면 system.img 파일이 생성. 이를 /platforms/android-8/images에 복사.

(4) 애뮬레이터 실행 후, HelloWorldManager를 통해 HelloWorldService를 이용하는 애플리케이션을 작성. 메서드 호출 후 로그 메시지 확인.

(5) adb의 셸 프롬프트상에서 정상 동작 하는지 확인.

Tags:

Categories:

Updated: