การใช้ Binder IPC

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

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

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

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

Common-4.4 และสูงกว่า รวมถึงอัปสตรีมด้วย

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

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

Common-4.4 และสูงกว่า รวมถึงอัปสตรีมด้วย

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

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

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

ตัวล็อคแบบละเอียด

Common-4.4 และสูงกว่า รวมถึงอัปสตรีมด้วย

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

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

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

การสืบทอดลำดับความสำคัญแบบเรียลไทม์

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

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

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

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

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

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

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

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

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

การใช้เครื่องผูก IPC

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

vndbinder

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

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

vndservicemanager

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

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

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

นโยบายของ SELinux

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

  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 ที่จำเป็นต้องพูดคุยผ่าน 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 ตัวอย่าง:

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;