HIDL 자바

Android 8.0에서는 기기에 독립적인 Android 플랫폼과 기기별, 공급업체별 코드 간의 인터페이스를 명확히 정의하도록 Android OS가 다시 설계되었습니다. Android는 hardware/libhardware에 C 헤더로 정의된 HAL 인터페이스 형태로 이미 많은 인터페이스를 정의했습니다. HIDL은 이러한 HAL 인터페이스를 C++로 된 클라이언트 측 및 서버 측 HIDL 인터페이스 또는 아래 설명된 자바의 안정적이고 버전이 관리되는 인터페이스로 대체했습니다.

HIDL 인터페이스는 기본적으로 네이티브 코드에서 사용하기 위한 것이므로 HIDL은 C++로 효율적인 코드를 자동 생성하는 데 중점을 둡니다. 하지만, 일부 Android 하위 시스템(예: 전화 통신)에는 자바 HIDL 인터페이스가 있으므로 HIDL 인터페이스를 자바에서 직접 사용할 수도 있어야 합니다.

이 섹션의 페이지는 HIDL 인터페이스의 자바 프런트엔드를 설명하고 서비스를 생성, 등록 및 사용하는 방법을 상세히 다루며 자바로 작성된 HAL 및 HAL 클라이언트가 HIDL RPC 시스템과 상호작용하는 방법을 설명합니다.

클라이언트

다음은 서비스 이름은 default이며 추가 서비스의 맞춤 서비스 이름은 second_impl로 등록되어 있는 android.hardware.foo@1.0 패키지의 IFoo 인터페이스를 위한 클라이언트의 예입니다.

라이브러리 추가

이를 사용하려면 상응하는 HIDL 스텁 라이브러리에 종속 항목을 추가해야 합니다. 보통 이는 정적 라이브러리입니다.

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

이미 이러한 라이브러리에서 종속 항목을 가져오고 있다면 다음과 같이 공유 연결을 사용할 수도 있습니다.

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Android 10에서 라이브러리를 추가할 때 추가 고려사항

Android 10 이상을 타겟팅하는 시스템 또는 공급업체 앱이 있는 경우 이러한 라이브러리를 정적으로 포함할 수 있습니다. 또한, 시스템 앱용 기존 uses-library 메커니즘을 통해 사용할 수 있는 안정적인 자바 API로 기기에 설치된 맞춤 JAR의 HIDL 클래스를 사용할 수도 있습니다. 후자의 접근 방식 사용 시 기기 저장공간을 절약할 수 있습니다. 자세한 내용은 자바 SDK 라이브러리 구현을 참고하세요. 이전 앱의 경우 이전 동작은 그대로 유지됩니다.

Android 10부터 이 라이브러리의 '얕은' 버전도 사용할 수 있습니다. 이러한 버전에는 문제의 클래스가 있지만 종속 클래스는 포함되지 않습니다. 예를 들어 android.hardware.foo-V1.0-java-shallow는 foo 패키지의 클래스를 포함하지만 모든 HIDL 인터페이스의 기본 클래스를 포함하는 android.hidl.base-V1.0-java의 클래스는 포함하지 않습니다. 선호하는 인터페이스의 기본 클래스를 종속 항목으로 포함하는 라이브러리를 만드는 경우 다음을 사용할 수 있습니다.

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

또한 HIDL 기본 및 관리자 라이브러리는 더 이상 앱의 부트 클래스 경로에서 사용할 수 없습니다(이전에는 Android의 위임을 우선으로 하는 ClassLoader로 인해 숨겨진 API로 사용되기도 함). 대신 이 두 항목은 jarjar를 사용하여 새 네임스페이스로 이동되었으며 이 두 항목을 사용하는 앱(priv 앱이어야 함)은 고유의 별도 사본을 보유해야 합니다. HIDL을 사용하는 부팅 클래스 경로의 모듈에서 부팅 클래스 경로에 존재하는 이러한 라이브러리의 버전을 사용하려면 이러한 자바 라이브러리의 얕은 변형을 사용하여 Android.bpjarjar_rules: ":framework-jarjar-rules"를 추가해야 합니다.

자바 소스 수정

이 서비스의 버전(@1.0)은 하나뿐이므로 다음 코드는 이 버전만 검색합니다. 여러 버전의 서비스를 처리하는 방법은 인터페이스 확장을 참고하세요.

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

서비스 제공

자바의 프레임워크 코드는 HAL에서 비동기 콜백을 수신하는 인터페이스를 제공해야 할 수 있습니다.

android.hardware.foo 패키지 버전 1.0의 IFooCallback 인터페이스는 다음 단계에 따라 자바로 인터페이스를 구현할 수 있습니다.

  1. 인터페이스를 HIDL로 정의합니다.
  2. /tmp/android/hardware/foo/IFooCallback.java를 참조로 엽니다.
  3. 자바 구현을 위한 새 모듈을 만듭니다.
  4. 추상 클래스 android.hardware.foo.V1_0.IFooCallback.Stub를 검토한 다음 이를 확장하는 새 클래스를 작성하고 추상 메서드를 구현합니다.

자동 생성된 파일 보기

자동으로 생성된 파일을 보려면 다음을 실행합니다.

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

이 명령어는 /tmp/android/hardware/foo/1.0 디렉터리를 생성합니다. hardware/interfaces/foo/1.0/IFooCallback.hal 파일의 경우 자바 인터페이스, 프록시 코드, 스텁을 캡슐화하는 /tmp/android/hardware/foo/1.0/IFooCallback.java 파일을 생성합니다. (프록시와 스텁은 인터페이스를 따라야 합니다.)

-Lmakefile은 빌드 시 이 명령어를 실행하는 규칙을 생성하고 android.hardware.foo-V1.0-java를 포함하여 적절한 파일에 링크할 수 있도록 합니다. 인터페이스로 가득한 프로젝트를 위해 이 작업을 자동으로 처리하는 스크립트는 hardware/interfaces/update-makefiles.sh에 있습니다. 이 예에서 경로는 상대적입니다. 하드웨어/인터페이스는 게시하기 전에 HAL을 개발할 수 있도록 코드 트리에서 임시 디렉터리일 수 있습니다.

서비스 실행

HAL은 IFoo 인터페이스를 제공하며, 이 인터페이스는 IFooCallback 인터페이스를 통한 프레임워크로 비동기 콜백을 만들어야 합니다. IFooCallback 인터페이스는 검색 가능한 서비스로 이름이 등록되지 않습니다. 대신 IFoosetFooCallback(IFooCallback x)과 같은 메서드를 포함해야 합니다.

android.hardware.foo 패키지 버전 1.0에서 IFooCallback을 설정하려면 android.hardware.foo-V1.0-javaAndroid.mk에 추가하세요. 서비스를 실행하는 코드는 다음과 같습니다.

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

인터페이스 확장

어떤 서비스가 모든 기기에서 IFoo 인터페이스를 구현한다고 가정하면 이 서비스는 특정 기기에서 다음과 같이 확장 인터페이스 IBetterFoo에 추가 기능을 구현하여 제공할 수 있습니다.

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

이러한 확장 인터페이스를 인식하는 호출 코드에서는 castFrom() 자바 메서드를 사용하여 기본 인터페이스를 확장 인터페이스로 안전하게 전송할 수 있습니다.

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}