자바 서비스 프레임워크

  • 자바 기반의 애플리케이션 프레임워크에서 동작하는 자바 시스템 서비스를 개발할 때 이용하는 클래스의 집합
  • 자바 서비스 프레임워크는 JNI를 통해 네이티브 서비스 프레임워크를 재사용함으로써 자바 레이어의 서비스 사용자가 자바로 작성된 서비스 뿐만 아니라 C++로 작성된 서비스도 이용할 수 있다.
  • 네이티브 서비스 프레임워크와의 차이점
    • 서비스 생성(자바 서비스 프레임워크에서 자바 서비스를 개발하는 방법 두 가지)
      1) Binder 클래스를 상속받아 개발하는 방식: 서비스를 정밀하게 제어할 때 적절, 자바 시스템 서비스를 개발할 때 사용
      2) Service 클래스를 상속받아 개발: 특정 작업을 주기적으로 백그라운드에서 수행하는 프로세스를 구현하는 데 사용
    • 바인더 IPC 처리: 자바 서비스 프레임워크에서는 바인더 IPC를 지원하기 위해 JNI를 통해 연결된 네이티브 서비스 프레임워크의 구성요소를 재사용

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

  • 서비스 사용자의 서비스 레이어에 매니저 클래스가 위치한다
  • RPC 레이어에 AIDL 도구로 자동 생성된 스텁과 프록시 클래스가 위치한다
  • IPC 레이어에 위치한 구성요서가 JNI를 통해 네이티브 프레임워크의 구성요소와 연결돼 있다
  • 서비스 레이어
    • 시스템 서비스 개발자는 애플리케이션 개발자가 시스템 서비스를 이용할 수 있게 SDK에 래퍼 클래스를 포함시켜야 한다
  • RPC 레이어
    • AIDL 언어와 컴파일러를 이용해 서비스 프록시와 서비스 스텁을 자동으로 생성한다
    • AIDL은 안드로이드에서 프로세스 간의 IPC를 통해 상호작용하는 자바 기반의 코드를 작성하는 데 사용되는 인터페이스 정의 언어이다
  • IPC 레이어
    • 서비스와 서비스 프록시가 상호작용 할 때도 바인더 IPC를 이용한다
    • 바인더 IPC를 위해 자바 서비스 프레임워크에서는 BinderProxy와 Binder 클래스가 이용된다
    • 자바 서비스 프레임워크에서는 JNI를 통해 네이티브 서비스 프레임워크의 바인더 IPC를 재사용 한다

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

  • 자바 서비스를 시스템에 등록하는 과정에서 자바 서비스 프레임워크 구성요소가 상호작용하는 과정
    1) 서비스 등록 요청(서비스)
    자바 서비스 매니저인 ServiceManager 클래스를 이용해 처리한다
    FooService는 ServiceManager의 addService() 메서드를 호출하여 시스템에 등록한다
    ServiceManager 내부에는 BinderProxy가 있다
    BinderProxy는 컨텍스트 매니저를 가리키는 BpBinder와 JNI를 통해 연결
    2) 서비스 등록(서비스 매니저)
    서비스 프록시는 addService() 메서드의 호출 정보를 RPC 데이터로 변환
    바인더 RPC 데이터는 Parcel 클래스에 저장되어 BinderProxy에 전달되고, JNI를 통해 BpBinder에 전달된다. 바인더 IPC를 통해 컨텍스트 매니저에 전달되어 FooService 서비스가 시스템에 등록된다.
    3) 서비스 검색 요청(서비스 사용자)
    SDK에서 제공하는 getSystemService() 메서드를 호출해서 서비스를 검색
    4) 서비스 검색(서비스 매니저)
    getSystemService()는 getService() 메서드를 호출. 서비스가 검색되면 서비스 프록시를 참조하는 매니저를 서비스 사용자에게 반환
    5) foo() 서비스 프록시 메서드 호출(서비스 사용자)
    메서드 호출 정보를 RPC 데이터로 변환한 다음 BpBinder에 전달
    6) foo() 서비스 스텁 메서드 실행(서비스)
    바인더 RPC 데이터를 전달 받아 Binder의 메서드 호출
    데이터를 분석하여 서비스 스텁 메서드

동작 메커니즘

  • 자바 서비스 프레임워크가 네이티브 서비스 프레임워크를 재사용하는 메커니즘 이해

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

  • app_process 실행 -> AndroidRuntime 클래스에서 startReg() 함수 호출 -> JNI 네이티브 함수가 달빅 가상 머신으로 로딩 -> register_android_os_Binder() 함수를 호출해서 JNI 네이티브 함수 등록(자바 서비스 프레임워크와 관련된 네이티브 함수)

