From d2506a506303ed94fd1991cf986b825b870a67c5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 29 Jan 2014 10:32:46 -0800 Subject: [PATCH] FINAL ATTEMPT: HTTP services are now provided from JAVA and made available to media code Change-Id: I7f6cdcfd2a28846d36d89dd5180ef20a22b03af8 --- Android.mk | 2 + .../android/media/IMediaHTTPConnection.aidl | 33 +++ .../java/android/media/IMediaHTTPService.aidl | 27 ++ media/java/android/media/MediaExtractor.java | 22 +- .../android/media/MediaHTTPConnection.java | 263 ++++++++++++++++++ .../java/android/media/MediaHTTPService.java | 44 +++ .../android/media/MediaMetadataRetriever.java | 10 +- media/java/android/media/MediaPlayer.java | 13 +- media/jni/Android.mk | 1 + media/jni/android_media_MediaExtractor.cpp | 27 +- media/jni/android_media_MediaExtractor.h | 2 + .../jni/android_media_MediaHTTPConnection.cpp | 179 ++++++++++++ media/jni/android_media_MediaHTTPConnection.h | 57 ++++ .../android_media_MediaMetadataRetriever.cpp | 17 +- media/jni/android_media_MediaPlayer.cpp | 27 +- .../players/invoke_mock_media_player.cpp | 2 + 16 files changed, 705 insertions(+), 21 deletions(-) create mode 100644 media/java/android/media/IMediaHTTPConnection.aidl create mode 100644 media/java/android/media/IMediaHTTPService.aidl create mode 100644 media/java/android/media/MediaHTTPConnection.java create mode 100644 media/java/android/media/MediaHTTPService.java create mode 100644 media/jni/android_media_MediaHTTPConnection.cpp create mode 100644 media/jni/android_media_MediaHTTPConnection.h diff --git a/Android.mk b/Android.mk index 781d5d1c8bd09..abf83a3685e13 100644 --- a/Android.mk +++ b/Android.mk @@ -258,6 +258,8 @@ LOCAL_SRC_FILES += \ media/java/android/media/IAudioService.aidl \ media/java/android/media/IAudioFocusDispatcher.aidl \ media/java/android/media/IAudioRoutesObserver.aidl \ + media/java/android/media/IMediaHTTPConnection.aidl \ + media/java/android/media/IMediaHTTPService.aidl \ media/java/android/media/IMediaRouterClient.aidl \ media/java/android/media/IMediaRouterService.aidl \ media/java/android/media/IMediaScannerListener.aidl \ diff --git a/media/java/android/media/IMediaHTTPConnection.aidl b/media/java/android/media/IMediaHTTPConnection.aidl new file mode 100644 index 0000000000000..300211b6f1385 --- /dev/null +++ b/media/java/android/media/IMediaHTTPConnection.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 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. + */ + +package android.media; + +import android.os.IBinder; + +/** MUST STAY IN SYNC WITH NATIVE CODE at libmedia/IMediaHTTPConnection.{cpp,h} */ + +/** @hide */ +interface IMediaHTTPConnection +{ + IBinder connect(in String uri, in String headers); + void disconnect(); + + int readAt(long offset, int size); + long getSize(); + String getMIMEType(); +} + diff --git a/media/java/android/media/IMediaHTTPService.aidl b/media/java/android/media/IMediaHTTPService.aidl new file mode 100644 index 0000000000000..8aaf6b3766b35 --- /dev/null +++ b/media/java/android/media/IMediaHTTPService.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 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. + */ + +package android.media; + +import android.media.IMediaHTTPConnection; + +/** MUST STAY IN SYNC WITH NATIVE CODE at libmedia/IMediaHTTPService.{cpp,h} */ + +/** @hide */ +interface IMediaHTTPService +{ + IMediaHTTPConnection makeHTTPConnection(); +} diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index c3e5035cfe535..f2753ee419a8a 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -21,7 +21,9 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.MediaCodec; import android.media.MediaFormat; +import android.media.MediaHTTPService; import android.net.Uri; +import android.os.IBinder; import java.io.FileDescriptor; import java.io.IOException; @@ -137,11 +139,19 @@ final public class MediaExtractor { ++i; } } - setDataSource(path, keys, values); + + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + keys, + values); } - private native final void setDataSource( - String path, String[] keys, String[] values) throws IOException; + private native final void nativeSetDataSource( + IBinder httpServiceBinder, + String path, + String[] keys, + String[] values) throws IOException; /** * Sets the data source (file-path or http URL) to use. @@ -156,7 +166,11 @@ final public class MediaExtractor { * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. */ public final void setDataSource(String path) throws IOException { - setDataSource(path, null, null); + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + null, + null); } /** diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java new file mode 100644 index 0000000000000..2732fbc4a95fb --- /dev/null +++ b/media/java/android/media/MediaHTTPConnection.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2013 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. + */ + +package android.media; + +import android.net.Uri; +import android.os.IBinder; +import android.os.StrictMode; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.URL; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; + +/** @hide */ +public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { + private static final String TAG = "MediaHTTPConnection"; + private static final boolean VERBOSE = false; + + private long mCurrentOffset = -1; + private URL mURL = null; + private Map mHeaders = null; + private HttpURLConnection mConnection = null; + private long mTotalSize = -1; + private InputStream mInputStream = null; + + public MediaHTTPConnection() { + if (CookieHandler.getDefault() == null) { + CookieHandler.setDefault(new CookieManager()); + } + + native_setup(); + } + + public IBinder connect(String uri, String headers) { + if (VERBOSE) { + Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers); + } + + try { + disconnect(); + mURL = new URL(uri); + mHeaders = convertHeaderStringToMap(headers); + } catch (MalformedURLException e) { + return null; + } + + return native_getIMemory(); + } + + private Map convertHeaderStringToMap(String headers) { + HashMap map = new HashMap(); + + String[] pairs = headers.split("\r\n"); + for (String pair : pairs) { + int colonPos = pair.indexOf(":"); + if (colonPos >= 0) { + String key = pair.substring(0, colonPos); + String val = pair.substring(colonPos + 1); + + map.put(key, val); + } + } + + return map; + } + + public void disconnect() { + teardownConnection(); + mHeaders = null; + mURL = null; + } + + private void teardownConnection() { + if (mConnection != null) { + mInputStream = null; + + mConnection.disconnect(); + mConnection = null; + + mCurrentOffset = -1; + } + } + + private void seekTo(long offset) throws IOException { + teardownConnection(); + + try { + mConnection = (HttpURLConnection)mURL.openConnection(); + + if (mHeaders != null) { + for (Map.Entry entry : mHeaders.entrySet()) { + mConnection.setRequestProperty( + entry.getKey(), entry.getValue()); + } + } + + if (offset > 0) { + mConnection.setRequestProperty( + "Range", "bytes=" + offset + "-"); + } + + if (mConnection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) { + // Partial content, we cannot just use getContentLength + // because what we want is not just the length of the range + // returned but the size of the full content if available. + + String contentRange = + mConnection.getHeaderField("Content-Range"); + + mTotalSize = -1; + if (contentRange != null) { + // format is "bytes xxx-yyy/zzz + // where "zzz" is the total number of bytes of the + // content or '*' if unknown. + + int lastSlashPos = contentRange.lastIndexOf('/'); + if (lastSlashPos >= 0) { + String total = + contentRange.substring(lastSlashPos + 1); + + try { + mTotalSize = Long.parseLong(total); + } catch (NumberFormatException e) { + } + } + } + } else if (mConnection.getResponseCode() + != HttpURLConnection.HTTP_OK) { + throw new IOException(); + } else { + mTotalSize = mConnection.getContentLength(); + } + + if (offset > 0 + && mConnection.getResponseCode() + != HttpURLConnection.HTTP_PARTIAL) { + // Some servers simply ignore "Range" requests and serve + // data from the start of the content. + throw new IOException(); + } + + mInputStream = + new BufferedInputStream(mConnection.getInputStream()); + + mCurrentOffset = offset; + } catch (IOException e) { + mTotalSize = -1; + mInputStream = null; + mConnection = null; + mCurrentOffset = -1; + + throw e; + } + } + + public int readAt(long offset, int size) { + return native_readAt(offset, size); + } + + private int readAt(long offset, byte[] data, int size) { + StrictMode.ThreadPolicy policy = + new StrictMode.ThreadPolicy.Builder().permitAll().build(); + + StrictMode.setThreadPolicy(policy); + + try { + if (offset != mCurrentOffset) { + seekTo(offset); + } + + int n = mInputStream.read(data, 0, size); + + if (n == -1) { + // InputStream signals EOS using a -1 result, our semantics + // are to return a 0-length read. + n = 0; + } + + mCurrentOffset += n; + + if (VERBOSE) { + Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); + } + + return n; + } catch (IOException e) { + if (VERBOSE) { + Log.d(TAG, "readAt " + offset + " / " + size + " => -1"); + } + return -1; + } catch (Exception e) { + if (VERBOSE) { + Log.d(TAG, "unknown exception " + e); + Log.d(TAG, "readAt " + offset + " / " + size + " => -1"); + } + return -1; + } + } + + public long getSize() { + if (mConnection == null) { + try { + seekTo(0); + } catch (IOException e) { + return -1; + } + } + + return mTotalSize; + } + + public String getMIMEType() { + if (mConnection == null) { + try { + seekTo(0); + } catch (IOException e) { + return "application/octet-stream"; + } + } + + return mConnection.getContentType(); + } + + @Override + protected void finalize() { + native_finalize(); + } + + private static native final void native_init(); + private native final void native_setup(); + private native final void native_finalize(); + + private native final IBinder native_getIMemory(); + private native final int native_readAt(long offset, int size); + + static { + System.loadLibrary("media_jni"); + native_init(); + } + + private int mNativeContext; +} diff --git a/media/java/android/media/MediaHTTPService.java b/media/java/android/media/MediaHTTPService.java new file mode 100644 index 0000000000000..3b4703dd52580 --- /dev/null +++ b/media/java/android/media/MediaHTTPService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 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. + */ + +package android.media; + +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +/** @hide */ +public class MediaHTTPService extends IMediaHTTPService.Stub { + private static final String TAG = "MediaHTTPService"; + + public MediaHTTPService() { + } + + public IMediaHTTPConnection makeHTTPConnection() { + return new MediaHTTPConnection(); + } + + /* package private */static IBinder createHttpServiceBinderIfNecessary( + String path) { + if (path.startsWith("http://") + || path.startsWith("https://") + || path.startsWith("widevine://")) { + return (new MediaHTTPService()).asBinder(); + } + + return null; + } +} diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index db27d09caa514..9a69c067e4982 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; +import android.os.IBinder; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -100,11 +101,16 @@ public class MediaMetadataRetriever values[i] = entry.getValue(); ++i; } - _setDataSource(uri, keys, values); + + _setDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(uri), + uri, + keys, + values); } private native void _setDataSource( - String uri, String[] keys, String[] values) + IBinder httpServiceBinder, String uri, String[] keys, String[] values) throws IllegalArgumentException; /** diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 3cceb037b5ba2..7c3c1977d56b1 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -27,6 +27,7 @@ import android.net.ProxyProperties; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -989,8 +990,18 @@ public class MediaPlayer implements SubtitleController.Listener } } - private native void _setDataSource( + private void _setDataSource( String path, String[] keys, String[] values) + throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + nativeSetDataSource( + MediaHTTPService.createHttpServiceBinderIfNecessary(path), + path, + keys, + values); + } + + private native void nativeSetDataSource( + IBinder httpServiceBinder, String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; /** diff --git a/media/jni/Android.mk b/media/jni/Android.mk index dea971e4cfbcc..51fccd4d1b231 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES:= \ android_media_MediaCodecList.cpp \ android_media_MediaDrm.cpp \ android_media_MediaExtractor.cpp \ + android_media_MediaHTTPConnection.cpp \ android_media_MediaMuxer.cpp \ android_media_MediaPlayer.cpp \ android_media_MediaRecorder.cpp \ diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index 705de88bba91a..d3a8b2256478a 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -26,6 +26,7 @@ #include "jni.h" #include "JNIHelp.h" +#include #include #include #include @@ -35,6 +36,8 @@ #include #include +#include "android_util_Binder.h" + namespace android { struct fields_t { @@ -135,8 +138,10 @@ JMediaExtractor::~JMediaExtractor() { } status_t JMediaExtractor::setDataSource( - const char *path, const KeyedVector *headers) { - return mImpl->setDataSource(path, headers); + const sp &httpService, + const char *path, + const KeyedVector *headers) { + return mImpl->setDataSource(httpService, path, headers); } status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { @@ -661,7 +666,10 @@ static void android_media_MediaExtractor_native_setup( static void android_media_MediaExtractor_setDataSource( JNIEnv *env, jobject thiz, - jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) { + jobject httpServiceBinderObj, + jstring pathObj, + jobjectArray keysArray, + jobjectArray valuesArray) { sp extractor = getMediaExtractor(env, thiz); if (extractor == NULL) { @@ -686,7 +694,13 @@ static void android_media_MediaExtractor_setDataSource( return; } - status_t err = extractor->setDataSource(path, &headers); + sp httpService; + if (httpServiceBinderObj != NULL) { + sp binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast(binder); + } + + status_t err = extractor->setDataSource(httpService, path, &headers); env->ReleaseStringUTFChars(pathObj, path); path = NULL; @@ -839,8 +853,9 @@ static JNINativeMethod gMethods[] = { { "native_finalize", "()V", (void *)android_media_MediaExtractor_native_finalize }, - { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;" - "[Ljava/lang/String;)V", + { "nativeSetDataSource", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;" + "[Ljava/lang/String;)V", (void *)android_media_MediaExtractor_setDataSource }, { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V", diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h index ccbad8c695dca..e5a0c16e3897a 100644 --- a/media/jni/android_media_MediaExtractor.h +++ b/media/jni/android_media_MediaExtractor.h @@ -29,6 +29,7 @@ namespace android { +struct IMediaHTTPService; struct MetaData; struct NuMediaExtractor; @@ -36,6 +37,7 @@ struct JMediaExtractor : public RefBase { JMediaExtractor(JNIEnv *env, jobject thiz); status_t setDataSource( + const sp &httpService, const char *path, const KeyedVector *headers); diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp new file mode 100644 index 0000000000000..c48af11248487 --- /dev/null +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2013, 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 "MediaHTTPConnection-JNI" +#include + +#include +#include +#include + +#include "android_media_MediaHTTPConnection.h" +#include "android_util_Binder.h" + +#include "android_runtime/AndroidRuntime.h" +#include "jni.h" +#include "JNIHelp.h" + +namespace android { + +JMediaHTTPConnection::JMediaHTTPConnection(JNIEnv *env, jobject thiz) + : mClass(NULL), + mObject(NULL), + mByteArrayObj(NULL) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); + + mDealer = new MemoryDealer(kBufferSize, "MediaHTTPConnection"); + mMemory = mDealer->allocate(kBufferSize); + + ScopedLocalRef tmp( + env, env->NewByteArray(JMediaHTTPConnection::kBufferSize)); + + mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get()); +} + +JMediaHTTPConnection::~JMediaHTTPConnection() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteGlobalRef(mByteArrayObj); + mByteArrayObj = NULL; + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +sp JMediaHTTPConnection::getIMemory() { + return mMemory; +} + +jbyteArray JMediaHTTPConnection::getByteArrayObj() { + return mByteArrayObj; +} + +} // namespace android + +using namespace android; + +struct fields_t { + jfieldID context; + + jmethodID readAtMethodID; +}; + +static fields_t gFields; + +static sp setObject( + JNIEnv *env, jobject thiz, const sp &conn) { + sp old = + (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context); + + if (conn != NULL) { + conn->incStrong(thiz); + } + if (old != NULL) { + old->decStrong(thiz); + } + env->SetIntField(thiz, gFields.context, (int)conn.get()); + + return old; +} + +static sp getObject(JNIEnv *env, jobject thiz) { + return (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context); +} + +static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { + ScopedLocalRef clazz( + env, env->FindClass("android/media/MediaHTTPConnection")); + CHECK(clazz.get() != NULL); + + gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I"); + CHECK(gFields.context != NULL); + + gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I"); +} + +static void android_media_MediaHTTPConnection_native_setup( + JNIEnv *env, jobject thiz) { + sp conn = new JMediaHTTPConnection(env, thiz); + + setObject(env, thiz, conn); +} + +static void android_media_MediaHTTPConnection_native_finalize( + JNIEnv *env, jobject thiz) { + setObject(env, thiz, NULL); +} + +static jobject android_media_MediaHTTPConnection_native_getIMemory( + JNIEnv *env, jobject thiz) { + sp conn = getObject(env, thiz); + + return javaObjectForIBinder(env, conn->getIMemory()->asBinder()); +} + +static jint android_media_MediaHTTPConnection_native_readAt( + JNIEnv *env, jobject thiz, jlong offset, jint size) { + sp conn = getObject(env, thiz); + + if (size > JMediaHTTPConnection::kBufferSize) { + size = JMediaHTTPConnection::kBufferSize; + } + + jbyteArray byteArrayObj = conn->getByteArrayObj(); + + jint n = env->CallIntMethod( + thiz, gFields.readAtMethodID, offset, byteArrayObj, size); + + if (n > 0) { + env->GetByteArrayRegion( + byteArrayObj, + 0, + n, + (jbyte *)conn->getIMemory()->pointer()); + } + + return n; +} + +static JNINativeMethod gMethods[] = { + { "native_getIMemory", "()Landroid/os/IBinder;", + (void *)android_media_MediaHTTPConnection_native_getIMemory }, + + { "native_readAt", "(JI)I", + (void *)android_media_MediaHTTPConnection_native_readAt }, + + { "native_init", "()V", + (void *)android_media_MediaHTTPConnection_native_init }, + + { "native_setup", "()V", + (void *)android_media_MediaHTTPConnection_native_setup }, + + { "native_finalize", "()V", + (void *)android_media_MediaHTTPConnection_native_finalize }, +}; + +int register_android_media_MediaHTTPConnection(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/media/MediaHTTPConnection", gMethods, NELEM(gMethods)); +} + diff --git a/media/jni/android_media_MediaHTTPConnection.h b/media/jni/android_media_MediaHTTPConnection.h new file mode 100644 index 0000000000000..62ff678208f66 --- /dev/null +++ b/media/jni/android_media_MediaHTTPConnection.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013, 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 _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ +#define _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ + +#include "jni.h" + +#include +#include + +namespace android { + +struct IMemory; +struct MemoryDealer; + +struct JMediaHTTPConnection : public RefBase { + enum { + kBufferSize = 32768, + }; + + JMediaHTTPConnection(JNIEnv *env, jobject thiz); + + sp getIMemory(); + + jbyteArray getByteArrayObj(); + +protected: + virtual ~JMediaHTTPConnection(); + +private: + jclass mClass; + jweak mObject; + jbyteArray mByteArrayObj; + + sp mDealer; + sp mMemory; + + DISALLOW_EVIL_CONSTRUCTORS(JMediaHTTPConnection); +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_MEDIAHTTPCONNECTION_H_ diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index a52b24d56f2ae..f17209f4cf5ac 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include "android_media_Utils.h" +#include "android_util_Binder.h" using namespace android; @@ -80,7 +82,7 @@ static void setRetriever(JNIEnv* env, jobject thiz, MediaMetadataRetriever* retr static void android_media_MediaMetadataRetriever_setDataSourceAndHeaders( - JNIEnv *env, jobject thiz, jstring path, + JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path, jobjectArray keys, jobjectArray values) { ALOGV("setDataSource"); @@ -122,10 +124,19 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( env, keys, values, &headersVector)) { return; } + + sp httpService; + if (httpServiceBinderObj != NULL) { + sp binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast(binder); + } + process_media_retriever_call( env, retriever->setDataSource( - pathStr.string(), headersVector.size() > 0 ? &headersVector : NULL), + httpService, + pathStr.string(), + headersVector.size() > 0 ? &headersVector : NULL), "java/lang/RuntimeException", "setDataSource failed"); @@ -442,7 +453,7 @@ static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobje static JNINativeMethod nativeMethods[] = { { "_setDataSource", - "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders }, diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 9d0d5a6eef78a..2af1c122777b8 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -20,6 +20,7 @@ #include "utils/Log.h" #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include +#include "android_util_Binder.h" // ---------------------------------------------------------------------------- using namespace android; @@ -183,7 +185,7 @@ static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStat static void android_media_MediaPlayer_setDataSourceAndHeaders( - JNIEnv *env, jobject thiz, jstring path, + JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path, jobjectArray keys, jobjectArray values) { sp mp = getMediaPlayer(env, thiz); @@ -214,8 +216,15 @@ android_media_MediaPlayer_setDataSourceAndHeaders( return; } + sp httpService; + if (httpServiceBinderObj != NULL) { + sp binder = ibinderForJavaObject(env, httpServiceBinderObj); + httpService = interface_cast(binder); + } + status_t opStatus = mp->setDataSource( + httpService, pathStr, headersVector.size() > 0? &headersVector : NULL); @@ -726,7 +735,8 @@ static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz } static jint -android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply) +android_media_MediaPlayer_pullBatteryData( + JNIEnv *env, jobject /* thiz */, jobject java_reply) { sp binder = defaultServiceManager()->getService(String16("media.player")); sp service = interface_cast(binder); @@ -856,8 +866,9 @@ android_media_MediaPlayer_updateProxyConfig( static JNINativeMethod gMethods[] = { { - "_setDataSource", - "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", + "nativeSetDataSource", + "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;" + "[Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSourceAndHeaders }, @@ -911,6 +922,7 @@ extern int register_android_media_Drm(JNIEnv *env); extern int register_android_media_MediaCodec(JNIEnv *env); extern int register_android_media_MediaExtractor(JNIEnv *env); extern int register_android_media_MediaCodecList(JNIEnv *env); +extern int register_android_media_MediaHTTPConnection(JNIEnv *env); extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); extern int register_android_media_MediaMuxer(JNIEnv *env); extern int register_android_media_MediaRecorder(JNIEnv *env); @@ -922,7 +934,7 @@ extern int register_android_mtp_MtpDatabase(JNIEnv *env); extern int register_android_mtp_MtpDevice(JNIEnv *env); extern int register_android_mtp_MtpServer(JNIEnv *env); -jint JNI_OnLoad(JavaVM* vm, void* reserved) +jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { JNIEnv* env = NULL; jint result = -1; @@ -1018,6 +1030,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_media_MediaHTTPConnection(env) < 0) { + ALOGE("ERROR: MediaHTTPConnection native registration failed"); + goto bail; + } + /* success -- return valid version number */ result = JNI_VERSION_1_4; diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp index a94fedbdb769f..d1fed7bb95d97 100644 --- a/media/tests/players/invoke_mock_media_player.cpp +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -27,6 +27,7 @@ using android::INVALID_OPERATION; using android::Surface; using android::IGraphicBufferProducer; +using android::IMediaHTTPService; using android::MediaPlayerBase; using android::OK; using android::Parcel; @@ -57,6 +58,7 @@ class Player: public MediaPlayerBase virtual bool hardwareOutput() {return true;} virtual status_t setDataSource( + const sp &httpService, const char *url, const KeyedVector *) { ALOGV("setDataSource %s", url);