Android chứa một Lớp trừu tượng phần cứng (HAL) HIDL dành cho ô tô, giúp cung cấp tính năng chụp và hiển thị hình ảnh ngay từ rất sớm trong quá trình khởi động Android và tiếp tục hoạt động trong suốt thời gian hoạt động của hệ thống. HAL bao gồm ngăn xếp hệ thống chế độ xem bên ngoài (EVS) và thường được dùng để hỗ trợ camera chiếu hậu và màn hình hiển thị chế độ xem toàn cảnh trong các xe có hệ thống Thông tin giải trí trong xe (IVI) dựa trên Android. EVS cũng cho phép triển khai các tính năng nâng cao trong ứng dụng người dùng.
Android cũng bao gồm một giao diện trình điều khiển chụp và hiển thị dành riêng cho EVS (trong /hardware/interfaces/automotive/evs/1.0
). Mặc dù có thể tạo một ứng dụng camera chiếu hậu dựa trên các dịch vụ camera và màn hình hiện có của Android, nhưng ứng dụng như vậy có thể chạy quá muộn trong quá trình khởi động Android. Việc sử dụng một HAL chuyên dụng giúp đơn giản hoá giao diện và cho thấy rõ những gì mà OEM cần triển khai để hỗ trợ ngăn xếp EVS.
Thành phần hệ thống
EVS bao gồm các thành phần hệ thống sau:

