| OLD | NEW | 
|---|
| 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   private static final boolean LOG_REQUESTS = false; | 
| 74   private static final boolean logRequests = 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 | 61 | 
| 83   static final int ONGOING_NOTIFICATION_ID = R.string.app_name; | 62   static final int ONGOING_NOTIFICATION_ID = R.string.app_name; | 
|  | 63 | 
| 84   private static final long POSITION_RIGHT = Build.VERSION.SDK_INT >= Build.VERS
     ION_CODES.GINGERBREAD ? Long.MIN_VALUE : Long.MAX_VALUE; | 64   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 | 65 | 
| 87   /** | 66   /** | 
| 88    * Broadcasted when service starts or stops. | 67    * Broadcasted when service starts or stops. | 
| 89    */ | 68    */ | 
| 90   public static final String BROADCAST_STATE_CHANGED = "org.adblockplus.android.
     service.state"; | 69   public static final String BROADCAST_STATE_CHANGED = "org.adblockplus.android.
     SERVICE_STATE_CHANGED"; | 
|  | 70 | 
| 91   /** | 71   /** | 
| 92    * Broadcasted if proxy fails to start. | 72    * Broadcasted if proxy fails to start. | 
| 93    */ | 73    */ | 
| 94   public static final String BROADCAST_PROXY_FAILED = "org.adblockplus.android.p
     roxy.failure"; | 74   public static final String BROADCAST_PROXY_FAILED = "org.adblockplus.android.P
     ROXY_FAILURE"; | 
| 95 | 75 | 
| 96   private static final String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{
     UID}} -A OUTPUT -p tcp -j RETURN\n"; | 76   /** | 
| 97   private static final String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dp
     ort 80 -j REDIRECT --to {{PORT}}\n"; | 77    * Common command bridge | 
|  | 78    */ | 
|  | 79   public static final String COMMAND_BRIDGE_ACTION = "org.adblockplus.android.CO
     MMAND_BRIDGE"; | 
