Máy ảnh xe HAL

Android chứa Lớp trừu tượng phần cứng HIDL (HAL) ô tô cung cấp khả năng chụp và hiển thị hình ảnh từ rấ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 ngăn xếp hệ thống quan sát bên ngoài (EVS) và thường được dùng để hỗ trợ camera chiếu hậu và màn hình quan sát xung quanh trên xe có hệ thống Thông tin giải trí trên 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 của người dùng.

Android cũng bao gồm 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ể xây dựng một ứng dụng camera chiếu hậu trên các dịch vụ hiển thị và camera hiện có của Android, nhưng ứ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 HAL chuyên dụng sẽ mang lại giao diện hợp lý và làm 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:

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

ứng dụng EVS

Một ứng dụng C++ EVS mẫu ( /packages/services/Car/evs/app ) đóng vai trò 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 thành để hiển thị lại cho Trình quản lý EVS. Nó dự kiến ​​sẽ được khởi động bằng init ngay khi EVS và Dịch vụ ô tô có sẵn, được nhắm mục tiêu trong vòng hai (2) giây sau khi bật nguồn. OEM có thể sửa đổi hoặc thay thế ứng dụng EVS theo ý muốn.

Người quản lý EVS

Trình quản lý EVS ( /packages/services/Car/evs/manager ) cung cấp các khối xây dựng cần thiết cho ứng dụng EVS để triển khai mọi thứ từ màn hình camera chiếu hậu đơn giản đến kết xuất nhiều camera 6DOF. Giao diện của nó được trình bày thông qua HIDL và được xây dựng để chấp nhận nhiều máy khách đồ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 trạng thái Trình quản lý EVS để tìm hiểu 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 hiển thị, được xác định trong gói android.hardware.automotive.evs . Một 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 hình ảnh thực hiện chuyến đi 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 thể hiện bằng các tệp .hal trong /hardware/interfaces/automotive/evs . Việc 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 vật lý và phân phối dữ liệu đó qua bộ đệm bộ nhớ dùng chung được Gralloc nhận dạng. Phần hiển thị của quá trình triển khai chịu trách nhiệm cung cấp bộ nhớ đệm dùng chung mà ứng dụng có thể lấp đầy (thường thông qua kết xuất EGL) và hiển thị các khung đã hoàn thiện theo sở thích so với bất kỳ thứ gì khác có thể muốn xuất hiện trên màn hình vật lý. Việc 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 hạt nhân

Thiết bị hỗ trợ ngăn xếp EVS yêu cầu trình điều khiển hạt nhân. Thay vì tạo trình điều khiển mới, OEM có tùy chọn 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 máy ảnh và/hoặc màn hình hiện có. Việc sử dụng lại trình điều khiển có thể có lợi, đặc biệt đối với trình điều khiển hiển thị nơi 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 hoạt động khác. Android 8.0 bao gồm trình điều khiển mẫu dựa trên v4l2 (trong packages/services/Car/evs/sampleDriver ) phụ thuộc vào kernel để hỗ trợ v4l2 và trên SurfaceFlinger để hiển thị hình ảnh đầu ra.

Mô tả giao diện phần cứng EVS

Phần mô tả HAL. Các nhà cung cấp dự kiến ​​sẽ cung cấp các triển khai API này được điều chỉnh cho phù hợp với phần cứng của họ.

IevsĐiều tra viên

Đối tượng này có nhiệm vụ liệt kê phần cứng EVS có sẵn trong hệ thống (một hoặc nhiều camera và thiết bị hiển thị duy nhất).

getCameraList() generates (vec<CameraDesc> cameras);

Trả về một vectơ chứa mô tả cho tất cả camera trong hệ thống. Giả định rằng bộ camera đã được cố định và có thể nhận biết được khi khởi động. Để biết chi tiết về mô tả camera, hãy xem CameraDesc .

openCamera(string camera_id) generates (IEvsCamera camera);

Lấy một đối tượng giao diện được sử 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ề NULL khi thất bại. Nỗ lực mở lại camera đã mở không thể thất bại. Để tránh tình trạng tương tranh liên quan đến việc 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 phiên bản máy ảnh đã được ưu tiên theo cách này phải được đặt ở trạng thái không hoạt động, chờ hủy lần cuối và phản hồi mọi yêu cầu tác động đến trạng thái máy ảnh bằng mã trả về là OWNERSHIP_LOST .

closeCamera(IEvsCamera camera);

