Zaplecze AIDL

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Backend AIDL jest celem generowania kodu pośredniczącego. Korzystając z plików AIDL, zawsze używasz ich w określonym języku z określonym środowiskiem wykonawczym. W zależności od kontekstu należy używać różnych backendów AIDL.

AIDL ma następujące backendy:

Zaplecze Język Powierzchnia API Buduj systemy
Jawa Jawa SDK/SystemApi (stabilny*) wszystko
NDK C++ libbinder_ndk (stabilny*) interfejs_pomocy
CPP C++ libbinder (niestabilny) wszystko
Rdza Rdza libbinder_rs (niestabilny) interfejs_pomocy
  • Te powierzchnie API są stabilne, ale wiele interfejsów API, na przykład do zarządzania usługami, jest zarezerwowanych do użytku na platformie wewnętrznej i nie jest dostępnych dla aplikacji. Aby uzyskać więcej informacji na temat korzystania z AIDL w aplikacjach, zapoznaj się z dokumentacją dla programistów .
  • Backend Rust został wprowadzony w Androidzie 12; backend NDK jest dostępny od Androida 10.
  • Skrzynia Rust jest zbudowana na libbinder_ndk . APEX używają skrzynki segregatorów w taki sam sposób, jak wszyscy inni po stronie systemu. Część Rust jest pakowana w APEX i wysyłana do środka. To zależy od libbinder_ndk.so na partycji systemowej.

Buduj systemy

W zależności od zaplecza istnieją dwa sposoby kompilacji AIDL do kodu pośredniczącego. Aby uzyskać więcej informacji na temat systemów kompilacji, zobacz informacje o module Soong .

Podstawowy system budowy

W dowolnym module java_ cc_ (lub w ich odpowiednikach w Android.mk ) pliki .aidl można określić jako pliki źródłowe. W tym przypadku używane są backendy AIDL Java/CPP (nie backend NDK), a klasy do korzystania z odpowiednich plików AIDL są automatycznie dodawane do modułu. Opcje takie jak local_include_dirs , które informują system kompilacji o ścieżce głównej do plików AIDL w tym module, można określić w tych modułach w grupie aidl: Pamiętaj, że backend Rust jest przeznaczony tylko do użytku z Rust. moduły rust_ są obsługiwane inaczej, ponieważ pliki AIDL nie są określone jako pliki źródłowe. Zamiast tego moduł aidl_interface tworzy rustlib o nazwie <aidl_interface name>-rust , z którą można połączyć. Aby uzyskać więcej informacji, zobacz przykład Rust AIDL .

interfejs_pomocy

Zobacz Stabilny AIDL . Typy używane w tym systemie kompilacji muszą mieć strukturę; to znaczy wyrażone bezpośrednio w AIDL. Oznacza to, że niestandardowe paczki nie mogą być używane.

typy

Kompilator aidl można uznać za implementację referencyjną dla typów. Kiedy tworzysz interfejs, aidl --lang=<backend> ... aby zobaczyć wynikowy plik interfejsu. Korzystając z modułu aidl_interface , możesz wyświetlić dane wyjściowe w out/soong/.intermediates/<path to module>/ .

Typ Java/AIDL Typ C++ Typ NDK Rodzaj rdzy
logiczna bool bool bool
bajt int8_t int8_t i8
zwęglać char16_t char16_t u16
int int32_t int32_t i32
długi int64_t int64_t i64
pływak pływak pływak f32
podwójnie podwójnie podwójnie f64
Strunowy Android::String16 std::ciąg Strunowy
android.os.Parcelable android::Paczkowalny Nie dotyczy Nie dotyczy
IBinder android::IBinder ndk::SpAIBinder binder::SpIBinder
T[] std::wektor<T> std::wektor<T> w: &[T]
Out: Vec<T>
bajt[] std::wektor<uint8_t> std::wektor<int8_t> 1 w: &[u8]
Out: Vec<u8>
Lista<T> std::wektor<T> 2 std::wektor<T> 3 W: &[T] 4
Out: Vec<T>
FileDescriptor android::base::unique_fd Nie dotyczy segregator::paczka::PaczkaPlikDeskryptor
ParcelFileDescriptor android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor segregator::paczka::PaczkaPlikDeskryptor
typ interfejsu (T) android::sp<T> std::shared_ptr<T> spoiwo::Mocne
typ paczkowany (T) T T T
typ złącza (T) 5 T T T
T[N] 6 std::tablica<T, N> std::tablica<T, N> [T; N]