| 98 | 80 | 
| 99   boolean hideIcon; | 81   boolean hideIcon; | 
| 100   private Handler notrafficHandler; |  | 
| 101 | 82 | 
| 102   protected ProxyServer proxy = null; | 83   protected ProxyServer proxy = null; | 
|  | 84 | 
| 103   protected int port; | 85   protected int port; | 
|  | 86 | 
| 104   private final Properties proxyConfiguration = new Properties(); | 87   private final Properties proxyConfiguration = new Properties(); | 
| 105 | 88 | 
| 106   /** | 89   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 | 90 | 
| 121   @SuppressLint("NewApi") | 91   @SuppressLint("NewApi") | 
| 122   @Override | 92   @Override | 
| 123   public void onCreate() | 93   public void onCreate() | 
| 124   { | 94   { | 
| 125     super.onCreate(); | 95     super.onCreate(); | 
| 126 | 96 | 
| 127     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) | 97     // FIXME 'StrictMode' is API9 | 
| 128     { | 98     StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() | 
| 129       // Proxy is running in separate thread, it's just some resolution request 
     during initialization. | 99         .permitAll() | 
| 130       // Not worth spawning a separate thread for this. | 100         .penaltyLog() | 
| 131       final StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder
     ().permitNetwork().build(); | 101         .build()); | 
| 132       StrictMode.setThreadPolicy(policy); |  | 
| 133     } |  | 
| 134 | 102 | 
| 135     // Get port for local proxy |  | 
| 136     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreference
     s(this); | 103     final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreference
     s(this); | 
| 137     final Resources resources = getResources(); |  | 
| 138 | 104 | 
| 139     // Try to read user proxy settings | 105     // Try to read user proxy settings | 
| 140     String proxyHost = null; | 106     String proxyHost = System.getProperty("http.proxyHost");; | 
| 141     String proxyPort = null; | 107     String proxyPort = System.getProperty("http.proxyPort"); | 
| 142     String proxyExcl = null; | 108     final String proxyExcl = System.getProperty("http.nonProxyHosts"); | 
| 143     String proxyUser = null; | 109     String proxyUser = null; | 
| 144     String proxyPass = null; | 110     String proxyPass = null; | 
| 145 | 111 | 
| 146     if (NATIVE_PROXY_SUPPORTED) | 112     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     { | 113     { | 
| 161       // Read application settings | 114       // Read application settings | 
| 162       proxyHost = prefs.getString(getString(R.string.pref_proxyhost), null); | 115       proxyHost = prefs.getString(getString(R.string.pref_proxyhost), null); | 
| 163       proxyPort = prefs.getString(getString(R.string.pref_proxyport), null); | 116       proxyPort = prefs.getString(getString(R.string.pref_proxyport), null); | 
| 164       proxyUser = prefs.getString(getString(R.string.pref_proxyuser), null); | 117       proxyUser = prefs.getString(getString(R.string.pref_proxyuser), null); | 
| 165       proxyPass = prefs.getString(getString(R.string.pref_proxypass), null); | 118       proxyPass = prefs.getString(getString(R.string.pref_proxypass), null); | 
| 166     } | 119     } | 
| 167 | 120 | 
| 168     // Check for root privileges and try to install transparent proxy | 121     registerReceiver(this.proxyReceiver, new IntentFilter(ProxyService.BROADCAST
     _PROXY_FAILED)); | 
| 169     if (RootTools.isAccessGiven()) | 122     registerReceiver(this.commandBridge, new IntentFilter(ProxyService.COMMAND_B
     RIDGE_ACTION)); | 
|  | 123 | 
|  | 124     final InetAddress inetAddress; | 
|  | 125     try | 
| 170     { | 126     { | 
| 171       try | 127       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     } | 128     } | 
| 204 | 129     catch (final UnknownHostException e) | 
| 205     if (!transparent) |  | 
| 206     { | 130     { | 
| 207       // Try to set native proxy | 131       sendBroadcast(new Intent(BROADCAST_PROXY_FAILED) | 
| 208       nativeProxyAutoConfigured = ProxySettings.setConnectionProxy(getApplicatio
     nContext(), LOCALHOST, port, ""); | 132           .putExtra("msg", "Could not resolve 'localhost'")); | 
| 209 | 133       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     } | 134     } | 
| 216 | 135 | 
| 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 | 136     // Start proxy | 
| 228     if (proxy == null) | 137     if (proxy == null) | 
| 229     { | 138     { | 
| 230       // Select available port and bind to it, use previously selected port by d
     efault | 139       // 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; | 140       ServerSocket listen = null; | 
| 233       String msg = null; | 141       String msg = null; | 
| 234       for (final int p : portVariants) | 142       for (final int p : PORT_VARIANTS) | 
| 235       { | 143       { | 
| 236         if (p < 0) | 144         final int toCheck = (p == -1) ? prefs.getInt(getString(R.string.pref_las
     tport), -1) : p; | 
| 237           continue; | 145 | 
| 238         try | 146         if (toCheck >= 0) | 
| 239         { | 147         { | 
| 240           // Fix for #232, bind proxy socket to loopback only | 148           try | 
| 241           listen = new ServerSocket(p, 1024, InetAddress.getByName(LOCALHOST)); | 149           { | 
| 242           port = p; | 150             // Fix for #232, bind proxy socket to loopback only | 
| 243           break; | 151             listen = new ServerSocket(toCheck, 1024, inetAddress); | 
| 244         } | 152             this.port = listen.getLocalPort(); | 
| 245         catch (final IOException e) | 153             break; | 
| 246         { | 154           } | 
| 247           Log.e(TAG, null, e); | 155           catch (final IOException e) | 
| 248           msg = e.getMessage(); | 156           { | 
|  | 157             Log.e(TAG, null, e); | 
|  | 158             msg = e.getMessage(); | 
|  | 159           } | 
| 249         } | 160         } | 
| 250       } | 161       } | 
|  | 162 | 
| 251       if (listen == null) | 163       if (listen == null) | 
| 252       { | 164       { | 
| 253         sendBroadcast(new Intent(BROADCAST_PROXY_FAILED).putExtra("msg", msg)); | 165         sendBroadcast(new Intent(BROADCAST_PROXY_FAILED).putExtra("msg", msg)); | 
| 254         return; | 166         return; | 
| 255       } | 167       } | 
| 256 | 168 | 
|  | 169       this.proxyConfigurator = ProxyConfigurators.registerProxy(this, inetAddres
     s, this.port); | 
|  | 170 | 
|  | 171       if (this.proxyConfigurator == null) | 
|  | 172       { | 
|  | 173         sendBroadcast(new Intent(BROADCAST_PROXY_FAILED) | 
|  | 174             .putExtra("msg", "Failed to register proxy")); | 
|  | 175         return; | 
|  | 176       } | 
|  | 177 | 
| 257       // Save selected port | 178       // Save selected port | 
|  | 179       final SharedPreferences.Editor editor = prefs.edit(); | 
|  | 180       editor.putBoolean(getString(R.string.pref_proxyautoconfigured), this.getPr
     oxyRegistrationType().isAutoConfigured()); | 
| 258       editor.putInt(getString(R.string.pref_lastport), port); | 181       editor.putInt(getString(R.string.pref_lastport), port); | 
| 259       editor.commit(); | 182       editor.commit(); | 
| 260 | 183 | 
| 261       // Initialize proxy | 184       // Initialize proxy | 
| 262       proxyConfiguration.put("handler", "main"); | 185       proxyConfiguration.put("handler", "main"); | 
| 263       proxyConfiguration.put("main.prefix", ""); | 186       proxyConfiguration.put("main.prefix", ""); | 
| 264       proxyConfiguration.put("main.class", "sunlabs.brazil.server.ChainHandler")
     ; | 187       proxyConfiguration.put("main.class", "sunlabs.brazil.server.ChainHandler")
     ; | 
| 265       if (transparent) | 188       switch (this.getProxyRegistrationType().getProxyType()) | 
| 266       { | 189       { | 
|  | 190       case HTTP: | 
| 267         proxyConfiguration.put("main.handlers", "urlmodifier adblock"); | 191         proxyConfiguration.put("main.handlers", "urlmodifier adblock"); | 
| 268         proxyConfiguration.put("urlmodifier.class", "org.adblockplus.brazil.Tran
     sparentProxyHandler"); | 192         proxyConfiguration.put("urlmodifier.class", "org.adblockplus.brazil.Tran
     sparentProxyHandler"); | 
| 269       } | 193         break; | 
| 270       else | 194       case HTTPS: | 
| 271       { |  | 
| 272         proxyConfiguration.put("main.handlers", "https adblock"); | 195         proxyConfiguration.put("main.handlers", "https adblock"); | 
| 273         proxyConfiguration.put("https.class", "org.adblockplus.brazil.SSLConnect
     ionHandler"); | 196         proxyConfiguration.put("https.class", "org.adblockplus.brazil.SSLConnect
     ionHandler"); | 
|  | 197         break; | 
|  | 198       default: | 
|  | 199         sendBroadcast(new Intent(BROADCAST_PROXY_FAILED) | 
|  | 200             .putExtra("msg", "Unsupported proxy server type: " + this.getProxyRe
     gistrationType().getProxyType())); | 
|  | 201         return; | 
| 274       } | 202       } | 
| 275       proxyConfiguration.put("adblock.class", "org.adblockplus.brazil.RequestHan
     dler"); | 203       proxyConfiguration.put("adblock.class", "org.adblockplus.brazil.RequestHan
     dler"); | 
| 276       if (logRequests) | 204       if (LOG_REQUESTS) | 
| 277         proxyConfiguration.put("adblock.proxylog", "yes"); | 205         proxyConfiguration.put("adblock.proxylog", "yes"); | 
| 278 | 206 | 
| 279       configureUserProxy(proxyConfiguration, proxyHost, proxyPort, proxyExcl, pr
     oxyUser, proxyPass); | 207       configureUserProxy(proxyConfiguration, proxyHost, proxyPort, proxyExcl, pr
     oxyUser, proxyPass); | 
| 280 | 208 | 
| 281       proxy = new ProxyServer(); | 209       proxy = new ProxyServer(); | 
| 282       proxy.logLevel = Server.LOG_DIAGNOSTIC; | 210       proxy.logLevel = Server.LOG_LOG; | 
| 283       proxy.setup(listen, proxyConfiguration.getProperty("handler"), proxyConfig
     uration); | 211       proxy.setup(listen, proxyConfiguration.getProperty("handler"), proxyConfig
     uration); | 
| 284       proxy.start(); | 212       proxy.start(); | 
| 285     } | 213     } | 
| 286 | 214 | 
| 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 |  | 
| 316     prefs.registerOnSharedPreferenceChangeListener(this); | 215     prefs.registerOnSharedPreferenceChangeListener(this); | 
| 317 | 216 | 
| 318     // Lock service | 217     // Lock service | 
| 319     hideIcon = prefs.getBoolean(getString(R.string.pref_hideicon), resources.get
     Boolean(R.bool.def_hideicon)); | 218     hideIcon = prefs.getBoolean(getString(R.string.pref_hideicon), getResources(
     ).getBoolean(R.bool.def_hideicon)); | 
| 320     startForeground(ONGOING_NOTIFICATION_ID, getNotification()); | 219     startForeground(ONGOING_NOTIFICATION_ID, getNotification()); | 
| 321 | 220 | 
| 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(); | 221     sendStateChangedBroadcast(); | 
| 331     Log.i(TAG, "Service started"); | 222     Log.i(TAG, "Service started"); | 
| 332   } | 223   } | 
| 333 | 224 | 
| 334   @Override | 225   @Override | 
| 335   public int onStartCommand(final Intent intent, final int flags, final int star
     tId) | 226   public int onStartCommand(final Intent intent, final int flags, final int star
     tId) | 
| 336   { | 227   { | 
| 337     return START_STICKY; | 228     return START_STICKY; | 
| 338   } | 229   } | 
| 339 | 230 | 
| 340   @Override | 231   @Override | 
| 341   public void onDestroy() | 232   public void onDestroy() | 
| 342   { | 233   { | 
| 343     super.onDestroy(); | 234     super.onDestroy(); | 
| 344 | 235 | 
| 345     stopNoTrafficCheck(); | 236     unregisterReceiver(this.proxyReceiver); | 
|  | 237     unregisterReceiver(this.commandBridge); | 
| 346 | 238 | 
| 347     unregisterReceiver(filterReceiver); | 239     if (this.proxyConfigurator != null) | 
| 348     unregisterReceiver(proxyReceiver); |  | 
| 349 |  | 
| 350     // Stop IP redirecting |  | 
| 351     if (transparent) |  | 
| 352     { | 240     { | 
| 353       new Thread() | 241       this.proxyConfigurator.unregisterProxy(); | 
| 354       { | 242       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     } | 243     } | 
| 378 | 244 | 
| 379     sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", false)
     ); | 245     sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", false)
     ); | 
| 380 | 246 | 
| 381     // Stop proxy server | 247     // Stop proxy server | 
| 382     if (proxy != null) | 248     if (proxy != null) | 
|  | 249     { | 
| 383       proxy.close(); | 250       proxy.close(); | 
|  | 251       proxy = null; | 
|  | 252     } | 
| 384 | 253 | 
| 385     // Release service lock | 254     // Release service lock | 
| 386     stopForeground(true); | 255     stopForeground(true); | 
| 387 | 256 | 
| 388     Log.i(TAG, "Service stopped"); | 257     Log.i(TAG, "Service stopped"); | 
| 389   } | 258   } | 
| 390 | 259 | 
| 391   /** | 260   /** | 
| 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. | 261    * Sets user proxy settings in proxy service properties. | 
| 415    */ | 262    */ | 
| 416   private void configureUserProxy(final Properties config, final String proxyHos
     t, final String proxyPort, final String proxyExcl, final String proxyUser, final
      String proxyPass) | 263   private void configureUserProxy(final Properties config, final String proxyHos
     t, final String proxyPort, final String proxyExcl, | 
|  | 264       final String proxyUser, final String proxyPass) | 
| 417   { | 265   { | 
| 418     // Clean previous settings | 266     // Clean previous settings | 
| 419     config.remove("adblock.proxyHost"); | 267     config.remove("adblock.proxyHost"); | 
| 420     config.remove("adblock.proxyPort"); | 268     config.remove("adblock.proxyPort"); | 
| 421     config.remove("adblock.auth"); | 269     config.remove("adblock.auth"); | 
| 422     config.remove("adblock.proxyExcl"); | 270     config.remove("adblock.proxyExcl"); | 
| 423     if (!transparent) | 271     config.remove("https.proxyHost"); | 
|  | 272     config.remove("https.proxyPort"); | 
|  | 273     config.remove("https.auth"); | 
|  | 274 | 
|  | 275     final ProxyRegistrationType regType = this.getProxyRegistrationType(); | 
|  | 276     if (regType == ProxyRegistrationType.NATIVE) | 
| 424     { | 277     { | 
| 425       config.remove("https.proxyHost"); | 278       passProxySettings(proxyHost, proxyPort, proxyExcl); | 
| 426       config.remove("https.proxyPort"); |  | 
| 427       config.remove("https.auth"); |  | 
| 428     } | 279     } | 
| 429 | 280 | 
| 430     if (nativeProxyAutoConfigured) |  | 
| 431       passProxySettings(proxyHost, proxyPort, proxyExcl); |  | 
| 432 |  | 
| 433     // Check if there are any settings | 281     // Check if there are any settings | 
| 434     if (proxyHost == null || "".equals(proxyHost)) | 282     if (StringUtils.isEmpty(proxyHost)) | 
|  | 283     { | 
| 435       return; | 284       return; | 
|  | 285     } | 
| 436 | 286 | 
| 437     // Check for dirty proxy settings - this indicated previous crash: | 287     // Check for dirty proxy settings - this indicated previous crash: | 
| 438     // proxy points to ourselves | 288     // proxy points to ourselves | 
| 439     // proxy port is null, 0 or not a number | 289     // proxy port is null, 0 or not a number | 
| 440     // proxy is 127.0.0.1:8080 | 290     // proxy is 127.0.0.1:8080 | 
| 441     if (proxyPort == null) | 291     if (proxyPort == null) | 
|  | 292     { | 
| 442       return; | 293       return; | 
| 443     int p = 0; | 294     } | 
|  | 295 | 
|  | 296     final int p; | 
| 444     try | 297     try | 
| 445     { | 298     { | 
| 446       p = Integer.valueOf(proxyPort); | 299       p = Integer.valueOf(proxyPort); | 
| 447     } | 300     } | 
| 448     catch (final NumberFormatException e) | 301     catch (final NumberFormatException e) | 
| 449     { | 302     { | 
| 450       return; | 303       return; | 
| 451     } | 304     } | 
| 452     if (p == 0 || isLocalHost(proxyHost) && (p == port || p == 8080)) | 305 | 
|  | 306     if (p == 0 || isLocalhost(proxyHost) && (p == port || p == 8080)) | 
| 453     { | 307     { | 
| 454       if (nativeProxyAutoConfigured) | 308       if (regType == ProxyRegistrationType.NATIVE) | 
|  | 309       { | 
| 455         passProxySettings(null, null, null); | 310         passProxySettings(null, null, null); | 
|  | 311       } | 
| 456       return; | 312       return; | 
| 457     } | 313     } | 
| 458 | 314 | 
| 459     config.put("adblock.proxyHost", proxyHost); | 315     config.put("adblock.proxyHost", proxyHost); | 
| 460     config.put("adblock.proxyPort", proxyPort); | 316     config.put("adblock.proxyPort", proxyPort); | 
| 461     if (!transparent) | 317     if (regType.getProxyType() == ProxyServerType.HTTPS) | 
| 462     { | 318     { | 
| 463       config.put("https.proxyHost", proxyHost); | 319       config.put("https.proxyHost", proxyHost); | 
| 464       config.put("https.proxyPort", proxyPort); | 320       config.put("https.proxyPort", proxyPort); | 
| 465     } | 321     } | 
| 466 | 322 | 
| 467     // TODO Not implemented in our proxy but needed to restore settings | 323     // TODO Not implemented in our proxy but needed to restore settings | 
| 468     if (proxyExcl != null) | 324     if (proxyExcl != null) | 
|  | 325     { | 
| 469       config.put("adblock.proxyExcl", proxyExcl); | 326       config.put("adblock.proxyExcl", proxyExcl); | 
|  | 327     } | 
| 470 | 328 | 
| 471     if (proxyUser != null && !"".equals(proxyUser) && proxyPass != null && !"".e
     quals(proxyPass)) | 329     if (StringUtils.isNotEmpty(proxyUser) && StringUtils.isNotEmpty(proxyPass)) | 
| 472     { | 330     { | 
| 473       // Base64 encode user:password | 331       // Base64 encode user:password | 
| 474       final String proxyAuth = "Basic " + new String(Base64.encode(proxyUser + "
     :" + proxyPass)); | 332       final String proxyAuth = "Basic " + new String(Base64.encode(proxyUser + "
     :" + proxyPass)); | 
| 475       config.put("adblock.auth", proxyAuth); | 333       config.put("adblock.auth", proxyAuth); | 
| 476       if (!transparent) | 334       if (regType.getProxyType() == ProxyServerType.HTTPS) | 
|  | 335       { | 
| 477         config.put("https.auth", proxyAuth); | 336         config.put("https.auth", proxyAuth); | 
|  | 337       } | 
| 478     } | 338     } | 
| 479   } | 339   } | 
| 480 | 340 | 
|  | 341   /** | 
|  | 342    * Lean method to send a {@link BridgeCommand} without additional extras. | 
|  | 343    * | 
|  | 344    * @param context | 
|  | 345    *          the context to use for sending the broadcast | 
|  | 346    * @param command | 
|  | 347    *          the command | 
|  | 348    */ | 
|  | 349   public static void sendBridgeCommand(final Context context, final BridgeComman
     d command) | 
|  | 350   { | 
|  | 351     context.sendBroadcast( | 
|  | 352         new Intent(COMMAND_BRIDGE_ACTION) | 
|  | 353             .putExtra("command", command.toString())); | 
|  | 354   } | 
|  | 355 | 
|  | 356   /** | 
|  | 357    * @return {@code true} if the given host string resolves to {@code localhost} | 
|  | 358    */ | 
|  | 359   public static boolean isLocalhost(final String host) | 
|  | 360   { | 
|  | 361     try | 
|  | 362     { | 
|  | 363       if (StringUtils.isEmpty(host)) | 
|  | 364       { | 
|  | 365         return false; | 
|  | 366       } | 
|  | 367 | 
|  | 368       if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost")) | 
|  | 369       { | 
|  | 370         return true; | 
|  | 371       } | 
|  | 372 | 
|  | 373       return InetAddress.getByName(host).isLoopbackAddress(); | 
|  | 374     } | 
|  | 375     catch (final Exception e) | 
|  | 376     { | 
|  | 377       return false; | 
|  | 378     } | 
|  | 379   } | 
|  | 380 | 
| 481   private void passProxySettings(final String proxyHost, final String proxyPort,
      final String proxyExcl) | 381   private void passProxySettings(final String proxyHost, final String proxyPort,
      final String proxyExcl) | 
| 482   { | 382   { | 
| 483     try | 383     try | 
| 484     { | 384     { | 
| 485       final CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExcep
     tionHandler(); | 385       final CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExcep
     tionHandler(); | 
| 486       handler.saveProxySettings(proxyHost, proxyPort, proxyExcl); | 386       handler.saveProxySettings(proxyHost, proxyPort, proxyExcl); | 
| 487     } | 387     } | 
| 488     catch (final ClassCastException e) | 388     catch (final ClassCastException e) | 
| 489     { | 389     { | 
| 490       // ignore - default handler in use | 390       // ignore - default handler in use | 
| 491     } | 391     } | 
| 492   } | 392   } | 
| 493 | 393 | 
| 494   @Override | 394   @Override | 
| 495   public void onSharedPreferenceChanged(final SharedPreferences sharedPreference
     s, final String key) | 395   public void onSharedPreferenceChanged(final SharedPreferences sharedPreference
     s, final String key) | 
| 496   { | 396   { | 
| 497     if (!NATIVE_PROXY_SUPPORTED) | 397     // TODO verify this | 
|  | 398     if (this.getProxyRegistrationType() != ProxyRegistrationType.NATIVE) | 
| 498     { | 399     { | 
| 499       final String ketHost = getString(R.string.pref_proxyhost); | 400       final String keyHost = getString(R.string.pref_proxyhost); | 
| 500       final String keyPort = getString(R.string.pref_proxyport); | 401       final String keyPort = getString(R.string.pref_proxyport); | 
| 501       final String keyUser = getString(R.string.pref_proxyuser); | 402       final String keyUser = getString(R.string.pref_proxyuser); | 
| 502       final String keyPass = getString(R.string.pref_proxypass); | 403       final String keyPass = getString(R.string.pref_proxypass); | 
| 503       if (key.equals(ketHost) || key.equals(keyPort) || key.equals(keyUser) || k
     ey.equals(keyPass)) | 404       if (key.equals(keyHost) || key.equals(keyPort) || key.equals(keyUser) || k
     ey.equals(keyPass)) | 
| 504       { | 405       { | 
| 505         final String proxyHost = sharedPreferences.getString(ketHost, null); | 406         final String proxyHost = sharedPreferences.getString(keyHost, null); | 
| 506         final String proxyPort = sharedPreferences.getString(keyPort, null); | 407         final String proxyPort = sharedPreferences.getString(keyPort, null); | 
| 507         final String proxyUser = sharedPreferences.getString(keyUser, null); | 408         final String proxyUser = sharedPreferences.getString(keyUser, null); | 
| 508         final String proxyPass = sharedPreferences.getString(keyPass, null); | 409         final String proxyPass = sharedPreferences.getString(keyPass, null); | 
| 509         if (proxy != null) | 410         if (proxy != null) | 
| 510         { | 411         { | 
| 511           configureUserProxy(proxyConfiguration, proxyHost, proxyPort, null, pro
     xyUser, proxyPass); | 412           configureUserProxy(proxyConfiguration, proxyHost, proxyPort, null, pro
     xyUser, proxyPass); | 
| 512           proxy.restart(proxyConfiguration.getProperty("handler")); | 413           proxy.restart(proxyConfiguration.getProperty("handler")); | 
| 513         } | 414         } | 
| 514       } | 415       } | 
| 515     } | 416     } | 
| 516   } | 417   } | 
| 517 | 418 | 
| 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   /** | 419   /** | 
| 639    * Raises or removes no traffic notification based on current link proxy | 420    * @return the active proxy configuration's type or {@link ProxyRegistrationTy
     pe#UNKNOWN} if none is configured | 
| 640    * settings |  | 
| 641    */ | 421    */ | 
| 642   private void updateNoTrafficCheck(final ConnectivityManager connectivityManage
     r) | 422   public ProxyRegistrationType getProxyRegistrationType() | 
| 643   { | 423   { | 
| 644     try | 424     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   } | 425   } | 
| 658 | 426 | 
| 659   /** | 427   /** | 
| 660    * Raises or removes no traffic notification based on the user proxy settings | 428    * @return {@code true} if there is a valid proxy configurator and registratio
     n succeeded | 
| 661    */ | 429    */ | 
| 662   private void updateNoTrafficCheck(final String[] userProxy) | 430   public boolean isRegistered() | 
| 663   { | 431   { | 
| 664     final boolean ourProxy = userProxy != null && isLocalHost(userProxy[0]) && I
     nteger.valueOf(userProxy[1]) == port; | 432     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   } | 433   } | 
| 683 | 434 | 
| 684   /** | 435   /** | 
| 685    * Stops no traffic check and resets notification message. | 436    * @return {@code true} if registration type is {@link ProxyRegistrationType#N
     ATIVE} and registration succeeded | 
| 686    */ | 437    */ | 
| 687   private void stopNoTrafficCheck() | 438   public boolean isNativeProxyAutoConfigured() | 
| 688   { | 439   { | 
| 689     if (notrafficHandler != null) | 440     return this.getProxyRegistrationType() == ProxyRegistrationType.NATIVE && th
     is.isRegistered(); | 
| 690     { | 441   } | 
| 691       notrafficHandler.removeCallbacks(noTraffic); | 442 | 
| 692       sendStateChangedBroadcast(); | 443   /** | 
| 693       final NotificationManager notificationManager = (NotificationManager) getS
     ystemService(NOTIFICATION_SERVICE); | 444    * @return {@code true} if registration type is {@link ProxyRegistrationType#M
     ANUAL} | 
| 694       notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()); | 445    */ | 
| 695       notificationManager.cancel(NOTRAFFIC_NOTIFICATION_ID); | 446   public boolean isManual() | 
| 696     } | 447   { | 
| 697     notrafficHandler = null; | 448     return this.getProxyRegistrationType() == ProxyRegistrationType.MANUAL; | 
| 698   } | 449   } | 
| 699 | 450 | 
| 700   @SuppressLint("NewApi") | 451   @SuppressLint("NewApi") | 
| 701   private Notification getNotification() | 452   private Notification getNotification() | 
| 702   { | 453   { | 
| 703     final boolean filtering = AdblockPlus.getApplication().isFilteringEnabled(); | 454     final boolean filtering = AdblockPlus.getApplication().isFilteringEnabled(); | 
| 704 | 455 | 
| 705     int msgId = R.string.notif_waiting; | 456     final int msgId; | 
| 706     if (nativeProxyAutoConfigured || proxyManualyConfigured) | 457     switch(this.getProxyRegistrationType()) | 
|  | 458     { | 
|  | 459     case MANUAL: | 
|  | 460       if (this.isRegistered()) | 
|  | 461       { | 
|  | 462         msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofilterin
     g; | 
|  | 463       } | 
|  | 464       else | 
|  | 465       { | 
|  | 466         msgId = filtering ? R.string.notif_waiting : R.string.notif_wifi_nofilte
     ring; | 
|  | 467       } | 
|  | 468       break; | 
|  | 469     case NATIVE: | 
| 707       msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofiltering; | 470       msgId = filtering ? R.string.notif_wifi : R.string.notif_wifi_nofiltering; | 
| 708     if (transparent) | 471       break; | 
|  | 472     case IPTABLES: | 
|  | 473     case CYANOGENMOD: | 
| 709       msgId = R.string.notif_all; | 474       msgId = R.string.notif_all; | 
|  | 475       break; | 
|  | 476     default: | 
|  | 477       msgId = R.string.notif_waiting; | 
|  | 478       break; | 
|  | 479     } | 
| 710 | 480 | 
| 711     final NotificationCompat.Builder builder = new NotificationCompat.Builder(th
     is); | 481     final NotificationCompat.Builder builder = new NotificationCompat.Builder(th
     is); | 
| 712     if (hideIcon && msgId != R.string.notif_waiting) | 482     if (hideIcon && msgId != R.string.notif_waiting) | 
| 713     { | 483     { | 
| 714       builder.setWhen(POSITION_RIGHT); | 484       builder.setWhen(POSITION_RIGHT); | 
| 715       builder.setSmallIcon(R.drawable.transparent); | 485       builder.setSmallIcon(R.drawable.transparent); | 
| 716       // builder.setContent(new RemoteViews(getPackageName(), R.layout.notif_hid
     den)); |  | 
| 717     } | 486     } | 
| 718     else | 487     else | 
| 719     { | 488     { | 
| 720       builder.setWhen(0); | 489       builder.setWhen(0); | 
| 721       builder.setSmallIcon(R.drawable.ic_stat_blocking); | 490       builder.setSmallIcon(R.drawable.ic_stat_blocking); | 
| 722     } | 491     } | 
| 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); | 492     final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, | 
|  | 493         new Intent(this, Preferences.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_
     TOP | Intent.FLAG_ACTIVITY_NEW_TASK), 0); | 
