// 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); }