Lớp trừu tượng phần cứng (HAL) cho camera trên xe

Android chứa một Lớp trừu tượng phần cứng HIDL (HAL) cho ô tô cung cấp tính năng chụp và hiển thị hình ảnh từ sớm trong quá trình khởi động Android và tiếp tục hoạt động trong suốt vòng đời của hệ thống. HAL bao gồm hệ thống nhìn từ bên ngoài (EVS) và thường dùng để hỗ trợ chiếu hậu camera và chế độ xem vòm hiển thị trong xe với tính năng Trong xe dựa trên Android Hệ thống thông tin giải trí (IVI). EVS cũng cho phép triển khai các tính năng nâng cao trong ứng dụng của người dùng.

Android cũng bao gồm trình điều khiển chụp và hiển thị dành riêng cho EVS giao diện (trong /hardware/interfaces/automotive/evs/1.0). Mặc dù xây dựng ứng dụng camera chiếu hậu bên trên thiết bị Android máy ảnh và dịch vụ hiển thị, một ứng dụng như vậy có thể sẽ chạy quá muộn trong quá trình khởi động Android. Việc sử dụng lớp trừu tượng phần cứng chuyên dụng giúp đơn giản hoá giao diện và nêu rõ những gì 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 đây:

Hệ thống xe điện
sơ đồ thành phần
Hình 1. Tổng quan về các thành phần hệ thống EVS.

ứng dụng EVS

Ứng dụng EVS C++ mẫu (/packages/services/Car/evs/app) đóng vai trò là tham chiếu trong quá trình triển khai. Ứng dụng này chịu trách nhiệm yêu cầu khung hình video từ EVS Manager và gửi khung hình hoàn chỉnh để hiển thị trở lại EVS Manager. Dự kiến chương trình này sẽ được bắt đầu hoạt động ngay khi có dịch vụ xe điện và dịch vụ xe hơi (EVS), nhắm mục tiêu trong vòng hai (2) giây kể từ khi bật nguồn. OEM có thể sửa đổi hoặc thay thế EVS ứng dụng như mong muốn.

Trình quản lý EVS

Trình quản lý EVS (/packages/services/Car/evs/manager) cung cấp các thành phần mà ứng dụng EVS cần để triển khai mọi thứ từ một màn hình camera chiếu sau đơn giản kết xuất nhiều camera 6DOF. Giao diện được trình bày thông qua HIDL và được xây dựng để 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à Dịch vụ ô tô) có thể truy vấn EVS Trạng thái của người quản lý để biết khi nào hệ thống EVS hoạt động.

Giao diện EVS HIDL

Hệ thống EVS, cả camera và các thành phần màn hình, được xác định trong Gói android.hardware.automotive.evs. Một phương thức triển khai mẫu để thực hiện giao diện (tạo hình ảnh thử nghiệm tổng hợp và xác thực ảnh chụp khứ hồi) được cung cấp ở /hardware/interfaces/automotive/evs/1.0/default.

OEM chịu trách nhiệm triển khai API được thể hiện trong các tệp .hal trong /hardware/interfaces/automotive/evs. Những cách 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ừ các camera thực và truyền dữ liệu qua vùng đệm bộ nhớ dùng chung mà Gralloc nhận dạng được. Màn hình bộ phận triển khai chịu trách nhiệm cung cấp vùng đệm bộ nhớ dùng chung mà ứng dụng có thể lấp đầy (thường là kết xuất EGL) và trình bày các khung hình hoàn thiện được ưu tiên so với bất kỳ nội dung nào khác có thể muốn xuất hiện trên đó màn hình thực tế. Triển khai giao diện EVS của nhà cung cấp có thể được lưu trữ dưới /vendor/… /device/… hoặc hardware/… (ví dụ: /hardware/[vendor]/[platform]/evs).

Trình điều khiển kernel

Một 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 cần thiết cho EVS thông qua trình điều khiển phần cứng hiển thị hoặc máy ảnh hiện có. Việc sử dụng lại trình điều khiển có thể thuận lợi, đặc biệt là đối với các trình điều khiển hiển thị mà việc trình bày hình ảnh có thể cần phối hợp với các luồng đang hoạt động khác. Android 8.0 có một phiên bản dựa trên v4l2 trình điều khiển mẫu (trong packages/services/Car/evs/sampleDriver) phụ thuộc vào kernel để hỗ trợ v4l2 và SurfaceFlinger để trình bày hình ảnh đầu ra.

