157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber/* 257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * Copyright (C) 2010 The Android Open Source Project 357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * 457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License"); 557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * you may not use this file except in compliance with the License. 657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * You may obtain a copy of the License at 757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * 857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * http://www.apache.org/licenses/LICENSE-2.0 957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * 1057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * Unless required by applicable law or agreed to in writing, software 1157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 1257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * See the License for the specific language governing permissions and 1457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber * limitations under the License. 1557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber */ 1657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 176e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber//#define LOG_NDEBUG 0 186e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber#define LOG_TAG "AAMRAssembler" 196e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber#include <utils/Log.h> 206e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber 2157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include "AAMRAssembler.h" 2257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 2357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include "ARTPSource.h" 2457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 2557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include <media/stagefright/foundation/ABuffer.h> 2657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include <media/stagefright/foundation/ADebug.h> 2757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include <media/stagefright/foundation/AMessage.h> 2857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include <media/stagefright/foundation/hexdump.h> 2957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber#include <media/stagefright/Utils.h> 3057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 3157648e4eec7dd2593af467877bc7cce4aa654759Andreas Hubernamespace android { 3257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 3357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huberstatic bool GetAttribute(const char *s, const char *key, AString *value) { 3457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber value->clear(); 3557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 3657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t keyLen = strlen(key); 3757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 3857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber for (;;) { 3957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber const char *colonPos = strchr(s, ';'); 4057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 4157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t len = 4257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber (colonPos == NULL) ? strlen(s) : colonPos - s; 4357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 4457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { 4557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber value->setTo(&s[keyLen + 1], len - keyLen - 1); 4657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return true; 4757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 4857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (len == keyLen && !strncmp(s, key, keyLen)) { 4957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber value->setTo("1"); 5057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return true; 5157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 5257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 5357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (colonPos == NULL) { 5457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return false; 5557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 5657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 5757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber s = colonPos + 1; 5857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 5957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 6057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 6157648e4eec7dd2593af467877bc7cce4aa654759Andreas HuberAAMRAssembler::AAMRAssembler( 6257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber const sp<AMessage> ¬ify, bool isWide, const AString ¶ms) 6357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber : mIsWide(isWide), 6457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber mNotifyMsg(notify), 6557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber mNextExpectedSeqNoValid(false), 6657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber mNextExpectedSeqNo(0) { 6757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber AString value; 6857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber CHECK(GetAttribute(params.c_str(), "octet-align", &value) && value == "1"); 6957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber CHECK(!GetAttribute(params.c_str(), "crc", &value) || value == "0"); 7057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber CHECK(!GetAttribute(params.c_str(), "interleaving", &value)); 7157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 7257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 7357648e4eec7dd2593af467877bc7cce4aa654759Andreas HuberAAMRAssembler::~AAMRAssembler() { 7457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 7557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 7657648e4eec7dd2593af467877bc7cce4aa654759Andreas HuberARTPAssembler::AssemblyStatus AAMRAssembler::assembleMore( 7757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber const sp<ARTPSource> &source) { 7857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return addPacket(source); 7957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 8057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 8157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huberstatic size_t getFrameSize(bool isWide, unsigned FT) { 829a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber static const size_t kFrameSizeNB[9] = { 839a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber 95, 103, 118, 134, 148, 159, 204, 244, 39 8457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber }; 859a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber static const size_t kFrameSizeWB[10] = { 869a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber 132, 177, 253, 285, 317, 365, 397, 461, 477, 40 8757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber }; 8857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 899a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber if (FT == 15) { 909a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber return 1; 919a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber } 929a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber 9357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT]; 9457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 9557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber // Round up bits to bytes and add 1 for the header byte. 9657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber frameSize = (frameSize + 7) / 8 + 1; 9757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 9857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return frameSize; 9957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 10057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 10157648e4eec7dd2593af467877bc7cce4aa654759Andreas HuberARTPAssembler::AssemblyStatus AAMRAssembler::addPacket( 10257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber const sp<ARTPSource> &source) { 10357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber List<sp<ABuffer> > *queue = source->queue(); 10457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 10557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (queue->empty()) { 10657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return NOT_ENOUGH_DATA; 10757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 10857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 10957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (mNextExpectedSeqNoValid) { 11057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber List<sp<ABuffer> >::iterator it = queue->begin(); 11157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber while (it != queue->end()) { 11257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) { 11357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber break; 11457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 11557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 11657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber it = queue->erase(it); 11757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 11857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 11957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (queue->empty()) { 12057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return NOT_ENOUGH_DATA; 12157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 12257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 12357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 12457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber sp<ABuffer> buffer = *queue->begin(); 12557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 12657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (!mNextExpectedSeqNoValid) { 12757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber mNextExpectedSeqNoValid = true; 12857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber mNextExpectedSeqNo = (uint32_t)buffer->int32Data(); 12957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) { 1306e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber LOGV("Not the sequence number I expected"); 13157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 13257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return WRONG_SEQUENCE_NUMBER; 13357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 13457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 13557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber // hexdump(buffer->data(), buffer->size()); 13657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 13757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (buffer->size() < 1) { 13857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber queue->erase(queue->begin()); 13957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 14057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 1416e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber LOGV("AMR packet too short."); 14257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 14357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return MALFORMED_PACKET; 14457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 14557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 14657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber unsigned payloadHeader = buffer->data()[0]; 14757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber unsigned CMR = payloadHeader >> 4; 14857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber CHECK_EQ(payloadHeader & 0x0f, 0u); // RR 14957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 15057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber Vector<uint8_t> tableOfContents; 15157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 15257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t offset = 1; 15357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t totalSize = 0; 15457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber for (;;) { 15557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (offset >= buffer->size()) { 15657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber queue->erase(queue->begin()); 15757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 15857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 1596e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber LOGV("Unable to parse TOC."); 16057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 16157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return MALFORMED_PACKET; 16257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 16357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 16457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber uint8_t toc = buffer->data()[offset++]; 16557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 16657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber unsigned FT = (toc >> 3) & 0x0f; 16757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if ((toc & 3) != 0 1689a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber || (mIsWide && FT > 9 && FT != 15) 1699a023f1f8e27dc9cae9066bd94a417e15e42b270Andreas Huber || (!mIsWide && FT > 8 && FT != 15)) { 17057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber queue->erase(queue->begin()); 17157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 17257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 1736e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber LOGV("Illegal TOC entry."); 17457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 17557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return MALFORMED_PACKET; 17657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 17757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 17857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber totalSize += getFrameSize(mIsWide, (toc >> 3) & 0x0f); 17957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 18057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber tableOfContents.push(toc); 18157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 18257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (0 == (toc & 0x80)) { 18357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber break; 18457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 18557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 18657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 18757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber sp<ABuffer> accessUnit = new ABuffer(totalSize); 188eeb97d91b97f1fc0b26815f098515e9c06d219b8Andreas Huber CopyTimes(accessUnit, buffer); 18957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 19057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t dstOffset = 0; 19157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber for (size_t i = 0; i < tableOfContents.size(); ++i) { 19257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber uint8_t toc = tableOfContents[i]; 19357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 19457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber size_t frameSize = getFrameSize(mIsWide, (toc >> 3) & 0x0f); 19557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 19657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber if (offset + frameSize - 1 > buffer->size()) { 19757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber queue->erase(queue->begin()); 19857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 19957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 2006e3fa444c5b3970666707bb2b6d25e2615dafe80Andreas Huber LOGV("AMR packet too short."); 20157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 20257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return MALFORMED_PACKET; 20357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 20457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 20557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber accessUnit->data()[dstOffset++] = toc; 20657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber memcpy(accessUnit->data() + dstOffset, 20757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber buffer->data() + offset, frameSize - 1); 20857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 20957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber offset += frameSize - 1; 21057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber dstOffset += frameSize - 1; 21157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber } 21257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 21357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber sp<AMessage> msg = mNotifyMsg->dup(); 21457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber msg->setObject("access-unit", accessUnit); 21557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber msg->post(); 21657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 21757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber queue->erase(queue->begin()); 21857648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 21957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 22057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber return OK; 22157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 22257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 22357648e4eec7dd2593af467877bc7cce4aa654759Andreas Hubervoid AAMRAssembler::packetLost() { 22457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber CHECK(mNextExpectedSeqNoValid); 22557648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber ++mNextExpectedSeqNo; 22657648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 22757648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 22857648e4eec7dd2593af467877bc7cce4aa654759Andreas Hubervoid AAMRAssembler::onByeReceived() { 22957648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber sp<AMessage> msg = mNotifyMsg->dup(); 23057648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber msg->setInt32("eos", true); 23157648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber msg->post(); 23257648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} 23357648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber 23457648e4eec7dd2593af467877bc7cce4aa654759Andreas Huber} // namespace android 235