AIDL เวอร์ชันเสถียร

Android 10 เพิ่มการรองรับ Android Interface Definition Language (AIDL) ที่เสถียร ซึ่งเป็นวิธีใหม่ในการติดตาม Application Program Interface (API) และ Application Binary Interface (ABI) ที่อินเทอร์เฟซ AIDL มีให้ Stable AIDL ทำงานเหมือนกับ AIDL ทุกประการ แต่ระบบบิลด์จะติดตาม ความเข้ากันได้ของอินเทอร์เฟซ และมีข้อจำกัดเกี่ยวกับสิ่งที่คุณทำได้ ดังนี้

  • อินเทอร์เฟซจะกำหนดไว้ในระบบบิลด์ด้วย aidl_interfaces
  • อินเทอร์เฟซมีได้เฉพาะ Structured Data ระบบจะสร้าง Parcelable ที่แสดงถึง ประเภทที่ต้องการโดยอัตโนมัติตามคำจำกัดความ AIDL และ จะจัดรูปแบบและยกเลิกการจัดรูปแบบโดยอัตโนมัติ
  • อินเทอร์เฟซสามารถประกาศเป็นแบบเสถียร (เข้ากันได้แบบย้อนหลัง) เมื่อเกิดเหตุการณ์นี้ ระบบจะติดตามและกำหนดเวอร์ชัน API ของแอปในไฟล์ที่อยู่ข้างอินเทอร์เฟซ AIDL

AIDL ที่มีโครงสร้างเทียบกับ AIDL ที่เสถียร

Structured AIDL หมายถึงประเภทที่กำหนดใน AIDL เท่านั้น เช่น การประกาศที่ส่งผ่านได้ (ที่ส่งผ่านได้ที่กำหนดเอง) ไม่ใช่ AIDL ที่มีโครงสร้าง Parcelable ที่มีฟิลด์ที่กำหนดใน AIDL เรียกว่า Parcelable ที่มีโครงสร้าง

