1f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten/* 2f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * Copyright (C) 2014 The Android Open Source Project 3f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * 4f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License"); 5f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * you may not use this file except in compliance with the License. 6f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * You may obtain a copy of the License at 7f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * 8f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * http://www.apache.org/licenses/LICENSE-2.0 9f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * 10f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * Unless required by applicable law or agreed to in writing, software 11f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS, 12f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * See the License for the specific language governing permissions and 14f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten * limitations under the License. 15f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten */ 16f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 17f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#define LOG_TAG "FastCapture" 18f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten//#define LOG_NDEBUG 0 19f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 20f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#define ATRACE_TAG ATRACE_TAG_AUDIO 21f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 22f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include "Configuration.h" 23f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include <linux/futex.h> 24f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include <sys/syscall.h> 25f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include <media/AudioBufferProvider.h> 26f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include <utils/Log.h> 27f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include <utils/Trace.h> 28f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten#include "FastCapture.h" 29f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 30f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastennamespace android { 31f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 32f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten/*static*/ const FastCaptureState FastCapture::initial; 33f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 34f91df1b368a140abd37c80b204bd48d78778cc43Glenn KastenFastCapture::FastCapture() : FastThread(), 35f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0), 36f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0), 37f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // dummyDumpState 38f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten totalNativeFramesRead(0) 39f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 40f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten previous = &initial; 41f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten current = &initial; 42f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 43f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten mDummyDumpState = &dummyDumpState; 44f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 45f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 46f91df1b368a140abd37c80b204bd48d78778cc43Glenn KastenFastCapture::~FastCapture() 47f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 48f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 49f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 50f91df1b368a140abd37c80b204bd48d78778cc43Glenn KastenFastCaptureStateQueue* FastCapture::sq() 51f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 52f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten return &mSQ; 53f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 54f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 55f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenconst FastThreadState *FastCapture::poll() 56f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 57f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten return mSQ.poll(); 58f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 59f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 60f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenvoid FastCapture::setLog(NBLog::Writer *logWriter __unused) 61f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 62f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 63f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 64f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenvoid FastCapture::onIdle() 65f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 66f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten preIdle = *(const FastCaptureState *)current; 67f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten current = &preIdle; 68f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 69f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 70f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenvoid FastCapture::onExit() 71f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 72f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten delete[] readBuffer; 73f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 74f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 75f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenbool FastCapture::isSubClassCommand(FastThreadState::Command command) 76f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 77f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten switch ((FastCaptureState::Command) command) { 78f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten case FastCaptureState::READ: 79f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten case FastCaptureState::WRITE: 80f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten case FastCaptureState::READ_WRITE: 81f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten return true; 82f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten default: 83f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten return false; 84f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 85f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 86f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 87f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenvoid FastCapture::onStateChange() 88f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 89f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const FastCaptureState * const current = (const FastCaptureState *) this->current; 90f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const FastCaptureState * const previous = (const FastCaptureState *) this->previous; 91f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; 92f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const size_t frameCount = current->mFrameCount; 93f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 94f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten bool eitherChanged = false; 95f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 96f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // check for change in input HAL configuration 97f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten NBAIO_Format previousFormat = format; 98f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (current->mInputSourceGen != inputSourceGen) { 99f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten inputSource = current->mInputSource; 100f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten inputSourceGen = current->mInputSourceGen; 101f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (inputSource == NULL) { 102f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten format = Format_Invalid; 103f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten sampleRate = 0; 104f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } else { 105f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten format = inputSource->format(); 106f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten sampleRate = Format_sampleRate(format); 107f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten unsigned channelCount = Format_channelCount(format); 108f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(channelCount == 1 || channelCount == 2); 109f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 110f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mSampleRate = sampleRate; 111f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten eitherChanged = true; 112f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 113f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 114f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // check for change in pipe 115f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (current->mPipeSinkGen != pipeSinkGen) { 116f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten pipeSink = current->mPipeSink; 117f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten pipeSinkGen = current->mPipeSinkGen; 118f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten eitherChanged = true; 119f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 120f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 121f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // input source and pipe sink must be compatible 122f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (eitherChanged && inputSource != NULL && pipeSink != NULL) { 123f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(Format_isEqual(format, pipeSink->format())); 124f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 125f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 126f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { 127f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME to avoid priority inversion, don't delete here 128f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten delete[] readBuffer; 129f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBuffer = NULL; 130f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (frameCount > 0 && sampleRate > 0) { 131f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME new may block for unbounded time at internal mutex of the heap 132f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // implementation; it would be better to have normal capture thread allocate for 133f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // us to avoid blocking here and to prevent possible priority inversion 134f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten unsigned channelCount = Format_channelCount(format); 135f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME frameSize 136f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBuffer = new short[frameCount * channelCount]; 137f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 138f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 139f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 140f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 141f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 142f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } else { 143f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten periodNs = 0; 144f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten underrunNs = 0; 145f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten overrunNs = 0; 146f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten forceNs = 0; 147f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten warmupNs = 0; 148f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 149f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBufferState = -1; 150f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mFrameCount = frameCount; 151f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 152f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 153f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 154f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 155f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kastenvoid FastCapture::onWork() 156f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 157f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const FastCaptureState * const current = (const FastCaptureState *) this->current; 158f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; 159f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const FastCaptureState::Command command = this->command; 160f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten const size_t frameCount = current->mFrameCount; 161f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 162f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if ((command & FastCaptureState::READ) /*&& isWarm*/) { 163f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(inputSource != NULL); 164f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(readBuffer != NULL); 165f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mReadSequence++; 166f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ATRACE_BEGIN("read"); 167f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ssize_t framesRead = inputSource->read(readBuffer, frameCount, 168f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten AudioBufferProvider::kInvalidPTS); 169f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ATRACE_END(); 170f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mReadSequence++; 171f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (framesRead >= 0) { 172f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); 173f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten totalNativeFramesRead += framesRead; 174f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mFramesRead = totalNativeFramesRead; 175f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBufferState = framesRead; 176f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } else { 177f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten dumpState->mReadErrors++; 178f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBufferState = 0; 179f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 180f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME rename to attemptedIO 181f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten attemptedWrite = true; 182f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 183f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 184f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (command & FastCaptureState::WRITE) { 185f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(pipeSink != NULL); 186f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ALOG_ASSERT(readBuffer != NULL); 187f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (readBufferState < 0) { 188f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten unsigned channelCount = Format_channelCount(format); 189f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME frameSize 190f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten memset(readBuffer, 0, frameCount * channelCount * sizeof(short)); 191f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten readBufferState = frameCount; 192f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 193f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (readBufferState > 0) { 194f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState); 195f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // FIXME This supports at most one fast capture client. 196f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // To handle multiple clients this could be converted to an array, 197f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // or with a lot more work the control block could be shared by all clients. 198f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten audio_track_cblk_t* cblk = current->mCblk; 199f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (cblk != NULL && framesWritten > 0) { 200f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten int32_t rear = cblk->u.mStreaming.mRear; 201f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear); 202f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten cblk->mServer += framesWritten; 203f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); 204f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten if (!(old & CBLK_FUTEX_WAKE)) { 205f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten // client is never in server process, so don't use FUTEX_WAKE_PRIVATE 206f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1); 207f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 208f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 209f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 210f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten } 211f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 212f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 213f91df1b368a140abd37c80b204bd48d78778cc43Glenn KastenFastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), 214f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) 215f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 216f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 217f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 218f91df1b368a140abd37c80b204bd48d78778cc43Glenn KastenFastCaptureDumpState::~FastCaptureDumpState() 219f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten{ 220f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} 221f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten 222f91df1b368a140abd37c80b204bd48d78778cc43Glenn Kasten} // namespace android 223