1. W Androidzie 12 lub nowszym tablice bajtowe używają uint8_t zamiast int8_t ze względu na kompatybilność.

2. Backend C++ obsługuje List<T> , gdzie T jest jednym z String , IBinder , ParcelFileDescriptor lub parcelable. W Androidzie 13 lub nowszym T może być dowolnym typem nieprymitywnym (w tym typami interfejsów) z wyjątkiem tablic. AOSP zaleca używanie typów tablicowych, takich jak T[] , ponieważ działają one we wszystkich backendach.

3. Backend NDK obsługuje List<T> , gdzie T jest jednym z String , ParcelFileDescriptor lub parcelable. W systemie Android 13 lub nowszym T może być dowolnym typem nieprymitywnym z wyjątkiem tablic.

4. Typy są przekazywane w różny sposób dla kodu Rust w zależności od tego, czy są one wejściowe (argument), czy wyjściowe (zwrócona wartość).

5. Typy Unii są obsługiwane w Androidzie 12 i nowszych.

6. W systemie Android 13 lub nowszym obsługiwane są tablice o stałym rozmiarze. Tablice o stałym rozmiarze mogą mieć wiele wymiarów (np. int[3][4] ). W zapleczu Java tablice o stałym rozmiarze są reprezentowane jako typy tablic.

Kierunkowość (wejście/wyjście/wyjście)

Podczas określania typów argumentów funkcji można określić je jako in , out lub inout . To kontroluje, w którym kierunku informacje są przekazywane dla połączenia IPC. in jest domyślnym kierunkiem i wskazuje, że dane są przekazywane od dzwoniącego do odbiorcy. out oznacza, że ​​dane są przekazywane od odbiorcy do dzwoniącego. inout to połączenie obu tych elementów. Jednak zespół systemu Android zaleca unikanie używania specyfikatora argumentów inout . Jeśli użyjesz inout z wersjonowanym interfejsem i starszym odbiorcą, dodatkowe pola, które są obecne tylko w wywołującym, zostaną zresetowane do wartości domyślnych. W odniesieniu do Rust, normalny typ inout otrzymuje &mut Vec<T> , a typ list inout otrzymuje &mut Vec<T> .

UTF8/UTF16

Dzięki backendowi CPP możesz wybrać, czy ciągi mają być utf-8, czy utf-16. Zadeklaruj ciągi znaków jako @utf8InCpp String w AIDL, aby automatycznie przekonwertować je na utf-8. Backendy NDK i Rust zawsze używają łańcuchów utf-8. Aby uzyskać więcej informacji na temat adnotacji utf8InCpp , zobacz Adnotacje w AIDL .

Możliwość zerowania

Możesz dodawać adnotacje do typów, które mogą być puste w zapleczu Java za pomocą @nullable , aby ujawnić wartości puste dla zaplecza CPP i NDK. W zapleczu Rusta te typy @nullable są widoczne jako Option<T> . Serwery natywne domyślnie odrzucają wartości null. Jedynymi wyjątkami są typy interface i IBinder , które zawsze mogą mieć wartość null dla odczytów NDK i zapisów CPP/NDK. Aby uzyskać więcej informacji na temat adnotacji nullable wartość null, zobacz Adnotacje w AIDL .

Paczki niestandardowe

W backendach C++ i Java w głównym systemie kompilacji możesz zadeklarować parcelable, który jest implementowany ręcznie w docelowym backendzie (w C++ lub w Javie).

    package my.package;
    parcelable Foo;