AIDL ที่เสถียรต้องใช้ AIDL ที่มีโครงสร้างเพื่อให้ระบบบิลด์และคอมไพเลอร์ สามารถเข้าใจได้ว่าการเปลี่ยนแปลงที่ทำกับ Parcelable เข้ากันได้แบบย้อนหลังหรือไม่ อย่างไรก็ตาม อินเทอร์เฟซที่มีโครงสร้างบางอย่างอาจไม่เสถียร อินเทอร์เฟซต้องใช้เฉพาะประเภทที่มีโครงสร้างและต้องใช้ฟีเจอร์การกำหนดเวอร์ชันต่อไปนี้จึงจะเสถียร ในทางกลับกัน อินเทอร์เฟซจะไม่เสถียรหากใช้ระบบบิลด์หลัก ในการสร้าง หรือหากตั้งค่า 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 ที่ประกอบกันเป็นอินเทอร์เฟซ เส้นทาง สำหรับประเภท AIDL Foo ที่กำหนดไว้ในแพ็กเกจ com.acme ควรอยู่ที่ <base_path>/com/acme/Foo.aidl โดย <base_path> อาจเป็นไดเรกทอรีใดก็ได้ ที่เกี่ยวข้องกับไดเรกทอรีที่ Android.bp อยู่ ในตัวอย่างก่อนหน้านี้ <base_path> คือ srcs/aidl
  • local_include_dir: เส้นทางที่ชื่อแพ็กเกจเริ่มต้น ซึ่งสอดคล้องกับ <base_path> ที่อธิบายไว้ข้างต้น
  • imports: รายการโมดูล aidl_interface ที่ใช้ หากอินเทอร์เฟซ AIDL รายการใดรายการหนึ่งของคุณใช้อินเทอร์เฟซหรือ Parcelable จากaidl_interfaceอื่น ให้ระบุชื่อที่นี่ ซึ่งอาจเป็นชื่อเพียงอย่างเดียว เพื่ออ้างอิงถึงเวอร์ชันล่าสุด หรือชื่อที่มีคำต่อท้ายเวอร์ชัน (เช่น -V1) เพื่ออ้างอิงถึง เวอร์ชันที่เฉพาะเจาะจง ระบบรองรับการระบุเวอร์ชันตั้งแต่ Android 12
  • versions: อินเทอร์เฟซเวอร์ชันก่อนหน้าซึ่ง หยุดการอัปเดตภายใต้ api_dir ตั้งแต่ Android 11 เป็นต้นไป versions จะหยุดการอัปเดตภายใต้ aidl_api/name หากไม่มีอินเทอร์เฟซเวอร์ชันที่หยุดการเปลี่ยนแปลงแล้ว คุณไม่ควรระบุค่านี้ และจะไม่มีการตรวจสอบความเข้ากันได้ ช่องนี้ถูกแทนที่ด้วย versions_with_info สำหรับ Android 13 ขึ้นไป
  • versions_with_info: รายการของทูเพิล ซึ่งแต่ละทูเพิลมีชื่อของ เวอร์ชันที่ตรึงไว้และรายการที่มีการนำเข้าเวอร์ชันของโมดูล aidl_interface อื่นๆ ที่ aidl_interface เวอร์ชันนี้นำเข้า คำจำกัดความ ของอินเทอร์เฟซ AIDL เวอร์ชัน V IFACE อยู่ที่ aidl_api/IFACE/V ฟิลด์นี้เปิดตัวใน Android 13 และไม่ควรแก้ไขใน Android.bp โดยตรง ระบบจะเพิ่มหรืออัปเดตฟิลด์โดยการเรียกใช้ *-update-api หรือ *-freeze-api นอกจากนี้ ระบบจะย้ายข้อมูลversionsโดยอัตโนมัติไปยัง versions_with_info เมื่อผู้ใช้เรียกใช้ *-update-api หรือ *-freeze-api
  • stability: แฟล็กที่ไม่บังคับสำหรับสัญญาความเสถียรของอินเทอร์เฟซนี้ โดยรองรับเฉพาะ "vintf" หากไม่ได้ตั้งค่า stability ไว้ ระบบบิลด์จะตรวจสอบว่าอินเทอร์เฟซเข้ากันได้แบบย้อนหลังหรือไม่ เว้นแต่จะระบุ unstable ไว้ การไม่ตั้งค่าจะสอดคล้องกับอินเทอร์เฟซที่มี ความเสถียรภายในบริบทการคอมไพล์นี้ (ดังนั้นไม่ว่าจะเป็นสิ่งต่างๆ ของระบบทั้งหมด เช่น สิ่งต่างๆ ใน system.img และพาร์ติชันที่เกี่ยวข้อง หรือสิ่งต่างๆ ของผู้ให้บริการทั้งหมด เช่น สิ่งต่างๆ ใน vendor.img และพาร์ติชันที่เกี่ยวข้อง) หากตั้งค่า stability เป็น "vintf" แสดงว่าเป็นการรับประกันความเสถียร อินเทอร์เฟซต้องมีความเสถียรตราบใดที่ยังมีการใช้งาน
  • gen_trace: แฟล็กที่ไม่บังคับเพื่อเปิดหรือปิดการติดตาม โดยเริ่มตั้งแต่ Android 14 ค่าเริ่มต้นคือ true สำหรับแบ็กเอนด์ cpp และ java
  • host_supported: แฟล็กที่ไม่บังคับซึ่งเมื่อตั้งค่าเป็น true จะทำให้ไลบรารีที่สร้างขึ้นพร้อมใช้งานในสภาพแวดล้อมโฮสต์
  • unstable: แฟล็กที่ไม่บังคับซึ่งใช้เพื่อทำเครื่องหมายว่าอินเทอร์เฟซนี้ไม่ จำเป็นต้องเสถียร เมื่อตั้งค่าเป็น true ระบบบิลด์จะไม่สร้างการดัมพ์ API สำหรับอินเทอร์เฟซและไม่จำเป็นต้องอัปเดต
  • frozen: ค่าสถานะที่ไม่บังคับซึ่งเมื่อตั้งค่าเป็น true หมายความว่าอินเทอร์เฟซ ไม่มีการเปลี่ยนแปลงใดๆ นับตั้งแต่เวอร์ชันก่อนหน้าของอินเทอร์เฟซ ซึ่งจะช่วยให้ ตรวจสอบได้มากขึ้นในเวลาบิลด์ เมื่อตั้งค่าเป็น false แสดงว่าอินเทอร์เฟซอยู่ระหว่างการพัฒนาและมีการเปลี่ยนแปลงใหม่ ดังนั้นการเรียกใช้ foo-freeze-api จะสร้างเวอร์ชันใหม่และเปลี่ยนค่าเป็น true โดยอัตโนมัติ เปิดตัวใน Android 14
  • backend.<type>.enabled: แฟล็กเหล่านี้จะสลับแต่ละแบ็กเอนด์ที่คอมไพเลอร์ AIDL สร้างโค้ดให้ ระบบรองรับแบ็กเอนด์ 4 รายการ ได้แก่ Java, C++, NDK และ Rust แบ็กเอนด์ Java, C++ และ NDK จะเปิดใช้ โดยค่าเริ่มต้น หากไม่จำเป็นต้องใช้แบ็กเอนด์ทั้ง 3 รายการนี้ คุณจะต้อง ปิดใช้แบ็กเอนด์อย่างชัดแจ้ง Rust จะปิดใช้โดยค่าเริ่มต้นจนกว่าจะถึง Android 15
  • backend.<type>.apex_available: รายชื่อชื่อ APEX ที่มีไลบรารี Stub ที่สร้างขึ้น
  • backend.[cpp|java].gen_log: แฟล็กที่ไม่บังคับซึ่งควบคุมว่าจะสร้างโค้ดเพิ่มเติมเพื่อรวบรวมข้อมูลเกี่ยวกับธุรกรรมหรือไม่
  • backend.[cpp|java].vndk.enabled: แฟล็กที่ไม่บังคับเพื่อทําให้อินเทอร์เฟซนี้ เป็นส่วนหนึ่งของ VNDK ค่าเริ่มต้นคือ false
  • backend.[cpp|ndk].additional_shared_libraries: เปิดตัวใน Android 14 แฟล็กนี้จะเพิ่มการอ้างอิงไปยัง ไลบรารีเนทีฟ แฟล็กนี้มีประโยชน์กับ ndk_header และ cpp_header
  • backend.java.sdk_version: แฟล็กที่ไม่บังคับสําหรับการระบุเวอร์ชัน ของ SDK ที่สร้างไลบรารี Stub ของ Java ค่าเริ่มต้นคือ "system_current" ไม่ควรตั้งค่านี้เมื่อ backend.java.platform_apis เป็น true
  • backend.java.platform_apis: แฟล็กที่ไม่บังคับซึ่งควรตั้งค่าเป็น true เมื่อไลบรารีที่สร้างขึ้นต้องสร้างเทียบกับ API ของแพลตฟอร์ม แทนที่จะเป็น SDK

ระบบจะสร้างไลบรารี Stub สำหรับชุดค่าผสมของเวอร์ชันและแบ็กเอนด์ที่เปิดใช้แต่ละชุด ดูวิธีอ้างอิงไลบรารี Stub เวอร์ชันที่เฉพาะเจาะจง สำหรับแบ็กเอนด์ที่เฉพาะเจาะจงได้ที่กฎการตั้งชื่อโมดูล

เขียนไฟล์ AIDL

