Allow MediaExtractor to create FileSource

Remove binder call to MediaServer for reading files.

Test: Photos, Play Music, Play Movies, Youtube
Bug: 29125703
Change-Id: Id6abf592bf6d70b81158f6038b1982f0a537b9b9
diff --git a/media/libmedia/IMediaExtractorService.cpp b/media/libmedia/IMediaExtractorService.cpp
index 8b00d85..7c0d08d 100644
--- a/media/libmedia/IMediaExtractorService.cpp
+++ b/media/libmedia/IMediaExtractorService.cpp
@@ -23,11 +23,13 @@
 #include <sys/types.h>
 #include <binder/Parcel.h>
 #include <media/IMediaExtractorService.h>
+#include <media/stagefright/MediaExtractor.h>
 
 namespace android {
 
 enum {
-    MAKE_EXTRACTOR = IBinder::FIRST_CALL_TRANSACTION
+    MAKE_EXTRACTOR = IBinder::FIRST_CALL_TRANSACTION,
+    MAKE_IDATA_SOURCE_FD,
 };
 
 class BpMediaExtractorService : public BpInterface<IMediaExtractorService>
@@ -52,6 +54,21 @@
         return NULL;
     }
 
+    virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaExtractorService::getInterfaceDescriptor());
+        data.writeFileDescriptor(fd);
+        data.writeInt64(offset);
+        data.writeInt64(length);
+        status_t ret = remote()->transact(MAKE_IDATA_SOURCE_FD, data, &reply);
+        ALOGV("fd:%d offset:%lld length:%lld ret:%d",
+                fd, (long long)offset, (long long)length, ret);
+        if (ret == NO_ERROR) {
+            return interface_cast<IDataSource>(reply.readStrongBinder());
+        }
+        return nullptr;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(MediaExtractorService, "android.media.IMediaExtractorService");
@@ -80,6 +97,23 @@
             reply->writeStrongBinder(IInterface::asBinder(ex));
             return NO_ERROR;
         }
+
+        case MAKE_IDATA_SOURCE_FD: {
+            CHECK_INTERFACE(IMediaExtractorService, data, reply);
+            const int fd = dup(data.readFileDescriptor()); // -1 fd checked in makeIDataSource
+            const int64_t offset = data.readInt64();
+            const int64_t length = data.readInt64();
+            ALOGV("fd %d  offset%lld  length:%lld", fd, (long long)offset, (long long)length);
+            sp<IDataSource> source = makeIDataSource(fd, offset, length);
+            reply->writeStrongBinder(IInterface::asBinder(source));
+            // The FileSource closes the descriptor, so if it is not created
+            // we need to close the descriptor explicitly.
+            if (source.get() == nullptr && fd != -1) {
+                close(fd);
+            }
+            return NO_ERROR;
+        }
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/include/IMediaExtractorService.h b/media/libmedia/include/IMediaExtractorService.h
index 4d7b317..45e9620 100644
--- a/media/libmedia/include/IMediaExtractorService.h
+++ b/media/libmedia/include/IMediaExtractorService.h
@@ -32,6 +32,7 @@
 
     virtual sp<IMediaExtractor> makeExtractor(const sp<IDataSource> &source, const char *mime) = 0;
 
+    virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length) = 0;
 };
 
 class BnMediaExtractorService: public BnInterface<IMediaExtractorService>
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 8378d24..aedca3f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -21,7 +21,9 @@
 #include "NuPlayerDrm.h"
 
 #include "AnotherPacketSource.h"
-
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <media/IMediaExtractorService.h>
 #include <media/IMediaHTTPService.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -361,7 +363,32 @@
                    mHTTPService, uri, &mUriHeaders, &contentType,
                    static_cast<HTTPBase *>(mHttpSource.get()));
         } else {
-            mDataSource = new FileSource(mFd, mOffset, mLength);
+            if (property_get_bool("media.stagefright.extractremote", true) &&
+                    !FileSource::requiresDrm(mFd, mOffset, mLength, nullptr /* mime */)) {
+                sp<IBinder> binder =
+                        defaultServiceManager()->getService(String16("media.extractor"));
+                if (binder != nullptr) {
+                    ALOGD("FileSource remote");
+                    sp<IMediaExtractorService> mediaExService(
+                            interface_cast<IMediaExtractorService>(binder));
+                    sp<IDataSource> source =
+                            mediaExService->makeIDataSource(mFd, mOffset, mLength);
+                    ALOGV("IDataSource(FileSource): %p %d %lld %lld",
+                            source.get(), mFd, (long long)mOffset, (long long)mLength);
+                    if (source.get() != nullptr) {
+                        mDataSource = DataSource::CreateFromIDataSource(source);
+                    } else {
+                        ALOGW("extractor service cannot make data source");
+                    }
+                } else {
+                    ALOGW("extractor service not running");
+                }
+            }
+            if (mDataSource == nullptr) {
+                ALOGD("FileSource local");
+                mDataSource = new FileSource(mFd, mOffset, mLength);
+            }
+
             mFd = -1;
         }
 
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index 0434bab..4309372 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -113,6 +113,10 @@
     return mIDataSource->DrmInitialization(mime);
 }
 
