안정적 AIDL

Android 10에는 AIDL 인터페이스에서 제공하는 애플리케이션 프로그램 인터페이스(API)/애플리케이션 바이너리 인터페이스(ABI)를 추적하는 새로운 방법인 안정적인 AIDL(Android 인터페이스 정의 언어)에 대한 지원이 추가되었습니다. 안정적인 AIDL은 AIDL과 다음과 같은 주요 차이점이 있습니다.

  • 인터페이스는 aidl_interfaces 사용하여 빌드 시스템에서 정의됩니다.
  • 인터페이스에는 구조화된 데이터만 포함될 수 있습니다. 원하는 유형을 나타내는 Parcelable은 AIDL 정의에 따라 자동으로 생성되며 자동으로 마샬링 및 마샬링 해제됩니다.
  • 인터페이스는 안정(역호환)으로 선언될 수 있습니다. 이런 일이 발생하면 해당 API가 AIDL 인터페이스 옆에 있는 파일에서 추적되고 버전이 지정됩니다.

구조화된 AIDL과 안정적인 AIDL

구조화된 AIDL은 순수하게 AIDL에 정의된 유형을 나타냅니다. 예를 들어, Parcelable 선언(사용자 정의 Parcelable)은 구조화된 AIDL이 아닙니다. AIDL에 정의된 필드가 있는 Parcelable을 구조화된 Parcelable 이라고 합니다.

안정적인 AIDL에는 빌드 시스템과 컴파일러가 Parcelable에 대한 변경 사항이 이전 버전과 호환되는지 이해할 수 있도록 구조화된 AIDL이 필요합니다. 그러나 모든 구조화된 인터페이스가 안정적인 것은 아닙니다. 안정성을 유지하려면 인터페이스는 구조화된 유형만 사용해야 하며 다음 버전 관리 기능도 사용해야 합니다. 반대로, 인터페이스를 빌드하는 데 핵심 빌드 시스템을 사용하거나 unstable:true 가 설정된 경우 인터페이스는 안정적이지 않습니다.

AIDL 인터페이스 정의

