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:
ứ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ặcBGRA
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ảoGralloc.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 bufferId
và memHandle
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).
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.
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;