+sp<IDataSource> CallbackDataSource::getIDataSource() const {
+    return mIDataSource;
+}
+
 TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
     : mSource(source), mCachedOffset(0), mCachedSize(0) {
     mName = String8::format("TinyCacheSource(%s)", mSource->toString().string());
@@ -190,4 +194,9 @@
     mCachedSize = 0;
     return mSource->DrmInitialization(mime);
 }
+
+sp<IDataSource> TinyCacheSource::getIDataSource() const {
+    return mSource->getIDataSource();
+}
+
 } // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index ded79f3..a5760d1 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -20,6 +20,7 @@
 #include "include/HTTPBase.h"
 #include "include/NuCachedSource2.h"
 
+#include <media/IDataSource.h>
 #include <media/IMediaHTTPConnection.h>
 #include <media/IMediaHTTPService.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -29,6 +30,7 @@
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/RemoteDataSource.h>
 #include <media/stagefright/Utils.h>
 #include <utils/String8.h>
 
@@ -36,8 +38,6 @@
 
 #include <private/android_filesystem_config.h>
 
-#include <arpa/inet.h>
-
 namespace android {
 
 bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
@@ -98,6 +98,10 @@
     return ERROR_UNSUPPORTED;
 }
 
+sp<IDataSource> DataSource::getIDataSource() const {
+    return nullptr;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 // static
@@ -167,6 +171,11 @@
     return source;
 }
 
+sp<DataSource> DataSource::CreateFromFd(int fd, int64_t offset, int64_t length) {
+    sp<FileSource> source = new FileSource(fd, offset, length);
+    return source->initCheck() != OK ? nullptr : source;
+}
+
 sp<DataSource> DataSource::CreateMediaHTTP(const sp<IMediaHTTPService> &httpService) {
     if (httpService == NULL) {
         return NULL;
@@ -188,4 +197,8 @@
     return String8("application/octet-stream");
 }
 
+sp<IDataSource> DataSource::asIDataSource() {
+    return RemoteDataSource::wrap(sp<DataSource>(this));
+}
+
 }  // namespace android
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index 5b92f91..97d8988 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -21,6 +21,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/Utils.h>
+#include <private/android_filesystem_config.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -171,6 +172,7 @@
 }
 
 sp<DecryptHandle> FileSource::DrmInitialization(const char *mime) {
+    if (getuid() == AID_MEDIA_EX) return nullptr; // no DRM in media extractor
     if (mDrmManagerClient == NULL) {
         mDrmManagerClient = new DrmManagerClient();
     }
@@ -227,4 +229,18 @@
         return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset);
     }
 }
+
+/* static */
+bool FileSource::requiresDrm(int fd, int64_t offset, int64_t length, const char *mime) {
+    std::unique_ptr<DrmManagerClient> drmClient(new DrmManagerClient());
+    sp<DecryptHandle> decryptHandle =
+            drmClient->openDecryptSession(fd, offset, length, mime);
+    bool requiresDrm = false;
+    if (decryptHandle != nullptr) {
+        requiresDrm = decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED;
+        drmClient->closeDecryptSession(decryptHandle);
+    }
+    return requiresDrm;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 76775c2..9965462 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -117,74 +117,6 @@
     return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK;
 }
 