aidl_interface 의 정의는 다음과 같습니다.

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name : AIDL 인터페이스를 고유하게 식별하는 AIDL 인터페이스 모듈의 이름입니다.
  • srcs : 인터페이스를 구성하는 AIDL 소스 파일 목록입니다. com.acme 패키지에 정의된 AIDL 유형 Foo 의 경로는 <base_path>/com/acme/Foo.aidl 에 있어야 합니다. 여기서 <base_path> Android.bp 가 있는 디렉터리와 관련된 디렉터리일 수 있습니다. 위의 예에서 <base_path>srcs/aidl 입니다.
  • local_include_dir : 패키지 이름이 시작되는 경로입니다. 위에서 설명한 <base_path> 에 해당합니다.
  • imports : 이것이 사용하는 aidl_interface 모듈의 목록입니다. AIDL 인터페이스 중 하나가 인터페이스 또는 다른 aidl_interface 의 소포 가능 항목을 사용하는 경우 여기에 해당 이름을 입력하세요. 최신 버전을 나타내기 위해 이름 자체일 수도 있고, 특정 버전을 나타내기 위해 버전 접미사(예: -V1 )가 붙은 이름일 수도 있습니다. 버전 지정은 Android 12부터 지원됩니다.
  • versions : api_dir 아래에 고정된 이전 버전의 인터페이스입니다. Android 11부터 versions aidl_api/ name 아래에 고정됩니다. 인터페이스의 고정 버전이 없으면 이를 지정해서는 안 되며 호환성 검사도 수행되지 않습니다. 13 이상에서는 이 필드가 versions_with_info 로 대체되었습니다.
  • versions_with_info : 각각 고정된 버전의 이름과 이 버전의 aiml_interface가 가져온 다른aidl_interface 모듈의 버전 가져오기 목록이 포함된 튜플 목록입니다. AIDL 인터페이스 IFACE의 버전 V 정의는 aidl_api/ IFACE / V 에 있습니다. 이 필드는 Android 13에서 도입되었으며 Android.bp에서 직접 수정하면 안 됩니다. 필드는 *-update-api 또는 *-freeze-api 호출하여 추가되거나 업데이트됩니다. 또한 사용자가 *-update-api 또는 *-freeze-api 호출하면 versions 필드가 versions_with_info 로 자동으로 마이그레이션됩니다.
  • stability : 이 인터페이스의 안정성을 약속하는 선택적 플래그입니다. 현재는 "vintf" 만 지원합니다. 설정되지 않은 경우 이는 이 컴파일 컨텍스트 내에서 안정성이 있는 인터페이스에 해당합니다(따라서 여기에 로드된 인터페이스는 예를 들어 system.img에서 함께 컴파일된 항목에만 사용할 수 있습니다). 이것이 "vintf" 로 설정되면 이는 안정성 약속에 해당합니다. 인터페이스는 사용되는 동안 안정적으로 유지되어야 합니다.
  • gen_trace : 추적을 켜거나 끄는 선택적 플래그입니다. Android 14부터 cppjava 백엔드의 기본값은 true 입니다.
  • host_supported : true 로 설정하면 생성된 라이브러리를 호스트 환경에서 사용할 수 있게 만드는 선택적 플래그입니다.
  • unstable : 이 인터페이스가 안정적일 필요가 없음을 표시하는 데 사용되는 선택적 플래그입니다. true 로 설정되면 빌드 시스템은 인터페이스에 대한 API 덤프를 생성하지도 않고 업데이트하도록 요구하지도 않습니다.
  • frozen : true 로 설정된 경우 인터페이스에 이전 버전의 인터페이스 이후 변경 사항이 없음을 의미하는 선택적 플래그입니다. 이를 통해 더 많은 빌드 시간 검사가 가능해졌습니다. false 로 설정하면 인터페이스가 개발 중이고 새로운 변경 사항이 있음을 의미하므로 foo-freeze-api 실행하면 새 버전이 생성되고 자동으로 값이 true 로 변경됩니다. Android 14에서 도입되었습니다.
  • backend.<type>.enabled : 이 플래그는 AIDL 컴파일러가 코드를 생성하는 각 백엔드를 토글합니다. 현재 Java, C++, NDK, Rust 등 4가지 백엔드가 지원됩니다. Java, C++, NDK 백엔드는 기본적으로 활성화됩니다. 이 세 가지 백엔드 중 하나라도 필요하지 않은 경우 명시적으로 비활성화해야 합니다. Rust는 기본적으로 비활성화되어 있습니다.
  • backend.<type>.apex_available : 생성된 스텁 라이브러리를 사용할 수 있는 APEX 이름 목록입니다.
  • backend.[cpp|java].gen_log : 트랜잭션에 대한 정보를 수집하기 위해 추가 코드를 생성할지 여부를 제어하는 ​​선택적 플래그입니다.
  • backend.[cpp|java].vndk.enabled : 이 인터페이스를 VNDK의 일부로 만드는 선택적 플래그입니다. 기본값은 false 입니다.
  • backend.[cpp|ndk].additional_shared_libraries : Android 14에 도입된 이 플래그는 네이티브 라이브러리에 종속성을 추가합니다. 이 플래그는 ndk_headercpp_header 와 함께 유용합니다.
  • backend.java.sdk_version : Java 스텁 라이브러리가 빌드되는 SDK 버전을 지정하기 위한 선택적 플래그입니다. 기본값은 "system_current" 입니다. backend.java.platform_apis true인 경우에는 설정하면 안 됩니다.
  • backend.java.platform_apis : 생성된 라이브러리가 SDK가 아닌 플랫폼 API에 대해 빌드해야 하는 경우 true 로 설정해야 하는 선택적 플래그입니다.

