From a6c969c036f22f7907eb45b48805bc5ca3cae801 Mon Sep 17 00:00:00 2001 From: Hassan Shojania Date: Wed, 15 Feb 2017 09:06:48 -0800 Subject: [PATCH] New setDataSource API for accepting cookies Bug: 34736056 Test: Manual through the test app Change-Id: Ibd48d5e292dda490d9e4e4528589b2b7ba97a4b4 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + .../android/media/MediaHTTPConnection.java | 5 +- .../java/android/media/MediaHTTPService.java | 57 ++++++++++++++++++- media/java/android/media/MediaPlayer.java | 44 +++++++++++--- 6 files changed, 97 insertions(+), 12 deletions(-) diff --git a/api/current.txt b/api/current.txt index 5a5f9bc96b3f0..86b7c61216ce0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22319,6 +22319,7 @@ package android.media { method public void setAuxEffectSendLevel(float); method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; + method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map, java.util.List) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException; diff --git a/api/system-current.txt b/api/system-current.txt index acbd8b9af7e2b..1c085d5f57d67 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -23962,6 +23962,7 @@ package android.media { method public void setAuxEffectSendLevel(float); method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; + method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map, java.util.List) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException; diff --git a/api/test-current.txt b/api/test-current.txt index 4de1b1d93d14b..883e7783f3dfc 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -22412,6 +22412,7 @@ package android.media { method public void setAuxEffectSendLevel(float); method public void setBufferingParams(android.media.BufferingParams); method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; + method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map, java.util.List) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException; method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException; diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index d6bf421ffa9f2..228a6de6907c8 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -61,8 +61,9 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private final static int MAX_REDIRECTS = 20; public MediaHTTPConnection() { - if (CookieHandler.getDefault() == null) { - CookieHandler.setDefault(new CookieManager()); + CookieManager cookieManager = (CookieManager)CookieHandler.getDefault(); + if (cookieManager == null) { + Log.w(TAG, "MediaHTTPConnection: Unexpected. No CookieManager found."); } native_setup(); diff --git a/media/java/android/media/MediaHTTPService.java b/media/java/android/media/MediaHTTPService.java index 52a68bfd96f90..b678630400cb5 100644 --- a/media/java/android/media/MediaHTTPService.java +++ b/media/java/android/media/MediaHTTPService.java @@ -19,25 +19,78 @@ package android.media; import android.os.IBinder; import android.util.Log; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.util.List; + /** @hide */ public class MediaHTTPService extends IMediaHTTPService.Stub { private static final String TAG = "MediaHTTPService"; + private List mCookies; + private Boolean mCookieStoreInitialized = new Boolean(false); - public MediaHTTPService() { + public MediaHTTPService(List cookies) { + mCookies = cookies; + Log.v(TAG, "MediaHTTPService(" + this + "): Cookies: " + cookies); } public IMediaHTTPConnection makeHTTPConnection() { + + synchronized (mCookieStoreInitialized) { + // Only need to do it once for all connections + if ( !mCookieStoreInitialized ) { + CookieManager cookieManager = (CookieManager)CookieHandler.getDefault(); + if (cookieManager == null) { + cookieManager = new CookieManager(); + CookieHandler.setDefault(cookieManager); + Log.v(TAG, "makeHTTPConnection: CookieManager created: " + cookieManager); + } + else { + Log.v(TAG, "makeHTTPConnection: CookieManager(" + cookieManager + ") exists."); + } + + // Applying the bootstrapping cookies + if ( mCookies != null ) { + CookieStore store = cookieManager.getCookieStore(); + for ( HttpCookie cookie : mCookies ) { + try { + store.add(null, cookie); + } catch ( Exception e ) { + Log.v(TAG, "makeHTTPConnection: CookieStore.add" + e); + } + //for extended debugging when needed + //Log.v(TAG, "MediaHTTPConnection adding Cookie[" + cookie.getName() + + // "]: " + cookie); + } + } // mCookies + + mCookieStoreInitialized = true; + + Log.v(TAG, "makeHTTPConnection(" + this + "): cookieManager: " + cookieManager + + " Cookies: " + mCookies); + } // mCookieStoreInitialized + } // synchronized + return new MediaHTTPConnection(); } /* package private */static IBinder createHttpServiceBinderIfNecessary( String path) { + return createHttpServiceBinderIfNecessary(path, null); + } + + // when cookies are provided + static IBinder createHttpServiceBinderIfNecessary( + String path, List cookies) { if (path.startsWith("http://") || path.startsWith("https://")) { - return (new MediaHTTPService()).asBinder(); + return (new MediaHTTPService(cookies)).asBinder(); } else if (path.startsWith("widevine://")) { Log.d(TAG, "Widevine classic is no longer supported"); } return null; } + } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index c5a47ecd2bb61..85c3c1c7068d5 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -73,6 +73,7 @@ import java.lang.Runnable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URL; @@ -80,6 +81,7 @@ import java.nio.ByteOrder; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; @@ -998,7 +1000,7 @@ public class MediaPlayer extends PlayerBase */ public void setDataSource(@NonNull Context context, @NonNull Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { - setDataSource(context, uri, null); + setDataSource(context, uri, null, null); } /** @@ -1011,11 +1013,13 @@ public class MediaPlayer extends PlayerBase * changed with key/value pairs through the headers parameter with * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value * to disallow or allow cross domain redirection. + * The headers must not include cookies. Instead, use the cookies param. + * @param cookies the cookies to be sent together with the request * @throws IllegalStateException if it is called in an invalid state */ public void setDataSource(@NonNull Context context, @NonNull Uri uri, - @Nullable Map headers) throws IOException, IllegalArgumentException, - SecurityException, IllegalStateException { + @Nullable Map headers, @Nullable List cookies) + throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { // The context and URI usually belong to the calling user. Get a resolver for that user // and strip out the userId from the URI if present. final ContentResolver resolver = context.getContentResolver(); @@ -1036,18 +1040,36 @@ public class MediaPlayer extends PlayerBase } else if (attemptDataSource(resolver, actualUri)) { return; } else { - setDataSource(uri.toString(), headers); + setDataSource(uri.toString(), headers, cookies); } } else { // Try requested Uri locally first, or fallback to media server if (attemptDataSource(resolver, uri)) { return; } else { - setDataSource(uri.toString(), headers); + setDataSource(uri.toString(), headers, cookies); } } } + /** + * Sets the data source as a content Uri. + * + * @param context the Context to use when resolving the Uri + * @param uri the Content URI of the data you want to play + * @param headers the headers to be sent together with the request for the data + * Note that the cross domain redirection is allowed by default, but that can be + * changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value + * to disallow or allow cross domain redirection. + * @throws IllegalStateException if it is called in an invalid state + */ + public void setDataSource(@NonNull Context context, @NonNull Uri uri, + @Nullable Map headers) + throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + setDataSource(context, uri, headers, null); + } + private boolean attemptDataSource(ContentResolver resolver, Uri uri) { try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) { setDataSource(afd); @@ -1085,6 +1107,11 @@ public class MediaPlayer extends PlayerBase * @hide pending API council */ public void setDataSource(String path, Map headers) + throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + setDataSource(path, headers, null); + } + + private void setDataSource(String path, Map headers, List cookies) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { String[] keys = null; @@ -1101,10 +1128,11 @@ public class MediaPlayer extends PlayerBase ++i; } } - setDataSource(path, keys, values); + setDataSource(path, keys, values, cookies); } - private void setDataSource(String path, String[] keys, String[] values) + private void setDataSource(String path, String[] keys, String[] values, + List cookies) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { final Uri uri = Uri.parse(path); final String scheme = uri.getScheme(); @@ -1113,7 +1141,7 @@ public class MediaPlayer extends PlayerBase } else if (scheme != null) { // handle non-file sources nativeSetDataSource( - MediaHTTPService.createHttpServiceBinderIfNecessary(path), + MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies), path, keys, values);