อินเทอร์เฟซใน AIDL ที่เสถียรจะคล้ายกับอินเทอร์เฟซแบบเดิม ยกเว้นว่าจะไม่ได้รับอนุญาตให้ใช้ Parcelable ที่ไม่มีโครงสร้าง (เนื่องจากไม่เสถียร โปรดดูAIDL ที่มีโครงสร้างเทียบกับ AIDL ที่เสถียร) ความแตกต่างหลักใน 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 12 ยังรองรับค่าเริ่มต้นสำหรับการแจงนับที่ผู้ใช้กำหนดด้วย เมื่อไม่ได้ระบุค่าเริ่มต้น ระบบจะใช้ค่าที่คล้ายกับ 0 หรือค่าว่าง การแจงนับที่ไม่มีค่าเริ่มต้นจะเริ่มต้นเป็น 0 แม้ว่าจะไม่มีตัวแจงนับเป็น 0 ก็ตาม

ใช้ไลบรารี Stub

หลังจากเพิ่มไลบรารี Stub เป็นทรัพยากร Dependency ในโมดูลแล้ว คุณ จะรวมไลบรารีเหล่านั้นไว้ในไฟล์ได้ ตัวอย่างไลบรารี Stub ใน ระบบบิลด์ (Android.mk ใช้กับการกําหนดโมดูลเดิมได้ด้วย) โปรดทราบว่าในตัวอย่างเหล่านี้ไม่มีเวอร์ชัน จึงแสดงถึงการใช้อินเทอร์เฟซที่ไม่เสถียร แต่ชื่อของอินเทอร์เฟซที่มีเวอร์ชันจะมีข้อมูลเพิ่มเติม โปรดดูการกำหนดเวอร์ชันของอินเทอร์เฟซ

