1d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber/* 2d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * Copyright (C) 2009 The Android Open Source Project 3d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * 4d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License"); 5d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * you may not use this file except in compliance with the License. 6d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * You may obtain a copy of the License at 7d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * 8d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * http://www.apache.org/licenses/LICENSE-2.0 9d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * 10d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * Unless required by applicable law or agreed to in writing, software 11d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 12d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * See the License for the specific language governing permissions and 14d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * limitations under the License. 15d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber */ 16d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 17d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include "AMRNBEncoder.h" 18d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 19d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include "gsmamr_enc.h" 20d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 21d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include <media/stagefright/MediaBufferGroup.h> 22d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include <media/stagefright/MediaDebug.h> 23d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include <media/stagefright/MediaDefs.h> 24d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include <media/stagefright/MediaErrors.h> 25d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber#include <media/stagefright/MetaData.h> 26d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 27d49b526dd2009270cb15f7fe4e70b74673950608Andreas Hubernamespace android { 28d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 29d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatic const int32_t kNumSamplesPerFrame = 160; 30d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatic const int32_t kSampleRate = 8000; 31d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 32d49b526dd2009270cb15f7fe4e70b74673950608Andreas HuberAMRNBEncoder::AMRNBEncoder(const sp<MediaSource> &source) 33d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber : mSource(source), 34d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mStarted(false), 35d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mBufferGroup(NULL), 36d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mEncState(NULL), 37d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mSidState(NULL), 38d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mAnchorTimeUs(0), 39d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumFramesOutput(0), 40d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer(NULL), 41d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mMode(MR475), 42d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumInputSamples(0) { 43d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 44d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 45d49b526dd2009270cb15f7fe4e70b74673950608Andreas HuberAMRNBEncoder::~AMRNBEncoder() { 46d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mStarted) { 47d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber stop(); 48d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 49d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 50d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 51d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatic Mode PickModeFromBitrate(int32_t bps) { 52d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (bps <= 4750) { 53d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR475; 54d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 5150) { 55d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR515; 56d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 5900) { 57d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR59; 58d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 6700) { 59d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR67; 60d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 7400) { 61d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR74; 62d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 7950) { 63d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR795; 64d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else if (bps <= 10200) { 65d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR102; 66d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else { 67d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return MR122; 68d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 69d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 70d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 71d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatus_t AMRNBEncoder::start(MetaData *params) { 72d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(!mStarted); 73d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 74d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mBufferGroup = new MediaBufferGroup; 75d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mBufferGroup->add_buffer(new MediaBuffer(32)); 76d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 77d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK_EQ(AMREncodeInit( 78d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber &mEncState, &mSidState, false /* dtx_enable */), 79d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 0); 80d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 81d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mSource->start(); 82d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 83d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mAnchorTimeUs = 0; 84d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumFramesOutput = 0; 85d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mStarted = true; 86d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumInputSamples = 0; 87d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 88d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int32_t bitrate; 89d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (params && params->findInt32(kKeyBitRate, &bitrate)) { 90d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mMode = PickModeFromBitrate(bitrate); 91d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } else { 92d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mMode = MR475; 93d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 94d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 95d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return OK; 96d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 97d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 98d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatus_t AMRNBEncoder::stop() { 99d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(mStarted); 100d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 101d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mInputBuffer) { 102d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer->release(); 103d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer = NULL; 104d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 105d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 106d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber delete mBufferGroup; 107d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mBufferGroup = NULL; 108d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 109d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mSource->stop(); 110d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 111d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber AMREncodeExit(&mEncState, &mSidState); 112d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mEncState = mSidState = NULL; 113d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 114d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mStarted = false; 115d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 116d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return OK; 117d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 118d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 119d49b526dd2009270cb15f7fe4e70b74673950608Andreas Hubersp<MetaData> AMRNBEncoder::getFormat() { 120d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber sp<MetaData> srcFormat = mSource->getFormat(); 121d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 122d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int32_t numChannels; 123d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int32_t sampleRate; 124d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 125d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); 126d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK_EQ(numChannels, 1); 127d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 128d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); 129d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK_EQ(sampleRate, kSampleRate); 130d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 131d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber sp<MetaData> meta = new MetaData; 132d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB); 133d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber meta->setInt32(kKeyChannelCount, numChannels); 134d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber meta->setInt32(kKeySampleRate, sampleRate); 135d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 136d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int64_t durationUs; 137d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (srcFormat->findInt64(kKeyDuration, &durationUs)) { 138d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber meta->setInt64(kKeyDuration, durationUs); 139d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 140d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 1417167472d843335f05fd7c9db2d44277677976097Andreas Huber meta->setCString(kKeyDecoderComponent, "AMRNBEncoder"); 1427167472d843335f05fd7c9db2d44277677976097Andreas Huber 143d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return meta; 144d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 145d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 146d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huberstatus_t AMRNBEncoder::read( 147d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber MediaBuffer **out, const ReadOptions *options) { 148d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber status_t err; 149d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 150d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber *out = NULL; 151d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 152d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int64_t seekTimeUs; 153d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(options == NULL || !options->getSeekTo(&seekTimeUs)); 154d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 155d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber while (mNumInputSamples < kNumSamplesPerFrame) { 156d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mInputBuffer == NULL) { 157d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber err = mSource->read(&mInputBuffer, options); 158d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 159d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (err != OK) { 160d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mNumInputSamples == 0) { 161d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return ERROR_END_OF_STREAM; 162d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 163d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber memset(&mInputFrame[mNumInputSamples], 164d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 0, 165d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber sizeof(int16_t) 166d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber * (kNumSamplesPerFrame - mNumInputSamples)); 167d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumInputSamples = kNumSamplesPerFrame; 168d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber break; 169d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 170d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 171d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber size_t align = mInputBuffer->range_length() % sizeof(int16_t); 172d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK_EQ(align, 0); 173d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 174d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int64_t timeUs; 175d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { 176d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mAnchorTimeUs = timeUs; 177d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumFramesOutput = 0; 178d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 179d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 180d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 181d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber size_t copy = 182d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber (kNumSamplesPerFrame - mNumInputSamples) * sizeof(int16_t); 183d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 184d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (copy > mInputBuffer->range_length()) { 185d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber copy = mInputBuffer->range_length(); 186d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 187d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 188d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber memcpy(&mInputFrame[mNumInputSamples], 189d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber (const uint8_t *)mInputBuffer->data() 190d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber + mInputBuffer->range_offset(), 191d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber copy); 192d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 193d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumInputSamples += copy / sizeof(int16_t); 194d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 195d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer->set_range( 196d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer->range_offset() + copy, 197d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer->range_length() - copy); 198d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 199d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber if (mInputBuffer->range_length() == 0) { 200d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer->release(); 201d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputBuffer = NULL; 202d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 203d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber } 204d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 205d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber MediaBuffer *buffer; 206d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); 207d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 208d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber uint8_t *outPtr = (uint8_t *)buffer->data(); 209d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 210d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber Frame_Type_3GPP frameType; 211d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber int res = AMREncode( 212d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mEncState, mSidState, (Mode)mMode, 213d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mInputFrame, outPtr, &frameType, AMR_TX_WMF); 214d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 215d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK(res >= 0); 216d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber CHECK((size_t)res < buffer->size()); 217d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 218d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber // Convert header byte from WMF to IETF format. 219d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c; 220d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 221d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber buffer->set_range(0, res); 222d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 223d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber // Each frame of 160 samples is 20ms long. 224d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber buffer->meta_data()->setInt64( 225d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber kKeyTime, mAnchorTimeUs + mNumFramesOutput * 20000); 226d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 227d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber ++mNumFramesOutput; 228d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 229d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber *out = buffer; 230d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 231d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber mNumInputSamples = 0; 232d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 233d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber return OK; 234d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} 235d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber 236d49b526dd2009270cb15f7fe4e70b74673950608Andreas Huber} // namespace android 237