Ein AIDL-Backend ist ein Ziel für die Stub-Code-Generierung. Wenn Sie AIDL-Dateien verwenden, verwenden Sie diese immer in einer bestimmten Sprache mit einer bestimmten Laufzeit. Je nach Kontext sollten Sie unterschiedliche AIDL-Backends verwenden.
AIDL verfügt über die folgenden Backends:
Backend | Sprache | API-Oberfläche | Bauen Sie Systeme auf |
---|---|---|---|
Java | Java | SDK/SystemApi (stabil*) | alle |
NDK | C++ | libbinder_ndk (stabil*) | helpl_interface |
CPP | C++ | libbinder (instabil) | alle |
Rost | Rost | libbinder_rs (instabil) | helpl_interface |
- Diese API-Oberflächen sind stabil, aber viele der APIs, beispielsweise für die Dienstverwaltung, sind für die interne Plattformnutzung reserviert und stehen Apps nicht zur Verfügung. Weitere Informationen zur Verwendung von AIDL in Apps finden Sie in der Entwicklerdokumentation .
- Das Rust-Backend wurde in Android 12 eingeführt; Das NDK-Backend ist ab Android 10 verfügbar.
- Die Rust-Kiste basiert auf
libbinder_ndk
. APEXes verwenden die Ordnerkiste auf die gleiche Weise wie alle anderen auf der Systemseite. Der Rust-Anteil wird in einem APEX gebündelt und darin versendet. Es hängt vonlibbinder_ndk.so
auf der Systempartition ab.
Bauen Sie Systeme auf
Abhängig vom Backend gibt es zwei Möglichkeiten, AIDL in Stub-Code zu kompilieren. Weitere Einzelheiten zu den Build-Systemen finden Sie in der Soong-Modulreferenz .
Kern-Build-System
In jedem cc_
oder java_
Android.bp-Modul (oder in ihren Android.mk
Entsprechungen) können .aidl
Dateien als Quelldateien angegeben werden. In diesem Fall werden die Java/CPP-Backends von AIDL verwendet (nicht das NDK-Backend) und die Klassen zur Verwendung der entsprechenden AIDL-Dateien werden dem Modul automatisch hinzugefügt. Optionen wie local_include_dirs
, die dem Build-System den Root-Pfad zu AIDL-Dateien in diesem Modul mitteilen, können in diesen Modulen unter einer Gruppe aidl:
angegeben werden. Beachten Sie, dass das Rust-Backend nur für die Verwendung mit Rust vorgesehen ist. rust_
Module werden insofern anders gehandhabt, als AIDL-Dateien nicht als Quelldateien angegeben werden. Stattdessen erzeugt das Modul aidl_interface
eine rustlib
namens <aidl_interface name>-rust
, mit der verknüpft werden kann. Weitere Einzelheiten finden Sie im Rust AIDL-Beispiel .
helpl_interface
Mit diesem Build-System verwendete Typen müssen strukturiert sein. Um strukturiert zu sein, müssen Parcelables Felder direkt enthalten und dürfen keine Deklarationen von Typen sein, die direkt in Zielsprachen definiert sind. Informationen dazu, wie strukturiertes AIDL mit stabilem AIDL zusammenpasst, finden Sie unter Strukturiertes vs. stabiles AIDL .
Typen
Sie können den aidl
Compiler als Referenzimplementierung für Typen betrachten. Wenn Sie eine Schnittstelle erstellen, rufen Sie aidl --lang=<backend> ...
auf, um die resultierende Schnittstellendatei anzuzeigen. Wenn Sie das Modul aidl_interface
verwenden, können Sie die Ausgabe in out/soong/.intermediates/<path to module>/
anzeigen.
Java/AIDL-Typ | C++-Typ | NDK-Typ | Rosttyp |
---|---|---|---|
Boolescher Wert | bool | bool | bool |
Byte | int8_t | int8_t | i8 |
verkohlen | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
lang | int64_t | int64_t | i64 |
schweben | schweben | schweben | f32 |
doppelt | doppelt | doppelt | f64 |
Zeichenfolge | android::String16 | std::string | Zeichenfolge |
android.os.Parcelable | android::Parcelable | N / A | N / A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | In: &[T] Aus: Vec<T> |
Byte[] | std::vector<uint8_t> | std::vector<int8_t> 1 | In: &[u8] Aus: Vec<u8> |
Liste<T> | std::vector<T> 2 | std::vector<T> 3 | In: &[T] 4 Aus: Vec<T> |
FileDescriptor | android::base::unique_fd | N / A | binder::parcel::ParcelFileDescriptor |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
Schnittstellentyp (T) | android::sp<T> | std::shared_ptr<T> | Bindemittel::Stark |
Paketierbarer Typ (T) | T | T | T |
Verbindungstyp (T) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. In Android 12 oder höher verwenden Byte-Arrays aus Kompatibilitätsgründen uint8_t anstelle von int8_t.
2. Das C++-Backend unterstützt List<T>
, wobei T
entweder String
, IBinder
, ParcelFileDescriptor
oder Parcelable ist. In Android 13 oder höher kann T
jeder nicht-primitive Typ (einschließlich Schnittstellentypen) außer Arrays sein. AOSP empfiehlt die Verwendung von Array-Typen wie T[]
, da diese in allen Backends funktionieren.
3. Das NDK-Backend unterstützt List<T>
, wobei T
entweder String
, ParcelFileDescriptor
oder Parcelable ist. In Android 13 oder höher kann T
jeder nicht-primitive Typ außer Arrays sein.
4. Typen werden für Rust-Code unterschiedlich übergeben, je nachdem, ob es sich um eine Eingabe (ein Argument) oder eine Ausgabe (ein zurückgegebener Wert) handelt.
5. Union-Typen werden in Android 12 und höher unterstützt.
6. In Android 13 oder höher werden Arrays mit fester Größe unterstützt. Arrays fester Größe können mehrere Dimensionen haben (z. B. int[3][4]
). Im Java-Backend werden Arrays fester Größe als Array-Typen dargestellt.
Direktionalität (ein/aus/ein)
Wenn Sie die Typen der Argumente für Funktionen angeben, können Sie diese als in
, out
oder inout
angeben. Dies steuert, in welche Richtung Informationen für einen IPC-Aufruf weitergeleitet werden. in
ist die Standardrichtung und gibt an, dass Daten vom Anrufer zum Angerufenen weitergeleitet werden. out
bedeutet, dass Daten vom Angerufenen an den Anrufer weitergegeben werden. inout
ist die Kombination aus beidem. Das Android-Team empfiehlt jedoch, die Verwendung des Argumentspezifizierers inout
zu vermeiden. Wenn Sie inout
mit einer versionierten Schnittstelle und einem älteren Aufrufer verwenden, werden die zusätzlichen Felder, die nur im Aufrufer vorhanden sind, auf ihre Standardwerte zurückgesetzt. In Bezug auf Rust empfängt ein normaler inout
Typ &mut Vec<T>
und ein Listen inout
-Typ &mut Vec<T>
.
UTF8/UTF16
Mit dem CPP-Backend können Sie wählen, ob Strings utf-8 oder utf-16 sind. Deklarieren Sie Zeichenfolgen in AIDL als @utf8InCpp String
, um sie automatisch in utf-8 zu konvertieren. Die NDK- und Rust-Backends verwenden immer UTF-8-Strings. Weitere Informationen zur utf8InCpp
Annotation finden Sie unter Annotationen in AIDL .
Nullbarkeit
Sie können Typen, die im Java-Backend null sein können, mit @nullable
annotieren, um Nullwerte für die CPP- und NDK-Backends verfügbar zu machen. Im Rust-Backend werden diese @nullable
Typen als Option<T>
verfügbar gemacht. Native Server lehnen standardmäßig Nullwerte ab. Die einzigen Ausnahmen hiervon sind interface
und IBinder
Typen, die für NDK-Lesevorgänge und CPP/NDK-Schreibvorgänge immer null sein können. Weitere Informationen zur nullable
Annotation finden Sie unter Annotationen in AIDL .
Individuelle Paketsendungen
Ein benutzerdefiniertes Parcelable ist ein Parcelable, das manuell in einem Ziel-Backend implementiert wird. Verwenden Sie benutzerdefinierte Parcelables nur, wenn Sie versuchen, Unterstützung für andere Sprachen für ein vorhandenes benutzerdefiniertes Parcelable hinzuzufügen, das nicht geändert werden kann.
Um eine benutzerdefinierte Parzellierung zu deklarieren, damit AIDL davon erfährt, sieht die AIDL-Parzellierung-Deklaration wie folgt aus:
package my.pack.age;
parcelable Foo;
Standardmäßig wird dadurch ein Java-Parcelable deklariert, wobei my.pack.age.Foo
eine Java-Klasse ist, die die Parcelable
Schnittstelle implementiert.
Für eine Deklaration eines benutzerdefinierten CPP-Backend-Parcelables in AIDL verwenden Sie cpp_header
:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
Die C++-Implementierung in my/pack/age/Foo.h
sieht folgendermaßen aus:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
Für eine Deklaration eines benutzerdefinierten NDK-Parcelables in AIDL verwenden Sie ndk_header
:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
Die NDK-Implementierung in android/package/Foo.h
sieht folgendermaßen aus:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
Dann können Sie dieses Parcelable als Typ in AIDL-Dateien verwenden, es wird jedoch nicht von AIDL generiert.
Rust unterstützt keine benutzerdefinierten Parcelables.
Standardwerte
Strukturierte Parcelables können Standardwerte pro Feld für Grundelemente, String
s und Arrays dieser Typen deklarieren.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
Wenn im Java-Backend Standardwerte fehlen, werden Felder für primitive Typen als Nullwerte und für nicht-primitive Typen als null
initialisiert.
In anderen Backends werden Felder mit standardmäßig initialisierten Werten initialisiert, wenn keine Standardwerte definiert sind. Beispielsweise werden im C++-Backend String
Felder als leere Zeichenfolge und List<T>
-Felder als leeres vector<T>
initialisiert. @nullable
Felder werden als Nullwertfelder initialisiert.
Fehlerbehandlung
Das Android-Betriebssystem bietet integrierte Fehlertypen für Dienste, die sie beim Melden von Fehlern verwenden können. Diese werden von Binder verwendet und können von allen Diensten verwendet werden, die eine Binder-Schnittstelle implementieren. Ihre Verwendung ist in der AIDL-Definition gut dokumentiert und sie erfordern keinen benutzerdefinierten Status oder Rückgabetyp.
Ausgabeparameter mit Fehlern
Wenn eine AIDL-Funktion einen Fehler meldet, initialisiert oder ändert die Funktion Ausgabeparameter möglicherweise nicht. Insbesondere können Ausgabeparameter geändert werden, wenn der Fehler beim Entpacken auftritt und nicht während der Verarbeitung der Transaktion selbst. Im Allgemeinen sollten beim Erhalten eines Fehlers von einer AIDL-Funktion alle inout
und out
Parameter sowie der Rückgabewert (der in einigen Backends wie ein out
Parameter wirkt) davon ausgegangen werden, dass sie sich in einem unbestimmten Zustand befinden.
Welche Fehlerwerte verwendet werden sollen
Viele der integrierten Fehlerwerte können in allen AIDL-Schnittstellen verwendet werden, einige werden jedoch auf besondere Weise behandelt. Beispielsweise können EX_UNSUPPORTED_OPERATION
und EX_ILLEGAL_ARGUMENT
verwendet werden, wenn sie den Fehlerzustand beschreiben, EX_TRANSACTION_FAILED
darf jedoch nicht verwendet werden, da es von der zugrunde liegenden Infrastruktur speziell behandelt wird. Weitere Informationen zu diesen integrierten Werten finden Sie in den Backend-spezifischen Definitionen.
Wenn die AIDL-Schnittstelle zusätzliche Fehlerwerte erfordert, die nicht von den integrierten Fehlertypen abgedeckt werden, können sie den speziellen dienstspezifischen integrierten Fehler verwenden, der die Einbeziehung eines dienstspezifischen Fehlerwerts ermöglicht, der vom Benutzer definiert wird . Diese dienstspezifischen Fehler werden in der Regel in der AIDL-Schnittstelle als const int
oder int
-backed enum
definiert und vom Binder nicht analysiert.
In Java werden Fehler Ausnahmen zugeordnet, beispielsweise android.os.RemoteException
. Für dienstspezifische Ausnahmen verwendet Java android.os.ServiceSpecificException
zusammen mit dem benutzerdefinierten Fehler.
Nativer Code in Android verwendet keine Ausnahmen. Das CPP-Backend verwendet android::binder::Status
. Das NDK-Backend verwendet ndk::ScopedAStatus
. Jede von AIDL generierte Methode gibt einen dieser Werte zurück, der den Status der Methode darstellt. Das Rust-Backend verwendet dieselben Ausnahmecodewerte wie das NDK, konvertiert sie jedoch in native Rust-Fehler ( StatusCode
, ExceptionCode
), bevor es sie an den Benutzer übermittelt. Bei dienstspezifischen Fehlern verwendet der zurückgegebene Status
oder ScopedAStatus
EX_SERVICE_SPECIFIC
zusammen mit dem benutzerdefinierten Fehler.
Die integrierten Fehlertypen finden Sie in den folgenden Dateien:
Backend | Definition |
---|---|
Java | android/os/Parcel.java |
CPP | binder/Status.h |
NDK | android/binder_status.h |
Rost | android/binder_status.h |
Verwendung verschiedener Backends
Diese Anweisungen gelten speziell für den Android-Plattformcode. Diese Beispiele verwenden einen definierten Typ, my.package.IFoo
. Anweisungen zur Verwendung des Rust-Backends finden Sie im Rust AIDL-Beispiel auf der Seite „Android Rust Patterns“ .
Typen importieren
Unabhängig davon, ob es sich bei dem definierten Typ um eine Schnittstelle, ein Parcelable oder eine Union handelt, können Sie ihn in Java importieren:
import my.package.IFoo;
Oder im CPP-Backend:
#include <my/package/IFoo.h>
Oder im NDK-Backend (beachten Sie den zusätzlichen aidl
Namespace):
#include <aidl/my/package/IFoo.h>
Oder im Rust-Backend:
use my_package::aidl::my::package::IFoo;
Obwohl Sie einen verschachtelten Typ in Java importieren können, müssen Sie in den CPP/NDK-Backends den Header für seinen Stammtyp einschließen. Wenn Sie beispielsweise einen verschachtelten Typ Bar
importieren, der in my/package/IFoo.aidl
definiert ist ( IFoo
ist der Stammtyp der Datei), müssen Sie <my/package/IFoo.h>
für das CPP-Backend (oder <aidl/my/package/IFoo.h>
einschließen <aidl/my/package/IFoo.h>
für das NDK-Backend).
Dienstleistungen umsetzen
Um einen Dienst zu implementieren, müssen Sie von der nativen Stub-Klasse erben. Diese Klasse liest Befehle vom Binder-Treiber und führt die von Ihnen implementierten Methoden aus. Stellen Sie sich vor, Sie hätten eine AIDL-Datei wie diese:
package my.package;
interface IFoo {
int doFoo();
}
In Java müssen Sie diese Klasse erweitern:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
Im CPP-Backend:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
Im NDK-Backend (beachten Sie den zusätzlichen aidl
Namespace):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
Im Rust-Backend:
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(())
}
}
Registrieren und Inanspruchnahme von Dienstleistungen
Dienste in der Android-Plattform werden normalerweise beim servicemanager
Prozess registriert. Zusätzlich zu den unten aufgeführten APIs überprüfen einige APIs den Dienst (das heißt, sie kehren sofort zurück, wenn der Dienst nicht verfügbar ist). Genaue Details finden Sie in der entsprechenden servicemanager
Schnittstelle. Diese Vorgänge können nur beim Kompilieren mit der Android-Plattform durchgeführt werden.
In Java:
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"));
Im CPP-Backend:
#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"));
Im NDK-Backend (beachten Sie den zusätzlichen aidl
Namespace):
#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")));
Im Rust-Backend:
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()
}
Verbindung zum Tod
Sie können eine Benachrichtigung anfordern, wenn ein Dienst, der einen Ordner hostet, ausfällt. Dies kann dazu beitragen, undichte Callback-Proxys zu vermeiden oder bei der Fehlerbeseitigung zu helfen. Führen Sie diese Aufrufe für Binder-Proxy-Objekte durch.
- Verwenden Sie in Java
android.os.IBinder::linkToDeath
. - Verwenden Sie im CPP-Backend
android::IBinder::linkToDeath
. - Verwenden Sie im NDK-Backend
AIBinder_linkToDeath
. - Erstellen Sie im Rust-Backend ein
DeathRecipient
Objekt und rufen Sie dannmy_binder.link_to_death(&mut my_death_recipient)
auf. Beachten Sie, dass Sie dieses Objekt so lange am Leben halten müssen, wie Sie Benachrichtigungen erhalten möchten, da derDeathRecipient
Eigentümer des Rückrufs ist.
Anruferinformationen
Beim Empfang eines Kernel-Binder-Aufrufs sind Aufruferinformationen in mehreren APIs verfügbar. Die PID (oder Prozess-ID) bezieht sich auf die Linux-Prozess-ID des Prozesses, der eine Transaktion sendet. Die UID (oder Benutzer-ID) bezieht sich auf die Linux-Benutzer-ID. Beim Empfang eines Einwegaufrufs ist die aufrufende PID 0. Außerhalb eines Binder-Transaktionskontexts geben diese Funktionen die PID und UID des aktuellen Prozesses zurück.
Im Java-Backend:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
Im CPP-Backend:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
Im NDK-Backend:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
Geben Sie im Rust-Backend bei der Implementierung der Schnittstelle Folgendes an (anstatt es als Standard zuzulassen):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Fehlerberichte und Debugging-API für Dienste
Wenn Bugreports ausgeführt werden (z. B. mit adb bugreport
), sammeln sie Informationen aus dem gesamten System, um bei der Fehlerbehebung verschiedener Probleme zu helfen. Für AIDL-Dienste verwenden Fehlerberichte die Binärdatei dumpsys
für alle beim Dienstmanager registrierten Dienste, um deren Informationen in den Fehlerbericht einzufügen. Sie können dumpsys
auch in der Befehlszeile verwenden, um Informationen von einem Dienst mit dumpsys SERVICE [ARGS]
abzurufen. In den C++- und Java-Backends können Sie die Reihenfolge steuern, in der Dienste ausgegeben werden, indem Sie zusätzliche Argumente für addService
verwenden. Sie können dumpsys --pid SERVICE
auch verwenden, um beim Debuggen die PID eines Dienstes abzurufen.
Um Ihrem Dienst eine benutzerdefinierte Ausgabe hinzuzufügen, können Sie die dump
Methode in Ihrem Serverobjekt überschreiben, so wie Sie jede andere in einer AIDL-Datei definierte IPC-Methode implementieren würden. Dabei sollten Sie das Dumping auf die App-Berechtigung android.permission.DUMP
oder auf bestimmte UIDs beschränken.
Im Java-Backend:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
Im CPP-Backend:
status_t dump(int, const android::android::Vector<android::String16>&) override;
Im NDK-Backend:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
Geben Sie im Rust-Backend bei der Implementierung der Schnittstelle Folgendes an (anstatt es als Standard zuzulassen):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Schnittstellendeskriptor dynamisch abrufen
Der Schnittstellendeskriptor identifiziert den Typ einer Schnittstelle. Dies ist nützlich beim Debuggen oder wenn Sie einen unbekannten Ordner haben.
In Java können Sie den Schnittstellendeskriptor mit Code wie dem folgenden abrufen:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
Im CPP-Backend:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
Die NDK- und Rust-Backends unterstützen diese Funktionalität nicht.
Schnittstellendeskriptor statisch abrufen
Manchmal (z. B. bei der Registrierung @VintfStability
Diensten) müssen Sie statisch wissen, wie der Schnittstellendeskriptor lautet. In Java können Sie den Deskriptor erhalten, indem Sie Code hinzufügen wie:
import my.package.IFoo;
... IFoo.DESCRIPTOR
Im CPP-Backend:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
Im NDK-Backend (beachten Sie den zusätzlichen aidl
Namespace):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
Im Rust-Backend:
aidl::my::package::BnFoo::get_descriptor()
Enum-Bereich
In nativen Backends können Sie die möglichen Werte, die eine Aufzählung annehmen kann, iterieren. Aus Gründen der Codegröße wird dies derzeit in Java nicht unterstützt.
Für eine in AIDL definierte Aufzählung MyEnum
wird die Iteration wie folgt bereitgestellt.
Im CPP-Backend:
::android::enum_range<MyEnum>()
Im NDK-Backend:
::ndk::enum_range<MyEnum>()
Im Rust-Backend:
MyEnum::enum_values()
Thread-Management
Jede Instanz von libbinder
in einem Prozess verwaltet einen Threadpool. In den meisten Anwendungsfällen sollte dies genau ein Threadpool sein, der von allen Backends gemeinsam genutzt wird. Die einzige Ausnahme besteht darin, dass der Herstellercode möglicherweise eine weitere Kopie von libbinder
lädt, um mit /dev/vndbinder
zu kommunizieren. Da sich dieser auf einem separaten Binderknoten befindet, wird der Threadpool nicht gemeinsam genutzt.
Für das Java-Backend kann der Threadpool nur vergrößert werden (da er bereits gestartet ist):
BinderInternal.setMaxThreads(<new larger value>);
Für das CPP-Backend stehen folgende Operationen zur Verfügung:
// 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();
Ebenso im NDK-Backend:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
Im Rust-Backend:
binder::ProcessState::start_thread_pool();
binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
binder::ProcessState::join_thread_pool();
Reservierte Namen
C++, Java und Rust reservieren einige Namen als Schlüsselwörter oder für die sprachspezifische Verwendung. Während AIDL keine auf Sprachregeln basierenden Einschränkungen erzwingt, kann die Verwendung von Feld- oder Typnamen, die mit einem reservierten Namen übereinstimmen, zu einem Kompilierungsfehler für C++ oder Java führen. Für Rust wird das Feld oder der Typ mithilfe der „Raw Identifier“-Syntax umbenannt, auf die über das Präfix r#
zugegriffen werden kann.
Wir empfehlen Ihnen, nach Möglichkeit die Verwendung reservierter Namen in Ihren AIDL-Definitionen zu vermeiden, um unergonomische Bindungen oder einen völligen Kompilierungsfehler zu vermeiden.
Wenn Sie in Ihren AIDL-Definitionen bereits reservierte Namen haben, können Sie Felder bedenkenlos umbenennen und gleichzeitig die Protokollkompatibilität bewahren. Möglicherweise müssen Sie Ihren Code aktualisieren, um mit der Erstellung fortzufahren. Alle bereits erstellten Programme funktionieren jedoch weiterhin.
Zu vermeidende Namen: * C++-Schlüsselwörter * Java-Schlüsselwörter * Rust-Schlüsselwörter