Merge "Revert "Move MediaBufferXXX from foundation to libmediaextractor""
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index fce8dd6..3fe2336 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -1,4 +1,5 @@
-cc_library_shared {
+cc_defaults {
+    name: "libmp4extractor_defaults",
 
     srcs: [
         "ItemTable.cpp",
@@ -23,17 +24,21 @@
         "libstagefright_id3",
     ],
 
-    name: "libmp4extractor",
-    relative_install_path: "extractors",
-
-    compile_multilib: "first",
-
     cflags: [
         "-Werror",
         "-Wall",
         "-fvisibility=hidden",
     ],
     version_script: "exports.lds",
+    relative_install_path: "extractors",
+    compile_multilib: "first",
+}
+
+cc_library_shared {
+
+
+    name: "libmp4extractor",
+    defaults: ["libmp4extractor_defaults"],
 
     sanitize: {
         cfi: true,
@@ -47,3 +52,9 @@
     },
 
 }
+
+cc_library_static {
+    name: "libmp4extractor_fuzzing",
+
+    defaults: ["libmp4extractor_defaults"],
+}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index bc294c5..3aebb8a 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -77,22 +77,47 @@
     return rawbuffer;
 }
 
+static std::string audioSourceString(audio_source_t value) {
+    std::string source;
+    if (SourceTypeConverter::toString(value, source)) {
+        return source;
+    }
+    char rawbuffer[16];  // room for "%d"
+    snprintf(rawbuffer, sizeof(rawbuffer), "%d", value);
+    return rawbuffer;
+}
+
 void AudioRecord::MediaMetrics::gather(const AudioRecord *record)
 {
     // key for media statistics is defined in the header
     // attrs for media statistics
     static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels";
-    static constexpr char kAudioRecordFormat[] = "android.media.audiorecord.format";
+    static constexpr char kAudioRecordEncoding[] = "android.media.audiorecord.encoding";
     static constexpr char kAudioRecordLatency[] = "android.media.audiorecord.latency";
     static constexpr char kAudioRecordSampleRate[] = "android.media.audiorecord.samplerate";
+    static constexpr char kAudioRecordSource[] = "android.media.audiotrack.source";
 
     // constructor guarantees mAnalyticsItem is valid
 
     mAnalyticsItem->setInt32(kAudioRecordLatency, record->mLatency);
     mAnalyticsItem->setInt32(kAudioRecordSampleRate, record->mSampleRate);
     mAnalyticsItem->setInt32(kAudioRecordChannelCount, record->mChannelCount);
-    mAnalyticsItem->setCString(kAudioRecordFormat,
+    mAnalyticsItem->setCString(kAudioRecordEncoding,
                                audioFormatTypeString(record->mFormat).c_str());
+    mAnalyticsItem->setCString(kAudioRecordSource,
+                               audioSourceString(record->mAttributes.source).c_str());
+}
+
+// hand the user a snapshot of the metrics.
+status_t AudioRecord::getMetrics(MediaAnalyticsItem * &item)
+{
+    mMediaMetrics.gather(this);
+    MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+    if (tmp == nullptr) {
+        return BAD_VALUE;
+    }
+    item = tmp;
+    return NO_ERROR;
 }
 
 AudioRecord::AudioRecord(const String16 &opPackageName)
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index a3c66fe..7dd3f29 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -189,22 +189,23 @@
     static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage";
     static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate";
     static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask";
-#if 0
-    // XXX: disabled temporarily for b/72027185
     static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes";
-#endif
     static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup";
 
+    // only if we're in a good state...
+    // XXX: shall we gather alternative info if failing?
+    const status_t lstatus = track->initCheck();
+    if (lstatus != NO_ERROR) {
+        ALOGD("no metrics gathered, track status=%d", (int) lstatus);
+        return;
+    }
+
     // constructor guarantees mAnalyticsItem is valid
 
-#if 0
-    // XXX: disabled temporarily for b/72027185
-    // must gather underrun info before cleaning mProxy information.
     const int32_t underrunFrames = track->getUnderrunFrames();
     if (underrunFrames != 0) {
         mAnalyticsItem->setInt32(kAudioTrackUnderrunFrames, underrunFrames);
     }
-#endif
 
     if (track->mTimestampStartupGlitchReported) {
         mAnalyticsItem->setInt32(kAudioTrackStartupGlitch, 1);
@@ -223,6 +224,17 @@
     mAnalyticsItem->setInt64(kAudioTrackChannelMask, track->mChannelMask);
 }
 
+// hand the user a snapshot of the metrics.
+status_t AudioTrack::getMetrics(MediaAnalyticsItem * &item)
+{
+    mMediaMetrics.gather(this);
+    MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+    if (tmp == nullptr) {
+        return BAD_VALUE;
+    }
+    item = tmp;
+    return NO_ERROR;
+}
 
 AudioTrack::AudioTrack()
     : mStatus(NO_INIT),
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index fea973a..caaefce 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -256,6 +256,11 @@
      */
             uint32_t    getNotificationPeriodInFrames() const { return mNotificationFramesAct; }
 
+    /*
+     * return metrics information for the current instance.
+     */
+            status_t getMetrics(MediaAnalyticsItem * &item);
+
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
      * If event is not AudioSystem::SYNC_EVENT_NONE, the capture start will be delayed until
@@ -703,6 +708,7 @@
             }
         }
         void gather(const AudioRecord *record);