버전과 활성화된 백엔드의 각 조합에 대해 스텁 라이브러리가 생성됩니다. 특정 백엔드에 대한 특정 버전의 스텁 라이브러리를 참조하는 방법은 모듈 명명 ​​규칙을 참조하세요.

AIDL 파일 작성

안정적인 AIDL의 인터페이스는 구조화되지 않은 Parcelable을 사용할 수 없다는 점을 제외하면 기존 인터페이스와 유사합니다(안정적이지 않기 때문입니다! 구조화된 AIDL과 안정적인 AIDL 참조). 안정적인 AIDL의 주요 차이점은 Parcelable이 정의되는 방식입니다. 이전에는 소포 가능 항목이 전방으로 선언되었습니다 . 안정적인(따라서 구조화된) AIDL에서는 Parcelable 필드와 변수가 명시적으로 정의됩니다.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

현재 boolean , char , float , double , byte , int , longString 에 대한 기본값이 지원됩니다(필수는 아님). Android 12에서는 사용자 정의 열거형의 기본값도 지원됩니다. 기본값을 지정하지 않으면 0과 같거나 빈 값이 사용됩니다. 기본값이 없는 열거형은 0 열거자가 없더라도 0으로 초기화됩니다.

스텁 라이브러리 사용

스텁 라이브러리를 모듈에 대한 종속성으로 추가한 후 이를 파일에 포함할 수 있습니다. 다음은 빌드 시스템의 스텁 라이브러리 예입니다( Android.mk 레거시 모듈 정의에도 사용할 수 있음).

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rust_libs: ["my-module-name-rust"],
    ...
}

C++의 예:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

자바의 예:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Rust의 예:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

버전 관리 인터페이스

foo 라는 이름으로 모듈을 선언하면 모듈의 API를 관리하는 데 사용할 수 있는 대상이 빌드 시스템에 생성됩니다. 빌드되면 foo-freeze-api는 Android 버전에 따라 api_dir 또는 aidl_api/ name 아래에 새 API 정의를 추가하고 새로 고정된 인터페이스 버전을 나타내는 .hash 파일을 추가합니다. foo-freeze-api는 또한 추가 버전과 버전에 대한 imports 반영하기 위해 versions_with_info 속성을 업데이트합니다. 기본적으로 versions_with_infoimports imports 필드에서 복사됩니다. 그러나 최신 안정 버전은 명시적인 버전이 없는 가져오기에 대해 versions_with_infoimports 에 지정됩니다. versions_with_info 속성이 지정되면 빌드 시스템은 고정 버전 간, 그리고 ToT(Top of Tree)와 최신 고정 버전 간 호환성 검사를 실행합니다.

또한 ToT 버전의 API 정의를 관리해야 합니다. API가 업데이트될 때마다 foo-update-api를 실행하여 ToT 버전의 API 정의가 포함된 aidl_api/ name /current 업데이트합니다.

인터페이스의 안정성을 유지하기 위해 소유자는 다음을 새로 추가할 수 있습니다.

  • 인터페이스 끝까지의 메서드(또는 명시적으로 정의된 새 직렬이 있는 메서드)
  • Parcelable 끝에 있는 요소(각 요소에 기본값을 추가해야 함)
  • 상수값
  • Android 11에서는 열거자
  • Android 12에서는 공용체 끝까지의 필드

다른 작업은 허용되지 않으며 누구도 인터페이스를 수정할 수 없습니다. 그렇지 않으면 소유자가 변경한 내용과 충돌할 위험이 있습니다.

릴리스를 위해 모든 인터페이스가 고정되었는지 테스트하려면 다음 환경 변수 세트를 사용하여 빌드할 수 있습니다.

  • AIDL_FROZEN_REL=true m ... - 빌드에서는 owner: 필드가 지정되지 않은 모든 안정적인 AIDL 인터페이스를 고정해야 합니다.
  • AIDL_FROZEN_OWNERS="aosp test" - 빌드를 위해서는 모든 안정적인 AIDL 인터페이스가 "aosp" 또는 "test"로 지정된 owner: 필드와 함께 고정되어야 합니다.

