Listen for HTTP proxy changes and propagate the information to the media

framework.

related-to-bug: 8873723

Change-Id: I2a34343f8006fa1b1448a1f047458fd58fe14fda
(cherry picked from commit bfe9154142428aa8abecaf943dfeffc55f411ea7)
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f745163..b729640 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,9 +16,14 @@
 
 package android.media;
 
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.AssetFileDescriptor;
+import android.net.Proxy;
+import android.net.ProxyProperties;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -864,6 +869,7 @@
      */
     public void setDataSource(Context context, Uri uri, Map<String, String> headers)
         throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        disableProxyListener();
 
         String scheme = uri.getScheme();
         if(scheme == null || scheme.equals("file")) {
@@ -896,8 +902,13 @@
         }
 
         Log.d(TAG, "Couldn't open file on client side, trying server side");
+
         setDataSource(uri.toString(), headers);
-        return;
+
+        if (scheme.equalsIgnoreCase("http")
+                || scheme.equalsIgnoreCase("https")) {
+            setupProxyListener(context);
+        }
     }
 
     /**
@@ -948,6 +959,8 @@
 
     private void setDataSource(String path, String[] keys, String[] values)
             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        disableProxyListener();
+
         final Uri uri = Uri.parse(path);
         if ("file".equals(uri.getScheme())) {
             path = uri.getPath();
@@ -991,7 +1004,13 @@
      * @param length the length in bytes of the data to be played
      * @throws IllegalStateException if it is called in an invalid state
      */
-    public native void setDataSource(FileDescriptor fd, long offset, long length)
+    public void setDataSource(FileDescriptor fd, long offset, long length)
+            throws IOException, IllegalArgumentException, IllegalStateException {
+        disableProxyListener();
+        _setDataSource(fd, offset, length);
+    }
+
+    private native void _setDataSource(FileDescriptor fd, long offset, long length)
             throws IOException, IllegalArgumentException, IllegalStateException;
 
     /**
@@ -1332,6 +1351,8 @@
         _reset();
         // make sure none of the listeners get called anymore
         mEventHandler.removeCallbacksAndMessages(null);
+
+        disableProxyListener();
     }
 
     private native void _reset();
@@ -2449,4 +2470,57 @@
         return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
                 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
     }
+
+    private Context mProxyContext = null;
+    private ProxyReceiver mProxyReceiver = null;
+
+    private void setupProxyListener(Context context) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
+        mProxyReceiver = new ProxyReceiver();
+        mProxyContext = context;
+
+        Intent currentProxy =
+            context.getApplicationContext().registerReceiver(mProxyReceiver, filter);
+
+        if (currentProxy != null) {
+            handleProxyBroadcast(currentProxy);
+        }
+    }
+
+    private void disableProxyListener() {
+        if (mProxyReceiver == null) {
+            return;
+        }
+
+        Context appContext = mProxyContext.getApplicationContext();
+        if (appContext != null) {
+            appContext.unregisterReceiver(mProxyReceiver);
+        }
+
+        mProxyReceiver = null;
+        mProxyContext = null;
+    }
+
+    private void handleProxyBroadcast(Intent intent) {
+        ProxyProperties props =
+            (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
+
+        if (props == null || props.getHost() == null) {
+            updateProxyConfig(null);
+        } else {
+            updateProxyConfig(props);
+        }
+    }
+
+    private class ProxyReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
+                handleProxyBroadcast(intent);
+            }
+        }
+    }
+
+    private native void updateProxyConfig(ProxyProperties props);
 }
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c5098ce..7c607ea 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -55,6 +55,10 @@
     jfieldID    surface_texture;
 
     jmethodID   post_event;
+
+    jmethodID   proxyConfigGetHost;
+    jmethodID   proxyConfigGetPort;
+    jmethodID   proxyConfigGetExclusionList;
 };
 static fields_t fields;
 
@@ -622,6 +626,20 @@
     if (fields.surface_texture == NULL) {
         return;
     }
+
+    clazz = env->FindClass("android/net/ProxyProperties");
+    if (clazz == NULL) {
+        return;
+    }
+
+    fields.proxyConfigGetHost =
+        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
+
+    fields.proxyConfigGetPort =
+        env->GetMethodID(clazz, "getPort", "()I");
+
+    fields.proxyConfigGetExclusionList =
+        env->GetMethodID(clazz, "getExclusionList", "()Ljava/lang/String;");
 }
 
 static void
@@ -823,6 +841,49 @@
     ;
 }
 
+static void
+android_media_MediaPlayer_updateProxyConfig(
+        JNIEnv *env, jobject thiz, jobject proxyProps)
+{
+    ALOGV("updateProxyConfig");
+    sp<MediaPlayer> thisplayer = getMediaPlayer(env, thiz);
+    if (thisplayer == NULL) {
+        return;
+    }
+
+    if (proxyProps == NULL) {
+        thisplayer->updateProxyConfig(
+                NULL /* host */, 0 /* port */, NULL /* exclusionList */);
+    } else {
+        jstring hostObj = (jstring)env->CallObjectMethod(
+                proxyProps, fields.proxyConfigGetHost);
+
+        const char *host = env->GetStringUTFChars(hostObj, NULL);
+
+        int port = env->CallIntMethod(proxyProps, fields.proxyConfigGetPort);
+
+        jstring exclusionListObj = (jstring)env->CallObjectMethod(
+                proxyProps, fields.proxyConfigGetExclusionList);
+
+        const char *exclusionList =
+            env->GetStringUTFChars(exclusionListObj, NULL);
+
+        if (host != NULL && exclusionListObj != NULL) {
+            thisplayer->updateProxyConfig(host, port, exclusionList);
+        }
+
+        if (exclusionList != NULL) {
+            env->ReleaseStringUTFChars(exclusionListObj, exclusionList);
+            exclusionList = NULL;
+        }
+
+        if (host != NULL) {
+            env->ReleaseStringUTFChars(hostObj, host);
+            host = NULL;
+        }
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -832,7 +893,7 @@
         (void *)android_media_MediaPlayer_setDataSourceAndHeaders
     },
 
-    {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
+    {"_setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
     {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
@@ -867,6 +928,7 @@
     {"getParameter",        "(ILandroid/os/Parcel;)V",          (void *)android_media_MediaPlayer_getParameter},
     {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
     {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
+    {"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig},
 };
 
 static const char* const kClassPathName = "android/media/MediaPlayer";