ProxyChangeListener.java revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.net;
6
7import android.content.BroadcastReceiver;
8import android.content.Context;
9import android.content.Intent;
10import android.content.IntentFilter;
11import android.net.Proxy;
12
13import org.chromium.base.CalledByNative;
14import org.chromium.base.JNINamespace;
15import org.chromium.base.NativeClassQualifiedName;
16
17import java.lang.reflect.InvocationTargetException;
18import java.lang.reflect.Method;
19
20// This class partners with native ProxyConfigServiceAndroid to listen for
21// proxy change notifications from Android.
22@JNINamespace("net")
23public class ProxyChangeListener {
24    private static final String TAG = "ProxyChangeListener";
25    private static boolean sEnabled = true;
26
27    private long mNativePtr;
28    private Context mContext;
29    private ProxyReceiver mProxyReceiver;
30    private Delegate mDelegate;
31
32    private static class ProxyConfig {
33        public ProxyConfig(String host, int port) {
34            mHost = host;
35            mPort = port;
36        }
37        public final String mHost;
38        public final int mPort;
39    };
40
41    public interface Delegate {
42        public void proxySettingsChanged();
43    }
44
45    private ProxyChangeListener(Context context) {
46        mContext = context;
47    }
48
49    public static void setEnabled(boolean enabled) {
50        sEnabled = enabled;
51    }
52
53    public void setDelegateForTesting(Delegate delegate) {
54        mDelegate = delegate;
55    }
56
57    @CalledByNative
58    static public ProxyChangeListener create(Context context) {
59        return new ProxyChangeListener(context);
60    }
61
62    @CalledByNative
63    static public String getProperty(String property) {
64        return System.getProperty(property);
65    }
66
67    @CalledByNative
68    public void start(long nativePtr) {
69        assert mNativePtr == 0;
70        mNativePtr = nativePtr;
71        registerReceiver();
72    }
73
74    @CalledByNative
75    public void stop() {
76        mNativePtr = 0;
77        unregisterReceiver();
78    }
79
80    private class ProxyReceiver extends BroadcastReceiver {
81        @Override
82        public void onReceive(Context context, Intent intent) {
83            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
84                proxySettingsChanged(extractNewProxy(intent));
85            }
86        }
87
88        // Extract a ProxyConfig object from the supplied Intent's extra data
89        // bundle. The android.net.ProxyProperties class is not exported from
90        // tne Android SDK, so we have to use reflection to get at it and invoke
91        // methods on it. If we fail, return an empty proxy config (meaning
92        // 'direct').
93        // TODO(ellyjones): once android.net.ProxyProperties is exported,
94        // rewrite this.
95        private ProxyConfig extractNewProxy(Intent intent) {
96            try {
97                final String CLASS_NAME = "android.net.ProxyProperties";
98                final String GET_HOST_NAME = "getHost";
99                final String GET_PORT_NAME = "getPort";
100                Object props = intent.getExtras().get("proxy");
101                if (props == null) {
102                    return null;
103                }
104                Class<?> cls = Class.forName(CLASS_NAME);
105                Method getHostMethod = cls.getDeclaredMethod(GET_HOST_NAME);
106                Method getPortMethod = cls.getDeclaredMethod(GET_PORT_NAME);
107
108                String host = (String)getHostMethod.invoke(props);
109                int port = (Integer)getPortMethod.invoke(props);
110
111                return new ProxyConfig(host, port);
112            } catch (ClassNotFoundException ex) {
113                return null;
114            } catch (NoSuchMethodException ex) {
115                return null;
116            } catch (IllegalAccessException ex) {
117                return null;
118            } catch (InvocationTargetException ex) {
119                return null;
120            } catch (NullPointerException ex) {
121                return null;
122            }
123        }
124    }
125
126    private void proxySettingsChanged(ProxyConfig cfg) {
127        if (!sEnabled) {
128            return;
129        }
130        if (mDelegate != null) {
131          mDelegate.proxySettingsChanged();
132        }
133        if (mNativePtr == 0) {
134          return;
135        }
136        // Note that this code currently runs on a MESSAGE_LOOP_UI thread, but
137        // the C++ code must run the callbacks on the network thread.
138        if (cfg != null) {
139            nativeProxySettingsChangedTo(mNativePtr, cfg.mHost, cfg.mPort);
140        } else {
141            nativeProxySettingsChanged(mNativePtr);
142        }
143    }
144
145    private void registerReceiver() {
146        if (mProxyReceiver != null) {
147            return;
148        }
149        IntentFilter filter = new IntentFilter();
150        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
151        mProxyReceiver = new ProxyReceiver();
152        mContext.getApplicationContext().registerReceiver(mProxyReceiver, filter);
153    }
154
155    private void unregisterReceiver() {
156        if (mProxyReceiver == null) {
157            return;
158        }
159        mContext.unregisterReceiver(mProxyReceiver);
160        mProxyReceiver = null;
161    }
162
163    /**
164     * See net/proxy/proxy_config_service_android.cc
165     */
166    @NativeClassQualifiedName("ProxyConfigServiceAndroid::JNIDelegate")
167    private native void nativeProxySettingsChangedTo(long nativePtr,
168                                                     String host,
169                                                     int port);
170    @NativeClassQualifiedName("ProxyConfigServiceAndroid::JNIDelegate")
171    private native void nativeProxySettingsChanged(long nativePtr);
172}
173