Phát hành giao diện IEvsCamera (và ngược lại với lệnh gọi openCamera() ). Luồng video camera phải được dừng 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 được sử dụng để tương tác riêng với màn hình EVS của hệ thống. Tại một thời điểm, chỉ một khách hàng có thể giữ phiên bản chức năng của IEvsDisplay. Tương tự như hành vi mở tích cực được mô tả trong openCamera , một đối tượng IEvsDisplay mới có thể được tạo bất kỳ lúc nào và sẽ vô hiệu hóa mọi phiên bản trước đó. Các phiên bản không hợp lệ tiếp tục 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 chết. Cuối cùng, ứng dụng khách sẽ nhận thấy mã trả về lỗi OWNERSHIP_LOST và đó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 lệnh gọi openDisplay() ). Bộ đệm chưa thanh toán nhận được qua lệnh gọi getTargetBuffer() phải được trả lại 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. Việc triển khai HAL phải báo cáo trạng thái hiện tại thực tế, trạng thái này 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 lớp thiết bị, khiến việc triển khai HAL không thể tự động thay đổi trạng thái hiển thị. Nếu màn hình hiện không được giữ bởi bất kỳ ứng dụng khách nào (bằng lệnh gọi tới openDisplay), thì hàm này sẽ trả về NOT_OPEN . Mặt khác, nó sẽ 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 . Một chuỗi xác định duy nhất một máy ảnh nhất định. Có thể là tên thiết bị kernel của thiết bị hoặc tên của thiết bị, chẳng hạn như chiếu hậu . Giá trị cho chuỗi này được chọn bởi quá trình triển khai HAL 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 pháp truyền thông tin camera chuyên dụng một cách mờ đục từ trình điều khiển đến ứng dụng EVS tùy chỉnh. Nó được truyền mà không được giải thích từ trình điều khiển đến ứng dụng EVS, bạn có thể bỏ qua nó mà không cần giải thích.

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 máy ảnh này.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Chỉ định độ sâu của chuỗi đệm mà máy ảnh được yêu cầu hỗ trợ. Nhiều khung hình có thể được giữ đồng thời bởi ứng dụng khách của IEvsCamera. Nếu nhiều khung hình này đã được gửi đến người nhận mà không được trả về bởi doneWithFrame thì luồng sẽ bỏ qua các khung hình cho đến khi bộ đệm được trả về để sử dụng lại. Việc gọi lệnh này đến bất kỳ lúc nào là hợp pháp, ngay cả khi các luồng đang chạy, trong trường hợp đó, bộ đệm sẽ được thêm hoặc xóa khỏi chuỗi nếu thích hợp. Nếu không có cuộc gọi nào được thực hiện tới điểm vào này, IEvsCamera sẽ hỗ trợ ít nhất một khung hình theo mặc định; với mức độ dễ chấp nhận hơn.

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

oneway doneWithFrame(BufferDesc buffer);

Trả về khung được gửi tới IEvsCameraStream. Khi hoàn tất việc sử dụng khung được phân phối tới giao diện IEvsCameraStream, khung đó phải được trả lại cho IEvsCamera để sử dụng lại. Có sẵn một số lượng nhỏ bộ đệm hữu hạn (có thể nhỏ bằng một bộ đệm) và nếu nguồn cung cạn kiệt, sẽ không có khung nào được phân phối nữa cho đến khi bộ đệm được trả về, có khả năng dẫn đến các khung bị bỏ qua (bộ đệm có tay cầm rỗng biểu thị sự kết thúc của luồng và không cần phải trả về thông qua chức năng này). Trả về OK nếu thành công hoặc mã lỗi thích hợp có khả năng bao gồm INVALID_ARG hoặc BUFFER_NOT_AVAILABLE .

stopVideoStream();

Dừng phân phối khung máy ảnh EVS. Vì quá trình phân phối không đồng bộ nên các khung có thể tiếp tục đến một thời gian sau khi lệnh gọi này quay trở lại. Mỗi khung hình phải được trả về cho đến khi việc đóng luồng được báo hiệu tới IEvsCameraStream. Việc gọi stopVideoStream trên luồng đã bị dừng hoặc chưa bao giờ bắt đầu là hợp pháp, trong trường hợp đó, nó 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ừ việc 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 chuyển có thể làm hỏng trình điều khiển. Trình điều khiển phải trả về 0 cho bất kỳ opaqueIdentifier nào không được nhận dạng.

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