수입의 안정성

인터페이스의 고정 버전에 대한 가져오기 버전 업데이트는 안정적인 AIDL 레이어에서 이전 버전과 호환됩니다. 그러나 이를 업데이트하려면 이전 버전의 인터페이스를 사용하는 모든 서버와 클라이언트를 업데이트해야 하며, 일부 애플리케이션은 다른 버전의 유형을 혼합할 때 혼동될 수 있습니다. 일반적으로 유형 전용 또는 공통 패키지의 경우 IPC 트랜잭션에서 알 수 없는 유형을 처리하기 위해 코드를 이미 작성해야 하므로 안전합니다.

Android 플랫폼 코드에서 android.hardware.graphics.common 이러한 버전 업그레이드 유형의 가장 큰 예입니다.

버전이 지정된 인터페이스 사용

인터페이스 방법

런타임 시 이전 서버에서 새 메서드를 호출하려고 하면 새 클라이언트에 백엔드에 따라 오류나 예외가 발생합니다.

  • cpp 백엔드는 ::android::UNKNOWN_TRANSACTION 가져옵니다.
  • ndk 백엔드는 STATUS_UNKNOWN_TRANSACTION 가져옵니다.
  • java 백엔드는 API가 구현되지 않았다는 메시지와 함께 android.os.RemoteException 가져옵니다.

이를 처리하는 전략은 버전 쿼리기본값 사용을 참조하세요.

소포

새로운 필드가 Parcelable에 추가되면 기존 클라이언트와 서버는 해당 필드를 삭제합니다. 새 클라이언트와 서버가 기존의 Parcelable을 수신하면 새 필드의 기본값이 자동으로 채워집니다. 즉, Parcelable의 모든 새 필드에 대해 기본값을 지정해야 합니다.

클라이언트는 서버가 정의된 필드가 있는 버전을 구현하고 있다는 것을 알지 않는 한 서버가 새 필드를 사용할 것이라고 기대해서는 안 됩니다( 버전 쿼리 참조).

열거형 및 상수

마찬가지로, 클라이언트와 서버는 나중에 더 많은 값이 추가될 수 있으므로 인식할 수 없는 상수 값과 열거자를 적절하게 거부하거나 무시해야 합니다. 예를 들어 서버는 자신이 알지 못하는 열거자를 수신할 때 중단되어서는 안 됩니다. 이를 무시하거나 클라이언트가 이 구현에서 지원되지 않는다는 것을 알 수 있도록 무언가를 반환해야 합니다.

노동조합

수신자가 오래되어 해당 필드에 대해 모르는 경우 새 필드가 포함된 통합을 보내려는 시도는 실패합니다. 구현에서는 새 필드와의 통합을 볼 수 없습니다. 단방향 트랜잭션인 경우 실패는 무시됩니다. 그렇지 않으면 오류는 BAD_VALUE (C++ 또는 NDK 백엔드의 경우) 또는 IllegalArgumentException (Java 백엔드의 경우)입니다. 클라이언트가 새 필드에 설정된 Union을 이전 서버로 보내는 경우 또는 이전 클라이언트가 새 서버에서 Union을 받는 경우 오류가 수신됩니다.

플래그 기반 개발

개발 중인(고정되지 않은) 인터페이스는 이전 버전과의 호환성이 보장되지 않으므로 릴리스 장치에서 사용할 수 없습니다.