Nội dung mô tả về giao diện phần cứng EVS

Phần này mô tả HAL. Nhà cung cấp cần phải cung cấp Các hoạt động triển khai API này được điều chỉnh cho phù hợp với phần cứng của họ.

Hàm IEvsEnumerator

Đối tượng này chịu trách nhiệm liệt kê phần cứng EVS hiện có 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ề vectơ chứa các mô tả của tất cả máy ảnh trong hệ thống. Đó là giả định nhóm camera đã được đặt cố định và có thể xác định được tại thời điểm khởi động. Để biết thông tin chi tiết về phần mô tả camera, hãy xem CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Lấy đối tượng giao diện dùng để tương tác với một máy ảnh 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. Bạn sẽ không thể mở lại máy ảnh đã mở. Để tránh cuộc đua các điều kiện liên quan đến việc khởi động và tắt ứng dụng, mở lại máy ảnh sẽ tắt thực thể trước đó để yêu cầu mới có thể được thực hiện. Đáp thực thể máy ảnh đã bị giành quyền theo cách này phải được đặt trong một phiên bản không hoạt động đang chờ phá huỷ cuối cùng và phản hồi bất kỳ yêu cầu nào để ảnh hưởng đến trạng thái của camera với mã trả về là OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Phát hành giao diện IEvsCamera (và ngược lại với openCamera()). Luồng video của camera phải 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 riêng với Màn hình EVS. Chỉ một ứng dụng có thể lưu giữ phiên bản chức năng của IEvsDisplay tại bất cứ lúc nào. Tương tự như hành vi mở linh hoạt được mô tả trong openCamera, có thể tạo đối tượng IEvsDisplay mới bất cứ lúc nào và vô hiệu hoá mọi đối tượng trước đó thực thể. 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 nhưng không được thực hiện thao tác biến đổi khi đã chết. Cuối cùng, ứng dụng khách dự kiến sẽ nhận thấy lỗi OWNERSHIP_LOST mã trả về, cũng như đóng và giải phóng giao diện không hoạt động.

closeDisplay(IEvsDisplay display);

Phát hành giao diện IEvsDisplay (và ngược lại với openDisplay()). Nhận được vùng đệm còn tồn đọng bằng Lệnh gọi getTargetBuffer() phải được trả về màn hình trước đóng màn hình.

getDisplayState() generates (DisplayState state);

Xem trạng thái hiển thị hiện tại. Việc triển khai HAL sẽ 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 tồn tại ở phía trên thiết bị nên việc triển khai HAL (Lớp trừu tượng phần cứng) có thể thay đổi một cách tự nhiên các trạng thái hiển thị. Nếu màn hình hiện không được bất kỳ ứng dụng nào giữ (bằng lệnh gọi đến openDisplay), thì hàm này sẽ trả về NOT_OPEN. Nếu không, báo cáo trạng thái hiện tại của Màn hình EVS (xem API IEvsDisplay).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id. Chuỗi ký tự xác định duy nhất một camera cụ thể. Có thể là tên thiết bị nhân của thiết bị hoặc tên cho thiết bị, chẳng hạn như quay lại. Giá trị cho chuỗi này do việc triển khai HAL (Lớp trừu tượng phần cứng) chọn và được ngăn xếp ở trên sử dụng một cách rõ ràng.
  • vendor_flags. Phương thức để truyền máy ảnh chuyên dụng thông tin mờ từ người lái xe đến một ứng dụng EVS tuỳ chỉnh. Đã thông qua không diễn giải từ người lái xe cho đến ứng dụng EVS. Bạn có thể bỏ qua điều này nó.

Máy ảnh IEvs

