การใช้ Binder IPC

หน้านี้อธิบายการเปลี่ยนแปลงของไดรเวอร์ Binder ใน Android 8 ให้รายละเอียดเกี่ยวกับการใช้ Binder IPC และแสดงรายการนโยบาย SELinux ที่จำเป็น

การเปลี่ยนแปลงไดรเวอร์เครื่องผูก

ตั้งแต่ Android 8 เป็นต้นไป เฟรมเวิร์กของ Android และ HAL จะสื่อสารกันโดยใช้เครื่องผูก เนื่องจากการสื่อสารนี้เพิ่มการรับส่งข้อมูลของ Binder อย่างมาก Android 8 จึงรวมการปรับปรุงหลายอย่างที่ออกแบบมาเพื่อรักษา Binder IPC ให้รวดเร็ว ผู้จำหน่าย SoC และ OEM ควรรวมโดยตรงจากสาขาที่เกี่ยวข้องของ android-4.4, android-4.9 และสูงกว่าของ เคอร์เนล/โปรเจ็กต์ทั่วไป

โดเมนเครื่องผูกหลายรายการ (บริบท)

Common-4.4 และสูงกว่า รวมทั้งต้นน้ำ

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

กระจาย-รวบรวม

Common-4.4 และสูงกว่า รวมทั้งต้นน้ำ

ใน Android รุ่นก่อนหน้า ข้อมูลทุกชิ้นในการเรียก Binder จะถูกคัดลอกสามครั้ง:

  • ครั้งเดียวเพื่อซีเรียลไลซ์ให้เป็น Parcel ในกระบวนการโทร
  • เมื่ออยู่ในไดรเวอร์เคอร์เนลเพื่อคัดลอก Parcel ไปยังกระบวนการเป้าหมาย
  • ครั้งหนึ่งเพื่อ unserialize Parcel ในกระบวนการเป้าหมาย

Android 8 ใช้ การเพิ่มประสิทธิภาพ scatter-gather เพื่อลดจำนวนสำเนาจาก 3 เหลือ 1 แทนที่จะต้องซีเรียลไลซ์ข้อมูลใน Parcel ก่อน ข้อมูลยังคงอยู่ในโครงสร้างเดิมและเลย์เอาต์หน่วยความจำ และไดรเวอร์จะคัดลอกไปยังกระบวนการเป้าหมายทันที หลังจากที่ข้อมูลอยู่ในกระบวนการเป้าหมาย โครงสร้างและเค้าโครงหน่วยความจำจะเหมือนกัน และสามารถอ่านข้อมูลได้โดยไม่ต้องคัดลอกอีก

การล็อคแบบละเอียด

Common-4.4 และสูงกว่า รวมทั้งต้นน้ำ

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

ความพยายามครั้งแรกในการแก้ไขปัญหานี้เกี่ยวข้องกับการปิดใช้การขอสงวนไว้ก่อนในขณะที่ล็อกโกลบอลไว้ อย่างไรก็ตาม นี่เป็นการแฮ็กมากกว่าวิธีแก้ปัญหาที่แท้จริง และในที่สุดก็ถูกปฏิเสธโดยอัปสตรีมและยกเลิกไป ความพยายามครั้งต่อมามุ่งเน้นไปที่การทำให้การล็อกมีความละเอียดมากขึ้น ซึ่งเป็นเวอร์ชันที่ใช้งานบนอุปกรณ์ Pixel ตั้งแต่มกราคม 2017 แม้ว่าการเปลี่ยนแปลงส่วนใหญ่จะเผยแพร่สู่สาธารณะ แต่ก็มีการปรับปรุงที่สำคัญในเวอร์ชันต่อๆ ไป

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

การสืบทอดลำดับความสำคัญตามเวลาจริง

Common-4.4 และ Common-4.9 (อัปสตรีมเร็ว ๆ นี้)

ไดรเวอร์เครื่องผูกสนับสนุนการสืบทอดลำดับความสำคัญที่ดีเสมอ เนื่องจากกระบวนการจำนวนมากขึ้นใน Android ทำงานตามลำดับความสำคัญแบบเรียลไทม์ ในบางกรณีตอนนี้ก็สมเหตุสมผลแล้วหากเธรดแบบเรียลไทม์ทำการเรียก Binder เธรดในกระบวนการที่จัดการการเรียกนั้นก็จะทำงานตามลำดับความสำคัญตามเวลาจริง . เพื่อรองรับกรณีการใช้งานเหล่านี้ ตอนนี้ Android 8 ได้ปรับใช้การสืบทอดลำดับความสำคัญแบบเรียลไทม์ในไดรเวอร์ Binder

นอกเหนือจากการสืบทอดลำดับความสำคัญระดับธุรกรรมแล้ว การสืบทอด ลำดับความสำคัญของโหนด ยังอนุญาตให้โหนด (วัตถุบริการเครื่องผูก) ระบุลำดับความสำคัญขั้นต่ำที่ควรเรียกใช้งานในโหนดนี้ Android เวอร์ชันก่อนหน้ารองรับการสืบทอดลำดับความสำคัญของโหนดด้วยค่าที่ดีแล้ว แต่ Android 8 เพิ่มการรองรับการสืบทอดโหนดนโยบายการตั้งเวลาแบบเรียลไทม์