Binder 클래스의 JNI 설정

  • int_register_android_os_Binder() 함수 호출 -> Binder 클래스 정보를 전역 변수인 gBinderOffsets에 저장 -> Binder 클래스의 네이티브 메서드와 JNI 네이티브 함수를 매핑

Binder 객체 생성

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

JavaBBinder 객체 생성

  • JavaBBinder의 인스턴스는 JavaBBinderHolder의 get() 함수에서 생성된다.
  • 요약하면, Binder의 생성자가 호출 -> 네이티브 메서드인 init()이 호출 -> 연결된 JNI 네이티브 함수에 의해 JavaBBinderHolder 객체가 생성 -> init() 메서드를 호출한 Binder 객체의 주소를 인자값으로 받아 mObject 변수에 저장 -> JavaBBinder는 JavaBBinderHolder의 get() 함수를 호출하면 생성 -> JavaBBinder의 생성자는 JavaBBinderHolder의 mObject 변수를 인자로 받아 자신의 mObject 변수에 저장

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

  • BBinder에서 기본으로 제공하는 바인더 RPC 함수 이외에 새로운 기능을 제공하려면 BBinder를 상속받은 서비스 스텁 클래스에서 onTransact() 메서드를 재정의해야한다

BinderProxy 클래스를 위한 JNI 설정

  • int_register_android_os_BinderProxy() 호출 -> BinderProxy 클래스 정보를 gBinderProxyOffsets 전역 변수에 저장 -> BinderProxy 클래스의 네이티브 메서드와 JNI 네이티브 함수를 매핑

BinderProxy 객체 생성

  • BinderProxy 클래스도 바인더 IPC를 수행하기 위해 네이티브 서비스 프레임워크의 BpBinder의 기능을 사용하므로 BinderProxy 객체가 생성될 때 BpBinder 객체가 필요
  • BpBinder 객체는 Parcel의 readStrongBinder() 함수에서 생성

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

  • BinderProxy의 transact() 네이티브 메서드는 android_os_BinderProxy_transact() JNI 네이티브 함수로 매핑되어 있으므로 BinderProxy와 BpBinder가 상호작용할 때 JNI 네이티브 함수가 호출된다

Parcel 클래스의 JNI 설정

  • Parcel 클래스는 바인더 IPC가 진행되는 동안 송신측에서 수신측으로 전달되는 데이터를 저장하는 데 사용한다
  • Parcel은 내부 버퍼 안에 IBinder 객체 레퍼런스를 가지고 있어 프로세스를 가로질러 이동할 대도 레퍼런스 값을 유지해야 한다

Parcel 클래스의 JNI 설정

  • int_register_android_os_BinderProxy() 함수 호출 -> Parcel 클래스의 정보를 gParcelOffsets 전역 변수에 저장 -> Parcel 클래스의 네이티브 메서드와 JNI 네이티브 함수를 매핑

Parcel 객체 생성

  • Parcel의 생성자는 private으로 선언, new Parcel() 형태로 인스턴스를 생성할 수 없다
  • Parcel 인스턴스를 획득하기 위해 Parcel의 obtain() 메서드를 사용

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

  • Parcel(Java) 클래스는 서비스 프록시에서 바인더 RPC 데이터를 저장할 때 사용
  • 서비스 프록시에서 바인더 RPC를 진행하면 달빅 가상 머신에서 생성된 Parcel(Java) 객체가 서비스에게 전달되어야 하는데 그러기 위해서는 Parcel(Java) 객체를 Parcel(C++) 객체로 변환해야 한다

자바 시스템 서비스 구현

  • 알람 매니저 서비스를 토대로 시스템 서비스의 구조 파악

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

  • AlaramManagerService 클래스 계층 구조
    • 자바 서비스 프레임워크의 구성요소인 IInterface 인터페이스, binder 클래스
    • IAlarmManager 서비스 인터페이스와 서비스 스텁에서 위 클래스 상속
    • AIDL을 통해 자동으로 생성된 서비스 스텝 클래스와 서비스 플록시 클래스
    • AlarmManagerService 클래스와 AlarmManager 래퍼 클래스

알람 매니저 서비스 구현 방식

  • AIDL을 이용하여 클래스를 자동으로 생성
  • AIDL 컴파일러로 컴파일하면 알람 매니저 서비스의 서비스 인터페이스, 서비스 프록시, 서비스 스텁 클래스가 자동으로 생성

알람 매니저 서비스 사용

  • 시스템 서비스를 사용하려면 SDK의 getSystemService() 메서드를 이용해야 한다
  • 알람 매니저 서비스는 Context 클래스의 ALARM_SERVICE 변수를 인자로 getSystemService() 메서드를 호출하면 애플리케이션에서 사용할 수 있다