หน้านี้อธิบายการเปลี่ยนแปลงในไดรเวอร์ 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 เพื่อสื่อสารระหว่างกันจำเป็นต้องมีสิ่งต่อไปนี้:
- เข้าถึง
/dev/vndbinder
- Binder
{transfer, call}
เชื่อมต่อvndservicemanager
-
binder_call(A, B)
สำหรับโดเมนผู้ขาย A ที่ต้องการโทรเข้าสู่โดเมนผู้ขาย B ผ่านอินเทอร์เฟซเครื่องผูกของผู้ขาย - การอนุญาตให้
{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;