+        MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
       private:
         std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
     };
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index c146db9..8fbe980 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -386,6 +386,11 @@
     /* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
             sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
 
+    /*
+     * return metrics information for the current track.
+     */
+            status_t getMetrics(MediaAnalyticsItem * &item);
+
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
      * If the track was previously paused, volume is ramped up over the first mix buffer.
@@ -1198,6 +1203,7 @@
             }
         }
         void gather(const AudioTrack *track);
+        MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
       private:
         std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
     };
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 17c9648..d96f7e0 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -660,6 +660,28 @@
     return err;
 }
 
+status_t NuMediaExtractor::getSampleSize(size_t *sampleSize) {
+    Mutex::Autolock autoLock(mLock);
+
+    ssize_t minIndex = fetchAllTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+    auto it = info->mSamples.begin();
+    *sampleSize = it->mBuffer->range_length();
+
+    if (info->mTrackFlags & kIsVorbis) {
+        // Each sample's data is suffixed by the number of page samples
+        // or -1 if not available.
+        *sampleSize += sizeof(int32_t);
+    }
+
+    return OK;
+}
+
 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index eed0f05..6a2e39b 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -85,6 +85,7 @@
     // readSampleData() reads the sample with the lowest timestamp.
     status_t readSampleData(const sp<ABuffer> &buffer);
 
