Dịch vụ & Truyền dữ liệu

Phần này mô tả cách đăng ký và khám phá các dịch vụ cũng như cách gửi dữ liệu đến một dịch vụ bằng cách gọi các phương thức được xác định trong các giao diện trong tệp .hal .

Đăng ký dịch vụ

Máy chủ giao diện HIDL (đối tượng triển khai giao diện) có thể được đăng ký dưới dạng dịch vụ được đặt tên. Tên đăng ký không nhất thiết phải liên quan đến giao diện hoặc tên gói. Nếu không có tên nào được chỉ định thì tên "mặc định" sẽ được sử dụng; điều này nên được sử dụng cho các HAL không cần đăng ký hai triển khai của cùng một giao diện. Ví dụ: lệnh gọi C++ để đăng ký dịch vụ được xác định trong mỗi giao diện là:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

Phiên bản của giao diện HIDL được bao gồm trong chính giao diện đó. Nó được tự động liên kết với đăng ký dịch vụ và có thể được truy xuất thông qua lệnh gọi phương thức ( android::hardware::IInterface::getInterfaceVersion() ) trên mọi giao diện HIDL. Các đối tượng máy chủ không cần phải đăng ký và có thể được chuyển qua các tham số phương thức HIDL tới một quy trình khác sẽ thực hiện các lệnh gọi phương thức HIDL vào máy chủ.

Khám phá dịch vụ

Các yêu cầu theo mã máy khách được thực hiện cho một giao diện nhất định theo tên và theo phiên bản, gọi getService trên lớp HAL mong muốn:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

Mỗi phiên bản của giao diện HIDL được coi là một giao diện riêng biệt. Do đó, IFooService phiên bản 1.1 và IFooService phiên bản 2.2 đều có thể được đăng ký là "foo_service" và getService("foo_service") trên một trong hai giao diện nhận dịch vụ đã đăng ký cho giao diện đó. Đây là lý do tại sao, trong hầu hết các trường hợp, không cần cung cấp tham số tên để đăng ký hoặc khám phá (có nghĩa là tên "mặc định").

Đối tượng Giao diện Nhà cung cấp cũng đóng một vai trò trong phương thức vận chuyển của giao diện được trả về. Đối với giao diện IFoo trong gói android.hardware.foo@1.0 , giao diện được IFoo::getService trả về luôn sử dụng phương thức truyền tải được khai báo cho android.hardware.foo trong bảng kê khai thiết bị nếu mục nhập tồn tại; và nếu phương thức vận chuyển không có sẵn, nullptr sẽ được trả về.

Trong một số trường hợp, có thể cần phải tiếp tục ngay cả khi chưa nhận được dịch vụ. Điều này có thể xảy ra (ví dụ) khi khách hàng muốn tự quản lý thông báo dịch vụ hoặc trong chương trình chẩn đoán (chẳng hạn như atrace ) cần lấy tất cả hwservices và truy xuất chúng. Trong trường hợp này, các API bổ sung được cung cấp như tryGetService trong C++ hoặc getService("instance-name", false) trong Java. API getService cũ được cung cấp trong Java cũng phải được sử dụng cùng với các thông báo dịch vụ. Việc sử dụng API này không tránh được tình trạng tương tranh trong đó máy chủ tự đăng ký sau khi khách hàng yêu cầu nó bằng một trong các API không thử lại này.

Thông báo ngừng dịch vụ

Những khách hàng muốn được thông báo khi một dịch vụ ngừng hoạt động có thể nhận được thông báo ngừng hoạt động do khung cung cấp. Để nhận được thông báo, khách hàng phải:

  1. Phân lớp lớp/giao diện HIDL hidl_death_recipient (trong mã C++, không phải trong HIDL).
  2. Ghi đè phương thức serviceDied() của nó.
  3. Khởi tạo một đối tượng của lớp con hidl_death_recipient .
  4. Gọi phương thức linkToDeath() trên dịch vụ để giám sát, truyền vào đối tượng giao diện của IDeathRecipient . Lưu ý rằng phương pháp này không có quyền sở hữu của người nhận cái chết hoặc proxy mà nó được gọi.