Đối tượng này đại diện cho một máy ảnh và là giao diện chính của 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à máy ảnh được yêu cầu hỗ trợ. Tối đa nhiều khung hình này có thể được ứng dụng khách của IEvsCamera giữ đồng thời. Nếu trường hợp này nhiều khung hình đã được gửi đến trình nhận mà không được trả lại doneWithFrame, luồng bỏ qua các khung cho đến khi một vùng đệm được trả về để tái sử dụng. Bạn được phép thực hiện cuộc gọi này bất cứ lúc nào, kể cả khi bạn đang phát trực tiếp đang chạy, trong trường hợp đó, vùng đệm cần được thêm hoặc xoá khỏi chuỗi khi phù hợp. Nếu không có lệnh gọi nào được thực hiện tới điểm truy cập này, IEvsCamera sẽ hỗ trợ ít nhất một khung hình theo mặc định; dễ chấp nhận hơn.

Nếu không thể đáp ứngBufferCount được yêu cầu, hàm sẽ trả về BUFFER_NOT_AVAILABLE hoặc mã lỗi khác có liên quan. 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 phân phối khung hình máy ảnh qua EVS từ máy ảnh này. IEvsCameraStream bắt đầu nhận cuộc gọi định kỳ với các khung hình ảnh mới cho đến stopVideoStream() sẽ được gọi. Khung hình phải bắt đầu được phân phối trong vòng 500 mili giây từ lệnh gọi startVideoStream và sau khi bắt đầu, phải được tạo ở tốc độ tối thiểu 10 khung hình/giây. Thời gian cần thiết để bắt đầu truyền trực tuyến video tính hiệu quả 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 được bắt đầu, phải trả về mã lỗi; nếu không thì sẽ trả về OK.

oneway doneWithFrame(BufferDesc buffer);

Trả về một khung do IEvsCameraStream phân phối. Khi hoàn tất sử dụng khung được phân phối đến giao diện IEvsCameraStream, khung đó phải quay lại IEvsCamera để sử dụng lại. Một số lượng nhỏ vùng đệm hữu hạn có sẵn (có thể nhỏ bằng một nguồn cung cấp) và nếu nguồn cung đã hết, thì không cần khung hình được phân phối cho đến khi bộ đệm được trả về, có khả năng dẫn đến khung bị bỏ qua (vùng đệm có tay cầm rỗng biểu thị việc kết thúc 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 phân phối khung hình máy ảnh EVS. Vì việc phân phối không đồng bộ, khung hình có thể tiếp tục đến trong một thời gian sau khi lệnh gọi này trở lại. Mỗi khung phải được trả về cho đến khi việc đóng luồng được báo hiệu cho IEvsCameraStream. Việc gọi stopVideoStream trên luồng là hợp pháp đã bị dừng hoặc chưa bao giờ bắt đầu, trong trường hợp đó, giá trị này sẽ bị bỏ qua.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Yêu cầu thông tin cụ thể của người lái xe từ quá trình triển khai HAL (Lớp trừu tượng phần cứng). Giá trị được phép cho opaqueIdentifier tuỳ theo người lái xe, nhưng không có giá trị vượt qua có thể gây tai nạn cho người lái xe. Người lái xe sẽ trả về 0 đối với mọi lỗi mà tài khoản không nhận dạng được opaqueIdentifier.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Gửi giá trị dành riêng cho người lái xe đến quá trình triển khai HAL (Lớp trừu tượng phần cứng). Phần mở rộng này là chỉ được cung cấp để hỗ trợ các tiện ích dành riêng cho xe và không có HAL quá trình triển khai phải 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 ra và chấp nhận các giá trị, trả về OK; nếu không Phải trả về INVALID_ARG hoặc mã lỗi đại diện khác.

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. Ổ 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 ở chế độ chỉ đọc. Các trường chứa đủ thông tin để cho phép ứng dụng tạo lại đối tượng ANativeWindowBuffer, có thể được yêu cầu để sử dụng hình ảnh với EGL với Tiện ích eglCreateImageKHR().

  • width. Chiều rộng tính bằng pixel của hình ảnh được trình bày.
  • height. Chiều cao (tính bằng pixel) của hình ảnh được trình bày.
  • stride. Số pixel mà mỗi hàng thực sự chiếm trong bộ nhớ, có tính đến bất kỳ khoảng đệm nào cho việc căn chỉnh hàng. Được biểu thị bằng pixel để khớp quy ước được gralloc áp dụng để mô tả vùng đệm.
  • pixelSize. Số byte được chiếm bởi mỗi pixel riêng lẻ, cho phép tính toán kích thước theo byte cần thiết để bước 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 cách triển khai OpenGL của nền tảng. Cần vượt qua kiểm tra khả năng tương thích, HAL_PIXEL_FORMAT_YCRCB_420_SP sẽ là được ưu tiên sử dụng cho máy ảnh, và RGBA hoặc BGRA nên được ưu tiên hiển thị.
  • usage. Cờ sử dụng do quá trình triển khai HAL (Lớp trừu tượng phần cứng) thiết lập. Ứng dụng HAL được dự kiến sẽ chuyển những thông số chưa được sửa đổi này (để biết chi tiết, hãy tham khảo Gralloc.h cờ liên quan).
  • bufferId. Một giá trị duy nhất được chỉ định bởi việc triển khai HAL để cho phép nhận dạng vùng đệm sau khi đi khứ hồi thông qua API HAL. Chiến lược phát hành đĩa đơn giá trị được lưu trữ trong trường này có thể được triển khai HAL (Lớp trừu tượng phần cứng) chọn tuỳ ý.
  • memHandle. Xử lý vùng đệm bộ nhớ cơ bản chứa dữ liệu hình ảnh. Việc triển khai HAL có thể chọn lưu trữ Gralloc xử lý vùng đệm tại đây.

IEvsCameraStream

Ứng dụng triển khai giao diện này để nhận khung video không đồng bộ các gói phân phối.

deliverFrame(BufferDesc buffer);

Nhận cuộc gọi từ HAL mỗi khi khung hình video đã sẵn sàng để kiểm tra. Các xử lý vùng đệ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 tới IEvsCamera::doneWithFrame(). Khi luồng video dừng bằng cuộc gọi đến IEvsCamera::stopVideoStream(), lệnh gọi lại này có thể tiếp tục khi đường ống tiêu hao. Hệ thống vẫn phải trả về từng khung; khi khung hình cuối cùng trong luồng đã được phân phối, bufferHandle NULL được phân phối, biểu thị điểm kết thúc luồng và không có thêm khung phân phối nào xảy ra. NULL (Rỗng) Bản thân bufferHandle không cần được gửi lại cùng với doneWithFrame() nhưng phải trả về tất cả các tên người dùng khác

Mặc dù về mặt kỹ thuật, các định dạng vùng đệm thuộc quyền sở hữu riêng nhưng khả năng tương thích yêu cầu vùng đệm phải ở một trong bốn định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Bán phẳng), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Xen kẽ), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Định dạng đã chọn phải là một định dạng hợp lệ Nguồn kết cấu GL trong quá trình triển khai GLES của nền tảng.