+    status_t getSampleSize(size_t *sampleSize);
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
     status_t getSampleMeta(sp<MetaData> *sampleMeta);
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index cea2f9e..ca691f7 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -37,6 +37,7 @@
     srcs: [
         "NdkMediaCodec.cpp",
         "NdkMediaCrypto.cpp",
+        "NdkMediaDataSource.cpp",
         "NdkMediaExtractor.cpp",
         "NdkMediaFormat.cpp",
         "NdkMediaMuxer.cpp",
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
new file mode 100644
index 0000000..0cae6f4
--- /dev/null
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkMediaDataSource"
+
+#include "NdkMediaDataSourcePriv.h"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <unistd.h>
+
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaDataSource.h>
+#include <media/stagefright/InterfaceUtils.h>
+
+#include "../../libstagefright/include/HTTPBase.h"
+#include "../../libstagefright/include/NuCachedSource2.h"
+
+using namespace android;
+
+struct AMediaDataSource {
+    void *userdata;
+    AMediaDataSourceReadAt readAt;
+    AMediaDataSourceGetSize getSize;
+};
+
+NdkDataSource::NdkDataSource(AMediaDataSource *dataSource)
+    : mDataSource(dataSource) {
+}
+
+status_t NdkDataSource::initCheck() const {
+    return OK;
+}
+
+ssize_t NdkDataSource::readAt(off64_t offset, void *data, size_t size) {
+    Mutex::Autolock l(mLock);
+    if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) {
+        return -1;
+    }
+    return mDataSource->readAt(mDataSource->userdata, offset, data, size);
+}
+
+status_t NdkDataSource::getSize(off64_t *size) {
+    Mutex::Autolock l(mLock);
+    if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) {
+        return NO_INIT;
+    }
+    if (size != NULL) {
+        *size = mDataSource->getSize(mDataSource->userdata);
+    }
+    return OK;
+}
+
+String8 NdkDataSource::toString() {
+    return String8::format("NdkDataSource(pid %d, uid %d)", getpid(), getuid());
+}
+
+String8 NdkDataSource::getMIMEType() const {
+    return String8("application/octet-stream");
+}
+
+extern "C" {
+
+EXPORT
+AMediaDataSource* AMediaDataSource_new() {
+    AMediaDataSource *mSource = new AMediaDataSource();
+    mSource->userdata = NULL;
+    mSource->readAt = NULL;
+    mSource->getSize = NULL;
+    return mSource;
+}
+
+EXPORT
+void AMediaDataSource_delete(AMediaDataSource *mSource) {
+    ALOGV("dtor");
+    if (mSource != NULL) {
+        delete mSource;
+    }
+}
+
+EXPORT
+void AMediaDataSource_setUserdata(AMediaDataSource *mSource, void *userdata) {
+    mSource->userdata = userdata;
+}
+
+EXPORT
+void AMediaDataSource_setReadAt(AMediaDataSource *mSource, AMediaDataSourceReadAt readAt) {
+    mSource->readAt = readAt;
+}
+
+EXPORT
+void AMediaDataSource_setGetSize(AMediaDataSource *mSource, AMediaDataSourceGetSize getSize) {
+    mSource->getSize = getSize;
+}
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaDataSourcePriv.h b/media/ndk/NdkMediaDataSourcePriv.h
new file mode 100644
index 0000000..a1cb331
--- /dev/null
+++ b/media/ndk/NdkMediaDataSourcePriv.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_DATASOURCE_PRIV_H
+#define _NDK_MEDIA_DATASOURCE_PRIV_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <media/DataSource.h>
+#include <media/NdkMediaDataSource.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+struct NdkDataSource : public DataSource {
+
+    NdkDataSource(AMediaDataSource *);
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+    virtual status_t getSize(off64_t *);
+    virtual String8 toString();
+    virtual String8 getMIMEType() const;
+
+private:
+
+    Mutex mLock;
+    AMediaDataSource *mDataSource;
+
+};
+
+#endif // _NDK_MEDIA_DATASOURCE_PRIV_H
+
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index e677d00..5dee8b0 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -20,6 +20,7 @@
 
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaExtractor.h>
+#include "NdkMediaDataSourcePriv.h"
 #include "NdkMediaFormatPriv.h"
 
 
@@ -121,6 +122,11 @@
 }
 
 EXPORT
+media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor* mData, AMediaDataSource *src) {
+    return translate_error(mData->mImpl->setDataSource(new NdkDataSource(src)));
+}
+
+EXPORT
 size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
     return mData->mImpl->countTracks();
 }
diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h
new file mode 100644
index 0000000..752b684
--- /dev/null
+++ b/media/ndk/include/media/NdkMediaDataSource.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_DATASOURCE_H
+#define _NDK_MEDIA_DATASOURCE_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <media/NdkMediaError.h>
+
+__BEGIN_DECLS
+
+struct AMediaDataSource;
+typedef struct AMediaDataSource AMediaDataSource;
+
+#if __ANDROID_API__ >= 28
+
+/*
+ * AMediaDataSource's callbacks will be invoked on an implementation-defined thread
+ * or thread pool. No guarantees are provided about which thread(s) will be used for
+ * callbacks. However, it is guaranteed that AMediaDataSource's callbacks will only
+ * ever be invoked by a single thread at a time.
+ *
+ * There will be a thread synchronization point between each call to ensure that
+ * modifications to the state of your AMediaDataSource are visible to future
+ * calls. This means you don't need to do your own synchronization unless you're
+ * modifying the AMediaDataSource from another thread while it's being used by the
+ * framework.
+ */
+
+/**
+ * Called to request data from the given |offset|.
+ *
+ * Implementations should should write up to |size| bytes into
+ * |buffer|, and return the number of bytes written.
+ *
+ * Return 0 if size is zero (thus no bytes are read).
+ *
+ * Return -1 to indicate that end of stream is reached.
+ */
+typedef ssize_t (*AMediaDataSourceReadAt)(
+        void *userdata, off64_t offset, void * buffer, size_t size);
+
+/**
+ * Called to get the size of the data source.
+ *
+ * Return the size of data source in bytes, or -1 if the size is unknown.
+ */
+typedef ssize_t (*AMediaDataSourceGetSize)(void *userdata);
+
+/**
+ * Create new media data source. Returns NULL if memory allocation
+ * for the new data source object fails.
+ */
+AMediaDataSource* AMediaDataSource_new();
+
+/**
+ * Delete a previously created media data source.
+ */
+void AMediaDataSource_delete(AMediaDataSource*);
+
+/**
+ * Set an user provided opaque handle. This opaque handle is passed as
+ * the first argument to the data source callbacks.
+ */
+void AMediaDataSource_setUserdata(
+        AMediaDataSource*, void *userdata);
+
+/**
+ * Set a custom callback for supplying random access media data to the
+ * NDK media framework.
+ *
+ * Implement this if your app has special requirements for the way media
+ * data is obtained, or if you need a callback when data is read by the
+ * NDK media framework.
+ *
+ * Please refer to the definition of AMediaDataSourceReadAt for
+ * additional details.
+ */
+void AMediaDataSource_setReadAt(
+        AMediaDataSource*,
+        AMediaDataSourceReadAt);
+
+/**
+ * Set a custom callback for supplying the size of the data source to the
+ * NDK media framework.
+ *
+ * Please refer to the definition of AMediaDataSourceGetSize for
+ * additional details.
+ */
+void AMediaDataSource_setGetSize(
+        AMediaDataSource*,
+        AMediaDataSourceGetSize);
+
+#endif  /*__ANDROID_API__ >= 28 */
+
+__END_DECLS
+
+#endif // _NDK_MEDIA_DATASOURCE_H
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index bf0e46d..820e9f5 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 
 #include "NdkMediaCodec.h"
+#include "NdkMediaDataSource.h"
 #include "NdkMediaFormat.h"
 #include "NdkMediaCrypto.h"
 
@@ -64,6 +65,15 @@
 media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location);
         // TODO support headers
 
+#if __ANDROID_API__ >= 28
+
+/**
+ * Set the custom data source implementation from which the extractor will read.
+ */
+media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*, AMediaDataSource *src);
+
+#endif /* __ANDROID_API__ >= 28 */
+
 /**
  * Return the number of tracks in the previously specified media file
  */
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index f2d97cd..613cc63 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -123,6 +123,11 @@
     AMediaCrypto_isCryptoSchemeSupported;
     AMediaCrypto_new;
     AMediaCrypto_requiresSecureDecoderComponent;
+    AMediaDataSource_delete;      # introduced=28
+    AMediaDataSource_new;         # introduced=28
+    AMediaDataSource_setGetSize;  # introduced=28
+    AMediaDataSource_setReadAt;   # introduced=28
+    AMediaDataSource_setUserdata; # introduced=28
     AMediaDrm_closeSession;
     AMediaDrm_createByUUID;
     AMediaDrm_decrypt;
@@ -160,6 +165,7 @@
     AMediaExtractor_seekTo;
     AMediaExtractor_selectTrack;
     AMediaExtractor_setDataSource;
+    AMediaExtractor_setDataSourceCustom; # introduced=28
     AMediaExtractor_setDataSourceFd;
     AMediaExtractor_unselectTrack;
     AMediaFormat_delete;
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 4c312f8..f3aaec8 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -35,6 +35,7 @@
 import android.media.SubtitleController;
 import android.media.TtmlRenderer;
 import android.media.WebVttRenderer;
+import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.media.update.VideoView2Provider;
@@ -95,6 +96,7 @@
     private MediaPlayer mMediaPlayer;
     private MediaControlView2 mMediaControlView;
     private MediaSession mMediaSession;
+    private MediaController mMediaController;
     private Metadata mMetadata;
     private String mTitle;
 
@@ -102,7 +104,7 @@
     private int mTargetState = STATE_IDLE;
     private int mCurrentState = STATE_IDLE;
     private int mCurrentBufferPercentage;