Một ví dụ về mã giả (C++ và Java tương tự nhau):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

Cùng một người nhận qua đời có thể được đăng ký trên nhiều dịch vụ khác nhau.

Truyền dữ liệu

Dữ liệu có thể được gửi đến một dịch vụ bằng cách gọi các phương thức được xác định trong giao diện trong tệp .hal . Có hai loại phương pháp:

  • Phương pháp chặn chờ cho đến khi máy chủ đưa ra kết quả.
  • Phương thức một chiều chỉ gửi dữ liệu theo một hướng và không chặn. Nếu lượng dữ liệu đang truyền trong lệnh gọi RPC vượt quá giới hạn triển khai thì lệnh gọi có thể chặn hoặc trả về chỉ báo lỗi (hành vi chưa được xác định).

Một phương thức không trả về giá trị nhưng oneway được khai báo vẫn đang bị chặn.

Tất cả các phương thức được khai báo trong giao diện HIDL đều được gọi theo một hướng duy nhất, từ HAL hoặc vào HAL. Giao diện không chỉ định hướng nó sẽ được gọi. Các kiến ​​trúc cần lệnh gọi bắt nguồn từ HAL phải cung cấp hai (hoặc nhiều) giao diện trong gói HAL và phục vụ giao diện thích hợp từ mỗi quy trình. Các từ máy kháchmáy chủ được sử dụng theo hướng gọi của giao diện (tức là HAL có thể là máy chủ của một giao diện và máy khách của giao diện khác).

Cuộc gọi lại

Từ gọi lại đề cập đến hai khái niệm khác nhau, được phân biệt bằng gọi lại đồng bộgọi lại không đồng bộ .

Lệnh gọi lại đồng bộ được sử dụng trong một số phương thức HIDL trả về dữ liệu. Phương thức HIDL trả về nhiều giá trị (hoặc trả về một giá trị thuộc loại không nguyên thủy) trả về kết quả của nó thông qua hàm gọi lại. Nếu chỉ có một giá trị được trả về và đó là kiểu nguyên thủy, lệnh gọi lại sẽ không được sử dụng và giá trị được trả về từ phương thức. Máy chủ triển khai các phương thức HIDL và máy khách triển khai các lệnh gọi lại.

Cuộc gọi lại không đồng bộ cho phép máy chủ của giao diện HIDL bắt đầu cuộc gọi. Điều này được thực hiện bằng cách chuyển một phiên bản của giao diện thứ hai qua giao diện đầu tiên. Máy khách của giao diện đầu tiên phải hoạt động như máy chủ của giao diện thứ hai. Máy chủ của giao diện đầu tiên có thể gọi các phương thức trên đối tượng giao diện thứ hai. Ví dụ: việc triển khai HAL có thể gửi thông tin không đồng bộ trở lại quy trình đang sử dụng nó bằng cách gọi các phương thức trên một đối tượng giao diện được quy trình đó tạo và phục vụ. Các phương thức trong giao diện được sử dụng để gọi lại không đồng bộ có thể bị chặn (và có thể trả về giá trị cho người gọi) hoặc oneway . Để biết ví dụ, hãy xem "Gọi lại không đồng bộ" trong HIDL C++ .

Để đơn giản hóa quyền sở hữu bộ nhớ, các lệnh gọi phương thức và lệnh gọi lại chỉ in tham số và không hỗ trợ tham số out hoặc inout .

Giới hạn mỗi giao dịch

Giới hạn trên mỗi giao dịch không được áp dụng đối với lượng dữ liệu được gửi trong các phương thức và lệnh gọi lại HIDL. Tuy nhiên, các cuộc gọi vượt quá 4KB cho mỗi giao dịch được coi là quá mức. Nếu thấy điều này thì nên kiến ​​trúc lại giao diện HIDL đã cho. Một hạn chế khác là tài nguyên sẵn có cho cơ sở hạ tầng HIDL để xử lý nhiều giao dịch đồng thời. Nhiều giao dịch có thể được thực hiện đồng thời do nhiều luồng hoặc quy trình gửi cuộc gọi đến một quy trình hoặc nhiều cuộc gọi oneway không được quy trình nhận xử lý nhanh chóng. Tổng dung lượng tối đa có sẵn cho tất cả các giao dịch đồng thời theo mặc định là 1MB.

