mediaplayer.cpp revision 2eeadf9ded4b5770a713496e9887d668889987bc
1/* mediaplayer.cpp 2** 3** Copyright 2006, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18//#define LOG_NDEBUG 0 19#define LOG_TAG "MediaPlayer" 20#include <utils/Log.h> 21 22#include <sys/types.h> 23#include <sys/stat.h> 24#include <unistd.h> 25#include <fcntl.h> 26 27#include <binder/IServiceManager.h> 28#include <binder/IPCThreadState.h> 29 30#include <media/mediaplayer.h> 31#include <media/AudioTrack.h> 32 33#include <surfaceflinger/Surface.h> 34 35#include <binder/MemoryBase.h> 36 37#include <utils/KeyedVector.h> 38#include <utils/String8.h> 39 40namespace android { 41 42MediaPlayer::MediaPlayer() 43{ 44 LOGV("constructor"); 45 mListener = NULL; 46 mCookie = NULL; 47 mDuration = -1; 48 mStreamType = AudioSystem::MUSIC; 49 mCurrentPosition = -1; 50 mSeekPosition = -1; 51 mCurrentState = MEDIA_PLAYER_IDLE; 52 mPrepareSync = false; 53 mPrepareStatus = NO_ERROR; 54 mLoop = false; 55 mLeftVolume = mRightVolume = 1.0; 56 mVideoWidth = mVideoHeight = 0; 57 mLockThreadId = 0; 58 mAudioSessionId = AudioSystem::newAudioSessionId(); 59 mSendLevel = 0; 60} 61 62MediaPlayer::~MediaPlayer() 63{ 64 LOGV("destructor"); 65 disconnect(); 66 IPCThreadState::self()->flushCommands(); 67} 68 69void MediaPlayer::disconnect() 70{ 71 LOGV("disconnect"); 72 sp<IMediaPlayer> p; 73 { 74 Mutex::Autolock _l(mLock); 75 p = mPlayer; 76 mPlayer.clear(); 77 } 78 79 if (p != 0) { 80 p->disconnect(); 81 } 82} 83 84// always call with lock held 85void MediaPlayer::clear_l() 86{ 87 mDuration = -1; 88 mCurrentPosition = -1; 89 mSeekPosition = -1; 90 mVideoWidth = mVideoHeight = 0; 91} 92 93status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener) 94{ 95 LOGV("setListener"); 96 Mutex::Autolock _l(mLock); 97 mListener = listener; 98 return NO_ERROR; 99} 100 101 102status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player) 103{ 104 status_t err = UNKNOWN_ERROR; 105 sp<IMediaPlayer> p; 106 { // scope for the lock 107 Mutex::Autolock _l(mLock); 108 109 if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) || 110 (mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) { 111 LOGE("setDataSource called in state %d", mCurrentState); 112 return INVALID_OPERATION; 113 } 114 115 clear_l(); 116 p = mPlayer; 117 mPlayer = player; 118 if (player != 0) { 119 mCurrentState = MEDIA_PLAYER_INITIALIZED; 120 err = NO_ERROR; 121 } else { 122 LOGE("Unable to to create media player"); 123 } 124 } 125 126 if (p != 0) { 127 p->disconnect(); 128 } 129 130 return err; 131} 132 133status_t MediaPlayer::setDataSource( 134 const char *url, const KeyedVector<String8, String8> *headers) 135{ 136 LOGV("setDataSource(%s)", url); 137 status_t err = BAD_VALUE; 138 if (url != NULL) { 139 const sp<IMediaPlayerService>& service(getMediaPlayerService()); 140 if (service != 0) { 141 sp<IMediaPlayer> player( 142 service->create(getpid(), this, url, headers, mAudioSessionId)); 143 err = setDataSource(player); 144 } 145 } 146 return err; 147} 148 149status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) 150{ 151 LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); 152 status_t err = UNKNOWN_ERROR; 153 const sp<IMediaPlayerService>& service(getMediaPlayerService()); 154 if (service != 0) { 155 sp<IMediaPlayer> player(service->create(getpid(), this, fd, offset, length, mAudioSessionId)); 156 err = setDataSource(player); 157 } 158 return err; 159} 160 161status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply) 162{ 163 Mutex::Autolock _l(mLock); 164 const bool hasBeenInitialized = 165 (mCurrentState != MEDIA_PLAYER_STATE_ERROR) && 166 ((mCurrentState & MEDIA_PLAYER_IDLE) != MEDIA_PLAYER_IDLE); 167 if ((mPlayer != NULL) && hasBeenInitialized) { 168 LOGV("invoke %d", request.dataSize()); 169 return mPlayer->invoke(request, reply); 170 } 171 LOGE("invoke failed: wrong state %X", mCurrentState); 172 return INVALID_OPERATION; 173} 174 175status_t MediaPlayer::suspend() { 176 Mutex::Autolock _l(mLock); 177 return mPlayer->suspend(); 178} 179 180status_t MediaPlayer::resume() { 181 Mutex::Autolock _l(mLock); 182 return mPlayer->resume(); 183} 184 185status_t MediaPlayer::setMetadataFilter(const Parcel& filter) 186{ 187 LOGD("setMetadataFilter"); 188 Mutex::Autolock lock(mLock); 189 if (mPlayer == NULL) { 190 return NO_INIT; 191 } 192 return mPlayer->setMetadataFilter(filter); 193} 194 195status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *metadata) 196{ 197 LOGD("getMetadata"); 198 Mutex::Autolock lock(mLock); 199 if (mPlayer == NULL) { 200 return NO_INIT; 201 } 202 return mPlayer->getMetadata(update_only, apply_filter, metadata); 203} 204 205status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) 206{ 207 LOGV("setVideoSurface"); 208 Mutex::Autolock _l(mLock); 209 if (mPlayer == 0) return NO_INIT; 210 211 status_t err = mPlayer->setVideoISurface( 212 surface == NULL ? NULL : surface->getISurface()); 213 214 if (err != OK) { 215 return err; 216 } 217 218 return mPlayer->setVideoSurface(surface); 219} 220 221// must call with lock held 222status_t MediaPlayer::prepareAsync_l() 223{ 224 if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { 225 mPlayer->setAudioStreamType(mStreamType); 226 mCurrentState = MEDIA_PLAYER_PREPARING; 227 return mPlayer->prepareAsync(); 228 } 229 LOGE("prepareAsync called in state %d", mCurrentState); 230 return INVALID_OPERATION; 231} 232 233// TODO: In case of error, prepareAsync provides the caller with 2 error codes, 234// one defined in the Android framework and one provided by the implementation 235// that generated the error. The sync version of prepare returns only 1 error 236// code. 237status_t MediaPlayer::prepare() 238{ 239 LOGV("prepare"); 240 Mutex::Autolock _l(mLock); 241 mLockThreadId = getThreadId(); 242 if (mPrepareSync) { 243 mLockThreadId = 0; 244 return -EALREADY; 245 } 246 mPrepareSync = true; 247 status_t ret = prepareAsync_l(); 248 if (ret != NO_ERROR) { 249 mLockThreadId = 0; 250 return ret; 251 } 252 253 if (mPrepareSync) { 254 mSignal.wait(mLock); // wait for prepare done 255 mPrepareSync = false; 256 } 257 LOGV("prepare complete - status=%d", mPrepareStatus); 258 mLockThreadId = 0; 259 return mPrepareStatus; 260} 261 262status_t MediaPlayer::prepareAsync() 263{ 264 LOGV("prepareAsync"); 265 Mutex::Autolock _l(mLock); 266 return prepareAsync_l(); 267} 268 269status_t MediaPlayer::start() 270{ 271 LOGV("start"); 272 Mutex::Autolock _l(mLock); 273 if (mCurrentState & MEDIA_PLAYER_STARTED) 274 return NO_ERROR; 275 if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | 276 MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) { 277 mPlayer->setLooping(mLoop); 278 mPlayer->setVolume(mLeftVolume, mRightVolume); 279 mPlayer->setAuxEffectSendLevel(mSendLevel); 280 mCurrentState = MEDIA_PLAYER_STARTED; 281 status_t ret = mPlayer->start(); 282 if (ret != NO_ERROR) { 283 mCurrentState = MEDIA_PLAYER_STATE_ERROR; 284 } else { 285 if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) { 286 LOGV("playback completed immediately following start()"); 287 } 288 } 289 return ret; 290 } 291 LOGE("start called in state %d", mCurrentState); 292 return INVALID_OPERATION; 293} 294 295status_t MediaPlayer::stop() 296{ 297 LOGV("stop"); 298 Mutex::Autolock _l(mLock); 299 if (mCurrentState & MEDIA_PLAYER_STOPPED) return NO_ERROR; 300 if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | 301 MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) ) { 302 status_t ret = mPlayer->stop(); 303 if (ret != NO_ERROR) { 304 mCurrentState = MEDIA_PLAYER_STATE_ERROR; 305 } else { 306 mCurrentState = MEDIA_PLAYER_STOPPED; 307 } 308 return ret; 309 } 310 LOGE("stop called in state %d", mCurrentState); 311 return INVALID_OPERATION; 312} 313 314status_t MediaPlayer::pause() 315{ 316 LOGV("pause"); 317 Mutex::Autolock _l(mLock); 318 if (mCurrentState & (MEDIA_PLAYER_PAUSED|MEDIA_PLAYER_PLAYBACK_COMPLETE)) 319 return NO_ERROR; 320 if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_STARTED)) { 321 status_t ret = mPlayer->pause(); 322 if (ret != NO_ERROR) { 323 mCurrentState = MEDIA_PLAYER_STATE_ERROR; 324 } else { 325 mCurrentState = MEDIA_PLAYER_PAUSED; 326 } 327 return ret; 328 } 329 LOGE("pause called in state %d", mCurrentState); 330 return INVALID_OPERATION; 331} 332 333bool MediaPlayer::isPlaying() 334{ 335 Mutex::Autolock _l(mLock); 336 if (mPlayer != 0) { 337 bool temp = false; 338 mPlayer->isPlaying(&temp); 339 LOGV("isPlaying: %d", temp); 340 if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) { 341 LOGE("internal/external state mismatch corrected"); 342 mCurrentState = MEDIA_PLAYER_PAUSED; 343 } 344 return temp; 345 } 346 LOGV("isPlaying: no active player"); 347 return false; 348} 349 350status_t MediaPlayer::getVideoWidth(int *w) 351{ 352 LOGV("getVideoWidth"); 353 Mutex::Autolock _l(mLock); 354 if (mPlayer == 0) return INVALID_OPERATION; 355 *w = mVideoWidth; 356 return NO_ERROR; 357} 358 359status_t MediaPlayer::getVideoHeight(int *h) 360{ 361 LOGV("getVideoHeight"); 362 Mutex::Autolock _l(mLock); 363 if (mPlayer == 0) return INVALID_OPERATION; 364 *h = mVideoHeight; 365 return NO_ERROR; 366} 367 368status_t MediaPlayer::getCurrentPosition(int *msec) 369{ 370 LOGV("getCurrentPosition"); 371 Mutex::Autolock _l(mLock); 372 if (mPlayer != 0) { 373 if (mCurrentPosition >= 0) { 374 LOGV("Using cached seek position: %d", mCurrentPosition); 375 *msec = mCurrentPosition; 376 return NO_ERROR; 377 } 378 return mPlayer->getCurrentPosition(msec); 379 } 380 return INVALID_OPERATION; 381} 382 383status_t MediaPlayer::getDuration_l(int *msec) 384{ 385 LOGV("getDuration"); 386 bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); 387 if (mPlayer != 0 && isValidState) { 388 status_t ret = NO_ERROR; 389 if (mDuration <= 0) 390 ret = mPlayer->getDuration(&mDuration); 391 if (msec) 392 *msec = mDuration; 393 return ret; 394 } 395 LOGE("Attempt to call getDuration without a valid mediaplayer"); 396 return INVALID_OPERATION; 397} 398 399status_t MediaPlayer::getDuration(int *msec) 400{ 401 Mutex::Autolock _l(mLock); 402 return getDuration_l(msec); 403} 404 405status_t MediaPlayer::seekTo_l(int msec) 406{ 407 LOGV("seekTo %d", msec); 408 if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { 409 if ( msec < 0 ) { 410 LOGW("Attempt to seek to invalid position: %d", msec); 411 msec = 0; 412 } else if ((mDuration > 0) && (msec > mDuration)) { 413 LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration); 414 msec = mDuration; 415 } 416 // cache duration 417 mCurrentPosition = msec; 418 if (mSeekPosition < 0) { 419 getDuration_l(NULL); 420 mSeekPosition = msec; 421 return mPlayer->seekTo(msec); 422 } 423 else { 424 LOGV("Seek in progress - queue up seekTo[%d]", msec); 425 return NO_ERROR; 426 } 427 } 428 LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState); 429 return INVALID_OPERATION; 430} 431 432status_t MediaPlayer::seekTo(int msec) 433{ 434 mLockThreadId = getThreadId(); 435 Mutex::Autolock _l(mLock); 436 status_t result = seekTo_l(msec); 437 mLockThreadId = 0; 438 439 return result; 440} 441 442status_t MediaPlayer::reset() 443{ 444 LOGV("reset"); 445 Mutex::Autolock _l(mLock); 446 mLoop = false; 447 if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR; 448 mPrepareSync = false; 449 if (mPlayer != 0) { 450 status_t ret = mPlayer->reset(); 451 if (ret != NO_ERROR) { 452 LOGE("reset() failed with return code (%d)", ret); 453 mCurrentState = MEDIA_PLAYER_STATE_ERROR; 454 } else { 455 mCurrentState = MEDIA_PLAYER_IDLE; 456 } 457 return ret; 458 } 459 clear_l(); 460 return NO_ERROR; 461} 462 463status_t MediaPlayer::setAudioStreamType(int type) 464{ 465 LOGV("MediaPlayer::setAudioStreamType"); 466 Mutex::Autolock _l(mLock); 467 if (mStreamType == type) return NO_ERROR; 468 if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | 469 MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) { 470 // Can't change the stream type after prepare 471 LOGE("setAudioStream called in state %d", mCurrentState); 472 return INVALID_OPERATION; 473 } 474 // cache 475 mStreamType = type; 476 return OK; 477} 478 479status_t MediaPlayer::setLooping(int loop) 480{ 481 LOGV("MediaPlayer::setLooping"); 482 Mutex::Autolock _l(mLock); 483 mLoop = (loop != 0); 484 if (mPlayer != 0) { 485 return mPlayer->setLooping(loop); 486 } 487 return OK; 488} 489 490bool MediaPlayer::isLooping() { 491 LOGV("isLooping"); 492 Mutex::Autolock _l(mLock); 493 if (mPlayer != 0) { 494 return mLoop; 495 } 496 LOGV("isLooping: no active player"); 497 return false; 498} 499 500status_t MediaPlayer::setVolume(float leftVolume, float rightVolume) 501{ 502 LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume); 503 Mutex::Autolock _l(mLock); 504 mLeftVolume = leftVolume; 505 mRightVolume = rightVolume; 506 if (mPlayer != 0) { 507 return mPlayer->setVolume(leftVolume, rightVolume); 508 } 509 return OK; 510} 511 512status_t MediaPlayer::setAudioSessionId(int sessionId) 513{ 514 LOGV("MediaPlayer::setAudioSessionId(%d)", sessionId); 515 Mutex::Autolock _l(mLock); 516 if (!(mCurrentState & MEDIA_PLAYER_IDLE)) { 517 LOGE("setAudioSessionId called in state %d", mCurrentState); 518 return INVALID_OPERATION; 519 } 520 if (sessionId < 0) { 521 return BAD_VALUE; 522 } 523 mAudioSessionId = sessionId; 524 return NO_ERROR; 525} 526 527int MediaPlayer::getAudioSessionId() 528{ 529 Mutex::Autolock _l(mLock); 530 return mAudioSessionId; 531} 532 533status_t MediaPlayer::setAuxEffectSendLevel(float level) 534{ 535 LOGV("MediaPlayer::setAuxEffectSendLevel(%f)", level); 536 Mutex::Autolock _l(mLock); 537 mSendLevel = level; 538 if (mPlayer != 0) { 539 return mPlayer->setAuxEffectSendLevel(level); 540 } 541 return OK; 542} 543 544status_t MediaPlayer::attachAuxEffect(int effectId) 545{ 546 LOGV("MediaPlayer::attachAuxEffect(%d)", effectId); 547 Mutex::Autolock _l(mLock); 548 if (mPlayer == 0 || 549 (mCurrentState & MEDIA_PLAYER_IDLE) || 550 (mCurrentState == MEDIA_PLAYER_STATE_ERROR )) { 551 LOGE("attachAuxEffect called in state %d", mCurrentState); 552 return INVALID_OPERATION; 553 } 554 555 return mPlayer->attachAuxEffect(effectId); 556} 557 558void MediaPlayer::notify(int msg, int ext1, int ext2) 559{ 560 LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); 561 bool send = true; 562 bool locked = false; 563 564 // TODO: In the future, we might be on the same thread if the app is 565 // running in the same process as the media server. In that case, 566 // this will deadlock. 567 // 568 // The threadId hack below works around this for the care of prepare 569 // and seekTo within the same process. 570 // FIXME: Remember, this is a hack, it's not even a hack that is applied 571 // consistently for all use-cases, this needs to be revisited. 572 if (mLockThreadId != getThreadId()) { 573 mLock.lock(); 574 locked = true; 575 } 576 577 // Allows calls from JNI in idle state to notify errors 578 if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) { 579 LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); 580 if (locked) mLock.unlock(); // release the lock when done. 581 return; 582 } 583 584 switch (msg) { 585 case MEDIA_NOP: // interface test message 586 break; 587 case MEDIA_PREPARED: 588 LOGV("prepared"); 589 mCurrentState = MEDIA_PLAYER_PREPARED; 590 if (mPrepareSync) { 591 LOGV("signal application thread"); 592 mPrepareSync = false; 593 mPrepareStatus = NO_ERROR; 594 mSignal.signal(); 595 } 596 break; 597 case MEDIA_PLAYBACK_COMPLETE: 598 LOGV("playback complete"); 599 if (mCurrentState == MEDIA_PLAYER_IDLE) { 600 LOGE("playback complete in idle state"); 601 } 602 if (!mLoop) { 603 mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE; 604 } 605 break; 606 case MEDIA_ERROR: 607 // Always log errors. 608 // ext1: Media framework error code. 609 // ext2: Implementation dependant error code. 610 LOGE("error (%d, %d)", ext1, ext2); 611 mCurrentState = MEDIA_PLAYER_STATE_ERROR; 612 if (mPrepareSync) 613 { 614 LOGV("signal application thread"); 615 mPrepareSync = false; 616 mPrepareStatus = ext1; 617 mSignal.signal(); 618 send = false; 619 } 620 break; 621 case MEDIA_INFO: 622 // ext1: Media framework error code. 623 // ext2: Implementation dependant error code. 624 LOGW("info/warning (%d, %d)", ext1, ext2); 625 break; 626 case MEDIA_SEEK_COMPLETE: 627 LOGV("Received seek complete"); 628 if (mSeekPosition != mCurrentPosition) { 629 LOGV("Executing queued seekTo(%d)", mSeekPosition); 630 mSeekPosition = -1; 631 seekTo_l(mCurrentPosition); 632 } 633 else { 634 LOGV("All seeks complete - return to regularly scheduled program"); 635 mCurrentPosition = mSeekPosition = -1; 636 } 637 break; 638 case MEDIA_BUFFERING_UPDATE: 639 LOGV("buffering %d", ext1); 640 break; 641 case MEDIA_SET_VIDEO_SIZE: 642 LOGV("New video size %d x %d", ext1, ext2); 643 mVideoWidth = ext1; 644 mVideoHeight = ext2; 645 break; 646 default: 647 LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2); 648 break; 649 } 650 651 sp<MediaPlayerListener> listener = mListener; 652 if (locked) mLock.unlock(); 653 654 // this prevents re-entrant calls into client code 655 if ((listener != 0) && send) { 656 Mutex::Autolock _l(mNotifyLock); 657 LOGV("callback application"); 658 listener->notify(msg, ext1, ext2); 659 LOGV("back from callback"); 660 } 661} 662 663/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) 664{ 665 LOGV("decode(%s)", url); 666 sp<IMemory> p; 667 const sp<IMediaPlayerService>& service = getMediaPlayerService(); 668 if (service != 0) { 669 p = service->decode(url, pSampleRate, pNumChannels, pFormat); 670 } else { 671 LOGE("Unable to locate media service"); 672 } 673 return p; 674 675} 676 677void MediaPlayer::died() 678{ 679 LOGV("died"); 680 notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0); 681} 682 683/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) 684{ 685 LOGV("decode(%d, %lld, %lld)", fd, offset, length); 686 sp<IMemory> p; 687 const sp<IMediaPlayerService>& service = getMediaPlayerService(); 688 if (service != 0) { 689 p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat); 690 } else { 691 LOGE("Unable to locate media service"); 692 } 693 return p; 694 695} 696 697}; // namespace android 698