Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/org/adblockplus/android/ProxyService.java

Issue 4705284891082752: Proxy configurators (Closed)
Patch Set: Created Aug. 11, 2014, 12:36 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 package org.adblockplus.android; 18 package org.adblockplus.android;
19 19
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.IOException; 20 import java.io.IOException;
23 import java.lang.reflect.Method;
24 import java.net.InetAddress; 21 import java.net.InetAddress;
25 import java.net.ServerSocket; 22 import java.net.ServerSocket;
26 import java.util.List; 23 import java.net.UnknownHostException;
27 import java.util.Properties; 24 import java.util.Properties;
28 import java.util.concurrent.TimeoutException; 25
26 import org.adblockplus.android.configurators.ProxyConfigurator;
27 import org.adblockplus.android.configurators.ProxyConfigurators;
28 import org.adblockplus.android.configurators.ProxyRegistrationType;
29 import org.apache.commons.lang.StringUtils;
29 30
30 import sunlabs.brazil.server.Server; 31 import sunlabs.brazil.server.Server;
31 import sunlabs.brazil.util.Base64; 32 import sunlabs.brazil.util.Base64;
32 import android.annotation.SuppressLint; 33 import android.annotation.SuppressLint;
33 import android.app.Notification; 34 import android.app.Notification;
34 import android.app.NotificationManager; 35 import android.app.NotificationManager;
35 import android.app.PendingIntent; 36 import android.app.PendingIntent;
36 import android.app.Service; 37 import android.app.Service;
37 import android.content.BroadcastReceiver; 38 import android.content.BroadcastReceiver;
38 import android.content.Context; 39 import android.content.Context;
39 import android.content.Intent; 40 import android.content.Intent;
40 import android.content.IntentFilter; 41 import android.content.IntentFilter;
41 import android.content.SharedPreferences; 42 import android.content.SharedPreferences;
42 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 43 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
43 import android.content.pm.PackageManager.NameNotFoundException;
44 import android.content.res.Resources;
45 import android.net.ConnectivityManager;
46 import android.net.NetworkInfo;
47 import android.net.Proxy;
48 import android.os.Binder; 44 import android.os.Binder;
49 import android.os.Build; 45 import android.os.Build;
50 import android.os.Handler;
51 import android.os.IBinder; 46 import android.os.IBinder;
52 import android.os.StrictMode; 47 import android.os.StrictMode;
53 import android.preference.PreferenceManager; 48 import android.preference.PreferenceManager;
54 import android.support.v4.app.NotificationCompat; 49 import android.support.v4.app.NotificationCompat;
55 import android.util.Log; 50 import android.util.Log;
56 51
57 import com.stericson.RootTools.RootTools;
58 import com.stericson.RootTools.RootToolsException;
59
60 public class ProxyService extends Service implements OnSharedPreferenceChangeLis tener 52 public class ProxyService extends Service implements OnSharedPreferenceChangeLis tener
61 { 53 {
54 private static final String TAG = Utils.getTag(ProxyService.class);
55
62 private static final String LOCALHOST = "127.0.0.1"; 56 private static final String LOCALHOST = "127.0.0.1";
63 /**
64 * Indicates that system supports native proxy configuration.
65 */
66 public static final boolean NATIVE_PROXY_SUPPORTED = Build.VERSION.SDK_INT >= 12; // Honeycomb 3.1
67 57
68 static 58 private static final int[] PORT_VARIANTS = new int[] {-1, 2020, 3030, 4040, 50 50, 6060, 7070, 9090, 1234, 12345, 4321, 0};
69 {
70 RootTools.debugMode = false;
71 }
72 59
73 private static final String TAG = Utils.getTag(ProxyService.class); 60 // FIXME remove me
Felix Dahlke 2014/08/19 09:06:00 Why not remove it?
René Jeschke 2014/08/19 10:41:33 When I remove it, we don't have a way to enable re
Felix Dahlke 2014/08/19 12:37:26 Can you remove the FIXME comment then? :)
René Jeschke 2014/08/19 12:57:36 Done.
74 private static final boolean logRequests = false; 61 private static final boolean LOG_REQUESTS = false;
75
76 // Do not use 8080 because it is a "dirty" port, Android uses it if something goes wrong
77 // first element is reserved for previously used port
78 private static final int[] portVariants = new int[] {-1, 2020, 3030, 4040, 505 0, 6060, 7070, 9090, 1234, 12345, 4321, 0};
79
80 private static final int DEFAULT_TIMEOUT = 3000;
81 private static final int NO_TRAFFIC_TIMEOUT = 5 * 60 * 1000; // 5 minutes
82 62
83 static final int ONGOING_NOTIFICATION_ID = R.string.app_name; 63 static final int ONGOING_NOTIFICATION_ID = R.string.app_name;
64
84 private static final long POSITION_RIGHT = Build.VERSION.SDK_INT >= Build.VERS ION_CODES.GINGERBREAD ? Long.MIN_VALUE : Long.MAX_VALUE; 65 private static final long POSITION_RIGHT = Build.VERSION.SDK_INT >= Build.VERS ION_CODES.GINGERBREAD ? Long.MIN_VALUE : Long.MAX_VALUE;
85 private static final int NOTRAFFIC_NOTIFICATION_ID = R.string.app_name + 3;
86 66
87 /** 67 /**
88 * Broadcasted when service starts or stops. 68 * Broadcasted when service starts or stops.
89 */ 69 */
90 public static final String BROADCAST_STATE_CHANGED = "org.adblockplus.android. service.state"; 70 public static final String BROADCAST_STATE_CHANGED = "org.adblockplus.android. SERVICE_STATE_CHANGED";
71
91 /** 72 /**
92 * Broadcasted if proxy fails to start. 73 * Broadcasted if proxy fails to start.
93 */ 74 */
94 public static final String BROADCAST_PROXY_FAILED = "org.adblockplus.android.p roxy.failure"; 75 public static final String BROADCAST_PROXY_FAILED = "org.adblockplus.android.P ROXY_FAILURE";
95 76
96 private static final String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{ UID}} -A OUTPUT -p tcp -j RETURN\n"; 77 /**
97 private static final String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dp ort 80 -j REDIRECT --to {{PORT}}\n"; 78 * Common command bridge
79 */
80 public static final String COMMAND_BRIDGE_ACTION = "org.adblockplus.android.CO MMAND_BRIDGE";
98 81
99 boolean hideIcon; 82 boolean hideIcon;
100 private Handler notrafficHandler;
101 83
102 protected ProxyServer proxy = null; 84 protected ProxyServer proxy = null;
85
103 protected int port; 86 protected int port;
87
104 private final Properties proxyConfiguration = new Properties(); 88 private final Properties proxyConfiguration = new Properties();
105 89
106 /** 90 private ProxyConfigurator proxyConfigurator = null;
107 * Indicates that service is working with root privileges.
108 */
109 private boolean transparent = false;
110 /**
111 * Indicates that service has autoconfigured Android proxy settings (version 3 .1+).
112 */
113 private boolean nativeProxyAutoConfigured = false;
114 /**
115 * Indicates that Android proxy settings are correctly configured (version 4.1 .2+ 4.2.2+).
116 */
117 private boolean proxyManualyConfigured = false;
118
119 private String iptables = null;
120 91
121 @SuppressLint("NewApi") 92 @SuppressLint("NewApi")
122 @Override 93 @Override
123 public void onCreate() 94 public void onCreate()
124 { 95 {
125 super.onCreate(); 96 super.onCreate();
126 97
127 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) 98 // FIXME 'StrictMode' is API9
Felix Dahlke 2014/08/19 09:06:00 Isn't our conclusion so far that API 9 should be o
René Jeschke 2014/08/19 10:41:33 Hm, there are devices out there that run API7 on a
Felix Dahlke 2014/08/19 12:37:26 Still - we're using API9 all over the place, don't
René Jeschke 2014/08/19 12:57:36 Thing is, as you disliked my tries to remove the w
Felix Dahlke 2014/08/19 13:17:50 Did I? I'm all for getting rid of warnings!
René Jeschke 2014/08/19 13:23:53 Yeah, think it's been unrelated changes back then,
Felix Dahlke 2014/08/19 13:28:51 8)
128 { 99 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
129 // Proxy is running in separate thread, it's just some resolution request during initialization. 100 .permitAll()
130 // Not worth spawning a separate thread for this. 101 .penaltyLog()
131 final StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder ().permitNetwork().build(); 102 .build());
132 StrictMode.setThreadPolicy(policy);
133 }
134 103
135 // Get port for local proxy
136 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreference s(this); 104 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreference s(this);
137 final Resources resources = getResources();
138 105
139 // Try to read user proxy settings 106 // Try to read user proxy settings
140 String proxyHost = null; 107 String proxyHost = System.getProperty("http.proxyHost");;
141 String proxyPort = null; 108 String proxyPort = System.getProperty("http.proxyPort");
142 String proxyExcl = null; 109 final String proxyExcl = System.getProperty("http.nonProxyHosts");
143 String proxyUser = null; 110 String proxyUser = null;
144 String proxyPass = null; 111 String proxyPass = null;
145 112
146 if (NATIVE_PROXY_SUPPORTED) 113 if (proxyHost == null || proxyPort == null)
147 {
148 // Read system settings
149 proxyHost = System.getProperty("http.proxyHost");
150 proxyPort = System.getProperty("http.proxyPort");
151 proxyExcl = System.getProperty("http.nonProxyHosts");
152
153 Log.d(TAG, "PRX: " + proxyHost + ":" + proxyPort + "(" + proxyExcl + ")");
154 // not used but left for future reference
155 final String[] px = ProxySettings.getUserProxy(getApplicationContext());
156 if (px != null)
157 Log.d(TAG, "PRX: " + px[0] + ":" + px[1] + "(" + px[2] + ")");
158 }
159 else
160 { 114 {
161 // Read application settings 115 // Read application settings
162 proxyHost = prefs.getString(getString(R.string.pref_proxyhost), null); 116 proxyHost = prefs.getString(getString(R.string.pref_proxyhost), null);
163 proxyPort = prefs.getString(getString(R.string.pref_proxyport), null); 117 proxyPort = prefs.getString(getString(R.string.pref_proxyport), null);
164 proxyUser = prefs.getString(getString(R.string.pref_proxyuser), null); 118 proxyUser = prefs.getString(getString(R.string.pref_proxyuser), null);
165 proxyPass = prefs.getString(getString(R.string.pref_proxypass), null); 119 proxyPass = prefs.getString(getString(R.string.pref_proxypass), null);
166 } 120 }
167 121
168 // Check for root privileges and try to install transparent proxy 122 registerReceiver(this.proxyReceiver, new IntentFilter(ProxyService.BROADCAST _PROXY_FAILED));
169 if (RootTools.isAccessGiven()) 123 registerReceiver(this.commandBridge, new IntentFilter(ProxyService.COMMAND_B RIDGE_ACTION));
124
125 final InetAddress inetAddress;
126 try
170 { 127 {
171 try 128 inetAddress = InetAddress.getByName(LOCALHOST);
172 {
173 initIptables();
174
175 final StringBuffer cmd = new StringBuffer();
176 final int uid = getPackageManager().getPackageInfo(getPackageName(), 0). applicationInfo.uid;
177 cmd.append(iptables);
178 cmd.append(IPTABLES_RETURN.replace("{{UID}}", String.valueOf(uid)));
179 final String rules = cmd.toString();
180 RootTools.sendShell(rules, DEFAULT_TIMEOUT);
181 transparent = true;
182 }
183 catch (final FileNotFoundException e)
184 {
185 // ignore - this is "normal" case
186 }
187 catch (final NameNotFoundException e)
188 {
189 Log.e(TAG, "Failed to initialize iptables", e);
190 }
191 catch (final IOException e)
192 {
193 Log.e(TAG, "Failed to initialize iptables", e);
194 }
195 catch (final RootToolsException e)
196 {
197 Log.e(TAG, "Failed to initialize iptables", e);
198 }
199 catch (final TimeoutException e)
200 {
201 Log.e(TAG, "Failed to initialize iptables", e);
202 }
203 } 129 }
204 130 catch (final UnknownHostException e)
205 if (!transparent)
206 { 131 {
207 // Try to set native proxy 132 sendBroadcast(new Intent(BROADCAST_PROXY_FAILED)
208 nativeProxyAutoConfigured = ProxySettings.setConnectionProxy(getApplicatio nContext(), LOCALHOST, port, ""); 133 .putExtra("msg", "Could not resolve localhost, this one is serious^^") );
Felix Dahlke 2014/08/19 09:06:00 I can see your point, but I think we shouldn't hav
René Jeschke 2014/08/19 10:41:33 Done.
209 134 return;
210 if (NATIVE_PROXY_SUPPORTED)
211 {
212 registerReceiver(connectionReceiver, new IntentFilter(ConnectivityManage r.CONNECTIVITY_ACTION));
213 registerReceiver(connectionReceiver, new IntentFilter(Proxy.PROXY_CHANGE _ACTION));
214 }
215 } 135 }
216 136
217 // Save current native proxy situation. The service is always started on the first run so
218 // we will always have a correct value from the box
219 final SharedPreferences.Editor editor = prefs.edit();
220 editor.putBoolean(getString(R.string.pref_proxyautoconfigured), transparent || nativeProxyAutoConfigured);
221 editor.commit();
222
223 registerReceiver(proxyReceiver, new IntentFilter(ProxyService.BROADCAST_PROX Y_FAILED));
224 registerReceiver(filterReceiver, new IntentFilter(AdblockPlus.BROADCAST_FILT ERING_CHANGE));
225 registerReceiver(filterReceiver, new IntentFilter(AdblockPlus.BROADCAST_FILT ER_MATCHES));
226
227 // Start proxy 137 // Start proxy
228 if (proxy == null) 138 if (proxy == null)
229 { 139 {
230 // Select available port and bind to it, use previously selected port by d efault 140 // Select available port and bind to it, use previously selected port by d efault
231 portVariants[0] = prefs.getInt(getString(R.string.pref_lastport), -1);
232 ServerSocket listen = null; 141 ServerSocket listen = null;
233 String msg = null; 142 String msg = null;
234 for (final int p : portVariants) 143 for (final int p : PORT_VARIANTS)
235 { 144 {
236 if (p < 0) 145 final int toCheck = (p == -1) ? prefs.getInt(getString(R.string.pref_las tport), -1) : p;
237 continue; 146
238 try 147 if (toCheck >= 0)
239 { 148 {
240 // Fix for #232, bind proxy socket to loopback only 149 try
241 listen = new ServerSocket(p, 1024, InetAddress.getByName(LOCALHOST)); 150 {
242 port = p; 151 // Fix for #232, bind proxy socket to loopback only
243 break; 152 listen = new ServerSocket(toCheck, 1024, inetAddress);
244 } 153 this.port = listen.getLocalPort();
245 catch (final IOException e) 154 break;
246 { 155 }
247 Log.e(TAG, null, e); 156 catch (final IOException e)
248 msg = e.getMessage(); 157 {
158 Log.e(TAG, null, e);
159 msg = e.getMessage();
160 }
249 } 161 }
250 } 162 }
163
251 if (listen == null) 164 if (listen == null)
252 { 165 {
253 sendBroadcast(new Intent(BROADCAST_PROXY_FAILED).putExtra("msg", msg)); 166 sendBroadcast(new Intent(BROADCAST_PROXY_FAILED).putExtra("msg", msg));
254 return; 167 return;
255 } 168 }
256 169
170 this.proxyConfigurator = ProxyConfigurators.registerProxy(this, inetAddres s, this.port);
171
172 if (this.proxyConfigurator == null)
173 {
174 sendBroadcast(new Intent(BROADCAST_PROXY_FAILED)
175 .putExtra("msg", "Failed to register proxy"));
176 return;
177 }
178
257 // Save selected port 179 // Save selected port
180 final SharedPreferences.Editor editor = prefs.edit();
181 editor.putBoolean(getString(R.string.pref_proxyautoconfigured), this.getPr oxyRegistrationType().isAutoConfigured());
258 editor.putInt(getString(R.string.pref_lastport), port); 182 editor.putInt(getString(R.string.pref_lastport), port);
259 editor.commit(); 183 editor.commit();
260 184
261 // Initialize proxy 185 // Initialize proxy
262 proxyConfiguration.put("handler", "main"); 186 proxyConfiguration.put("handler", "main");
263 proxyConfiguration.put("main.prefix", ""); 187 proxyConfiguration.put("main.prefix", "");
264 proxyConfiguration.put("main.class", "sunlabs.brazil.server.ChainHandler") ; 188 proxyConfiguration.put("main.class", "sunlabs.brazil.server.ChainHandler") ;
265 if (transparent) 189 switch (this.getProxyRegistrationType().getProxyType())
266 { 190 {
191 case HTTP:
267 proxyConfiguration.put("main.handlers", "urlmodifier adblock"); 192 proxyConfiguration.put("main.handlers", "urlmodifier adblock");
268 proxyConfiguration.put("urlmodifier.class", "org.adblockplus.brazil.Tran sparentProxyHandler"); 193 proxyConfiguration.put("urlmodifier.class", "org.adblockplus.brazil.Tran sparentProxyHandler");
269 } 194 break;
270 else 195 case HTTPS:
271 {
272 proxyConfiguration.put("main.handlers", "https adblock"); 196 proxyConfiguration.put("main.handlers", "https adblock");
273 proxyConfiguration.put("https.class", "org.adblockplus.brazil.SSLConnect ionHandler"); 197 proxyConfiguration.put("https.class", "org.adblockplus.brazil.SSLConnect ionHandler");
198 break;
199 default:
200 // FIXME should we reset the settings here? (Can't happen currently)
Felix Dahlke 2014/08/19 09:06:00 As with most TODO comments, I think this should be
René Jeschke 2014/08/19 10:41:33 Ah, just see that this TODO is now out-of-scope, r
201 sendBroadcast(new Intent(BROADCAST_PROXY_FAILED)
202 .putExtra("msg", "Unsupported proxy server type: " + this.getProxyRe gistrationType().getProxyType()));
203 return;
274 } 204 }
275 proxyConfiguration.put("adblock.class", "org.adblockplus.brazil.RequestHan dler"); 205 proxyConfiguration.put("adblock.class", "org.adblockplus.brazil.RequestHan dler");
276 if (logRequests) 206 if (LOG_REQUESTS)
277 proxyConfiguration.put("adblock.proxylog", "yes"); 207 proxyConfiguration.put("adblock.proxylog", "yes");
278 208
279 configureUserProxy(proxyConfiguration, proxyHost, proxyPort, proxyExcl, pr oxyUser, proxyPass); 209 configureUserProxy(proxyConfiguration, proxyHost, proxyPort, proxyExcl, pr oxyUser, proxyPass);
280 210
281 proxy = new ProxyServer(); 211 proxy = new ProxyServer();
282 proxy.logLevel = Server.LOG_DIAGNOSTIC; 212 proxy.logLevel = Server.LOG_LOG;
283 proxy.setup(listen, proxyConfiguration.getProperty("handler"), proxyConfig uration); 213 proxy.setup(listen, proxyConfiguration.getProperty("handler"), proxyConfig uration);
284 proxy.start(); 214 proxy.start();
285 } 215 } // if (proxy == null) // Start proxy
Felix Dahlke 2014/08/19 09:06:00 I think this comment is ridiculously likely to get
René Jeschke 2014/08/19 10:41:33 Done.
286
287 if (transparent)
288 {
289 // Redirect traffic via iptables
290 try
291 {
292 final StringBuffer cmd = new StringBuffer();
293 cmd.append(iptables);
294 cmd.append(IPTABLES_ADD_HTTP.replace("{{PORT}}", String.valueOf(port)));
295 final String rules = cmd.toString();
296 RootTools.sendShell(rules, DEFAULT_TIMEOUT);
297 }
298 catch (final FileNotFoundException e)
299 {
300 // ignore - this is "normal" case
301 }
302 catch (final IOException e)
303 {
304 Log.e(TAG, "Failed to initialize iptables", e);
305 }
306 catch (final RootToolsException e)
307 {
308 Log.e(TAG, "Failed to initialize iptables", e);
309 }
310 catch (final TimeoutException e)
311 {
312 Log.e(TAG, "Failed to initialize iptables", e);
313 }
314 }
315 216
316 prefs.registerOnSharedPreferenceChangeListener(this); 217 prefs.registerOnSharedPreferenceChangeListener(this);
317 218
318 // Lock service 219 // Lock service
319 hideIcon = prefs.getBoolean(getString(R.string.pref_hideicon), resources.get Boolean(R.bool.def_hideicon)); 220 hideIcon = prefs.getBoolean(getString(R.string.pref_hideicon), getResources( ).getBoolean(R.bool.def_hideicon));
320 startForeground(ONGOING_NOTIFICATION_ID, getNotification()); 221 startForeground(ONGOING_NOTIFICATION_ID, getNotification());
321 222
322 // If automatic setting of proxy was blocked, check if user has set it manua lly
323 final boolean manual = isManual();
324 if (manual && NATIVE_PROXY_SUPPORTED)
325 {
326 final ConnectivityManager connectivityManager = (ConnectivityManager) getS ystemService(Context.CONNECTIVITY_SERVICE);
327 updateNoTrafficCheck(connectivityManager);
328 }
329
330 sendStateChangedBroadcast(); 223 sendStateChangedBroadcast();
331 Log.i(TAG, "Service started"); 224 Log.i(TAG, "Service started");
332 } 225 }
333 226
334 @Override 227 @Override
335 public int onStartCommand(final Intent intent, final int flags, final int star tId) 228 public int onStartCommand(final Intent intent, final int flags, final int star tId)
336 { 229 {
337 return START_STICKY; 230 return START_STICKY;
338 } 231 }
339 232
340 @Override 233 @Override
341 public void onDestroy() 234 public void onDestroy()
342 { 235 {
343 super.onDestroy(); 236 super.onDestroy();
344 237
345 stopNoTrafficCheck(); 238 unregisterReceiver(this.proxyReceiver);
239 unregisterReceiver(this.commandBridge);
346 240
347 unregisterReceiver(filterReceiver); 241 if (this.proxyConfigurator != null)
348 unregisterReceiver(proxyReceiver);
349
350 // Stop IP redirecting
351 if (transparent)
352 { 242 {
353 new Thread() 243 this.proxyConfigurator.unregisterProxy();
354 { 244 this.proxyConfigurator.shutdown();
355 @Override
356 public void run()
357 {
358 try
359 {
360 RootTools.sendShell(iptables + " -t nat -F OUTPUT", DEFAULT_TIMEOUT) ;
361 }
362 catch (final Exception e)
363 {
364 Log.e(TAG, "Failed to clear iptables", e);
365 }
366 }
367 }.start();
368 }
369
370 if (!transparent && NATIVE_PROXY_SUPPORTED)
371 unregisterReceiver(connectionReceiver);
372
373 // Clear native proxy
374 if (nativeProxyAutoConfigured)
375 {
376 clearConnectionProxy();
377 } 245 }
378 246
379 sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", false) ); 247 sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", false) );
380 248
381 // Stop proxy server 249 // Stop proxy server
382 if (proxy != null) 250 if (proxy != null)
251 {
383 proxy.close(); 252 proxy.close();
253 proxy = null;
254 }
384 255
385 // Release service lock 256 // Release service lock
386 stopForeground(true); 257 stopForeground(true);
387 258
388 Log.i(TAG, "Service stopped"); 259 Log.i(TAG, "Service stopped");
389 } 260 }
390 261
391 /** 262 /**
392 * Restores system proxy settings via native call on Android 3.1+ devices
393 * using Java reflection.
394 */
395 private void clearConnectionProxy()
396 {
397 final String proxyHost = proxyConfiguration.getProperty("adblock.proxyHost") ;
398 final String proxyPort = proxyConfiguration.getProperty("adblock.proxyPort") ;
399 final String proxyExcl = proxyConfiguration.getProperty("adblock.proxyExcl") ;
400 int port = 0;
401 try
402 {
403 if (proxyHost != null)
404 port = Integer.valueOf(proxyPort);
405 }
406 catch (final NumberFormatException e)
407 {
408 Log.e(TAG, "Bad port setting", e);
409 }
410 ProxySettings.setConnectionProxy(getApplicationContext(), proxyHost, port, p roxyExcl);
411 }
412
413 /**
414 * Sets user proxy settings in proxy service properties. 263 * Sets user proxy settings in proxy service properties.
415 */ 264 */
416 private void configureUserProxy(final Properties config, final String proxyHos t, final String proxyPort, final String proxyExcl, final String proxyUser, final String proxyPass) 265 private void configureUserProxy(final Properties config, final String proxyHos t, final String proxyPort, final String proxyExcl,
266 final String proxyUser, final String proxyPass)
417 { 267 {
268 final ProxyRegistrationType regType = this.getProxyRegistrationType();
Felix Dahlke 2014/08/19 09:06:00 How about moving this down to where it's used?
René Jeschke 2014/08/19 10:41:33 Done.
269
418 // Clean previous settings 270 // Clean previous settings
419 config.remove("adblock.proxyHost"); 271 config.remove("adblock.proxyHost");
420 config.remove("adblock.proxyPort"); 272 config.remove("adblock.proxyPort");
421 config.remove("adblock.auth"); 273 config.remove("adblock.auth");
422 config.remove("adblock.proxyExcl"); 274 config.remove("adblock.proxyExcl");
423 if (!transparent) 275 config.remove("https.proxyHost");
276 config.remove("https.proxyPort");
277 config.remove("https.auth");
278
279 if (regType == ProxyRegistrationType.REFLECTION)
424 { 280 {
425 config.remove("https.proxyHost"); 281 passProxySettings(proxyHost, proxyPort, proxyExcl);
426 config.remove("https.proxyPort");
427 config.remove("https.auth");
428 } 282 }
429 283
430 if (nativeProxyAutoConfigured)
431 passProxySettings(proxyHost, proxyPort, proxyExcl);
432
433 // Check if there are any settings 284 // Check if there are any settings
434 if (proxyHost == null || "".equals(proxyHost)) 285 if (StringUtils.isEmpty(proxyHost))
286 {
435 return; 287 return;
288 }
436 289
437 // Check for dirty proxy settings - this indicated previous crash: 290 // Check for dirty proxy settings - this indicated previous crash:
438 // proxy points to ourselves 291 // proxy points to ourselves
439 // proxy port is null, 0 or not a number 292 // proxy port is null, 0 or not a number
440 // proxy is 127.0.0.1:8080 293 // proxy is 127.0.0.1:8080
441 if (proxyPort == null) 294 if (proxyPort == null)
295 {
442 return; 296 return;
443 int p = 0; 297 }
298
299 final int p;
444 try 300 try
445 { 301 {
446 p = Integer.valueOf(proxyPort); 302 p = Integer.valueOf(proxyPort);
447 } 303 }
448 catch (final NumberFormatException e) 304 catch (final NumberFormatException e)
449 { 305 {
450 return; 306 return;
451 } 307 }
452 if (p == 0 || isLocalHost(proxyHost) && (p == port || p == 8080)) 308
309 if (p == 0 || isLocalhost(proxyHost) && (p == port || p == 8080))
453 { 310 {
454 if (nativeProxyAutoConfigured) 311 if (regType == ProxyRegistrationType.REFLECTION)
312 {
455 passProxySettings(null, null, null); 313 passProxySettings(null, null, null);
314 }
456 return; 315 return;
457 } 316 }
458 317
459 config.put("adblock.proxyHost", proxyHost); 318 config.put("adblock.proxyHost", proxyHost);
460 config.put("adblock.proxyPort", proxyPort); 319 config.put("adblock.proxyPort", proxyPort);
461 if (!transparent) 320 if (regType.getProxyType() == ProxyServerType.HTTPS)
462 { 321 {
463 config.put("https.proxyHost", proxyHost); 322 config.put("https.proxyHost", proxyHost);
464 config.put("https.proxyPort", proxyPort); 323 config.put("https.proxyPort", proxyPort);
465 } 324 }
466 325
467 // TODO Not implemented in our proxy but needed to restore settings 326 // TODO Not implemented in our proxy but needed to restore settings
468 if (proxyExcl != null) 327 if (proxyExcl != null)
328 {
469 config.put("adblock.proxyExcl", proxyExcl); 329 config.put("adblock.proxyExcl", proxyExcl);
330 }
470 331
471 if (proxyUser != null && !"".equals(proxyUser) && proxyPass != null && !"".e quals(proxyPass)) 332 if (StringUtils.isNotEmpty(proxyUser) && StringUtils.isNotEmpty(proxyPass))
472 { 333 {
473 // Base64 encode user:password 334 // Base64 encode user:password
474 final String proxyAuth = "Basic " + new String(Base64.encode(proxyUser + " :" + proxyPass)); 335 final String proxyAuth = "Basic " + new String(Base64.encode(proxyUser + " :" + proxyPass));
475 config.put("adblock.auth", proxyAuth); 336 config.put("adblock.auth", proxyAuth);
476 if (!transparent) 337 if (regType.getProxyType() == ProxyServerType.HTTPS)
338 {
477 config.put("https.auth", proxyAuth); 339 config.put("https.auth", proxyAuth);
340 }
478 } 341 }
479 } 342 }
480 343
344 /**
345 * Lean method to send a {@link BridgeCommand} without additional extras.
346 *
347 * @param context
348 * the context to use for sending the broadcast
349 * @param command
350 * the command
351 */
352 public static void sendBridgeCommand(final Context context, final BridgeComman d command)
353 {
354 context.sendBroadcast(
355 new Intent(COMMAND_BRIDGE_ACTION)
356 .putExtra("command", command.toString()));
357 }
358
359 /**
360 * @return {@code true} if the given host string resolves to {@code localhost}
361 */
362 public static boolean isLocalhost(final String host)
363 {
364 try
365 {
366 if (StringUtils.isEmpty(host))
367 {
368 return false;
369 }
370
371 if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost"))
372 {
373 return true;
374 }
375
376 return InetAddress.getByName(host).isLoopbackAddress();
377 }
378 catch (Exception e)
379 {
380 return false;
381 }
382 }
383
481 private void passProxySettings(final String proxyHost, final String proxyPort, final String proxyExcl) 384 private void passProxySettings(final String proxyHost, final String proxyPort, final String proxyExcl)
482 { 385 {
483 try 386 try
484 { 387 {
485 final CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExcep tionHandler(); 388 final CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExcep tionHandler();
486 handler.saveProxySettings(proxyHost, proxyPort, proxyExcl); 389 handler.saveProxySettings(proxyHost, proxyPort, proxyExcl);
487 } 390 }
488 catch (final ClassCastException e) 391 catch (final ClassCastException e)
489 { 392 {
490 // ignore - default handler in use 393 // ignore - default handler in use
491 } 394 }
492 } 395 }
493 396
494 @Override 397 @Override
495 public void onSharedPreferenceChanged(final SharedPreferences sharedPreference s, final String key) 398 public void onSharedPreferenceChanged(final SharedPreferences sharedPreference s, final String key)
496 { 399 {
497 if (!NATIVE_PROXY_SUPPORTED) 400 // TODO verify this
Felix Dahlke 2014/08/19 09:06:00 We should probably do that before pushing the code
René Jeschke 2014/08/19 10:41:33 Right, it's the '!NATIVE_PROXY_SUPPORTED' thingy I
401 if (this.getProxyRegistrationType() != ProxyRegistrationType.REFLECTION)
498 { 402 {
499 final String ketHost = getString(R.string.pref_proxyhost); 403 final String keyHost = getString(R.string.pref_proxyhost);
500 final String keyPort = getString(R.string.pref_proxyport); 404 final String keyPort = getString(R.string.pref_proxyport);
501 final String keyUser = getString(R.string.pref_proxyuser); 405 final String keyUser = getString(R.string.pref_proxyuser);
502 final String keyPass = getString(R.string.pref_proxypass); 406 final String keyPass = getString(R.string.pref_proxypass);
503 if (key.equals(ketHost) || key.equals(keyPort) || key.equals(keyUser) || k ey.equals(keyPass)) 407 if (key.equals(keyHost) || key.equals(keyPort) || key.equals(keyUser) || k ey.equals(keyPass))
504 { 408 {
505 final String proxyHost = sharedPreferences.getString(ketHost, null); 409 final String proxyHost = sharedPreferences.getString(keyHost, null);
506 final String proxyPort = sharedPreferences.getString(keyPort, null); 410 final String proxyPort = sharedPreferences.getString(keyPort, null);
507 final String proxyUser = sharedPreferences.getString(keyUser, null); 411 final String proxyUser = sharedPreferences.getString(keyUser, null);
508 final String proxyPass = sharedPreferences.getString(keyPass, null); 412 final String proxyPass = sharedPreferences.getString(keyPass, null);
509 if (proxy != null) 413 if (proxy != null)
510 { 414 {
511 configureUserProxy(proxyConfiguration, proxyHost, proxyPort, null, pro xyUser, proxyPass); 415 configureUserProxy(proxyConfiguration, proxyHost, proxyPort, null, pro xyUser, proxyPass);
512 proxy.restart(proxyConfiguration.getProperty("handler")); 416 proxy.restart(proxyConfiguration.getProperty("handler"));
513 } 417 }
514 } 418 }
515 } 419 }
516 } 420 }
517 421
518 public boolean isTransparent()
519 {
520 return transparent;
521 }
522
523 public boolean isNativeProxyAutoConfigured()
524 {
525 return nativeProxyAutoConfigured;
526 }
527
528 /**
529 * Checks if user has to set proxy settings manually
530 */
531 public boolean isManual()
532 {
533 return !transparent && !nativeProxyAutoConfigured;
534 }
535
536 /**
537 * Checks whether traffic check is pending
538 */
539 public boolean noTraffic()
540 {
541 return notrafficHandler != null;
542 }
543
544 /**
545 * Checks if specified host is local.
546 */
547 private static final boolean isLocalHost(final String host)
548 {
549 if (host == null)
550 return false;
551
552 try
553 {
554 if (host.equalsIgnoreCase("localhost"))
555 return true;
556
557 final String className = "android.net.NetworkUtils";
558 final Class<?> c = Class.forName(className);
559 /*
560 * InetAddress address = NetworkUtils.numericToInetAddress(host);
561 */
562 final Method method = c.getMethod("numericToInetAddress", String.class);
563 final InetAddress address = (InetAddress) method.invoke(null, host);
564
565 if (address.isLoopbackAddress())
566 return true;
567 }
568 catch (final Exception e)
569 {
570 Log.w(TAG, null, e);
571 }
572 return false;
573 }
574
575 /**
576 * Initializes iptables executable.
577 *
578 * @throws FileNotFoundException
579 * If iptables initialization failed due to provided reasons.
580 */
581 private void initIptables() throws IOException, RootToolsException, TimeoutExc eption, FileNotFoundException
582 {
583 if (!RootTools.isAccessGiven())
584 throw new FileNotFoundException("No root access");
585
586 final File ipt = getFileStreamPath("iptables");
587
588 if (!ipt.exists())
589 {
590 Log.e(TAG, "No iptables excutable found");
591 throw new FileNotFoundException("No iptables executable");
592 }
593
594 final String path = ipt.getAbsolutePath();
595
596 RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT);
597
598 boolean compatible = false;
599 boolean version = false;
600
601 final String command = path + " --version\n" + path + " -L -t nat -n\n";
602
603 final List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT);
604 for (final String line : result)
605 {
606 if (line.contains("OUTPUT"))
607 compatible = true;
608 if (line.contains("v1.4."))
609 version = true;
610 }
611
612 if (!compatible || !version)
613 {
614 Log.e(TAG, "Incompatible iptables excutable");
615 throw new FileNotFoundException("Incompatible iptables excutable");
616 }
617
618 iptables = path;
619 }
620
621 public List<String> getIptablesOutput()
622 {
623 if (iptables == null)
624 return null;
625
626 final String command = iptables + " -L -t nat -n\n";
627 try
628 {
629 return RootTools.sendShell(command, DEFAULT_TIMEOUT);
630 }
631 catch (final Exception e)
632 {
633 Log.e(TAG, "Failed to get iptables configuration", e);
634 return null;
635 }
636 }
637
638 /** 422 /**
639 * Raises or removes no traffic notification based on current link proxy 423 * @return the active proxy configuration's type or {@link ProxyRegistrationTy pe#UNKNOWN} if none is configured
640 * settings
641 */ 424 */
642 private void updateNoTrafficCheck(final ConnectivityManager connectivityManage r) 425 public ProxyRegistrationType getProxyRegistrationType()
643 { 426 {
644 try 427 return this.proxyConfigurator != null ? this.proxyConfigurator.getType() : P roxyRegistrationType.UNKNOWN;
645 {
646 final Object pp = ProxySettings.getActiveLinkProxy(connectivityManager);
647 final String[] userProxy = ProxySettings.getUserProxy(pp);
648 if (userProxy != null)
649 Log.i(TAG, "Proxy settings: " + userProxy[0] + ":" + userProxy[1] + "(" + userProxy[2] + ")");
650 updateNoTrafficCheck(userProxy);
651 }
652 catch (final Exception e)
653 {
654 // This should not happen
655 Log.e(TAG, null, e);
656 }
657 } 428 }
658 429
659 /** 430 /**
660 * Raises or removes no traffic notification based on the user proxy settings 431 * @return {@code true} if there is a valid proxy configurator and registratio n succeeded
661 */ 432 */
662 private void updateNoTrafficCheck(final String[] userProxy) 433 public boolean isRegistered()
663 { 434 {
664 final boolean ourProxy = userProxy != null && isLocalHost(userProxy[0]) && I nteger.valueOf(userProxy[1]) == port; 435 return this.proxyConfigurator != null && this.proxyConfigurator.isRegistered ();
665 if (ourProxy != proxyManualyConfigured)
666 {
667 proxyManualyConfigured = ourProxy;
668 sendStateChangedBroadcast();
669 }
670 if (ourProxy)
671 {
672 stopNoTrafficCheck();
673 }
674 else
675 {
676 // Initiate no traffic check
677 notrafficHandler = new Handler();
678 notrafficHandler.postDelayed(noTraffic, NO_TRAFFIC_TIMEOUT);
679 }
680 final NotificationManager notificationManager = (NotificationManager) getSys temService(NOTIFICATION_SERVICE);
681 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification());
682 } 436 }
683 437
684 /** 438 /**
685 * Stops no traffic check and resets notification message. 439 * @return {@code true} if registration type is {@link ProxyRegistrationType#R EFLECTION} and registration succeeded
686 */ 440 */
687 private void stopNoTrafficCheck() 441 public boolean isNativeProxyAutoConfigured()
688 { 442 {
689 if (notrafficHandler != null) 443 return this.getProxyRegistrationType() == ProxyRegistrationType.REFLECTION & & this.isRegistered();
690 { 444 }
691 notrafficHandler.removeCallbacks(noTraffic); 445
692 sendStateChangedBroadcast(); 446 /**
693 final NotificationManager notificationManager = (NotificationManager) getS ystemService(NOTIFICATION_SERVICE); 447 * @return {@code true} if registration type is {@link ProxyRegistrationType#M ANUAL}
694 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()); 448 */
695 notificationManager.cancel(NOTRAFFIC_NOTIFICATION_ID); 449 public boolean isManual()
696 } 450 {
697 notrafficHandler = null; 451 return this.getProxyRegistrationType() == ProxyRegistrationType.MANUAL;
698 } 452 }
699 453
700 @SuppressLint("NewApi") 454 @SuppressLint("NewApi")
701 private Notification getNotification() 455 private Notification getNotification()
702 { 456 {
703 final boolean filtering = AdblockPlus.getApplication().isFilteringEnabled(); 457 final boolean filtering = AdblockPlus.getApplication().isFilteringEnabled();
704 458
705 int msgId = R.string.notif_waiting; 459 final int msgId;
706 if (nativeProxyAutoConfigured || proxyManualyConfigured) 460 switch(this.getProxyRegistrationType())
461 {
462 case MANUAL:
463 if (this.isRegistered())
464 {
465 msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofilterin g;
466 }
467 else
468 {
469 msgId = filtering ? R.string.notif_waiting : R.string.notif_wifi_nofilte ring;
470 }
471 break;
472 case REFLECTION:
707 msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofiltering; 473 msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofiltering;
708 if (transparent) 474 break;
475 case IPTABLES:
476 case CYANOGENMOD:
709 msgId = R.string.notif_all; 477 msgId = R.string.notif_all;
478 break;
479 default:
480 msgId = R.string.notif_waiting;
481 break;
482 }
710 483
711 final NotificationCompat.Builder builder = new NotificationCompat.Builder(th is); 484 final NotificationCompat.Builder builder = new NotificationCompat.Builder(th is);
712 if (hideIcon && msgId != R.string.notif_waiting) 485 if (hideIcon && msgId != R.string.notif_waiting)
713 { 486 {
714 builder.setWhen(POSITION_RIGHT); 487 builder.setWhen(POSITION_RIGHT);
715 builder.setSmallIcon(R.drawable.transparent); 488 builder.setSmallIcon(R.drawable.transparent);
716 // builder.setContent(new RemoteViews(getPackageName(), R.layout.notif_hid den));
717 } 489 }
718 else 490 else
719 { 491 {
720 builder.setWhen(0); 492 builder.setWhen(0);
721 builder.setSmallIcon(R.drawable.ic_stat_blocking); 493 builder.setSmallIcon(R.drawable.ic_stat_blocking);
722 } 494 }
723 final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new I ntent(this, Preferences.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent. FLAG_ACTIVITY_NEW_TASK), 0); 495 final PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
496 new Intent(this, Preferences.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_ TOP | Intent.FLAG_ACTIVITY_NEW_TASK), 0);
497
724 builder.setContentIntent(contentIntent); 498 builder.setContentIntent(contentIntent);
725 builder.setContentTitle(getText(R.string.app_name)); 499 builder.setContentTitle(getText(R.string.app_name));
726 builder.setContentText(getString(msgId, port)); 500 builder.setContentText(getString(msgId, port));
727 builder.setOngoing(true); 501 builder.setOngoing(true);
728 502
729 final Notification notification = builder.getNotification(); 503 return builder.getNotification();
730 return notification;
731 } 504 }
732 505
733 public void setEmptyIcon(final boolean hide) 506 public void setEmptyIcon(final boolean hide)
734 { 507 {
735 hideIcon = hide; 508 hideIcon = hide;
736 final NotificationManager notificationManager = (NotificationManager) getSys temService(NOTIFICATION_SERVICE); 509 final NotificationManager notificationManager = (NotificationManager) getSys temService(NOTIFICATION_SERVICE);
737 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()); 510 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification());
738 } 511 }
739 512
740 public void sendStateChangedBroadcast() 513 public void sendStateChangedBroadcast()
741 { 514 {
742 Log.i(TAG, "Broadcasting " + BROADCAST_STATE_CHANGED);
743 final boolean manual = isManual(); 515 final boolean manual = isManual();
744 final Intent stateIntent = new Intent(BROADCAST_STATE_CHANGED).putExtra("ena bled", true).putExtra("port", port).putExtra("manual", manual); 516 final Intent stateIntent = new Intent(BROADCAST_STATE_CHANGED)
517 .putExtra("enabled", true)
518 .putExtra("port", port)
519 .putExtra("manual", manual);
520
745 if (manual) 521 if (manual)
746 stateIntent.putExtra("configured", proxyManualyConfigured); 522 {
523 stateIntent.putExtra("configured", this.isRegistered());
524 }
525
747 sendBroadcast(stateIntent); 526 sendBroadcast(stateIntent);
748 } 527 }
749 528
750 private final IBinder binder = new LocalBinder(); 529 private final IBinder binder = new LocalBinder();
751 530
752 public final class LocalBinder extends Binder 531 public final class LocalBinder extends Binder
753 { 532 {
754 public ProxyService getService() 533 public ProxyService getService()
755 { 534 {
756 return ProxyService.this; 535 return ProxyService.this;
757 } 536 }
758 } 537 }
759 538
760 @Override 539 @Override
761 public IBinder onBind(final Intent intent) 540 public IBinder onBind(final Intent intent)
762 { 541 {
763 return binder; 542 return binder;
764 } 543 }
765 544
766 /** 545 /**
767 * Executed if no traffic is detected after a period of time. Notifies user
768 * about possible configuration problems.
769 */
770 private final Runnable noTraffic = new Runnable()
771 {
772 @Override
773 public void run()
774 {
775 // It's weird but notrafficHandler.removeCallbacks(noTraffic) does not rem ove this callback
776 if (notrafficHandler == null)
777 return;
778 // Show warning notification
779 final NotificationCompat.Builder builder = new NotificationCompat.Builder( ProxyService.this);
780 builder.setSmallIcon(R.drawable.ic_stat_warning);
781 builder.setWhen(System.currentTimeMillis());
782 builder.setAutoCancel(true);
783 final Intent intent = new Intent(ProxyService.this, ConfigurationActivity. class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
784 intent.putExtra("port", port);
785 final PendingIntent contentIntent = PendingIntent.getActivity(ProxyService .this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
786 builder.setContentIntent(contentIntent);
787 builder.setContentTitle(getText(R.string.app_name));
788 builder.setContentText(getText(R.string.notif_notraffic));
789 final NotificationManager notificationManager = (NotificationManager) getS ystemService(NOTIFICATION_SERVICE);
790 notificationManager.notify(NOTRAFFIC_NOTIFICATION_ID, builder.getNotificat ion());
791 }
792 };
793
794 /**
795 * Stops no traffic check if traffic is detected by proxy service.
796 */
797 private final BroadcastReceiver filterReceiver = new BroadcastReceiver()
798 {
799 @Override
800 public void onReceive(final Context context, final Intent intent)
801 {
802 if (intent.getAction().equals(AdblockPlus.BROADCAST_FILTERING_CHANGE))
803 {
804 // It's rather a hack but things are happening simultaneously and we
805 // receive this broadcast despite the fact we have unsubscribed from
806 // it and notification is not removed because it is changed to new one
807 // during removal.
808 if (!isNativeProxyAutoConfigured())
809 {
810 final NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
811 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()) ;
812 }
813 }
814 if (intent.getAction().equals(AdblockPlus.BROADCAST_FILTER_MATCHES))
815 {
816 proxyManualyConfigured = true;
817 stopNoTrafficCheck();
818 }
819 }
820 };
821
822 /**
823 * Stops service if proxy fails. 546 * Stops service if proxy fails.
824 */ 547 */
825 private final BroadcastReceiver proxyReceiver = new BroadcastReceiver() 548 private final BroadcastReceiver proxyReceiver = new BroadcastReceiver()
826 { 549 {
827 @Override 550 @Override
828 public void onReceive(final Context context, final Intent intent) 551 public void onReceive(final Context context, final Intent intent)
829 { 552 {
830 if (intent.getAction().equals(ProxyService.BROADCAST_PROXY_FAILED)) 553 if (intent.getAction().equals(ProxyService.BROADCAST_PROXY_FAILED))
831 { 554 {
832 stopSelf(); 555 stopSelf();
833 } 556 }
834 } 557 }
835 }; 558 };
836 559
837 /** 560 /**
838 * Monitors system network connection settings changes and updates proxy 561 * <p>
839 * settings accordingly. 562 * A BR for simple message passing from various ProxyConfigurators.
563 * </p>
564 * <p>
565 * The purpose of this BroadcastReceiver is to allow other components to send notifications to the service and to be prepared for upcoming service
566 * refactorings.
567 * </p>
840 */ 568 */
841 private final BroadcastReceiver connectionReceiver = new BroadcastReceiver() 569 private final BroadcastReceiver commandBridge = new BroadcastReceiver()
842 { 570 {
843 @Override 571 @Override
844 public void onReceive(final Context ctx, final Intent intent) 572 public void onReceive(final Context context, final Intent intent)
845 { 573 {
846 final String action = intent.getAction(); 574 if (intent != null)
847 Log.i(TAG, "Action: " + action);
848 // Connectivity change
849 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action))
850 { 575 {
851 final ConnectivityManager connectivityManager = (ConnectivityManager) ge tSystemService(Context.CONNECTIVITY_SERVICE); 576 switch (BridgeCommand.fromString(intent.getStringExtra("command")))
852 // TODO Should we use ConnectivityManagerCompat.getNetworkInfoFromBroadc ast() instead?
853 final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
854 if (info == null)
855 return;
856 final String typeName = info.getTypeName();
857 final String subtypeName = info.getSubtypeName();
858 final boolean available = info.isAvailable();
859
860 Log.i(TAG, "Network Type: " + typeName + ", subtype: " + subtypeName + " , available: " + available);
861 if (info.getType() == ConnectivityManager.TYPE_WIFI)
862 { 577 {
863 if (nativeProxyAutoConfigured) 578 case STATE_CHANGED:
864 { 579 final NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
865 ProxySettings.setConnectionProxy(getApplicationContext(), LOCALHOST, port, ""); 580 notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()) ;
866 } 581 ProxyService.this.sendStateChangedBroadcast();
867 else 582 break;
868 { 583 default:
869 updateNoTrafficCheck(connectivityManager); 584 // ignore
870 } 585 break;
871 }
872 }
873 // Proxy change
874 else if (Proxy.PROXY_CHANGE_ACTION.equals(action))
875 {
876 final Object pp = intent.getParcelableExtra("proxy");
877 try
878 {
879 final String[] userProxy = ProxySettings.getUserProxy(pp);
880 if (nativeProxyAutoConfigured)
881 {
882 if (userProxy != null && Integer.valueOf(userProxy[1]) != port)
883 {
884 Log.i(TAG, "User has set new proxy: " + userProxy[0] + ":" + userP roxy[1] + "(" + userProxy[2] + ")");
885 if (proxy != null)
886 {
887 configureUserProxy(proxyConfiguration, userProxy[0], userProxy[1 ], userProxy[2], null, null);
888 proxy.restart(proxyConfiguration.getProperty("handler"));
889 }
890 }
891 }
892 else
893 {
894 Log.i(TAG, "User has set proxy: " + userProxy[0] + ":" + userProxy[1 ] + "(" + userProxy[2] + ")");
895 updateNoTrafficCheck(userProxy);
896 }
897 }
898 catch (final Exception e)
899 {
900 // This should not happen
901 Log.e(TAG, null, e);
902 } 586 }
903 } 587 }
904 } 588 }
905 }; 589 };
906 590
907 final class ProxyServer extends Server 591 final class ProxyServer extends Server
908 { 592 {
909 @Override 593 @Override
910 public void close() 594 public void close()
911 { 595 {
(...skipping 13 matching lines...) Expand all
925 @Override 609 @Override
926 public void log(final int level, final Object obj, final String message) 610 public void log(final int level, final Object obj, final String message)
927 { 611 {
928 if (level <= logLevel) 612 if (level <= logLevel)
929 { 613 {
930 Log.println(7 - level, obj != null ? obj.toString() : TAG, message); 614 Log.println(7 - level, obj != null ? obj.toString() : TAG, message);
931 } 615 }
932 } 616 }
933 } 617 }
934 } 618 }
OLDNEW

Powered by Google App Engine
This is Rietveld