mediaplayer.cpp revision b483c4724846c0b8d4e82afcbb7c17f671bae81c
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::setMetadataFilter(const Parcel& filter)
176{
177    LOGD("setMetadataFilter");
178    Mutex::Autolock lock(mLock);
179    if (mPlayer == NULL) {
180        return NO_INIT;
181    }
182    return mPlayer->setMetadataFilter(filter);
183}
184
185status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
186{
187    LOGD("getMetadata");
188    Mutex::Autolock lock(mLock);
189    if (mPlayer == NULL) {
190        return NO_INIT;
191    }
192    return mPlayer->getMetadata(update_only, apply_filter, metadata);
193}
194
195status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
196{
197    LOGV("setVideoSurface");
198    Mutex::Autolock _l(mLock);
199    if (mPlayer == 0) return NO_INIT;
200
201    return mPlayer->setVideoSurface(surface);
202}
203
204status_t MediaPlayer::setVideoSurfaceTexture(
205        const sp<ISurfaceTexture>& surfaceTexture)
206{
207    LOGV("setVideoSurfaceTexture");
208    Mutex::Autolock _l(mLock);
209    if (mPlayer == 0) return NO_INIT;
210
211    return mPlayer->setVideoSurfaceTexture(surfaceTexture);
212}
213
214// must call with lock held
215status_t MediaPlayer::prepareAsync_l()
216{
217    if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
218        mPlayer->setAudioStreamType(mStreamType);
219        mCurrentState = MEDIA_PLAYER_PREPARING;
220        return mPlayer->prepareAsync();
221    }
222    LOGE("prepareAsync called in state %d", mCurrentState);
223    return INVALID_OPERATION;
224}
225
226// TODO: In case of error, prepareAsync provides the caller with 2 error codes,
227// one defined in the Android framework and one provided by the implementation
228// that generated the error. The sync version of prepare returns only 1 error
229// code.
230status_t MediaPlayer::prepare()
231{
232    LOGV("prepare");
233    Mutex::Autolock _l(mLock);
234    mLockThreadId = getThreadId();
235    if (mPrepareSync) {
236        mLockThreadId = 0;
237        return -EALREADY;
238    }
239    mPrepareSync = true;
240    status_t ret = prepareAsync_l();
241    if (ret != NO_ERROR) {
242        mLockThreadId = 0;
243        return ret;
244    }
245
246    if (mPrepareSync) {
247        mSignal.wait(mLock);  // wait for prepare done
248        mPrepareSync = false;
249    }
250    LOGV("prepare complete - status=%d", mPrepareStatus);
251    mLockThreadId = 0;
252    return mPrepareStatus;
253}
254
255status_t MediaPlayer::prepareAsync()
256{
257    LOGV("prepareAsync");
258    Mutex::Autolock _l(mLock);
259    return prepareAsync_l();
260}
261
262status_t MediaPlayer::start()
263{
264    LOGV("start");
265    Mutex::Autolock _l(mLock);
266    if (mCurrentState & MEDIA_PLAYER_STARTED)
267        return NO_ERROR;
268    if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
269                    MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
270        mPlayer->setLooping(mLoop);
271        mPlayer->setVolume(mLeftVolume, mRightVolume);
272        mPlayer->setAuxEffectSendLevel(mSendLevel);
273        mCurrentState = MEDIA_PLAYER_STARTED;
274        status_t ret = mPlayer->start();
275        if (ret != NO_ERROR) {
276            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
277        } else {
278            if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
279                LOGV("playback completed immediately following start()");
280            }
281        }
282        return ret;
283    }
284    LOGE("start called in state %d", mCurrentState);
285    return INVALID_OPERATION;
286}
287
288status_t MediaPlayer::stop()
289{
290    LOGV("stop");
291    Mutex::Autolock _l(mLock);
292    if (mCurrentState & MEDIA_PLAYER_STOPPED) return NO_ERROR;
293    if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED |
294                    MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) ) {
295        status_t ret = mPlayer->stop();
296        if (ret != NO_ERROR) {
297            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
298        } else {
299            mCurrentState = MEDIA_PLAYER_STOPPED;
300        }
301        return ret;
302    }
303    LOGE("stop called in state %d", mCurrentState);
304    return INVALID_OPERATION;
305}
306
307status_t MediaPlayer::pause()
308{
309    LOGV("pause");
310    Mutex::Autolock _l(mLock);
311    if (mCurrentState & (MEDIA_PLAYER_PAUSED|MEDIA_PLAYER_PLAYBACK_COMPLETE))
312        return NO_ERROR;
313    if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_STARTED)) {
314        status_t ret = mPlayer->pause();
315        if (ret != NO_ERROR) {
316            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
317        } else {
318            mCurrentState = MEDIA_PLAYER_PAUSED;
319        }
320        return ret;
321    }
322    LOGE("pause called in state %d", mCurrentState);
323    return INVALID_OPERATION;
324}
325
326bool MediaPlayer::isPlaying()
327{
328    Mutex::Autolock _l(mLock);
329    if (mPlayer != 0) {
330        bool temp = false;
331        mPlayer->isPlaying(&temp);
332        LOGV("isPlaying: %d", temp);
333        if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) {
334            LOGE("internal/external state mismatch corrected");
335            mCurrentState = MEDIA_PLAYER_PAUSED;
336        }
337        return temp;
338    }
339    LOGV("isPlaying: no active player");
340    return false;
341}
342
343status_t MediaPlayer::getVideoWidth(int *w)
344{
345    LOGV("getVideoWidth");
346    Mutex::Autolock _l(mLock);
347    if (mPlayer == 0) return INVALID_OPERATION;
348    *w = mVideoWidth;
349    return NO_ERROR;
350}
351
352status_t MediaPlayer::getVideoHeight(int *h)
353{
354    LOGV("getVideoHeight");
355    Mutex::Autolock _l(mLock);
356    if (mPlayer == 0) return INVALID_OPERATION;
357    *h = mVideoHeight;
358    return NO_ERROR;
359}
360
361status_t MediaPlayer::getCurrentPosition(int *msec)
362{
363    LOGV("getCurrentPosition");
364    Mutex::Autolock _l(mLock);
365    if (mPlayer != 0) {
366        if (mCurrentPosition >= 0) {
367            LOGV("Using cached seek position: %d", mCurrentPosition);
368            *msec = mCurrentPosition;
369            return NO_ERROR;
370        }
371        return mPlayer->getCurrentPosition(msec);
372    }
373    return INVALID_OPERATION;
374}
375
376status_t MediaPlayer::getDuration_l(int *msec)
377{
378    LOGV("getDuration");
379    bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE));
380    if (mPlayer != 0 && isValidState) {
381        status_t ret = NO_ERROR;
382        if (mDuration <= 0)
383            ret = mPlayer->getDuration(&mDuration);
384        if (msec)
385            *msec = mDuration;
386        return ret;
387    }
388    LOGE("Attempt to call getDuration without a valid mediaplayer");
389    return INVALID_OPERATION;
390}
391
392status_t MediaPlayer::getDuration(int *msec)
393{
394    Mutex::Autolock _l(mLock);
395    return getDuration_l(msec);
396}
397
398status_t MediaPlayer::seekTo_l(int msec)
399{
400    LOGV("seekTo %d", msec);
401    if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED |  MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) {
402        if ( msec < 0 ) {
403            LOGW("Attempt to seek to invalid position: %d", msec);
404            msec = 0;
405        } else if ((mDuration > 0) && (msec > mDuration)) {
406            LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration);
407            msec = mDuration;
408        }
409        // cache duration
410        mCurrentPosition = msec;
411        if (mSeekPosition < 0) {
412            getDuration_l(NULL);
413            mSeekPosition = msec;
414            return mPlayer->seekTo(msec);
415        }
416        else {
417            LOGV("Seek in progress - queue up seekTo[%d]", msec);
418            return NO_ERROR;
419        }
420    }
421    LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState);
422    return INVALID_OPERATION;
423}
424
425status_t MediaPlayer::seekTo(int msec)
426{
427    mLockThreadId = getThreadId();
428    Mutex::Autolock _l(mLock);
429    status_t result = seekTo_l(msec);
430    mLockThreadId = 0;
431
432    return result;
433}
434
435status_t MediaPlayer::reset()
436{
437    LOGV("reset");
438    Mutex::Autolock _l(mLock);
439    mLoop = false;
440    if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR;
441    mPrepareSync = false;
442    if (mPlayer != 0) {
443        status_t ret = mPlayer->reset();
444        if (ret != NO_ERROR) {
445            LOGE("reset() failed with return code (%d)", ret);
446            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
447        } else {
448            mCurrentState = MEDIA_PLAYER_IDLE;
449        }
450        // setDataSource has to be called again to create a
451        // new mediaplayer.
452        mPlayer = 0;
453        return ret;
454    }
455    clear_l();
456    return NO_ERROR;
457}
458
459status_t MediaPlayer::setAudioStreamType(int type)
460{
461    LOGV("MediaPlayer::setAudioStreamType");
462    Mutex::Autolock _l(mLock);
463    if (mStreamType == type) return NO_ERROR;
464    if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
465                MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {
466        // Can't change the stream type after prepare
467        LOGE("setAudioStream called in state %d", mCurrentState);
468        return INVALID_OPERATION;
469    }
470    // cache
471    mStreamType = type;
472    return OK;
473}
474
475status_t MediaPlayer::setLooping(int loop)
476{
477    LOGV("MediaPlayer::setLooping");
478    Mutex::Autolock _l(mLock);
479    mLoop = (loop != 0);
480    if (mPlayer != 0) {
481        return mPlayer->setLooping(loop);
482    }
483    return OK;
484}
485
486bool MediaPlayer::isLooping() {
487    LOGV("isLooping");
488    Mutex::Autolock _l(mLock);
489    if (mPlayer != 0) {
490        return mLoop;
491    }
492    LOGV("isLooping: no active player");
493    return false;
494}
495
496status_t MediaPlayer::setVolume(float leftVolume, float rightVolume)
497{
498    LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume);
499    Mutex::Autolock _l(mLock);
500    mLeftVolume = leftVolume;
501    mRightVolume = rightVolume;
502    if (mPlayer != 0) {
503        return mPlayer->setVolume(leftVolume, rightVolume);
504    }
505    return OK;
506}
507
508status_t MediaPlayer::setAudioSessionId(int sessionId)
509{
510    LOGV("MediaPlayer::setAudioSessionId(%d)", sessionId);
511    Mutex::Autolock _l(mLock);
512    if (!(mCurrentState & MEDIA_PLAYER_IDLE)) {
513        LOGE("setAudioSessionId called in state %d", mCurrentState);
514        return INVALID_OPERATION;
515    }
516    if (sessionId < 0) {
517        return BAD_VALUE;
518    }
519    mAudioSessionId = sessionId;
520    return NO_ERROR;
521}
522
523int MediaPlayer::getAudioSessionId()
524{
525    Mutex::Autolock _l(mLock);
526    return mAudioSessionId;
527}
528
529status_t MediaPlayer::setAuxEffectSendLevel(float level)
530{
531    LOGV("MediaPlayer::setAuxEffectSendLevel(%f)", level);
532    Mutex::Autolock _l(mLock);
533    mSendLevel = level;
534    if (mPlayer != 0) {
535        return mPlayer->setAuxEffectSendLevel(level);
536    }
537    return OK;
538}
539
540status_t MediaPlayer::attachAuxEffect(int effectId)
541{
542    LOGV("MediaPlayer::attachAuxEffect(%d)", effectId);
543    Mutex::Autolock _l(mLock);
544    if (mPlayer == 0 ||
545        (mCurrentState & MEDIA_PLAYER_IDLE) ||
546        (mCurrentState == MEDIA_PLAYER_STATE_ERROR )) {
547        LOGE("attachAuxEffect called in state %d", mCurrentState);
548        return INVALID_OPERATION;
549    }
550
551    return mPlayer->attachAuxEffect(effectId);
552}
553
554void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
555{
556    LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
557    bool send = true;
558    bool locked = false;
559
560    // TODO: In the future, we might be on the same thread if the app is
561    // running in the same process as the media server. In that case,
562    // this will deadlock.
563    //
564    // The threadId hack below works around this for the care of prepare
565    // and seekTo within the same process.
566    // FIXME: Remember, this is a hack, it's not even a hack that is applied
567    // consistently for all use-cases, this needs to be revisited.
568     if (mLockThreadId != getThreadId()) {
569        mLock.lock();
570        locked = true;
571    }
572
573    // Allows calls from JNI in idle state to notify errors
574    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
575        LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
576        if (locked) mLock.unlock();   // release the lock when done.
577        return;
578    }
579
580    switch (msg) {
581    case MEDIA_NOP: // interface test message
582        break;
583    case MEDIA_PREPARED:
584        LOGV("prepared");
585        mCurrentState = MEDIA_PLAYER_PREPARED;
586        if (mPrepareSync) {
587            LOGV("signal application thread");
588            mPrepareSync = false;
589            mPrepareStatus = NO_ERROR;
590            mSignal.signal();
591        }
592        break;
593    case MEDIA_PLAYBACK_COMPLETE:
594        LOGV("playback complete");
595        if (mCurrentState == MEDIA_PLAYER_IDLE) {
596            LOGE("playback complete in idle state");
597        }
598        if (!mLoop) {
599            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
600        }
601        break;
602    case MEDIA_ERROR:
603        // Always log errors.
604        // ext1: Media framework error code.
605        // ext2: Implementation dependant error code.
606        LOGE("error (%d, %d)", ext1, ext2);
607        mCurrentState = MEDIA_PLAYER_STATE_ERROR;
608        if (mPrepareSync)
609        {
610            LOGV("signal application thread");
611            mPrepareSync = false;
612            mPrepareStatus = ext1;
613            mSignal.signal();
614            send = false;
615        }
616        break;
617    case MEDIA_INFO:
618        // ext1: Media framework error code.
619        // ext2: Implementation dependant error code.
620        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
621            LOGW("info/warning (%d, %d)", ext1, ext2);
622        }
623        break;
624    case MEDIA_SEEK_COMPLETE:
625        LOGV("Received seek complete");
626        if (mSeekPosition != mCurrentPosition) {
627            LOGV("Executing queued seekTo(%d)", mSeekPosition);
628            mSeekPosition = -1;
629            seekTo_l(mCurrentPosition);
630        }
631        else {
632            LOGV("All seeks complete - return to regularly scheduled program");
633            mCurrentPosition = mSeekPosition = -1;
634        }
635        break;
636    case MEDIA_BUFFERING_UPDATE:
637        LOGV("buffering %d", ext1);
638        break;
639    case MEDIA_SET_VIDEO_SIZE:
640        LOGV("New video size %d x %d", ext1, ext2);
641        mVideoWidth = ext1;
642        mVideoHeight = ext2;
643        break;
644    case MEDIA_TIMED_TEXT:
645        LOGV("Received timed text message");
646        break;
647    default:
648        LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
649        break;
650    }
651
652    sp<MediaPlayerListener> listener = mListener;
653    if (locked) mLock.unlock();
654
655    // this prevents re-entrant calls into client code
656    if ((listener != 0) && send) {
657        Mutex::Autolock _l(mNotifyLock);
658        LOGV("callback application");
659        listener->notify(msg, ext1, ext2, obj);
660        LOGV("back from callback");
661    }
662}
663
664/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
665{
666    LOGV("decode(%s)", url);
667    sp<IMemory> p;
668    const sp<IMediaPlayerService>& service = getMediaPlayerService();
669    if (service != 0) {
670        p = service->decode(url, pSampleRate, pNumChannels, pFormat);
671    } else {
672        LOGE("Unable to locate media service");
673    }
674    return p;
675
676}
677
678void MediaPlayer::died()
679{
680    LOGV("died");
681    notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
682}
683
684/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
685{
686    LOGV("decode(%d, %lld, %lld)", fd, offset, length);
687    sp<IMemory> p;
688    const sp<IMediaPlayerService>& service = getMediaPlayerService();
689    if (service != 0) {
690        p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat);
691    } else {
692        LOGE("Unable to locate media service");
693    }
694    return p;
695
696}
697
698}; // namespace android
699