1 /**
2 * Created by shengdong.huang on 2015/9/18.
3 */
4 public class ProxySettings {
5
6 private static final String LOG_TAG = "halfman";
7
8 private static final String APPLICATION_NAME = "android.app.Application";
9
10
11 public static boolean setProxy(WebView webview, String host, int port, String applicationName) {
12
13
14 // 3.2 (HC) or lower
15 if (Build.VERSION.SDK_INT <= 13) {
16 return setProxyUpToHC(webview, host, port);
17 }
18 // ICS: 4.0
19 else if (Build.VERSION.SDK_INT <= 15) {
20 return setProxyICS(webview, host, port);
21 }
22 // 4.1-4.3 (JB)
23 else if (Build.VERSION.SDK_INT <= 18) {
24 return setProxyJB(webview, host, port);
25 }
26 // 4.4 (KK) & 5.0 (Lollipop)
27 else {
28 return setProxyKKPlus(webview, host, port,
29 applicationName == null ? APPLICATION_NAME : applicationName);
30 }
31 }
32
33 /**
34 * Set Proxy for Android 3.2 and below.
35 */
36 @SuppressWarnings("all")
37 private static boolean setProxyUpToHC(WebView webview, String host, int port) {
38 Log.d(LOG_TAG, "Setting proxy with <= 3.2 API.");
39
40 HttpHost proxyServer = new HttpHost(host, port);
41 // Getting network
42 Class networkClass = null;
43 Object network = null;
44 try {
45 networkClass = Class.forName("android.webkit.Network");
46 if (networkClass == null) {
47 Log.e(LOG_TAG, "failed to get class for android.webkit.Network");
48 return false;
49 }
50 Method getInstanceMethod = networkClass.getMethod("getInstance", Context.class);
51 if (getInstanceMethod == null) {
52 Log.e(LOG_TAG, "failed to get getInstance method");
53 }
54 network = getInstanceMethod.invoke(networkClass, new Object[]{webview.getContext()});
55 } catch (Exception ex) {
56 Log.e(LOG_TAG, "error getting network: " + ex);
57 return false;
58 }
59 if (network == null) {
60 Log.e(LOG_TAG, "error getting network: network is null");
61 return false;
62 }
63 Object requestQueue = null;
64 try {
65 Field requestQueueField = networkClass
66 .getDeclaredField("mRequestQueue");
67 requestQueue = getFieldValueSafely(requestQueueField, network);
68 } catch (Exception ex) {
69 Log.e(LOG_TAG, "error getting field value");
70 return false;
71 }
72 if (requestQueue == null) {
73 Log.e(LOG_TAG, "Request queue is null");
74 return false;
75 }
76 Field proxyHostField = null;
77 try {
78 Class requestQueueClass = Class.forName("android.net.http.RequestQueue");
79 proxyHostField = requestQueueClass
80 .getDeclaredField("mProxyHost");
81 } catch (Exception ex) {
82 Log.e(LOG_TAG, "error getting proxy host field");
83 return false;
84 }
85
86 boolean temp = proxyHostField.isAccessible();
87 try {
88 proxyHostField.setAccessible(true);
89 proxyHostField.set(requestQueue, proxyServer);
90 } catch (Exception ex) {
91 Log.e(LOG_TAG, "error setting proxy host");
92 } finally {
93 proxyHostField.setAccessible(temp);
94 }
95
96 Log.d(LOG_TAG, "Setting proxy with <= 3.2 API successful!");
97 return true;
98 }
99
100 @SuppressWarnings("all")
101 private static boolean setProxyICS(WebView webview, String host, int port) {
102 try
103 {
104 Log.d(LOG_TAG, "Setting proxy with 4.0 API.");
105
106 Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
107 Class params[] = new Class[1];
108 params[0] = Class.forName("android.net.ProxyProperties");
109 Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
110
111 Class wv = Class.forName("android.webkit.WebView");
112 Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
113 Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview);
114
115 Class wvc = Class.forName("android.webkit.WebViewCore");
116 Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
117 Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
118
119 Class bf = Class.forName("android.webkit.BrowserFrame");
120 Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
121 Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
122
123 Class ppclass = Class.forName("android.net.ProxyProperties");
124 Class pparams[] = new Class[3];
125 pparams[0] = String.class;
126 pparams[1] = int.class;
127 pparams[2] = String.class;
128 Constructor ppcont = ppclass.getConstructor(pparams);
129
130 updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
131
132 Log.d(LOG_TAG, "Setting proxy with 4.0 API successful!");
133 return true;
134 }
135 catch (Exception ex)
136 {
137 Log.e(LOG_TAG, "failed to set HTTP proxy: " + ex);
138 return false;
139 }
140 }
141
142 /**
143 * Set Proxy for Android 4.1 - 4.3.
144 */
145 @SuppressWarnings("all")
146 private static boolean setProxyJB(WebView webview, String host, int port) {
147 Log.d(LOG_TAG, "Setting proxy with 4.1 - 4.3 API.");
148
149 try {
150 Class wvcClass = Class.forName("android.webkit.WebViewClassic");
151 Class wvParams[] = new Class[1];
152 wvParams[0] = Class.forName("android.webkit.WebView");
153 Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams);
154 Object webViewClassic = fromWebView.invoke(null, webview);
155
156 Class wv = Class.forName("android.webkit.WebViewClassic");
157 Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
158 Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic);
159
160 Class wvc = Class.forName("android.webkit.WebViewCore");
161 Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
162 Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
163
164 Class bf = Class.forName("android.webkit.BrowserFrame");
165 Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
166 Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
167
168 Class ppclass = Class.forName("android.net.ProxyProperties");
169 Class pparams[] = new Class[3];
170 pparams[0] = String.class;
171 pparams[1] = int.class;
172 pparams[2] = String.class;
173 Constructor ppcont = ppclass.getConstructor(pparams);
174
175 Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
176 Class params[] = new Class[1];
177 params[0] = Class.forName("android.net.ProxyProperties");
178 Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
179
180 updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
181 } catch (Exception ex) {
182 Log.e(LOG_TAG,"Setting proxy with >= 4.1 API failed with error: " + ex.getMessage());
183 return false;
184 }
185
186 Log.d(LOG_TAG, "Setting proxy with 4.1 - 4.3 API successful!");
187 return true;
188 }
189
190 @SuppressWarnings("all")
191 private static boolean setProxyKKPlus(WebView webView, String host, int port, String applicationClassName) {
192 Log.d(LOG_TAG, "Setting proxy with >= 4.4 API.");
193
194 Context appContext = webView.getContext().getApplicationContext();
195 System.setProperty("http.proxyHost", host);
196 System.setProperty("http.proxyPort", port + "");
197 System.setProperty("https.proxyHost", host);
198 System.setProperty("https.proxyPort", port + "");
199 try {
200 Class applictionCls = Class.forName(applicationClassName);
201 Field loadedApkField = applictionCls.getField("mLoadedApk");
202 loadedApkField.setAccessible(true);
203 Object loadedApk = loadedApkField.get(appContext);
204 Class loadedApkCls = Class.forName("android.app.LoadedApk");
205 Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
206 receiversField.setAccessible(true);
207 ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
208 for (Object receiverMap : receivers.values()) {
209 for (Object rec : ((ArrayMap) receiverMap).keySet()) {
210 Class clazz = rec.getClass();
211 if (clazz.getName().contains("ProxyChangeListener")) {
212 Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
213 Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
214
215 onReceiveMethod.invoke(rec, appContext, intent);
216 }
217 }
218 }
219
220 Log.d(LOG_TAG, "Setting proxy with >= 4.4 API successful!");
221 return true;
222 } catch (ClassNotFoundException e) {
223 StringWriter sw = new StringWriter();
224 e.printStackTrace(new PrintWriter(sw));
225 String exceptionAsString = sw.toString();
226 Log.v(LOG_TAG, e.getMessage());
227 Log.v(LOG_TAG, exceptionAsString);
228 } catch (NoSuchFieldException e) {
229 StringWriter sw = new StringWriter();
230 e.printStackTrace(new PrintWriter(sw));
231 String exceptionAsString = sw.toString();
232 Log.v(LOG_TAG, e.getMessage());
233 Log.v(LOG_TAG, exceptionAsString);
234 } catch (IllegalAccessException e) {
235 StringWriter sw = new StringWriter();
236 e.printStackTrace(new PrintWriter(sw));
237 String exceptionAsString = sw.toString();
238 Log.v(LOG_TAG, e.getMessage());
239 Log.v(LOG_TAG, exceptionAsString);
240 } catch (IllegalArgumentException e) {
241 StringWriter sw = new StringWriter();
242 e.printStackTrace(new PrintWriter(sw));
243 String exceptionAsString = sw.toString();
244 Log.v(LOG_TAG, e.getMessage());
245 Log.v(LOG_TAG, exceptionAsString);
246 } catch (NoSuchMethodException e) {
247 StringWriter sw = new StringWriter();
248 e.printStackTrace(new PrintWriter(sw));
249 String exceptionAsString = sw.toString();
250 Log.v(LOG_TAG, e.getMessage());
251 Log.v(LOG_TAG, exceptionAsString);
252 } catch (InvocationTargetException e) {
253 StringWriter sw = new StringWriter();
254 e.printStackTrace(new PrintWriter(sw));
255 String exceptionAsString = sw.toString();
256 Log.v(LOG_TAG, e.getMessage());
257 Log.v(LOG_TAG, exceptionAsString);
258 }
259 return false;
260 }
261
262 private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException {
263 boolean oldAccessibleValue = field.isAccessible();
264 field.setAccessible(true);
265 Object result = field.get(classInstance);
266 field.setAccessible(oldAccessibleValue);
267 return result;
268 }
269 }