|  | 494 | 
| 724     builder.setContentIntent(contentIntent); | 495     builder.setContentIntent(contentIntent); | 
| 725     builder.setContentTitle(getText(R.string.app_name)); | 496     builder.setContentTitle(getText(R.string.app_name)); | 
| 726     builder.setContentText(getString(msgId, port)); | 497     builder.setContentText(getString(msgId, port)); | 
| 727     builder.setOngoing(true); | 498     builder.setOngoing(true); | 
| 728 | 499 | 
| 729     final Notification notification = builder.getNotification(); | 500     return builder.getNotification(); | 
| 730     return notification; |  | 
| 731   } | 501   } | 
| 732 | 502 | 
| 733   public void setEmptyIcon(final boolean hide) | 503   public void setEmptyIcon(final boolean hide) | 
| 734   { | 504   { | 
| 735     hideIcon = hide; | 505     hideIcon = hide; | 
| 736     final NotificationManager notificationManager = (NotificationManager) getSys
     temService(NOTIFICATION_SERVICE); | 506     final NotificationManager notificationManager = (NotificationManager) getSys
     temService(NOTIFICATION_SERVICE); | 
| 737     notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()); | 507     notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification()); | 
| 738   } | 508   } | 
| 739 | 509 | 
| 740   public void sendStateChangedBroadcast() | 510   public void sendStateChangedBroadcast() | 
| 741   { | 511   { | 
| 742     Log.i(TAG, "Broadcasting " + BROADCAST_STATE_CHANGED); |  | 
| 743     final boolean manual = isManual(); | 512     final boolean manual = isManual(); | 
| 744     final Intent stateIntent = new Intent(BROADCAST_STATE_CHANGED).putExtra("ena
     bled", true).putExtra("port", port).putExtra("manual", manual); | 513     final Intent stateIntent = new Intent(BROADCAST_STATE_CHANGED) | 
|  | 514         .putExtra("enabled", true) | 
|  | 515         .putExtra("port", port) | 
|  | 516         .putExtra("manual", manual); | 
|  | 517 | 
| 745     if (manual) | 518     if (manual) | 
| 746       stateIntent.putExtra("configured", proxyManualyConfigured); | 519     { | 
|  | 520       stateIntent.putExtra("configured", this.isRegistered()); | 
|  | 521     } | 
|  | 522 | 
| 747     sendBroadcast(stateIntent); | 523     sendBroadcast(stateIntent); | 
| 748   } | 524   } | 
| 749 | 525 | 
| 750   private final IBinder binder = new LocalBinder(); | 526   private final IBinder binder = new LocalBinder(); | 
| 751 | 527 | 
| 752   public final class LocalBinder extends Binder | 528   public final class LocalBinder extends Binder | 
| 753   { | 529   { | 
| 754     public ProxyService getService() | 530     public ProxyService getService() | 
| 755     { | 531     { | 
| 756       return ProxyService.this; | 532       return ProxyService.this; | 
| 757     } | 533     } | 
| 758   } | 534   } | 
| 759 | 535 | 
| 760   @Override | 536   @Override | 
| 761   public IBinder onBind(final Intent intent) | 537   public IBinder onBind(final Intent intent) | 
| 762   { | 538   { | 
| 763     return binder; | 539     return binder; | 
| 764   } | 540   } | 
| 765 | 541 | 
| 766   /** | 542   /** | 
| 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. | 543    * Stops service if proxy fails. | 
| 824    */ | 544    */ | 
| 825   private final BroadcastReceiver proxyReceiver = new BroadcastReceiver() | 545   private final BroadcastReceiver proxyReceiver = new BroadcastReceiver() | 
| 826   { | 546   { | 
| 827     @Override | 547     @Override | 
| 828     public void onReceive(final Context context, final Intent intent) | 548     public void onReceive(final Context context, final Intent intent) | 
| 829     { | 549     { | 
| 830       if (intent.getAction().equals(ProxyService.BROADCAST_PROXY_FAILED)) | 550       if (intent.getAction().equals(ProxyService.BROADCAST_PROXY_FAILED)) | 
| 831       { | 551       { | 
| 832         stopSelf(); | 552         stopSelf(); | 
| 833       } | 553       } | 
| 834     } | 554     } | 
| 835   }; | 555   }; | 
| 836 | 556 | 
| 837   /** | 557   /** | 
| 838    * Monitors system network connection settings changes and updates proxy | 558    * <p> | 
| 839    * settings accordingly. | 559    * A BR for simple message passing from various ProxyConfigurators. | 
|  | 560    * </p> | 
|  | 561    * <p> | 
|  | 562    * The purpose of this BroadcastReceiver is to allow other components to send 
     notifications to the service and to be prepared for upcoming service | 
|  | 563    * refactorings. | 
|  | 564    * </p> | 
| 840    */ | 565    */ | 
| 841   private final BroadcastReceiver connectionReceiver = new BroadcastReceiver() | 566   private final BroadcastReceiver commandBridge = new BroadcastReceiver() | 
| 842   { | 567   { | 
| 843     @Override | 568     @Override | 
| 844     public void onReceive(final Context ctx, final Intent intent) | 569     public void onReceive(final Context context, final Intent intent) | 
| 845     { | 570     { | 
| 846       final String action = intent.getAction(); | 571       if (intent != null) | 
| 847       Log.i(TAG, "Action: " + action); |  | 
| 848       // Connectivity change |  | 
| 849       if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) |  | 
| 850       { | 572       { | 
| 851         final ConnectivityManager connectivityManager = (ConnectivityManager) ge
     tSystemService(Context.CONNECTIVITY_SERVICE); | 573         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         { | 574         { | 
| 863           if (nativeProxyAutoConfigured) | 575         case STATE_CHANGED: | 
| 864           { | 576           final NotificationManager notificationManager = (NotificationManager) 
     getSystemService(NOTIFICATION_SERVICE); | 
| 865             ProxySettings.setConnectionProxy(getApplicationContext(), LOCALHOST,
      port, ""); | 577           notificationManager.notify(ONGOING_NOTIFICATION_ID, getNotification())
     ; | 
| 866           } | 578           ProxyService.this.sendStateChangedBroadcast(); | 
| 867           else | 579           break; | 
| 868           { | 580         default: | 
| 869             updateNoTrafficCheck(connectivityManager); | 581           // ignore | 
| 870           } | 582           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         } | 583         } | 
| 903       } | 584       } | 
| 904     } | 585     } | 
| 905   }; | 586   }; | 
| 906 | 587 | 
| 907   final class ProxyServer extends Server | 588   final class ProxyServer extends Server | 
| 908   { | 589   { | 
| 909     @Override | 590     @Override | 
| 910     public void close() | 591     public void close() | 
| 911     { | 592     { | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 925     @Override | 606     @Override | 
| 926     public void log(final int level, final Object obj, final String message) | 607     public void log(final int level, final Object obj, final String message) | 
| 927     { | 608     { | 
| 928       if (level <= logLevel) | 609       if (level <= logLevel) | 
| 929       { | 610       { | 
| 930         Log.println(7 - level, obj != null ? obj.toString() : TAG, message); | 611         Log.println(7 - level, obj != null ? obj.toString() : TAG, message); | 
| 931       } | 612       } | 
| 932     } | 613     } | 
| 933   } | 614   } | 
| 934 } | 615 } | 
| OLD | NEW | 
|---|