Ứng dụng không nên dựa vào bất kỳ thư từ nào giữa trường bufferIdmemHandle trong Cấu trúc BufferDesc. Các giá trị bufferId là về cơ bản là riêng tư đối với việc triển khai trình điều khiển HAL và trình điều khiển này có thể sử dụng (và sử dụng lại) cho phù hợp.

Hiển thị IEvs

Đối tượng này đại diện cho màn hình Evs, điều khiển trạng thái của màn hình, và xử lý cách 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ị. Khách hà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 yêu cầu một cách linh hoạt bất kỳ trạng thái nào khi ở bất kỳ trạng thái nào khác, mặc dù phản hồi có thể là bỏ qua của bạn.

Khi khởi chạy, màn hình được xác định để bắt đầu trong 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 màn hình không còn cần thiết nữa, ứ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.

Việc yêu cầu ở bất kỳ tiểu bang nào tại bất kỳ thời điểm nào cũng có hiệu lực. Nếu màn hình là đã hiển thị, màn hình sẽ vẫn hiển thị nếu được đặ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 là bị trả lại.

getDisplayState() generates (DisplayState state);

Xem trạng thái hiển thị. Việc triển khai HAL sẽ báo cáo trạng thái hiện tại, có thể khác với trạng thái được yêu cầu gần đây nhất. Chiến lược phát hành đĩa đơn logic chịu trách nhiệm thay đổi trạng thái hiển thị phải tồn tại phía trên thiết bị nên việc triển khai HAL (Lớp trừu tượng phần cứng) có thể thay đổi một cách tự nhiên các trạng thái hiển thị.

