| OLD | NEW | 
| (Empty) |  | 
 |    1 package org.adblockplus.android; | 
 |    2  | 
 |    3 import java.io.File; | 
 |    4 import java.io.IOException; | 
 |    5 import java.lang.reflect.Method; | 
 |    6 import java.net.InetAddress; | 
 |    7 import java.net.ServerSocket; | 
 |    8 import java.util.List; | 
 |    9 import java.util.Properties; | 
 |   10 import java.util.concurrent.TimeoutException; | 
 |   11  | 
 |   12 import sunlabs.brazil.server.Server; | 
 |   13 import sunlabs.brazil.util.Base64; | 
 |   14 import android.app.Notification; | 
 |   15 import android.app.NotificationManager; | 
 |   16 import android.app.PendingIntent; | 
 |   17 import android.app.Service; | 
 |   18 import android.content.BroadcastReceiver; | 
 |   19 import android.content.Context; | 
 |   20 import android.content.Intent; | 
 |   21 import android.content.IntentFilter; | 
 |   22 import android.content.SharedPreferences; | 
 |   23 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; | 
 |   24 import android.content.pm.PackageManager.NameNotFoundException; | 
 |   25 import android.net.ConnectivityManager; | 
 |   26 import android.net.NetworkInfo; | 
 |   27 import android.os.Binder; | 
 |   28 import android.os.Build; | 
 |   29 import android.os.Handler; | 
 |   30 import android.os.IBinder; | 
 |   31 import android.preference.PreferenceManager; | 
 |   32 import android.util.Log; | 
 |   33 import android.widget.Toast; | 
 |   34  | 
 |   35 import com.stericson.RootTools.RootTools; | 
 |   36 import com.stericson.RootTools.RootToolsException; | 
 |   37  | 
 |   38 public class ProxyService extends Service implements OnSharedPreferenceChangeLis
     tener | 
 |   39 { | 
 |   40   private static final String LOCALHOST = "127.0.0.1"; | 
 |   41  | 
 |   42   static | 
 |   43   { | 
 |   44     RootTools.debugMode = false; | 
 |   45   } | 
 |   46  | 
 |   47   private static final String TAG = "ProxyService"; | 
 |   48   private static final boolean logRequests = true; | 
 |   49  | 
 |   50   private final static int DEFAULT_TIMEOUT = 3000; | 
 |   51   private final static int NO_TRAFFIC_TIMEOUT = 5 * 60 * 1000; // 5 minutes | 
 |   52  | 
 |   53   final static int ONGOING_NOTIFICATION_ID = R.string.app_name; | 
 |   54   private final static int NOTRAFFIC_NOTIFICATION_ID = R.string.app_name + 3; | 
 |   55  | 
 |   56   /** | 
 |   57    * Broadcasted when service starts or stops. | 
 |   58    */ | 
 |   59   public final static String BROADCAST_STATE_CHANGED = "org.adblockplus.android.
     service.state"; | 
 |   60   /** | 
 |   61    * Broadcasted if proxy fails to start. | 
 |   62    */ | 
 |   63   public final static String BROADCAST_PROXY_FAILED = "org.adblockplus.android.p
     roxy.failure"; | 
 |   64  | 
 |   65   private final static String IPTABLES_RETURN = " -t nat -m owner --uid-owner {{
     UID}} -A OUTPUT -p tcp -j RETURN\n"; | 
 |   66   private final static String IPTABLES_ADD_HTTP = " -t nat -A OUTPUT -p tcp --dp
     ort 80 -j REDIRECT --to {{PORT}}\n"; | 
 |   67  | 
 |   68   private Notification ongoingNotification; | 
 |   69   private PendingIntent contentIntent; | 
 |   70  | 
 |   71   private Handler notrafficHandler; | 
 |   72  | 
 |   73   protected ProxyServer proxy = null; | 
 |   74   protected int port; | 
 |   75  | 
 |   76   /** | 
 |   77    * Indicates that service is working with root privileges. | 
 |   78    */ | 
 |   79   private boolean transparent = false; | 
 |   80   /** | 
 |   81    * Indicates that service has autoconfigured Android proxy settings (version | 
 |   82    * 3.1+). | 
 |   83    */ | 
 |   84   private boolean nativeProxy = false; | 
 |   85  | 
 |   86   private String iptables = null; | 
 |   87  | 
 |   88   @Override | 
 |   89   public void onCreate() | 
 |   90   { | 
 |   91     super.onCreate(); | 
 |   92  | 
 |   93     // Get port for local proxy | 
 |   94     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this
     ); | 
 |   95     String p = prefs.getString(getString(R.string.pref_port), null); | 
 |   96     try | 
 |   97     { | 
 |   98       port = p != null ? Integer.valueOf(p) : getResources().getInteger(R.intege
     r.def_port); | 
 |   99     } | 
 |  100     catch (NumberFormatException e) | 
 |  101     { | 
 |  102       Toast.makeText(this, getString(R.string.msg_badport) + ": " + p, Toast.LEN
     GTH_LONG).show(); | 
 |  103       port = getResources().getInteger(R.integer.def_port); | 
 |  104     } | 
 |  105  | 
 |  106     // Try to read user proxy settings | 
 |  107     String proxyHost = null; | 
 |  108     String proxyPort = null; | 
 |  109     String proxyExcl = null; | 
 |  110     String proxyUser = null; | 
 |  111     String proxyPass = null; | 
 |  112  | 
 |  113     if (Build.VERSION.SDK_INT >= 12) // Honeycomb 3.1 | 
 |  114     { | 
 |  115       // Read system settings | 
 |  116       proxyHost = System.getProperty("http.proxyHost"); | 
 |  117       proxyPort = System.getProperty("http.proxyPort"); | 
 |  118       proxyExcl = System.getProperty("http.nonProxyHosts"); | 
 |  119  | 
 |  120       Log.d(TAG, "PRX: " + proxyHost + ":" + proxyPort + "(" + proxyExcl + ")"); | 
 |  121       String[] px = ProxySettings.getUserProxy(getApplicationContext()); // not 
     used but left for future reference | 
 |  122       if (px != null) | 
 |  123         Log.d(TAG, "PRX: " + px[0] + ":" + px[1] + "(" + px[2] + ")"); | 
 |  124     } | 
 |  125     else | 
 |  126     { | 
 |  127       // Read application settings | 
 |  128       proxyHost = prefs.getString(getString(R.string.pref_proxyhost), null); | 
 |  129       proxyPort = prefs.getString(getString(R.string.pref_proxyport), null); | 
 |  130       proxyUser = prefs.getString(getString(R.string.pref_proxyuser), null); | 
 |  131       proxyPass = prefs.getString(getString(R.string.pref_proxypass), null); | 
 |  132     } | 
 |  133  | 
 |  134     // Check for root privileges and try to install transparent proxy | 
 |  135     if (RootTools.isAccessGiven()) | 
 |  136     { | 
 |  137       try | 
 |  138       { | 
 |  139         iptables = getIptables(); | 
 |  140         if (iptables != null) | 
 |  141         { | 
 |  142           StringBuffer cmd = new StringBuffer(); | 
 |  143           int uid = getPackageManager().getPackageInfo(getPackageName(), 0).appl
     icationInfo.uid; | 
 |  144           cmd.append(iptables); | 
 |  145           cmd.append(IPTABLES_RETURN.replace("{{UID}}", String.valueOf(uid))); | 
 |  146           cmd.append(iptables); | 
 |  147           cmd.append(IPTABLES_ADD_HTTP.replace("{{PORT}}", String.valueOf(port))
     ); | 
 |  148           String rules = cmd.toString(); | 
 |  149           RootTools.sendShell(rules, DEFAULT_TIMEOUT); | 
 |  150           transparent = true; | 
 |  151         } | 
 |  152         else | 
 |  153         { | 
 |  154           Log.w(TAG, "Have root access, but no iptables found"); | 
 |  155         } | 
 |  156       } | 
 |  157       catch (NameNotFoundException e) | 
 |  158       { | 
 |  159         Log.e(TAG, "Failed to initialize iptables", e); | 
 |  160       } | 
 |  161       catch (IOException e) | 
 |  162       { | 
 |  163         Log.e(TAG, "Failed to initialize iptables", e); | 
 |  164       } | 
 |  165       catch (RootToolsException e) | 
 |  166       { | 
 |  167         Log.e(TAG, "Failed to initialize iptables", e); | 
 |  168       } | 
 |  169       catch (TimeoutException e) | 
 |  170       { | 
 |  171         Log.e(TAG, "Failed to initialize iptables", e); | 
 |  172       } | 
 |  173     } | 
 |  174  | 
 |  175     if (!transparent) | 
 |  176     { | 
 |  177       // Try to set native proxy | 
 |  178       nativeProxy = ProxySettings.setConnectionProxy(getApplicationContext(), LO
     CALHOST, port, ""); | 
 |  179  | 
 |  180       if (nativeProxy) | 
 |  181       { | 
 |  182         registerReceiver(connectionReceiver, new IntentFilter(ConnectivityManage
     r.CONNECTIVITY_ACTION)); | 
 |  183         registerReceiver(connectionReceiver, new IntentFilter("android.net.wifi.
     LINK_CONFIGURATION_CHANGED")); | 
 |  184       } | 
 |  185     } | 
 |  186  | 
 |  187     // Start engine | 
 |  188     AdblockPlus.getApplication().startEngine(); | 
 |  189  | 
 |  190     registerReceiver(proxyReceiver, new IntentFilter(ProxyService.BROADCAST_PROX
     Y_FAILED)); | 
 |  191     registerReceiver(matchesReceiver, new IntentFilter(AdblockPlus.BROADCAST_FIL
     TER_MATCHES)); | 
 |  192  | 
 |  193     // Start proxy | 
 |  194     if (proxy == null) | 
 |  195     { | 
 |  196       ServerSocket listen = null; | 
 |  197       try | 
 |  198       { | 
 |  199         // TODO Add port travel | 
 |  200         listen = new ServerSocket(port, 1024); | 
 |  201       } | 
 |  202       catch (IOException e) | 
 |  203       { | 
 |  204         sendBroadcast(new Intent(BROADCAST_PROXY_FAILED).putExtra("msg", e.getMe
     ssage())); | 
 |  205         Log.e(TAG, null, e); | 
 |  206         return; | 
 |  207       } | 
 |  208  | 
 |  209       Properties config = new Properties(); | 
 |  210       config.put("handler", "main"); | 
 |  211       config.put("main.prefix", ""); | 
 |  212       config.put("main.class", "sunlabs.brazil.server.ChainHandler"); | 
 |  213       if (transparent) | 
 |  214       { | 
 |  215         config.put("main.handlers", "urlmodifier adblock"); | 
 |  216         config.put("urlmodifier.class", "org.adblockplus.brazil.TransparentProxy
     Handler"); | 
 |  217       } | 
 |  218       else | 
 |  219       { | 
 |  220         config.put("main.handlers", "https adblock"); | 
 |  221         config.put("https.class", "org.adblockplus.brazil.SSLConnectionHandler")
     ; | 
 |  222       } | 
 |  223       config.put("adblock.class", "org.adblockplus.brazil.RequestHandler"); | 
 |  224       if (logRequests) | 
 |  225         config.put("adblock.proxylog", "yes"); | 
 |  226  | 
 |  227       configureUserProxy(config, proxyHost, proxyPort, proxyExcl, proxyUser, pro
     xyPass); | 
 |  228  | 
 |  229       proxy = new ProxyServer(); | 
 |  230       proxy.logLevel = Server.LOG_DIAGNOSTIC; | 
 |  231       proxy.setup(listen, config.getProperty("handler"), config); | 
 |  232       proxy.start(); | 
 |  233     } | 
 |  234  | 
 |  235     prefs.registerOnSharedPreferenceChangeListener(this); | 
 |  236  | 
 |  237     String msg = getString(transparent ? R.string.notif_all : nativeProxy ? R.st
     ring.notif_wifi : R.string.notif_waiting); | 
 |  238     if (!transparent && !nativeProxy) | 
 |  239     { | 
 |  240       // Initiate no traffic check | 
 |  241       notrafficHandler = new Handler(); | 
 |  242       notrafficHandler.postDelayed(noTraffic, NO_TRAFFIC_TIMEOUT); | 
 |  243     } | 
 |  244     // Lock service | 
 |  245     ongoingNotification = new Notification(); | 
 |  246     ongoingNotification.when = 0; | 
 |  247     contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Preferen
     ces.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TA
     SK), 0); | 
 |  248     ongoingNotification.icon = R.drawable.ic_stat_blocking; | 
 |  249     ongoingNotification.setLatestEventInfo(getApplicationContext(), getText(R.st
     ring.app_name), msg, contentIntent); | 
 |  250     startForeground(ONGOING_NOTIFICATION_ID, ongoingNotification); | 
 |  251  | 
 |  252     sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", true).
     putExtra("port", port).putExtra("manual", !transparent && !nativeProxy)); | 
 |  253     Log.i(TAG, "Service started"); | 
 |  254   } | 
 |  255  | 
 |  256   @Override | 
 |  257   public void onDestroy() | 
 |  258   { | 
 |  259     super.onDestroy(); | 
 |  260  | 
 |  261     stopNoTrafficCheck(false); | 
 |  262  | 
 |  263     unregisterReceiver(matchesReceiver); | 
 |  264     unregisterReceiver(proxyReceiver); | 
 |  265  | 
 |  266     // Stop IP redirecting | 
 |  267     if (transparent) | 
 |  268     { | 
 |  269       new Thread() { | 
 |  270         @Override | 
 |  271         public void run() | 
 |  272         { | 
 |  273           try | 
 |  274           { | 
 |  275             RootTools.sendShell(iptables + " -t nat -F OUTPUT", DEFAULT_TIMEOUT)
     ; | 
 |  276           } | 
 |  277           catch (Exception e) | 
 |  278           { | 
 |  279             Log.e(TAG, "Failed to clear iptables", e); | 
 |  280           } | 
 |  281         } | 
 |  282       }.start(); | 
 |  283     } | 
 |  284  | 
 |  285     // Clear native proxy | 
 |  286     if (nativeProxy) | 
 |  287     { | 
 |  288       unregisterReceiver(connectionReceiver); | 
 |  289       clearConnectionProxy(); | 
 |  290     } | 
 |  291  | 
 |  292     sendBroadcast(new Intent(BROADCAST_STATE_CHANGED).putExtra("enabled", false)
     ); | 
 |  293  | 
 |  294     // Stop proxy server | 
 |  295     proxy.close(); | 
 |  296  | 
 |  297     // Stop engine if not in interactive mode | 
 |  298     AdblockPlus.getApplication().stopEngine(false); | 
 |  299  | 
 |  300     // Release service lock | 
 |  301     stopForeground(true); | 
 |  302  | 
 |  303     Log.i(TAG, "Service stopped"); | 
 |  304   } | 
 |  305  | 
 |  306   /** | 
 |  307    * Restores system proxy settings via native call on Android 3.1+ devices usin
     g | 
 |  308    * Java reflection. | 
 |  309    */ | 
 |  310   private void clearConnectionProxy() | 
 |  311   { | 
 |  312     String proxyHost = (String) proxy.props.getProperty("adblock.proxyHost"); | 
 |  313     String proxyPort = (String) proxy.props.getProperty("adblock.proxyPort"); | 
 |  314     String proxyExcl = (String) proxy.props.getProperty("adblock.proxyExcl"); | 
 |  315     int port = 0; | 
 |  316     try | 
 |  317     { | 
 |  318       if (proxyHost != null) | 
 |  319         port = Integer.valueOf(proxyPort); | 
 |  320     } | 
 |  321     catch (NumberFormatException e) | 
 |  322     { | 
 |  323       Log.e(TAG, "Bad port setting", e); | 
 |  324     } | 
 |  325     ProxySettings.setConnectionProxy(getApplicationContext(), proxyHost, port, p
     roxyExcl); | 
 |  326   } | 
 |  327  | 
 |  328   /** | 
 |  329    * Sets user proxy settings in proxy service properties. | 
 |  330    */ | 
 |  331   private void configureUserProxy(Properties config, String proxyHost, String pr
     oxyPort, String proxyExcl, String proxyUser, String proxyPass) | 
 |  332   { | 
 |  333     // Clean previous settings | 
 |  334     config.remove("adblock.proxyHost"); | 
 |  335     config.remove("adblock.proxyPort"); | 
 |  336     config.remove("adblock.auth"); | 
 |  337     config.remove("adblock.proxyExcl"); | 
 |  338     if (!transparent) | 
 |  339     { | 
 |  340       config.remove("https.proxyHost"); | 
 |  341       config.remove("https.proxyPort"); | 
 |  342       config.remove("https.auth"); | 
 |  343     } | 
 |  344  | 
 |  345     if (nativeProxy) | 
 |  346       passProxySettings(proxyHost, proxyPort, proxyExcl); | 
 |  347  | 
 |  348     // Check if there are any settings | 
 |  349     if (proxyHost == null || "".equals(proxyHost)) | 
 |  350       return; | 
 |  351  | 
 |  352     // Check for dirty proxy settings - this indicated previous crash: | 
 |  353     // proxy points to ourselves | 
 |  354     // proxy port is null, 0 or not a number | 
 |  355     // proxy is 127.0.0.1:8080 | 
 |  356     if (proxyPort == null) | 
 |  357       return; | 
 |  358     int p = 0; | 
 |  359     try | 
 |  360     { | 
 |  361       p = Integer.valueOf(proxyPort); | 
 |  362     } | 
 |  363     catch (NumberFormatException e) | 
 |  364     { | 
 |  365       return; | 
 |  366     } | 
 |  367     if (p == 0 || isLocalHost(proxyHost) && (p == port || p == 8080)) | 
 |  368     { | 
 |  369       if (nativeProxy) | 
 |  370         passProxySettings(null, null, null); | 
 |  371       return; | 
 |  372     } | 
 |  373  | 
 |  374     config.put("adblock.proxyHost", proxyHost); | 
 |  375     config.put("adblock.proxyPort", proxyPort); | 
 |  376     if (!transparent) | 
 |  377     { | 
 |  378       config.put("https.proxyHost", proxyHost); | 
 |  379       config.put("https.proxyPort", proxyPort); | 
 |  380     } | 
 |  381  | 
 |  382     // TODO Not implemented in our proxy but needed to restore settings | 
 |  383     if (proxyExcl != null) | 
 |  384       config.put("adblock.proxyExcl", proxyExcl); | 
 |  385  | 
 |  386     if (proxyUser != null && !"".equals(proxyUser) && proxyPass != null && !"".e
     quals(proxyPass)) | 
 |  387     { | 
 |  388       // Base64 encode user:password | 
 |  389       String proxyAuth = "Basic " + new String(Base64.encode(proxyUser + ":" + p
     roxyPass)); | 
 |  390       config.put("adblock.auth", proxyAuth); | 
 |  391       if (!transparent) | 
 |  392         config.put("https.auth", proxyAuth); | 
 |  393     } | 
 |  394   } | 
 |  395  | 
 |  396   private void passProxySettings(String proxyHost, String proxyPort, String prox
     yExcl) | 
 |  397   { | 
 |  398     try | 
 |  399     { | 
 |  400       CrashHandler handler = (CrashHandler) Thread.getDefaultUncaughtExceptionHa
     ndler(); | 
 |  401       handler.saveProxySettings(proxyHost, proxyPort, proxyExcl); | 
 |  402     } | 
 |  403     catch (ClassCastException e) | 
 |  404     { | 
 |  405       // ignore - default handler in use | 
 |  406     } | 
 |  407   } | 
 |  408  | 
 |  409   @Override | 
 |  410   public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Str
     ing key) | 
 |  411   { | 
 |  412     if (Build.VERSION.SDK_INT < 12) // Honeycomb 3.1 | 
 |  413     { | 
 |  414       String ketHost = getString(R.string.pref_proxyhost); | 
 |  415       String keyPort = getString(R.string.pref_proxyport); | 
 |  416       String keyUser = getString(R.string.pref_proxyuser); | 
 |  417       String keyPass = getString(R.string.pref_proxypass); | 
 |  418       if (key.equals(ketHost) || key.equals(keyPort) || key.equals(keyUser) || k
     ey.equals(keyPass)) | 
 |  419       { | 
 |  420         String proxyHost = sharedPreferences.getString(ketHost, null); | 
 |  421         String proxyPort = sharedPreferences.getString(keyPort, null); | 
 |  422         String proxyUser = sharedPreferences.getString(keyUser, null); | 
 |  423         String proxyPass = sharedPreferences.getString(keyPass, null); | 
 |  424         if (proxy != null) | 
 |  425         { | 
 |  426           configureUserProxy(proxy.props, proxyHost, proxyPort, null, proxyUser,
      proxyPass); | 
 |  427           proxy.restart(proxy.props.getProperty("handler")); | 
 |  428         } | 
 |  429       } | 
 |  430     } | 
 |  431   } | 
 |  432  | 
 |  433   public boolean isTransparent() | 
 |  434   { | 
 |  435     return transparent; | 
 |  436   } | 
 |  437  | 
 |  438   public boolean isNativeProxy() | 
 |  439   { | 
 |  440     return nativeProxy; | 
 |  441   } | 
 |  442  | 
 |  443   /** | 
 |  444    * Checks if specified host is local. | 
 |  445    */ | 
 |  446   private static final boolean isLocalHost(String host) | 
 |  447   { | 
 |  448     if (host == null) | 
 |  449       return false; | 
 |  450  | 
 |  451     try | 
 |  452     { | 
 |  453       if (host.equalsIgnoreCase("localhost")) | 
 |  454         return true; | 
 |  455  | 
 |  456       String className = "android.net.NetworkUtils"; | 
 |  457       Class<?> c = Class.forName(className); | 
 |  458       /* | 
 |  459        * InetAddress address = NetworkUtils.numericToInetAddress(host); | 
 |  460        */ | 
 |  461       Method method = c.getMethod("numericToInetAddress", String.class); | 
 |  462       InetAddress address = (InetAddress) method.invoke(null, host); | 
 |  463  | 
 |  464       if (address.isLoopbackAddress()) | 
 |  465         return true; | 
 |  466     } | 
 |  467     catch (Exception e) | 
 |  468     { | 
 |  469       Log.w(TAG, null, e); | 
 |  470     } | 
 |  471     return false; | 
 |  472   } | 
 |  473  | 
 |  474   /** | 
 |  475    * Returns path to iptables executable. | 
 |  476    */ | 
 |  477   public String getIptables() throws IOException, RootToolsException, TimeoutExc
     eption | 
 |  478   { | 
 |  479     if (!RootTools.isAccessGiven()) | 
 |  480       return null; | 
 |  481  | 
 |  482     File ipt = getFileStreamPath("iptables"); | 
 |  483  | 
 |  484     if (!ipt.exists()) | 
 |  485     { | 
 |  486       Log.e(TAG, "No iptables excutable found"); | 
 |  487       return null; | 
 |  488     } | 
 |  489      | 
 |  490     String path = ipt.getAbsolutePath(); | 
 |  491  | 
 |  492     RootTools.sendShell("chmod 700 " + path, DEFAULT_TIMEOUT); | 
 |  493  | 
 |  494     boolean compatible = false; | 
 |  495     boolean version = false; | 
 |  496  | 
 |  497     String command = path + " --version\n" + path + " -L -t nat -n\n"; | 
 |  498  | 
 |  499     List<String> result = RootTools.sendShell(command, DEFAULT_TIMEOUT); | 
 |  500     for (String line : result) | 
 |  501     { | 
 |  502       if (line.contains("OUTPUT")) | 
 |  503         compatible = true; | 
 |  504       if (line.contains("v1.4.")) | 
 |  505         version = true; | 
 |  506     } | 
 |  507  | 
 |  508     if (!compatible || !version) | 
 |  509     { | 
 |  510       Log.e(TAG, "Incompatible iptables excutable"); | 
 |  511       return null; | 
 |  512     } | 
 |  513  | 
 |  514     return path; | 
 |  515   } | 
 |  516    | 
 |  517   public List<String> getIptablesOutput() | 
 |  518   { | 
 |  519     if (iptables == null) | 
 |  520       return null; | 
 |  521  | 
 |  522     String command = iptables + " -L -t nat -n\n"; | 
 |  523     try | 
 |  524     { | 
 |  525       return RootTools.sendShell(command, DEFAULT_TIMEOUT); | 
 |  526     } | 
 |  527     catch (Exception e) | 
 |  528     { | 
 |  529       Log.e(TAG, "Failed to get iptables configuration", e); | 
 |  530       return null; | 
 |  531     } | 
 |  532   } | 
 |  533  | 
 |  534   /** | 
 |  535    * Stops no traffic check, optionally resetting notification message. | 
 |  536    *  | 
 |  537    * @param changeStatus | 
 |  538    *          true if notification message should be set to normal operating | 
 |  539    *          mode | 
 |  540    */ | 
 |  541   private void stopNoTrafficCheck(boolean changeStatus) | 
 |  542   { | 
 |  543     if (notrafficHandler != null) | 
 |  544     { | 
 |  545       notrafficHandler.removeCallbacks(noTraffic); | 
 |  546       if (changeStatus) | 
 |  547       { | 
 |  548         NotificationManager notificationManager = (NotificationManager) getSyste
     mService(NOTIFICATION_SERVICE); | 
 |  549         ongoingNotification.setLatestEventInfo(ProxyService.this, getText(R.stri
     ng.app_name), getText(R.string.notif_wifi), contentIntent); | 
 |  550         notificationManager.notify(ONGOING_NOTIFICATION_ID, ongoingNotification)
     ; | 
 |  551       } | 
 |  552     } | 
 |  553     notrafficHandler = null; | 
 |  554   } | 
 |  555  | 
 |  556   private final IBinder binder = new LocalBinder(); | 
 |  557  | 
 |  558   public final class LocalBinder extends Binder | 
 |  559   { | 
 |  560     public ProxyService getService() | 
 |  561     { | 
 |  562       return ProxyService.this; | 
 |  563     } | 
 |  564   } | 
 |  565  | 
 |  566   @Override | 
 |  567   public IBinder onBind(Intent intent) | 
 |  568   { | 
 |  569     return binder; | 
 |  570   } | 
 |  571  | 
 |  572   /** | 
 |  573    * Executed if no traffic is detected after a period of time. Notifies user | 
 |  574    * about possible configuration problems. | 
 |  575    */ | 
 |  576   private Runnable noTraffic = new Runnable() { | 
 |  577     public void run() | 
 |  578     { | 
 |  579       // Show warning notification | 
 |  580       Notification notification = new Notification(); | 
 |  581       notification.icon = R.drawable.ic_stat_warning; | 
 |  582       notification.when = System.currentTimeMillis(); | 
 |  583       notification.flags |= Notification.FLAG_AUTO_CANCEL; | 
 |  584       notification.defaults |= Notification.DEFAULT_SOUND; | 
 |  585       Intent intent = new Intent(ProxyService.this, ConfigurationActivity.class)
     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | 
 |  586       intent.putExtra("port", port); | 
 |  587       PendingIntent contentIntent = PendingIntent.getActivity(ProxyService.this,
      0, intent, PendingIntent.FLAG_UPDATE_CURRENT); | 
 |  588       notification.setLatestEventInfo(ProxyService.this, getText(R.string.app_na
     me), getString(R.string.notif_notraffic), contentIntent); | 
 |  589       NotificationManager notificationManager = (NotificationManager) getSystemS
     ervice(NOTIFICATION_SERVICE); | 
 |  590       notificationManager.notify(NOTRAFFIC_NOTIFICATION_ID, notification); | 
 |  591     } | 
 |  592   }; | 
 |  593  | 
 |  594   /** | 
 |  595    * Stops no traffic check if traffic is detected by proxy service. | 
 |  596    */ | 
 |  597   private BroadcastReceiver matchesReceiver = new BroadcastReceiver() { | 
 |  598     @Override | 
 |  599     public void onReceive(final Context context, Intent intent) | 
 |  600     { | 
 |  601       if (intent.getAction().equals(AdblockPlus.BROADCAST_FILTER_MATCHES)) | 
 |  602         stopNoTrafficCheck(true); | 
 |  603     } | 
 |  604   }; | 
 |  605  | 
 |  606   /** | 
 |  607    * Stops service if proxy fails. | 
 |  608    */ | 
 |  609   private BroadcastReceiver proxyReceiver = new BroadcastReceiver() { | 
 |  610     @Override | 
 |  611     public void onReceive(final Context context, Intent intent) | 
 |  612     { | 
 |  613       if (intent.getAction().equals(ProxyService.BROADCAST_PROXY_FAILED)) | 
 |  614       { | 
 |  615         stopSelf(); | 
 |  616       } | 
 |  617     } | 
 |  618   }; | 
 |  619  | 
 |  620   /** | 
 |  621    * Monitors system network connection settings changes and updates proxy | 
 |  622    * settings accordingly. | 
 |  623    */ | 
 |  624   private BroadcastReceiver connectionReceiver = new BroadcastReceiver() { | 
 |  625     @Override | 
 |  626     public void onReceive(Context ctx, Intent intent) | 
 |  627     { | 
 |  628       String action = intent.getAction(); | 
 |  629       Log.i(TAG, "Action: " + action); | 
 |  630       if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) | 
 |  631       { | 
 |  632         NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_N
     ETWORK_INFO); | 
 |  633         String typeName = info.getTypeName(); | 
 |  634         String subtypeName = info.getSubtypeName(); | 
 |  635         boolean available = info.isAvailable(); | 
 |  636         Log.i(TAG, "Network Type: " + typeName + ", subtype: " + subtypeName + "
     , available: " + available); | 
 |  637         if (info.getType() == ConnectivityManager.TYPE_WIFI) | 
 |  638           ProxySettings.setConnectionProxy(getApplicationContext(), LOCALHOST, p
     ort, ""); | 
 |  639       } | 
 |  640       else if ("android.net.wifi.LINK_CONFIGURATION_CHANGED".equals(action)) | 
 |  641       { | 
 |  642         Object lp = intent.getParcelableExtra("linkProperties"); | 
 |  643         Method method; | 
 |  644         try | 
 |  645         { | 
 |  646           /* | 
 |  647            * lp.getHttpProxy(); | 
 |  648            */ | 
 |  649           method = lp.getClass().getMethod("getHttpProxy"); | 
 |  650           Object pp = method.invoke(lp); | 
 |  651  | 
 |  652           String[] userProxy = ProxySettings.getUserProxy(pp); | 
 |  653           if (userProxy != null && Integer.valueOf(userProxy[1]) != port) | 
 |  654           { | 
 |  655             Log.i(TAG, "User has set new proxy: " + userProxy[0] + ":" + userPro
     xy[1] + "(" + userProxy[2] + ")"); | 
 |  656             if (proxy != null) | 
 |  657             { | 
 |  658               configureUserProxy(proxy.props, userProxy[0], userProxy[1], userPr
     oxy[2], null, null); | 
 |  659               proxy.restart(proxy.props.getProperty("handler")); | 
 |  660             } | 
 |  661           } | 
 |  662         } | 
 |  663         catch (Exception e) | 
 |  664         { | 
 |  665           // This should not happen | 
 |  666           Log.e(TAG, null, e); | 
 |  667         } | 
 |  668  | 
 |  669       } | 
 |  670     } | 
 |  671   }; | 
 |  672  | 
 |  673   final class ProxyServer extends Server | 
 |  674   { | 
 |  675     @Override | 
 |  676     public void close() | 
 |  677     { | 
 |  678       try | 
 |  679       { | 
 |  680         listen.close(); | 
 |  681         this.interrupt(); | 
 |  682         this.join(); | 
 |  683       } | 
 |  684       catch (Exception e) | 
 |  685       { | 
 |  686       } | 
 |  687       log(LOG_WARNING, null, "server stopped"); | 
 |  688     } | 
 |  689  | 
 |  690     @Override | 
 |  691     public void log(int level, Object obj, String message) | 
 |  692     { | 
 |  693       if (level <= logLevel) | 
 |  694       { | 
 |  695         Log.println(7 - level, obj != null ? obj.toString() : TAG, message); | 
 |  696       } | 
 |  697     } | 
 |  698   } | 
 |  699 } | 
| OLD | NEW |