-    private int mSeekWhenPrepared;  // recording the seek position while preparing
+    private long mSeekWhenPrepared;  // recording the seek position while preparing
 
     private int mVideoWidth;
     private int mVideoHeight;
@@ -179,79 +181,16 @@
     }
 
     @Override
+    public MediaController getMediaController_impl() {
+        return mMediaController;
+    }
+
+    @Override
     public MediaControlView2 getMediaControlView2_impl() {
         return mMediaControlView;
     }
 
     @Override
-    public void start_impl() {
-        if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
-            applySpeed();
-            mMediaPlayer.start();
-            mCurrentState = STATE_PLAYING;
-            updatePlaybackState();
-        }
-        mTargetState = STATE_PLAYING;
-        if (DEBUG) {
-            Log.d(TAG, "start(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState);
-        }
-    }
-
-    @Override
-    public void pause_impl() {
-        if (isInPlaybackState()) {
-            if (mMediaPlayer.isPlaying()) {
-                mMediaPlayer.pause();
-                mCurrentState = STATE_PAUSED;
-                updatePlaybackState();
-            }
-        }
-        mTargetState = STATE_PAUSED;
-        if (DEBUG) {
-            Log.d(TAG, "pause(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState);
-        }
-    }
-
-    @Override
-    public int getDuration_impl() {
-        if (isInPlaybackState()) {
-            return mMediaPlayer.getDuration();
-        }
-        return -1;
-    }
-
-    @Override
-    public int getCurrentPosition_impl() {
-        if (isInPlaybackState()) {
-            return mMediaPlayer.getCurrentPosition();
-        }
-        return 0;
-    }
-
-    @Override
-    public void seekTo_impl(int msec) {
-        if (isInPlaybackState()) {
-            mMediaPlayer.seekTo(msec);
-            mSeekWhenPrepared = 0;
-            updatePlaybackState();
-        } else {
-            mSeekWhenPrepared = msec;
-        }
-    }
-
-    @Override
-    public boolean isPlaying_impl() {
-        return (isInPlaybackState()) && mMediaPlayer.isPlaying();
-    }
-
-    @Override
-    public int getBufferPercentage_impl() {
-        return mCurrentBufferPercentage;
-    }
-
-    @Override
     public int getAudioSessionId_impl() {
         if (mAudioSession == 0) {
             MediaPlayer foo = new MediaPlayer();
@@ -296,6 +235,7 @@
         }
     }
 
+    // TODO: remove setSpeed_impl once MediaController2 is ready.
     @Override
     public void setSpeed_impl(float speed) {
         if (speed <= 0.0f) {
@@ -306,20 +246,7 @@
         if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
             applySpeed();
         }
-    }
-
-    @Override
-    public float getSpeed_impl() {
-        if (DEBUG) {
-            if (mMediaPlayer != null) {
-                float speed = mMediaPlayer.getPlaybackParams().getSpeed();
-                if (speed != mSpeed) {
-                    Log.w(TAG, "VideoView2's speed : " + mSpeed + " is different from "
-                            + "MediaPlayer's speed : " + speed);
-                }
-            }
-        }
-        return mSpeed;
+        updatePlaybackState();
     }
 
     @Override
@@ -389,11 +316,6 @@
     }
 
     @Override
-    public void stopPlayback_impl() {
-        resetPlayer();
-    }
-
-    @Override
     public void setOnPreparedListener_impl(VideoView2.OnPreparedListener l) {
         mOnPreparedListener = l;
     }
@@ -430,6 +352,7 @@
         // Create MediaSession
         mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
         mMediaSession.setCallback(new MediaSessionCallback());
+        mMediaController = mMediaSession.getController();
 
         attachMediaControlView();
     }
@@ -482,23 +405,23 @@
             if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
                     || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
                 if (mMediaPlayer.isPlaying()) {
-                    mInstance.pause();
+                    mMediaController.getTransportControls().pause();
                     mMediaControlView.show();
                 } else {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                     mMediaControlView.hide();
                 }
                 return true;
             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
                 if (!mMediaPlayer.isPlaying()) {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                     mMediaControlView.hide();
                 }
                 return true;
             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
                     || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
                 if (mMediaPlayer.isPlaying()) {
-                    mInstance.pause();
+                    mMediaController.getTransportControls().pause();
                     mMediaControlView.show();
                 }
                 return true;
