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