1a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen/* 2a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * Copyright (C) 2012 The Android Open Source Project 3a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * 4a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * Licensed under the Apache License, Version 2.0 (the "License"); 5a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * you may not use this file except in compliance with the License. 6a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * You may obtain a copy of the License at 7a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * 8a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * http://www.apache.org/licenses/LICENSE-2.0 9a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * 10a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * Unless required by applicable law or agreed to in writing, software 11a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * distributed under the License is distributed on an "AS IS" BASIS, 12a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * See the License for the specific language governing permissions and 14a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen * limitations under the License. 15a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen */ 16a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 17a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen//#define LOG_NDEBUG 0 18a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen#define LOG_TAG "SkipCutBuffer" 19a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen#include <utils/Log.h> 20a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 21a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen#include <media/stagefright/foundation/ADebug.h> 22a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen#include <media/stagefright/MediaBuffer.h> 23a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen#include <media/stagefright/SkipCutBuffer.h> 24a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 25a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissennamespace android { 26a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 27b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco NelissenSkipCutBuffer::SkipCutBuffer(size_t skip, size_t cut, size_t num16BitChannels) { 28d5fa8d5dcdf8742f03fdc165f7529d7627b0eb01Marco Nelissen 29b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen mWriteHead = 0; 30b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen mReadHead = 0; 31b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen mCapacity = 0; 32b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen mCutBuffer = NULL; 33b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen 34b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen if (num16BitChannels == 0 || num16BitChannels > INT32_MAX / 2) { 35b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen ALOGW("# channels out of range: %zu, using passthrough instead", num16BitChannels); 36b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen return; 37d5fa8d5dcdf8742f03fdc165f7529d7627b0eb01Marco Nelissen } 38b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen size_t frameSize = num16BitChannels * 2; 39b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen if (skip > INT32_MAX / frameSize || cut > INT32_MAX / frameSize 40b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen || cut * frameSize > INT32_MAX - 4096) { 41b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen ALOGW("out of range skip/cut: %zu/%zu, using passthrough instead", 42b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen skip, cut); 43b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen return; 44b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen } 45b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen skip *= frameSize; 46b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen cut *= frameSize; 47d5fa8d5dcdf8742f03fdc165f7529d7627b0eb01Marco Nelissen 48a6cdabf38b29ee98bdb3e874b4e2978dbc409624Marco Nelissen mFrontPadding = mSkip = skip; 49a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mBackPadding = cut; 50cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen mCapacity = cut + 4096; 51b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen mCutBuffer = new (std::nothrow) char[mCapacity]; 52b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen ALOGV("skipcutbuffer %zu %zu %d", skip, cut, mCapacity); 53a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 54a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 55a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco NelissenSkipCutBuffer::~SkipCutBuffer() { 56a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen delete[] mCutBuffer; 57a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 58a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 59a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissenvoid SkipCutBuffer::submit(MediaBuffer *buffer) { 60b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen if (mCutBuffer == NULL) { 61b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen // passthrough mode 62b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen return; 63b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen } 64b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen 65a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t offset = buffer->range_offset(); 66a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t buflen = buffer->range_length(); 67a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 68a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen // drop the initial data from the buffer if needed 69a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (mFrontPadding > 0) { 70a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen // still data left to drop 71a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding; 72a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen offset += to_drop; 73a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen buflen -= to_drop; 74a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen buffer->set_range(offset, buflen); 75a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mFrontPadding -= to_drop; 76a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 77a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 78a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 79a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen // append data to cutbuffer 80a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen char *src = ((char*) buffer->data()) + offset; 81a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen write(src, buflen); 82a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 83a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 84a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen // the mediabuffer is now empty. Fill it from cutbuffer, always leaving 85a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen // at least mBackPadding bytes in the cutbuffer 86a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen char *dst = (char*) buffer->data(); 87a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen size_t copied = read(dst, buffer->size()); 88a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen buffer->set_range(0, copied); 89a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 90a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 917e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kimtemplate <typename T> 927e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kimvoid SkipCutBuffer::submitInternal(const sp<T>& buffer) { 93b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen if (mCutBuffer == NULL) { 94b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen // passthrough mode 95b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen return; 96b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen } 97b1cf03160fa7e7bc6e5cf138db07a7e1ab2ecb26Marco Nelissen 98cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen int32_t offset = buffer->offset(); 99cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen int32_t buflen = buffer->size(); 100cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 101cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // drop the initial data from the buffer if needed 102cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen if (mFrontPadding > 0) { 103cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // still data left to drop 104cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen int32_t to_drop = (buflen < mFrontPadding) ? buflen : mFrontPadding; 105cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen offset += to_drop; 106cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen buflen -= to_drop; 107cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen buffer->setRange(offset, buflen); 108cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen mFrontPadding -= to_drop; 109cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen } 110cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 111cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 112cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // append data to cutbuffer 113cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen char *src = (char*) buffer->data(); 114cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen write(src, buflen); 115cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 116cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 117cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // the mediabuffer is now empty. Fill it from cutbuffer, always leaving 118cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // at least mBackPadding bytes in the cutbuffer 119cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen char *dst = (char*) buffer->base(); 120cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen size_t copied = read(dst, buffer->capacity()); 121cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen buffer->setRange(0, copied); 122cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen} 123cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen 1247e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kimvoid SkipCutBuffer::submit(const sp<ABuffer>& buffer) { 1257e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim submitInternal(buffer); 1267e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim} 1277e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim 1287e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kimvoid SkipCutBuffer::submit(const sp<MediaCodecBuffer>& buffer) { 1297e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim submitInternal(buffer); 1307e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim} 1317e34bf5af26f8752d4786d3098740cdf51e2438fWonsik Kim 132a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissenvoid SkipCutBuffer::clear() { 133a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mWriteHead = mReadHead = 0; 134a6cdabf38b29ee98bdb3e874b4e2978dbc409624Marco Nelissen mFrontPadding = mSkip; 135a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 136a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 137a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissenvoid SkipCutBuffer::write(const char *src, size_t num) { 138a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t sizeused = (mWriteHead - mReadHead); 139a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (sizeused < 0) sizeused += mCapacity; 140a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 141cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // Everything must fit. Make sure the buffer is a little larger than needed, 142cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // so there is no ambiguity as to whether mWriteHead == mReadHead means buffer 143cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen // full or empty 144cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen size_t available = mCapacity - sizeused - 32; 145cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen if (available < num) { 146cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen int32_t newcapacity = mCapacity + (num - available); 147cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen char * newbuffer = new char[newcapacity]; 148cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen memcpy(newbuffer, mCutBuffer, mCapacity); 149cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen delete [] mCutBuffer; 150cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen mCapacity = newcapacity; 151cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen mCutBuffer = newbuffer; 152cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen ALOGV("reallocated buffer at size %d", newcapacity); 153cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen } 154a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 155a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen size_t copyfirst = (mCapacity - mWriteHead); 156a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (copyfirst > num) copyfirst = num; 157a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (copyfirst) { 158a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen memcpy(mCutBuffer + mWriteHead, src, copyfirst); 159a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen num -= copyfirst; 160a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen src += copyfirst; 161a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mWriteHead += copyfirst; 162a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen CHECK_LE(mWriteHead, mCapacity); 163a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (mWriteHead == mCapacity) mWriteHead = 0; 164a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (num) { 165a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen memcpy(mCutBuffer, src, num); 166a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mWriteHead += num; 167a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 168a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 169a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 170a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 171a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissensize_t SkipCutBuffer::read(char *dst, size_t num) { 172a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t available = (mWriteHead - mReadHead); 173a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (available < 0) available += mCapacity; 174a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 175a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen available -= mBackPadding; 176a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (available <=0) { 177a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen return 0; 178a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 179cb5b766bb0a3ed992998a5bd66de0ee1d2223b81Marco Nelissen if (available < int32_t(num)) { 180a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen num = available; 181a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 182a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 183a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen size_t copyfirst = (mCapacity - mReadHead); 184a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (copyfirst > num) copyfirst = num; 185a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (copyfirst) { 186a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen memcpy(dst, mCutBuffer + mReadHead, copyfirst); 187a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen num -= copyfirst; 188a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen dst += copyfirst; 189a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mReadHead += copyfirst; 190a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen CHECK_LE(mReadHead, mCapacity); 191a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (mReadHead == mCapacity) mReadHead = 0; 192a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (num) { 193a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen memcpy(dst, mCutBuffer, num); 194a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen mReadHead += num; 195a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 196a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen } 197a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen return available; 198a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 199a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 200a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissensize_t SkipCutBuffer::size() { 201a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen int32_t available = (mWriteHead - mReadHead); 202a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen if (available < 0) available += mCapacity; 203a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen return available; 204a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} 205a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen 206a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen} // namespace android 207