การเปลี่ยนแปลงพื้นที่ผู้ใช้

Android 8 มีการเปลี่ยนแปลงพื้นที่ผู้ใช้ทั้งหมดที่จำเป็นในการทำงานกับไดรเวอร์ Binder ปัจจุบันในเคอร์เนลทั่วไปโดยมีข้อยกเว้นประการหนึ่ง: การใช้งานดั้งเดิมเพื่อปิดใช้งานการสืบทอดลำดับความสำคัญแบบเรียลไทม์สำหรับ /dev/binder ใช้ ioctl การพัฒนาที่ตามมาได้เปลี่ยนการควบคุมของการสืบทอดลำดับความสำคัญเป็นวิธีการที่ละเอียดยิ่งขึ้นซึ่งเป็นไปตามโหมดสารยึดเกาะ (และไม่ใช่ตามบริบท) ดังนั้น ioctl ไม่ได้อยู่ในสาขาทั่วไปของ Android และถูก ส่งในเมล็ดทั่วไปของเรา แทน

ผลกระทบของการเปลี่ยนแปลงนี้คือ การสืบทอดลำดับความสำคัญตามเวลาจริงถูกปิดใช้งานตามค่าเริ่มต้นสำหรับ ทุก โหนด ทีมประสิทธิภาพของ Android พบว่าการเปิดใช้การสืบทอดลำดับความสำคัญแบบเรียลไทม์สำหรับโหนดทั้งหมดในโดเมน hwbinder นั้นมีประโยชน์ เพื่อให้ได้ผลลัพธ์แบบเดียวกัน เชอร์รี่เลือก การเปลี่ยนแปลงนี้ ในพื้นที่ผู้ใช้

SHA สำหรับเมล็ดพืชทั่วไป

หากต้องการรับการเปลี่ยนแปลงที่จำเป็นในไดรเวอร์ Binder ให้ซิงค์กับ SHA ที่เหมาะสม:

  • สามัญ-3.18
    cc8b90c121de ANDROID: เครื่องผูก: อย่าตรวจสอบสิทธิ์ก่อนในการกู้คืน
  • สามัญ-4.4
    76b376eac7a2 ANDROID: เครื่องผูก: อย่าตรวจสอบสิทธิ์ก่อนในการกู้คืน
  • สามัญ-4.9
    ecd972d4f9b5 ANDROID: Binder: อย่าตรวจสอบสิทธิ์ก่อนในการกู้คืน

การใช้สารยึดเกาะ IPC

ในอดีต กระบวนการของผู้ขายใช้ Binder Interprocess Communication (IPC) เพื่อสื่อสาร ใน Android 8 โหนดอุปกรณ์ /dev/binder จะกลายเป็นเอกสิทธิ์ของกระบวนการเฟรมเวิร์ก ซึ่งหมายความว่ากระบวนการของผู้ขายไม่สามารถเข้าถึงได้อีกต่อไป กระบวนการของผู้จำหน่ายสามารถเข้าถึง /dev/hwbinder ได้ แต่ต้องแปลงอินเทอร์เฟซ AIDL เพื่อใช้ HIDL สำหรับผู้ขายที่ต้องการใช้อินเทอร์เฟซ AIDL ต่อระหว่างกระบวนการของผู้ขาย Android รองรับ Binder IPC ตามที่อธิบายไว้ด้านล่าง

vndbinder

Android 8 รองรับโดเมน Binder ใหม่สำหรับการใช้งานโดยบริการของผู้ขาย เข้าถึงโดยใช้ /dev/vndbinder แทน /dev/binder ด้วยการเพิ่ม /dev/vndbinder ตอนนี้ Android มีโดเมน IPC สามโดเมนต่อไปนี้:

โดเมน IPC คำอธิบาย
/dev/binder IPC ระหว่างกระบวนการเฟรมเวิร์ก/แอปกับอินเทอร์เฟซ AIDL
/dev/hwbinder IPC ระหว่างเฟรมเวิร์ก/กระบวนการของผู้จำหน่ายกับอินเทอร์เฟซ HIDL
IPC ระหว่างกระบวนการของผู้ขายที่มีอินเทอร์เฟซ HIDL
/dev/vndbinder IPC ระหว่างกระบวนการของผู้ขาย/ผู้จำหน่ายด้วย AIDL Interfaces

เพื่อให้ /dev/vndbinder ปรากฏขึ้น ตรวจสอบให้แน่ใจว่ารายการการกำหนดค่าเคอร์เนล CONFIG_ANDROID_BINDER_DEVICES ถูกตั้งค่าเป็น "binder,hwbinder,vndbinder" (นี่เป็นค่าเริ่มต้นในแผนผังเคอร์เนลทั่วไปของ Android)