AIDL은 고정되지 않은 최신 버전에 대해 코드를 작성하고 릴리스 장치에서 계속 사용할 수 있도록 이러한 고정되지 않은 인터페이스 라이브러리에 대한 런타임 대체를 지원합니다. 클라이언트의 이전 버전과 호환되는 동작은 기존 동작과 유사하며 폴백을 사용하면 구현도 이러한 동작을 따라야 합니다. 버전이 지정된 인터페이스 사용을 참조하세요.

AIDL 빌드 플래그

이 동작을 제어하는 ​​플래그는 build/release/build_flags.bzl 에 정의된 RELEASE_AIDL_USE_UNFROZEN 입니다. true 는 고정 해제된 버전의 인터페이스가 런타임에 사용됨을 의미하고 false 는 고정 해제된 버전의 라이브러리가 모두 마지막 고정 버전처럼 작동함을 의미합니다. 로컬 개발에서는 플래그를 true 로 재정의할 수 있지만 릴리스하기 전에는 false 로 되돌려야 합니다. 일반적으로 개발은 플래그가 true 로 설정된 구성으로 수행됩니다.

호환성 매트릭스 및 매니페스트

공급업체 인터페이스 개체(VINTF 개체)는 예상되는 버전과 공급업체 인터페이스 양쪽에 제공되는 버전을 정의합니다.

Cuttlefish가 아닌 대부분의 장치는 인터페이스가 고정된 후에만 최신 호환성 매트릭스를 대상으로 하므로 RELEASE_AIDL_USE_UNFROZEN 기반 AIDL 라이브러리에는 차이가 없습니다.

파트너 소유 인터페이스는 개발 중에 장치가 대상으로 하는 장치별 또는 제품별 호환성 매트릭스에 추가됩니다. 따라서 고정되지 않은 새 버전의 인터페이스가 호환성 매트릭스에 추가되면 이전 고정 버전은 RELEASE_AIDL_USE_UNFROZEN=false 동안 유지되어야 합니다. 다양한 RELEASE_AIDL_USE_UNFROZEN 구성에 대해 서로 다른 호환성 매트릭스 파일을 사용하거나 모든 구성에 사용되는 단일 호환성 매트릭스 파일에서 두 버전을 모두 허용하여 이를 처리할 수 있습니다.

예를 들어 고정되지 않은 버전 4를 추가하는 경우 <version>3-4</version> 사용하세요.

버전 4가 고정되면 RELEASE_AIDL_USE_UNFROZENfalse 일 때 고정된 버전 4가 사용되므로 호환성 매트릭스에서 버전 3을 제거할 수 있습니다.

HAL 클라이언트 변경

HAL 클라이언트 코드는 이전에 지원되는 각 고정 버전과 역호환되어야 합니다. RELEASE_AIDL_USE_UNFROZENfalse 인 경우 서비스는 항상 마지막 고정 버전 또는 이전 버전처럼 보입니다. 예를 들어 고정 해제된 새 메서드를 호출하면 UNKNOWN_TRANSACTION 반환되거나 새로운 parcelable 필드에 기본값이 있습니다. Android 프레임워크 클라이언트는 추가 이전 버전과 호환되어야 하지만 이는 공급업체 클라이언트 및 파트너 소유 인터페이스 클라이언트에 대한 새로운 세부정보입니다.

HAL 구현 변경사항

HAL 개발과 플래그 기반 개발의 가장 큰 차이점은 RELEASE_AIDL_USE_UNFROZEN false 일 때 작동하려면 HAL 구현이 마지막 고정 버전과 역호환되어야 한다는 요구사항입니다. 구현 및 장치 코드에서 이전 버전과의 호환성을 고려하는 것은 새로운 연습입니다. 버전이 지정된 인터페이스 사용을 참조하세요.

이전 버전과의 호환성 고려 사항은 일반적으로 클라이언트와 서버, 프레임워크 코드와 공급업체 코드에 대해 동일하지만, 이제 동일한 소스 코드를 사용하는 두 가지 버전을 효과적으로 구현하고 있으므로 알아야 할 미묘한 차이점이 있습니다. (현재 고정되지 않은 버전).