lub z deklaracją nagłówka C++:

    package my.package;
    parcelable Foo cpp_header "my/package/Foo.h";

Następnie możesz użyć tego parcelable jako typu w plikach AIDL, ale nie zostanie on wygenerowany przez AIDL.

Rust nie obsługuje niestandardowych paczek.

Wartości domyślne

Strukturyzowane parcelables mogą deklarować wartości domyślne dla poszczególnych pól dla prymitywów, String i tablic tego typu.

    parcelable Foo {
      int numField = 42;
      String stringField = "string value";
      char charValue = 'a';
      ...
    }

W zapleczu Java, gdy brakuje wartości domyślnych, pola są inicjowane jako wartości zerowe dla typów pierwotnych i null dla typów innych niż podstawowe.

W innych zapleczach pola są inicjowane domyślnymi wartościami inicjowanymi, gdy wartości domyślne nie są zdefiniowane. Na przykład w zapleczu C++ pola String są inicjowane jako pusty ciąg, a pola List<T> są inicjowane jako pusty vector<T> . Pola @nullable są inicjowane jako pola o wartości null.

Obsługa błędów

System operacyjny Android udostępnia wbudowane typy błędów, których usługi mogą używać podczas zgłaszania błędów. Są one używane przez program wiążący i mogą być używane przez dowolne usługi implementujące interfejs programu wiążącego. Ich użycie jest dobrze udokumentowane w definicji AIDL i nie wymagają żadnego statusu zdefiniowanego przez użytkownika ani typu zwracanego.

Parametry wyjściowe z błędami

Gdy funkcja AIDL zgłasza błąd, funkcja może nie zainicjować ani zmodyfikować parametrów wyjściowych. W szczególności parametry wyjściowe mogą zostać zmodyfikowane, jeśli błąd wystąpi podczas rozpakowywania, a nie podczas przetwarzania samej transakcji. Ogólnie rzecz biorąc, podczas uzyskiwania błędu z funkcji AIDL wszystkie parametry inout i out , a także wartość zwracana (która działa jak parametr out w niektórych backendach) powinny być traktowane jako w stanie nieokreślonym.

Których wartości błędów użyć

Wiele wbudowanych wartości błędów może być używanych w dowolnych interfejsach AIDL, ale niektóre są traktowane w specjalny sposób. Na przykład EX_UNSUPPORTED_OPERATION i EX_ILLEGAL_ARGUMENT można użyć, gdy opisują stan błędu, ale EX_TRANSACTION_FAILED nie wolno używać, ponieważ jest traktowany specjalnie przez podstawową infrastrukturę. Sprawdź definicje specyficzne dla zaplecza, aby uzyskać więcej informacji na temat tych wbudowanych wartości.

Jeśli interfejs AIDL wymaga dodatkowych wartości błędów, które nie są objęte wbudowanymi typami błędów, może użyć specjalnego wbudowanego błędu specyficznego dla usługi, który umożliwia włączenie wartości błędu specyficznej dla usługi zdefiniowanej przez użytkownika . Te błędy specyficzne dla usługi są zwykle definiowane w interfejsie AIDL jako enum typu const int lub int i nie są analizowane przez program wiążący.

W Javie błędy są mapowane na wyjątki, takie jak android.os.RemoteException . W przypadku wyjątków specyficznych dla usług Java używa wyjątku android.os.ServiceSpecificException wraz z błędem zdefiniowanym przez użytkownika.

Natywny kod w Androidzie nie używa wyjątków. Zaplecze CPP używa android::binder::Status . Zaplecze NDK używa ndk::ScopedAStatus . Każda metoda wygenerowana przez AIDL zwraca jeden z nich, reprezentujący status metody. Backend Rust używa tych samych wartości kodów wyjątków co NDK, ale konwertuje je na natywne błędy Rust ( StatusCode , ExceptionCode ) przed dostarczeniem ich do użytkownika. W przypadku błędów specyficznych dla usługi zwrócony Status lub ScopedAStatus używa EX_SERVICE_SPECIFIC wraz z błędem zdefiniowanym przez użytkownika.

