1 /*
2 * Copyright (C) 2019 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_NDEBUG 0
18 #define LOG_TAG "GCH_RgbirdCaptureSession"
19 #define ATRACE_TAG ATRACE_TAG_CAMERA
20 #include <cutils/properties.h>
21 #include <log/log.h>
22 #include <utils/Trace.h>
23
24 #include <inttypes.h>
25 #include <set>
26
27 #include "basic_result_processor.h"
28 #include "depth_process_block.h"
29 #include "hal_utils.h"
30 #include "hdrplus_process_block.h"
31 #include "hdrplus_request_processor.h"
32 #include "hdrplus_result_processor.h"
33 #include "multicam_realtime_process_block.h"
34 #include "rgbird_capture_session.h"
35 #include "rgbird_depth_result_processor.h"
36 #include "rgbird_result_request_processor.h"
37 #include "rgbird_rt_request_processor.h"
38
39 namespace android {
40 namespace google_camera_hal {
41
IsStreamConfigurationSupported(CameraDeviceSessionHwl * device_session_hwl,const StreamConfiguration &)42 bool RgbirdCaptureSession::IsStreamConfigurationSupported(
43 CameraDeviceSessionHwl* device_session_hwl,
44 const StreamConfiguration& /*stream_config*/) {
45 ATRACE_CALL();
46 if (device_session_hwl == nullptr) {
47 ALOGE("%s: device_session_hwl is nullptr", __FUNCTION__);
48 return false;
49 }
50
51 std::vector<uint32_t> physical_camera_ids =
52 device_session_hwl->GetPhysicalCameraIds();
53 if (physical_camera_ids.size() != 3) {
54 ALOGD("%s: RgbirdCaptureSession doesn't support %zu physical cameras",
55 __FUNCTION__, physical_camera_ids.size());
56 return false;
57 }
58
59 // Check if this is a logical camera containing two IR cameras.
60 uint32_t num_ir_camera = 0;
61 for (auto id : physical_camera_ids) {
62 std::unique_ptr<HalCameraMetadata> characteristics;
63 status_t res = device_session_hwl->GetPhysicalCameraCharacteristics(
64 id, &characteristics);
65
66 if (res != OK) {
67 ALOGE("%s: Cannot get physical camera characteristics for camera %u",
68 __FUNCTION__, id);
69 return false;
70 }
71
72 // TODO(b/129088371): Work around b/129088371 because current IR camera's
73 // CFA is MONO instead of NIR.
74 if (hal_utils::IsIrCamera(characteristics.get()) ||
75 hal_utils::IsMonoCamera(characteristics.get())) {
76 num_ir_camera++;
77 }
78 }
79
80 if (num_ir_camera != 2) {
81 ALOGD("%s: RgbirdCaptureSession only supports 2 ir cameras", __FUNCTION__);
82 return false;
83 }
84
85 ALOGD("%s: RgbirdCaptureSession supports the stream config", __FUNCTION__);
86 return true;
87 }
88
Create(CameraDeviceSessionHwl * device_session_hwl,const StreamConfiguration & stream_config,ProcessCaptureResultFunc process_capture_result,NotifyFunc notify,HwlSessionCallback session_callback,std::vector<HalStream> * hal_configured_streams,CameraBufferAllocatorHwl *)89 std::unique_ptr<CaptureSession> RgbirdCaptureSession::Create(
90 CameraDeviceSessionHwl* device_session_hwl,
91 const StreamConfiguration& stream_config,
92 ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
93 HwlSessionCallback session_callback,
94 std::vector<HalStream>* hal_configured_streams,
95 CameraBufferAllocatorHwl* /*camera_allocator_hwl*/) {
96 ATRACE_CALL();
97 auto session =
98 std::unique_ptr<RgbirdCaptureSession>(new RgbirdCaptureSession());
99 if (session == nullptr) {
100 ALOGE("%s: Creating RgbirdCaptureSession failed.", __FUNCTION__);
101 return nullptr;
102 }
103
104 status_t res = session->Initialize(
105 device_session_hwl, stream_config, process_capture_result, notify,
106 session_callback.request_stream_buffers, hal_configured_streams);
107 if (res != OK) {
108 ALOGE("%s: Initializing RgbirdCaptureSession failed: %s (%d).",
109 __FUNCTION__, strerror(-res), res);
110 return nullptr;
111 }
112
113 return session;
114 }
115
~RgbirdCaptureSession()116 RgbirdCaptureSession::~RgbirdCaptureSession() {
117 if (device_session_hwl_ != nullptr) {
118 device_session_hwl_->DestroyPipelines();
119 }
120
121 rt_request_processor_ = nullptr;
122 hdrplus_request_processor_ = nullptr;
123 result_dispatcher_ = nullptr;
124 }
125
AreAllStreamsConfigured(const StreamConfiguration & stream_config,const StreamConfiguration & process_block_stream_config) const126 bool RgbirdCaptureSession::AreAllStreamsConfigured(
127 const StreamConfiguration& stream_config,
128 const StreamConfiguration& process_block_stream_config) const {
129 ATRACE_CALL();
130 // Check all streams are configured.
131 if (stream_config.streams.size() > process_block_stream_config.streams.size()) {
132 ALOGE("%s: stream_config has %zu streams but only configured %zu streams",
133 __FUNCTION__, stream_config.streams.size(),
134 process_block_stream_config.streams.size());
135 return false;
136 }
137
138 for (auto& stream : stream_config.streams) {
139 bool found = false;
140 for (auto& configured_stream : process_block_stream_config.streams) {
141 if (stream.id == configured_stream.id) {
142 found = true;
143 break;
144 }
145 }
146
147 if (!found) {
148 ALOGE("%s: Cannot find stream %u in configured streams.", __FUNCTION__,
149 stream.id);
150 return false;
151 }
152 }
153
154 return true;
155 }
156
ConfigureStreams(const StreamConfiguration & stream_config,RequestProcessor * request_processor,ProcessBlock * process_block,StreamConfiguration * process_block_stream_config)157 status_t RgbirdCaptureSession::ConfigureStreams(
158 const StreamConfiguration& stream_config,
159 RequestProcessor* request_processor, ProcessBlock* process_block,
160 StreamConfiguration* process_block_stream_config) {
161 ATRACE_CALL();
162 if (request_processor == nullptr || process_block == nullptr ||
163 process_block_stream_config == nullptr) {
164 ALOGE(
165 "%s: request_processor(%p) or process_block(%p) or "
166 "process_block_stream_config(%p) is nullptr",
167 __FUNCTION__, request_processor, process_block,
168 process_block_stream_config);
169 return BAD_VALUE;
170 }
171
172 status_t res = request_processor->ConfigureStreams(
173 internal_stream_manager_.get(), stream_config,
174 process_block_stream_config);
175 if (res != OK) {
176 ALOGE("%s: Configuring stream for RequestProcessor failed: %s(%d)",
177 __FUNCTION__, strerror(-res), res);
178 return res;
179 }
180
181 res = process_block->ConfigureStreams(*process_block_stream_config,
182 stream_config);
183 if (res != OK) {
184 ALOGE("%s: Configuring streams for ProcessBlock failed: %s(%d)",
185 __FUNCTION__, strerror(-res), res);
186 return res;
187 }
188
189 return OK;
190 }
191
SetDepthInternalStreamId(const StreamConfiguration & process_block_stream_config,const StreamConfiguration & stream_config)192 status_t RgbirdCaptureSession::SetDepthInternalStreamId(
193 const StreamConfiguration& process_block_stream_config,
194 const StreamConfiguration& stream_config) {
195 // Assuming there is at most one internal YUV stream configured when this
196 // function is called(i.e. when depth stream is configured).
197 for (auto& configured_stream : process_block_stream_config.streams) {
198 if (configured_stream.format == HAL_PIXEL_FORMAT_YCBCR_420_888) {
199 bool matching_found = false;
200 for (auto& framework_stream : stream_config.streams) {
201 if (configured_stream.id == framework_stream.id) {
202 matching_found = true;
203 break;
204 }
205 }
206 if (!matching_found) {
207 rgb_internal_yuv_stream_id_ = configured_stream.id;
208 }
209 } else if (configured_stream.format == HAL_PIXEL_FORMAT_Y8) {
210 if (configured_stream.physical_camera_id == ir1_camera_id_) {
211 ir1_internal_raw_stream_id_ = configured_stream.id;
212 } else if (configured_stream.physical_camera_id == ir2_camera_id_) {
213 ir2_internal_raw_stream_id_ = configured_stream.id;
214 } else {
215 ALOGV("%s: Y8 stream found from non-IR sensors.", __FUNCTION__);
216 }
217 }
218 }
219
220 if (rgb_internal_yuv_stream_id_ == kInvalidStreamId ||
221 ir1_internal_raw_stream_id_ == kInvalidStreamId ||
222 ir2_internal_raw_stream_id_ == kInvalidStreamId) {
223 ALOGE(
224 "%s: Internal YUV or IR stream not found in "
225 "process_block_stream_config.",
226 __FUNCTION__);
227 return UNKNOWN_ERROR;
228 }
229
230 return OK;
231 }
232
ConfigureHdrplusRawStreamId(const StreamConfiguration & process_block_stream_config)233 status_t RgbirdCaptureSession::ConfigureHdrplusRawStreamId(
234 const StreamConfiguration& process_block_stream_config) {
235 ATRACE_CALL();
236 std::unique_ptr<HalCameraMetadata> characteristics;
237 status_t res = device_session_hwl_->GetCameraCharacteristics(&characteristics);
238 if (res != OK) {
239 ALOGE("%s: GetCameraCharacteristics failed.", __FUNCTION__);
240 return BAD_VALUE;
241 }
242
243 uint32_t active_array_width, active_array_height;
244 camera_metadata_ro_entry entry;
245 res = characteristics->Get(
246 ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, &entry);
247 if (res == OK) {
248 active_array_width = entry.data.i32[2];
249 active_array_height = entry.data.i32[3];
250 ALOGI("%s Active size (%d x %d).", __FUNCTION__, active_array_width,
251 active_array_height);
252 } else {
253 ALOGE("%s Get active size failed: %s (%d).", __FUNCTION__, strerror(-res),
254 res);
255 return UNKNOWN_ERROR;
256 }
257
258 for (auto& configured_stream : process_block_stream_config.streams) {
259 if (configured_stream.format == kHdrplusRawFormat &&
260 configured_stream.width == active_array_width &&
261 configured_stream.height == active_array_height) {
262 rgb_raw_stream_id_ = configured_stream.id;
263 break;
264 }
265 }
266
267 if (rgb_raw_stream_id_ == -1) {
268 ALOGE("%s: Configuring stream fail due to wrong raw_stream_id",
269 __FUNCTION__);
270 return UNKNOWN_ERROR;
271 }
272
273 return OK;
274 }
275
AllocateInternalBuffers(const StreamConfiguration & framework_stream_config,std::vector<HalStream> * hal_configured_streams,ProcessBlock * hdrplus_process_block)276 status_t RgbirdCaptureSession::AllocateInternalBuffers(
277 const StreamConfiguration& framework_stream_config,
278 std::vector<HalStream>* hal_configured_streams,
279 ProcessBlock* hdrplus_process_block) {
280 ATRACE_CALL();
281 status_t res = OK;
282
283 std::set<int32_t> framework_stream_id_set;
284 for (auto& stream : framework_stream_config.streams) {
285 framework_stream_id_set.insert(stream.id);
286 }
287
288 for (uint32_t i = 0; i < hal_configured_streams->size(); i++) {
289 HalStream& hal_stream = hal_configured_streams->at(i);
290
291 if (framework_stream_id_set.find(hal_stream.id) ==
292 framework_stream_id_set.end()) {
293 // hdrplus rgb raw stream buffers is allocated separately
294 if (hal_stream.id == rgb_raw_stream_id_) {
295 continue;
296 }
297
298 uint32_t additional_num_buffers =
299 (hal_stream.max_buffers >= kDefaultInternalBufferCount)
300 ? 0
301 : (kDefaultInternalBufferCount - hal_stream.max_buffers);
302 res = internal_stream_manager_->AllocateBuffers(
303 hal_stream, hal_stream.max_buffers + additional_num_buffers);
304 if (res != OK) {
305 ALOGE("%s: Failed to allocate buffer for internal stream %d: %s(%d)",
306 __FUNCTION__, hal_stream.id, strerror(-res), res);
307 return res;
308 } else {
309 ALOGI("%s: Allocating %d internal buffers for stream %d", __FUNCTION__,
310 additional_num_buffers + hal_stream.max_buffers, hal_stream.id);
311 }
312 }
313 }
314
315 if (is_hdrplus_supported_) {
316 std::vector<HalStream> hdrplus_hal_configured_streams;
317 res = hdrplus_process_block->GetConfiguredHalStreams(
318 &hdrplus_hal_configured_streams);
319 if (res != OK) {
320 ALOGE("%s: Getting HDR+ HAL streams failed: %s(%d)", __FUNCTION__,
321 strerror(-res), res);
322 return res;
323 }
324
325 res = ConfigureHdrplusUsageAndBuffers(hal_configured_streams,
326 &hdrplus_hal_configured_streams);
327 if (res != OK) {
328 ALOGE("%s: ConfigureHdrplusUsageAndBuffer failed: %s(%d)", __FUNCTION__,
329 strerror(-res), res);
330 return res;
331 }
332 }
333 return res;
334 }
335
PurgeHalConfiguredStream(const StreamConfiguration & stream_config,std::vector<HalStream> * hal_configured_streams)336 status_t RgbirdCaptureSession::PurgeHalConfiguredStream(
337 const StreamConfiguration& stream_config,
338 std::vector<HalStream>* hal_configured_streams) {
339 if (hal_configured_streams == nullptr) {
340 ALOGE("%s: HAL configured stream list is null.", __FUNCTION__);
341 return BAD_VALUE;
342 }
343
344 std::set<int32_t> framework_stream_id_set;
345 for (auto& stream : stream_config.streams) {
346 framework_stream_id_set.insert(stream.id);
347 }
348
349 std::vector<HalStream> configured_streams;
350 for (auto& hal_stream : *hal_configured_streams) {
351 if (framework_stream_id_set.find(hal_stream.id) !=
352 framework_stream_id_set.end()) {
353 configured_streams.push_back(hal_stream);
354 }
355 }
356 *hal_configured_streams = configured_streams;
357 return OK;
358 }
359
NeedDepthProcessBlock() const360 bool RgbirdCaptureSession::NeedDepthProcessBlock() const {
361 // TODO(b/128633958): remove force flag after FLL syncing is verified
362 return force_internal_stream_ || has_depth_stream_;
363 }
364
CreateDepthChainSegment(std::unique_ptr<DepthProcessBlock> * depth_process_block,std::unique_ptr<RgbirdDepthResultProcessor> * depth_result_processor,RgbirdResultRequestProcessor * rt_result_processor,const StreamConfiguration & stream_config,const StreamConfiguration & overall_config,StreamConfiguration * depth_block_stream_config)365 status_t RgbirdCaptureSession::CreateDepthChainSegment(
366 std::unique_ptr<DepthProcessBlock>* depth_process_block,
367 std::unique_ptr<RgbirdDepthResultProcessor>* depth_result_processor,
368 RgbirdResultRequestProcessor* rt_result_processor,
369 const StreamConfiguration& stream_config,
370 const StreamConfiguration& overall_config,
371 StreamConfiguration* depth_block_stream_config) {
372 ATRACE_CALL();
373 DepthProcessBlock::DepthProcessBlockCreateData data = {
374 .rgb_internal_yuv_stream_id = rgb_internal_yuv_stream_id_,
375 .ir1_internal_raw_stream_id = ir1_internal_raw_stream_id_,
376 .ir2_internal_raw_stream_id = ir2_internal_raw_stream_id_};
377 auto process_block = DepthProcessBlock::Create(device_session_hwl_,
378 request_stream_buffers_, data);
379 if (process_block == nullptr) {
380 ALOGE("%s: Creating DepthProcessBlock failed.", __FUNCTION__);
381 return UNKNOWN_ERROR;
382 }
383
384 auto result_processor =
385 RgbirdDepthResultProcessor::Create(internal_stream_manager_.get());
386 if (result_processor == nullptr) {
387 ALOGE("%s: Creating RgbirdDepthResultProcessor", __FUNCTION__);
388 return UNKNOWN_ERROR;
389 }
390
391 status_t res = rt_result_processor->ConfigureStreams(
392 internal_stream_manager_.get(), stream_config, depth_block_stream_config);
393 if (res != OK) {
394 ALOGE("%s: Configuring streams for ResultRequestProcessor failed: %s(%d)",
395 __FUNCTION__, strerror(-res), res);
396 return res;
397 }
398
399 res = process_block->ConfigureStreams(*depth_block_stream_config,
400 overall_config);
401 if (res != OK) {
402 ALOGE("%s: Configuring streams for DepthProcessBlock failed: %s(%d)",
403 __FUNCTION__, strerror(-res), res);
404 return res;
405 }
406
407 *depth_process_block = std::move(process_block);
408 *depth_result_processor = std::move(result_processor);
409
410 return OK;
411 }
412
SetupDepthChainSegment(const StreamConfiguration & stream_config,RgbirdResultRequestProcessor * realtime_result_processor,std::unique_ptr<ProcessBlock> * depth_process_block,std::unique_ptr<ResultProcessor> * depth_result_processor,StreamConfiguration * rt_process_block_stream_config)413 status_t RgbirdCaptureSession::SetupDepthChainSegment(
414 const StreamConfiguration& stream_config,
415 RgbirdResultRequestProcessor* realtime_result_processor,
416 std::unique_ptr<ProcessBlock>* depth_process_block,
417 std::unique_ptr<ResultProcessor>* depth_result_processor,
418 StreamConfiguration* rt_process_block_stream_config) {
419 ATRACE_CALL();
420 // Create the depth segment of realtime process chain if need depth processing
421 std::unique_ptr<DepthProcessBlock> d_process_block;
422 std::unique_ptr<RgbirdDepthResultProcessor> d_result_processor;
423 if (NeedDepthProcessBlock()) {
424 StreamConfiguration depth_chain_segment_stream_config;
425 status_t res =
426 MakeDepthStreamConfig(*rt_process_block_stream_config, stream_config,
427 &depth_chain_segment_stream_config);
428 if (res != OK) {
429 ALOGE(
430 "%s: Making depth chain segment stream configuration failed: "
431 "%s(%d).",
432 __FUNCTION__, strerror(-res), res);
433 return res;
434 }
435
436 StreamConfiguration depth_block_stream_config;
437 res = CreateDepthChainSegment(&d_process_block, &d_result_processor,
438 realtime_result_processor,
439 depth_chain_segment_stream_config,
440 stream_config, &depth_block_stream_config);
441 if (res != OK) {
442 ALOGE("%s: Creating depth chain segment failed: %s(%d).", __FUNCTION__,
443 strerror(-res), res);
444 return res;
445 }
446
447 // process_block_stream_config may contain internal streams(some may be
448 // duplicated as both input and output for bridging the rt and depth
449 // segments of the realtime process chain.)
450 rt_process_block_stream_config->streams.insert(
451 rt_process_block_stream_config->streams.end(),
452 depth_block_stream_config.streams.begin(),
453 depth_block_stream_config.streams.end());
454
455 *depth_process_block = std::move(d_process_block);
456 *depth_result_processor = std::move(d_result_processor);
457 }
458
459 return OK;
460 }
461
MakeDepthStreamConfig(const StreamConfiguration & rt_process_block_stream_config,const StreamConfiguration & stream_config,StreamConfiguration * depth_stream_config)462 status_t RgbirdCaptureSession::MakeDepthStreamConfig(
463 const StreamConfiguration& rt_process_block_stream_config,
464 const StreamConfiguration& stream_config,
465 StreamConfiguration* depth_stream_config) {
466 ATRACE_CALL();
467 if (depth_stream_config == nullptr) {
468 ALOGE("%s: depth_stream_config is nullptr", __FUNCTION__);
469 return BAD_VALUE;
470 }
471
472 if (!NeedDepthProcessBlock()) {
473 ALOGE("%s: No need to create depth process chain segment stream config.",
474 __FUNCTION__);
475 return BAD_VALUE;
476 }
477
478 // Assuming all internal streams must be for depth process block as input,
479 // if depth stream is configured by framework.
480 depth_stream_config->operation_mode = stream_config.operation_mode;
481 depth_stream_config->session_params =
482 HalCameraMetadata::Clone(stream_config.session_params.get());
483 depth_stream_config->stream_config_counter =
484 stream_config.stream_config_counter;
485 depth_stream_config->streams = stream_config.streams;
486 for (auto& stream : rt_process_block_stream_config.streams) {
487 bool is_internal_stream = true;
488 for (auto& framework_stream : stream_config.streams) {
489 if (stream.id == framework_stream.id) {
490 is_internal_stream = false;
491 break;
492 }
493 }
494
495 // Change all internal streams to input streams and keep others untouched
496 if (is_internal_stream) {
497 Stream input_stream = stream;
498 input_stream.stream_type = StreamType::kInput;
499 depth_stream_config->streams.push_back(input_stream);
500 }
501 }
502
503 return OK;
504 }
505
SetupRealtimeProcessChain(const StreamConfiguration & stream_config,ProcessCaptureResultFunc process_capture_result,NotifyFunc notify,std::unique_ptr<ProcessBlock> * realtime_process_block,std::unique_ptr<RgbirdResultRequestProcessor> * realtime_result_processor,std::unique_ptr<ProcessBlock> * depth_process_block,std::unique_ptr<ResultProcessor> * depth_result_processor)506 status_t RgbirdCaptureSession::SetupRealtimeProcessChain(
507 const StreamConfiguration& stream_config,
508 ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
509 std::unique_ptr<ProcessBlock>* realtime_process_block,
510 std::unique_ptr<RgbirdResultRequestProcessor>* realtime_result_processor,
511 std::unique_ptr<ProcessBlock>* depth_process_block,
512 std::unique_ptr<ResultProcessor>* depth_result_processor) {
513 ATRACE_CALL();
514 if (realtime_process_block == nullptr ||
515 realtime_result_processor == nullptr) {
516 ALOGE("%s: realtime_process_block(%p) or realtime_result_processor(%p) or ",
517 __FUNCTION__, realtime_process_block, realtime_result_processor);
518 return BAD_VALUE;
519 }
520
521 auto rt_process_block = MultiCameraRtProcessBlock::Create(device_session_hwl_);
522 if (rt_process_block == nullptr) {
523 ALOGE("%s: Creating RealtimeProcessBlock failed.", __FUNCTION__);
524 return UNKNOWN_ERROR;
525 }
526
527 // TODO(b/128632740): Create and connect depth process block.
528 rt_request_processor_ = RgbirdRtRequestProcessor::Create(
529 device_session_hwl_, is_hdrplus_supported_);
530 if (rt_request_processor_ == nullptr) {
531 ALOGE("%s: Creating RealtimeZslsRequestProcessor failed.", __FUNCTION__);
532 return UNKNOWN_ERROR;
533 }
534
535 StreamConfiguration process_block_stream_config;
536 status_t res =
537 ConfigureStreams(stream_config, rt_request_processor_.get(),
538 rt_process_block.get(), &process_block_stream_config);
539 if (res != OK) {
540 ALOGE("%s: Configuring stream failed: %s(%d)", __FUNCTION__, strerror(-res),
541 res);
542 return res;
543 }
544
545 if (is_hdrplus_supported_) {
546 res = ConfigureHdrplusRawStreamId(process_block_stream_config);
547 if (res != OK) {
548 ALOGE("%s: ConfigureHdrplusRawStreamId failed: %s(%d)", __FUNCTION__,
549 strerror(-res), res);
550 return res;
551 }
552 }
553
554 if (has_depth_stream_) {
555 res = SetDepthInternalStreamId(process_block_stream_config, stream_config);
556 if (res != OK) {
557 ALOGE("%s: ConfigureDepthOnlyRawStreamId failed: %s(%d)", __FUNCTION__,
558 strerror(-res), res);
559 return res;
560 }
561 }
562
563 // Create realtime result processor.
564 RgbirdResultRequestProcessor::RgbirdResultRequestProcessorCreateData data = {
565 .rgb_camera_id = rgb_camera_id_,
566 .ir1_camera_id = ir1_camera_id_,
567 .ir2_camera_id = ir2_camera_id_,
568 .rgb_raw_stream_id = rgb_raw_stream_id_,
569 .is_hdrplus_supported = is_hdrplus_supported_,
570 .rgb_internal_yuv_stream_id = rgb_internal_yuv_stream_id_};
571 auto rt_result_processor = RgbirdResultRequestProcessor::Create(data);
572 if (rt_result_processor == nullptr) {
573 ALOGE("%s: Creating RgbirdResultRequestProcessor failed.", __FUNCTION__);
574 return UNKNOWN_ERROR;
575 }
576 rt_result_processor->SetResultCallback(process_capture_result, notify);
577
578 if (is_hdrplus_supported_) {
579 res = rt_result_processor->ConfigureStreams(internal_stream_manager_.get(),
580 stream_config,
581 &process_block_stream_config);
582 if (res != OK) {
583 ALOGE("%s: Configuring streams for ResultRequestProcessor failed: %s(%d)",
584 __FUNCTION__, strerror(-res), res);
585 return res;
586 }
587 }
588
589 res = SetupDepthChainSegment(stream_config, rt_result_processor.get(),
590 depth_process_block, depth_result_processor,
591 &process_block_stream_config);
592 if (res != OK) {
593 ALOGE("%s: Failed to setup depth chain segment.", __FUNCTION__);
594 return UNKNOWN_ERROR;
595 }
596
597 // TODO(b/128632740): Remove force internal flag after depth block is in place
598 // and the FLL sync is verified.
599 // This should be done after depth process block stream
600 // configuration.
601 if (!AreAllStreamsConfigured(stream_config, process_block_stream_config) &&
602 !force_internal_stream_) {
603 // TODO(b/127322570): Handle the case where RT request processor configures
604 // internal streams for depth.
605 ALOGE("%s: Not all streams are configured.", __FUNCTION__);
606 return INVALID_OPERATION;
607 }
608
609 *realtime_process_block = std::move(rt_process_block);
610 *realtime_result_processor = std::move(rt_result_processor);
611
612 return OK;
613 }
614
SetupHdrplusProcessChain(const StreamConfiguration & stream_config,ProcessCaptureResultFunc process_capture_result,NotifyFunc notify,std::unique_ptr<ProcessBlock> * hdrplus_process_block,std::unique_ptr<ResultProcessor> * hdrplus_result_processor)615 status_t RgbirdCaptureSession::SetupHdrplusProcessChain(
616 const StreamConfiguration& stream_config,
617 ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
618 std::unique_ptr<ProcessBlock>* hdrplus_process_block,
619 std::unique_ptr<ResultProcessor>* hdrplus_result_processor) {
620 ATRACE_CALL();
621 if (hdrplus_process_block == nullptr || hdrplus_result_processor == nullptr) {
622 ALOGE(
623 "%s: hdrplus_process_block(%p) or hdrplus_result_processor(%p) is "
624 "nullptr",
625 __FUNCTION__, hdrplus_process_block, hdrplus_result_processor);
626 return BAD_VALUE;
627 }
628
629 // Create hdrplus process block.
630 std::vector<uint32_t> physical_camera_ids =
631 device_session_hwl_->GetPhysicalCameraIds();
632 // TODO: Check the static metadata and determine which one is rgb camera
633 auto process_block =
634 HdrplusProcessBlock::Create(device_session_hwl_, physical_camera_ids[0]);
635 if (process_block == nullptr) {
636 ALOGE("%s: Creating HdrplusProcessBlock failed.", __FUNCTION__);
637 return UNKNOWN_ERROR;
638 }
639
640 // Create hdrplus request processor.
641 hdrplus_request_processor_ = HdrplusRequestProcessor::Create(
642 device_session_hwl_, rgb_raw_stream_id_, physical_camera_ids[0]);
643 if (hdrplus_request_processor_ == nullptr) {
644 ALOGE("%s: Creating HdrplusRequestProcessor failed.", __FUNCTION__);
645 return UNKNOWN_ERROR;
646 }
647
648 // Create hdrplus result processor.
649 auto result_processor = HdrplusResultProcessor::Create(
650 internal_stream_manager_.get(), rgb_raw_stream_id_);
651 if (result_processor == nullptr) {
652 ALOGE("%s: Creating HdrplusResultProcessor failed.", __FUNCTION__);
653 return UNKNOWN_ERROR;
654 }
655 result_processor->SetResultCallback(process_capture_result, notify);
656
657 StreamConfiguration process_block_stream_config;
658 status_t res =
659 ConfigureStreams(stream_config, hdrplus_request_processor_.get(),
660 process_block.get(), &process_block_stream_config);
661 if (res != OK) {
662 ALOGE("%s: Configuring hdrplus stream failed: %s(%d)", __FUNCTION__,
663 strerror(-res), res);
664 return res;
665 }
666
667 *hdrplus_process_block = std::move(process_block);
668 *hdrplus_result_processor = std::move(result_processor);
669
670 return OK;
671 }
672
CreateProcessChain(const StreamConfiguration & stream_config,ProcessCaptureResultFunc process_capture_result,NotifyFunc notify,std::vector<HalStream> * hal_configured_streams)673 status_t RgbirdCaptureSession::CreateProcessChain(
674 const StreamConfiguration& stream_config,
675 ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
676 std::vector<HalStream>* hal_configured_streams) {
677 ATRACE_CALL();
678 // Setup realtime process chain
679 std::unique_ptr<ProcessBlock> realtime_process_block;
680 std::unique_ptr<RgbirdResultRequestProcessor> realtime_result_processor;
681 std::unique_ptr<ProcessBlock> depth_process_block;
682 std::unique_ptr<ResultProcessor> depth_result_processor;
683
684 status_t res = SetupRealtimeProcessChain(
685 stream_config, process_capture_result, notify, &realtime_process_block,
686 &realtime_result_processor, &depth_process_block, &depth_result_processor);
687 if (res != OK) {
688 ALOGE("%s: SetupRealtimeProcessChain fail: %s(%d)", __FUNCTION__,
689 strerror(-res), res);
690 return res;
691 }
692
693 // Setup hdrplus process chain
694 std::unique_ptr<ProcessBlock> hdrplus_process_block;
695 std::unique_ptr<ResultProcessor> hdrplus_result_processor;
696 if (is_hdrplus_supported_) {
697 res = SetupHdrplusProcessChain(stream_config, process_capture_result,
698 notify, &hdrplus_process_block,
699 &hdrplus_result_processor);
700 if (res != OK) {
701 ALOGE("%s: SetupHdrplusProcessChain fail: %s(%d)", __FUNCTION__,
702 strerror(-res), res);
703 return res;
704 }
705 }
706 // Realtime and HDR+ streams are configured
707 // Start to build pipleline
708 res = BuildPipelines(stream_config, realtime_process_block.get(),
709 depth_process_block.get(), hdrplus_process_block.get(),
710 hal_configured_streams);
711 if (res != OK) {
712 ALOGE("%s: Building pipelines failed: %s(%d)", __FUNCTION__, strerror(-res),
713 res);
714 return res;
715 }
716
717 // Connecting the depth segment of the realtime process chain.
718 if (NeedDepthProcessBlock()) {
719 depth_result_processor->SetResultCallback(process_capture_result, notify);
720
721 res = ConnectProcessChain(realtime_result_processor.get(),
722 std::move(depth_process_block),
723 std::move(depth_result_processor));
724 if (res != OK) {
725 ALOGE("%s: Connecting depth segment of realtime chain failed: %s(%d)",
726 __FUNCTION__, strerror(-res), res);
727 return res;
728 }
729 }
730
731 // Connect realtime process chain
732 res = ConnectProcessChain(rt_request_processor_.get(),
733 std::move(realtime_process_block),
734 std::move(realtime_result_processor));
735 if (res != OK) {
736 ALOGE("%s: Connecting process chain failed: %s(%d)", __FUNCTION__,
737 strerror(-res), res);
738 return res;
739 }
740
741 if (is_hdrplus_supported_) {
742 // Connect HDR+ process chain
743 res = ConnectProcessChain(hdrplus_request_processor_.get(),
744 std::move(hdrplus_process_block),
745 std::move(hdrplus_result_processor));
746 if (res != OK) {
747 ALOGE("%s: Connecting HDR+ process chain failed: %s(%d)", __FUNCTION__,
748 strerror(-res), res);
749 return res;
750 }
751 }
752 return OK;
753 }
754
ConnectProcessChain(RequestProcessor * request_processor,std::unique_ptr<ProcessBlock> process_block,std::unique_ptr<ResultProcessor> result_processor)755 status_t RgbirdCaptureSession::ConnectProcessChain(
756 RequestProcessor* request_processor,
757 std::unique_ptr<ProcessBlock> process_block,
758 std::unique_ptr<ResultProcessor> result_processor) {
759 ATRACE_CALL();
760 if (request_processor == nullptr) {
761 ALOGE("%s: request_processor is nullptr", __FUNCTION__);
762 return BAD_VALUE;
763 }
764
765 status_t res = process_block->SetResultProcessor(std::move(result_processor));
766 if (res != OK) {
767 ALOGE("%s: Setting result process in process block failed.", __FUNCTION__);
768 return res;
769 }
770
771 res = request_processor->SetProcessBlock(std::move(process_block));
772 if (res != OK) {
773 ALOGE("%s: Setting process block for request processor failed: %s(%d)",
774 __FUNCTION__, strerror(-res), res);
775 return res;
776 }
777
778 return OK;
779 }
780
ConfigureHdrplusUsageAndBuffers(std::vector<HalStream> * hal_configured_streams,std::vector<HalStream> * hdrplus_hal_configured_streams)781 status_t RgbirdCaptureSession::ConfigureHdrplusUsageAndBuffers(
782 std::vector<HalStream>* hal_configured_streams,
783 std::vector<HalStream>* hdrplus_hal_configured_streams) {
784 ATRACE_CALL();
785 if (hal_configured_streams == nullptr ||
786 hdrplus_hal_configured_streams == nullptr) {
787 ALOGE(
788 "%s: hal_configured_streams (%p) or hdrplus_hal_configured_streams "
789 "(%p) is nullptr",
790 __FUNCTION__, hal_configured_streams, hdrplus_hal_configured_streams);
791 return BAD_VALUE;
792 }
793 // Combine realtime and HDR+ hal stream.
794 // Only usage of internal raw stream is different, so combine usage directly
795 uint64_t consumer_usage = 0;
796 for (uint32_t i = 0; i < (*hdrplus_hal_configured_streams).size(); i++) {
797 if (hdrplus_hal_configured_streams->at(i).override_format ==
798 kHdrplusRawFormat &&
799 hdrplus_hal_configured_streams->at(i).id == rgb_raw_stream_id_) {
800 consumer_usage = hdrplus_hal_configured_streams->at(i).consumer_usage;
801 break;
802 }
803 }
804
805 for (uint32_t i = 0; i < hal_configured_streams->size(); i++) {
806 if (hal_configured_streams->at(i).override_format == kHdrplusRawFormat &&
807 hal_configured_streams->at(i).id == rgb_raw_stream_id_) {
808 hal_configured_streams->at(i).consumer_usage = consumer_usage;
809 // Allocate internal raw stream buffers
810 if (hal_configured_streams->at(i).max_buffers < kRgbMinRawBufferCount) {
811 hal_configured_streams->at(i).max_buffers = kRgbMinRawBufferCount;
812 }
813
814 uint32_t additional_num_buffers =
815 (hal_configured_streams->at(i).max_buffers >= kRgbRawBufferCount)
816 ? 0
817 : (kRgbRawBufferCount - hal_configured_streams->at(i).max_buffers);
818 status_t res = internal_stream_manager_->AllocateBuffers(
819 hal_configured_streams->at(i), additional_num_buffers);
820 if (res != OK) {
821 ALOGE("%s: AllocateBuffers failed.", __FUNCTION__);
822 return UNKNOWN_ERROR;
823 }
824 break;
825 }
826 }
827
828 return OK;
829 }
830
BuildPipelines(const StreamConfiguration & stream_config,ProcessBlock * realtime_process_block,ProcessBlock * depth_process_block,ProcessBlock * hdrplus_process_block,std::vector<HalStream> * hal_configured_streams)831 status_t RgbirdCaptureSession::BuildPipelines(
832 const StreamConfiguration& stream_config,
833 ProcessBlock* realtime_process_block, ProcessBlock* depth_process_block,
834 ProcessBlock* hdrplus_process_block,
835 std::vector<HalStream>* hal_configured_streams) {
836 ATRACE_CALL();
837 if (realtime_process_block == nullptr) {
838 ALOGE("%s: realtime_process_block (%p) is nullptr", __FUNCTION__,
839 realtime_process_block);
840 return BAD_VALUE;
841 }
842
843 if (depth_process_block == nullptr && has_depth_stream_) {
844 ALOGE("%s: depth_process_block (%p) is nullptr", __FUNCTION__,
845 depth_process_block);
846 return BAD_VALUE;
847 }
848
849 if (hal_configured_streams == nullptr) {
850 ALOGE("%s: hal_configured_streams (%p) is nullptr", __FUNCTION__,
851 hal_configured_streams);
852 return BAD_VALUE;
853 }
854
855 if (is_hdrplus_supported_ && hdrplus_process_block == nullptr) {
856 ALOGE("%s: hdrplus_process_block is nullptr", __FUNCTION__);
857 return BAD_VALUE;
858 }
859
860 status_t res = device_session_hwl_->BuildPipelines();
861 if (res != OK) {
862 ALOGE("%s: Building pipelines failed: %s(%d)", __FUNCTION__, strerror(-res),
863 res);
864 return res;
865 }
866
867 res = realtime_process_block->GetConfiguredHalStreams(hal_configured_streams);
868 if (res != OK) {
869 ALOGE("%s: Getting HAL streams failed: %s(%d)", __FUNCTION__,
870 strerror(-res), res);
871 return res;
872 }
873
874 res = AllocateInternalBuffers(stream_config, hal_configured_streams,
875 hdrplus_process_block);
876
877 // Need to update hal_configured_streams if there is a depth stream
878 std::vector<HalStream> depth_streams;
879 if (has_depth_stream_) {
880 res = depth_process_block->GetConfiguredHalStreams(&depth_streams);
881 if (res != OK) {
882 ALOGE("%s: Failed to get configured hal streams from DepthProcessBlock",
883 __FUNCTION__);
884 return UNKNOWN_ERROR;
885 }
886
887 // Depth Process Block can only configure one depth stream so far
888 if (depth_streams.size() != 1) {
889 ALOGE("%s: DepthProcessBlock configured more than one stream.",
890 __FUNCTION__);
891 return UNKNOWN_ERROR;
892 }
893
894 hal_configured_streams->push_back(depth_streams[0]);
895 }
896
897 if (res != OK) {
898 ALOGE("%s: Allocating buffer for internal stream managers failed: %s(%d)",
899 __FUNCTION__, strerror(-res), res);
900 return res;
901 }
902
903 hal_utils::DumpHalConfiguredStreams(*hal_configured_streams,
904 "hal_configured_streams BEFORE purge");
905
906 // TODO(b/128633958): cover the streams Depth PB processes
907 res = PurgeHalConfiguredStream(stream_config, hal_configured_streams);
908 if (res != OK) {
909 ALOGE("%s: Removing internal streams from configured stream failed: %s(%d)",
910 __FUNCTION__, strerror(-res), res);
911 return res;
912 }
913
914 hal_utils::DumpHalConfiguredStreams(*hal_configured_streams,
915 "hal_configured_streams AFTER purge");
916
917 return OK;
918 }
919
InitializeCameraIds(CameraDeviceSessionHwl * device_session_hwl)920 status_t RgbirdCaptureSession::InitializeCameraIds(
921 CameraDeviceSessionHwl* device_session_hwl) {
922 ATRACE_CALL();
923 if (device_session_hwl == nullptr) {
924 ALOGE("%s: Device session hwl is null.", __FUNCTION__);
925 return BAD_VALUE;
926 }
927
928 std::vector<uint32_t> physical_camera_ids =
929 device_session_hwl->GetPhysicalCameraIds();
930 if (physical_camera_ids.size() != 3) {
931 ALOGE("%s: Failed to initialize camera ids. Only support 3 cameras",
932 __FUNCTION__);
933 return UNKNOWN_ERROR;
934 }
935
936 // TODO(b/127322570): Figure out physical camera IDs from static metadata.
937 rgb_camera_id_ = physical_camera_ids[0];
938 ir1_camera_id_ = physical_camera_ids[1];
939 ir2_camera_id_ = physical_camera_ids[2];
940 return OK;
941 }
942
Initialize(CameraDeviceSessionHwl * device_session_hwl,const StreamConfiguration & stream_config,ProcessCaptureResultFunc process_capture_result,NotifyFunc notify,HwlRequestBuffersFunc request_stream_buffers,std::vector<HalStream> * hal_configured_streams)943 status_t RgbirdCaptureSession::Initialize(
944 CameraDeviceSessionHwl* device_session_hwl,
945 const StreamConfiguration& stream_config,
946 ProcessCaptureResultFunc process_capture_result, NotifyFunc notify,
947 HwlRequestBuffersFunc request_stream_buffers,
948 std::vector<HalStream>* hal_configured_streams) {
949 ATRACE_CALL();
950 if (!IsStreamConfigurationSupported(device_session_hwl, stream_config)) {
951 ALOGE("%s: stream configuration is not supported.", __FUNCTION__);
952 return BAD_VALUE;
953 }
954
955 // TODO(b/128633958): remove this after FLL syncing is verified
956 force_internal_stream_ =
957 property_get_bool("persist.vendor.camera.rgbird.forceinternal", false);
958 if (force_internal_stream_) {
959 ALOGI("%s: Force creating internal streams for IR pipelines", __FUNCTION__);
960 }
961
962 device_session_hwl_ = device_session_hwl;
963 internal_stream_manager_ = InternalStreamManager::Create();
964 if (internal_stream_manager_ == nullptr) {
965 ALOGE("%s: Cannot create internal stream manager.", __FUNCTION__);
966 return UNKNOWN_ERROR;
967 }
968
969 std::unique_ptr<HalCameraMetadata> characteristics;
970 status_t res = device_session_hwl->GetCameraCharacteristics(&characteristics);
971 if (res != OK) {
972 ALOGE("%s: GetCameraCharacteristics failed.", __FUNCTION__);
973 return BAD_VALUE;
974 }
975
976 is_hdrplus_supported_ = hal_utils::IsStreamHdrplusCompatible(
977 stream_config, characteristics.get());
978
979 if (is_hdrplus_supported_) {
980 for (auto stream : stream_config.streams) {
981 if (utils::IsPreviewStream(stream)) {
982 hal_preview_stream_id_ = stream.id;
983 break;
984 }
985 }
986 }
987
988 // Create result dispatcher
989 result_dispatcher_ =
990 ResultDispatcher::Create(kPartialResult, process_capture_result, notify);
991 if (result_dispatcher_ == nullptr) {
992 ALOGE("%s: Cannot create result dispatcher.", __FUNCTION__);
993 return UNKNOWN_ERROR;
994 }
995
996 // Reroute callback functions
997 device_session_notify_ = notify;
998 process_capture_result_ =
999 ProcessCaptureResultFunc([this](std::unique_ptr<CaptureResult> result) {
1000 ProcessCaptureResult(std::move(result));
1001 });
1002 notify_ = NotifyFunc(
1003 [this](const NotifyMessage& message) { NotifyHalMessage(message); });
1004 request_stream_buffers_ = request_stream_buffers;
1005
1006 // Initialize physical camera ids
1007 res = InitializeCameraIds(device_session_hwl_);
1008 if (res != OK) {
1009 ALOGE("%s: Initializing camera ids failed: %s(%d)", __FUNCTION__,
1010 strerror(-res), res);
1011 return res;
1012 }
1013
1014 for (auto& stream : stream_config.streams) {
1015 if (utils::IsDepthStream(stream)) {
1016 ALOGI("%s: Depth stream exists in the stream config.", __FUNCTION__);
1017 has_depth_stream_ = true;
1018 }
1019 }
1020
1021 // Finally create the process chains
1022 res = CreateProcessChain(stream_config, process_capture_result_, notify_,
1023 hal_configured_streams);
1024 if (res != OK) {
1025 ALOGE("%s: Creating the process chain failed: %s(%d)", __FUNCTION__,
1026 strerror(-res), res);
1027 return res;
1028 }
1029
1030 return OK;
1031 }
1032
ProcessRequest(const CaptureRequest & request)1033 status_t RgbirdCaptureSession::ProcessRequest(const CaptureRequest& request) {
1034 ATRACE_CALL();
1035 bool is_hdrplus_request = false;
1036 if (is_hdrplus_supported_) {
1037 is_hdrplus_request =
1038 hal_utils::IsRequestHdrplusCompatible(request, hal_preview_stream_id_);
1039 // TODO: Check if request is HDR+ request when contains a depth buffer
1040 }
1041
1042 status_t res = result_dispatcher_->AddPendingRequest(request);
1043 if (res != OK) {
1044 ALOGE("%s: frame(%d) fail to AddPendingRequest", __FUNCTION__,
1045 request.frame_number);
1046 return BAD_VALUE;
1047 }
1048
1049 if (is_hdrplus_request) {
1050 ALOGI("%s: hdrplus snapshot (%d), output stream size:%zu", __FUNCTION__,
1051 request.frame_number, request.output_buffers.size());
1052 res = hdrplus_request_processor_->ProcessRequest(request);
1053 if (res != OK) {
1054 ALOGI("%s: hdrplus snapshot frame(%d) request to realtime process",
1055 __FUNCTION__, request.frame_number);
1056 res = rt_request_processor_->ProcessRequest(request);
1057 }
1058 } else {
1059 res = rt_request_processor_->ProcessRequest(request);
1060 }
1061
1062 if (res != OK) {
1063 ALOGE("%s: ProcessRequest (%d) fail and remove pending request",
1064 __FUNCTION__, request.frame_number);
1065 result_dispatcher_->RemovePendingRequest(request.frame_number);
1066 }
1067 return res;
1068 }
1069
Flush()1070 status_t RgbirdCaptureSession::Flush() {
1071 ATRACE_CALL();
1072 return rt_request_processor_->Flush();
1073 }
1074
ProcessCaptureResult(std::unique_ptr<CaptureResult> result)1075 void RgbirdCaptureSession::ProcessCaptureResult(
1076 std::unique_ptr<CaptureResult> result) {
1077 ATRACE_CALL();
1078 std::lock_guard<std::mutex> lock(callback_lock_);
1079 status_t res = result_dispatcher_->AddResult(std::move(result));
1080 if (res != OK) {
1081 ALOGE("%s: fail to AddResult", __FUNCTION__);
1082 return;
1083 }
1084 }
1085
NotifyHalMessage(const NotifyMessage & message)1086 void RgbirdCaptureSession::NotifyHalMessage(const NotifyMessage& message) {
1087 ATRACE_CALL();
1088 std::lock_guard<std::mutex> lock(callback_lock_);
1089 if (device_session_notify_ == nullptr) {
1090 ALOGE("%s: device_session_notify_ is nullptr. Dropping a message.",
1091 __FUNCTION__);
1092 return;
1093 }
1094
1095 if (message.type == MessageType::kShutter) {
1096 status_t res =
1097 result_dispatcher_->AddShutter(message.message.shutter.frame_number,
1098 message.message.shutter.timestamp_ns);
1099 if (res != OK) {
1100 ALOGE("%s: frame(%d) fail to AddShutter", __FUNCTION__,
1101 message.message.shutter.frame_number);
1102 return;
1103 }
1104 } else if (message.type == MessageType::kError) {
1105 // drop the error notifications for the internal streams
1106 auto error_stream_id = message.message.error.error_stream_id;
1107 if (has_depth_stream_ &&
1108 message.message.error.error_code == ErrorCode::kErrorBuffer &&
1109 error_stream_id != kInvalidStreamId &&
1110 (error_stream_id == rgb_internal_yuv_stream_id_ ||
1111 error_stream_id == ir1_internal_raw_stream_id_ ||
1112 error_stream_id == ir2_internal_raw_stream_id_)) {
1113 return;
1114 }
1115
1116 status_t res = result_dispatcher_->AddError(message.message.error);
1117 if (res != OK) {
1118 ALOGE("%s: AddError for frame %u failed: %s (%d).", __FUNCTION__,
1119 message.message.error.frame_number, strerror(-res), res);
1120 return;
1121 }
1122 } else {
1123 ALOGW("%s: Unsupported message type: %u", __FUNCTION__, message.type);
1124 device_session_notify_(message);
1125 }
1126 }
1127 } // namespace google_camera_hal
1128 } // namespace android
1129