예: 인터페이스에 세 가지 고정 버전이 있습니다. 인터페이스가 새로운 방법으로 업데이트되었습니다. 클라이언트와 서비스는 모두 새 버전 4 라이브러리를 사용하도록 업데이트되었습니다. V4 라이브러리는 고정되지 않은 인터페이스 버전을 기반으로 하기 때문에 RELEASE_AIDL_USE_UNFROZENfalse 인 경우 마지막 고정 버전인 버전 3처럼 동작하고 새 메서드의 사용을 방지합니다.

인터페이스가 고정되면 RELEASE_AIDL_USE_UNFROZEN 의 모든 값은 해당 고정 버전을 사용하며 이전 버전과의 호환성을 처리하는 코드는 제거될 수 있습니다.

콜백에서 메서드를 호출할 때 UNKNOWN_TRANSACTION 반환되는 경우를 적절하게 처리해야 합니다. 클라이언트는 릴리스 구성에 따라 두 가지 다른 버전의 콜백을 구현할 수 있으므로 클라이언트가 최신 버전을 보낼 것이라고 가정할 수 없으며 새 메서드가 이를 반환할 수 있습니다. 이는 버전이 지정된 인터페이스 사용 에 설명된 안정적인 AIDL 클라이언트가 서버와의 이전 버전과의 호환성을 유지하는 방법과 유사합니다.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

RELEASE_AIDL_USE_UNFROZENfalse 이고 서비스가 보내려고 하는 새 필드의 값이 프로세스 도중에 삭제되면 기존 유형( parcelable , enum , union )의 새 필드가 존재하지 않거나 기본값을 포함할 수 있습니다.

고정 해제된 버전에 추가된 새 유형은 인터페이스를 통해 전송하거나 수신할 수 없습니다.

RELEASE_AIDL_USE_UNFROZEN false 인 경우 구현은 어떤 클라이언트에서도 새 메서드에 대한 호출을 가져오지 않습니다.

이전 버전이 아닌 새로운 열거자를 도입된 버전에서만 사용하도록 주의하세요.

일반적으로 원격 인터페이스가 사용 중인 버전을 확인하려면 foo->getInterfaceVersion() 사용합니다. 그러나 플래그 기반 버전 관리 지원을 사용하면 두 가지 다른 버전을 구현하므로 현재 인터페이스의 버전을 얻고 싶을 수도 있습니다. 현재 객체의 인터페이스 버전을 가져와서 이를 수행할 수 있습니다(예: this->getInterfaceVersion() 또는 my_ver 에 대한 다른 메소드). 자세한 내용은 원격 개체의 인터페이스 버전 쿼리를 참조하세요.

새로운 VINTF 안정적인 인터페이스

새 AIDL 인터페이스 패키지가 추가되면 마지막 고정 버전이 없으므로 RELEASE_AIDL_USE_UNFROZENfalse 일 때 대체할 동작이 없습니다. 이러한 인터페이스를 사용하지 마십시오. RELEASE_AIDL_USE_UNFROZENfalse 이면 Service Manager는 서비스가 인터페이스를 등록하는 것을 허용하지 않으며 클라이언트는 인터페이스를 찾지 못합니다.

장치 makefile의 RELEASE_AIDL_USE_UNFROZEN 플래그 값을 기반으로 조건부로 서비스를 추가할 수 있습니다.

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

서비스가 더 큰 프로세스의 일부이므로 조건부로 장치에 추가할 수 없는 경우 서비스가 IServiceManager::isDeclared() 사용하여 선언되었는지 확인할 수 있습니다. 선언되었지만 등록에 실패한 경우 프로세스를 중단합니다. 선언되지 않은 경우 등록에 실패할 것으로 예상됩니다.

개발 도구로서의 Cuttlefish

