Sử dụng IPC liên kết

Trang này mô tả các thay đổi đối với trình điều khiển liên kết trong Android 8, cung cấp thông tin chi tiết về cách sử dụng IPC liên kết và liệt kê chính sách SELinux bắt buộc.

Thay đổi đối với trình điều khiển liên kết

Kể từ Android 8, khung Android và HAL hiện giao tiếp với nhau bằng cách sử dụng trình liên kết. Vì hoạt động giao tiếp này làm tăng đáng kể lưu lượng truy cập của liên kết, nên Android 8 có một số điểm cải tiến được thiết kế để giúp liên kết IPC hoạt động nhanh. Nhà cung cấp SoC và OEM nên hợp nhất trực tiếp từ các nhánh liên quan của android-4.4, android-4.9 trở lên của dự án kernel/common.

Nhiều miền liên kết (bối cảnh)

Common-4.4 trở lên, bao gồm cả thượng nguồn

Để phân tách rõ ràng lưu lượng truy cập liên kết giữa mã khung (độc lập với thiết bị) và mã nhà cung cấp (dành riêng cho thiết bị), Android 8 đã giới thiệu khái niệm về ngữ cảnh liên kết. Mỗi ngữ cảnh liên kết có nút thiết bị riêng và trình quản lý ngữ cảnh (dịch vụ) riêng. Bạn chỉ có thể truy cập vào trình quản lý ngữ cảnh thông qua nút thiết bị mà trình quản lý đó thuộc về và khi truyền nút liên kết thông qua một ngữ cảnh nhất định, bạn chỉ có thể truy cập vào nút liên kết đó từ cùng một ngữ cảnh đó bằng một quy trình khác, do đó hoàn toàn tách biệt các miền với nhau. Để biết thông tin chi tiết về cách sử dụng, hãy xem vndbindervndservicemanager.

Tán xạ-thu thập

Common-4.4 trở lên, bao gồm cả upstream

Trong các bản phát hành Android trước, mọi phần dữ liệu trong lệnh gọi liên kết đều được sao chép ba lần:

  • Một lần để chuyển đổi tuần tự thành Parcel trong quá trình gọi
  • Sau khi vào trình điều khiển hạt nhân để sao chép Parcel vào quy trình mục tiêu
  • Một lần để huỷ chuyển đổi tuần tự Parcel trong quy trình mục tiêu

Android 8 sử dụng tính năng tối ưu hoá thu thập-phân tán để giảm số lượng bản sao từ 3 xuống còn 1. Thay vì chuyển đổi tuần tự dữ liệu trong Parcel trước, dữ liệu vẫn ở cấu trúc và bố cục bộ nhớ ban đầu và trình điều khiển sẽ sao chép ngay dữ liệu đó vào quy trình mục tiêu. Sau khi dữ liệu nằm trong quy trình mục tiêu, cấu trúc và bố cục bộ nhớ sẽ giống nhau và bạn có thể đọc dữ liệu mà không cần bản sao khác.

Khoá chi tiết

Common-4.4 trở lên, bao gồm cả thượng nguồn

Trong các bản phát hành Android trước, trình điều khiển liên kết đã sử dụng một khoá toàn cục để bảo vệ khỏi việc truy cập đồng thời vào các cấu trúc dữ liệu quan trọng. Mặc dù có rất ít tranh chấp về khoá, nhưng vấn đề chính là nếu một luồng có mức độ ưu tiên thấp lấy được khoá rồi bị chiếm quyền, thì điều này có thể làm chậm đáng kể các luồng có mức độ ưu tiên cao hơn cần lấy cùng một khoá. Điều này đã gây ra hiện tượng giật trong nền tảng.

Các nỗ lực ban đầu để giải quyết vấn đề này liên quan đến việc tắt tính năng ưu tiên trong khi giữ khoá toàn cục. Tuy nhiên, đây là một cách hack hơn là một giải pháp thực sự, và cuối cùng đã bị từ chối và bị loại bỏ. Các nỗ lực tiếp theo tập trung vào việc tăng độ chi tiết của tính năng khoá, một phiên bản đã chạy trên các thiết bị Pixel kể từ tháng 1 năm 2017. Mặc dù phần lớn các thay đổi đó đã được công bố, nhưng các phiên bản tiếp theo đã có những cải tiến đáng kể.

Sau khi xác định các vấn đề nhỏ trong quá trình triển khai tính năng khoá chi tiết, chúng tôi đã thiết kế một giải pháp cải tiến với cấu trúc khoá khác và gửi các thay đổi trong tất cả các nhánh nhân phổ biến. Chúng tôi tiếp tục thử nghiệm cách triển khai này trên nhiều thiết bị khác nhau; vì chúng tôi không nhận thấy vấn đề nào đáng kể, nên đây là cách triển khai được đề xuất cho các thiết bị chạy Android 8.

