หน้านี้อธิบายถึงการเปลี่ยนแปลงไดรเวอร์ binder ใน Android 8 ให้รายละเอียดเกี่ยวกับการใช้ binder IPC และรายการนโยบาย SELinux ที่จำเป็น
การเปลี่ยนแปลงไดรเวอร์เครื่องผูก
ตั้งแต่ Android 8 ตอนนี้เฟรมเวิร์กของ Android และ HAL จะสื่อสารกันโดยใช้ Binder ขณะที่การสื่อสารนี้เพิ่มขึ้นอย่างรวดเร็วจราจรเครื่องผูก, Android 8 รวมถึงการปรับปรุงหลายออกแบบมาเพื่อให้เครื่องผูก IPC รวดเร็ว ผู้ให้บริการ SoC และ OEM ควรผสานโดยตรงจากสาขาที่เกี่ยวข้องของ android-4.4, android-4.9 และรุ่นที่สูงกว่าของ เคอร์เนล / โครงการ ทั่วไป
โดเมน Binder หลายโดเมน (บริบท)
Common-4.4 และสูงกว่ารวมถึงต้นน้ำในการแยกทราฟฟิกของ binder อย่างหมดจดระหว่างเฟรมเวิร์ก (ไม่ขึ้นกับอุปกรณ์) และโค้ดของผู้ขาย (เฉพาะอุปกรณ์) Android 8 ได้นำเสนอแนวคิดของ บริบทตัวประสาน บริบท Binder แต่ละรายการมีโหนดอุปกรณ์ของตัวเองและตัวจัดการบริบท (บริการ) ของตัวเอง คุณสามารถเข้าถึงตัวจัดการบริบทผ่านโหนดอุปกรณ์ที่เป็นสมาชิกเท่านั้นและเมื่อส่งโหนด binder ผ่านบริบทบางอย่างจะสามารถเข้าถึงได้จากบริบทเดียวกันโดยกระบวนการอื่นเท่านั้นดังนั้นจึงแยกโดเมนออกจากกันอย่างสมบูรณ์ ดูรายละเอียดการใช้งานได้ที่ vndbinder และ vndservicemanager
กระจัดกระจาย
Common-4.4 และสูงกว่ารวมถึงต้นน้ำใน Android รุ่นก่อนหน้าข้อมูลทุกชิ้นในการเรียก Binder จะถูกคัดลอกสามครั้ง:
- หนึ่งครั้งเพื่อต่ออนุกรมลงใน
Parcel
ในขั้นตอนการโทร - เมื่ออยู่ในไดรเวอร์เคอร์เนลเพื่อคัดลอก
Parcel
ไปยังกระบวนการเป้าหมาย - หนึ่งครั้งเพื่อยกเลิกการปิดผนึก
Parcel
ในกระบวนการเป้าหมาย
Android 8 ใช้ การเพิ่มประสิทธิภาพการรวบรวมกระจาย เพื่อลดจำนวนสำเนาจาก 3 เป็น 1 แทนที่จะจัดลำดับข้อมูลใน Parcel
ก่อนข้อมูลจะยังคงอยู่ในโครงสร้างเดิมและเค้าโครงหน่วยความจำและไดรเวอร์จะคัดลอกไปยังกระบวนการเป้าหมายทันที หลังจากข้อมูลอยู่ในกระบวนการเป้าหมายโครงสร้างและเค้าโครงหน่วยความจำจะเหมือนกันและสามารถอ่านข้อมูลได้โดยไม่ต้องใช้สำเนาอื่น
การล็อคแบบละเอียด
Common-4.4 และสูงกว่ารวมถึงต้นน้ำใน Android รุ่นก่อนหน้าโปรแกรมควบคุมเครื่องผูกใช้การล็อกทั่วโลกเพื่อป้องกันการเข้าถึงโครงสร้างข้อมูลที่สำคัญพร้อมกัน แม้ว่าจะมีการโต้แย้งเล็กน้อยสำหรับการล็อก แต่ปัญหาหลักก็คือหากเธรดที่มีลำดับความสำคัญต่ำได้รับการล็อกและได้รับการป้องกันไว้ก่อนแล้วเธรดที่มีลำดับความสำคัญสูงกว่าจะล่าช้าอย่างมากซึ่งจำเป็นต้องได้รับการล็อกเดียวกัน สิ่งนี้ทำให้เกิดปัญหาในแพลตฟอร์ม
ความพยายามครั้งแรกในการแก้ไขปัญหานี้เกี่ยวข้องกับการปิดการใช้งานใบจองในขณะที่ล็อกโกลบอลล็อก อย่างไรก็ตามนี่เป็นการแฮ็กมากกว่าวิธีแก้ปัญหาที่แท้จริงและในที่สุดก็ถูกปฏิเสธโดยต้นน้ำและทิ้งไป ความพยายามในครั้งต่อมามุ่งเน้นไปที่การล็อกแบบละเอียดยิ่งขึ้นซึ่งเป็นเวอร์ชันที่ใช้งานบนอุปกรณ์ Pixel ตั้งแต่เดือนมกราคม 2017 ในขณะที่การเปลี่ยนแปลงส่วนใหญ่เปิดเผยต่อสาธารณะ แต่มีการปรับปรุงที่สำคัญในเวอร์ชันต่อ ๆ ไป
หลังจากระบุปัญหาเล็กน้อยในการใช้งานการล็อกแบบละเอียดแล้วเราได้คิดค้นโซลูชันที่ได้รับการปรับปรุงด้วยสถาปัตยกรรมการล็อกที่แตกต่างกันและส่งการเปลี่ยนแปลงในสาขาเคอร์เนลทั่วไปทั้งหมด เรายังคงทดสอบการใช้งานนี้กับอุปกรณ์ต่างๆจำนวนมาก เนื่องจากเราไม่ทราบถึงปัญหาที่ค้างคานี่คือการใช้งานที่แนะนำสำหรับอุปกรณ์ที่จัดส่งด้วย Android 8
การสืบทอดลำดับความสำคัญแบบเรียลไทม์
Common-4.4 และ common-4.9 (อัปสตรีมเร็ว ๆ นี้)โปรแกรมควบคุมเครื่องผูกสนับสนุนการสืบทอดลำดับความสำคัญที่ดีเสมอ เนื่องจากจำนวนกระบวนการที่เพิ่มขึ้นใน Android ทำงานตามลำดับความสำคัญแบบเรียลไทม์ในบางกรณีตอนนี้จึงสมเหตุสมผลแล้วว่าหากเธรดแบบเรียลไทม์ทำการเรียกตัวประสานเธรดในกระบวนการที่จัดการการโทรนั้นจะทำงานตามลำดับความสำคัญแบบเรียลไทม์ . เพื่อรองรับกรณีการใช้งานเหล่านี้ตอนนี้ Android 8 ใช้การสืบทอดลำดับความสำคัญแบบเรียลไทม์ในโปรแกรมควบคุมเครื่องผูก
นอกเหนือจากการสืบทอดลำดับความสำคัญระดับธุรกรรมแล้วการสืบทอดลำดับความสำคัญของ โหนดยัง อนุญาตให้โหนด (วัตถุบริการ binder) ระบุลำดับความสำคัญขั้นต่ำที่ควรเรียกใช้ในโหนดนี้ Android เวอร์ชันก่อนหน้ารองรับการสืบทอดลำดับความสำคัญของโหนดที่มีค่าที่ดีอยู่แล้ว แต่ Android 8 เพิ่มการรองรับการสืบทอดโหนดของนโยบายการตั้งเวลาแบบเรียลไทม์
การเปลี่ยนแปลง Userspace
Android 8 มีการเปลี่ยนแปลงพื้นที่ผู้ใช้ทั้งหมดที่จำเป็นในการทำงานกับไดรเวอร์ตัวประสานปัจจุบันในเคอร์เนลทั่วไปโดยมีข้อยกเว้นหนึ่งข้อ: การใช้งานดั้งเดิมเพื่อปิดใช้งานการสืบทอดลำดับความสำคัญแบบเรียลไทม์สำหรับ /dev/binder
ใช้ ioctl การพัฒนาในภายหลังได้เปลี่ยนการควบคุมการสืบทอดลำดับความสำคัญไปเป็นวิธีการที่ละเอียดยิ่งขึ้นซึ่งเป็นโหมดต่อ binder (ไม่ใช่ตามบริบท) ดังนั้น IOCTL ไม่ได้ใน Android สาขาทั่วไปและแทนที่จะ ส่งในเมล็ดร่วมกันของเรา
ผลของการเปลี่ยนแปลงนี้คือการสืบทอดลำดับความสำคัญแบบเรียลไทม์ถูกปิดใช้งานโดยค่าเริ่มต้นสำหรับ ทุก โหนด ทีมประสิทธิภาพ Android พบว่ามีประโยชน์ในการเปิดใช้งานการสืบทอดลำดับความสำคัญแบบเรียลไทม์สำหรับทุกโหนดในโดเมน hwbinder
เพื่อให้ได้ผลเช่นเดียวกันเชอร์รี่เลือก การเปลี่ยนแปลงนี้ ใน userspace
SHAs สำหรับเมล็ดพืชทั่วไป
ในการรับการเปลี่ยนแปลงที่จำเป็นในโปรแกรมควบคุมเครื่องผูกให้ซิงค์กับ SHA ที่เหมาะสม:
- สามัญ - 3.18
cc8b90c121de ANDROID: binder: อย่าตรวจสอบสิทธิ์เบื้องต้นในการกู้คืน - สามัญ -4.4
76b376eac7a2 ANDROID: binder: อย่าตรวจสอบสิทธิ์เบื้องต้นในการกู้คืน - Common-4.9
ecd972d4f9b5 ANDROID: binder: อย่าตรวจสอบสิทธิ์เบื้องต้นในการกู้คืน
ใช้ 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)
โดยปกติกระบวนการของผู้ขายจะไม่เปิดโปรแกรมควบคุมเครื่องผูกโดยตรงและแทนที่จะเชื่อมโยงกับ libbinder
userspace ซึ่งจะเปิดโปรแกรมควบคุมเครื่องผูก การเพิ่มวิธีการสำหรับ ::android::ProcessState()
จะเลือกไดรเวอร์ binder สำหรับ 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
กระบวนการของผู้ขายที่ต้องการใช้ฟังก์ชันการทำงานของสารยึดเกาะเพื่อสื่อสารกันจำเป็นต้องมีสิ่งต่อไปนี้:
- เข้าถึง
/dev/vndbinder
- Binder
{transfer, call}
hooks intovndservicemanager
. -
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;