1 /* 2 * Copyright (C) 2011 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 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "SimpleSoftOMXComponent" 19 #include <utils/Log.h> 20 21 #include "include/SimpleSoftOMXComponent.h" 22 23 #include <media/stagefright/foundation/ADebug.h> 24 #include <media/stagefright/foundation/ALooper.h> 25 #include <media/stagefright/foundation/AMessage.h> 26 27 namespace android { 28 29 SimpleSoftOMXComponent::SimpleSoftOMXComponent( 30 const char *name, 31 const OMX_CALLBACKTYPE *callbacks, 32 OMX_PTR appData, 33 OMX_COMPONENTTYPE **component) 34 : SoftOMXComponent(name, callbacks, appData, component), 35 mLooper(new ALooper), 36 mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)), 37 mState(OMX_StateLoaded), 38 mTargetState(OMX_StateLoaded) { 39 mLooper->setName(name); 40 mLooper->registerHandler(mHandler); 41 42 mLooper->start( 43 false, // runOnCallingThread 44 false, // canCallJava 45 ANDROID_PRIORITY_FOREGROUND); 46 } 47 48 void SimpleSoftOMXComponent::prepareForDestruction() { 49 // The looper's queue may still contain messages referencing this 50 // object. Make sure those are flushed before returning so that 51 // a subsequent dlunload() does not pull out the rug from under us. 52 53 mLooper->unregisterHandler(mHandler->id()); 54 mLooper->stop(); 55 } 56 57 OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand( 58 OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) { 59 CHECK(data == NULL); 60 61 sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler); 62 msg->setInt32("cmd", cmd); 63 msg->setInt32("param", param); 64 msg->post(); 65 66 return OMX_ErrorNone; 67 } 68 69 bool SimpleSoftOMXComponent::isSetParameterAllowed( 70 OMX_INDEXTYPE index, const OMX_PTR params) const { 71 if (mState == OMX_StateLoaded) { 72 return true; 73 } 74 75 OMX_U32 portIndex; 76 77 switch (index) { 78 case OMX_IndexParamPortDefinition: 79 { 80 const OMX_PARAM_PORTDEFINITIONTYPE *portDefs = 81 (const OMX_PARAM_PORTDEFINITIONTYPE *) params; 82 if (!isValidOMXParam(portDefs)) { 83 return false; 84 } 85 portIndex = portDefs->nPortIndex; 86 break; 87 } 88 89 case OMX_IndexParamAudioPcm: 90 { 91 const OMX_AUDIO_PARAM_PCMMODETYPE *pcmMode = 92 (const OMX_AUDIO_PARAM_PCMMODETYPE *) params; 93 if (!isValidOMXParam(pcmMode)) { 94 return false; 95 } 96 portIndex = pcmMode->nPortIndex; 97 break; 98 } 99 100 case OMX_IndexParamAudioAac: 101 { 102 const OMX_AUDIO_PARAM_AACPROFILETYPE *aacMode = 103 (const OMX_AUDIO_PARAM_AACPROFILETYPE *) params; 104 if (!isValidOMXParam(aacMode)) { 105 return false; 106 } 107 portIndex = aacMode->nPortIndex; 108 break; 109 } 110 111 default: 112 return false; 113 } 114 115 CHECK(portIndex < mPorts.size()); 116 117 return !mPorts.itemAt(portIndex).mDef.bEnabled; 118 } 119 120 OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter( 121 OMX_INDEXTYPE index, OMX_PTR params) { 122 Mutex::Autolock autoLock(mLock); 123 return internalGetParameter(index, params); 124 } 125 126 OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter( 127 OMX_INDEXTYPE index, const OMX_PTR params) { 128 Mutex::Autolock autoLock(mLock); 129 130 CHECK(isSetParameterAllowed(index, params)); 131 132 return internalSetParameter(index, params); 133 } 134 135 OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter( 136 OMX_INDEXTYPE index, OMX_PTR params) { 137 switch (index) { 138 case OMX_IndexParamPortDefinition: 139 { 140 OMX_PARAM_PORTDEFINITIONTYPE *defParams = 141 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 142 143 if (!isValidOMXParam(defParams)) { 144 return OMX_ErrorBadParameter; 145 } 146 147 if (defParams->nPortIndex >= mPorts.size() 148 || defParams->nSize 149 != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { 150 return OMX_ErrorUndefined; 151 } 152 153 const PortInfo *port = 154 &mPorts.itemAt(defParams->nPortIndex); 155 156 memcpy(defParams, &port->mDef, sizeof(port->mDef)); 157 158 return OMX_ErrorNone; 159 } 160 161 default: 162 return OMX_ErrorUnsupportedIndex; 163 } 164 } 165 166 OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter( 167 OMX_INDEXTYPE index, const OMX_PTR params) { 168 switch (index) { 169 case OMX_IndexParamPortDefinition: 170 { 171 OMX_PARAM_PORTDEFINITIONTYPE *defParams = 172 (OMX_PARAM_PORTDEFINITIONTYPE *)params; 173 174 if (!isValidOMXParam(defParams)) { 175 return OMX_ErrorBadParameter; 176 } 177 178 if (defParams->nPortIndex >= mPorts.size()) { 179 return OMX_ErrorBadPortIndex; 180 } 181 if (defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) { 182 return OMX_ErrorUnsupportedSetting; 183 } 184 185 PortInfo *port = 186 &mPorts.editItemAt(defParams->nPortIndex); 187 188 // default behavior is that we only allow buffer size to increase 189 if (defParams->nBufferSize > port->mDef.nBufferSize) { 190 port->mDef.nBufferSize = defParams->nBufferSize; 191 } 192 193 if (defParams->nBufferCountActual < port->mDef.nBufferCountMin) { 194 ALOGW("component requires at least %u buffers (%u requested)", 195 port->mDef.nBufferCountMin, defParams->nBufferCountActual); 196 return OMX_ErrorUnsupportedSetting; 197 } 198 199 port->mDef.nBufferCountActual = defParams->nBufferCountActual; 200 return OMX_ErrorNone; 201 } 202 203 default: 204 return OMX_ErrorUnsupportedIndex; 205 } 206 } 207 208 OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer( 209 OMX_BUFFERHEADERTYPE **header, 210 OMX_U32 portIndex, 211 OMX_PTR appPrivate, 212 OMX_U32 size, 213 OMX_U8 *ptr) { 214 Mutex::Autolock autoLock(mLock); 215 CHECK_LT(portIndex, mPorts.size()); 216 217 *header = new OMX_BUFFERHEADERTYPE; 218 (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE); 219 (*header)->nVersion.s.nVersionMajor = 1; 220 (*header)->nVersion.s.nVersionMinor = 0; 221 (*header)->nVersion.s.nRevision = 0; 222 (*header)->nVersion.s.nStep = 0; 223 (*header)->pBuffer = ptr; 224 (*header)->nAllocLen = size; 225 (*header)->nFilledLen = 0; 226 (*header)->nOffset = 0; 227 (*header)->pAppPrivate = appPrivate; 228 (*header)->pPlatformPrivate = NULL; 229 (*header)->pInputPortPrivate = NULL; 230 (*header)->pOutputPortPrivate = NULL; 231 (*header)->hMarkTargetComponent = NULL; 232 (*header)->pMarkData = NULL; 233 (*header)->nTickCount = 0; 234 (*header)->nTimeStamp = 0; 235 (*header)->nFlags = 0; 236 (*header)->nOutputPortIndex = portIndex; 237 (*header)->nInputPortIndex = portIndex; 238 239 PortInfo *port = &mPorts.editItemAt(portIndex); 240 241 CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE); 242 243 CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual); 244 245 port->mBuffers.push(); 246 247 BufferInfo *buffer = 248 &port->mBuffers.editItemAt(port->mBuffers.size() - 1); 249 250 buffer->mHeader = *header; 251 buffer->mOwnedByUs = false; 252 253 if (port->mBuffers.size() == port->mDef.nBufferCountActual) { 254 port->mDef.bPopulated = OMX_TRUE; 255 checkTransitions(); 256 } 257 258 return OMX_ErrorNone; 259 } 260 261 OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer( 262 OMX_BUFFERHEADERTYPE **header, 263 OMX_U32 portIndex, 264 OMX_PTR appPrivate, 265 OMX_U32 size) { 266 OMX_U8 *ptr = new OMX_U8[size]; 267 268 OMX_ERRORTYPE err = 269 useBuffer(header, portIndex, appPrivate, size, ptr); 270 271 if (err != OMX_ErrorNone) { 272 delete[] ptr; 273 ptr = NULL; 274 275 return err; 276 } 277 278 CHECK((*header)->pPlatformPrivate == NULL); 279 (*header)->pPlatformPrivate = ptr; 280 281 return OMX_ErrorNone; 282 } 283 284 OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer( 285 OMX_U32 portIndex, 286 OMX_BUFFERHEADERTYPE *header) { 287 Mutex::Autolock autoLock(mLock); 288 289 CHECK_LT(portIndex, mPorts.size()); 290 291 PortInfo *port = &mPorts.editItemAt(portIndex); 292 293 #if 0 // XXX 294 CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded) 295 || port->mDef.bEnabled == OMX_FALSE); 296 #endif 297 298 bool found = false; 299 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 300 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 301 302 if (buffer->mHeader == header) { 303 CHECK(!buffer->mOwnedByUs); 304 305 if (header->pPlatformPrivate != NULL) { 306 // This buffer's data was allocated by us. 307 CHECK(header->pPlatformPrivate == header->pBuffer); 308 309 delete[] header->pBuffer; 310 header->pBuffer = NULL; 311 } 312 313 delete header; 314 header = NULL; 315 316 port->mBuffers.removeAt(i); 317 port->mDef.bPopulated = OMX_FALSE; 318 319 checkTransitions(); 320 321 found = true; 322 break; 323 } 324 } 325 326 CHECK(found); 327 328 return OMX_ErrorNone; 329 } 330 331 OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( 332 OMX_BUFFERHEADERTYPE *buffer) { 333 sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler); 334 msg->setPointer("header", buffer); 335 msg->post(); 336 337 return OMX_ErrorNone; 338 } 339 340 OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer( 341 OMX_BUFFERHEADERTYPE *buffer) { 342 sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler); 343 msg->setPointer("header", buffer); 344 msg->post(); 345 346 return OMX_ErrorNone; 347 } 348 349 OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) { 350 Mutex::Autolock autoLock(mLock); 351 352 *state = mState; 353 354 return OMX_ErrorNone; 355 } 356 357 void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) { 358 Mutex::Autolock autoLock(mLock); 359 uint32_t msgType = msg->what(); 360 ALOGV("msgType = %d", msgType); 361 switch (msgType) { 362 case kWhatSendCommand: 363 { 364 int32_t cmd, param; 365 CHECK(msg->findInt32("cmd", &cmd)); 366 CHECK(msg->findInt32("param", ¶m)); 367 368 onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param); 369 break; 370 } 371 372 case kWhatEmptyThisBuffer: 373 case kWhatFillThisBuffer: 374 { 375 OMX_BUFFERHEADERTYPE *header; 376 CHECK(msg->findPointer("header", (void **)&header)); 377 378 CHECK(mState == OMX_StateExecuting && mTargetState == mState); 379 380 bool found = false; 381 size_t portIndex = (kWhatEmptyThisBuffer == msgType)? 382 header->nInputPortIndex: header->nOutputPortIndex; 383 PortInfo *port = &mPorts.editItemAt(portIndex); 384 385 for (size_t j = 0; j < port->mBuffers.size(); ++j) { 386 BufferInfo *buffer = &port->mBuffers.editItemAt(j); 387 388 if (buffer->mHeader == header) { 389 CHECK(!buffer->mOwnedByUs); 390 391 buffer->mOwnedByUs = true; 392 393 CHECK((msgType == kWhatEmptyThisBuffer 394 && port->mDef.eDir == OMX_DirInput) 395 || (port->mDef.eDir == OMX_DirOutput)); 396 397 port->mQueue.push_back(buffer); 398 onQueueFilled(portIndex); 399 400 found = true; 401 break; 402 } 403 } 404 405 CHECK(found); 406 break; 407 } 408 409 default: 410 TRESPASS(); 411 break; 412 } 413 } 414 415 void SimpleSoftOMXComponent::onSendCommand( 416 OMX_COMMANDTYPE cmd, OMX_U32 param) { 417 switch (cmd) { 418 case OMX_CommandStateSet: 419 { 420 onChangeState((OMX_STATETYPE)param); 421 break; 422 } 423 424 case OMX_CommandPortEnable: 425 case OMX_CommandPortDisable: 426 { 427 onPortEnable(param, cmd == OMX_CommandPortEnable); 428 break; 429 } 430 431 case OMX_CommandFlush: 432 { 433 onPortFlush(param, true /* sendFlushComplete */); 434 break; 435 } 436 437 default: 438 TRESPASS(); 439 break; 440 } 441 } 442 443 void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) { 444 ALOGV("%p requesting change from %d to %d", this, mState, state); 445 // We shouldn't be in a state transition already. 446 447 if (mState == OMX_StateLoaded 448 && mTargetState == OMX_StateIdle 449 && state == OMX_StateLoaded) { 450 // OMX specifically allows "canceling" a state transition from loaded 451 // to idle. Pretend we made it to idle, and go back to loaded 452 ALOGV("load->idle canceled"); 453 mState = mTargetState = OMX_StateIdle; 454 state = OMX_StateLoaded; 455 } 456 457 CHECK_EQ((int)mState, (int)mTargetState); 458 459 switch (mState) { 460 case OMX_StateLoaded: 461 CHECK_EQ((int)state, (int)OMX_StateIdle); 462 break; 463 case OMX_StateIdle: 464 CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting); 465 break; 466 case OMX_StateExecuting: 467 { 468 CHECK_EQ((int)state, (int)OMX_StateIdle); 469 470 for (size_t i = 0; i < mPorts.size(); ++i) { 471 onPortFlush(i, false /* sendFlushComplete */); 472 } 473 474 mState = OMX_StateIdle; 475 notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL); 476 break; 477 } 478 479 default: 480 TRESPASS(); 481 } 482 483 mTargetState = state; 484 485 checkTransitions(); 486 } 487 488 void SimpleSoftOMXComponent::onReset() { 489 // no-op 490 } 491 492 void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) { 493 CHECK_LT(portIndex, mPorts.size()); 494 495 PortInfo *port = &mPorts.editItemAt(portIndex); 496 CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE); 497 CHECK(port->mDef.bEnabled == !enable); 498 499 if (port->mDef.eDir != OMX_DirOutput) { 500 ALOGE("Port enable/disable allowed only on output ports."); 501 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); 502 android_errorWriteLog(0x534e4554, "29421804"); 503 return; 504 } 505 506 if (!enable) { 507 port->mDef.bEnabled = OMX_FALSE; 508 port->mTransition = PortInfo::DISABLING; 509 510 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 511 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 512 513 if (buffer->mOwnedByUs) { 514 buffer->mOwnedByUs = false; 515 516 if (port->mDef.eDir == OMX_DirInput) { 517 notifyEmptyBufferDone(buffer->mHeader); 518 } else { 519 CHECK_EQ(port->mDef.eDir, OMX_DirOutput); 520 notifyFillBufferDone(buffer->mHeader); 521 } 522 } 523 } 524 525 port->mQueue.clear(); 526 } else { 527 port->mTransition = PortInfo::ENABLING; 528 } 529 530 checkTransitions(); 531 } 532 533 void SimpleSoftOMXComponent::onPortFlush( 534 OMX_U32 portIndex, bool sendFlushComplete) { 535 if (portIndex == OMX_ALL) { 536 for (size_t i = 0; i < mPorts.size(); ++i) { 537 onPortFlush(i, sendFlushComplete); 538 } 539 540 if (sendFlushComplete) { 541 notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL); 542 } 543 544 return; 545 } 546 547 CHECK_LT(portIndex, mPorts.size()); 548 549 PortInfo *port = &mPorts.editItemAt(portIndex); 550 // Ideally, the port should not in transitioning state when flushing. 551 // However, in error handling case, e.g., the client can't allocate buffers 552 // when it tries to re-enable the port, the port will be stuck in ENABLING. 553 // The client will then transition the component from Executing to Idle, 554 // which leads to flushing ports. At this time, it should be ok to notify 555 // the client of the error and still clear all buffers on the port. 556 if (port->mTransition != PortInfo::NONE) { 557 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 558 } 559 560 for (size_t i = 0; i < port->mBuffers.size(); ++i) { 561 BufferInfo *buffer = &port->mBuffers.editItemAt(i); 562 563 if (!buffer->mOwnedByUs) { 564 continue; 565 } 566 567 buffer->mHeader->nFilledLen = 0; 568 buffer->mHeader->nOffset = 0; 569 buffer->mHeader->nFlags = 0; 570 571 buffer->mOwnedByUs = false; 572 573 if (port->mDef.eDir == OMX_DirInput) { 574 notifyEmptyBufferDone(buffer->mHeader); 575 } else { 576 CHECK_EQ(port->mDef.eDir, OMX_DirOutput); 577 578 notifyFillBufferDone(buffer->mHeader); 579 } 580 } 581 582 port->mQueue.clear(); 583 584 if (sendFlushComplete) { 585 notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL); 586 587 onPortFlushCompleted(portIndex); 588 } 589 } 590 591 void SimpleSoftOMXComponent::checkTransitions() { 592 if (mState != mTargetState) { 593 bool transitionComplete = true; 594 595 if (mState == OMX_StateLoaded) { 596 CHECK_EQ((int)mTargetState, (int)OMX_StateIdle); 597 598 for (size_t i = 0; i < mPorts.size(); ++i) { 599 const PortInfo &port = mPorts.itemAt(i); 600 if (port.mDef.bEnabled == OMX_FALSE) { 601 continue; 602 } 603 604 if (port.mDef.bPopulated == OMX_FALSE) { 605 transitionComplete = false; 606 break; 607 } 608 } 609 } else if (mTargetState == OMX_StateLoaded) { 610 CHECK_EQ((int)mState, (int)OMX_StateIdle); 611 612 for (size_t i = 0; i < mPorts.size(); ++i) { 613 const PortInfo &port = mPorts.itemAt(i); 614 if (port.mDef.bEnabled == OMX_FALSE) { 615 continue; 616 } 617 618 size_t n = port.mBuffers.size(); 619 620 if (n > 0) { 621 CHECK_LE(n, port.mDef.nBufferCountActual); 622 623 if (n == port.mDef.nBufferCountActual) { 624 CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE); 625 } else { 626 CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE); 627 } 628 629 transitionComplete = false; 630 break; 631 } 632 } 633 } 634 635 if (transitionComplete) { 636 ALOGV("state transition from %d to %d complete", mState, mTargetState); 637 mState = mTargetState; 638 639 if (mState == OMX_StateLoaded) { 640 onReset(); 641 } 642 643 notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL); 644 } else { 645 ALOGV("state transition from %d to %d not yet complete", mState, mTargetState); 646 } 647 } 648 649 for (size_t i = 0; i < mPorts.size(); ++i) { 650 PortInfo *port = &mPorts.editItemAt(i); 651 652 if (port->mTransition == PortInfo::DISABLING) { 653 if (port->mBuffers.empty()) { 654 ALOGV("Port %zu now disabled.", i); 655 656 port->mTransition = PortInfo::NONE; 657 notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL); 658 659 onPortEnableCompleted(i, false /* enabled */); 660 } 661 } else if (port->mTransition == PortInfo::ENABLING) { 662 if (port->mDef.bPopulated == OMX_TRUE) { 663 ALOGV("Port %zu now enabled.", i); 664 665 port->mTransition = PortInfo::NONE; 666 port->mDef.bEnabled = OMX_TRUE; 667 notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL); 668 669 onPortEnableCompleted(i, true /* enabled */); 670 } 671 } 672 } 673 } 674 675 void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) { 676 CHECK_EQ(def.nPortIndex, mPorts.size()); 677 678 mPorts.push(); 679 PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1); 680 info->mDef = def; 681 info->mTransition = PortInfo::NONE; 682 } 683 684 void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex __unused) { 685 } 686 687 void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex __unused) { 688 } 689 690 void SimpleSoftOMXComponent::onPortEnableCompleted( 691 OMX_U32 portIndex __unused, bool enabled __unused) { 692 } 693 694 List<SimpleSoftOMXComponent::BufferInfo *> & 695 SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) { 696 CHECK_LT(portIndex, mPorts.size()); 697 return mPorts.editItemAt(portIndex).mQueue; 698 } 699 700 SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo( 701 OMX_U32 portIndex) { 702 CHECK_LT(portIndex, mPorts.size()); 703 return &mPorts.editItemAt(portIndex); 704 } 705 706 } // namespace android 707