ใช้ Binder IPC

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

การเปลี่ยนแปลงไดรฟ์ Binder

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

โดเมน (บริบท) ของ Binder หลายรายการ

Common-4.4 ขึ้นไป รวมถึงเวอร์ชันที่อัปเดตจาก upstream

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

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

Common-4.4 ขึ้นไป รวมถึงเวอร์ชันที่อัปเดตจาก upstream

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

  • 1 ครั้งเพื่อแปลงเป็นรูปแบบอนุกรมเป็น Parcel ในกระบวนการเรียกใช้
  • เมื่ออยู่ในไดรเวอร์เคอร์เนล ให้คัดลอก Parcel ไปยังกระบวนการเป้าหมาย
  • 1 ครั้งเพื่อยกเลิกการจัดรูปแบบ Parcel ในกระบวนการเป้าหมาย

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

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

ทั่วไป - 4.4 ขึ้นไป รวมถึงเวอร์ชันที่อัปเดตจาก upstream

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

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

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

การรับช่วงลําดับความสําคัญแบบเรียลไทม์

Common-4.4 และ common-4.9 (เวอร์ชันสตรีมหลักจะมีให้บริการเร็วๆ นี้)

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

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

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

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

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

SHA สำหรับเคอร์เนลทั่วไป

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

  • Common-3.18
    cc8b90c121de ANDROID: binder: don't check prio permissions on restore.
  • Common-4.4
    76b376eac7a2 ANDROID: binder: don't check prio permissions on restore.
  • Common-4.9
    ecd972d4f9b5 ANDROID: binder: don't check prio permissions on restore.

ทำงานกับ IPC ของ Binder

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

vndbinder

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

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

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

โดยปกติแล้ว กระบวนการของผู้ให้บริการจะไม่เปิดไดรเวอร์ Binder โดยตรง แต่จะใช้ลิงก์กับlibbinderไลบรารีพื้นที่ผู้ใช้ ซึ่งจะเปิดไดรเวอร์ Binder การเพิ่มวิธีการสำหรับ ::android::ProcessState() จะเลือกไดรฟ์ Binder สำหรับ 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 จะรวมอยู่ในไฟล์ make ของอุปกรณ์เริ่มต้นของ Android

นโยบาย SELinux

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

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

หากต้องการปฏิบัติตามข้อกำหนดที่ 1 และ 2 ให้ใช้vndbinder_use() มาโครต่อไปนี้

vndbinder_use(some_vendor_process_domain);

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

หากต้องการปฏิบัติตามข้อกำหนดที่ 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 rules

ก่อนหน้านี้ กฎจะอนุญาตให้โดเมนเข้าถึงเพื่อเพิ่มหรือค้นหาบริการจาก 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;