cc_... {
    name: ...,
    // use `shared_libs:` to load your library and its transitive dependencies
    // dynamically
    shared_libs: ["my-module-name-cpp"],
    // use `static_libs:` to include the library in this binary and drop
    // transitive dependencies
    static_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // use `static_libs:` to add all jars and classes to this jar
    static_libs: ["my-module-name-java"],
    // use `libs:` to make these classes available during build time, but
    // not add them to the jar, in case the classes are already present on the
    // boot classpath (such as if it's in framework.jar) or another jar.
    libs: ["my-module-name-java"],
    // use `srcs:` with `-java-sources` if you want to add classes in this
    // library jar directly, but you get transitive dependencies from
    // somewhere else, such as the boot classpath or another jar.
    srcs: ["my-module-name-java-source", ...],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

ตัวอย่างใน C++

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

ตัวอย่างใน Java

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 จะเพิ่มคำจำกัดความ API ใหม่ภายใต้ api_dir หรือ aidl_api/name ขึ้นอยู่กับเวอร์ชัน Android และ เพิ่มไฟล์ .hash ซึ่งทั้ง 2 อย่างแสดงถึงเวอร์ชันที่เพิ่งตรึงของ อินเทอร์เฟซ foo-freeze-api ยังอัปเดตพร็อพเพอร์ตี้ versions_with_info เพื่อแสดงเวอร์ชันเพิ่มเติมและ imports สำหรับเวอร์ชันด้วย โดยพื้นฐานแล้ว imports ใน versions_with_info จะคัดลอกจากฟิลด์ imports แต่มีการระบุเวอร์ชันเสถียรล่าสุดใน imports ใน versions_with_info สำหรับการนำเข้า ซึ่งไม่มีเวอร์ชันที่ชัดเจน หลังจากระบุพร็อพเพอร์ตี้ versions_with_info แล้ว ระบบบิลด์จะเรียกใช้การตรวจสอบความเข้ากันได้ระหว่างเวอร์ชันที่หยุดการอัปเดต และระหว่าง Top of Tree (ToT) กับเวอร์ชันที่หยุดการอัปเดตล่าสุด

นอกจากนี้ คุณยังต้องจัดการคำจำกัดความ API ของเวอร์ชัน ToT ด้วย เมื่อใดก็ตามที่มีการอัปเดต API ให้เรียกใช้ foo-update-api เพื่ออัปเดต aidl_api/name/current ซึ่งมีคำจำกัดความ API ของเวอร์ชัน ToT

เจ้าของสามารถเพิ่มรายการใหม่ต่อไปนี้เพื่อรักษาความเสถียรของอินเทอร์เฟซ

  • วิธีการที่ส่วนท้ายของอินเทอร์เฟซ (หรือวิธีการที่มีการกำหนดซีเรียลใหม่ อย่างชัดเจน)
  • องค์ประกอบที่ท้ายของ Parcelable (ต้องเพิ่มค่าเริ่มต้นสำหรับแต่ละองค์ประกอบ)
  • ค่าคงที่
  • ใน Android 11 ตัวแจงนับ
  • ใน Android 12 ฟิลด์ที่ส่วนท้ายของสหภาพ

ไม่อนุญาตให้ดำเนินการอื่นๆ และไม่มีใครแก้ไขอินเทอร์เฟซได้ (มิฉะนั้นอาจเกิดการทับซ้อนกับการเปลี่ยนแปลงที่เจ้าของทำ)

หากต้องการทดสอบว่าอินเทอร์เฟซทั้งหมดหยุดการเปลี่ยนแปลงเพื่อการเผยแพร่แล้ว ให้สร้างโดยตั้งค่าตัวแปรสภาพแวดล้อมต่อไปนี้

  • AIDL_FROZEN_REL=true m ... - บิลด์กำหนดให้อินเทอร์เฟซ AIDL ที่เสถียรทั้งหมดต้อง ได้รับการตรึงซึ่งไม่มีการระบุฟิลด์ owner:
  • AIDL_FROZEN_OWNERS="aosp test" - build กำหนดให้ต้องตรึงอินเทอร์เฟซ AIDL ที่เสถียรทั้งหมด โดยระบุฟิลด์ owner: เป็น "aosp" หรือ "test"

ความเสถียรของการนำเข้า

การอัปเดตเวอร์ชันของการนำเข้าสำหรับเวอร์ชันที่ตรึงของอินเทอร์เฟซจะ เข้ากันได้แบบย้อนหลังที่เลเยอร์ AIDL ที่เสถียร อย่างไรก็ตาม การอัปเดตเหล่านี้ต้อง อัปเดตเซิร์ฟเวอร์และไคลเอ็นต์ทั้งหมดที่ใช้อินเทอร์เฟซเวอร์ชันก่อนหน้า และแอปบางแอปอาจสับสนเมื่อใช้เวอร์ชันประเภทต่างๆ ร่วมกัน โดยทั่วไปแล้ว สำหรับแพ็กเกจประเภทเท่านั้นหรือแพ็กเกจทั่วไป การดำเนินการนี้จะปลอดภัยเนื่องจากต้องเขียนโค้ดเพื่อจัดการประเภทที่ไม่รู้จักจากธุรกรรม IPC อยู่แล้ว

ในโค้ดแพลตฟอร์ม Android android.hardware.graphics.common เป็นตัวอย่างที่ใหญ่ที่สุด ของการอัปเกรดเวอร์ชันประเภทนี้

ใช้อินเทอร์เฟซที่มีการควบคุมเวอร์ชัน

วิธีการเชื่อมต่อ

เมื่อรันไทม์ ขณะพยายามเรียกใช้เมธอดใหม่ในเซิร์ฟเวอร์เก่า ไคลเอ็นต์ใหม่จะได้รับข้อผิดพลาดหรือข้อยกเว้น ทั้งนี้ขึ้นอยู่กับแบ็กเอนด์

  • cppแบ็กเอนด์จะได้รับ ::android::UNKNOWN_TRANSACTION
  • ndkแบ็กเอนด์จะได้รับ STATUS_UNKNOWN_TRANSACTION
  • java แบ็กเอนด์จะได้รับ android.os.RemoteException พร้อมข้อความที่ระบุว่าไม่ได้ใช้ API

ดูกลยุทธ์ในการจัดการปัญหานี้ได้ที่หัวข้อ การค้นหาเวอร์ชันและ การใช้ค่าเริ่มต้น

Parcelables

เมื่อมีการเพิ่มฟิลด์ใหม่ลงใน Parcelable ไคลเอ็นต์และเซิร์ฟเวอร์เก่าจะทิ้งฟิลด์เหล่านั้น เมื่อไคลเอ็นต์และเซิร์ฟเวอร์ใหม่ได้รับ Parcelable เก่า ระบบจะป้อนค่าเริ่มต้นสำหรับฟิลด์ใหม่โดยอัตโนมัติ ซึ่งหมายความว่าต้อง ระบุค่าเริ่มต้นสำหรับฟิลด์ใหม่ทั้งหมดใน Parcelable

ไคลเอ็นต์ไม่ควรคาดหวังให้เซิร์ฟเวอร์ใช้ฟิลด์ใหม่ เว้นแต่จะทราบว่าเซิร์ฟเวอร์ใช้เวอร์ชันที่มีการกำหนดฟิลด์ (ดูการค้นหาเวอร์ชัน)

Enum และค่าคงที่

ในทำนองเดียวกัน ไคลเอ็นต์และเซิร์ฟเวอร์ควรปฏิเสธหรือเพิกเฉยต่อค่าคงที่และตัวแจงนับที่ไม่รู้จักตามความเหมาะสม เนื่องจากอาจมีการเพิ่มค่าดังกล่าวในอนาคต เช่น เซิร์ฟเวอร์ไม่ควรยกเลิกเมื่อได้รับ ตัวแจงนับที่ไม่รู้จัก เซิร์ฟเวอร์ควรไม่สนใจ ตัวแจงนับ หรือส่งคืนบางอย่างเพื่อให้ไคลเอ็นต์ทราบว่าการใช้งานนี้ไม่รองรับ

สหภาพ

การพยายามส่งสหภาพที่มีฟิลด์ใหม่จะล้มเหลวหากผู้รับเป็นเวอร์ชันเก่าและ ไม่รู้จักฟิลด์ดังกล่าว การใช้งานจะไม่เห็นการรวมกับฟิลด์ใหม่ ระบบจะละเว้นความล้มเหลวหากเป็นธุรกรรมทางเดียว มิฉะนั้นข้อผิดพลาดจะเป็น BAD_VALUE(สำหรับแบ็กเอนด์ C++ หรือ NDK) หรือ IllegalArgumentException(สำหรับแบ็กเอนด์ Java) ข้อผิดพลาดจะเกิดขึ้นหากไคลเอ็นต์ส่งชุดยูเนียนไปยังฟิลด์ใหม่ในเซิร์ฟเวอร์เก่า หรือเมื่อไคลเอ็นต์เก่าได้รับยูเนียนจากเซิร์ฟเวอร์ใหม่

จัดการหลายเวอร์ชัน

เนมสเปซของ Linker ใน Android มีได้เฉพาะ aidl อินเทอร์เฟซเวอร์ชันเดียวเพื่อหลีกเลี่ยงสถานการณ์ที่ประเภท aidl ที่สร้างขึ้นมีคำจำกัดความหลายรายการ C++ มีกฎการกำหนด 1 รายการที่กำหนดให้มีคำจำกัดความเพียง 1 รายการ ของแต่ละสัญลักษณ์

การบิลด์ Android จะแสดงข้อผิดพลาดเมื่อโมดูลขึ้นอยู่กับaidl_interfaceไลบรารีaidl_interfaceเดียวกันแต่เป็นเวอร์ชันที่แตกต่างกัน โมดูลอาจขึ้นต่อกันกับไลบรารีเหล่านี้โดยตรงหรือโดยอ้อมผ่านทรัพยากร Dependency ของทรัพยากร Dependency ข้อผิดพลาดเหล่านี้แสดงกราฟการอ้างอิงจากโมดูลที่ไม่สำเร็จไปยังไลบรารี aidl_interface เวอร์ชันที่ขัดแย้งกัน คุณต้องอัปเดตการอ้างอิงทั้งหมดให้มีไลบรารีเหล่านี้ในเวอร์ชันเดียวกัน (โดยปกติจะเป็นเวอร์ชันล่าสุด)

หากโมดูลต่างๆ จำนวนมากใช้ไลบรารีอินเทอร์เฟซ การสร้าง cc_defaults, java_defaults และ rust_defaults สำหรับกลุ่มไลบรารีและกระบวนการที่ต้องใช้เวอร์ชันเดียวกันอาจเป็นประโยชน์ เมื่อเปิดตัวอินเทอร์เฟซเวอร์ชันใหม่ ค่าเริ่มต้นเหล่านั้นจะได้รับการอัปเดต และโมดูลทั้งหมดที่ใช้ค่าเริ่มต้นจะได้รับการอัปเดตพร้อมกัน เพื่อให้มั่นใจว่าโมดูลไม่ได้ใช้อินเทอร์เฟซเวอร์ชันอื่น

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

เมื่อaidl_interfaceโมดูลนำเข้าโมดูลaidl_interfaceอื่นๆ จะทำให้เกิดการอ้างอิงเพิ่มเติมที่ต้องใช้เวอร์ชันที่เฉพาะเจาะจงร่วมกัน aidl_interface สถานการณ์นี้อาจจัดการได้ยากเมื่อมีaidl_interface โมดูลทั่วไปที่นำเข้าในaidl_interfaceโมดูลหลายรายการที่ใช้ ร่วมกันในกระบวนการเดียวกัน

aidl_interfaces_defaults สามารถใช้เพื่อเก็บคำจำกัดความเดียวของ การอ้างอิงเวอร์ชันล่าสุดสำหรับ aidl_interface ที่อัปเดตได้ใน ที่เดียว และใช้โดยโมดูล aidl_interface ทั้งหมดที่ต้องการนำเข้า อินเทอร์เฟซทั่วไปนั้น

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

การพัฒนาที่อิงตามฟีเจอร์แฟลก

คุณไม่สามารถใช้อินเทอร์เฟซที่อยู่ระหว่างการพัฒนา (ไม่คงที่) ในอุปกรณ์ที่เผยแพร่แล้ว เนื่องจาก เราไม่รับประกันความเข้ากันได้แบบย้อนหลัง

AIDL รองรับการเปลี่ยนกลับรันไทม์สำหรับไลบรารีอินเทอร์เฟซที่ไม่ได้ตรึงเหล่านี้ เพื่อให้สามารถเขียนโค้ดกับเวอร์ชันล่าสุดที่ไม่ได้ตรึงและยังคงใช้ในอุปกรณ์ที่เผยแพร่ได้ ลักษณะการทำงานที่เข้ากันได้แบบย้อนหลังของไคลเอ็นต์จะคล้ายกับลักษณะการทำงานที่มีอยู่ และการติดตั้งใช้งานก็ต้องเป็นไปตามลักษณะการทำงานเหล่านั้นด้วย ดูหัวข้อ ใช้อินเทอร์เฟซที่มีการควบคุมเวอร์ชัน

แฟล็กการสร้าง AIDL

Flag ที่ควบคุมลักษณะการทำงานนี้คือ RELEASE_AIDL_USE_UNFROZEN ซึ่งกำหนดไว้ใน build/release/build_flags.bzl true หมายถึงใช้เวอร์ชันที่ไม่ได้ตรึงของอินเทอร์เฟซในรันไทม์ และ false หมายถึงไลบรารีของเวอร์ชันที่ไม่ได้ตรึงทั้งหมดจะทำงานเหมือนเวอร์ชันที่ตรึงล่าสุด คุณสามารถลบล้าง Flag เพื่อtrueสำหรับ การพัฒนาในเครื่องได้ แต่ต้องเปลี่ยนกลับเป็น false ก่อนเผยแพร่ โดยปกติแล้ว การพัฒนาจะทำด้วยการกำหนดค่าที่มีการตั้งค่าสถานะเป็น true

เมทริกซ์ความเข้ากันได้และไฟล์ Manifest

ออบเจ็กต์อินเทอร์เฟซของผู้ให้บริการ (ออบเจ็กต์ VINTF) จะกำหนด เวอร์ชันที่คาดไว้และเวอร์ชันที่ระบุในทั้ง 2 ด้านของ อินเทอร์เฟซของผู้ให้บริการ

อุปกรณ์ส่วนใหญ่ที่ไม่ใช่ Cuttlefish จะกำหนดเป้าหมายเป็นเมทริกซ์ความเข้ากันได้ล่าสุด หลังจากที่อินเทอร์เฟซได้รับการตรึงแล้วเท่านั้น ดังนั้นจึงไม่มีความแตกต่างในไลบรารี AIDL ตาม RELEASE_AIDL_USE_UNFROZEN

เมทริกซ์

ระบบจะเพิ่มอินเทอร์เฟซที่พาร์ทเนอร์เป็นเจ้าของลงในเมทริกซ์ความเข้ากันได้เฉพาะอุปกรณ์หรือเฉพาะผลิตภัณฑ์ ที่อุปกรณ์กำหนดเป้าหมายไว้ในระหว่างการพัฒนา ดังนั้นเมื่อมีการเพิ่มอินเทอร์เฟซเวอร์ชันใหม่ที่ไม่ได้ตรึงไว้ลงในเมทริกซ์ความเข้ากันได้ เวอร์ชันก่อนหน้าที่ตรึงไว้จะต้องยังคงอยู่สำหรับ RELEASE_AIDL_USE_UNFROZEN=false คุณจัดการปัญหานี้ได้โดยใช้ไฟล์เมทริกซ์ความเข้ากันได้ที่แตกต่างกันสำหรับการกำหนดค่า RELEASE_AIDL_USE_UNFROZEN ที่แตกต่างกัน หรืออนุญาตทั้ง 2 เวอร์ชันในไฟล์เมทริกซ์ความเข้ากันได้ไฟล์เดียวที่ใช้ในการกำหนดค่าทั้งหมด

เช่น เมื่อเพิ่มเวอร์ชัน 4 ที่ไม่ได้หยุด ให้ใช้ <version>3-4</version>

เมื่อตรึงเวอร์ชัน 4 แล้ว คุณจะนำเวอร์ชัน 3 ออกจากเมทริกซ์ความเข้ากันได้ได้ เนื่องจากระบบจะใช้เวอร์ชัน 4 ที่ตรึงไว้เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false

Manifest

ใน Android 15 มีการเปลี่ยนแปลงใน libvintf เพื่อ แก้ไขไฟล์ Manifest ในเวลาบิลด์ตามค่าของ RELEASE_AIDL_USE_UNFROZEN

ไฟล์ Manifest และส่วนของไฟล์ Manifest จะประกาศว่าบริการใช้เวอร์ชันใดของอินเทอร์เฟซ เมื่อใช้เวอร์ชันล่าสุดที่ไม่ได้หยุดการอัปเดตของอินเทอร์เฟซ คุณต้องอัปเดตไฟล์ Manifest เพื่อให้สอดคล้องกับเวอร์ชันใหม่นี้ เมื่อ RELEASE_AIDL_USE_UNFROZEN=false ปรับรายการในไฟล์ Manifest โดย libvintf เพื่อให้สอดคล้องกับการเปลี่ยนแปลงในไลบรารี AIDL ที่สร้างขึ้น เวอร์ชัน ได้รับการแก้ไขจากเวอร์ชันที่อัปเดตได้ N เป็น เวอร์ชันที่อัปเดตไม่ได้ล่าสุด N - 1 ดังนั้น ผู้ใช้จึงไม่จำเป็นต้องจัดการไฟล์ Manifest หรือส่วนของไฟล์ Manifest หลายรายการสำหรับแต่ละบริการ

การเปลี่ยนแปลงไคลเอ็นต์ HAL

โค้ดไคลเอ็นต์ HAL ต้องเข้ากันได้แบบย้อนหลังกับเวอร์ชันที่รองรับก่อนหน้าแต่ละเวอร์ชันที่หยุดการอัปเดตแล้ว เมื่อ RELEASE_AIDL_USE_UNFROZEN false บริการจะดูเหมือนเวอร์ชันที่หยุดการอัปเดตล่าสุดหรือเวอร์ชันก่อนหน้าเสมอ (เช่น การเรียกใช้เมธอดใหม่ที่ไม่ได้หยุดการอัปเดตจะแสดงผลเป็น UNKNOWN_TRANSACTION หรือฟิลด์ parcelable ใหม่จะมีค่าเริ่มต้น) ไคลเอ็นต์เฟรมเวิร์ก Android ต้องเข้ากันได้กับเวอร์ชันก่อนหน้าเพิ่มเติม แต่รายละเอียดนี้เป็นรายละเอียดใหม่สำหรับไคลเอ็นต์ของผู้ให้บริการและไคลเอ็นต์ของอินเทอร์เฟซที่พาร์ทเนอร์เป็นเจ้าของ

การเปลี่ยนแปลงการใช้งาน HAL

ความแตกต่างที่ใหญ่ที่สุดในการพัฒนา HAL ด้วยการพัฒนาที่อิงตาม Flag คือ ข้อกำหนดสำหรับการใช้งาน HAL ให้เข้ากันได้แบบย้อนหลังกับเวอร์ชันล่าสุด ที่หยุดการเปลี่ยนแปลงเพื่อทำงานเมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false การพิจารณาความเข้ากันได้แบบย้อนหลังในการติดตั้งใช้งานและรหัสอุปกรณ์เป็น แบบฝึกหัดใหม่ ดูใช้อินเทอร์เฟซที่มีการกำหนดเวอร์ชัน

โดยทั่วไปแล้ว ข้อควรพิจารณาเกี่ยวกับการทำงานร่วมกันกับเวอร์ชันก่อนหน้าจะเหมือนกันสำหรับไคลเอ็นต์และเซิร์ฟเวอร์ รวมถึงสำหรับโค้ดเฟรมเวิร์กและโค้ดของผู้ให้บริการ แต่ก็มีความแตกต่างเล็กน้อยที่คุณต้องทราบ เนื่องจากตอนนี้คุณกำลังใช้ 2 เวอร์ชันที่ใช้ซอร์สโค้ดเดียวกัน (เวอร์ชันปัจจุบันที่ยังไม่หยุดการอัปเดต)

ตัวอย่าง: อินเทอร์เฟซมีเวอร์ชันที่หยุดการเปลี่ยนแปลง 3 เวอร์ชัน อินเทอร์เฟซได้รับการอัปเดตด้วย วิธีการใหม่ ทั้งไคลเอ็นต์และบริการได้รับการอัปเดตให้ใช้ไลบรารีเวอร์ชัน 4 ใหม่ เนื่องจากไลบรารี V4 อิงตามอินเทอร์เฟซเวอร์ชันที่ไม่ได้ตรึงไว้ จึงทํางานเหมือนเวอร์ชันที่ตรึงไว้ล่าสุด ซึ่งก็คือเวอร์ชัน 3 เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false และป้องกันการใช้วิธีการใหม่

เมื่ออินเทอร์เฟซหยุดทำงาน ค่าทั้งหมดของ RELEASE_AIDL_USE_UNFROZEN จะใช้เวอร์ชันที่หยุดทำงานนั้น และสามารถนำโค้ดที่จัดการความเข้ากันได้แบบย้อนหลังออกได้

เมื่อเรียกใช้เมธอดใน Callback คุณต้องจัดการกรณีที่ระบบแสดงผล UNKNOWN_TRANSACTION อย่างเหมาะสม ไคลเอ็นต์อาจใช้การเรียกกลับ 2 เวอร์ชันที่แตกต่างกัน โดยอิงตามการกำหนดค่าการเผยแพร่ ดังนั้นคุณจึงไม่สามารถ ถือว่าไคลเอ็นต์ส่งเวอร์ชันล่าสุด และเมธอดใหม่อาจแสดงผล ค่านี้ ซึ่งคล้ายกับวิธีที่ไคลเอ็นต์ 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();
  }
}

