1 /*
2 * Copyright (C) 2013 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 "CameraDeviceClient"
18 #define ATRACE_TAG ATRACE_TAG_CAMERA
19 // #define LOG_NDEBUG 0
20
21 #include <cutils/properties.h>
22 #include <utils/Log.h>
23 #include <utils/Trace.h>
24 #include <gui/Surface.h>
25 #include <camera/camera2/CaptureRequest.h>
26
27 #include "common/CameraDeviceBase.h"
28 #include "api2/CameraDeviceClient.h"
29
30
31
32 namespace android {
33 using namespace camera2;
34
CameraDeviceClientBase(const sp<CameraService> & cameraService,const sp<ICameraDeviceCallbacks> & remoteCallback,const String16 & clientPackageName,int cameraId,int cameraFacing,int clientPid,uid_t clientUid,int servicePid)35 CameraDeviceClientBase::CameraDeviceClientBase(
36 const sp<CameraService>& cameraService,
37 const sp<ICameraDeviceCallbacks>& remoteCallback,
38 const String16& clientPackageName,
39 int cameraId,
40 int cameraFacing,
41 int clientPid,
42 uid_t clientUid,
43 int servicePid) :
44 BasicClient(cameraService, remoteCallback->asBinder(), clientPackageName,
45 cameraId, cameraFacing, clientPid, clientUid, servicePid),
46 mRemoteCallback(remoteCallback) {
47 }
48
49 // Interface used by CameraService
50
CameraDeviceClient(const sp<CameraService> & cameraService,const sp<ICameraDeviceCallbacks> & remoteCallback,const String16 & clientPackageName,int cameraId,int cameraFacing,int clientPid,uid_t clientUid,int servicePid)51 CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
52 const sp<ICameraDeviceCallbacks>& remoteCallback,
53 const String16& clientPackageName,
54 int cameraId,
55 int cameraFacing,
56 int clientPid,
57 uid_t clientUid,
58 int servicePid) :
59 Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
60 cameraId, cameraFacing, clientPid, clientUid, servicePid),
61 mRequestIdCounter(0) {
62
63 ATRACE_CALL();
64 ALOGI("CameraDeviceClient %d: Opened", cameraId);
65 }
66
initialize(camera_module_t * module)67 status_t CameraDeviceClient::initialize(camera_module_t *module)
68 {
69 ATRACE_CALL();
70 status_t res;
71
72 res = Camera2ClientBase::initialize(module);
73 if (res != OK) {
74 return res;
75 }
76
77 String8 threadName;
78 mFrameProcessor = new FrameProcessorBase(mDevice);
79 threadName = String8::format("CDU-%d-FrameProc", mCameraId);
80 mFrameProcessor->run(threadName.string());
81
82 mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
83 FRAME_PROCESSOR_LISTENER_MAX_ID,
84 /*listener*/this,
85 /*quirkSendPartials*/true);
86
87 return OK;
88 }
89
~CameraDeviceClient()90 CameraDeviceClient::~CameraDeviceClient() {
91 }
92
submitRequest(sp<CaptureRequest> request,bool streaming)93 status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request,
94 bool streaming) {
95 ATRACE_CALL();
96 ALOGV("%s", __FUNCTION__);
97
98 status_t res;
99
100 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
101
102 Mutex::Autolock icl(mBinderSerializationLock);
103
104 if (!mDevice.get()) return DEAD_OBJECT;
105
106 if (request == 0) {
107 ALOGE("%s: Camera %d: Sent null request. Rejecting request.",
108 __FUNCTION__, mCameraId);
109 return BAD_VALUE;
110 }
111
112 CameraMetadata metadata(request->mMetadata);
113
114 if (metadata.isEmpty()) {
115 ALOGE("%s: Camera %d: Sent empty metadata packet. Rejecting request.",
116 __FUNCTION__, mCameraId);
117 return BAD_VALUE;
118 } else if (request->mSurfaceList.size() == 0) {
119 ALOGE("%s: Camera %d: Requests must have at least one surface target. "
120 "Rejecting request.", __FUNCTION__, mCameraId);
121 return BAD_VALUE;
122 }
123
124 if (!enforceRequestPermissions(metadata)) {
125 // Callee logs
126 return PERMISSION_DENIED;
127 }
128
129 /**
130 * Write in the output stream IDs which we calculate from
131 * the capture request's list of surface targets
132 */
133 Vector<int32_t> outputStreamIds;
134 outputStreamIds.setCapacity(request->mSurfaceList.size());
135 for (size_t i = 0; i < request->mSurfaceList.size(); ++i) {
136 sp<Surface> surface = request->mSurfaceList[i];
137
138 if (surface == 0) continue;
139
140 sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer();
141 int idx = mStreamMap.indexOfKey(gbp->asBinder());
142
143 // Trying to submit request with surface that wasn't created
144 if (idx == NAME_NOT_FOUND) {
145 ALOGE("%s: Camera %d: Tried to submit a request with a surface that"
146 " we have not called createStream on",
147 __FUNCTION__, mCameraId);
148 return BAD_VALUE;
149 }
150
151 int streamId = mStreamMap.valueAt(idx);
152 outputStreamIds.push_back(streamId);
153 ALOGV("%s: Camera %d: Appending output stream %d to request",
154 __FUNCTION__, mCameraId, streamId);
155 }
156
157 metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
158 outputStreamIds.size());
159
160 int32_t requestId = mRequestIdCounter++;
161 metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
162 ALOGV("%s: Camera %d: Submitting request with ID %d",
163 __FUNCTION__, mCameraId, requestId);
164
165 if (streaming) {
166 res = mDevice->setStreamingRequest(metadata);
167 if (res != OK) {
168 ALOGE("%s: Camera %d: Got error %d after trying to set streaming "
169 "request", __FUNCTION__, mCameraId, res);
170 } else {
171 mStreamingRequestList.push_back(requestId);
172 }
173 } else {
174 res = mDevice->capture(metadata);
175 if (res != OK) {
176 ALOGE("%s: Camera %d: Got error %d after trying to set capture",
177 __FUNCTION__, mCameraId, res);
178 }
179 }
180
181 ALOGV("%s: Camera %d: End of function", __FUNCTION__, mCameraId);
182 if (res == OK) {
183 return requestId;
184 }
185
186 return res;
187 }
188
cancelRequest(int requestId)189 status_t CameraDeviceClient::cancelRequest(int requestId) {
190 ATRACE_CALL();
191 ALOGV("%s, requestId = %d", __FUNCTION__, requestId);
192
193 status_t res;
194
195 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
196
197 Mutex::Autolock icl(mBinderSerializationLock);
198
199 if (!mDevice.get()) return DEAD_OBJECT;
200
201 Vector<int>::iterator it, end;
202 for (it = mStreamingRequestList.begin(), end = mStreamingRequestList.end();
203 it != end; ++it) {
204 if (*it == requestId) {
205 break;
206 }
207 }
208
209 if (it == end) {
210 ALOGE("%s: Camera%d: Did not find request id %d in list of streaming "
211 "requests", __FUNCTION__, mCameraId, requestId);
212 return BAD_VALUE;
213 }
214
215 res = mDevice->clearStreamingRequest();
216
217 if (res == OK) {
218 ALOGV("%s: Camera %d: Successfully cleared streaming request",
219 __FUNCTION__, mCameraId);
220 mStreamingRequestList.erase(it);
221 }
222
223 return res;
224 }
225
deleteStream(int streamId)226 status_t CameraDeviceClient::deleteStream(int streamId) {
227 ATRACE_CALL();
228 ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
229
230 status_t res;
231 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
232
233 Mutex::Autolock icl(mBinderSerializationLock);
234
235 if (!mDevice.get()) return DEAD_OBJECT;
236
237 // Guard against trying to delete non-created streams
238 ssize_t index = NAME_NOT_FOUND;
239 for (size_t i = 0; i < mStreamMap.size(); ++i) {
240 if (streamId == mStreamMap.valueAt(i)) {
241 index = i;
242 break;
243 }
244 }
245
246 if (index == NAME_NOT_FOUND) {
247 ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
248 "created yet", __FUNCTION__, mCameraId, streamId);
249 return BAD_VALUE;
250 }
251
252 // Also returns BAD_VALUE if stream ID was not valid
253 res = mDevice->deleteStream(streamId);
254
255 if (res == BAD_VALUE) {
256 ALOGE("%s: Camera %d: Unexpected BAD_VALUE when deleting stream, but we"
257 " already checked and the stream ID (%d) should be valid.",
258 __FUNCTION__, mCameraId, streamId);
259 } else if (res == OK) {
260 mStreamMap.removeItemsAt(index);
261
262 ALOGV("%s: Camera %d: Successfully deleted stream ID (%d)",
263 __FUNCTION__, mCameraId, streamId);
264 }
265
266 return res;
267 }
268
createStream(int width,int height,int format,const sp<IGraphicBufferProducer> & bufferProducer)269 status_t CameraDeviceClient::createStream(int width, int height, int format,
270 const sp<IGraphicBufferProducer>& bufferProducer)
271 {
272 ATRACE_CALL();
273 ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
274
275 status_t res;
276 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
277
278 Mutex::Autolock icl(mBinderSerializationLock);
279
280 if (!mDevice.get()) return DEAD_OBJECT;
281
282 // Don't create multiple streams for the same target surface
283 {
284 ssize_t index = mStreamMap.indexOfKey(bufferProducer->asBinder());
285 if (index != NAME_NOT_FOUND) {
286 ALOGW("%s: Camera %d: Buffer producer already has a stream for it "
287 "(ID %d)",
288 __FUNCTION__, mCameraId, index);
289 return ALREADY_EXISTS;
290 }
291 }
292
293 // HACK b/10949105
294 // Query consumer usage bits to set async operation mode for
295 // GLConsumer using controlledByApp parameter.
296 bool useAsync = false;
297 int32_t consumerUsage;
298 if ((res = bufferProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS,
299 &consumerUsage)) != OK) {
300 ALOGE("%s: Camera %d: Failed to query consumer usage", __FUNCTION__,
301 mCameraId);
302 return res;
303 }
304 if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
305 ALOGW("%s: Camera %d: Forcing asynchronous mode for stream",
306 __FUNCTION__, mCameraId);
307 useAsync = true;
308 }
309
310 sp<IBinder> binder;
311 sp<ANativeWindow> anw;
312 if (bufferProducer != 0) {
313 binder = bufferProducer->asBinder();
314 anw = new Surface(bufferProducer, useAsync);
315 }
316
317 // TODO: remove w,h,f since we are ignoring them
318
319 if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
320 ALOGE("%s: Camera %d: Failed to query Surface width", __FUNCTION__,
321 mCameraId);
322 return res;
323 }
324 if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
325 ALOGE("%s: Camera %d: Failed to query Surface height", __FUNCTION__,
326 mCameraId);
327 return res;
328 }
329 if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &format)) != OK) {
330 ALOGE("%s: Camera %d: Failed to query Surface format", __FUNCTION__,
331 mCameraId);
332 return res;
333 }
334
335 // FIXME: remove this override since the default format should be
336 // IMPLEMENTATION_DEFINED. b/9487482
337 if (format >= HAL_PIXEL_FORMAT_RGBA_8888 &&
338 format <= HAL_PIXEL_FORMAT_BGRA_8888) {
339 ALOGW("%s: Camera %d: Overriding format 0x%x to IMPLEMENTATION_DEFINED",
340 __FUNCTION__, mCameraId, format);
341 format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
342 }
343
344 // TODO: add startConfigure/stopConfigure call to CameraDeviceBase
345 // this will make it so Camera3Device doesn't call configure_streams
346 // after each call, but only once we are done with all.
347
348 int streamId = -1;
349 if (format == HAL_PIXEL_FORMAT_BLOB) {
350 // JPEG buffers need to be sized for maximum possible compressed size
351 CameraMetadata staticInfo = mDevice->info();
352 camera_metadata_entry_t entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE);
353 if (entry.count == 0) {
354 ALOGE("%s: Camera %d: Can't find maximum JPEG size in "
355 "static metadata!", __FUNCTION__, mCameraId);
356 return INVALID_OPERATION;
357 }
358 int32_t maxJpegSize = entry.data.i32[0];
359 res = mDevice->createStream(anw, width, height, format, maxJpegSize,
360 &streamId);
361 } else {
362 // All other streams are a known size
363 res = mDevice->createStream(anw, width, height, format, /*size*/0,
364 &streamId);
365 }
366
367 if (res == OK) {
368 mStreamMap.add(bufferProducer->asBinder(), streamId);
369
370 ALOGV("%s: Camera %d: Successfully created a new stream ID %d",
371 __FUNCTION__, mCameraId, streamId);
372
373 /**
374 * Set the stream transform flags to automatically
375 * rotate the camera stream for preview use cases.
376 */
377 int32_t transform = 0;
378 res = getRotationTransformLocked(&transform);
379
380 if (res != OK) {
381 // Error logged by getRotationTransformLocked.
382 return res;
383 }
384
385 res = mDevice->setStreamTransform(streamId, transform);
386 if (res != OK) {
387 ALOGE("%s: Failed to set stream transform (stream id %d)",
388 __FUNCTION__, streamId);
389 return res;
390 }
391
392 return streamId;
393 }
394
395 return res;
396 }
397
398 // Create a request object from a template.
createDefaultRequest(int templateId,CameraMetadata * request)399 status_t CameraDeviceClient::createDefaultRequest(int templateId,
400 /*out*/
401 CameraMetadata* request)
402 {
403 ATRACE_CALL();
404 ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId);
405
406 status_t res;
407 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
408
409 Mutex::Autolock icl(mBinderSerializationLock);
410
411 if (!mDevice.get()) return DEAD_OBJECT;
412
413 CameraMetadata metadata;
414 if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK &&
415 request != NULL) {
416
417 request->swap(metadata);
418 }
419
420 return res;
421 }
422
getCameraInfo(CameraMetadata * info)423 status_t CameraDeviceClient::getCameraInfo(/*out*/CameraMetadata* info)
424 {
425 ATRACE_CALL();
426 ALOGV("%s", __FUNCTION__);
427
428 status_t res = OK;
429
430 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
431
432 Mutex::Autolock icl(mBinderSerializationLock);
433
434 if (!mDevice.get()) return DEAD_OBJECT;
435
436 if (info != NULL) {
437 *info = mDevice->info(); // static camera metadata
438 // TODO: merge with device-specific camera metadata
439 }
440
441 return res;
442 }
443
waitUntilIdle()444 status_t CameraDeviceClient::waitUntilIdle()
445 {
446 ATRACE_CALL();
447 ALOGV("%s", __FUNCTION__);
448
449 status_t res = OK;
450 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
451
452 Mutex::Autolock icl(mBinderSerializationLock);
453
454 if (!mDevice.get()) return DEAD_OBJECT;
455
456 // FIXME: Also need check repeating burst.
457 if (!mStreamingRequestList.isEmpty()) {
458 ALOGE("%s: Camera %d: Try to waitUntilIdle when there are active streaming requests",
459 __FUNCTION__, mCameraId);
460 return INVALID_OPERATION;
461 }
462 res = mDevice->waitUntilDrained();
463 ALOGV("%s Done", __FUNCTION__);
464
465 return res;
466 }
467
flush()468 status_t CameraDeviceClient::flush() {
469 ATRACE_CALL();
470 ALOGV("%s", __FUNCTION__);
471
472 status_t res = OK;
473 if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
474
475 Mutex::Autolock icl(mBinderSerializationLock);
476
477 if (!mDevice.get()) return DEAD_OBJECT;
478
479 return mDevice->flush();
480 }
481
dump(int fd,const Vector<String16> & args)482 status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
483 String8 result;
484 result.appendFormat("CameraDeviceClient[%d] (%p) PID: %d, dump:\n",
485 mCameraId,
486 getRemoteCallback()->asBinder().get(),
487 mClientPid);
488 result.append(" State: ");
489
490 // TODO: print dynamic/request section from most recent requests
491 mFrameProcessor->dump(fd, args);
492
493 return dumpDevice(fd, args);
494 }
495
496
notifyError()497 void CameraDeviceClient::notifyError() {
498 // Thread safe. Don't bother locking.
499 sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
500
501 if (remoteCb != 0) {
502 remoteCb->onDeviceError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE);
503 }
504 }
505
notifyIdle()506 void CameraDeviceClient::notifyIdle() {
507 // Thread safe. Don't bother locking.
508 sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
509
510 if (remoteCb != 0) {
511 remoteCb->onDeviceIdle();
512 }
513 }
514
notifyShutter(int requestId,nsecs_t timestamp)515 void CameraDeviceClient::notifyShutter(int requestId,
516 nsecs_t timestamp) {
517 // Thread safe. Don't bother locking.
518 sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
519 if (remoteCb != 0) {
520 remoteCb->onCaptureStarted(requestId, timestamp);
521 }
522 }
523
524 // TODO: refactor the code below this with IProCameraUser.
525 // it's 100% copy-pasted, so lets not change it right now to make it easier.
526
detachDevice()527 void CameraDeviceClient::detachDevice() {
528 if (mDevice == 0) return;
529
530 ALOGV("Camera %d: Stopping processors", mCameraId);
531
532 mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
533 FRAME_PROCESSOR_LISTENER_MAX_ID,
534 /*listener*/this);
535 mFrameProcessor->requestExit();
536 ALOGV("Camera %d: Waiting for threads", mCameraId);
537 mFrameProcessor->join();
538 ALOGV("Camera %d: Disconnecting device", mCameraId);
539
540 // WORKAROUND: HAL refuses to disconnect while there's streams in flight
541 {
542 mDevice->clearStreamingRequest();
543
544 status_t code;
545 if ((code = mDevice->waitUntilDrained()) != OK) {
546 ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__,
547 code);
548 }
549 }
550
551 Camera2ClientBase::detachDevice();
552 }
553
554 /** Device-related methods */
onFrameAvailable(int32_t requestId,const CameraMetadata & frame)555 void CameraDeviceClient::onFrameAvailable(int32_t requestId,
556 const CameraMetadata& frame) {
557 ATRACE_CALL();
558 ALOGV("%s", __FUNCTION__);
559
560 // Thread-safe. No lock necessary.
561 sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
562 if (remoteCb != NULL) {
563 ALOGV("%s: frame = %p ", __FUNCTION__, &frame);
564 remoteCb->onResultReceived(requestId, frame);
565 }
566 }
567
568 // TODO: move to Camera2ClientBase
enforceRequestPermissions(CameraMetadata & metadata)569 bool CameraDeviceClient::enforceRequestPermissions(CameraMetadata& metadata) {
570
571 const int pid = IPCThreadState::self()->getCallingPid();
572 const int selfPid = getpid();
573 camera_metadata_entry_t entry;
574
575 /**
576 * Mixin default important security values
577 * - android.led.transmit = defaulted ON
578 */
579 CameraMetadata staticInfo = mDevice->info();
580 entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS);
581 for(size_t i = 0; i < entry.count; ++i) {
582 uint8_t led = entry.data.u8[i];
583
584 switch(led) {
585 case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: {
586 uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON;
587 if (!metadata.exists(ANDROID_LED_TRANSMIT)) {
588 metadata.update(ANDROID_LED_TRANSMIT,
589 &transmitDefault, 1);
590 }
591 break;
592 }
593 }
594 }
595
596 // We can do anything!
597 if (pid == selfPid) {
598 return true;
599 }
600
601 /**
602 * Permission check special fields in the request
603 * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT
604 */
605 entry = metadata.find(ANDROID_LED_TRANSMIT);
606 if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) {
607 String16 permissionString =
608 String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED");
609 if (!checkCallingPermission(permissionString)) {
610 const int uid = IPCThreadState::self()->getCallingUid();
611 ALOGE("Permission Denial: "
612 "can't disable transmit LED pid=%d, uid=%d", pid, uid);
613 return false;
614 }
615 }
616
617 return true;
618 }
619
getRotationTransformLocked(int32_t * transform)620 status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) {
621 ALOGV("%s: begin", __FUNCTION__);
622
623 if (transform == NULL) {
624 ALOGW("%s: null transform", __FUNCTION__);
625 return BAD_VALUE;
626 }
627
628 *transform = 0;
629
630 const CameraMetadata& staticInfo = mDevice->info();
631 camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_SENSOR_ORIENTATION);
632 if (entry.count == 0) {
633 ALOGE("%s: Camera %d: Can't find android.sensor.orientation in "
634 "static metadata!", __FUNCTION__, mCameraId);
635 return INVALID_OPERATION;
636 }
637
638 int32_t& flags = *transform;
639
640 int orientation = entry.data.i32[0];
641 switch (orientation) {
642 case 0:
643 flags = 0;
644 break;
645 case 90:
646 flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
647 break;
648 case 180:
649 flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
650 break;
651 case 270:
652 flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
653 break;
654 default:
655 ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
656 __FUNCTION__, orientation);
657 return INVALID_OPERATION;
658 }
659
660 /**
661 * This magic flag makes surfaceflinger un-rotate the buffers
662 * to counter the extra global device UI rotation whenever the user
663 * physically rotates the device.
664 *
665 * By doing this, the camera buffer always ends up aligned
666 * with the physical camera for a "see through" effect.
667 *
668 * In essence, the buffer only gets rotated during preview use-cases.
669 * The user is still responsible to re-create streams of the proper
670 * aspect ratio, or the preview will end up looking non-uniformly
671 * stretched.
672 */
673 flags |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
674
675 ALOGV("%s: final transform = 0x%x", __FUNCTION__, flags);
676
677 return OK;
678 }
679
680 } // namespace android
681