Ứng dụng EVS
Một ứng dụng EVS mẫu bằng C++ (/packages/services/Car/evs/app
) đóng vai trò là một cách triển khai tham chiếu. Ứng dụng này chịu trách nhiệm yêu cầu các khung hình video từ Trình quản lý EVS và gửi các khung hình đã hoàn tất để hiển thị lại cho Trình quản lý EVS.
Hệ thống này dự kiến sẽ được init khởi động ngay khi EVS và Car Service có sẵn, nhắm đến mục tiêu trong vòng 2 giây kể từ khi bật nguồn. Các OEM có thể sửa đổi hoặc thay thế ứng dụng EVS theo ý muốn.
Trình quản lý EVS
EVS Manager (/packages/services/Car/evs/manager
) cung cấp các khối xây dựng mà một ứng dụng EVS cần để triển khai mọi thứ, từ màn hình camera chiếu hậu đơn giản đến chế độ kết xuất nhiều camera 6DOF. Giao diện của dịch vụ này được trình bày thông qua HIDL và được thiết kế để chấp nhận nhiều ứng dụng đồng thời.
Các ứng dụng và dịch vụ khác (cụ thể là Car Service) có thể truy vấn trạng thái EVS Manager để biết thời điểm hệ thống EVS đang hoạt động.
Giao diện HIDL của EVS
Hệ thống EVS, cả camera và các phần tử hiển thị, đều được xác định trong gói android.hardware.automotive.evs
. Một mẫu triển khai sử dụng giao diện (tạo hình ảnh kiểm thử tổng hợp và xác thực hình ảnh thực hiện chuyến khứ hồi) được cung cấp trong /hardware/interfaces/automotive/evs/1.0/default
.
OEM chịu trách nhiệm triển khai API được biểu thị bằng các tệp .hal trong /hardware/interfaces/automotive/evs
. Những hoạt động triển khai như vậy chịu trách nhiệm định cấu hình và thu thập dữ liệu từ camera thực, đồng thời phân phối dữ liệu đó thông qua các vùng đệm bộ nhớ dùng chung mà Gralloc có thể nhận dạng. Phía hiển thị của quá trình triển khai chịu trách nhiệm cung cấp một vùng đệm bộ nhớ dùng chung mà ứng dụng có thể điền (thường là bằng cách kết xuất EGL) và trình bày các khung hình đã hoàn tất thay vì bất kỳ nội dung nào khác có thể xuất hiện trên màn hình thực. Các hoạt động triển khai giao diện EVS của nhà cung cấp có thể được lưu trữ trong /vendor/… /device/…
hoặc hardware/…
(ví dụ:
/hardware/[vendor]/[platform]/evs
).
Trình điều khiển kernel
Thiết bị hỗ trợ ngăn xếp EVS cần có trình điều khiển nhân. Thay vì tạo trình điều khiển mới, OEM có thể hỗ trợ các tính năng bắt buộc của EVS thông qua trình điều khiển phần cứng camera hoặc màn hình hiện có. Việc dùng lại trình điều khiển có thể mang lại lợi ích, đặc biệt là đối với trình điều khiển màn hình, trong đó việc trình bày hình ảnh có thể yêu cầu phối hợp với các luồng đang hoạt động khác. Android 8.0 có một trình điều khiển mẫu dựa trên v4l2 (trong packages/services/Car/evs/sampleDriver
) phụ thuộc vào nhân để hỗ trợ v4l2 và phụ thuộc vào SurfaceFlinger để trình bày hình ảnh đầu ra.
Nội dung mô tả giao diện phần cứng EVS
Phần này mô tả HAL. Các nhà cung cấp dự kiến sẽ cung cấp các cách triển khai API này cho phù hợp với phần cứng của họ.
IEvsEnumerator
Đối tượng này chịu trách nhiệm liệt kê phần cứng EVS có sẵn trong hệ thống (một hoặc nhiều camera và một thiết bị hiển thị).
getCameraList() generates (vec<CameraDesc> cameras);
Trả về một vectơ chứa nội dung mô tả cho tất cả camera trong hệ thống. Giả sử bộ camera được cố định và có thể biết được tại thời điểm khởi động. Để biết thông tin chi tiết về nội dung mô tả camera, hãy xem phần CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Lấy một đối tượng giao diện dùng để tương tác với một camera cụ thể được xác định bằng chuỗi camera_id duy nhất. Trả về giá trị NULL khi không thành công.
Không thể thất bại khi cố gắng mở lại một camera đã mở. Để tránh các điều kiện xung đột liên quan đến quá trình khởi động và tắt ứng dụng, việc mở lại camera sẽ tắt phiên bản trước đó để yêu cầu mới có thể được thực hiện. Một thực thể camera đã bị ưu tiên theo cách này phải được đặt ở trạng thái không hoạt động, chờ huỷ hoàn toàn và phản hồi mọi yêu cầu ảnh hưởng đến trạng thái camera bằng mã trả về OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Giải phóng giao diện IEvsCamera (và ngược lại với lệnh gọi openCamera()
). Bạn phải dừng luồng video từ camera bằng cách gọi stopVideoStream()
trước khi gọi closeCamera
.
openDisplay() generates (IEvsDisplay display);
Lấy một đối tượng giao diện dùng để tương tác độc quyền với màn hình EVS của hệ thống. Mỗi lần, chỉ một ứng dụng có thể giữ một phiên bản chức năng của IEvsDisplay. Tương tự như hành vi mở chủ động được mô tả trong openCamera
, đối tượng IEvsDisplay mới có thể được tạo bất cứ lúc nào và vô hiệu hoá mọi thực thể trước đó. Các thực thể không hợp lệ vẫn tồn tại và phản hồi các lệnh gọi hàm từ chủ sở hữu của chúng, nhưng không được thực hiện các thao tác đột biến khi không hoạt động. Cuối cùng, ứng dụng khách dự kiến sẽ nhận thấy mã trả về lỗi OWNERSHIP_LOST
, đồng thời đóng và phát hành giao diện không hoạt động.
closeDisplay(IEvsDisplay display);
Giải phóng giao diện IEvsDisplay (và ngược lại với lệnh gọi openDisplay()
). Bạn phải trả lại các vùng đệm chưa xử lý nhận được bằng lệnh gọi getTargetBuffer()
cho màn hình trước khi đóng màn hình.
getDisplayState() generates (DisplayState state);
Lấy trạng thái hiển thị hiện tại. Quá trình triển khai HAL phải báo cáo trạng thái hiện tại thực tế, có thể khác với trạng thái được yêu cầu gần đây nhất.
Logic chịu trách nhiệm thay đổi trạng thái hiển thị phải nằm phía trên lớp thiết bị, khiến việc triển khai HAL tự động thay đổi trạng thái hiển thị là không mong muốn. Nếu hiện không có ứng dụng nào giữ màn hình (bằng lệnh gọi đến openDisplay), thì hàm này sẽ trả về NOT_OPEN
. Nếu không, nó sẽ báo cáo trạng thái hiện tại của Màn hình EVS (xem IEvsDisplay API).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
camera_id
. Một chuỗi xác định duy nhất một camera nhất định. Có thể là tên thiết bị của nhân hoặc tên của thiết bị, chẳng hạn như rearview. Giá trị cho chuỗi này do quá trình triển khai HAL chọn và được ngăn xếp ở trên sử dụng một cách không rõ ràng.vendor_flags
. Một phương thức để truyền thông tin camera chuyên biệt một cách không rõ ràng từ trình điều khiển đến một ứng dụng EVS tuỳ chỉnh. Thông tin này được truyền mà không cần diễn giải từ trình điều khiển lên ứng dụng EVS. Ứng dụng EVS có thể bỏ qua thông tin này.
IEvsCamera
Đối tượng này đại diện cho một camera duy nhất và là giao diện chính để chụp ảnh.
getCameraInfo() generates (CameraDesc info);
Trả về CameraDesc
của camera này.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Chỉ định độ sâu của chuỗi vùng đệm mà camera được yêu cầu hỗ trợ. Khách hàng của IEvsCamera có thể giữ tối đa số khung hình này cùng một lúc. Nếu số lượng khung hình này đã được gửi đến receiver mà không được doneWithFrame
trả về, thì luồng sẽ bỏ qua các khung hình cho đến khi một bộ đệm được trả về để sử dụng lại. Lệnh gọi này có thể được thực hiện bất cứ lúc nào, ngay cả khi các luồng đang chạy. Trong trường hợp đó, các vùng đệm sẽ được thêm hoặc xoá khỏi chuỗi một cách thích hợp. Nếu không có lệnh gọi nào được thực hiện đến điểm nhập này, thì IEvsCamera sẽ hỗ trợ ít nhất một khung hình theo mặc định; với nhiều khung hình hơn.
Nếu không thể đáp ứng bufferCount được yêu cầu, hàm sẽ trả về BUFFER_NOT_AVAILABLE
hoặc mã lỗi thích hợp khác. Trong trường hợp này, hệ thống sẽ tiếp tục hoạt động với giá trị đã đặt trước đó.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Yêu cầu gửi các khung hình camera qua EVS từ camera này. IEvsCameraStream bắt đầu nhận các lệnh gọi định kỳ bằng khung hình ảnh mới cho đến khi stopVideoStream()
được gọi. Các khung hình phải bắt đầu được phân phối trong vòng 500 mili giây kể từ lệnh gọi startVideoStream
và sau khi bắt đầu, phải được tạo ở tốc độ tối thiểu là 10 FPS. Thời gian cần thiết để bắt đầu luồng video được tính vào mọi yêu cầu về thời gian khởi động camera chiếu hậu. Nếu luồng chưa bắt đầu, bạn phải trả về mã lỗi; nếu không, OK sẽ được trả về.
oneway doneWithFrame(BufferDesc buffer);
Trả về một khung hình được IEvsCameraStream gửi đến. Khi hoàn tất việc sử dụng một khung hình được gửi đến giao diện IEvsCameraStream, khung hình đó phải được trả về IEvsCamera để sử dụng lại. Một số lượng nhỏ, hữu hạn các vùng đệm có sẵn (có thể chỉ có một vùng đệm) và nếu hết nguồn cung, sẽ không có khung hình nào khác được phân phối cho đến khi một vùng đệm được trả về, có thể dẫn đến việc bỏ qua khung hình (vùng đệm có giá trị rỗng biểu thị phần cuối của luồng và không cần được trả về thông qua hàm này). Trả về OK khi thành công hoặc mã lỗi thích hợp có thể bao gồm INVALID_ARG
hoặc BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Dừng truyền khung hình camera EVS. Vì quá trình phân phối là không đồng bộ, nên các khung hình có thể tiếp tục đến trong một khoảng thời gian sau khi lệnh gọi này trả về. Mỗi khung hình phải được trả về cho đến khi việc đóng luồng được báo hiệu cho IEvsCameraStream. Bạn có thể gọi stopVideoStream
trên một luồng đã dừng hoặc chưa bao giờ bắt đầu. Trong những trường hợp đó, lệnh gọi này sẽ bị bỏ qua.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Yêu cầu thông tin cụ thể về trình điều khiển từ quá trình triển khai HAL. Các giá trị được phép cho opaqueIdentifier
là dành riêng cho trình điều khiển, nhưng không có giá trị nào được truyền có thể khiến trình điều khiển gặp sự cố. Trình điều khiển phải trả về 0 cho mọi opaqueIdentifier
không xác định.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Gửi một giá trị dành riêng cho trình điều khiển đến quá trình triển khai HAL. Tiện ích này chỉ được cung cấp để hỗ trợ các tiện ích dành riêng cho xe và không có hoạt động triển khai HAL nào yêu cầu lệnh gọi này hoạt động ở trạng thái mặc định. Nếu trình điều khiển nhận dạng và chấp nhận các giá trị, thì OK sẽ được trả về; nếu không, INVALID_ARG
hoặc mã lỗi đại diện khác sẽ được trả về.
struct BufferDesc { uint32 width; // Units of pixels uint32 height; // Units of pixels uint32 stride; // Units of pixels uint32 pixelSize; // Size of single pixel in bytes uint32 format; // May contain values from android_pixel_format_t uint32 usage; // May contain values from Gralloc.h uint32 bufferId; // Opaque value handle memHandle; // gralloc memory buffer handle }
Mô tả một hình ảnh được truyền qua API. Ổ đĩa HAL chịu trách nhiệm điền vào cấu trúc này để mô tả vùng đệm hình ảnh và ứng dụng HAL phải coi cấu trúc này là chỉ đọc. Các trường này chứa đủ thông tin để cho phép ứng dụng khách tạo lại một đối tượng ANativeWindowBuffer
, khi có thể cần thiết để sử dụng hình ảnh với EGL bằng tiện ích eglCreateImageKHR()
.
width
. Chiều rộng của hình ảnh được trình bày, tính bằng pixel.height
. Chiều cao của hình ảnh được trình bày, tính bằng pixel.stride
. Số lượng pixel mà mỗi hàng thực sự chiếm dụng trong bộ nhớ, tính cả mọi khoảng đệm để căn chỉnh các hàng. Được biểu thị bằng pixel để khớp với quy ước mà gralloc áp dụng cho nội dung mô tả bộ đệm.pixelSize
. Số byte mà mỗi pixel riêng lẻ chiếm, cho phép tính toán kích thước tính bằng byte cần thiết để chuyển đổi giữa các hàng trong hình ảnh (stride
tính bằng byte =stride
tính bằng pixel *pixelSize
).format
. Định dạng pixel mà hình ảnh sử dụng. Định dạng được cung cấp phải tương thích với việc triển khai OpenGL của nền tảng. Để vượt qua kiểm thử khả năng tương thích, bạn nên dùngHAL_PIXEL_FORMAT_YCRCB_420_SP
cho việc sử dụng camera và nên dùngRGBA
hoặcBGRA
cho màn hình.usage
. Cờ sử dụng do quá trình triển khai HAL đặt. Các ứng dụng HAL dự kiến sẽ truyền những cờ này mà không sửa đổi (để biết thông tin chi tiết, hãy tham khảo các cờ liên quan đếnGralloc.h
).bufferId
. Một giá trị duy nhất do quá trình triển khai HAL chỉ định để cho phép nhận dạng bộ đệm sau một chuyến khứ hồi thông qua các API HAL. Việc triển khai HAL có thể tuỳ ý chọn giá trị được lưu trữ trong trường này.memHandle
. Xử lý cho vùng đệm bộ nhớ cơ bản chứa dữ liệu hình ảnh. Quá trình triển khai HAL có thể chọn lưu trữ một mã nhận dạng vùng đệm Gralloc tại đây.
IEvsCameraStream
Ứng dụng triển khai giao diện này để nhận các lượt phân phối khung hình video không đồng bộ.
deliverFrame(BufferDesc buffer);
Nhận các lệnh gọi từ HAL mỗi khi một khung hình video sẵn sàng để kiểm tra.
Các thao tác với bộ đệm nhận được bằng phương thức này phải được trả về thông qua các lệnh gọi đến IEvsCamera::doneWithFrame()
. Khi luồng video bị dừng bằng lệnh gọi đến IEvsCamera::stopVideoStream()
, lệnh gọi lại này có thể tiếp tục khi quy trình truyền dữ liệu được xả. Mỗi khung hình vẫn phải được trả về; khi khung hình cuối cùng trong luồng đã được phân phối, một bufferHandle
NULL sẽ được phân phối, cho biết luồng đã kết thúc và không có thêm khung hình nào được phân phối nữa. Bản thân bufferHandle
NULL không cần được gửi lại bằng doneWithFrame()
, nhưng tất cả các đối tượng khác phải được trả về
Mặc dù có thể sử dụng các định dạng bộ đệm độc quyền, nhưng việc kiểm thử khả năng tương thích yêu cầu bộ đệm phải ở một trong 4 định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Định dạng đã chọn phải là một nguồn kết cấu GL hợp lệ trên chế độ triển khai GLES của nền tảng.
Ứng dụng không được dựa vào bất kỳ mối tương ứng nào giữa trường bufferId
và memHandle
trong cấu trúc BufferDesc
. Về cơ bản, các giá trị bufferId
là riêng tư đối với quá trình triển khai trình điều khiển HAL và có thể sử dụng (và sử dụng lại) các giá trị này nếu thấy phù hợp.
IEvsDisplay
Đối tượng này đại diện cho màn hình Evs, kiểm soát trạng thái của màn hình và xử lý việc trình bày hình ảnh thực tế.
getDisplayInfo() generates (DisplayDesc info);
Trả về thông tin cơ bản về màn hình EVS do hệ thống cung cấp (xem DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Đặt trạng thái hiển thị. Các ứng dụng có thể đặt trạng thái hiển thị để thể hiện trạng thái mong muốn và việc triển khai HAL phải chấp nhận một cách thích hợp yêu cầu cho bất kỳ trạng thái nào trong khi ở bất kỳ trạng thái nào khác, mặc dù phản hồi có thể là bỏ qua yêu cầu.
Khi khởi chạy, màn hình được xác định là bắt đầu ở trạng thái NOT_VISIBLE
, sau đó ứng dụng dự kiến sẽ yêu cầu trạng thái VISIBLE_ON_NEXT_FRAME
và bắt đầu cung cấp video. Khi không còn cần hiển thị, ứng dụng dự kiến sẽ yêu cầu trạng thái NOT_VISIBLE
sau khi truyền khung hình video cuối cùng.
Bạn có thể yêu cầu bất kỳ trạng thái nào vào bất cứ lúc nào. Nếu màn hình đã hiển thị, thì màn hình đó sẽ vẫn hiển thị nếu bạn đặt thành VISIBLE_ON_NEXT_FRAME
. Luôn trả về OK, trừ phi trạng thái được yêu cầu là một giá trị enum không xác định. Trong trường hợp đó, INVALID_ARG
sẽ được trả về.
getDisplayState() generates (DisplayState state);
Lấy trạng thái hiển thị. Quá trình triển khai HAL phải báo cáo trạng thái hiện tại thực tế, có thể khác với trạng thái được yêu cầu gần đây nhất. Logic chịu trách nhiệm thay đổi trạng thái hiển thị phải nằm phía trên lớp thiết bị, khiến việc triển khai HAL tự động thay đổi trạng thái hiển thị là không mong muốn.
getTargetBuffer() generates (handle bufferHandle);
Trả về một handle cho vùng đệm khung liên kết với màn hình. Bộ đệm này có thể bị khoá và được ghi bởi phần mềm và/hoặc GL. Vùng đệm này phải được trả về bằng lệnh gọi đến returnTargetBufferForDisplay()
ngay cả khi màn hình không còn hiển thị nữa.
Mặc dù có thể sử dụng các định dạng bộ nhớ đệm độc quyền, nhưng kiểm thử khả năng tương thích yêu cầu bộ nhớ đệm phải ở một trong 4 định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Định dạng đã chọn phải là mục tiêu kết xuất GL hợp lệ trên chế độ triển khai GLES của nền tảng.
Khi có lỗi, một vùng đệm có giá trị rỗng sẽ được trả về, nhưng bạn không cần phải truyền vùng đệm đó trở lại returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Cho biết vùng đệm đã sẵn sàng hiển thị. Chỉ những vùng đệm được truy xuất thông qua lệnh gọi đến getTargetBuffer()
mới hợp lệ để sử dụng với lệnh gọi này và ứng dụng khách không được sửa đổi nội dung của BufferDesc
. Sau lệnh gọi này, vùng đệm sẽ không còn hợp lệ để ứng dụng khách sử dụng nữa. Trả về OK khi thành công hoặc mã lỗi thích hợp có thể bao gồm INVALID_ARG
hoặc BUFFER_NOT_AVAILABLE
.
struct DisplayDesc { string display_id; int32 vendor_flags; // Opaque value }
Mô tả các thuộc tính cơ bản của màn hình EVS và do quá trình triển khai EVS yêu cầu. HAL chịu trách nhiệm điền thông tin vào cấu trúc này để mô tả màn hình EVS. Có thể là màn hình thực hoặc màn hình ảo được phủ lên hoặc kết hợp với một thiết bị trình chiếu khác.
display_id
. Một chuỗi xác định duy nhất màn hình. Đây có thể là tên thiết bị của nhân hoặc tên của thiết bị, chẳng hạn như rearview. Giá trị cho chuỗi này do quá trình triển khai HAL chọn và được ngăn xếp ở trên sử dụng một cách không rõ ràng.vendor_flags
. Một phương thức để truyền thông tin camera chuyên biệt một cách không rõ ràng từ trình điều khiển đến một Ứng dụng EVS tuỳ chỉnh. Thông tin này được truyền mà không cần diễn giải từ trình điều khiển lên ứng dụng EVS. Ứng dụng EVS có thể bỏ qua thông tin này.
enum DisplayState : uint32 { NOT_OPEN, // Display has not been “opened” yet NOT_VISIBLE, // Display is inhibited VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame VISIBLE, // Display is currently active DEAD, // Display is not available. Interface should be closed }
Mô tả trạng thái của màn hình EVS, có thể là đã tắt (không hiển thị cho người lái xe) hoặc đã bật (hiển thị hình ảnh cho người lái xe).
Bao gồm một trạng thái tạm thời khi màn hình chưa hiển thị nhưng đã chuẩn bị để hiển thị khi phân phối khung hình ảnh tiếp theo bằng lệnh gọi returnTargetBufferForDisplay()
.
Trình quản lý EVS
EVS Manager cung cấp giao diện công khai cho hệ thống EVS để thu thập và trình bày chế độ xem camera bên ngoài. Trong trường hợp trình điều khiển phần cứng chỉ cho phép một giao diện đang hoạt động cho mỗi tài nguyên (camera hoặc màn hình), EVS Manager sẽ tạo điều kiện cho quyền truy cập dùng chung vào camera. Một ứng dụng EVS chính là ứng dụng khách đầu tiên của Trình quản lý EVS và là ứng dụng khách duy nhất được phép ghi dữ liệu hiển thị (các ứng dụng khách khác có thể được cấp quyền chỉ đọc đối với hình ảnh camera).
EVS Manager triển khai cùng một API như các trình điều khiển HAL cơ bản và cung cấp dịch vụ mở rộng bằng cách hỗ trợ nhiều ứng dụng đồng thời (nhiều ứng dụng có thể mở camera thông qua EVS Manager và nhận luồng video).

Các ứng dụng không thấy có sự khác biệt khi hoạt động thông qua việc triển khai HAL phần cứng EVS hoặc EVS Manager API, ngoại trừ việc EVS Manager API cho phép truy cập đồng thời vào luồng camera. Bản thân EVS Manager là một ứng dụng khách được phép của lớp HAL Phần cứng EVS và đóng vai trò là một proxy cho HAL Phần cứng EVS.
Các phần sau đây chỉ mô tả những lệnh gọi có hành vi khác (mở rộng) trong quá trình triển khai Trình quản lý EVS; các lệnh gọi còn lại giống với nội dung mô tả HAL EVS.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Lấy một đối tượng giao diện dùng để tương tác với một camera cụ thể được xác định bằng chuỗi camera_id duy nhất. Trả về giá trị NULL khi không thành công.
Ở lớp Trình quản lý EVS, miễn là có đủ tài nguyên hệ thống, một quy trình khác có thể mở lại camera đã mở, cho phép truyền trực tiếp luồng video đến nhiều ứng dụng tiêu dùng. Các chuỗi camera_id
ở lớp Trình quản lý EVS giống với các chuỗi được báo cáo cho lớp Phần cứng EVS.
IEvsCamera
Chế độ triển khai IEvsCamera do EVS Manager cung cấp được ảo hoá nội bộ để các thao tác trên camera của một ứng dụng không ảnh hưởng đến các ứng dụng khác. Các ứng dụng này vẫn giữ quyền truy cập độc lập vào camera của mình.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Bắt đầu truyền phát video. Các ứng dụng có thể độc lập bắt đầu và dừng luồng video trên cùng một camera cơ bản. Camera cơ bản sẽ khởi động khi ứng dụng khách đầu tiên khởi động.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Trả về một khung hình. Mỗi ứng dụng phải trả lại các khung hình khi hoàn tất, nhưng được phép giữ lại các khung hình đó trong thời gian tuỳ ý. Khi số khung hình mà một ứng dụng lưu giữ đạt đến giới hạn đã định cấu hình, ứng dụng đó sẽ không nhận thêm khung hình nào cho đến khi trả về một khung hình. Hiện tượng bỏ qua khung hình này không ảnh hưởng đến các ứng dụng khác, những ứng dụng này vẫn tiếp tục nhận được tất cả khung hình như mong đợi.
stopVideoStream();
Dừng luồng video. Mỗi ứng dụng có thể dừng luồng video bất cứ lúc nào mà không ảnh hưởng đến các ứng dụng khác. Luồng camera cơ bản ở lớp phần cứng sẽ dừng khi ứng dụng khách cuối cùng của một camera nhất định dừng luồng của camera đó.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Gửi một giá trị dành riêng cho trình điều khiển, có thể cho phép một ứng dụng ảnh hưởng đến một ứng dụng khác. Vì EVS Manager không thể hiểu được ý nghĩa của các từ điều khiển do nhà cung cấp xác định, nên các từ này không được ảo hoá và mọi tác dụng phụ đều áp dụng cho tất cả các ứng dụng của một camera nhất định. Ví dụ: nếu một nhà cung cấp sử dụng lệnh gọi này để thay đổi tốc độ khung hình, thì tất cả các ứng dụng của camera lớp phần cứng bị ảnh hưởng sẽ nhận được khung hình ở tốc độ mới.
IEvsDisplay
Chỉ được phép có một chủ sở hữu màn hình, ngay cả ở cấp Trình quản lý EVS. Trình quản lý không thêm chức năng nào và chỉ truyền trực tiếp giao diện IEvsDisplay thông qua việc triển khai HAL cơ bản.
Ứng dụng EVS
Android có một hoạt động triển khai tham chiếu C++ gốc của ứng dụng EVS giao tiếp với Trình quản lý EVS và HAL của Xe để cung cấp các chức năng cơ bản của camera chiếu hậu. Ứng dụng dự kiến sẽ khởi động rất sớm trong quá trình khởi động hệ thống, với video phù hợp xuất hiện tuỳ thuộc vào các camera hiện có và trạng thái của ô tô (trạng thái số và đèn báo rẽ). Các nhà sản xuất thiết bị gốc có thể sửa đổi hoặc thay thế ứng dụng EVS bằng logic và bản trình bày dành riêng cho xe của họ.


Vì dữ liệu hình ảnh được trình bày cho ứng dụng trong một vùng đệm đồ hoạ tiêu chuẩn, nên ứng dụng có trách nhiệm di chuyển hình ảnh từ vùng đệm nguồn vào vùng đệm đầu ra. Mặc dù điều này làm tăng chi phí sao chép dữ liệu, nhưng nó cũng mang đến cơ hội để ứng dụng kết xuất hình ảnh vào vùng đệm hiển thị theo bất kỳ cách nào mà ứng dụng muốn.
Ví dụ: ứng dụng có thể chọn di chuyển chính dữ liệu pixel, có thể là bằng thao tác xoay hoặc thu phóng nội tuyến. Ứng dụng cũng có thể chọn dùng hình ảnh nguồn làm kết cấu OpenGL và kết xuất một cảnh phức tạp vào bộ đệm đầu ra, bao gồm cả các phần tử ảo như biểu tượng, đường hướng dẫn và ảnh động. Một ứng dụng phức tạp hơn cũng có thể chọn nhiều camera đầu vào đồng thời và hợp nhất chúng thành một khung đầu ra duy nhất (chẳng hạn như để sử dụng trong chế độ xem ảo từ trên xuống của môi trường xung quanh xe).
Sử dụng EGL/SurfaceFlinger trong HAL Hiển thị EVS
Phần này giải thích cách sử dụng EGL để kết xuất một chế độ triển khai HAL Hiển thị EVS trong Android 10.
Hoạt động triển khai tham chiếu EVS HAL sử dụng EGL để kết xuất bản xem trước của camera trên màn hình và sử dụng libgui
để tạo bề mặt kết xuất EGL mục tiêu. Trong Android 8 (trở lên), libgui
được phân loại là VNDK-private, tức là một nhóm thư viện có sẵn cho các thư viện VNDK mà các quy trình của nhà cung cấp không thể sử dụng.
Vì các triển khai HAL phải nằm trong phân vùng nhà cung cấp, nên các nhà cung cấp không được phép sử dụng Surface trong các triển khai HAL.
Tạo libgui cho các quy trình của nhà cung cấp
Việc sử dụng libgui
là lựa chọn duy nhất để sử dụng EGL/SurfaceFlinger trong các triển khai HAL Hiển thị EVS. Cách đơn giản nhất để triển khai libgui
là thông qua frameworks/native/libs/gui trực tiếp bằng cách sử dụng một mục tiêu bản dựng bổ sung trong tập lệnh bản dựng. Mục tiêu này hoàn toàn giống với mục tiêu libgui
, ngoại trừ việc bổ sung hai trường:
name
vendor_available
cc_library_shared { name: "libgui_vendor", vendor_available: true, vndk: { enabled: false, }, double_loadable: true,
defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …
Lưu ý: Mục tiêu của nhà cung cấp được tạo bằng macro NO_INPUT
, macro này sẽ xoá một từ 32 bit khỏi dữ liệu gói. Vì SurfaceFlinger mong đợi trường này đã bị xoá, nên SurfaceFlinger không phân tích cú pháp được gói. Điều này được ghi nhận là lỗi fcntl
:
W Parcel : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list E Parcel : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647 W Parcel : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list
Cách giải quyết vấn đề này:
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 6066421fa..25cf5f0ce 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const output.writeFloat(color.b); #ifndef NO_INPUT inputInfo.write(output); +#else + // Write a dummy 32-bit word. + output.writeInt32(0); #endif output.write(transparentRegion); output.writeUint32(transform);
Dưới đây là hướng dẫn xây dựng mẫu. Bạn có thể nhận được $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so
.
$ cd <your_android_source_tree_top> $ . ./build/envsetup. $ lunch <product_name>-<build_variant> ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=10 TARGET_PRODUCT=<product_name> TARGET_BUILD_VARIANT=<build_variant> TARGET_BUILD_TYPE=release TARGET_ARCH=arm64 TARGET_ARCH_VARIANT=armv8-a TARGET_CPU_VARIANT=generic TARGET_2ND_ARCH=arm TARGET_2ND_ARCH_VARIANT=armv7-a-neon TARGET_2ND_CPU_VARIANT=cortex-a9 HOST_ARCH=x86_64 HOST_2ND_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=<host_linux_version> HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release BUILD_ID=QT OUT_DIR=out ============================================
$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so
Sử dụng liên kết trong quá trình triển khai HAL EVS
Trong Android 8 (trở lên), nút thiết bị /dev/binder
chỉ dành riêng cho các quy trình khung và do đó, các quy trình của nhà cung cấp không thể truy cập vào nút này. Thay vào đó, các quy trình của nhà cung cấp nên sử dụng /dev/hwbinder
và phải chuyển đổi mọi giao diện AIDL sang HIDL. Đối với những người muốn tiếp tục sử dụng các giao diện AIDL giữa các quy trình của nhà cung cấp, hãy sử dụng miền liên kết /dev/vndbinder
.
Miền IPC | Mô tả |
---|---|
/dev/binder |
IPC giữa các quy trình khung/ứng dụng bằng giao diện AIDL |
/dev/hwbinder |
IPC giữa các quy trình của khung/nhà cung cấp với các giao diện HIDL IPC giữa các quy trình của nhà cung cấp với cá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 bằng Giao diện AIDL |
Mặc dù SurfaceFlinger xác định các giao diện AIDL, nhưng các quy trình của nhà cung cấp chỉ có thể sử dụng các giao diện HIDL để giao tiếp với các quy trình của khung. Bạn cần phải thực hiện một lượng công việc đáng kể để chuyển đổi các giao diện AIDL hiện có thành HIDL. Rất may, Android cung cấp một phương thức để chọn trình điều khiển liên kết cho libbinder
, mà các quy trình thư viện không gian người dùng được liên kết.
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp index d8fb3166..5fd02935 100644 --- a/evs/sampleDriver/service.cpp +++ b/evs/sampleDriver/service.cpp @@ -21,6 +21,7 @@ #include <utils/Errors.h> #include <utils/StrongPointer.h> #include <utils/Log.h> +#include <binder/ProcessState.h> #include "ServiceNames.h" #include "EvsEnumerator.h" @@ -43,6 +44,9 @@ using namespace android; int main() { ALOGI("EVS Hardware Enumerator service is starting"); + // Use /dev/binder for SurfaceFlinger + ProcessState::initWithDriver("/dev/binder"); + // Start a thread to listen to video device addition events. std::atomic<bool> running { true }; std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
Lưu ý: Các quy trình của nhà cung cấp nên gọi hàm này trước khi gọi vào Process
hoặc IPCThreadState
, hoặc trước khi thực hiện bất kỳ lệnh gọi binder nào.
Chính sách SELinux
Nếu quá trình triển khai thiết bị là treble đầy đủ, SELinux sẽ ngăn các quy trình của nhà cung cấp sử dụng /dev/binder
. Ví dụ: một chế độ triển khai mẫu HAL EVS được chỉ định cho miền hal_evs_driver
và yêu cầu quyền đọc/ghi đối với miền binder_device
.
W ProcessState: Opening '/dev/binder' failed: Permission denied F ProcessState: Binder driver could not be opened. Terminating. F libc : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar) W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0
Tuy nhiên, việc thêm các quyền này sẽ khiến bản dựng không thành công vì vi phạm các quy tắc neverallow sau đây được xác định trong system/sepolicy/domain.te
cho một thiết bị có cấu trúc ba lớp đầy đủ.
libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write }; libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(` neverallow { domain -coredomain -appdomain -binder_in_vendor_violators } binder_device:chr_file rw_file_perms; ')
binder_in_vendor_violators
là một thuộc tính được cung cấp để phát hiện lỗi và hướng dẫn quá trình phát triển. Bạn cũng có thể sử dụng công cụ này để giải quyết lỗi vi phạm Android 10 như mô tả ở trên.
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te index f1f31e9fc..6ee67d88e 100644 --- a/evs/sepolicy/evs_driver.te +++ b/evs/sepolicy/evs_driver.te @@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain; hal_server_domain(hal_evs_driver, hal_evs) hal_client_domain(hal_evs_driver, hal_evs) +# Allow to use /dev/binder +typeattribute hal_evs_driver binder_in_vendor_violators; + # allow init to launch processes in this context type hal_evs_driver_exec, exec_type, file_type, system_file_type; init_daemon_domain(hal_evs_driver)
Tạo bản triển khai tham chiếu HAL EVS dưới dạng quy trình của nhà cung cấp
Để tham khảo, bạn có thể áp dụng các thay đổi sau cho packages/services/Car/evs/Android.mk
. Hãy nhớ xác nhận rằng tất cả các thay đổi được mô tả đều hoạt động cho quá trình triển khai của bạn.
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk index 734feea7d..0d257214d 100644 --- a/evs/sampleDriver/Android.mk +++ b/evs/sampleDriver/Android.mk @@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES := \ android.hardware.automotive.evs@1.0 \ libui \ - libgui \ + libgui_vendor \ libEGL \ libGLESv2 \ libbase \ @@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample +LOCAL_PROPRIETARY_MODULE := true LOCAL_MODULE_TAGS := optional LOCAL_STRIP_MODULE := keep_symbols @@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +LOCAL_CFLAGS += -Iframeworks/native/include #NOTE: It can be helpful, while debugging, to disable optimizations #LOCAL_CFLAGS += -O0 -g diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp index d8fb31669..5fd029358 100644 --- a/evs/sampleDriver/service.cpp +++ b/evs/sampleDriver/service.cpp @@ -21,6 +21,7 @@ #include <utils/Errors.h> #include <utils/StrongPointer.h> #include <utils/Log.h> +#include <binder/ProcessState.h> #include "ServiceNames.h" #include "EvsEnumerator.h" @@ -43,6 +44,9 @@ using namespace android; int main() { ALOGI("EVS Hardware Enumerator service is starting"); + // Use /dev/binder for SurfaceFlinger + ProcessState::initWithDriver("/dev/binder"); + // Start a thread to listen video device addition events. std::atomic<bool> running { true }; std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running)); diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te index f1f31e9fc..632fc7337 100644 --- a/evs/sepolicy/evs_driver.te +++ b/evs/sepolicy/evs_driver.te @@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain; hal_server_domain(hal_evs_driver, hal_evs) hal_client_domain(hal_evs_driver, hal_evs) +# allow to use /dev/binder +typeattribute hal_evs_driver binder_in_vendor_violators; + # allow init to launch processes in this context type hal_evs_driver_exec, exec_type, file_type, system_file_type; init_daemon_domain(hal_evs_driver) @@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms; # Allow the driver to access kobject uevents allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl; + +# Allow the driver to use the binder device +allow hal_evs_driver binder_device:chr_file rw_file_perms;