Gửi giá trị dành riêng cho trình điều khiển để triển khai HAL. Tiện ích mở rộng này chỉ được cung cấp để tạo điều kiện thuận lợi cho các tiện ích mở rộng dành riêng cho phương tiện và không việc 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 ra và chấp nhận các giá trị, hàm OK sẽ được trả về; nếu không thì 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. Ổ đĩa HAL chịu trách nhiệm điền vào cấu trúc này để mô tả bộ đệm hình ảnh và máy khách HAL sẽ coi cấu trúc này là chỉ đọc. Các trường chứa đủ thông tin để cho phép máy khách xây dựng lại đối tượng ANativeWindowBuffer , vì có thể được yêu cầu sử dụng hình ảnh với EGL thông qua tiện ích mở rộng 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ớ, chiếm bất kỳ phần đệm nào để căn chỉnh các hàng. Được biểu thị bằng pixel để phù hợp với quy ước được gralloc áp dụng cho các mô tả bộ đệm của nó.
  • pixelSize . Số byte mà mỗi pixel riêng lẻ chiếm giữ, cho phép tính toán kích thước tính bằng byte cần thiết để di chuyển giữa các hàng trong hình ảnh ( stride tính bằng byte = stride bằng pixel * pixelSize ).
  • format . Định dạng pixel được 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 tra khả năng tương thích, HAL_PIXEL_FORMAT_YCRCB_420_SP phải được ưu tiên sử dụng cho máy ảnh và RGBA hoặc BGRA phải được ưu tiên cho hiển thị.
  • usage . Cờ sử dụng do quá trình triển khai HAL đặt ra. Các máy khách HAL dự kiến ​​sẽ chuyển những thông tin chưa được sửa đổi này (để biết chi tiết, hãy tham khảo các cờ liên quan đến Gralloc.h ).
  • bufferId . 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 đi khứ hồi thông qua API HAL. Giá trị được lưu trữ trong trường này có thể được lựa chọn tùy ý khi triển khai HAL.
  • memHandle . Phần xử lý của bộ nhớ đệm cơ bản chứa dữ liệu hình ảnh. Việc triển khai HAL có thể chọn lưu trữ bộ xử lý bộ đệm Gralloc tại đây.

IEvsCameraStream

Máy khách triển khai giao diện này để nhận việc phân phối khung hình video không đồng bộ.

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

Mặc dù về mặt kỹ thuật có thể thực hiện được các định dạng bộ đệm độc quyền, nhưng việc kiểm tra khả năng tương thích yêu cầu bộ đệm phải ở một trong bốn định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Semi-Planar), 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à nguồn kết cấu GL hợp lệ khi triển khai GLES của nền tảng.

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

IevsHiển thị

Đố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ị. 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 một cách khéo léo 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 tạo, màn hình được xác định để bắt đầu ở trạng thái NOT_VISIBLE , sau đó khách hàng 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, máy khách sẽ yêu cầu trạng thái NOT_VISIBLE sau khi truyền khung hình video cuối cùng.

Nó có giá trị cho bất kỳ trạng thái nào được yêu cầu bất cứ lúc nào. Nếu màn hình đã hiển thị, nó sẽ vẫn hiển thị nếu được đặt thành VISIBLE_ON_NEXT_FRAME . Luôn trả về OK trừ khi trạng thái được yêu cầu là giá trị enum không được nhận dạng, trong trường hợp đó INVALID_ARG được trả về.

getDisplayState() generates (DisplayState state);

Nhận trạng thái hiển thị. Việc triển khai HAL phải báo cáo trạng thái hiện tại thực tế, trạng thái này 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 lớp thiết bị, khiến việc triển khai HAL không thể tự động thay đổi trạng thái hiển thị.

getTargetBuffer() generates (handle bufferHandle);

Trả về một điều khiển cho bộ đệm khung được liên kết với màn hình. Bộ đệm này có thể bị khóa và ghi vào bằng phần mềm và/hoặc GL. Bộ đệm này phải được trả về thông qua lệnh gọi returnTargetBufferForDisplay() ngay cả khi màn hình không còn hiển thị nữa.

Mặc dù về mặt kỹ thuật có thể thực hiện được các định dạng bộ đệm độc quyền, nhưng việc kiểm tra khả năng tương thích yêu cầu bộ đệm phải ở một trong bốn định dạng được hỗ trợ: NV21 (YCrCb 4:2:0 Semi-Planar), 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ục tiêu kết xuất GL hợp lệ khi triển khai GLES của nền tảng.