getTargetBuffer() generates (handle bufferHandle);

Trả về một điều khiển (handler) đến vùng đệm khung liên kết với màn hình. Vùng đệm này có thể đã bị phần mềm và/hoặc GL khoá và ghi vào. Phải trả về vùng đệm này bằng lệnh gọi đến returnTargetBufferForDisplay() ngay cả khi màn hình đang không còn hiển thị.

Mặc dù về mặt kỹ thuật có thể có các định dạng vùng đệm độc quyền nhưng việc kiểm tra khả năng tương thích yêu cầu bộ đệm ở một trong bốn định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Bán phẳng), YV12 (YCrCb 4:2:0 hai chiều), YUYV (YCrCb 4:2:2 xen kẽ), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). Định dạng đã chọn phải là một GL hợp lệ mục tiêu kết xuất trong quá trình triển khai GLES của nền tảng.

Khi xảy ra lỗi, bộ đệm có tay cầm rỗng sẽ được trả về, nhưng bộ đệm như vậy thì không cần được trả về cho returnTargetBufferForDisplay.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Cho màn hình biết vùng đệm đã sẵn sàng để hiển thị. Chỉ truy xuất các vùng đệm thông qua lệnh gọi đến getTargetBuffer() đều hợp lệ để sử dụng với lệnh này lệnh gọi và nội dung của BufferDesc không thể được sửa đổi bằng ứng dụng khách. Sau lệnh gọi này, vùng đệm không còn hợp lệ để dùng khách hàng. Trả về OK khi thành công hoặc có thể có mã lỗi thích hợp bao gồm INVALID_ARG hoặc BUFFER_NOT_AVAILABLE.

struct DisplayDesc {
    string  display_id;
    int32   vendor_flags;  // Opaque value
}

Mô tả các đặc điểm cơ bản của màn hình EVS và theo yêu cầu của EVS trong quá trình triển khai. HAL chịu trách nhiệm điền 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 phủ hoặc kết hợp với một thiết bị trình bày 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ị nhân của thiết bị hoặc tên cho thiết bị, như tính năng quan sát ngược. Giá trị cho chuỗi này do HAL (Lớp trừu tượng phần cứng) chọn và được ngăn xếp ở trên sử dụng một cách mờ đục.
  • vendor_flags. Phương thức để truyền máy ảnh chuyên dụng thông tin mờ từ người lái xe đến một Ứng dụng EVS tuỳ chỉnh. Đã thông qua không diễn giải từ người lái xe cho đến ứng dụng EVS. Bạn có thể bỏ qua điều này nó.
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ể tắt (không phải) 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 đã được chuẩn bị hiển thị với nhau khi phân phối khung hình ảnh tiếp theo với Cuộc gọi returnTargetBufferForDisplay().

Trình quản lý EVS

Trình quản lý EVS cung cấp giao diện công khai cho hệ thống EVS để thu thập và trình bày các khung hiển thị camera bên ngoài. Trường hợp trình điều khiển phần cứng cho phép chỉ một giao diện hoạt động cho mỗi tài nguyên (máy ảnh hoặc màn hình), Trình quản lý EVS tạo điều kiện cho quyền truy cập chung vào camera. Một ứng dụng EVS chính duy nhất là ứng dụng đầu tiên của Trình quản lý EVS và là ứng dụng duy nhất được phép ghi dữ liệu hiển thị (các ứng dụng khác có thể được cấp quyền truy cập chỉ đọc vào máy ảnh hình ảnh).

Trình quản lý EVS triển khai cùng một API như các trình điều khiển HAL (Lớp trừu tượng phần cứng) cơ bản và cung cấp dịch vụ mở rộng bằng cách hỗ trợ nhiều máy khách đồng thời (hơn một khách hàng có thể mở camera thông qua Trình quản lý EVS và nhận video luồng).

Người quản lý EVS và
Sơ đồ API Phần cứng EVS.
Hình 2. Trình quản lý EVS phản ánh các EVS cơ bản Hardware API (API Phần cứng).

