blob: af1f83356d35cfb20d1b97deba0e3d4a1a10ecb5 [file] [log] [blame]
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -08001/*
2 * Copyright 2018 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
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -080017#if defined(__clang__)
18#pragma clang diagnostic push
19#pragma clang diagnostic ignored "-Weverything"
20#endif
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080021
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -080022// The following headers are included without checking every warning.
23// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
24// in these headers and their dependencies.
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -070025#include <dvr/dvr_api.h>
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -080026#include <gui/BufferHubProducer.h>
27
28#if defined(__clang__)
29#pragma clang diagnostic pop
30#endif
31
Alex Vakulenko4fe60582017-02-02 11:35:59 -080032#include <inttypes.h>
33#include <log/log.h>
Mathias Agopian6a3c05b2017-04-27 20:06:55 -070034#include <system/window.h>
Alex Vakulenko4fe60582017-02-02 11:35:59 -080035
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080036namespace android {
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080037
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -070038/* static */
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080039sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<dvr::ProducerQueue>& queue) {
40 if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
41 ALOGE("BufferHubProducer::Create producer's metadata size is different "
42 "than the size of DvrNativeBufferMetadata");
43 return nullptr;
44 }
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -070045
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080046 sp<BufferHubProducer> producer = new BufferHubProducer;
47 producer->queue_ = queue;
48 return producer;
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -070049}
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080050
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -080051/* static */
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080052sp<BufferHubProducer> BufferHubProducer::Create(dvr::ProducerQueueParcelable parcelable) {
53 if (!parcelable.IsValid()) {
54 ALOGE("BufferHubProducer::Create: Invalid producer parcelable.");
55 return nullptr;
56 }
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -080057
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080058 sp<BufferHubProducer> producer = new BufferHubProducer;
59 producer->queue_ = dvr::ProducerQueue::Import(parcelable.TakeChannelHandle());
60 return producer;
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -080061}
62
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -080063status_t BufferHubProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080064 ALOGV("requestBuffer: slot=%d", slot);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080065
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080066 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080067
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080068 if (connected_api_ == kNoConnectedApi) {
69 ALOGE("requestBuffer: BufferHubProducer has no connected producer");
70 return NO_INIT;
71 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -070072
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080073 if (slot < 0 || slot >= max_buffer_count_) {
74 ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
75 return BAD_VALUE;
76 } else if (!buffers_[slot].mBufferState.isDequeued()) {
77 ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot,
78 buffers_[slot].mBufferState.string());
79 return BAD_VALUE;
80 } else if (buffers_[slot].mGraphicBuffer != nullptr) {
81 ALOGE("requestBuffer: slot %d is not empty.", slot);
82 return BAD_VALUE;
83 } else if (buffers_[slot].mBufferProducer == nullptr) {
84 ALOGE("requestBuffer: slot %d is not dequeued.", slot);
85 return BAD_VALUE;
86 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080087
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080088 const auto& buffer_producer = buffers_[slot].mBufferProducer;
89 sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -070090
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080091 buffers_[slot].mGraphicBuffer = graphic_buffer;
92 buffers_[slot].mRequestBufferCalled = true;
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -070093
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080094 *buf = graphic_buffer;
95 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -080096}
97
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -080098status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) {
99 ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800100
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800101 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800102
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800103 if (max_dequeued_buffers <= 0 ||
104 max_dequeued_buffers >
105 int(dvr::BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
106 ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers,
107 dvr::BufferHubQueue::kMaxQueueCapacity);
108 return BAD_VALUE;
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700109 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700110
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800111 // The new dequeued_buffers count should not be violated by the number
112 // of currently dequeued buffers.
113 int dequeued_count = 0;
114 for (const auto& buf : buffers_) {
115 if (buf.mBufferState.isDequeued()) {
116 dequeued_count++;
117 }
118 }
119 if (dequeued_count > max_dequeued_buffers) {
120 ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers"
121 "count (%d) exceeds the current dequeued buffer count (%d)",
122 max_dequeued_buffers, dequeued_count);
123 return BAD_VALUE;
124 }
125
126 max_dequeued_buffer_count_ = max_dequeued_buffers;
127 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800128}
129
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800130status_t BufferHubProducer::setAsyncMode(bool async) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800131 if (async) {
132 // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
133 // automatically and behaves differently from IGraphicBufferConsumer. Thus,
134 // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
135 // to prevent dequeueBuffer from being blocking) technically does not apply
136 // here.
137 //
138 // In Daydream, non-blocking producer side dequeue is guaranteed by careful
139 // buffer consumer implementations. In another word, BufferHubQueue based
140 // dequeueBuffer should never block whether setAsyncMode(true) is set or
141 // not.
142 //
143 // See: IGraphicBufferProducer::setAsyncMode and
144 // BufferQueueProducer::setAsyncMode for more about original implementation.
145 ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be "
146 "asynchronous. This call makes no effact.");
147 return NO_ERROR;
148 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700149 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800150}
151
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800152status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
153 uint32_t height, PixelFormat format, uint64_t usage,
154 uint64_t* /*outBufferAge*/,
155 FrameEventHistoryDelta* /* out_timestamps */) {
156 ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800157
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800158 status_t ret;
159 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800160
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800161 if (connected_api_ == kNoConnectedApi) {
162 ALOGE("dequeueBuffer: BufferQueue has no connected producer");
163 return NO_INIT;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800164 }
165
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800166 const uint32_t kLayerCount = 1;
167 if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
168 // Lazy allocation. When the capacity of |queue_| has not reached
169 // |max_dequeued_buffer_count_|, allocate new buffer.
170 // TODO(jwcai) To save memory, the really reasonable thing to do is to go
171 // over existing slots and find first existing one to dequeue.
172 ret = AllocateBuffer(width, height, kLayerCount, format, usage);
173 if (ret < 0) return ret;
174 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800175
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800176 size_t slot = 0;
177 std::shared_ptr<dvr::BufferProducer> buffer_producer;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800178
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800179 for (size_t retry = 0; retry < dvr::BufferHubQueue::kMaxQueueCapacity; retry++) {
180 LocalHandle fence;
181 auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
182 if (!buffer_status) return NO_MEMORY;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800183
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800184 buffer_producer = buffer_status.take();
185 if (!buffer_producer) return NO_MEMORY;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800186
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800187 if (width == buffer_producer->width() && height == buffer_producer->height() &&
188 uint32_t(format) == buffer_producer->format()) {
189 // The producer queue returns a buffer producer matches the request.
190 break;
191 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800192
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800193 // Needs reallocation.
194 // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
195 ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
196 "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
197 "re-allocattion.",
198 width, height, format, slot, buffer_producer->width(), buffer_producer->height(),
199 buffer_producer->format());
200 // Mark the slot as reallocating, so that later we can set
201 // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
202 buffers_[slot].mIsReallocating = true;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800203
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800204 // Remove the old buffer once the allocation before allocating its
205 // replacement.
206 RemoveBuffer(slot);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800207
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800208 // Allocate a new producer buffer with new buffer configs. Note that if
209 // there are already multiple buffers in the queue, the next one returned
210 // from |queue_->Dequeue| may not be the new buffer we just reallocated.
211 // Retry up to BufferHubQueue::kMaxQueueCapacity times.
212 ret = AllocateBuffer(width, height, kLayerCount, format, usage);
213 if (ret < 0) return ret;
214 }
215
216 // With the BufferHub backed solution. Buffer slot returned from
217 // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
218 // It's either in free state (if the buffer has never been used before) or
219 // in queued state (if the buffer has been dequeued and queued back to
220 // BufferHubQueue).
221 LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
222 !buffers_[slot].mBufferState.isQueued()),
223 "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
224 buffers_[slot].mBufferState.string());
225
226 buffers_[slot].mBufferState.freeQueued();
227 buffers_[slot].mBufferState.dequeue();
228 ALOGV("dequeueBuffer: slot=%zu", slot);
229
230 // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
231 // just need to exopose that through |BufferHubQueue| once we need fence.
232 *out_fence = Fence::NO_FENCE;
233 *out_slot = int(slot);
234 ret = NO_ERROR;
235
236 if (buffers_[slot].mIsReallocating) {
237 ret |= BUFFER_NEEDS_REALLOCATION;
238 buffers_[slot].mIsReallocating = false;
239 }
240
241 return ret;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800242}
243
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800244status_t BufferHubProducer::detachBuffer(int /* slot */) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800245 ALOGE("BufferHubProducer::detachBuffer not implemented.");
246 return INVALID_OPERATION;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800247}
248
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800249status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */,
250 sp<Fence>* /* out_fence */) {
251 ALOGE("BufferHubProducer::detachNextBuffer not implemented.");
252 return INVALID_OPERATION;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800253}
254
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800255status_t BufferHubProducer::attachBuffer(int* /* out_slot */,
256 const sp<GraphicBuffer>& /* buffer */) {
257 // With this BufferHub backed implementation, we assume (for now) all buffers
258 // are allocated and owned by the BufferHub. Thus the attempt of transfering
259 // ownership of a buffer to the buffer queue is intentionally unsupported.
260 LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported.");
261 return INVALID_OPERATION;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800262}
263
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800264status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input,
265 QueueBufferOutput* output) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800266 ALOGV("queueBuffer: slot %d", slot);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800267
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800268 if (output == nullptr) {
269 return BAD_VALUE;
270 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700271
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800272 int64_t timestamp;
273 bool is_auto_timestamp;
274 android_dataspace dataspace;
275 Rect crop(Rect::EMPTY_RECT);
276 int scaling_mode;
277 uint32_t transform;
278 sp<Fence> fence;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800279
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800280 input.deflate(&timestamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform,
281 &fence);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800282
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800283 // Check input scaling mode is valid.
284 switch (scaling_mode) {
285 case NATIVE_WINDOW_SCALING_MODE_FREEZE:
286 case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
287 case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
288 case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
289 break;
290 default:
291 ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
292 return BAD_VALUE;
293 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700294
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800295 // Check input fence is valid.
296 if (fence == nullptr) {
297 ALOGE("queueBuffer: fence is NULL");
298 return BAD_VALUE;
299 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800300
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800301 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800302
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800303 if (connected_api_ == kNoConnectedApi) {
304 ALOGE("queueBuffer: BufferQueue has no connected producer");
305 return NO_INIT;
306 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700307
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800308 if (slot < 0 || slot >= max_buffer_count_) {
309 ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
310 return BAD_VALUE;
311 } else if (!buffers_[slot].mBufferState.isDequeued()) {
312 ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot,
313 buffers_[slot].mBufferState.string());
314 return BAD_VALUE;
315 } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) {
316 ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
317 "mGraphicBuffer=%p)",
318 slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get());
319 return BAD_VALUE;
320 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800321
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800322 // Post the buffer producer with timestamp in the metadata.
323 const auto& buffer_producer = buffers_[slot].mBufferProducer;
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700324
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800325 // Check input crop is not out of boundary of current buffer.
326 Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
327 Rect cropped_rect(Rect::EMPTY_RECT);
328 crop.intersect(buffer_rect, &cropped_rect);
329 if (cropped_rect != crop) {
330 ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
331 return BAD_VALUE;
332 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700333
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800334 LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800335
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800336 DvrNativeBufferMetadata meta_data;
337 meta_data.timestamp = timestamp;
338 meta_data.is_auto_timestamp = int32_t(is_auto_timestamp);
339 meta_data.dataspace = int32_t(dataspace);
340 meta_data.crop_left = crop.left;
341 meta_data.crop_top = crop.top;
342 meta_data.crop_right = crop.right;
343 meta_data.crop_bottom = crop.bottom;
344 meta_data.scaling_mode = int32_t(scaling_mode);
345 meta_data.transform = int32_t(transform);
Jiwen 'Steve' Caicb4751c2017-04-14 17:56:55 -0700346
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800347 buffer_producer->PostAsync(&meta_data, fence_fd);
348 buffers_[slot].mBufferState.queue();
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800349
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800350 output->width = buffer_producer->width();
351 output->height = buffer_producer->height();
352 output->transformHint = 0; // default value, we don't use it yet.
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700353
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800354 // |numPendingBuffers| counts of the number of buffers that has been enqueued
355 // by the producer but not yet acquired by the consumer. Due to the nature
356 // of BufferHubQueue design, this is hard to trace from the producer's client
357 // side, but it's safe to assume it's zero.
358 output->numPendingBuffers = 0;
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700359
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800360 // Note that we are not setting nextFrameNumber here as it seems to be only
361 // used by surface flinger. See more at b/22802885, ag/791760.
362 output->nextFrameNumber = 0;
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700363
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800364 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800365}
366
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800367status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800368 ALOGV(__FUNCTION__);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800369
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800370 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800371
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800372 if (connected_api_ == kNoConnectedApi) {
373 ALOGE("cancelBuffer: BufferQueue has no connected producer");
374 return NO_INIT;
375 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700376
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800377 if (slot < 0 || slot >= max_buffer_count_) {
378 ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
379 return BAD_VALUE;
380 } else if (!buffers_[slot].mBufferState.isDequeued()) {
381 ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot,
382 buffers_[slot].mBufferState.string());
383 return BAD_VALUE;
384 } else if (fence == nullptr) {
385 ALOGE("cancelBuffer: fence is NULL");
386 return BAD_VALUE;
387 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800388
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800389 auto buffer_producer = buffers_[slot].mBufferProducer;
390 queue_->Enqueue(buffer_producer, size_t(slot), 0ULL);
391 buffers_[slot].mBufferState.cancel();
392 buffers_[slot].mFence = fence;
393 ALOGV("cancelBuffer: slot %d", slot);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800394
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800395 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800396}
397
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800398status_t BufferHubProducer::query(int what, int* out_value) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800399 ALOGV(__FUNCTION__);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800400
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800401 std::unique_lock<std::mutex> lock(mutex_);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800402
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800403 if (out_value == nullptr) {
404 ALOGE("query: out_value was NULL");
405 return BAD_VALUE;
406 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800407
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800408 int value = 0;
409 switch (what) {
410 case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
411 // TODO(b/36187402) This should be the maximum number of buffers that this
412 // producer queue's consumer can acquire. Set to be at least one. Need to
413 // find a way to set from the consumer side.
414 value = kDefaultUndequeuedBuffers;
415 break;
416 case NATIVE_WINDOW_BUFFER_AGE:
417 value = 0;
418 break;
419 case NATIVE_WINDOW_WIDTH:
420 value = int32_t(queue_->default_width());
421 break;
422 case NATIVE_WINDOW_HEIGHT:
423 value = int32_t(queue_->default_height());
424 break;
425 case NATIVE_WINDOW_FORMAT:
426 value = int32_t(queue_->default_format());
427 break;
428 case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
429 // BufferHubQueue is always operating in async mode, thus semantically
430 // consumer can never be running behind. See BufferQueueCore.cpp core
431 // for more information about the original meaning of this flag.
432 value = 0;
433 break;
434 case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
435 // TODO(jwcai) This is currently not implement as we don't need
436 // IGraphicBufferConsumer parity.
437 value = 0;
438 break;
439 case NATIVE_WINDOW_DEFAULT_DATASPACE:
440 // TODO(jwcai) Return the default value android::BufferQueue is using as
441 // there is no way dvr::ConsumerQueue can set it.
442 value = 0; // HAL_DATASPACE_UNKNOWN
443 break;
444 case NATIVE_WINDOW_STICKY_TRANSFORM:
445 // TODO(jwcai) Return the default value android::BufferQueue is using as
446 // there is no way dvr::ConsumerQueue can set it.
447 value = 0;
448 break;
449 case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
450 // In Daydream's implementation, the consumer end (i.e. VR Compostior)
451 // knows how to handle protected buffers.
452 value = 1;
453 break;
454 default:
455 return BAD_VALUE;
456 }
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800457
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800458 ALOGV("query: key=%d, v=%d", what, value);
459 *out_value = value;
460 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800461}
462
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800463status_t BufferHubProducer::connect(const sp<IProducerListener>& /* listener */, int api,
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800464 bool /* producer_controlled_by_app */,
465 QueueBufferOutput* output) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800466 // Consumer interaction are actually handled by buffer hub, and we need
467 // to maintain consumer operations here. We only need to perform basic input
468 // parameter checks here.
469 ALOGV(__FUNCTION__);
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700470
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800471 if (output == nullptr) {
472 return BAD_VALUE;
473 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700474
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800475 std::unique_lock<std::mutex> lock(mutex_);
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700476
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800477 if (connected_api_ != kNoConnectedApi) {
478 return BAD_VALUE;
479 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700480
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800481 if (!queue_->is_connected()) {
482 ALOGE("BufferHubProducer::connect: This BufferHubProducer is not "
483 "connected to bufferhud. Has it been taken out as a parcelable?");
484 return BAD_VALUE;
485 }
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800486
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800487 switch (api) {
488 case NATIVE_WINDOW_API_EGL:
489 case NATIVE_WINDOW_API_CPU:
490 case NATIVE_WINDOW_API_MEDIA:
491 case NATIVE_WINDOW_API_CAMERA:
492 connected_api_ = api;
Jiwen 'Steve' Caicb4751c2017-04-14 17:56:55 -0700493
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800494 output->width = queue_->default_width();
495 output->height = queue_->default_height();
Jiwen 'Steve' Caicb4751c2017-04-14 17:56:55 -0700496
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800497 // default values, we don't use them yet.
498 output->transformHint = 0;
499 output->numPendingBuffers = 0;
500 output->nextFrameNumber = 0;
501 output->bufferReplaced = false;
Jiwen 'Steve' Caicb4751c2017-04-14 17:56:55 -0700502
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800503 break;
504 default:
505 ALOGE("BufferHubProducer::connect: unknow API %d", api);
506 return BAD_VALUE;
507 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700508
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800509 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800510}
511
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800512status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800513 // Consumer interaction are actually handled by buffer hub, and we need
514 // to maintain consumer operations here. We only need to perform basic input
515 // parameter checks here.
516 ALOGV(__FUNCTION__);
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700517
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800518 std::unique_lock<std::mutex> lock(mutex_);
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700519
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800520 if (kNoConnectedApi == connected_api_) {
521 return NO_INIT;
522 } else if (api != connected_api_) {
523 return BAD_VALUE;
524 }
Jiwen 'Steve' Cai1601bcf2017-03-24 14:03:06 -0700525
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800526 FreeAllBuffers();
527 connected_api_ = kNoConnectedApi;
528 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800529}
530
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800531status_t BufferHubProducer::setSidebandStream(const sp<NativeHandle>& stream) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800532 if (stream != nullptr) {
533 // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
534 // metadata.
535 ALOGE("SidebandStream is not currently supported.");
536 return INVALID_OPERATION;
537 }
538 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800539}
540
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800541void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */,
542 PixelFormat /* format */, uint64_t /* usage */) {
543 // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
544 // of buffers permitted by the current BufferQueue configuration (aka
545 // |max_buffer_count_|).
546 ALOGE("BufferHubProducer::allocateBuffers not implemented.");
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800547}
548
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800549status_t BufferHubProducer::allowAllocation(bool /* allow */) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800550 ALOGE("BufferHubProducer::allowAllocation not implemented.");
551 return INVALID_OPERATION;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800552}
553
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800554status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800555 ALOGV(__FUNCTION__);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800556
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800557 std::unique_lock<std::mutex> lock(mutex_);
558 generation_number_ = generation_number;
559 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800560}
561
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800562String8 BufferHubProducer::getConsumerName() const {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800563 // BufferHub based implementation could have one to many producer/consumer
564 // relationship, thus |getConsumerName| from the producer side does not
565 // make any sense.
566 ALOGE("BufferHubProducer::getConsumerName not supported.");
567 return String8("BufferHubQueue::DummyConsumer");
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800568}
569
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800570status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800571 if (shared_buffer_mode) {
572 ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported.");
573 // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
574 return INVALID_OPERATION;
575 }
576 // Setting to default should just work as a no-op.
577 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800578}
579
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800580status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800581 if (auto_refresh) {
582 ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported.");
583 return INVALID_OPERATION;
584 }
585 // Setting to default should just work as a no-op.
586 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800587}
588
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800589status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800590 ALOGV(__FUNCTION__);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800591
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800592 std::unique_lock<std::mutex> lock(mutex_);
593 dequeue_timeout_ms_ = int(timeout / (1000 * 1000));
594 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800595}
596
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800597status_t BufferHubProducer::getLastQueuedBuffer(sp<GraphicBuffer>* /* out_buffer */,
598 sp<Fence>* /* out_fence */,
599 float /*out_transform_matrix*/[16]) {
600 ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented.");
601 return INVALID_OPERATION;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800602}
603
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800604void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {
605 ALOGE("BufferHubProducer::getFrameTimestamps not implemented.");
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800606}
607
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800608status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800609 ALOGV(__FUNCTION__);
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800610
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800611 *out_id = unique_id_;
612 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800613}
614
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800615status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800616 ALOGV(__FUNCTION__);
Chia-I Wue2786ea2017-08-07 10:36:08 -0700617
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800618 // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
619 *out_usage = 0;
620 return NO_ERROR;
Chia-I Wue2786ea2017-08-07 10:36:08 -0700621}
622
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800623status_t BufferHubProducer::TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable) {
624 if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE;
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800625
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800626 if (connected_api_ != kNoConnectedApi) {
627 ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has "
628 "connected client. Must disconnect first.");
629 return BAD_VALUE;
630 }
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800631
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800632 if (!queue_->is_connected()) {
633 ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer "
634 "is not connected to bufferhud. Has it been taken out as a "
635 "parcelable?");
636 return BAD_VALUE;
637 }
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800638
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800639 auto status = queue_->TakeAsParcelable();
640 if (!status) {
641 ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out "
642 "ProducuerQueueParcelable from the producer queue, error: %s.",
643 status.GetErrorMessage().c_str());
644 return BAD_VALUE;
645 }
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800646
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800647 *out_parcelable = status.take();
648 return NO_ERROR;
Jiwen 'Steve' Cai5afb7402017-11-09 18:50:32 -0800649}
650
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800651status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800652 PixelFormat format, uint64_t usage) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800653 auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage);
654 if (!status) {
655 ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s",
656 status.GetErrorMessage().c_str());
657 return NO_MEMORY;
658 }
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700659
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800660 size_t slot = status.get();
661 auto buffer_producer = queue_->GetBuffer(slot);
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700662
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800663 LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu",
664 slot);
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700665
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800666 buffers_[slot].mBufferProducer = buffer_producer;
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700667
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800668 return NO_ERROR;
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700669}
670
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800671status_t BufferHubProducer::RemoveBuffer(size_t slot) {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800672 auto status = queue_->RemoveBuffer(slot);
673 if (!status) {
674 ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s",
675 status.GetErrorMessage().c_str());
676 return INVALID_OPERATION;
677 }
Jiwen 'Steve' Caid6cb17f2017-05-08 16:15:35 -0700678
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800679 // Reset in memory objects related the the buffer.
680 buffers_[slot].mBufferProducer = nullptr;
681 buffers_[slot].mGraphicBuffer = nullptr;
682 buffers_[slot].mBufferState.detachProducer();
683 return NO_ERROR;
Alex Vakulenkoe4eec202017-01-27 14:41:04 -0800684}
685
Jiwen 'Steve' Cai0f950842018-01-16 17:05:54 -0800686status_t BufferHubProducer::FreeAllBuffers() {
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800687 for (size_t slot = 0; slot < dvr::BufferHubQueue::kMaxQueueCapacity; slot++) {
688 // Reset in memory objects related the the buffer.
689 buffers_[slot].mGraphicBuffer = nullptr;
690 buffers_[slot].mBufferState.reset();
691 buffers_[slot].mRequestBufferCalled = false;
692 buffers_[slot].mBufferProducer = nullptr;
693 buffers_[slot].mFence = Fence::NO_FENCE;
694 }
Jiwen 'Steve' Cai005f45d2017-08-04 17:34:37 -0700695
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800696 auto status = queue_->FreeAllBuffers();
697 if (!status) {
698 ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on "
699 "the queue: %s",
700 status.GetErrorMessage().c_str());
701 }
Jiwen 'Steve' Cai005f45d2017-08-04 17:34:37 -0700702
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800703 if (queue_->capacity() != 0 || queue_->count() != 0) {
704 LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed.");
705 }
Jiwen 'Steve' Cai005f45d2017-08-04 17:34:37 -0700706
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800707 return NO_ERROR;
Jiwen 'Steve' Cai005f45d2017-08-04 17:34:37 -0700708}
709
Jiwen 'Steve' Cai9a6ddf72018-01-18 14:37:24 -0800710} // namespace android