Trong một giao diện được thiết kế tốt, việc vượt quá những giới hạn tài nguyên này sẽ không xảy ra; nếu đúng như vậy, cuộc gọi vượt quá chúng có thể bị chặn cho đến khi tài nguyên sẵn có hoặc báo hiệu lỗi truyền tải. Mỗi lần vượt quá giới hạn trên mỗi giao dịch hoặc làm tràn tài nguyên triển khai HIDL bằng các giao dịch tổng hợp trong quá trình thực hiện đều được ghi lại để tạo điều kiện gỡ lỗi.

Triển khai phương pháp

HIDL tạo các tệp tiêu đề khai báo các kiểu, phương thức và lệnh gọi lại cần thiết bằng ngôn ngữ đích (C++ hoặc Java). Nguyên mẫu của các phương thức và lệnh gọi lại do HIDL xác định là giống nhau cho cả mã máy khách và mã máy chủ. Hệ thống HIDL cung cấp các triển khai proxy của các phương thức ở phía người gọi để tổ chức dữ liệu cho việc truyền tải IPC và mã sơ khai ở phía người được gọi để chuyển dữ liệu vào việc triển khai các phương thức của nhà phát triển.

Người gọi hàm (phương thức HIDL hoặc gọi lại) có quyền sở hữu cấu trúc dữ liệu được truyền vào hàm và giữ quyền sở hữu sau lệnh gọi; trong mọi trường hợp, người được gọi không cần giải phóng hoặc giải phóng bộ nhớ.

  • Trong C++, dữ liệu có thể ở dạng chỉ đọc (cố gắng ghi vào dữ liệu có thể gây ra lỗi phân đoạn) và có giá trị trong suốt thời gian của cuộc gọi. Máy khách có thể sao chép sâu dữ liệu để truyền dữ liệu ra ngoài cuộc gọi.
  • Trong Java, mã nhận được một bản sao dữ liệu cục bộ (một đối tượng Java bình thường), nó có thể giữ và sửa đổi hoặc cho phép thu thập rác.

Truyền dữ liệu không phải RPC

HIDL có hai cách để truyền dữ liệu mà không cần sử dụng lệnh gọi RPC: bộ nhớ dùng chung và Hàng đợi tin nhắn nhanh (FMQ), cả hai đều chỉ được hỗ trợ trong C++.

  • Bộ nhớ chia sẻ . memory loại HIDL tích hợp được sử dụng để truyền một đối tượng đại diện cho bộ nhớ dùng chung đã được phân bổ. Có thể được sử dụng trong quá trình nhận để ánh xạ bộ nhớ dùng chung.
  • Hàng đợi tin nhắn nhanh (FMQ) . HIDL cung cấp một loại hàng đợi tin nhắn theo khuôn mẫu để thực hiện chuyển tin nhắn không chờ đợi. Nó không sử dụng kernel hoặc bộ lập lịch ở chế độ chuyển tiếp hoặc liên kết (giao tiếp giữa các thiết bị sẽ không có các thuộc tính này). Thông thường, HAL thiết lập phần cuối của hàng đợi, tạo một đối tượng có thể được truyền qua RPC thông qua tham số loại HIDL tích hợp MQDescriptorSync hoặc MQDescriptorUnsync . Đối tượng này có thể được quá trình nhận sử dụng để thiết lập đầu bên kia của hàng đợi.
    • Hàng đợi đồng bộ hóa không được phép tràn và chỉ có thể có một đầu đọc.
    • Hàng đợi không đồng bộ được phép tràn và có thể có nhiều đầu đọc, mỗi đầu đọc phải đọc dữ liệu kịp thời nếu không sẽ mất dữ liệu.
    Cả hai loại đều không được phép tràn (đọc từ hàng đợi trống sẽ không thành công) và mỗi loại chỉ có thể có một người ghi.

Để biết thêm chi tiết về FMQ, hãy xem Hàng đợi tin nhắn nhanh (FMQ) .