ฟิลด์ใหม่ในประเภทที่มีอยู่ (parcelable, enum, union) อาจไม่มีอยู่หรือมีค่าเริ่มต้นเมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false และระบบจะทิ้งค่าของฟิลด์ใหม่ที่บริการพยายามส่งออกนอกกระบวนการ

คุณจะส่งหรือรับประเภทใหม่ที่เพิ่มในเวอร์ชันที่เลิกตรึงนี้ผ่านอินเทอร์เฟซไม่ได้

การติดตั้งใช้งานจะไม่ได้รับการเรียกใช้เมธอดใหม่จากไคลเอ็นต์ใดๆ เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false

โปรดระมัดระวังในการใช้ตัวแจงนับใหม่กับเวอร์ชันที่เปิดตัวเท่านั้น และไม่ใช่เวอร์ชันก่อนหน้า

โดยปกติแล้ว คุณจะใช้ foo->getInterfaceVersion() เพื่อดูว่าอินเทอร์เฟซระยะไกลใช้เวอร์ชันใด แต่การรองรับการกำหนดเวอร์ชันตาม Flag จะทำให้คุณ ใช้ 2 เวอร์ชันที่แตกต่างกัน ดังนั้นคุณอาจต้องดูเวอร์ชันของ อินเทอร์เฟซปัจจุบัน คุณทำได้โดยรับเวอร์ชันอินเทอร์เฟซของ ออบเจ็กต์ปัจจุบัน เช่น this->getInterfaceVersion() หรือวิธีอื่นๆ สำหรับ my_ver ดูข้อมูลเพิ่มเติมได้ที่การค้นหาเวอร์ชันอินเทอร์เฟซของออบเจ็กต์ระยะไกล