โดยปกติ กระบวนการของผู้ขายจะไม่เปิดโปรแกรมควบคุมเครื่องผูกโดยตรง แต่จะลิงก์กับไลบรารี userspace ของ libbinder ซึ่งจะเปิดโปรแกรมควบคุมเครื่องผูก การเพิ่มวิธีการสำหรับ ::android::ProcessState() จะเลือกไดรเวอร์เครื่องผูกสำหรับ libbinder กระบวนการของผู้จำหน่ายควรเรียกใช้เมธอดนี้ ก่อนที่จะ เรียกใช้ ProcessState, IPCThreadState หรือก่อนทำการเรียก Binder โดยทั่วไป ในการใช้งาน ให้ทำการเรียกต่อไปนี้ต่อจาก main() ของกระบวนการของผู้ขาย (ไคลเอนต์และเซิร์ฟเวอร์):

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

ก่อนหน้านี้ บริการ Binder ถูกลงทะเบียนกับ servicemanager ซึ่งกระบวนการอื่นสามารถเรียกคืนได้ ใน Android 8 ตอนนี้ servicemanager ถูกใช้โดยกระบวนการเฟรมเวิร์กและแอปเท่านั้น และกระบวนการของผู้ขายไม่สามารถเข้าถึงได้อีกต่อไป

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

ไบนารี vndservicemanager รวมอยู่ใน makefiles อุปกรณ์เริ่มต้นของ Android

นโยบาย SELinux

กระบวนการของผู้จำหน่ายที่ต้องการใช้ฟังก์ชันตัวประสานเพื่อสื่อสารระหว่างกันจำเป็นต้องมีสิ่งต่อไปนี้:

  1. เข้าถึง /dev/vndbinder
  2. Binder {transfer, call} ขอเข้า vndservicemanager
  3. binder_call(A, B) สำหรับโดเมนผู้ขาย A ที่ต้องการเรียกไปยังโดเมนของผู้ขาย B ผ่านอินเทอร์เฟซตัวประสานของผู้ขาย
  4. อนุญาตให้ {add, find} บริการใน vndservicemanager

เพื่อให้เป็นไปตามข้อกำหนด 1 และ 2 ให้ใช้ vndbinder_use()

vndbinder_use(some_vendor_process_domain);

เพื่อให้เป็นไปตามข้อกำหนด 3 binder_call(A, B) สำหรับกระบวนการของผู้จัดจำหน่าย A และ B ที่จำเป็นต้องพูดคุยผ่านเครื่องผูกสามารถคงอยู่ในตำแหน่งเดิม และไม่จำเป็นต้องเปลี่ยนชื่อ

เพื่อให้เป็นไปตามข้อกำหนด 4 คุณต้องทำการเปลี่ยนแปลงวิธีจัดการชื่อบริการ ป้ายชื่อบริการ และกฎ

สำหรับรายละเอียดเกี่ยวกับ SELinux โปรดดู ที่ Security-Enhanced Linux ใน Android สำหรับรายละเอียดเกี่ยวกับ SELinux ใน Android 8.0 โปรดดูที่ SELinux สำหรับ Android 8.0

ชื่อบริการ

ก่อนหน้านี้ ผู้ขายจะประมวลผลชื่อบริการที่ลงทะเบียนในไฟล์ service_contexts และเพิ่มกฎที่เกี่ยวข้องสำหรับการเข้าถึงไฟล์นั้น ตัวอย่างไฟล์ service_contexts จาก device/google/marlin/sepolicy :

AtCmdFwd                              u:object_r:atfwd_service:s0
cneservice                            u:object_r:cne_service:s0
qti.ims.connectionmanagerservice      u:object_r:imscm_service:s0
rcs                                   u:object_r:radio_service:s0
uce                                   u:object_r:uce_service:s0
vendor.qcom.PeripheralManager         u:object_r:per_mgr_service:s0

ใน Android 8 vndservicemanager โหลดไฟล์ vndservice_contexts แทน ควรเพิ่มบริการของผู้จำหน่ายที่ย้ายไปยัง vndservicemanager (และอยู่ในไฟล์ service_contexts เก่าแล้ว) ลงในไฟล์ vndservice_contexts ใหม่

ป้ายบริการ

ก่อนหน้านี้ ป้ายกำกับบริการ เช่น u:object_r:atfwd_service:s0 ถูกกำหนดไว้ในไฟล์ service.te ตัวอย่าง:

type atfwd_service,      service_manager_type;

ใน Android 8 คุณต้องเปลี่ยนประเภทเป็น vndservice_manager_type และย้ายกฎไปที่ไฟล์ vndservice.te ตัวอย่าง:

type atfwd_service,      vndservice_manager_type;

กฎของผู้จัดการบริการ

ก่อนหน้านี้ กฎอนุญาตให้โดเมนเข้าถึงเพื่อเพิ่มหรือค้นหาบริการจาก servicemanager ตัวอย่าง:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

ใน Android 8 กฎดังกล่าวสามารถคงอยู่และใช้คลาสเดียวกันได้ ตัวอย่าง:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;