blob: ebb8fcb735d92dcfdc1c0b8c235be2e7f5f69bd3 [file] [log] [blame]
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "CamDevSession@3.2-impl"
18#include <android/log.h>
19
Yin-Chia Yehbed3a942017-03-06 14:14:17 -080020#include <set>
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -070021#include <utils/Trace.h>
22#include <hardware/gralloc.h>
23#include <hardware/gralloc1.h>
24#include "CameraDeviceSession.h"
25
26namespace android {
27namespace hardware {
28namespace camera {
29namespace device {
30namespace V3_2 {
31namespace implementation {
32
Yifan Hong1192e1d2017-04-11 14:45:00 -070033// Size of request metadata fast message queue. Change to 0 to always use hwbinder buffer.
34static constexpr size_t CAMERA_REQUEST_METADATA_QUEUE_SIZE = 1 << 20 /* 1MB */;
35
Yin-Chia Yeh248ed702017-01-23 17:27:26 -080036HandleImporter& CameraDeviceSession::sHandleImporter = HandleImporter::getInstance();
Yin-Chia Yehbed3a942017-03-06 14:14:17 -080037const int CameraDeviceSession::ResultBatcher::NOT_BATCHED;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -070038
39CameraDeviceSession::CameraDeviceSession(
Yin-Chia Yehbed3a942017-03-06 14:14:17 -080040 camera3_device_t* device,
41 const camera_metadata_t* deviceInfo,
42 const sp<ICameraDeviceCallback>& callback) :
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -070043 camera3_callback_ops({&sProcessCaptureResult, &sNotify}),
44 mDevice(device),
Yin-Chia Yehbed3a942017-03-06 14:14:17 -080045 mResultBatcher(callback) {
46
47 mDeviceInfo = deviceInfo;
48 uint32_t numPartialResults = 1;
49 camera_metadata_entry partialResultsCount =
50 mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
51 if (partialResultsCount.count > 0) {
52 numPartialResults = partialResultsCount.data.i32[0];
53 }
54 mResultBatcher.setNumPartialResults(numPartialResults);
55
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -070056 mInitFail = initialize();
57}
58
59bool CameraDeviceSession::initialize() {
60 /** Initialize device with callback functions */
61 ATRACE_BEGIN("camera3->initialize");
62 status_t res = mDevice->ops->initialize(mDevice, this);
63 ATRACE_END();
64
65 if (res != OK) {
66 ALOGE("%s: Unable to initialize HAL device: %s (%d)",
67 __FUNCTION__, strerror(-res), res);
68 mDevice->common.close(&mDevice->common);
69 mClosed = true;
70 return true;
71 }
Yifan Hong1192e1d2017-04-11 14:45:00 -070072
73 mRequestMetadataQueue = std::make_unique<RequestMetadataQueue>(
74 CAMERA_REQUEST_METADATA_QUEUE_SIZE, false /* non blocking */);
75 if (!mRequestMetadataQueue->isValid()) {
76 ALOGE("%s: invalid fmq", __FUNCTION__);
77 return true;
78 }
79
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -070080 return false;
81}
82
83CameraDeviceSession::~CameraDeviceSession() {
84 if (!isClosed()) {
85 ALOGE("CameraDeviceSession deleted before close!");
86 close();
87 }
88}
89
90bool CameraDeviceSession::isClosed() {
91 Mutex::Autolock _l(mStateLock);
92 return mClosed;
93}
94
95Status CameraDeviceSession::initStatus() const {
96 Mutex::Autolock _l(mStateLock);
97 Status status = Status::OK;
98 if (mInitFail) {
99 status = Status::INTERNAL_ERROR;
100 } else if (mDisconnected) {
101 status = Status::CAMERA_DISCONNECTED;
102 } else if (mClosed) {
103 status = Status::INTERNAL_ERROR;
104 }
105 return status;
106}
107
108void CameraDeviceSession::disconnect() {
109 Mutex::Autolock _l(mStateLock);
110 mDisconnected = true;
111 ALOGW("%s: Camera device is disconnected. Closing.", __FUNCTION__);
112 if (!mClosed) {
113 mDevice->common.close(&mDevice->common);
114 mClosed = true;
115 }
116}
117
118void CameraDeviceSession::dumpState(const native_handle_t* fd) {
119 if (!isClosed()) {
120 mDevice->ops->dump(mDevice, fd->data[0]);
121 }
122}
123
124Status CameraDeviceSession::importRequest(
125 const CaptureRequest& request,
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800126 hidl_vec<buffer_handle_t*>& allBufPtrs,
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700127 hidl_vec<int>& allFences) {
128 bool hasInputBuf = (request.inputBuffer.streamId != -1 &&
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800129 request.inputBuffer.bufferId != 0);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700130 size_t numOutputBufs = request.outputBuffers.size();
131 size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
132 // Validate all I/O buffers
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800133 hidl_vec<buffer_handle_t> allBufs;
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800134 hidl_vec<uint64_t> allBufIds;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700135 allBufs.resize(numBufs);
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800136 allBufIds.resize(numBufs);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800137 allBufPtrs.resize(numBufs);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700138 allFences.resize(numBufs);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800139 std::vector<int32_t> streamIds(numBufs);
140
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700141 for (size_t i = 0; i < numOutputBufs; i++) {
142 allBufs[i] = request.outputBuffers[i].buffer.getNativeHandle();
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800143 allBufIds[i] = request.outputBuffers[i].bufferId;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800144 allBufPtrs[i] = &allBufs[i];
145 streamIds[i] = request.outputBuffers[i].streamId;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700146 }
147 if (hasInputBuf) {
148 allBufs[numOutputBufs] = request.inputBuffer.buffer.getNativeHandle();
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800149 allBufIds[numOutputBufs] = request.inputBuffer.bufferId;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800150 allBufPtrs[numOutputBufs] = &allBufs[numOutputBufs];
151 streamIds[numOutputBufs] = request.inputBuffer.streamId;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700152 }
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800153
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700154 for (size_t i = 0; i < numBufs; i++) {
155 buffer_handle_t buf = allBufs[i];
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800156 uint64_t bufId = allBufIds[i];
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800157 CirculatingBuffers& cbs = mCirculatingBuffers[streamIds[i]];
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800158 if (cbs.count(bufId) == 0) {
159 if (buf == nullptr) {
160 ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
161 return Status::ILLEGAL_ARGUMENT;
162 }
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800163 // Register a newly seen buffer
164 buffer_handle_t importedBuf = buf;
165 sHandleImporter.importBuffer(importedBuf);
166 if (importedBuf == nullptr) {
167 ALOGE("%s: output buffer %zu is invalid!", __FUNCTION__, i);
168 return Status::INTERNAL_ERROR;
169 } else {
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800170 cbs[bufId] = importedBuf;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800171 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700172 }
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800173 allBufPtrs[i] = &cbs[bufId];
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700174 }
175
176 // All buffers are imported. Now validate output buffer acquire fences
177 for (size_t i = 0; i < numOutputBufs; i++) {
178 if (!sHandleImporter.importFence(
179 request.outputBuffers[i].acquireFence, allFences[i])) {
180 ALOGE("%s: output buffer %zu acquire fence is invalid", __FUNCTION__, i);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800181 cleanupInflightFences(allFences, i);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700182 return Status::INTERNAL_ERROR;
183 }
184 }
185
186 // Validate input buffer acquire fences
187 if (hasInputBuf) {
188 if (!sHandleImporter.importFence(
189 request.inputBuffer.acquireFence, allFences[numOutputBufs])) {
190 ALOGE("%s: input buffer acquire fence is invalid", __FUNCTION__);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800191 cleanupInflightFences(allFences, numOutputBufs);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700192 return Status::INTERNAL_ERROR;
193 }
194 }
195 return Status::OK;
196}
197
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800198void CameraDeviceSession::cleanupInflightFences(
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700199 hidl_vec<int>& allFences, size_t numFences) {
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700200 for (size_t j = 0; j < numFences; j++) {
201 sHandleImporter.closeFence(allFences[j]);
202 }
203}
204
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800205CameraDeviceSession::ResultBatcher::ResultBatcher(
206 const sp<ICameraDeviceCallback>& callback) : mCallback(callback) {};
207
208bool CameraDeviceSession::ResultBatcher::InflightBatch::allDelivered() const {
209 if (!mShutterDelivered) return false;
210
211 if (mPartialResultProgress < mNumPartialResults) {
212 return false;
213 }
214
215 for (const auto& pair : mBatchBufs) {
216 if (!pair.second.mDelivered) {
217 return false;
218 }
219 }
220 return true;
221}
222
223void CameraDeviceSession::ResultBatcher::setNumPartialResults(uint32_t n) {
224 Mutex::Autolock _l(mLock);
225 mNumPartialResults = n;
226}
227
228void CameraDeviceSession::ResultBatcher::setBatchedStreams(
229 const std::vector<int>& streamsToBatch) {
230 Mutex::Autolock _l(mLock);
231 mStreamsToBatch = streamsToBatch;
232}
233
234void CameraDeviceSession::ResultBatcher::registerBatch(
235 const hidl_vec<CaptureRequest>& requests) {
236 auto batch = std::make_shared<InflightBatch>();
237 batch->mFirstFrame = requests[0].frameNumber;
238 batch->mBatchSize = requests.size();
239 batch->mLastFrame = batch->mFirstFrame + batch->mBatchSize - 1;
240 batch->mNumPartialResults = mNumPartialResults;
241 for (int id : mStreamsToBatch) {
242 batch->mBatchBufs[id] = InflightBatch::BufferBatch();
243 }
244 Mutex::Autolock _l(mLock);
245 mInflightBatches.push_back(batch);
246}
247
248std::pair<int, std::shared_ptr<CameraDeviceSession::ResultBatcher::InflightBatch>>
249CameraDeviceSession::ResultBatcher::getBatch(
250 uint32_t frameNumber) {
251 Mutex::Autolock _l(mLock);
252 int numBatches = mInflightBatches.size();
253 if (numBatches == 0) {
254 return std::make_pair(NOT_BATCHED, nullptr);
255 }
256 uint32_t frameMin = mInflightBatches[0]->mFirstFrame;
257 uint32_t frameMax = mInflightBatches[numBatches - 1]->mLastFrame;
258 if (frameNumber < frameMin || frameNumber > frameMax) {
259 return std::make_pair(NOT_BATCHED, nullptr);
260 }
261 for (int i = 0; i < numBatches; i++) {
262 if (frameNumber >= mInflightBatches[i]->mFirstFrame &&
263 frameNumber <= mInflightBatches[i]->mLastFrame) {
264 return std::make_pair(i, mInflightBatches[i]);
265 }
266 }
267 return std::make_pair(NOT_BATCHED, nullptr);
268}
269
270void CameraDeviceSession::ResultBatcher::checkAndRemoveFirstBatch() {
271 Mutex::Autolock _l(mLock);
272 if (mInflightBatches.size() > 0) {
273 std::shared_ptr<InflightBatch> batch = mInflightBatches[0];
274 bool shouldRemove = false;
275 {
276 Mutex::Autolock _l(batch->mLock);
277 if (batch->allDelivered()) {
278 batch->mRemoved = true;
279 shouldRemove = true;
280 }
281 }
282 if (shouldRemove) {
283 mInflightBatches.pop_front();
284 }
285 }
286}
287
288void CameraDeviceSession::ResultBatcher::sendBatchShutterCbsLocked(std::shared_ptr<InflightBatch> batch) {
289 if (batch->mShutterDelivered) {
290 ALOGW("%s: batch shutter callback already sent!", __FUNCTION__);
291 return;
292 }
293
294 mCallback->notify(batch->mShutterMsgs);
295 batch->mShutterDelivered = true;
296 batch->mShutterMsgs.clear();
297}
298
299void CameraDeviceSession::ResultBatcher::freeReleaseFences(hidl_vec<CaptureResult>& results) {
300 for (auto& result : results) {
301 if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
302 native_handle_t* handle = const_cast<native_handle_t*>(
303 result.inputBuffer.releaseFence.getNativeHandle());
304 native_handle_delete(handle);
305 }
306 for (auto& buf : result.outputBuffers) {
307 if (buf.releaseFence.getNativeHandle() != nullptr) {
308 native_handle_t* handle = const_cast<native_handle_t*>(
309 buf.releaseFence.getNativeHandle());
310 native_handle_delete(handle);
311 }
312 }
313 }
314 return;
315}
316
317void CameraDeviceSession::ResultBatcher::sendBatchBuffersLocked(std::shared_ptr<InflightBatch> batch) {
318 sendBatchBuffersLocked(batch, mStreamsToBatch);
319}
320
321void CameraDeviceSession::ResultBatcher::sendBatchBuffersLocked(
322 std::shared_ptr<InflightBatch> batch, const std::vector<int>& streams) {
323 size_t batchSize = 0;
324 for (int streamId : streams) {
325 auto it = batch->mBatchBufs.find(streamId);
326 if (it != batch->mBatchBufs.end()) {
327 InflightBatch::BufferBatch& bb = it->second;
328 if (bb.mDelivered) {
329 continue;
330 }
331 if (bb.mBuffers.size() > batchSize) {
332 batchSize = bb.mBuffers.size();
333 }
334 } else {
335 ALOGE("%s: stream ID %d is not batched!", __FUNCTION__, streamId);
336 return;
337 }
338 }
339
340 if (batchSize == 0) {
341 ALOGW("%s: there is no buffer to be delivered for this batch.", __FUNCTION__);
342 for (int streamId : streams) {
343 InflightBatch::BufferBatch& bb = batch->mBatchBufs[streamId];
344 bb.mDelivered = true;
345 }
346 return;
347 }
348
349 hidl_vec<CaptureResult> results;
350 results.resize(batchSize);
351 for (size_t i = 0; i < batchSize; i++) {
352 results[i].frameNumber = batch->mFirstFrame + i;
353 results[i].partialResult = 0; // 0 for buffer only results
354 results[i].inputBuffer.streamId = -1;
355 results[i].inputBuffer.bufferId = 0;
356 results[i].inputBuffer.buffer = nullptr;
357 std::vector<StreamBuffer> outBufs;
358 for (int streamId : streams) {
359 InflightBatch::BufferBatch& bb = batch->mBatchBufs[streamId];
360 if (bb.mDelivered) {
361 continue;
362 }
363 if (i < bb.mBuffers.size()) {
364 outBufs.push_back(bb.mBuffers[i]);
365 }
366 }
367 results[i].outputBuffers = outBufs;
368 }
369 mCallback->processCaptureResult(results);
370 freeReleaseFences(results);
371 for (int streamId : streams) {
372 InflightBatch::BufferBatch& bb = batch->mBatchBufs[streamId];
373 bb.mDelivered = true;
374 bb.mBuffers.clear();
375 }
376}
377
378void CameraDeviceSession::ResultBatcher::sendBatchMetadataLocked(
379 std::shared_ptr<InflightBatch> batch, uint32_t lastPartialResultIdx) {
380 if (lastPartialResultIdx <= batch->mPartialResultProgress) {
381 // Result has been delivered. Return
382 ALOGW("%s: partial result %u has been delivered", __FUNCTION__, lastPartialResultIdx);
383 return;
384 }
385
386 std::vector<CaptureResult> results;
387 std::vector<uint32_t> toBeRemovedIdxes;
388 for (auto& pair : batch->mResultMds) {
389 uint32_t partialIdx = pair.first;
390 if (partialIdx > lastPartialResultIdx) {
391 continue;
392 }
393 toBeRemovedIdxes.push_back(partialIdx);
394 InflightBatch::MetadataBatch& mb = pair.second;
395 for (const auto& p : mb.mMds) {
396 CaptureResult result;
397 result.frameNumber = p.first;
398 result.result = std::move(p.second);
399 result.inputBuffer.streamId = -1;
400 result.inputBuffer.bufferId = 0;
401 result.inputBuffer.buffer = nullptr;
402 result.partialResult = partialIdx;
403 results.push_back(std::move(result));
404 }
405 mb.mMds.clear();
406 }
407 mCallback->processCaptureResult(results);
408 batch->mPartialResultProgress = lastPartialResultIdx;
409 for (uint32_t partialIdx : toBeRemovedIdxes) {
410 batch->mResultMds.erase(partialIdx);
411 }
412}
413
414void CameraDeviceSession::ResultBatcher::notifySingleMsg(NotifyMsg& msg) {
415 mCallback->notify({msg});
416 return;
417}
418
419void CameraDeviceSession::ResultBatcher::notify(NotifyMsg& msg) {
420 uint32_t frameNumber;
421 if (CC_LIKELY(msg.type == MsgType::SHUTTER)) {
422 frameNumber = msg.msg.shutter.frameNumber;
423 } else {
424 frameNumber = msg.msg.error.frameNumber;
425 }
426
427 auto pair = getBatch(frameNumber);
428 int batchIdx = pair.first;
429 if (batchIdx == NOT_BATCHED) {
430 notifySingleMsg(msg);
431 return;
432 }
433
434 // When error happened, stop batching for all batches earlier
435 if (CC_UNLIKELY(msg.type == MsgType::ERROR)) {
436 Mutex::Autolock _l(mLock);
437 for (int i = 0; i <= batchIdx; i++) {
438 // Send batched data up
439 std::shared_ptr<InflightBatch> batch = mInflightBatches[0];
440 {
441 Mutex::Autolock _l(batch->mLock);
442 sendBatchShutterCbsLocked(batch);
443 sendBatchBuffersLocked(batch);
444 sendBatchMetadataLocked(batch, mNumPartialResults);
445 if (!batch->allDelivered()) {
446 ALOGE("%s: error: some batch data not sent back to framework!",
447 __FUNCTION__);
448 }
449 batch->mRemoved = true;
450 }
451 mInflightBatches.pop_front();
452 }
453 // Send the error up
454 notifySingleMsg(msg);
455 return;
456 }
457 // Queue shutter callbacks for future delivery
458 std::shared_ptr<InflightBatch> batch = pair.second;
459 {
460 Mutex::Autolock _l(batch->mLock);
461 // Check if the batch is removed (mostly by notify error) before lock was acquired
462 if (batch->mRemoved) {
463 // Fall back to non-batch path
464 notifySingleMsg(msg);
465 return;
466 }
467
468 batch->mShutterMsgs.push_back(msg);
469 if (frameNumber == batch->mLastFrame) {
470 sendBatchShutterCbsLocked(batch);
471 }
472 } // end of batch lock scope
473
474 // see if the batch is complete
475 if (frameNumber == batch->mLastFrame) {
476 checkAndRemoveFirstBatch();
477 }
478}
479
480void CameraDeviceSession::ResultBatcher::processOneCaptureResult(CaptureResult& result) {
481 hidl_vec<CaptureResult> results = {result};
482 mCallback->processCaptureResult(results);
483 freeReleaseFences(results);
484 return;
485}
486
487void CameraDeviceSession::ResultBatcher::processCaptureResult(CaptureResult& result) {
488 auto pair = getBatch(result.frameNumber);
489 int batchIdx = pair.first;
490 if (batchIdx == NOT_BATCHED) {
491 processOneCaptureResult(result);
492 return;
493 }
494 std::shared_ptr<InflightBatch> batch = pair.second;
495 {
496 Mutex::Autolock _l(batch->mLock);
497 // Check if the batch is removed (mostly by notify error) before lock was acquired
498 if (batch->mRemoved) {
499 // Fall back to non-batch path
500 processOneCaptureResult(result);
501 return;
502 }
503
504 // queue metadata
505 if (result.result.size() != 0) {
506 // Save a copy of metadata
507 batch->mResultMds[result.partialResult].mMds.push_back(
508 std::make_pair(result.frameNumber, result.result));
509 }
510
511 // queue buffer
512 std::vector<int> filledStreams;
513 std::vector<StreamBuffer> nonBatchedBuffers;
514 for (auto& buffer : result.outputBuffers) {
515 auto it = batch->mBatchBufs.find(buffer.streamId);
516 if (it != batch->mBatchBufs.end()) {
517 InflightBatch::BufferBatch& bb = it->second;
518 bb.mBuffers.push_back(buffer);
519 filledStreams.push_back(buffer.streamId);
520 } else {
521 nonBatchedBuffers.push_back(buffer);
522 }
523 }
524
525 // send non-batched buffers up
526 if (nonBatchedBuffers.size() > 0 || result.inputBuffer.streamId != -1) {
527 CaptureResult nonBatchedResult;
528 nonBatchedResult.frameNumber = result.frameNumber;
529 nonBatchedResult.outputBuffers = nonBatchedBuffers;
530 nonBatchedResult.inputBuffer = result.inputBuffer;
531 nonBatchedResult.partialResult = 0; // 0 for buffer only results
532 processOneCaptureResult(nonBatchedResult);
533 }
534
535 if (result.frameNumber == batch->mLastFrame) {
536 // Send data up
537 if (result.partialResult > 0) {
538 sendBatchMetadataLocked(batch, result.partialResult);
539 }
540 // send buffer up
541 if (filledStreams.size() > 0) {
542 sendBatchBuffersLocked(batch, filledStreams);
543 }
544 }
545 } // end of batch lock scope
546
547 // see if the batch is complete
548 if (result.frameNumber == batch->mLastFrame) {
549 checkAndRemoveFirstBatch();
550 }
551}
552
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700553// Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow.
554Return<void> CameraDeviceSession::constructDefaultRequestSettings(
555 RequestTemplate type, constructDefaultRequestSettings_cb _hidl_cb) {
556 Status status = initStatus();
557 CameraMetadata outMetadata;
558 const camera_metadata_t *rawRequest;
559 if (status == Status::OK) {
560 ATRACE_BEGIN("camera3->construct_default_request_settings");
561 rawRequest = mDevice->ops->construct_default_request_settings(mDevice, (int) type);
562 ATRACE_END();
563 if (rawRequest == nullptr) {
564 ALOGI("%s: template %d is not supported on this camera device",
565 __FUNCTION__, type);
566 status = Status::ILLEGAL_ARGUMENT;
567 } else {
568 convertToHidl(rawRequest, &outMetadata);
569 }
570 }
571 _hidl_cb(status, outMetadata);
572 return Void();
573}
574
575Return<void> CameraDeviceSession::configureStreams(
576 const StreamConfiguration& requestedConfiguration, configureStreams_cb _hidl_cb) {
577 Status status = initStatus();
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800578 HalStreamConfiguration outStreams;
579
580 // hold the inflight lock for entire configureStreams scope since there must not be any
581 // inflight request/results during stream configuration.
582 Mutex::Autolock _l(mInflightLock);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700583 if (!mInflightBuffers.empty()) {
584 ALOGE("%s: trying to configureStreams while there are still %zu inflight buffers!",
585 __FUNCTION__, mInflightBuffers.size());
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800586 _hidl_cb(Status::INTERNAL_ERROR, outStreams);
587 return Void();
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700588 }
589
Emilian Peev98014ff2017-02-02 16:20:12 +0000590 if (status != Status::OK) {
591 _hidl_cb(status, outStreams);
592 return Void();
593 }
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800594
Emilian Peev98014ff2017-02-02 16:20:12 +0000595 camera3_stream_configuration_t stream_list;
596 hidl_vec<camera3_stream_t*> streams;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800597
Emilian Peev98014ff2017-02-02 16:20:12 +0000598 stream_list.operation_mode = (uint32_t) requestedConfiguration.operationMode;
599 stream_list.num_streams = requestedConfiguration.streams.size();
600 streams.resize(stream_list.num_streams);
601 stream_list.streams = streams.data();
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700602
Emilian Peev98014ff2017-02-02 16:20:12 +0000603 for (uint32_t i = 0; i < stream_list.num_streams; i++) {
604 int id = requestedConfiguration.streams[i].id;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700605
Emilian Peev98014ff2017-02-02 16:20:12 +0000606 if (mStreamMap.count(id) == 0) {
607 Camera3Stream stream;
608 convertFromHidl(requestedConfiguration.streams[i], &stream);
609 mStreamMap[id] = stream;
610 mCirculatingBuffers.emplace(stream.mId, CirculatingBuffers{});
611 } else {
612 // width/height/format must not change, but usage/rotation might need to change
613 if (mStreamMap[id].stream_type !=
614 (int) requestedConfiguration.streams[i].streamType ||
615 mStreamMap[id].width != requestedConfiguration.streams[i].width ||
616 mStreamMap[id].height != requestedConfiguration.streams[i].height ||
617 mStreamMap[id].format != (int) requestedConfiguration.streams[i].format ||
618 mStreamMap[id].data_space != (android_dataspace_t)
619 requestedConfiguration.streams[i].dataSpace) {
620 ALOGE("%s: stream %d configuration changed!", __FUNCTION__, id);
621 _hidl_cb(Status::INTERNAL_ERROR, outStreams);
622 return Void();
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800623 }
Emilian Peev98014ff2017-02-02 16:20:12 +0000624 mStreamMap[id].rotation = (int) requestedConfiguration.streams[i].rotation;
Yin-Chia Yehc25c54f2017-04-04 13:00:35 -0700625 mStreamMap[id].usage = (uint32_t) requestedConfiguration.streams[i].usage;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700626 }
Emilian Peev98014ff2017-02-02 16:20:12 +0000627 streams[i] = &mStreamMap[id];
628 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700629
Emilian Peev98014ff2017-02-02 16:20:12 +0000630 ATRACE_BEGIN("camera3->configure_streams");
631 status_t ret = mDevice->ops->configure_streams(mDevice, &stream_list);
632 ATRACE_END();
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700633
Emilian Peev98014ff2017-02-02 16:20:12 +0000634 // In case Hal returns error most likely it was not able to release
635 // the corresponding resources of the deleted streams.
636 if (ret == OK) {
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800637 // delete unused streams, note we do this after adding new streams to ensure new stream
638 // will not have the same address as deleted stream, and HAL has a chance to reference
639 // the to be deleted stream in configure_streams call
640 for(auto it = mStreamMap.begin(); it != mStreamMap.end();) {
641 int id = it->first;
642 bool found = false;
643 for (const auto& stream : requestedConfiguration.streams) {
644 if (id == stream.id) {
645 found = true;
646 break;
647 }
648 }
649 if (!found) {
650 // Unmap all buffers of deleted stream
Emilian Peev98014ff2017-02-02 16:20:12 +0000651 // in case the configuration call succeeds and HAL
652 // is able to release the corresponding resources too.
653 cleanupBuffersLocked(id);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800654 it = mStreamMap.erase(it);
655 } else {
656 ++it;
657 }
658 }
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800659
660 // Track video streams
661 mVideoStreamIds.clear();
662 for (const auto& stream : requestedConfiguration.streams) {
663 if (stream.streamType == StreamType::OUTPUT &&
Chia-I Wu79d13ff2017-03-31 12:48:11 -0700664 stream.usage &
665 graphics::common::V1_0::BufferUsage::VIDEO_ENCODER) {
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800666 mVideoStreamIds.push_back(stream.id);
667 }
668 }
669 mResultBatcher.setBatchedStreams(mVideoStreamIds);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700670 }
Emilian Peev98014ff2017-02-02 16:20:12 +0000671
672 if (ret == -EINVAL) {
673 status = Status::ILLEGAL_ARGUMENT;
674 } else if (ret != OK) {
675 status = Status::INTERNAL_ERROR;
676 } else {
677 convertToHidl(stream_list, &outStreams);
678 }
679
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700680 _hidl_cb(status, outStreams);
681 return Void();
682}
683
Emilian Peev98014ff2017-02-02 16:20:12 +0000684// Needs to get called after acquiring 'mInflightLock'
685void CameraDeviceSession::cleanupBuffersLocked(int id) {
686 for (auto& pair : mCirculatingBuffers.at(id)) {
687 sHandleImporter.freeBuffer(pair.second);
688 }
689 mCirculatingBuffers[id].clear();
690 mCirculatingBuffers.erase(id);
691}
692
Yin-Chia Yeh28eebbf2017-03-30 15:06:20 -0700693void CameraDeviceSession::updateBufferCaches(const hidl_vec<BufferCache>& cachesToRemove) {
694 Mutex::Autolock _l(mInflightLock);
695 for (auto& cache : cachesToRemove) {
696 auto cbsIt = mCirculatingBuffers.find(cache.streamId);
697 if (cbsIt == mCirculatingBuffers.end()) {
698 // The stream could have been removed
699 continue;
700 }
701 CirculatingBuffers& cbs = cbsIt->second;
702 auto it = cbs.find(cache.bufferId);
703 if (it != cbs.end()) {
704 sHandleImporter.freeBuffer(it->second);
705 cbs.erase(it);
706 } else {
707 ALOGE("%s: stream %d buffer %" PRIu64 " is not cached",
708 __FUNCTION__, cache.streamId, cache.bufferId);
709 }
710 }
711}
712
Yifan Hong1192e1d2017-04-11 14:45:00 -0700713Return<void> CameraDeviceSession::getCaptureRequestMetadataQueue(
714 getCaptureRequestMetadataQueue_cb _hidl_cb) {
715 _hidl_cb(*mRequestMetadataQueue->getDesc());
716 return Void();
717}
718
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800719Return<void> CameraDeviceSession::processCaptureRequest(
Yin-Chia Yeh28eebbf2017-03-30 15:06:20 -0700720 const hidl_vec<CaptureRequest>& requests,
721 const hidl_vec<BufferCache>& cachesToRemove,
722 processCaptureRequest_cb _hidl_cb) {
723 updateBufferCaches(cachesToRemove);
724
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800725 uint32_t numRequestProcessed = 0;
726 Status s = Status::OK;
727 for (size_t i = 0; i < requests.size(); i++, numRequestProcessed++) {
728 s = processOneCaptureRequest(requests[i]);
729 if (s != Status::OK) {
730 break;
731 }
732 }
733
734 if (s == Status::OK && requests.size() > 1) {
735 mResultBatcher.registerBatch(requests);
736 }
737
738 _hidl_cb(s, numRequestProcessed);
739 return Void();
740}
741
742Status CameraDeviceSession::processOneCaptureRequest(const CaptureRequest& request) {
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700743 Status status = initStatus();
744 if (status != Status::OK) {
745 ALOGE("%s: camera init failed or disconnected", __FUNCTION__);
746 return status;
747 }
748
749 camera3_capture_request_t halRequest;
750 halRequest.frame_number = request.frameNumber;
Yifan Hong1192e1d2017-04-11 14:45:00 -0700751
752 bool converted = true;
753 CameraMetadata settingsFmq; // settings from FMQ
754 if (request.fmqSettingsSize > 0) {
755 // non-blocking read; client must write metadata before calling
756 // processOneCaptureRequest
757 settingsFmq.resize(request.fmqSettingsSize);
758 bool read = mRequestMetadataQueue->read(settingsFmq.data(), request.fmqSettingsSize);
759 if (read) {
760 converted = convertFromHidl(settingsFmq, &halRequest.settings);
761 } else {
762 ALOGE("%s: capture request settings metadata couldn't be read from fmq!", __FUNCTION__);
763 converted = false;
764 }
765 } else {
766 converted = convertFromHidl(request.settings, &halRequest.settings);
767 }
768
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700769 if (!converted) {
770 ALOGE("%s: capture request settings metadata is corrupt!", __FUNCTION__);
771 return Status::INTERNAL_ERROR;
772 }
773
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800774 hidl_vec<buffer_handle_t*> allBufPtrs;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700775 hidl_vec<int> allFences;
776 bool hasInputBuf = (request.inputBuffer.streamId != -1 &&
Yin-Chia Yehd926f932017-01-09 15:21:11 -0800777 request.inputBuffer.bufferId != 0);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700778 size_t numOutputBufs = request.outputBuffers.size();
779 size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800780 status = importRequest(request, allBufPtrs, allFences);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700781 if (status != Status::OK) {
782 return status;
783 }
784
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700785 hidl_vec<camera3_stream_buffer_t> outHalBufs;
786 outHalBufs.resize(numOutputBufs);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800787 {
788 Mutex::Autolock _l(mInflightLock);
789 if (hasInputBuf) {
790 auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber);
791 auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{};
792 convertFromHidl(
793 allBufPtrs[numOutputBufs], request.inputBuffer.status,
794 &mStreamMap[request.inputBuffer.streamId], allFences[numOutputBufs],
795 &bufCache);
796 halRequest.input_buffer = &bufCache;
797 } else {
798 halRequest.input_buffer = nullptr;
799 }
800
801 halRequest.num_output_buffers = numOutputBufs;
802 for (size_t i = 0; i < numOutputBufs; i++) {
803 auto key = std::make_pair(request.outputBuffers[i].streamId, request.frameNumber);
804 auto& bufCache = mInflightBuffers[key] = camera3_stream_buffer_t{};
805 convertFromHidl(
806 allBufPtrs[i], request.outputBuffers[i].status,
807 &mStreamMap[request.outputBuffers[i].streamId], allFences[i],
808 &bufCache);
809 outHalBufs[i] = bufCache;
810 }
811 halRequest.output_buffers = outHalBufs.data();
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700812 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700813
814 ATRACE_ASYNC_BEGIN("frame capture", request.frameNumber);
815 ATRACE_BEGIN("camera3->process_capture_request");
816 status_t ret = mDevice->ops->process_capture_request(mDevice, &halRequest);
817 ATRACE_END();
818 if (ret != OK) {
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800819 Mutex::Autolock _l(mInflightLock);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700820 ALOGE("%s: HAL process_capture_request call failed!", __FUNCTION__);
821
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800822 cleanupInflightFences(allFences, numBufs);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700823 if (hasInputBuf) {
824 auto key = std::make_pair(request.inputBuffer.streamId, request.frameNumber);
825 mInflightBuffers.erase(key);
826 }
827 for (size_t i = 0; i < numOutputBufs; i++) {
828 auto key = std::make_pair(request.outputBuffers[i].streamId, request.frameNumber);
829 mInflightBuffers.erase(key);
830 }
831 return Status::INTERNAL_ERROR;
832 }
833
834 return Status::OK;
835}
836
837Return<Status> CameraDeviceSession::flush() {
838 Status status = initStatus();
839 if (status == Status::OK) {
840 // Flush is always supported on device 3.1 or later
841 status_t ret = mDevice->ops->flush(mDevice);
842 if (ret != OK) {
843 status = Status::INTERNAL_ERROR;
844 }
845 }
846 return status;
847}
848
849Return<void> CameraDeviceSession::close() {
850 Mutex::Autolock _l(mStateLock);
851 if (!mClosed) {
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800852 {
853 Mutex::Autolock _l(mInflightLock);
854 if (!mInflightBuffers.empty()) {
855 ALOGE("%s: trying to close while there are still %zu inflight buffers!",
856 __FUNCTION__, mInflightBuffers.size());
857 }
858 }
859
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700860 ATRACE_BEGIN("camera3->close");
861 mDevice->common.close(&mDevice->common);
862 ATRACE_END();
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800863
864 // free all imported buffers
865 for(auto& pair : mCirculatingBuffers) {
866 CirculatingBuffers& buffers = pair.second;
867 for (auto& p2 : buffers) {
868 sHandleImporter.freeBuffer(p2.second);
869 }
870 }
871
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700872 mClosed = true;
873 }
874 return Void();
875}
876
877/**
878 * Static callback forwarding methods from HAL to instance
879 */
880void CameraDeviceSession::sProcessCaptureResult(
881 const camera3_callback_ops *cb,
882 const camera3_capture_result *hal_result) {
883 CameraDeviceSession *d =
884 const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
885
886 uint32_t frameNumber = hal_result->frame_number;
887 bool hasInputBuf = (hal_result->input_buffer != nullptr);
888 size_t numOutputBufs = hal_result->num_output_buffers;
889 size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800890 if (numBufs > 0) {
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800891 Mutex::Autolock _l(d->mInflightLock);
892 if (hasInputBuf) {
893 int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
894 // validate if buffer is inflight
895 auto key = std::make_pair(streamId, frameNumber);
896 if (d->mInflightBuffers.count(key) != 1) {
897 ALOGE("%s: input buffer for stream %d frame %d is not inflight!",
898 __FUNCTION__, streamId, frameNumber);
899 return;
900 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700901 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700902
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800903 for (size_t i = 0; i < numOutputBufs; i++) {
904 int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
905 // validate if buffer is inflight
906 auto key = std::make_pair(streamId, frameNumber);
907 if (d->mInflightBuffers.count(key) != 1) {
908 ALOGE("%s: output buffer for stream %d frame %d is not inflight!",
909 __FUNCTION__, streamId, frameNumber);
910 return;
911 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700912 }
913 }
914 // We don't need to validate/import fences here since we will be passing them to camera service
915 // within the scope of this function
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700916 CaptureResult result;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700917 result.frameNumber = frameNumber;
918 result.partialResult = hal_result->partial_result;
919 convertToHidl(hal_result->result, &result.result);
920 if (hasInputBuf) {
921 result.inputBuffer.streamId =
922 static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
923 result.inputBuffer.buffer = nullptr;
924 result.inputBuffer.status = (BufferStatus) hal_result->input_buffer->status;
925 // skip acquire fence since it's no use to camera service
926 if (hal_result->input_buffer->release_fence != -1) {
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800927 native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
928 handle->data[0] = hal_result->input_buffer->release_fence;
929 result.inputBuffer.releaseFence = handle;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800930 } else {
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800931 result.inputBuffer.releaseFence = nullptr;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700932 }
933 } else {
934 result.inputBuffer.streamId = -1;
935 }
936
937 result.outputBuffers.resize(numOutputBufs);
938 for (size_t i = 0; i < numOutputBufs; i++) {
939 result.outputBuffers[i].streamId =
940 static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
941 result.outputBuffers[i].buffer = nullptr;
942 result.outputBuffers[i].status = (BufferStatus) hal_result->output_buffers[i].status;
943 // skip acquire fence since it's of no use to camera service
944 if (hal_result->output_buffers[i].release_fence != -1) {
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800945 native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
946 handle->data[0] = hal_result->output_buffers[i].release_fence;
947 result.outputBuffers[i].releaseFence = handle;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800948 } else {
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800949 result.outputBuffers[i].releaseFence = nullptr;
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800950 }
951 }
952
953 // Free inflight record/fences.
954 // Do this before call back to camera service because camera service might jump to
955 // configure_streams right after the processCaptureResult call so we need to finish
956 // updating inflight queues first
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800957 if (numBufs > 0) {
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800958 Mutex::Autolock _l(d->mInflightLock);
959 if (hasInputBuf) {
960 int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
961 auto key = std::make_pair(streamId, frameNumber);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800962 d->mInflightBuffers.erase(key);
963 }
964
965 for (size_t i = 0; i < numOutputBufs; i++) {
966 int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
967 auto key = std::make_pair(streamId, frameNumber);
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800968 d->mInflightBuffers.erase(key);
969 }
970
971 if (d->mInflightBuffers.empty()) {
972 ALOGV("%s: inflight buffer queue is now empty!", __FUNCTION__);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700973 }
974 }
975
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800976 d->mResultBatcher.processCaptureResult(result);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700977}
978
979void CameraDeviceSession::sNotify(
980 const camera3_callback_ops *cb,
981 const camera3_notify_msg *msg) {
982 CameraDeviceSession *d =
983 const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
984 NotifyMsg hidlMsg;
985 convertToHidl(msg, &hidlMsg);
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800986
Yin-Chia Yeh9c6dbd52016-12-22 14:55:02 -0800987 if (hidlMsg.type == (MsgType) CAMERA3_MSG_ERROR &&
988 hidlMsg.msg.error.errorStreamId != -1) {
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700989 if (d->mStreamMap.count(hidlMsg.msg.error.errorStreamId) != 1) {
990 ALOGE("%s: unknown stream ID %d reports an error!",
991 __FUNCTION__, hidlMsg.msg.error.errorStreamId);
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800992 return;
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700993 }
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700994 }
Yin-Chia Yehbed3a942017-03-06 14:14:17 -0800995 d->mResultBatcher.notify(hidlMsg);
Yin-Chia Yehfaef8f92016-10-31 12:53:56 -0700996}
997
998} // namespace implementation
999} // namespace V3_2
1000} // namespace device
1001} // namespace camera
1002} // namespace hardware
1003} // namespace android