-
-
-class RemoteDataSource : public BnDataSource {
-public:
-    enum {
-        kBufferSize = 64 * 1024,
-    };
-
-    static sp<IDataSource> wrap(const sp<DataSource> &source);
-    virtual ~RemoteDataSource();
-
-    virtual sp<IMemory> getIMemory();
-    virtual ssize_t readAt(off64_t offset, size_t size);
-    virtual status_t getSize(off64_t* size);
-    virtual void close();
-    virtual uint32_t getFlags();
-    virtual String8 toString();
-    virtual sp<DecryptHandle> DrmInitialization(const char *mime);
-
-private:
-    sp<IMemory> mMemory;
-    sp<DataSource> mSource;
-    String8 mName;
-    explicit RemoteDataSource(const sp<DataSource> &source);
-    DISALLOW_EVIL_CONSTRUCTORS(RemoteDataSource);
-};
-
-
-sp<IDataSource> RemoteDataSource::wrap(const sp<DataSource> &source) {
-    return new RemoteDataSource(source);
-}
-RemoteDataSource::RemoteDataSource(const sp<DataSource> &source) {
-    mSource = source;
-    sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "RemoteDataSource");
-    mMemory = memoryDealer->allocate(kBufferSize);
-    if (mMemory == NULL) {
-        ALOGE("Failed to allocate memory!");
-    }
-    mName = String8::format("RemoteDataSource(%s)", mSource->toString().string());
-}
-RemoteDataSource::~RemoteDataSource() {
-    close();
-}
-sp<IMemory> RemoteDataSource::getIMemory() {
-    return mMemory;
-}
-ssize_t RemoteDataSource::readAt(off64_t offset, size_t size) {
-    ALOGV("readAt(%" PRId64 ", %zu)", offset, size);
-    return mSource->readAt(offset, mMemory->pointer(), size);
-}
-status_t RemoteDataSource::getSize(off64_t* size) {
-    return mSource->getSize(size);
-}
-void RemoteDataSource::close() {
-    mSource = NULL;
-}
-uint32_t RemoteDataSource::getFlags() {
-    return mSource->flags();
-}
-
-String8 RemoteDataSource::toString() {
-    return mName;
-}
-
-sp<DecryptHandle> RemoteDataSource::DrmInitialization(const char *mime) {
-    return mSource->DrmInitialization(mime);
-}
-
 // static
 sp<IMediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
@@ -201,7 +133,7 @@
 
         if (binder != 0) {
             sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
-            sp<IMediaExtractor> ex = mediaExService->makeExtractor(RemoteDataSource::wrap(source), mime);
+            sp<IMediaExtractor> ex = mediaExService->makeExtractor(source->asIDataSource(), mime);
             return ex;
         } else {
             ALOGE("extractor service not running");
@@ -218,7 +150,7 @@
     RegisterDefaultSniffers();
 
     // initialize source decryption if needed
-    source->DrmInitialization();
+    source->DrmInitialization(nullptr /* mime */);
 
     sp<AMessage> meta;
 
diff --git a/media/libstagefright/include/CallbackDataSource.h b/media/libstagefright/include/CallbackDataSource.h
index 80cd1f7..0d775e6 100644
--- a/media/libstagefright/include/CallbackDataSource.h
+++ b/media/libstagefright/include/CallbackDataSource.h
@@ -42,6 +42,7 @@
         return mName;
     }
     virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
+    virtual sp<IDataSource> getIDataSource() const;
 
 private:
     sp<IDataSource> mIDataSource;
@@ -70,6 +71,7 @@
         return mName;
     }
     virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
+    virtual sp<IDataSource> getIDataSource() const;
 
 private:
     // 2kb comes from experimenting with the time-to-first-frame from a MediaPlayer
diff --git a/media/libstagefright/include/DataSource.h b/media/libstagefright/include/DataSource.h
index e7135a2..8f2c7eb 100644
--- a/media/libstagefright/include/DataSource.h
+++ b/media/libstagefright/include/DataSource.h
@@ -56,6 +56,7 @@
 
     static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
     static sp<DataSource> CreateFromIDataSource(const sp<IDataSource> &source);
+    static sp<DataSource> CreateFromFd(int fd, int64_t offset, int64_t length);
 
     DataSource() {}
 
@@ -117,6 +118,12 @@
 
     virtual void close() {};
 
+    // creates an IDataSource wrapper to the DataSource.
+    virtual sp<IDataSource> asIDataSource();
+
+    // returns a pointer to IDataSource if it is wrapped.
+    virtual sp<IDataSource> getIDataSource() const;
+
 protected:
     virtual ~DataSource() {}
 
diff --git a/media/libstagefright/include/FileSource.h b/media/libstagefright/include/FileSource.h
index 9f3bb5e..7267e9a 100644
--- a/media/libstagefright/include/FileSource.h
+++ b/media/libstagefright/include/FileSource.h
@@ -51,6 +51,8 @@
         return mName;
     }
 
+    static bool requiresDrm(int fd, int64_t offset, int64_t length, const char *mime);
+
 protected:
     virtual ~FileSource();
 