매년 VINTF가 동결된 후 우리는 프레임워크 호환성 매트릭스(FCM) target-level 과 Cuttlefish의 PRODUCT_SHIPPING_API_LEVEL 을 조정하여 내년 릴리스와 함께 출시되는 장치를 반영합니다. 테스트를 거쳐 내년 릴리스에 대한 새로운 요구 사항을 충족하는 일부 출시 장치가 있는지 확인하기 위해 target-levelPRODUCT_SHIPPING_API_LEVEL 조정합니다.

RELEASE_AIDL_USE_UNFROZENtrue 이면 Cuttlefish는 향후 Android 릴리스 개발에 사용됩니다. 이는 내년 Android 릴리스의 FCM 수준 및 PRODUCT_SHIPPING_API_LEVEL 대상으로 하며 다음 릴리스의 VSR(공급업체 소프트웨어 요구 사항)을 충족해야 합니다.

RELEASE_AIDL_USE_UNFROZENfalse 이면 Cuttlefish는 릴리스 장치를 반영하기 위해 이전 target-levelPRODUCT_SHIPPING_API_LEVEL 갖습니다. Android 14 이하에서는 FCM target-level , 배송 API 수준 또는 다음 릴리스를 대상으로 하는 기타 코드에 대한 변경 사항을 선택하지 않는 다양한 Git 분기를 통해 이러한 차별화가 이루어집니다.

모듈 명명 ​​규칙

Android 11에서는 활성화된 버전과 백엔드의 각 조합에 대해 스텁 라이브러리 모듈이 자동으로 생성됩니다. 연결을 위해 특정 스텁 라이브러리 모듈을 참조하려면 aidl_interface 모듈의 이름을 사용하지 말고 스텁 라이브러리 모듈의 이름( ifacename - version - backend )을 사용하세요.

  • ifacename : aidl_interface 모듈의 이름
  • version 다음 중 하나입니다.
    • 고정 버전의 V version-number
    • V latest-frozen-version-number + 1 (아직 동결되지 않은) 버전의 경우 1
  • backend 는 다음 중 하나입니다.
    • java 백엔드용 java,
    • C++ 백엔드의 경우 cpp ,
    • NDK 백엔드용 ndk 또는 ndk_platform . 전자는 앱용이고, 후자는 플랫폼 사용용이고,
    • Rust 백엔드를 위한 rust .

foo 라는 이름의 모듈이 있고 최신 버전이 2 이며 NDK와 C++를 모두 지원한다고 가정합니다. 이 경우 AIDL은 다음 모듈을 생성합니다.

  • 버전 1 기준
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • 버전 2(최신 안정 버전) 기준
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • ToT 버전 기준
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

안드로이드 11과 비교하면,

  • 최신 안정 버전을 참조하는 foo- backend foo- V2 - backend 가 됩니다.
  • ToT 버전을 참조하는 foo-unstable- backend foo- V3 - backend 가 됩니다.

출력 파일 이름은 항상 모듈 이름과 동일합니다.

  • 버전 1 기준: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • 버전 2 기반: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • ToT 버전 기준: foo-V3-(cpp|ndk|ndk_platform|rust).so

AIDL 컴파일러는 unstable 버전 모듈이나 안정적인 AIDL 인터페이스를 위한 버전이 지정되지 않은 모듈을 생성하지 않습니다. Android 12부터 안정적인 AIDL 인터페이스에서 생성된 모듈 이름에는 항상 해당 버전이 포함됩니다.

새로운 메타 인터페이스 메소드

Android 10에는 안정적인 AIDL을 위한 여러 메타 인터페이스 메서드가 추가되었습니다.

원격 개체의 인터페이스 버전 쿼리

클라이언트는 원격 개체가 구현하는 인터페이스의 버전과 해시를 쿼리하고 반환된 값을 클라이언트가 사용 중인 인터페이스의 값과 비교할 수 있습니다.

cpp 백엔드의 예:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

