Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Patrones de óxido de Android

Esta página contiene información sobre registro de Android , proporciona un ejemplo Rust AIDL , le indica cómo llamar Rust de C , y proporciona instrucciones para Rust / C ++ de interoperabilidad Usando CXX .

Registro de Android

El siguiente ejemplo muestra cómo se puede registrar mensajes a logcat (en el dispositivo) o stdout (el huésped).

En su Android.bp módulo, agregue liblogger y liblog_rust como dependencias:

rust_binary {
    name: "logging_test",
    srcs: ["src/main.rs"],
    rustlibs: [
        "liblogger",
        "liblog_rust",
    ],
}

A continuación, en su fuente de Rust agregue este código:

use log::{debug, error, Level};

fn main() {
    let init_success = logger::init(
        logger::Config::default()
            .with_tag_on_device("mytag")
            .with_min_level(Level::Trace),
    );
    debug!("This is a debug message.");
    error!("Something went wrong!");
}

Es decir, añadir las dos dependencias que se muestran arriba ( liblogger y liblog_rust ), llame al init método de una vez (se le puede llamar más de una vez si es necesario), y los mensajes que utilizan las macros proporcionados conectarse. Vea la caja registrador para obtener una lista de posibles opciones de configuración.

La caja del registrador proporciona una API para definir lo que desea registrar. Dependiendo de si el código se ejecuta en el dispositivo o en el huésped (tal como parte de una prueba del lado del anfitrión), los mensajes se registran utilizando android_logger o env_logger .

Ejemplo de Rust AIDL

Esta sección proporciona un ejemplo al estilo Hello World del uso de AIDL con Rust.

Uso de Android Guía del desarrollador AIDL Descripción sección como punto de partida, cree external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl con el siguiente contenido en el IRemoteService.aidl archivo:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

Luego, dentro de la external/rust/binder_example/aidl/Android.bp archivo, definir el aidl_interface módulo. Debe habilitar explícitamente el backend Rust, ya que no está activado por defecto.

aidl_interface {
    name: "com.example.android.remoteservice",
    srcs: [ "aidl/com/example/android/*.aidl", ],
    unstable: true, // Add during development until the interface is stabilized.
    backend: {
        rust: {
            // By default, the Rust backend is not enabled
            enabled: true,
        },
    },
}

El backend AIDL es un generador de fuente Rust, por lo que funciona como otros generadores de fuente Rust y produce una biblioteca Rust. El módulo de biblioteca de Rust producido puede ser utilizado por otros módulos de Rust como una dependencia. Como un ejemplo del uso de la biblioteca producida como una dependencia, un rust_library se puede definir como sigue en external/rust/binder_example/Android.bp :

rust_library {
    name: "libmyservice",
    srcs: ["src/lib.rs"],
    crate_name: "myservice",
    rustlibs: [
        "com.example.android.remoteservice-rust",
        "libbinder_rs",
    ],
}

Tenga en cuenta que el formato de nombre de módulo para la biblioteca generada por AIDL utilizado en rustlibs es la aidl_interface nombre del módulo seguido de -rust ; en este caso, com.example.android.remoteservice-rust .

La interfaz AIDL continuación, se puede hacer referencia en src/lib.rs como sigue:

// Note carefully the AIDL crates structure:
// * the AIDL module name: "com_example_android_remoteservice"
// * next "::aidl"
// * next the AIDL package name "::com::example::android"
// * the interface: "::IRemoteService"
// * finally, the 'BnRemoteService' and 'IRemoteService' submodules

//! This module implements the IRemoteService AIDL interface
use com_example_android_remoteservice::aidl::com::example::android::{
  IRemoteService::{BnRemoteService, IRemoteService}
};
use com_example_android_remoteservice::binder::{
    BinderFeatures, Interface, Result as BinderResult, Strong,
};

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

impl Interface for MyService {}

impl IRemoteService for MyService {
    fn getPid(&self) -> BinderResult<i32> {
        Ok(42)
    }

    fn basicTypes(&self, _: i32, _: i64, _: bool, _: f32, _: f64, _: &str) -> BinderResult<()> {
        // Do something interesting...
        Ok(())
    }
}

Finalmente, inicie el servicio en un binario de Rust como se muestra a continuación:

use myservice::MyService;

fn main() {
    // [...]
    let my_service = MyService;
    let my_service_binder = BnRemoteService::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()
}

Llamar a Rust desde C

Este ejemplo muestra cómo llamar a Rust desde C.

Ejemplo de biblioteca de Rust

Definir el libsimple_printer archivo en external/rust/simple_printer/libsimple_printer.rs de la siguiente manera:

//! A simple hello world example that can be called from C

#[no_mangle]
/// Print "Hello Rust!"
pub extern fn print_c_hello_rust() {
    println!("Hello Rust!");
}

La biblioteca Rust debe definir cabeceras que los módulos C dependientes puede tirar en, por lo que definir el external/rust/simple_printer/simple_printer.h cabecera como sigue:

#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H

void print_c_hello_rust();


#endif

Definir external/rust/simple_printer/Android.bp como se ve aquí:

rust_ffi {
    name: "libsimple_c_printer",
    crate_name: "simple_c_printer",
    srcs: ["libsimple_c_printer.rs"],

    // Define include_dirs so cc_binary knows where the headers are.
    include_dirs: ["."],
}

Ejemplo C binario

Definir external/rust/c_hello_rust/main.c como sigue:

#include "simple_printer.h"

int main() {
  print_c_hello_rust();
  return 0;
}

Definir external/rust/c_hello_rust/Android.bp como sigue:

cc_binary {
    name: "c_hello_rust",
    srcs: ["main.c"],
    shared_libs: ["libsimple_c_printer"],
}

Por último, la acumulación llamando m c_hello_rust .

Interoperabilidad de Rust-Java

La jni del cajón proporciona interoperabilidad Rust con Java a través de la interfaz nativa de Java (JNI). Define las definiciones de tipos necesarios para Rust para producir un Rust cdylib biblioteca que se conecta directamente en Java JNI ( JNIEnv , JClass , JString , y así sucesivamente). A diferencia de los enlaces de C ++ que llevan a cabo a través CODEGEN cxx , la interoperabilidad de Java a través de la JNI no requiere una etapa de generación de código durante una generación. Por lo tanto, no necesita soporte especial para el sistema de compilación. Las cargas de código Java proporcionado el Rust- cdylib como cualquier otra biblioteca nativa.

Uso

Uso tanto en código Java Rust y está cubierto de la jni documentación del cajón . Por favor siga el Getting Started ejemplo proporcionado allí. Después de escribir src/lib.rs , vuelva a esta página para aprender cómo construir la biblioteca con sistema de construcción de Android.

Definición de construcción

Java requiere la biblioteca Rust ser proporcionado como un cdylib de modo que se puede cargar de forma dinámica. La definición de la biblioteca Rust en Soong es la siguiente:

rust_ffi_shared {
    name: "libhello_jni",
    crate_name: "hello_jni",
    srcs: ["src/lib.rs"],

    // The jni crate is required
    rustlibs: ["libjni"],
}

Las listas de bibliotecas Java biblioteca de la roya como required la dependencia; esto asegura que esté instalado en el dispositivo junto con la biblioteca de Java, aunque no sea una dependencia de tiempo de compilación:

java_library {
        name: "libhelloworld",
        [...]
        required: ["libhellorust"]
        [...]
}

Como alternativa, si debe incluir la biblioteca Rust en una AndroidManifest.xml archivo, agregue la biblioteca para uses_libs de la siguiente manera:

java_library {
        name: "libhelloworld",
        [...]
        uses_libs: ["libhellorust"]
        [...]
}

Interoperabilidad de Rust-C ++ con CXX

El CXX cajón proporciona FFI segura entre Rust y un subconjunto de C ++. La documentación CXX da buenos ejemplos de cómo funciona en general. El siguiente ejemplo muestra cómo usarlo en Android.

Tener CXX generar el código C ++ que pone en Rust, definir un genrule para invocar CXX y una cc_library_static para empaquetar que en una biblioteca. Si planea que C ++ llame al código de Rust, o use tipos compartidos entre C ++ y Rust, defina un segundo genrule (para generar un encabezado de C ++ que contenga los enlaces de Rust).

cc_library_static {
    name: "libcxx_test_cpp",
    srcs: ["cxx_test.cpp"],
    generated_headers: [
        "cxx-bridge-header",
        "libcxx_test_bridge_header"
    ],
    generated_sources: ["libcxx_test_bridge_code"],
}

genrule {
    name: "libcxx_test_bridge_code",
    tools: ["cxxbridge"],
    cmd: "$(location cxxbridge) $(in) >> $(out)",
    srcs: ["lib.rs"],
    out: ["libcxx_test_cxx_generated.cc"],
}

// This defines a second genrule to generate a C++ header.
// * The generated header contains the C++ bindings to the Rust exported
// * functions in lib.rs.

genrule {
    name: "libcxx_test_bridge_header",
    tools: ["cxxbridge"],
    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
    srcs: ["lib.rs"],
    out: ["lib.rs.h"],
}

Luego, vincule esto a una biblioteca o ejecutable de Rust:

rust_binary {
    name: "cxx_test",
    srcs: ["lib.rs"],
    rustlibs: ["libcxx"],
    static_libs: ["libcxx_test_cpp"],
}

En los .cpp y .hpp archivos, definir las funciones de C ++ como desee, utilizando el CXX envoltorio tipos según se desee. Por ejemplo, un cxx_test.hpp definición contiene lo siguiente:

#pragma once

#include "rust/cxx.h"

int greet(rust::Str greetee);

Mientras cxx_test.cpp contiene

#include "cxx_test.hpp"
#include "lib.rs.h"

#include <iostream>

int greet(rust::Str greetee) {
  std::cout << "Hello, " << greetee << std::endl;
  return get_num();
}

Para utilizar esto desde Rust, definir un puente CXX de la siguiente manera en lib.rs :

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("cxx_test.hpp");
        fn greet(greetee: &str) -> i32;
    }
    extern "Rust" {
        fn get_num() -> i32;
    }
}

fn main() {
    let result = ffi::greet("world");
    println!("C++ returned {}", result);
}

fn get_num() -> i32 {
    return 42;
}