Nếu có lỗi, bộ đệm có tay cầm null sẽ được trả về, nhưng bộ đệm như vậy không cần phải được chuyển trở lại returnTargetBufferForDisplay .

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Cho màn hình biết bộ đệm đã sẵn sàng để hiển thị. Chỉ các bộ đệm được truy xuất thông qua lệnh gọi tới getTargetBuffer() mới hợp lệ để sử dụng với lệnh gọi này và ứng dụng khách có thể không sửa đổi nội dung của BufferDesc . Sau cuộc gọi này, bộ đệm không còn hợp lệ để khách hàng sử dụng. Trả về OK nếu thành công hoặc mã lỗi thích hợp có khả năng 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à được yêu cầu khi triển khai EVS. 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 vật lý hoặc màn hình ảo được phủ hoặc trộn 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ị hạt nhân của thiết bị hoặc tên của thiết bị, chẳng hạn như chiếu hậu . Giá trị cho chuỗi này được chọn bởi quá trình triển khai HAL 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 pháp truyền thông tin camera chuyên dụng một cách mờ đục từ trình điều khiển đến Ứng dụng EVS tùy chỉnh. Nó được truyền mà không được giải thích từ trình điều khiển đến Ứng dụng EVS, bạn có thể bỏ qua nó mà không cần giải thích.
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ể bị tắt (người lái xe không nhìn thấy) hoặc được bật (hiển thị hình ảnh cho người lái xe). Bao gồm trạng thái nhất thời trong đó màn hình chưa hiển thị nhưng được chuẩn bị để hiển thị khi phân phối khung hình ảnh tiếp theo thông qua lệnh gọi returnTargetBufferForDisplay() .

Người quản lý EVS

Trình quản lý EVS cung cấp giao diện chung cho hệ thống EVS để thu thập và trình bày các 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 hoạt động trên mỗi tài nguyên (máy ảnh hoặc màn hình), Trình quản lý EVS sẽ tạo điều kiện truy cập chung vào máy ảnh. Một ứng dụng EVS chính duy nhất 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 bổ sung có thể được cấp quyền truy cập chỉ đọc vào hình ảnh camera).

Trình quản lý EVS triển khai API giống như 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 máy khách đồng thời (nhiều máy khách có thể mở camera thông qua Trình quản lý EVS và nhận luồng video).

Trình quản lý EVS và sơ đồ API phần cứng EVS.
Hình 2. Trình quản lý EVS phản ánh API phần cứng EVS cơ bản

Các ứng dụng không thấy có sự khác biệt nào khi hoạt động thông qua triển khai HAL Phần cứng EVS hoặc API Trình quản lý EVS ngoại trừ API Trình quản lý EVS cho phép truy cập luồng camera đồng thời. Bản thân Trình quản lý EVS là ứng dụng khách được phép duy nhất của lớp HAL Phần cứng EVS và hoạt động như một proxy cho HAL Phần cứng EVS.

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

IevsĐiều tra viên

openCamera(string camera_id) generates (IEvsCamera camera);

Lấy một đối tượng giao diện được sử 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ề NULL khi thất bại. Ở lớp Trình quản lý EVS, miễn là có đủ tài nguyên hệ thống, một camera đã mở có thể được mở lại bằng một quy trình khác, cho phép truyền luồng video tới 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

Trình quản lý EVS cung cấp việc triển khai IEvsCamera được ảo hóa nội bộ để các hoạt động trên camera của một khách hàng không ảnh hưởng đến các khách hàng khác, những khách hàng này vẫn có quyền truy cập độc lập vào camera của họ.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Bắt đầu luồng video. Khách hà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. Camera bên dưới 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. Mỗi khách hàng phải trả lại khung của mình khi hoàn thành, nhưng được phép giữ khung của họ bao lâu tùy thích. Khi số lượng khung hình do máy khách nắm giữ đạt đến giới hạn được định cấu hình, nó sẽ không nhận thêm bất kỳ khung hình nào 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 máy khách khác, tiếp tục nhận được tất cả các khung như mong đợi.

stopVideoStream();

Dừng một luồng video. Mỗi khách hàng có thể dừng luồng video của mình bất kỳ lúc nào mà không ảnh hưởng đến các khách hàng khác. Luồng camera cơ bản ở lớp phần cứng bị dừng khi máy khách cuối cùng của một camera nhất định 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 trình điều khiển, có khả năng cho phép một máy khách ảnh hưởng đến một máy khách khác. Vì Trình quản lý EVS 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 chúng không được ảo hóa và mọi tác dụng phụ đều áp dụng cho tất cả máy khách của một máy ảnh nhất định. Ví dụ: nếu nhà cung cấp sử dụng lệnh gọi này để thay đổi tốc độ khung hình, tất cả máy khách của máy ảnh lớp phần cứng bị ảnh hưởng sẽ nhận được khung hình ở tốc độ mới.