Wbudowane typy błędów można znaleźć w następujących plikach:

Zaplecze Definicja
Jawa android/os/Parcel.java
CPP binder/Status.h
NDK android/binder_status.h
Rdza android/binder_status.h

Korzystanie z różnych backendów

Te instrukcje dotyczą kodu platformy Android. Te przykłady używają zdefiniowanego typu, my.package.IFoo . Aby uzyskać instrukcje dotyczące korzystania z backendu Rust, zobacz przykład Rust AIDL na stronie Android Rust Patterns .

Importowanie typów

Niezależnie od tego, czy zdefiniowany typ jest interfejsem, pakietowalnym czy unią, możesz go zaimportować w Javie:

import my.package.IFoo;

Lub w zapleczu CPP:

#include <my/package/IFoo.h>

Lub w zapleczu NDK (zwróć uwagę na dodatkową aidl nazw pomocy):

#include <aidl/my/package/IFoo.h>

Lub w zapleczu Rusta:

use my_package::aidl::my::package::IFoo;

Chociaż można zaimportować typ zagnieżdżony w Javie, w backendach CPP/NDK należy dołączyć nagłówek dla jego typu głównego. Na przykład podczas importowania zagnieżdżonego typu Bar zdefiniowanego w my/package/IFoo.aidl ( IFoo jest typem głównym pliku) należy dołączyć <my/package/IFoo.h> dla backendu CPP (lub <aidl/my/package/IFoo.h> dla zaplecza NDK).

Wdrażanie usług

Aby zaimplementować usługę, musisz dziedziczyć z natywnej klasy pośredniczącej. Ta klasa odczytuje polecenia ze sterownika bindera i wykonuje zaimplementowane metody. Wyobraź sobie, że masz taki plik AIDL:

    package my.package;
    interface IFoo {
        int doFoo();
    }

W Javie musisz rozszerzyć tę klasę:

    import my.package.IFoo;
    public class MyFoo extends IFoo.Stub {
        @Override
        int doFoo() { ... }
    }

W zapleczu CPP:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo {
        android::binder::Status doFoo(int32_t* out) override;
    }

W zapleczu NDK (zwróć uwagę na dodatkową aidl nazw pomocy):

    #include <aidl/my/package/BnFoo.h>
    class MyFoo : public aidl::my::package::BnFoo {
        ndk::ScopedAStatus doFoo(int32_t* out) override;
    }

W zapleczu Rusta:

    use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
    use binder;

    /// This struct is defined to implement IRemoteService AIDL interface.
    pub struct MyFoo;

    impl Interface for MyFoo {}

    impl IFoo for MyFoo {
        fn doFoo(&self) -> binder::Result<()> {
           ...
           Ok(())
        }
    }

Rejestracja i uzyskiwanie usług

Usługi na platformie Android są zwykle rejestrowane w procesie servicemanager . Oprócz poniższych interfejsów API niektóre interfejsy API sprawdzają usługę (co oznacza, że ​​zwracają ją natychmiast, jeśli usługa jest niedostępna). Sprawdź odpowiedni interfejs servicemanager , aby uzyskać szczegółowe informacje. Te operacje można wykonać tylko podczas kompilacji na platformę Android.

w Javie:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // return if service is started now
    myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
    // waiting until service comes up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));

W zapleczu CPP:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // return if service is started now
    status_t err = checkService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

W zapleczu NDK (zwróć uwagę na dodatkową aidl nazw pomocy):

    #include <android/binder_manager.h>
    // registering
    status_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
    // return if service is started now
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_checkService("service-name")));
    // is a service declared in the VINTF manifest
    // VINTF services have the type in the interface instance name.
    bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
    // wait until a service is available (if isDeclared or you know it's available)
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_waitForService("service-name")));

W zapleczu Rusta:

use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;

fn main() {
    binder::ProcessState::start_thread_pool();
    // [...]
    let my_service = MyFoo;
    let my_service_binder = BnFoo::new_binder(
        my_service,
        BinderFeatures::default(),
    );
    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
    // Does not return - spawn or perform any work you mean to do before this call.
    binder::ProcessState::join_thread_pool()
}