อินเทอร์เฟซที่เสถียรของ VINTF ใหม่

เมื่อมีการเพิ่มแพ็กเกจอินเทอร์เฟซ AIDL ใหม่ จะไม่มีเวอร์ชันที่หยุดการเปลี่ยนแปลงล่าสุด ดังนั้น จึงไม่มีลักษณะการทำงานที่จะใช้เป็นข้อมูลสำรองเมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false อย่าใช้อินเทอร์เฟซเหล่านี้ เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false Service Manager จะไม่อนุญาตให้ลงทะเบียนอินเทอร์เฟซ และไคลเอ็นต์จะไม่พบอินเทอร์เฟซดังกล่าว

คุณเพิ่มบริการแบบมีเงื่อนไขได้โดยอิงตามค่าของ RELEASE_AIDL_USE_UNFROZEN แฟล็กในไฟล์ Makefile ของอุปกรณ์

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

หากบริการเป็นส่วนหนึ่งของกระบวนการที่ใหญ่กว่านั้น คุณจึงเพิ่มบริการลงในอุปกรณ์แบบมีเงื่อนไขไม่ได้ ให้ตรวจสอบว่ามีการประกาศบริการด้วย IServiceManager::isDeclared()หรือไม่ หากมีการประกาศและลงทะเบียนไม่สำเร็จ ให้ ยกเลิกกระบวนการ หากไม่ได้ประกาศไว้ การลงทะเบียนก็อาจไม่สำเร็จ