Kế thừa mức độ ưu tiên theo thời gian thực

Common-4.4 và common-4.9 (sắp có phiên bản thượng nguồn)

Trình điều khiển liên kết luôn hỗ trợ tính năng kế thừa mức độ ưu tiên tốt. Khi số lượng quy trình trong Android chạy ở mức độ ưu tiên theo thời gian thực ngày càng tăng, trong một số trường hợp, nếu luồng theo thời gian thực thực hiện lệnh gọi liên kết, thì luồng trong quy trình xử lý lệnh gọi đó cũng chạy ở mức độ ưu tiên theo thời gian thực. Để hỗ trợ các trường hợp sử dụng này, Android 8 hiện triển khai tính năng kế thừa mức độ ưu tiên theo thời gian thực trong trình điều khiển liên kết.

Ngoài tính năng kế thừa mức độ ưu tiên ở cấp giao dịch, tính năng kế thừa mức độ ưu tiên của nút cho phép một nút (đối tượng dịch vụ liên kết) chỉ định mức độ ưu tiên tối thiểu mà các lệnh gọi vào nút này sẽ được thực thi. Các phiên bản Android trước đã hỗ trợ tính năng kế thừa mức độ ưu tiên của nút bằng các giá trị phù hợp, nhưng Android 8 bổ sung tính năng hỗ trợ kế thừa nút chính sách lên lịch theo thời gian thực.

Thay đổi về không gian người dùng

Android 8 bao gồm tất cả các thay đổi về không gian người dùng cần thiết để hoạt động với trình điều khiển liên kết hiện tại trong nhân chung, ngoại trừ một trường hợp: Phương thức triển khai ban đầu để tắt tính năng kế thừa mức độ ưu tiên theo thời gian thực cho /dev/binder đã sử dụng ioctl. Quá trình phát triển tiếp theo đã chuyển quyền kiểm soát việc kế thừa ưu tiên sang một phương thức chi tiết hơn theo chế độ liên kết (chứ không phải theo ngữ cảnh). Do đó, ioctl không nằm trong nhánh Android chung mà thay vào đó là được gửi trong các hạt nhân chung của chúng tôi.

Hiệu quả của thay đổi này là tính năng kế thừa mức độ ưu tiên theo thời gian thực bị tắt theo mặc định cho mọi nút. Nhóm hiệu suất Android nhận thấy rằng việc bật tính năng kế thừa mức độ ưu tiên theo thời gian thực cho tất cả các nút trong miền hwbinder sẽ mang lại lợi ích. Để đạt được hiệu ứng tương tự, hãy chọn thay đổi này trong không gian người dùng.

SHA cho các hạt nhân phổ biến

Để nhận các thay đổi cần thiết đối với trình điều khiển liên kết, hãy đồng bộ hoá với SHA thích hợp:

  • Common-3.18
    cc8b90c121de ANDROID: liên kết: không kiểm tra quyền ưu tiên khi khôi phục.
  • Common-4.4
    76b376eac7a2 ANDROID: liên kết: không kiểm tra quyền ưu tiên khi khôi phục.
  • Common-4.9
    ecd972d4f9b5 ANDROID: liên kết: không kiểm tra quyền ưu tiên khi khôi phục.

Làm việc với liên kết IPC

Trước đây, các quy trình của nhà cung cấp đã sử dụng giao tiếp liên quy trình của liên kết (IPC) để giao tiếp. Trong Android 8, nút thiết bị /dev/binder trở thành nút dành riêng cho các quy trình khung, nghĩa là các quy trình của nhà cung cấp không còn quyền truy cập vào nút này nữa. Các quy trình của nhà cung cấp có thể truy cập vào /dev/hwbinder, nhưng phải chuyển đổi giao diện AIDL để sử dụng HIDL. Đối với những nhà cung cấp muốn tiếp tục sử dụng giao diện AIDL giữa các quy trình của nhà cung cấp, Android hỗ trợ IPC liên kết như mô tả bên dưới. Trong Android 10, AIDL ổn định cho phép tất cả các quy trình sử dụng /dev/binder, đồng thời giải quyết các đảm bảo về độ ổn định của HIDL và /dev/hwbinder. Để biết cách sử dụng AIDL ổn định, hãy xem phần AIDL cho HAL.

vndbinder

Android 8 hỗ trợ một miền liên kết mới để các dịch vụ của nhà cung cấp sử dụng, truy cập bằng /dev/vndbinder thay vì /dev/binder. Với việc thêm /dev/vndbinder, Android hiện có 3 miền IPC sau:

Miền IPC Mô tả
/dev/binder IPC giữa các quy trình khung/ứng dụng có giao diện AIDL
/dev/hwbinder IPC giữa các quy trình khung/nhà cung cấp có giao diện HIDL
IPC giữa các quy trình của nhà cung cấp có giao diện HIDL
/dev/vndbinder IPC giữa các quy trình của nhà cung cấp/nhà cung cấp có Giao diện AIDL

Để /dev/vndbinder xuất hiện, hãy đảm bảo mục cấu hình hạt nhân CONFIG_ANDROID_BINDER_DEVICES được đặt thành "binder,hwbinder,vndbinder" (đây là giá trị mặc định trong cây hạt nhân phổ biến của Android).

Thông thường, các quy trình của nhà cung cấp không trực tiếp mở trình điều khiển liên kết mà thay vào đó sẽ liên kết với thư viện không gian người dùng libbinder để mở trình điều khiển liên kết. Việc thêm một phương thức cho ::android::ProcessState() sẽ chọn trình điều khiển liên kết cho libbinder. Các quy trình của nhà cung cấp nên gọi phương thức này trước khi gọi vào ProcessState, IPCThreadState hoặc trước khi thực hiện bất kỳ lệnh gọi liên kết nào nói chung. Để sử dụng, hãy đặt lệnh gọi sau đây sau main() của quy trình của nhà cung cấp (ứng dụng khách và máy chủ):

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

vndservicemanager

Trước đây, các dịch vụ liên kết được đăng ký bằng servicemanager, nơi các dịch vụ này có thể được các quy trình khác truy xuất. Trong Android 8, servicemanager hiện chỉ được các quy trình khung và ứng dụng sử dụng riêng, còn các quy trình của nhà cung cấp không thể truy cập vào servicemanager nữa.

Tuy nhiên, các dịch vụ của nhà cung cấp hiện có thể sử dụng vndservicemanager, một thực thể mới của servicemanager sử dụng /dev/vndbinder thay vì /dev/binder và được tạo từ cùng một nguồn với khung servicemanager. Các quy trình của nhà cung cấp không cần thực hiện thay đổi để giao tiếp với vndservicemanager; khi một quy trình của nhà cung cấp mở /dev/vndbinder, các lượt tra cứu dịch vụ sẽ tự động chuyển đến vndservicemanager.

Tệp nhị phân vndservicemanager có trong tệp bản dựng thiết bị mặc định của Android.

Chính sách SELinux

Các quy trình của nhà cung cấp muốn sử dụng chức năng liên kết để giao tiếp với nhau cần có những điều sau:

  1. Quyền truy cập vào /dev/vndbinder.
  2. Binder {transfer, call} nối vào vndservicemanager.
  3. binder_call(A, B) cho mọi miền nhà cung cấp A muốn gọi vào miền nhà cung cấp B qua giao diện liên kết nhà cung cấp.
  4. Quyền sử dụng các dịch vụ {add, find} trong vndservicemanager.

Để đáp ứng yêu cầu 1 và 2, hãy sử dụng macro vndbinder_use():

vndbinder_use(some_vendor_process_domain);

Để đáp ứng yêu cầu 3, binder_call(A, B) cho các quy trình của nhà cung cấp A và B cần giao tiếp qua liên kết có thể giữ nguyên vị trí và không cần đổi tên.

Để đáp ứng yêu cầu 4, bạn phải thay đổi cách xử lý tên dịch vụ, nhãn dịch vụ và quy tắc.

Để biết thông tin chi tiết về SELinux, hãy xem bài viết Security-Enhanced Linux trong Android. Để biết thông tin chi tiết về SELinux trong Android 8.0, hãy xem bài viết SELinux cho Android 8.0.

Tên dịch vụ

Trước đây, nhà cung cấp xử lý tên dịch vụ đã đăng ký trong tệp service_contexts và thêm các quy tắc tương ứng để truy cập vào tệp đó. Tệp service_contexts mẫu từ 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

Trong Android 8, vndservicemanager sẽ tải tệp vndservice_contexts. Bạn nên thêm các dịch vụ của nhà cung cấp di chuyển sang vndservicemanager (và đã có trong tệp service_contexts cũ) vào tệp vndservice_contexts mới.

Nhãn dịch vụ

Trước đây, các nhãn dịch vụ như u:object_r:atfwd_service:s0 được xác định trong tệp service.te. Ví dụ:

type atfwd_service,      service_manager_type;

Trong Android 8, bạn phải thay đổi loại thành vndservice_manager_type và chuyển quy tắc sang tệp vndservice.te. Ví dụ:

type atfwd_service,      vndservice_manager_type;

quy tắc servicemanager

Trước đây, các quy tắc cấp cho miền quyền thêm hoặc tìm dịch vụ từ servicemanager. Ví dụ:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

Trong Android 8, các quy tắc như vậy có thể vẫn được áp dụng và sử dụng cùng một lớp. Ví dụ:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;