Các ứng dụng không thấy có sự khác biệt nào khi vận hành thông qua lớp trừu tượng phần cứng (HAL) cho phần cứng EVS triển khai 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. Trình quản lý EVS là công cụ được phép máy khách của lớp EVS Hardware HAL và đóng vai trò là proxy cho EVS Hardware Lớp trừu tượng phần cứng (HAL).

Các phần sau đây chỉ mô tả những lệnh gọi có (mở rộng) trong quá trình triển khai EVS Manager; các cuộc gọi còn lại là giống với mô tả EVS HAL.

Hàm IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Lấy đối tượng giao diện dùng để tương tác với một máy ảnh 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. Ở tầng Trình quản lý EVS, miễn là có đủ tài nguyên hệ thống, máy ảnh đã mở có thể được mở lại bằng một quy trình khác, cho phép phát trực tuyến video cho nhiều ứng dụng dành cho người tiêu dùng. Chiến lược phát hành đĩa đơn Các chuỗi camera_id ở lớp EVS Manager cũng giống như các chuỗi được báo cáo cho lớp Phần cứng EVS.

Máy ảnh IEvs

Trình quản lý EVS do triển khai IEvsCamera 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, điều này duy trì quyền truy cập độc lập vào máy ảnh của họ.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Bắt đầu truyền trực tuyến video. Ứng dụng có thể bắt đầu và dừng luồng video một cách độc lập trên cùng một camera bên dưới. Máy ảnh bên dưới khởi động khi máy khách của bạn.

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

Trả về một khung. Mỗi ứng dụng phải trả lại khung hình của mình khi hoàn tất, nhưng được phép giữ trên khung hình bao lâu họ muốn. Khi số lượng khung hình do máy khách giữ đạt đến giới hạn đã định cấu hình thì máy khách sẽ không nhận được bất kỳ khung hình nào khác cho đến khi trả về một khung hình. Việc bỏ qua khung này không ảnh hưởng đến các chế độ cài đặt khác để tiếp tục nhận được tất cả các khung hình như mong đợi.

stopVideoStream();

Dừng luồng video. Mỗi khách hàng có thể dừng luồng video của mình bất cứ lúc nào mà không ảnh hưởng đến các ứng dụng khách khác. Luồng camera cơ bản ở lớp phần cứng là đã dừng khi ứng dụng cuối cùng của một camera cụ thể dừng luồng của nó.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Gửi một giá trị dành riêng cho người lái xe, có thể cho phép một ứng dụng tác động một khách hàng khác. Vì Người quản lý EVS không thể hiểu được hệ quả của các từ kiểm soát do nhà cung cấp xác định, không được ảo hoá và có bất kỳ tác dụng phụ nào áp dụng cho tất cả khách hàng của một máy ảnh 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, tất cả ứng dụng khách của máy ảnh lớp phần cứng bị ảnh hưởng sẽ nhận khung hình ở tốc độ mới.

Hiển thị IEvs

Chỉ cho phép một chủ sở hữu của màn hình, ngay cả ở cấp Người quản lý EVS. Chiến lược phát hành đĩa đơn Người quản lý không thêm chức năng nào và chỉ truyền giao diện IEvsDisplay trực tiếp thông qua việc triển khai HAL (Lớp trừu tượng phần cứng) cơ bản.

ứng dụng EVS

Android bao gồm một phương thức triển khai tham chiếu C++ gốc của EVS ứng dụng giao tiếp với Trình quản lý EVS và HAL của xe để cung cấp 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 được hiển thị tuỳ thuộc vào các camera có sẵn và trạng thái của xe (trạng thái bánh răng và tín hiệu rẽ). OEM có thể sửa đổi hoặc thay thế ứng dụng EVS bằng ứng dụng dành riêng cho xe của họ logic và bản trình bày.

Hình 3. Logic mẫu ứng dụng EVS, tải camera danh sách.


Hình 4. Logic mẫu ứng dụng EVS, nhận lệnh gọi lại khung.

Vì dữ liệu hình ảnh được trình bày cho ứng dụng dưới dạng đồ hoạ chuẩn vùng đệm, ứng dụng sẽ chịu trách nhiệm di chuyển hình ảnh từ nguồn vào bộ đệm đầu ra. Mặc dù phương pháp này dẫn đến chi phí của bản sao dữ liệu, nhưng nó cũng mang đến cơ hội để ứng dụng hiển thị hình ảnh vùng đệm hiển thị theo bất kỳ cách nào mong muốn.