อินเทอร์เฟซส่วนขยายที่เสถียรของ VINTF ใหม่

อินเทอร์เฟซส่วนขยายใหม่ ไม่มีเวอร์ชันก่อนหน้าให้ย้อนกลับ และเนื่องจากไม่ได้ ลงทะเบียนกับ ServiceManager หรือประกาศในไฟล์ Manifest ของ VINTF จึงใช้ IServiceManager::isDeclared() เพื่อกำหนดเวลาที่จะแนบ อินเทอร์เฟซส่วนขยายกับอินเทอร์เฟซอื่นไม่ได้

ตัวแปร RELEASE_AIDL_USE_UNFROZEN สามารถใช้เพื่อพิจารณาว่าจะ แนบอินเทอร์เฟซส่วนขยายใหม่ที่เลิกตรึงกับอินเทอร์เฟซที่มีอยู่หรือไม่ เพื่อหลีกเลี่ยง การใช้อินเทอร์เฟซดังกล่าวในอุปกรณ์ที่เผยแพร่ ต้องหยุดการอัปเดตอินเทอร์เฟซเพื่อใช้ในอุปกรณ์ที่เปิดตัวแล้ว

การทดสอบ vts_treble_vintf_vendor_test และ vts_treble_vintf_framework_test VTS จะตรวจหาเมื่อมีการใช้อินเทอร์เฟซส่วนขยายที่ไม่ได้ตรึงในอุปกรณ์ที่เผยแพร่แล้ว และแสดงข้อผิดพลาด

หากอินเทอร์เฟซส่วนขยายไม่ใช่เวอร์ชันใหม่และมีเวอร์ชันที่หยุดไว้ก่อนหน้านี้ ระบบจะเปลี่ยนกลับไปใช้เวอร์ชันที่หยุดไว้ก่อนหน้านี้และไม่ต้องทำตามขั้นตอนเพิ่มเติม

Cuttlefish เป็นเครื่องมือพัฒนา

ทุกปีหลังจากที่ VINTF หยุดการเปลี่ยนแปลง เราจะปรับเมทริกซ์ความเข้ากันได้ของเฟรมเวิร์ก (FCM) target-level และPRODUCT_SHIPPING_API_LEVEL ของ Cuttlefish เพื่อให้สอดคล้องกับอุปกรณ์ที่เปิดตัวพร้อมกับรุ่นของปีถัดไป เราปรับ target-levelและPRODUCT_SHIPPING_API_LEVELเพื่อให้มั่นใจว่ามี อุปกรณ์ที่เปิดตัวซึ่งผ่านการทดสอบและเป็นไปตามข้อกำหนดใหม่สำหรับการเปิดตัวในปีหน้า

เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น true ระบบจะใช้ Cuttlefish ในการพัฒนา Android เวอร์ชันในอนาคต โดยมีเป้าหมายเป็นระดับ FCM ของ Android รุ่นที่จะเปิดตัวในปีหน้าและ PRODUCT_SHIPPING_API_LEVEL ซึ่งกำหนดให้เป็นไปตาม ข้อกำหนดด้านซอฟต์แวร์ของผู้ให้บริการ (VSR) ของรุ่นถัดไป

เมื่อ RELEASE_AIDL_USE_UNFROZEN เป็น false Cuttlefish จะมี target-level และ PRODUCT_SHIPPING_API_LEVEL ก่อนหน้าเพื่อแสดงอุปกรณ์รุ่นที่เผยแพร่ ใน Android 14 และเวอร์ชันที่ต่ำกว่า การแยกความแตกต่างนี้จะทำได้ด้วยสาขา Git ที่แตกต่างกันซึ่งจะไม่รับการเปลี่ยนแปลงใน FCM target-level, ระดับ API การจัดส่ง หรือโค้ดอื่นๆ ที่กำหนดเป้าหมายเป็นการเผยแพร่ครั้งถัดไป

กฎการตั้งชื่อโมดูล

ใน Android 11 ระบบจะสร้างโมดูลไลบรารี Stub โดยอัตโนมัติสำหรับแต่ละชุดค่าผสมของเวอร์ชันและ แบ็กเอนด์ที่เปิดใช้ หากต้องการอ้างอิงโมดูลไลบรารี Stub ที่เฉพาะเจาะจงสำหรับการลิงก์ อย่าใช้ชื่อโมดูล aidl_interface แต่ให้ใช้ชื่อโมดูลไลบรารี Stub ซึ่งคือ ifacename-version-backend โดยที่

  • ifacename: ชื่อของโมดูล aidl_interface
  • version เป็นอย่างใดอย่างหนึ่งต่อไปนี้
    • Vversion-number สำหรับเวอร์ชันที่อัปเดตไม่ได้
    • Vlatest-frozen-version-number + 1 สำหรับเวอร์ชัน tip-of-tree (ยังไม่ได้อัปเดต)
  • backend เป็นอย่างใดอย่างหนึ่งต่อไปนี้
    • java สำหรับแบ็กเอนด์ Java
    • cpp สำหรับแบ็กเอนด์ C++
    • ndk หรือ ndk_platform สำหรับแบ็กเอนด์ NDK โดยรายการแรกมีไว้สำหรับแอป และรายการที่สองมีไว้สำหรับการใช้งานแพลตฟอร์มจนถึง Android 13 ใน Android 13 ขึ้นไป ให้ใช้เฉพาะ ndk
    • 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)

เมื่อเทียบกับ Android 11

  • foo-backend ซึ่งอ้างอิงถึงเวอร์ชันเสถียรล่าสุด จะกลายเป็น foo-V2-backend
  • foo-unstable-backend ซึ่งอ้างอิงถึง ToT version จะกลายเป็น 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() ดังนี้ (ใช้ super แทน IFoo เพื่อหลีกเลี่ยง ข้อผิดพลาดในการคัดลอกและวาง คุณอาจต้องใช้คำอธิบายประกอบ @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 ได้ ระบบจะเรียกใช้เมธอดในการติดตั้งใช้งานเริ่มต้น ก็ต่อเมื่อไม่ได้ติดตั้งใช้งานเมธอดในฝั่งระยะไกล (เนื่องจากสร้างขึ้นด้วยอินเทอร์เฟซเวอร์ชันเก่ากว่า) เนื่องจากค่าเริ่มต้นได้รับการตั้งค่าทั่วโลก จึงไม่ควรใช้จากบริบทที่อาจมีการแชร์

ตัวอย่างใน C++ ใน Android 13 ขึ้นไป

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

ตัวอย่างใน Java

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 อยู่แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อแปลงอินเทอร์เฟซเป็นอินเทอร์เฟซ AIDL ที่เสถียร

  1. ระบุทรัพยากร Dependency ทั้งหมดของอินเทอร์เฟซ สำหรับทุกแพ็กเกจที่อินเทอร์เฟซขึ้นอยู่กับ ให้พิจารณาว่าแพ็กเกจนั้นกำหนดไว้ใน AIDL ที่เสถียรหรือไม่ หากไม่ได้กำหนดไว้ คุณต้องแปลงแพ็กเกจ

  2. แปลง Parcelable ทั้งหมดในอินเทอร์เฟซเป็น Parcelable ที่เสถียร (ไฟล์อินเทอร์เฟซเองอาจยังคงเดิม) โดยทำได้ด้วยการ แสดงโครงสร้างโดยตรงในไฟล์ AIDL ต้องเขียนคลาสการจัดการใหม่เพื่อใช้ประเภทใหม่เหล่านี้ คุณทำขั้นตอนนี้ได้ก่อนที่จะสร้างaidl_interfaceแพ็กเกจ (ด้านล่าง)

  3. สร้างaidl_interfaceแพ็กเกจ (ตามที่อธิบายไว้ข้างต้น) ที่มี ชื่อของโมดูล การอ้างอิง และข้อมูลอื่นๆ ที่คุณต้องการ นอกจากนี้ยังต้องมีการควบคุมเวอร์ชันด้วยเพื่อให้มีความเสถียร (ไม่ใช่แค่มีโครงสร้าง) ดูข้อมูลเพิ่มเติมได้ที่การกำหนดเวอร์ชันของอินเทอร์เฟซ