ndk (및 ndk_platform ) 백엔드의 예:

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

java 백엔드의 예:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Java 언어의 경우 원격 측은 다음과 같이 getInterfaceVersion()getInterfaceHash() 구현해야 합니다(복사/붙여넣기 실수를 방지하기 위해 IFoo 대신 super 사용됩니다. @SuppressWarnings("static") 주석은 다음에 따라 경고를 비활성화하는 데 필요할 수 있습니다. javac 구성):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

이는 생성된 클래스( IFoo , IFoo.Stub 등)가 클라이언트와 서버 간에 공유되기 때문입니다(예: 클래스가 부팅 클래스 경로에 있을 수 있음). 클래스가 공유되면 서버는 이전 버전의 인터페이스로 구축되었더라도 최신 버전의 클래스에 대해서도 연결됩니다. 이 메타 인터페이스가 공유 클래스에 구현되면 항상 최신 버전을 반환합니다. 그러나 위와 같이 메서드를 구현하면 인터페이스의 버전 번호가 서버 코드에 포함되므로( IFoo.VERSION 참조 시 인라인되는 static final int 이기 때문입니다) 서버가 구축된 정확한 버전을 메서드에서 반환할 수 있습니다. 와 함께.

오래된 인터페이스 다루기

클라이언트는 최신 버전의 AIDL 인터페이스로 업데이트되었지만 서버는 이전 AIDL 인터페이스를 사용하고 있을 수 있습니다. 이러한 경우 이전 인터페이스에서 메서드를 호출하면 UNKNOWN_TRANSACTION 반환됩니다.

안정적인 AIDL을 사용하면 클라이언트가 더 많은 제어권을 갖게 됩니다. 클라이언트 측에서는 기본 구현을 AIDL 인터페이스로 설정할 수 있습니다. 기본 구현의 메소드는 해당 메소드가 원격 측에서 구현되지 않은 경우에만 호출됩니다(이전 버전의 인터페이스로 빌드되었기 때문입니다). 기본값은 전역적으로 설정되므로 잠재적으로 공유되는 컨텍스트에서 사용하면 안 됩니다.

Android 13 이상의 C++ 예:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

자바의 예:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process


foo.anAddedMethod(...);

AIDL 인터페이스에서 모든 메서드의 기본 구현을 제공할 필요는 없습니다. 원격 측에서 구현이 보장되는 메서드(메소드가 AIDL 인터페이스 설명에 있을 때 원격이 빌드되었음을 확신하기 때문에)는 기본 impl 클래스에서 재정의할 필요가 없습니다.

기존 AIDL을 구조적/안정적 AIDL로 변환

기존 AIDL 인터페이스와 이를 사용하는 코드가 있는 경우 다음 단계에 따라 인터페이스를 안정적인 AIDL 인터페이스로 변환하세요.

  1. 인터페이스의 모든 종속성을 식별합니다. 인터페이스가 의존하는 모든 패키지에 대해 패키지가 안정적인 AIDL에 정의되어 있는지 확인하세요. 정의되지 않은 경우 패키지를 변환해야 합니다.

  2. 인터페이스의 모든 소포 가능 항목을 안정적인 소포 가능 항목으로 변환합니다(인터페이스 파일 자체는 변경되지 않은 상태로 유지될 수 있음). AIDL 파일에 구조를 직접 표현하여 이를 수행합니다. 이러한 새로운 유형을 사용하려면 관리 클래스를 다시 작성해야 합니다. 이 작업은 aidl_interface 패키지를 생성하기 전에 수행할 수 있습니다(아래).

  3. 모듈 이름, 종속성 및 기타 필요한 정보가 포함된 aidl_interface 패키지(위에 설명된 대로)를 만듭니다. 구조화뿐만 아니라 안정화하려면 버전 관리도 필요합니다. 자세한 내용은 버전 관리 인터페이스를 참조하세요.