Dịch vụ và chuyển dữ liệu

Trang 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 giao diện của tệp .hal.

Đăng ký dịch vụ

Bạn có thể đăng ký máy chủ giao diện HIDL (các đối tượng triển khai giao diện) dưới dạng dịch vụ được đặt tên. Tên đã đăng ký không cần liên quan đến giao diện hoặc tên gói. Nếu không chỉ định tên, tên "mặc định" sẽ được sử dụng; tên này nên được dùng cho các HAL không cần đăng ký hai phương thức 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 giao diện HIDL được đưa vào chính giao diện đó. Mã này được tự động liên kết với việc đă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. Bạn không cần đăng ký đối tượng máy chủ và có thể truyền đối tượng đó qua các tham số phương thức HIDL đến một quy trình khác thực hiện lệnh gọi phương thức HIDL vào máy chủ.

Khám phá các dịch vụ

Các yêu cầu theo mã ứng dụng được thực hiện cho một giao diện nhất định theo tên và 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 giao diện HIDL được coi là một giao diện riêng biệt. Do đó, cả IFooService phiên bản 1.1 và IFooService phiên bản 2.2 đều có thể được đăng ký dưới dạng "foo_service" và getService("foo_service") trên một trong hai giao diện sẽ nhận được dịch vụ đã đăng ký cho giao diện đó. Đó là lý do tại sao trong hầu hết các trường hợp, bạn không cần cung cấp thông số tên để đăng ký hoặc khám phá (nghĩa là tên "mặc định").

Đối tượng giao diện của nhà cung cấp cũng đóng vai trò trong phương thức truyền tải 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 do 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 tệp kê khai thiết bị nếu mục nhập đó tồn tại; và nếu không có phương thức truyền tải, thì giá trị rỗng sẽ được trả về.

Trong một số trường hợp, bạn có thể cần phải tiếp tục ngay lập tức ngay cả khi không nhận được dịch vụ. Điều này có thể xảy ra (ví dụ:) khi ứng dụng muốn tự quản lý thông báo dịch vụ hoặc trong một 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 các thông báo đó. Trong trường hợp này, các API bổ sung sẽ được cung cấp, chẳng hạn như tryGetService trong C++ hoặc getService("instance-name", false) trong Java. Bạn cũng phải sử dụng API cũ getService được cung cấp trong Java với 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 ứng dụng yêu cầu bằng một trong các API không thử lại này.

Thông báo về sự cố dịch vụ

Những ứng dụ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 này phân phối. Để nhận thông báo, ứng dụng phải:

  1. Lớp con của 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().
  3. Tạo bản sao của đối tượng lớp con hidl_death_recipient.
  4. Gọi phương thức linkToDeath() trên dịch vụ để theo dõi, truyền đối tượng giao diện của IDeathRecipient vào. Xin lưu ý rằng phương thức này không nhận quyền sở hữu đối với trình nhận sự kiện ngừng hoạt động hoặc proxy mà phương thức này được gọi.

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);

Bạn có thể đăng ký cùng một người nhận thông báo về trường hợp tử vong trên nhiều dịch vụ.

Chuyển dữ liệu

Bạn có thể 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 giao diện trong tệp .hal. Có hai loại phương thức:

  • Các phương thức Chặn sẽ đợi cho đến khi máy chủ tạo 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 các lệnh gọi RPC vượt quá giới hạn triển khai, thì các lệnh gọi đó có thể chặn hoặc trả về chỉ báo lỗi (vẫn chưa xác định được hành vi).

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

Tất 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 được gọi. Các cấu trúc cần các 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ân phát 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 liên quan đến 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 một giao diện khác).

Lệnh gọi lại

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

Lệnh gọi lại đồng bộ được 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 phải nguyên gốc) sẽ trả về kết quả thông qua một hàm gọi lại. Nếu chỉ trả về một giá trị và đó là loại gốc, thì lệnh gọi lại sẽ không được sử dụng và giá trị sẽ được trả về từ phương thức. Máy chủ triển khai các phương thức HIDL và ứng dụng triển khai các lệnh gọi lại.

Lệnh gọi lại không đồng bộ cho phép máy chủ của giao diện HIDL tạo lệnh gọi. Việc này được thực hiện bằng cách truyền một thực thể của giao diện thứ hai thông qua giao diện đầu tiên. Ứng dụng của giao diện đầu tiên phải đóng vai trò là 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ụ: quá trình triển khai HAL có thể gửi thông tin một cách không đồng bộ trở lại quy trình đang sử dụng thông tin đó bằng cách gọi các phương thức trên đối tượng giao diện do quy trình đó tạo và phân phát. Các phương thức trong giao diện dùng cho lệnh gọi lại không đồng bộ có thể đang chặn (và có thể trả về giá trị cho phương thức gọi) hoặc oneway. Để biết ví dụ, hãy xem phần "Lệnh gọi lại không đồng bộ" trong HIDL C++.

Để đơn giản hoá 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ỉ nhận tham số in và không hỗ trợ tham số out hoặc inout.

Hạn mức mỗi giao dịch

Không áp dụng giới hạn trên mỗi giao dịch đố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 lệnh gọi vượt quá 4 KB cho mỗi giao dịch sẽ được coi là quá mức. Nếu thấy lỗi này, bạn nên thiết kế lại giao diện HIDL đã cho. Một hạn chế khác là các tài nguyên có sẵn cho cơ sở hạ tầng HIDL để xử lý nhiều giao dịch đồng thời. Nhiều giao dịch có thể diễn ra đồng thời do nhiều luồng hoặc quy trình gửi lệnh gọi đến một quy trình hoặc nhiều lệnh gọi oneway không được quy trình nhận xử lý nhanh chóng. Theo mặc định, tổng dung lượng tối đa có sẵn cho tất cả các giao dịch đồng thời là 1 MB.

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

Triển khai phương thức

HIDL tạo các tệp tiêu đề khai báo các loại, 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 giống nhau đối với cả mã máy khách và máy chủ. Hệ thống HIDL cung cấp các phương thức triển khai proxy ở phía phương thức gọi để sắp xếp dữ liệu cho quá trình truyền IPC và mã stub ở phía phương thức được gọi để truyền dữ liệu vào các phương thức triển khai của nhà phát triển.

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

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

Chuyển dữ liệu không phải RPC

HIDL có hai cách để chuyể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ớ dùng chung. Loại HIDL tích hợp memory được dùng để truyền một đối tượng đại diện cho bộ nhớ dùng chung đã được phân bổ. Có thể dùng trong quy 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 thông báo mẫu triển khai tính năng truyền thông báo không chờ. Lớp này không sử dụng hạt nhân hoặc trình lập lịch biểu ở chế độ truyền tải hoặc liên kết (giao tiếp giữa các thiết bị 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ố của loại HIDL tích hợp MQDescriptorSync hoặc MQDescriptorUnsync. Quá trình nhận có thể sử dụng đối tượng này để thiết lập đầu kia của hàng đợi.
    • Hàng đợi Đồng bộ hoá không được phép tràn và chỉ có thể có một trình đọc.
    • Hàng đợi Unsync (Không đồng bộ hoá) được phép tràn và có thể có nhiều trình đọc, mỗi trình đọc phải đọc dữ liệu kịp thời nếu không sẽ mất dữ liệu.
    Không được phép để cả hai loại đều bị thiếu dữ liệu (không đọc được từ hàng đợi trống) và mỗi loại chỉ được có một trình ghi.

Để biết thêm thông tin chi tiết về FMQ, hãy xem bài viết Fast Message Queue (FMQ) (Hàng đợi thông báo nhanh).