blob: b0ed2df596ab91fc77de22f3cd8b325c8e4b4440 [file] [log] [blame]
Mathias Agopian076b1cc2009-04-10 14:24:30 -07001/*
2 * Copyright (C) 2007 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
Mathias Agopian3330b202009-10-05 17:07:12 -070017#define LOG_TAG "GraphicBufferMapper"
Mathias Agopiancf563192012-02-29 20:43:29 -080018#define ATRACE_TAG ATRACE_TAG_GRAPHICS
Dan Stoza8deb4da2016-06-01 18:21:44 -070019//#define LOG_NDEBUG 0
Mathias Agopian076b1cc2009-04-10 14:24:30 -070020
21#include <stdint.h>
Mathias Agopian076b1cc2009-04-10 14:24:30 -070022#include <errno.h>
Mathias Agopian076b1cc2009-04-10 14:24:30 -070023
Dan Stozad3182402014-11-17 12:03:59 -080024// We would eliminate the non-conforming zero-length array, but we can't since
25// this is effectively included from the Linux kernel
26#pragma clang diagnostic push
27#pragma clang diagnostic ignored "-Wzero-length-array"
Francis Hart8f396012014-04-01 15:30:53 +030028#include <sync/sync.h>
Dan Stozad3182402014-11-17 12:03:59 -080029#pragma clang diagnostic pop
Francis Hart8f396012014-04-01 15:30:53 +030030
Mathias Agopian076b1cc2009-04-10 14:24:30 -070031#include <utils/Errors.h>
Mathias Agopian076b1cc2009-04-10 14:24:30 -070032#include <utils/Log.h>
Mathias Agopiancf563192012-02-29 20:43:29 -080033#include <utils/Trace.h>
Mathias Agopian076b1cc2009-04-10 14:24:30 -070034
Dan Stoza8deb4da2016-06-01 18:21:44 -070035#include <ui/Gralloc1On0Adapter.h>
Chia-I Wu9ba189d2016-09-22 17:13:08 +080036#include <ui/GrallocMapper.h>
Mathias Agopian3330b202009-10-05 17:07:12 -070037#include <ui/GraphicBufferMapper.h>
Mathias Agopian076b1cc2009-04-10 14:24:30 -070038#include <ui/Rect.h>
39
Dan Stoza8deb4da2016-06-01 18:21:44 -070040#include <system/graphics.h>
Mathias Agopian8b765b72009-04-10 20:34:46 -070041
Mathias Agopian076b1cc2009-04-10 14:24:30 -070042namespace android {
43// ---------------------------------------------------------------------------
44
Mathias Agopian3330b202009-10-05 17:07:12 -070045ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
Mathias Agopian4243e662009-04-15 18:34:24 -070046
Mathias Agopian3330b202009-10-05 17:07:12 -070047GraphicBufferMapper::GraphicBufferMapper()
Chia-I Wu9ba189d2016-09-22 17:13:08 +080048 : mMapper(std::make_unique<const Gralloc2::Mapper>())
49{
50 if (!mMapper->valid()) {
51 mLoader = std::make_unique<Gralloc1::Loader>();
52 mDevice = mLoader->getDevice();
53 }
54}
Dan Stoza8deb4da2016-06-01 18:21:44 -070055
56
Mathias Agopian076b1cc2009-04-10 14:24:30 -070057
Mathias Agopian3330b202009-10-05 17:07:12 -070058status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle)
Mathias Agopian076b1cc2009-04-10 14:24:30 -070059{
Mathias Agopiancf563192012-02-29 20:43:29 -080060 ATRACE_CALL();
Mathias Agopian0a757812010-12-08 16:40:01 -080061
Chia-I Wu9ba189d2016-09-22 17:13:08 +080062 gralloc1_error_t error;
63 if (mMapper->valid()) {
64 error = static_cast<gralloc1_error_t>(mMapper->retain(handle));
65 } else {
66 error = mDevice->retain(handle);
67 }
68
Dan Stoza8deb4da2016-06-01 18:21:44 -070069 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
70 handle, error);
Mathias Agopian0a757812010-12-08 16:40:01 -080071
Dan Stoza8deb4da2016-06-01 18:21:44 -070072 return error;
73}
74
75status_t GraphicBufferMapper::registerBuffer(const GraphicBuffer* buffer)
76{
77 ATRACE_CALL();
78
Chia-I Wu9ba189d2016-09-22 17:13:08 +080079 gralloc1_error_t error;
80 if (mMapper->valid()) {
81 error = static_cast<gralloc1_error_t>(
82 mMapper->retain(buffer->getNativeBuffer()->handle));
83 } else {
84 error = mDevice->retain(buffer);
85 }
86
Dan Stoza8deb4da2016-06-01 18:21:44 -070087 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
88 buffer->getNativeBuffer()->handle, error);
89
90 return error;
Mathias Agopian076b1cc2009-04-10 14:24:30 -070091}
92
Mathias Agopian3330b202009-10-05 17:07:12 -070093status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle)
Mathias Agopian076b1cc2009-04-10 14:24:30 -070094{
Mathias Agopiancf563192012-02-29 20:43:29 -080095 ATRACE_CALL();
Mathias Agopian0a757812010-12-08 16:40:01 -080096
Chia-I Wu9ba189d2016-09-22 17:13:08 +080097 gralloc1_error_t error;
98 if (mMapper->valid()) {
99 mMapper->release(handle);
100 error = GRALLOC1_ERROR_NONE;
101 } else {
102 error = mDevice->release(handle);
103 }
104
Dan Stoza8deb4da2016-06-01 18:21:44 -0700105 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
106 handle, error);
Mathias Agopian0a757812010-12-08 16:40:01 -0800107
Dan Stoza8deb4da2016-06-01 18:21:44 -0700108 return error;
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700109}
110
Dan Stoza8deb4da2016-06-01 18:21:44 -0700111static inline gralloc1_rect_t asGralloc1Rect(const Rect& rect) {
112 gralloc1_rect_t outRect{};
113 outRect.left = rect.left;
114 outRect.top = rect.top;
115 outRect.width = rect.width();
116 outRect.height = rect.height();
117 return outRect;
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700118}
119
Craig Donner58a1ef22017-02-02 12:40:05 -0800120
121status_t GraphicBufferMapper::getDimensions(buffer_handle_t handle,
122 uint32_t* outWidth, uint32_t* outHeight) const
123{
124 ATRACE_CALL();
125
126 gralloc1_error_t error;
127 if (mMapper->valid()) {
128 mMapper->getDimensions(handle, outWidth, outHeight);
129 error = GRALLOC1_ERROR_NONE;
130 } else {
131 error = mDevice->getDimensions(handle, outWidth, outHeight);
132 }
133
134 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getDimensions(%p, ...): failed %d",
135 handle, error);
136
137 return error;
138}
139
140status_t GraphicBufferMapper::getFormat(buffer_handle_t handle,
141 int32_t* outFormat) const
142{
143 ATRACE_CALL();
144
145 gralloc1_error_t error;
146 if (mMapper->valid()) {
147 mMapper->getFormat(handle, outFormat);
148 error = GRALLOC1_ERROR_NONE;
149 } else {
150 error = mDevice->getFormat(handle, outFormat);
151 }
152
153 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getFormat(%p, ...): failed %d",
154 handle, error);
155
156 return error;
157}
158
159status_t GraphicBufferMapper::getLayerCount(buffer_handle_t handle,
160 uint32_t* outLayerCount) const
161{
162 ATRACE_CALL();
163
164 gralloc1_error_t error;
165 if (mMapper->valid()) {
166 mMapper->getLayerCount(handle, outLayerCount);
167 error = GRALLOC1_ERROR_NONE;
168 } else {
169 error = mDevice->getLayerCount(handle, outLayerCount);
170 }
171
172 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getLayerCount(%p, ...): failed %d",
173 handle, error);
174
175 return error;
176}
177
178status_t GraphicBufferMapper::getProducerUsage(buffer_handle_t handle,
179 uint64_t* outProducerUsage) const
180{
181 ATRACE_CALL();
182
183 gralloc1_error_t error;
184 if (mMapper->valid()) {
185 mMapper->getProducerUsage(handle, outProducerUsage);
186 error = GRALLOC1_ERROR_NONE;
187 } else {
188 error = mDevice->getProducerUsage(handle, outProducerUsage);
189 }
190
191 ALOGW_IF(error != GRALLOC1_ERROR_NONE,
192 "getProducerUsage(%p, ...): failed %d", handle, error);
193
194 return error;
195}
196
197status_t GraphicBufferMapper::getConsumerUsage(buffer_handle_t handle,
198 uint64_t* outConsumerUsage) const
199{
200 ATRACE_CALL();
201
202 gralloc1_error_t error;
203 if (mMapper->valid()) {
204 mMapper->getConsumerUsage(handle, outConsumerUsage);
205 error = GRALLOC1_ERROR_NONE;
206 } else {
207 error = mDevice->getConsumerUsage(handle, outConsumerUsage);
208 }
209
210 ALOGW_IF(error != GRALLOC1_ERROR_NONE,
211 "getConsumerUsage(%p, ...): failed %d", handle, error);
212
213 return error;
214}
215
216status_t GraphicBufferMapper::getBackingStore(buffer_handle_t handle,
217 uint64_t* outBackingStore) const
218{
219 ATRACE_CALL();
220
221 gralloc1_error_t error;
222 if (mMapper->valid()) {
223 mMapper->getBackingStore(handle, outBackingStore);
224 error = GRALLOC1_ERROR_NONE;
225 } else {
226 error = mDevice->getBackingStore(handle, outBackingStore);
227 }
228
229 ALOGW_IF(error != GRALLOC1_ERROR_NONE,
230 "getBackingStore(%p, ...): failed %d", handle, error);
231
232 return error;
233}
234
235status_t GraphicBufferMapper::getStride(buffer_handle_t handle,
236 uint32_t* outStride) const
237{
238 ATRACE_CALL();
239
240 gralloc1_error_t error;
241 if (mMapper->valid()) {
242 mMapper->getStride(handle, outStride);
243 error = GRALLOC1_ERROR_NONE;
244 } else {
245 error = mDevice->getStride(handle, outStride);
246 }
247
248 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getStride(%p, ...): failed %d",
249 handle, error);
250
251 return error;
252}
253
Dan Stoza8deb4da2016-06-01 18:21:44 -0700254status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
255 const Rect& bounds, void** vaddr)
Eino-Ville Talvalac43946b2013-05-04 18:07:43 -0700256{
Dan Stoza8deb4da2016-06-01 18:21:44 -0700257 return lockAsync(handle, usage, bounds, vaddr, -1);
258}
Eino-Ville Talvalac43946b2013-05-04 18:07:43 -0700259
Dan Stoza8deb4da2016-06-01 18:21:44 -0700260status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage,
261 const Rect& bounds, android_ycbcr *ycbcr)
262{
263 return lockAsyncYCbCr(handle, usage, bounds, ycbcr, -1);
Eino-Ville Talvalac43946b2013-05-04 18:07:43 -0700264}
265
Mathias Agopian3330b202009-10-05 17:07:12 -0700266status_t GraphicBufferMapper::unlock(buffer_handle_t handle)
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700267{
Dan Stoza8deb4da2016-06-01 18:21:44 -0700268 int32_t fenceFd = -1;
269 status_t error = unlockAsync(handle, &fenceFd);
270 if (error == NO_ERROR) {
271 sync_wait(fenceFd, -1);
272 close(fenceFd);
273 }
274 return error;
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700275}
276
Francis Hart8f396012014-04-01 15:30:53 +0300277status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
Dan Stozad3182402014-11-17 12:03:59 -0800278 uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
Francis Hart8f396012014-04-01 15:30:53 +0300279{
Craig Donnere96a3252017-02-02 12:13:34 -0800280 return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
281}
282
283status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
284 uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
285 void** vaddr, int fenceFd)
286{
Francis Hart8f396012014-04-01 15:30:53 +0300287 ATRACE_CALL();
Francis Hart8f396012014-04-01 15:30:53 +0300288
Dan Stoza8deb4da2016-06-01 18:21:44 -0700289 gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800290 gralloc1_error_t error;
291 if (mMapper->valid()) {
Chia-I Wu31669472016-12-07 14:55:24 +0800292 const Gralloc2::IMapper::Rect& accessRect =
293 *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800294 error = static_cast<gralloc1_error_t>(mMapper->lock(
Craig Donnere96a3252017-02-02 12:13:34 -0800295 handle, producerUsage, consumerUsage, accessRect,
296 fenceFd, vaddr));
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800297 } else {
298 sp<Fence> fence = new Fence(fenceFd);
299 error = mDevice->lock(handle,
Craig Donnere96a3252017-02-02 12:13:34 -0800300 static_cast<gralloc1_producer_usage_t>(producerUsage),
301 static_cast<gralloc1_consumer_usage_t>(consumerUsage),
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800302 &accessRegion, vaddr, fence);
303 }
304
Dan Stoza8deb4da2016-06-01 18:21:44 -0700305 ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
306 error);
307
308 return error;
309}
310
311static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) {
312 if (plane.bits_per_component != 8) {
313 ALOGV("Invalid number of bits per component: %d",
314 plane.bits_per_component);
315 return false;
316 }
317 if (plane.bits_used != 8) {
318 ALOGV("Invalid number of bits used: %d", plane.bits_used);
319 return false;
Francis Hart8f396012014-04-01 15:30:53 +0300320 }
321
Dan Stoza8deb4da2016-06-01 18:21:44 -0700322 bool hasValidIncrement = plane.h_increment == 1 ||
323 (plane.component != FLEX_COMPONENT_Y && plane.h_increment == 2);
324 hasValidIncrement = hasValidIncrement && plane.v_increment > 0;
325 if (!hasValidIncrement) {
326 ALOGV("Invalid increment: h %d v %d", plane.h_increment,
327 plane.v_increment);
328 return false;
329 }
330
331 return true;
Francis Hart8f396012014-04-01 15:30:53 +0300332}
333
334status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
Dan Stozad3182402014-11-17 12:03:59 -0800335 uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd)
Francis Hart8f396012014-04-01 15:30:53 +0300336{
337 ATRACE_CALL();
Francis Hart8f396012014-04-01 15:30:53 +0300338
Dan Stoza8deb4da2016-06-01 18:21:44 -0700339 gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
Dan Stoza8deb4da2016-06-01 18:21:44 -0700340
Chia-I Wu31669472016-12-07 14:55:24 +0800341 std::vector<android_flex_plane_t> planes;
342 android_flex_layout_t flexLayout{};
343 gralloc1_error_t error;
344
345 if (mMapper->valid()) {
346 const Gralloc2::IMapper::Rect& accessRect =
347 *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
348 Gralloc2::FlexLayout layout{};
349 error = static_cast<gralloc1_error_t>(mMapper->lock(
350 handle, usage, usage, accessRect, fenceFd, &layout));
351
352 if (error == GRALLOC1_ERROR_NONE) {
353 planes.resize(layout.planes.size());
354 memcpy(planes.data(), layout.planes.data(),
355 sizeof(planes[0]) * planes.size());
356
357 flexLayout.format = static_cast<android_flex_format_t>(
358 layout.format);
359 flexLayout.num_planes = static_cast<uint32_t>(planes.size());
360 flexLayout.planes = planes.data();
361 }
362 } else {
363 sp<Fence> fence = new Fence(fenceFd);
364
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800365 if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
Chia-I Wu31669472016-12-07 14:55:24 +0800366 error = mDevice->lockYCbCr(handle,
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800367 static_cast<gralloc1_producer_usage_t>(usage),
368 static_cast<gralloc1_consumer_usage_t>(usage),
369 &accessRegion, ycbcr, fence);
370 ALOGW_IF(error != GRALLOC1_ERROR_NONE,
371 "lockYCbCr(%p, ...) failed: %d", handle, error);
372 return error;
373 }
Francis Hart8f396012014-04-01 15:30:53 +0300374
Chia-I Wu31669472016-12-07 14:55:24 +0800375 uint32_t numPlanes = 0;
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800376 error = mDevice->getNumFlexPlanes(handle, &numPlanes);
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800377
Chia-I Wu31669472016-12-07 14:55:24 +0800378 if (error != GRALLOC1_ERROR_NONE) {
379 ALOGV("Failed to retrieve number of flex planes: %d", error);
380 return error;
381 }
382 if (numPlanes < 3) {
383 ALOGV("Not enough planes for YCbCr (%u found)", numPlanes);
384 return GRALLOC1_ERROR_UNSUPPORTED;
385 }
Dan Stoza8deb4da2016-06-01 18:21:44 -0700386
Chia-I Wu31669472016-12-07 14:55:24 +0800387 planes.resize(numPlanes);
388 flexLayout.num_planes = numPlanes;
389 flexLayout.planes = planes.data();
Dan Stoza8deb4da2016-06-01 18:21:44 -0700390
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800391 error = mDevice->lockFlex(handle,
392 static_cast<gralloc1_producer_usage_t>(usage),
393 static_cast<gralloc1_consumer_usage_t>(usage),
394 &accessRegion, &flexLayout, fence);
395 }
396
Dan Stoza8deb4da2016-06-01 18:21:44 -0700397 if (error != GRALLOC1_ERROR_NONE) {
398 ALOGW("lockFlex(%p, ...) failed: %d", handle, error);
399 return error;
400 }
401 if (flexLayout.format != FLEX_FORMAT_YCbCr) {
402 ALOGV("Unable to convert flex-format buffer to YCbCr");
403 unlock(handle);
404 return GRALLOC1_ERROR_UNSUPPORTED;
405 }
406
407 // Find planes
408 auto yPlane = planes.cend();
409 auto cbPlane = planes.cend();
410 auto crPlane = planes.cend();
411 for (auto planeIter = planes.cbegin(); planeIter != planes.cend();
412 ++planeIter) {
413 if (planeIter->component == FLEX_COMPONENT_Y) {
414 yPlane = planeIter;
415 } else if (planeIter->component == FLEX_COMPONENT_Cb) {
416 cbPlane = planeIter;
417 } else if (planeIter->component == FLEX_COMPONENT_Cr) {
418 crPlane = planeIter;
419 }
420 }
421 if (yPlane == planes.cend()) {
422 ALOGV("Unable to find Y plane");
423 unlock(handle);
424 return GRALLOC1_ERROR_UNSUPPORTED;
425 }
426 if (cbPlane == planes.cend()) {
427 ALOGV("Unable to find Cb plane");
428 unlock(handle);
429 return GRALLOC1_ERROR_UNSUPPORTED;
430 }
431 if (crPlane == planes.cend()) {
432 ALOGV("Unable to find Cr plane");
433 unlock(handle);
434 return GRALLOC1_ERROR_UNSUPPORTED;
435 }
436
437 // Validate planes
438 if (!isValidYCbCrPlane(*yPlane)) {
439 ALOGV("Y plane is invalid");
440 unlock(handle);
441 return GRALLOC1_ERROR_UNSUPPORTED;
442 }
443 if (!isValidYCbCrPlane(*cbPlane)) {
444 ALOGV("Cb plane is invalid");
445 unlock(handle);
446 return GRALLOC1_ERROR_UNSUPPORTED;
447 }
448 if (!isValidYCbCrPlane(*crPlane)) {
449 ALOGV("Cr plane is invalid");
450 unlock(handle);
451 return GRALLOC1_ERROR_UNSUPPORTED;
452 }
453 if (cbPlane->v_increment != crPlane->v_increment) {
454 ALOGV("Cb and Cr planes have different step (%d vs. %d)",
455 cbPlane->v_increment, crPlane->v_increment);
456 unlock(handle);
457 return GRALLOC1_ERROR_UNSUPPORTED;
458 }
459 if (cbPlane->h_increment != crPlane->h_increment) {
460 ALOGV("Cb and Cr planes have different stride (%d vs. %d)",
461 cbPlane->h_increment, crPlane->h_increment);
462 unlock(handle);
463 return GRALLOC1_ERROR_UNSUPPORTED;
464 }
465
466 // Pack plane data into android_ycbcr struct
467 ycbcr->y = yPlane->top_left;
468 ycbcr->cb = cbPlane->top_left;
469 ycbcr->cr = crPlane->top_left;
470 ycbcr->ystride = static_cast<size_t>(yPlane->v_increment);
471 ycbcr->cstride = static_cast<size_t>(cbPlane->v_increment);
472 ycbcr->chroma_step = static_cast<size_t>(cbPlane->h_increment);
473
474 return error;
Francis Hart8f396012014-04-01 15:30:53 +0300475}
476
477status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
478{
479 ATRACE_CALL();
Francis Hart8f396012014-04-01 15:30:53 +0300480
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800481 gralloc1_error_t error;
482 if (mMapper->valid()) {
483 *fenceFd = mMapper->unlock(handle);
484 error = GRALLOC1_ERROR_NONE;
485 } else {
486 sp<Fence> fence = Fence::NO_FENCE;
487 error = mDevice->unlock(handle, &fence);
488 if (error != GRALLOC1_ERROR_NONE) {
489 ALOGE("unlock(%p) failed: %d", handle, error);
490 return error;
491 }
Francis Hart8f396012014-04-01 15:30:53 +0300492
Chia-I Wu9ba189d2016-09-22 17:13:08 +0800493 *fenceFd = fence->dup();
494 }
Dan Stoza8deb4da2016-06-01 18:21:44 -0700495 return error;
Francis Hart8f396012014-04-01 15:30:53 +0300496}
497
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700498// ---------------------------------------------------------------------------
Mathias Agopian076b1cc2009-04-10 14:24:30 -0700499}; // namespace android