IevsHiển thị

Chỉ cho phép 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ỉ chuyển trực tiếp giao diện IEvsDisplay sang quá trình triển khai HAL cơ bản.

ứng dụng EVS

Android bao gồm 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à Vehicle HAL để 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ẽ bắt đầu từ rất sớm trong quá trình khởi động hệ thống, với video phù hợp được hiển thị tùy thuộc vào camera có sẵn và trạng thái của xe (trạng thái chuyển số và đèn xi nhan). Các OEM có thể sửa đổi hoặc thay thế ứng dụng EVS bằng cách trình bày và logic dành riêng cho xe của họ.

Hình 3. Logic mẫu ứng dụng EVS, lấy danh sách camera.


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 đưa đến ứng dụng trong bộ đệm đồ họa tiêu chuẩn nên ứng dụng chịu trách nhiệm di chuyển hình ảnh từ bộ đệm nguồn sang bộ đệ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 lại cơ hội cho ứng dụng hiển thị hình ảnh vào bộ đệm hiển thị theo bất kỳ cách nào nó 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 chia tỷ lệ nội tuyến. Ứng dụng cũng có thể chọn sử dụng hình ảnh nguồn làm kết cấu OpenGL và hiển thị một cảnh phức tạp cho bộ đệm đầu ra, bao gồm các phần tử ảo như biểu tượng, hướng dẫn và hoạt ảnh. 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 ở chế độ xem ảo, từ trên xuống về môi trường xung quanh xe).

Sử dụng EGL/SurfaceFlinger trong EVS Display HAL

Phần này giải thích cách sử dụng EGL để hiển thị triển khai EVS Display HAL trong Android 10.

Việc triển khai tham chiếu EVS HAL sử dụng EGL để hiển thị bản xem trước của máy ảnh trên màn hình và sử dụng libgui để tạo bề mặt hiển thị EGL mục tiêu. Trong Android 8 (và cao hơn), libgui được phân loại là VNĐK-private , dùng để chỉ một nhóm thư viện có sẵn cho các thư viện VNĐK 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 của nhà cung cấp nên các nhà cung cấp không được phép sử dụng Surface trong quá trình triển khai HAL.

Xây dựng libgui cho quy trình của nhà cung cấp

Việc sử dụng libgui đóng vai trò là tùy chọn duy nhất để sử dụng EGL/SurfaceFlinger trong triển khai EVS Display HAL. Cách đơn giản nhất để triển khai libgui là trực tiếp thông qua frameworks/native/libs/gui bằng cách sử dụng mục tiêu xây dựng bổ sung trong tập lệnh xây 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 xây dựng bằng macro NO_INPUT , loại bỏ một từ 32 bit khỏi dữ liệu bưu kiện. Vì SurfaceFlinger dự kiến ​​trường này đã bị xóa nên SurfaceFlinger không thể phân tích bưu kiện. Điều này được coi 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

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

Hướng dẫn xây dựng mẫu được cung cấp dưới đây. Dự kiến ​​sẽ 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 chất kết dính trong triển khai EVS HAL

Trong Android 8 (và cao hơ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ể truy cập được vào các quy trình của nhà cung cấp. Thay vào đó, 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 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 .

Tên miền IPC Sự miêu tả
/dev/binder IPC giữa các quy trình khung/ứng dụng với 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 với giao diện HIDL
/dev/vndbinder IPC giữa các quy trình của nhà cung cấp/nhà cung cấp với Giao diện AIDL

Trong khi SurfaceFlinger xác định giao diện AIDL, 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 hề nhỏ để chuyển đổi giao diện AIDL hiện có thành HIDL. May mắn thay, Android cung cấp một phương pháp để 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 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 ý: Các quy trình của nhà cung cấp nên gọi điều này trước khi gọi tới 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 của SELinux

Nếu việc triển khai thiết bị ở mức tối đa, SELinux sẽ ngăn các quy trình của nhà cung cấp sử dụng /dev/binder . Ví dụ: việc triển khai mẫu EVS HAL được gán 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 xây dựng vì nó vi phạm các quy tắc không bao giờ cho phép sau đây được xác định trong system/sepolicy/domain.te dành cho thiết bị có âm bổng đầ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à thuộc tính được cung cấp để phát hiện lỗi và hướng dẫn phát triển. Nó cũng có thể được sử dụng để giải quyết vi phạm Android 10 được 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 triển khai tham chiếu EVS HAL như một 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 đảm bảo xác nhận rằng tất cả các 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;