@@ -537,7 +460,7 @@
                     + ", " + view.toString());
         }
         if (needToStart()) {
-            mInstance.start();
+            mMediaController.getTransportControls().play();
         }
     }
 
@@ -571,7 +494,7 @@
             mOnViewTypeChangedListener.onViewTypeChanged(view.getViewType());
         }
         if (needToStart()) {
-            mInstance.start();
+            mMediaController.getTransportControls().play();
         }
     }
 
@@ -738,9 +661,9 @@
             mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, -1);
         }
         mStateBuilder.setState(getCorrespondingPlaybackState(),
-                mInstance.getCurrentPosition(), 1.0f);
+                mMediaPlayer.getCurrentPosition(), mSpeed);
         mStateBuilder.setBufferedPosition(
-                (long) (mCurrentBufferPercentage / 100.0) * mInstance.getDuration());
+                (long) (mCurrentBufferPercentage / 100.0) * mMediaPlayer.getDuration());
 
         // Set PlaybackState for MediaSession
         if (mMediaSession != null) {
@@ -837,9 +760,9 @@
             int videoHeight = mp.getVideoHeight();
 
             // mSeekWhenPrepared may be changed after seekTo() call
-            int seekToPosition = mSeekWhenPrepared;
+            long seekToPosition = mSeekWhenPrepared;
             if (seekToPosition != 0) {
-                mInstance.seekTo(seekToPosition);
+                mMediaController.getTransportControls().seekTo(seekToPosition);
             }
 
             if (videoWidth != 0 && videoHeight != 0) {
@@ -859,12 +782,12 @@
                 }
 
                 if (needToStart()) {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                     if (mMediaControlView != null) {
                         mMediaControlView.show();
                     }
-                } else if (!mInstance.isPlaying() && (seekToPosition != 0
-                        || mInstance.getCurrentPosition() > 0)) {
+                } else if (!(isInPlaybackState() && mMediaPlayer.isPlaying())
+                        && (seekToPosition != 0 || mMediaPlayer.getCurrentPosition() > 0)) {
                     if (mMediaControlView != null) {
                         // Show the media controls when we're paused into a video and
                         // make them stick.
@@ -875,7 +798,7 @@
                 // We don't know the video size yet, but should start anyway.
                 // The video size might be reported to us later.
                 if (needToStart()) {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                 }
             }
             // Create and set playback state for MediaControlView2
@@ -887,7 +810,7 @@
                 mTitle = mMetadata.getString(Metadata.TITLE);
             }
             builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
-            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mInstance.getDuration());
+            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
 
             if (mMediaSession != null) {
                 mMediaSession.setMetadata(builder.build());
@@ -1006,17 +929,49 @@
 
         @Override
         public void onPlay() {
-            mInstance.start();
+            if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+                applySpeed();
+                mMediaPlayer.start();
+                mCurrentState = STATE_PLAYING;
+                updatePlaybackState();
+            }
+            mTargetState = STATE_PLAYING;
+            if (DEBUG) {
+                Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
+                        + ", mTargetState=" + mTargetState);
+            }
         }
 
         @Override
         public void onPause() {
-            mInstance.pause();
+            if (isInPlaybackState()) {
+                if (mMediaPlayer.isPlaying()) {
+                    mMediaPlayer.pause();
+                    mCurrentState = STATE_PAUSED;
+                    updatePlaybackState();
+                }
+            }
+            mTargetState = STATE_PAUSED;
+            if (DEBUG) {
+                Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
+                        + ", mTargetState=" + mTargetState);
+            }
         }
 
         @Override
         public void onSeekTo(long pos) {
-            mInstance.seekTo((int) pos);
+            if (isInPlaybackState()) {
+                mMediaPlayer.seekTo(pos, MediaPlayer.SEEK_PREVIOUS_SYNC);
+                mSeekWhenPrepared = 0;
+                updatePlaybackState();
+            } else {
+                mSeekWhenPrepared = pos;
+            }
+        }
+
+        @Override
+        public void onStop() {
+            resetPlayer();
         }
     }
 }