Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

안정적 AIDL

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

  • 인터페이스는 aidl_interfaces를 통해 빌드 시스템에서 정의됩니다.
  • 인터페이스에는 구조화된 데이터만 포함될 수 있습니다. 원하는 유형을 나타내는 parcelable은 AIDL 정의에 따라 자동으로 생성되며 자동으로 마셜링되고 마셜링 해제됩니다.
  • 인터페이스는 안정적인 것으로 선언할 수 있습니다(이전 버전과 호환 가능). 이 경우 API는 AIDL 인터페이스에 인접한 파일을 통해 추적되고 버전이 지정됩니다.

AIDL 인터페이스 정의

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

aidl_interface {
        name: "my-module-name",
        local_include_dir: "tests_1",
        srcs: [
            "tests_1/some/package/IFoo.aidl",
            "tests_1/some/package/Thing.aidl",
        ],
        api_dir: "api/test-piece-1",
        versions: ["1"],
    }
    
  • name: 모듈 이름입니다. 이 경우 해당 언어로 된 스터브 라이브러리 두 개, my-module-name-javamy-module-name-cpp가 생성됩니다. C++ 라이브러리가 생성되는 것을 방지하려면 gen_cpp를 사용합니다. 이는 API를 검사하고 업데이트하는 데 사용할 수 있는 추가 빌드 시스템 작업도 생성합니다.
  • local_include_dir: 패키지가 시작되는 곳의 경로입니다.
  • srcs: 타겟 언어로 컴파일된 안정적 AIDL 소스 파일의 목록입니다.
  • api_dir: 인터페이스의 이전 버전에 대한 API 정의가 덤프되는 경로입니다. 인터페이스에 대한 API의 브레이킹 체인지를 확인하는 데 사용됩니다(아래에 설명된 프로세스 참조).
  • versions: api_dir에서 비활성화되는 인터페이스의 이전 버전입니다. 이는 선택사항입니다.

AIDL 파일 쓰기

안정적 AIDL의 인터페이스는 구조화되지 않은 parcelable이 안정적이지 않아 허용되지 않는다는 점을 제외하면 기존 인터페이스와 유사합니다. 안정적 AIDL의 가장 큰 차이점은 parcelable이 정의되는 방식입니다. 이전에는 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, long, String의 기본값이 현재 지원되지만 필수는 아닙니다.

스터브 라이브러리 사용

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

cc_... {
        name: ...,
        shared_libs: ["my-module-name-cpp"],
        ...
    }
    # or
    java_... {
        name: ...,
        static_libs: ["my-module-name-java"],
        ...
    }
    

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
    

버전 관리 인터페이스

이름이 foo인 모듈을 선언하면 빌드 시스템에 모듈의 API를 관리하는 데 사용할 수 있는 타겟도 생성됩니다. 빌드 시 foo-freeze-api는 인터페이스의 다음 버전에 대한 새 API 정의를 api_dir에 추가합니다.

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

  • 메서드 끝에 메서드(또는 명시적으로 새 일련 번호가 정의된 메서드) 추가
  • parcel 끝에 요소 추가(각 요소의 기본값 추가 필요)

다른 작업은 허용되지 않습니다.

새 메타 인터페이스 메서드

Android 10은 안정적 AIDL을 위해 여러 메타 인터페이스 메서드를 추가합니다.

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

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

C++ 예시:

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
    }
    

자바 예시:

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

자바 언어의 경우 원격 측은 getInterfaceVersion()을 다음과 같이 구현해야 합니다.

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

이는 생성된 클래스(IFoo, IFoo.Stubs 등)가 클라이언트와 서버 간에 공유되기 때문입니다(예: 클래스가 부팅 클래스 경로에 있을 수 있음). 클래스가 공유될 때 서버는 이전 버전의 인터페이스로 빌드된 경우라도 클래스의 최신 버전과도 연동됩니다. 이 메타 인터페이스는 공유된 클래스에 구현될 경우 항상 최신 버전을 반환합니다. 그러나 위와 같이 메서드를 구현하면 (IFoo.VERSION은 참조 시 인라인된 static final int이므로) 인터페이스의 버전 번호가 서버의 코드에 삽입되고, 이에 따라 메서드는 서버가 빌드될 때 사용된 정확한 버전을 반환할 수 있습니다.

이전 인터페이스 처리

클라이언트가 최신 버전의 AIDL 인터페이스로 업데이트되었지만 서버는 이전 AIDL 인터페이스를 사용 중일 수 있습니다. 이러한 경우 클라이언트는 이전 인터페이스에 없는 새 메서드를 호출해서는 안됩니다. 안정적 AIDL 이전에는 이렇게 존재하지 않는 메서드를 호출하는 것은 묵시적으로 무시되고 클라이언트는 메서드가 호출되었는지 알지 못했습니다.

안정적 AIDL을 사용하면 클라이언트는 더 큰 통제권을 갖습니다. 클라이언트 측에서 AIDL 인터페이스의 기본 구현을 설정할 수 있습니다. 기본 구현 메서드는 (메서드가 이전 버전의 인터페이스로 빌드되었으므로) 메서드가 원격 측에서 구현되지 않은 경우에만 호출됩니다.

C++ 예시:

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

    // once per an interface in a process
    IFoo::setDefaultImpl(std::unique_ptr<IFoo>(MyDefault));

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

자바 예시:

IFoo.Stubs.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. 인터페이스의 모든 parcelable을 안정적 parcelable로 변환합니다(인터페이스 파일 자체는 변경되지 않은 상태로 둘 수 있음). AIDL 파일에서 구조를 직접 표현하여 이를 처리합니다. 이러한 새 유형을 사용하려면 관리 클래스를 다시 작성해야 합니다. 아래에서 aidl_interface 패키지를 생성하기 전에 이 작업을 수행할 수 있습니다.

  3. (위에서 설명한 대로) 모듈 이름, 종속 항목 및 필요한 기타 정보가 포함된 aidl_interface 패키지를 생성합니다. 구조화하는 것뿐만 아니라 안정화하려면 api_dir 경로도 지정합니다.