| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.net.Proxy; |
| |
| import org.chromium.base.CalledByNative; |
| import org.chromium.base.JNINamespace; |
| import org.chromium.base.NativeClassQualifiedName; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| /** |
| * This class partners with native ProxyConfigServiceAndroid to listen for |
| * proxy change notifications from Android. |
| */ |
| @JNINamespace("net") |
| public class ProxyChangeListener { |
| private static final String TAG = "ProxyChangeListener"; |
| private static boolean sEnabled = true; |
| |
| private long mNativePtr; |
| private Context mContext; |
| private ProxyReceiver mProxyReceiver; |
| private Delegate mDelegate; |
| |
| private static class ProxyConfig { |
| public ProxyConfig(String host, int port) { |
| mHost = host; |
| mPort = port; |
| } |
| public final String mHost; |
| public final int mPort; |
| } |
| |
| public interface Delegate { |
| public void proxySettingsChanged(); |
| } |
| |
| private ProxyChangeListener(Context context) { |
| mContext = context; |
| } |
| |
| public static void setEnabled(boolean enabled) { |
| sEnabled = enabled; |
| } |
| |
| public void setDelegateForTesting(Delegate delegate) { |
| mDelegate = delegate; |
| } |
| |
| @CalledByNative |
| public static ProxyChangeListener create(Context context) { |
| return new ProxyChangeListener(context); |
| } |
| |
| @CalledByNative |
| public static String getProperty(String property) { |
| return System.getProperty(property); |
| } |
| |
| @CalledByNative |
| public void start(long nativePtr) { |
| assert mNativePtr == 0; |
| mNativePtr = nativePtr; |
| registerReceiver(); |
| } |
| |
| @CalledByNative |
| public void stop() { |
| mNativePtr = 0; |
| unregisterReceiver(); |
| } |
| |
| private class ProxyReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) { |
| proxySettingsChanged(extractNewProxy(intent)); |
| } |
| } |
| |
| // Extract a ProxyConfig object from the supplied Intent's extra data |
| // bundle. The android.net.ProxyProperties class is not exported from |
| // tne Android SDK, so we have to use reflection to get at it and invoke |
| // methods on it. If we fail, return an empty proxy config (meaning |
| // 'direct'). |
| // TODO(ellyjones): once android.net.ProxyProperties is exported, |
| // rewrite this. |
| private ProxyConfig extractNewProxy(Intent intent) { |
| try { |
| final String CLASS_NAME = "android.net.ProxyProperties"; |
| final String GET_HOST_NAME = "getHost"; |
| final String GET_PORT_NAME = "getPort"; |
| Object props = intent.getExtras().get("proxy"); |
| if (props == null) { |
| return null; |
| } |
| Class<?> cls = Class.forName(CLASS_NAME); |
| Method getHostMethod = cls.getDeclaredMethod(GET_HOST_NAME); |
| Method getPortMethod = cls.getDeclaredMethod(GET_PORT_NAME); |
| |
| String host = (String) getHostMethod.invoke(props); |
| int port = (Integer) getPortMethod.invoke(props); |
| |
| return new ProxyConfig(host, port); |
| } catch (ClassNotFoundException ex) { |
| return null; |
| } catch (NoSuchMethodException ex) { |
| return null; |
| } catch (IllegalAccessException ex) { |
| return null; |
| } catch (InvocationTargetException ex) { |
| return null; |
| } catch (NullPointerException ex) { |
| return null; |
| } |
| } |
| } |
| |
| private void proxySettingsChanged(ProxyConfig cfg) { |
| if (!sEnabled) { |
| return; |
| } |
| if (mDelegate != null) { |
| mDelegate.proxySettingsChanged(); |
| } |
| if (mNativePtr == 0) { |
| return; |
| } |
| // Note that this code currently runs on a MESSAGE_LOOP_UI thread, but |
| // the C++ code must run the callbacks on the network thread. |
| if (cfg != null) { |
| nativeProxySettingsChangedTo(mNativePtr, cfg.mHost, cfg.mPort); |
| } else { |
| nativeProxySettingsChanged(mNativePtr); |
| } |
| } |
| |
| private void registerReceiver() { |
| if (mProxyReceiver != null) { |
| return; |
| } |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(Proxy.PROXY_CHANGE_ACTION); |
| mProxyReceiver = new ProxyReceiver(); |
| mContext.getApplicationContext().registerReceiver(mProxyReceiver, filter); |
| } |
| |
| private void unregisterReceiver() { |
| if (mProxyReceiver == null) { |
| return; |
| } |
| mContext.unregisterReceiver(mProxyReceiver); |
| mProxyReceiver = null; |
| } |
| |
| /** |
| * See net/proxy/proxy_config_service_android.cc |
| */ |
| @NativeClassQualifiedName("ProxyConfigServiceAndroid::JNIDelegate") |
| private native void nativeProxySettingsChangedTo(long nativePtr, |
| String host, |
| int port); |
| @NativeClassQualifiedName("ProxyConfigServiceAndroid::JNIDelegate") |
| private native void nativeProxySettingsChanged(long nativePtr); |
| } |