Ví dụ: ứng dụng có thể chọn tự di chuyển dữ liệu pixel, có thể bằng thao tác xoay hoặc điều chỉnh theo tỷ lệ cùng dòng. Ứng dụng có thể chọn sử dụng hình ảnh nguồn làm hoạ tiết OpenGL và kết xuất hình ảnh cảnh vào vùng đệm đầu ra, bao gồm cả các phần tử ảo như biểu tượng, nguyên tắc và ảnh động. Một ứng dụng tinh vi hơn cũng có thể chọn nhiều camera đầu vào đồng thời và hợp nhất các camera đó vào 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 bao quanh khu vực xung quanh xe).

Sử dụng EGL/SurfaceFlinger trong HAL Display & Video 360

Phần này giải thích cách sử dụng EGL để kết xuất cách triển khai lớp trừu tượng phần cứng (HAL) cho màn hình EVS trong Android 10.

EVS Quy trình triển khai tham chiếu HAL sử dụng EGL để bật bản xem trước của máy ảnh màn hình và sử dụng libgui để tạo giao diện kết xuất EGL mục tiêu. Trên Android 8 (trở lên), libgui được phân loại là VNDK-riêng tư, tức là một nhóm thư viện có sẵn cho các thư viện VNDK mà quy trình của nhà cung cấp không thể sử dụng. Vì việc triển khai HAL phải nằm trong phân vùng nhà cung cấp nên nhà cung cấp không được sử dụng Nền tảng trong quá trình triển khai HAL (Lớp trừu tượng phần cứng).

Xây dựng 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 triển khai HAL Display & Video 360 EVS. Cách đơn giản nhất để triển khai libgui là qua frameworks/native/libs/gui bằng cách sử dụng 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 thêm 2 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 ý: Các 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 dự kiến trường này đã bị xoá nên SurfaceFlinger không thể phân tích cú pháp lô này. Đây là một 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 tình trạng 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);

Bản dựng mẫu hướng dẫn của chúng tôi như sau. Dự kiến 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 trình liên kết trong quá trình triển khai lớp trừu tượng phần cứng (HAL) qua EVS

Trong Android 8 (trở lên), nút thiết bị /dev/binder trở thành độc quyền cho các quy trình khung và do đó không thể tiếp cận quy trình của nhà cung cấp. Thay vào đó, quy trình của nhà cung cấp phải sử dụng /dev/hwbinder và phải chuyển đổi mọi giao diện AIDL sang HIDL. Nếu 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, 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 có giao diện AIDL
/dev/hwbinder IPC giữa các quy trình khung/nhà cung cấp với 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 bằng giao diện AIDL

Mặc dù SurfaceFlinger định nghĩa giao diện AIDL, nhưng các quy trình của nhà cung cấp chỉ có thể sử dụng giao diện HIDL để giao tiếp với các quy trình khung. Cần một lượng công việc không nhỏ để chuyển đổi Giao diện AIDL thành HIDL. Rất may là Android cung cấp một phương thức để chọn liên kết trình điều khiển cho libbinder mà các quy trình xử lý thư viện không gian người dùng được liên kết với.

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 ý: Quy trình của nhà cung cấp nên gọi lệnh 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 liên kết nào.

Chính sách SELinux

Nếu quá trình triển khai thiết bị ở mức tối đa, SELinux sẽ ngăn nhà cung cấp xử lý bằng cách sử dụng /dev/binder. Ví dụ: mẫu EVS HAL được chỉ định cho miền hal_evs_driver và yêu cầu quyền r/w đố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ẽ gây ra lỗi bản dựng vì quyền này vi phạm những điều sau các quy tắc Neverallow được xác định trong system/sepolicy/domain.te cho thiết bị âm thanh đầ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 phát triển. Bạn cũng có thể dùng công cụ này để giải quyết lỗi vi phạm về 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)

Xây dựng quy trình triển khai tham chiếu lớp trừu tượng phần cứng (HAL) cho EVS theo 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ả nội dung thay đổi được mô tả đều phù hợp với việc 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;