Możesz poprosić o powiadomienie, gdy usługa hostująca segregator przestanie działać. Może to pomóc w uniknięciu wycieków proxy wywołania zwrotnego lub pomóc w naprawie błędów. Wykonaj te wywołania na obiektach proxy programu wiążącego.

  • W Javie użyj android.os.IBinder::linkToDeath .
  • W zapleczu CPP użyj android::IBinder::linkToDeath .
  • W zapleczu NDK użyj AIBinder_linkToDeath .
  • W backendzie Rust utwórz obiekt DeathRecipient , a następnie wywołaj my_binder.link_to_death(&mut my_death_recipient) . Zauważ, że ponieważ DeathRecipient jest właścicielem wywołania zwrotnego, musisz utrzymywać ten obiekt aktywny tak długo, jak chcesz otrzymywać powiadomienia.

Informacje o dzwoniącym

Podczas odbierania wywołania spinacza jądra informacje o dzwoniącym są dostępne w kilku interfejsach API. PID (lub identyfikator procesu) odnosi się do identyfikatora procesu systemu Linux procesu, który wysyła transakcję. Identyfikator UID (lub identyfikator użytkownika) odnosi się do identyfikatora użytkownika systemu Linux. Podczas odbierania wywołania jednokierunkowego wywołujący identyfikator PID wynosi 0. Gdy znajdują się poza kontekstem transakcji spinacza, te funkcje zwracają identyfikatory PID i UID bieżącego procesu.

W zapleczu Javy:

    ... = Binder.getCallingPid();
    ... = Binder.getCallingUid();

W zapleczu CPP:

    ... = IPCThreadState::self()->getCallingPid();
    ... = IPCThreadState::self()->getCallingUid();

W backendzie NDK:

    ... = AIBinder_getCallingPid();
    ... = AIBinder_getCallingUid();

W zapleczu Rust, podczas implementacji interfejsu, określ następujące elementy (zamiast zezwalania na ustawienie domyślne):

    ... = ThreadState::get_calling_pid();
    ... = ThreadState::get_calling_uid();

Raporty o błędach i debugowanie API dla usług

Kiedy raporty o błędach są uruchamiane (na przykład za pomocą adb bugreport ), zbierają informacje z całego systemu, aby pomóc w debugowaniu różnych problemów. W przypadku usług AIDL raporty o błędach używają binarnych dumpsys we wszystkich usługach zarejestrowanych przez menedżera usług, aby zrzucić ich informacje do raportu o błędzie. Możesz także użyć dumpsys w wierszu poleceń, aby uzyskać informacje z usługi z dumpsys SERVICE [ARGS] . W backendach C++ i Java możesz kontrolować kolejność, w jakiej usługi są zrzucane, używając dodatkowych argumentów do addService . Możesz także użyć dumpsys --pid SERVICE , aby uzyskać PID usługi podczas debugowania.

Aby dodać niestandardowe dane wyjściowe do swojej usługi, możesz zastąpić metodę dump w swoim obiekcie serwera, tak jak implementujesz dowolną inną metodę IPC zdefiniowaną w pliku AIDL. Robiąc to, powinieneś ograniczyć zrzut do uprawnień aplikacji android.permission.DUMP lub ograniczyć zrzut do określonych identyfikatorów UID.

W zapleczu Javy:

    @Override
    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
        @Nullable String[] args) {...}

W zapleczu CPP:

    status_t dump(int, const android::android::Vector<android::String16>&) override;

W backendzie NDK:

    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;

W zapleczu Rust, podczas implementacji interfejsu, określ następujące elementy (zamiast zezwalania na ustawienie domyślne):

    fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>

Dynamiczne pobieranie deskryptora interfejsu

Deskryptor interfejsu identyfikuje typ interfejsu. Jest to przydatne podczas debugowania lub gdy masz nieznany program wiążący.