diff --git a/media/libstagefright/include/RemoteDataSource.h b/media/libstagefright/include/RemoteDataSource.h
new file mode 100644
index 0000000..255b7ae
--- /dev/null
+++ b/media/libstagefright/include/RemoteDataSource.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+#ifndef REMOTE_DATA_SOURCE_H_
+#define REMOTE_DATA_SOURCE_H_
+
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
+#include <media/IDataSource.h>
+#include <media/stagefright/DataSource.h>
+
+namespace android {
+
+// Originally in MediaExtractor.cpp
+class RemoteDataSource : public BnDataSource {
+public:
+    static sp<IDataSource> wrap(const sp<DataSource> &source) {
+        if (source.get() == nullptr) {
+            return nullptr;
+        }
+        if (source->getIDataSource().get() != nullptr) {
+            return source->getIDataSource();
+        }
+        return new RemoteDataSource(source);
+    }
+
+    virtual ~RemoteDataSource() {
+        close();
+    }
+    virtual sp<IMemory> getIMemory() {
+        return mMemory;
+    }
+    virtual ssize_t readAt(off64_t offset, size_t size) {
+        ALOGV("readAt(%lld, %zu)", (long long)offset, size);
+        if (size > kBufferSize) {
+            size = kBufferSize;
+        }
+        return mSource->readAt(offset, mMemory->pointer(), size);
+    }
+    virtual status_t getSize(off64_t *size) {
+        return mSource->getSize(size);
+    }
+    virtual void close() {
+        mSource = nullptr;
+        mMemory = nullptr;
+    }
+    virtual uint32_t getFlags() {
+        return mSource->flags();
+    }
+    virtual String8 toString()  {
+        return mName;
+    }
+    virtual sp<DecryptHandle> DrmInitialization(const char *mime) {
+        return mSource->DrmInitialization(mime);
+    }
+
+private:
+    enum {
+        kBufferSize = 64 * 1024,
+    };
+
+    sp<IMemory> mMemory;
+    sp<DataSource> mSource;
+    String8 mName;
+
+    explicit RemoteDataSource(const sp<DataSource> &source) {
+        mSource = source;
+        sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "RemoteDataSource");
+        mMemory = memoryDealer->allocate(kBufferSize);
+        if (mMemory.get() == nullptr) {
+            ALOGE("Failed to allocate memory!");
+        }
+        mName = String8::format("RemoteDataSource(%s)", mSource->toString().string());
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(RemoteDataSource);
+};
+
+}  // namespace android
+
+#endif  // REMOTE_DATA_SOURCE_H_
diff --git a/services/mediaextractor/MediaExtractorService.cpp b/services/mediaextractor/MediaExtractorService.cpp
index 4a80166..08cbef6 100644
--- a/services/mediaextractor/MediaExtractorService.cpp
+++ b/services/mediaextractor/MediaExtractorService.cpp
@@ -22,6 +22,7 @@
 
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/RemoteDataSource.h>
 #include "MediaExtractorService.h"
 
 namespace android {
@@ -45,6 +46,12 @@
     return ret;
 }
 
+sp<IDataSource> MediaExtractorService::makeIDataSource(int fd, int64_t offset, int64_t length)
+{
+    sp<DataSource> source = DataSource::CreateFromFd(fd, offset, length);
+    return source.get() != nullptr ? source->asIDataSource() : nullptr;
+}
+
 status_t MediaExtractorService::dump(int fd, const Vector<String16>& args) {
     return dumpExtractors(fd, args);
 }
diff --git a/services/mediaextractor/MediaExtractorService.h b/services/mediaextractor/MediaExtractorService.h
index 078af0c..9df3ecd 100644
--- a/services/mediaextractor/MediaExtractorService.h
+++ b/services/mediaextractor/MediaExtractorService.h
@@ -34,6 +34,9 @@
     static const char*  getServiceName() { return "media.extractor"; }
 
     virtual sp<IMediaExtractor> makeExtractor(const sp<IDataSource> &source, const char *mime);
+
+    virtual sp<IDataSource> makeIDataSource(int fd, int64_t offset, int64_t length);
+
     virtual status_t    dump(int fd, const Vector<String16>& args);
 
     virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 96840a0..4e4ce30 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -37,6 +37,11 @@
 getgid32: 1
 getegid32: 1
 getgroups32: 1
+nanosleep: 1
+
+# for FileSource
+readlinkat: 1
+_llseek: 1
 
 # for attaching to debuggerd on process crash
 sigaction: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index c95ddb7..1683adb 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -26,6 +26,11 @@
 exit_group: 1
 rt_sigreturn: 1
 getrlimit: 1
+nanosleep: 1
+
+# for FileSource
+readlinkat: 1
+_llseek: 1
 
 # for attaching to debuggerd on process crash
 tgkill: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 2ce6cf8..83725cd 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -37,6 +37,10 @@
 getgroups32: 1
 nanosleep: 1
 
+# for FileSource
+readlinkat: 1
+_llseek: 1
+
 # for attaching to debuggerd on process crash
 socketcall: 1
 sigaction: 1