W Javie możesz uzyskać deskryptor interfejsu z kodem takim jak:

    service = /* get ahold of service object */
    ... = service.asBinder().getInterfaceDescriptor();

W zapleczu CPP:

    service = /* get ahold of service object */
    ... = IInterface::asBinder(service)->getInterfaceDescriptor();

Backendy NDK i Rust nie obsługują tej funkcjonalności.

Statyczne pobieranie deskryptora interfejsu

Czasami (na przykład podczas rejestracji usług @VintfStability ) musisz wiedzieć, jaki jest statyczny deskryptor interfejsu. W Javie możesz uzyskać deskryptor, dodając kod, taki jak:

    import my.package.IFoo;
    ... IFoo.DESCRIPTOR

W zapleczu CPP:

    #include <my/package/BnFoo.h>
    ... my::package::BnFoo::descriptor

W zapleczu NDK (zwróć uwagę na dodatkową aidl nazw pomocy):

    #include <aidl/my/package/BnFoo.h>
    ... aidl::my::package::BnFoo::descriptor

W zapleczu Rusta:

    aidl::my::package::BnFoo::get_descriptor()

Zakres wyliczeniowy

W natywnych backendach możesz iterować nad możliwymi wartościami, które może przyjąć enum. Ze względu na rozmiar kodu nie jest to obecnie obsługiwane w Javie.

Dla wyliczenia MyEnum zdefiniowanego w AIDL iteracja jest zapewniona w następujący sposób.

W zapleczu CPP:

    ::android::enum_range<MyEnum>()

W backendzie NDK:

   ::ndk::enum_range<MyEnum>()

W zapleczu Rusta:

    MyEnum::enum_range()

Zarządzanie wątkami

Każda instancja libbinder w procesie utrzymuje jedną pulę wątków. W większości przypadków użycia powinna to być dokładnie jedna pula wątków, współdzielona przez wszystkie backendy. Jedynym wyjątkiem jest sytuacja, w której kod dostawcy może załadować inną kopię libbinder , aby porozmawiać z /dev/vndbinder . Ponieważ znajduje się to w osobnym węźle wiążącym, pula wątków nie jest udostępniana.

W przypadku zaplecza Java pula wątków może tylko zwiększać rozmiar (ponieważ jest już uruchomiona):

    BinderInternal.setMaxThreads(<new larger value>);

W przypadku zaplecza CPP dostępne są następujące operacje:

    // set max threadpool count (default is 15)
    status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
    // create threadpool
    ProcessState::self()->startThreadPool();
    // add current thread to threadpool (adds thread to max thread count)
    IPCThreadState::self()->joinThreadPool();

Podobnie w backendzie NDK:

    bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
    ABinderProcess_startThreadPool();
    ABinderProcess_joinThreadPool();

W zapleczu Rusta:

    binder::ProcessState::start_thread_pool();
    binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
    binder::ProcessState::join_thread_pool();

Nazwy zastrzeżone

C++, Java i Rust rezerwują niektóre nazwy jako słowa kluczowe lub do użytku specyficznego dla języka. Chociaż AIDL nie wymusza ograniczeń opartych na regułach językowych, użycie nazw pól lub typów, które pasują do zastrzeżonej nazwy, może spowodować błąd kompilacji dla C++ lub Java. W Rust nazwa pola lub typu jest zmieniana przy użyciu składni „surowego identyfikatora”, dostępnej za pomocą przedrostka r# .

Zalecamy unikanie używania zastrzeżonych nazw w definicjach AIDL, jeśli to możliwe, aby uniknąć nieergonomicznych powiązań lub całkowitego niepowodzenia kompilacji.

Jeśli masz już zarezerwowane nazwy w definicjach AIDL, możesz bezpiecznie zmieniać nazwy pól, zachowując przy tym zgodność z protokołami; może być konieczne zaktualizowanie kodu, aby kontynuować budowanie, ale wszelkie już utworzone programy będą nadal współdziałać.

Nazwy, których należy unikać: * Słowa kluczowe C++ * Słowa kluczowe Java * Słowa kluczowe Rust