| OLD | NEW | 
| (Empty) |  | 
 |    1 /* | 
 |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
 |    3  * Copyright (C) 2006-2016 Eyeo GmbH | 
 |    4  * | 
 |    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 | 
 |    7  * published by the Free Software Foundation. | 
 |    8  * | 
 |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
 |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |   12  * GNU General Public License for more details. | 
 |   13  * | 
 |   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/>. | 
 |   16  */ | 
 |   17  | 
 |   18 package org.adblockplus.libadblockplus.android; | 
 |   19  | 
 |   20 import java.util.ArrayList; | 
 |   21 import java.util.Arrays; | 
 |   22 import java.util.Collection; | 
 |   23 import java.util.HashSet; | 
 |   24 import java.util.List; | 
 |   25 import java.util.Locale; | 
 |   26 import java.util.Set; | 
 |   27  | 
 |   28 import org.adblockplus.libadblockplus.AppInfo; | 
 |   29 import org.adblockplus.libadblockplus.Filter; | 
 |   30 import org.adblockplus.libadblockplus.FilterChangeCallback; | 
 |   31 import org.adblockplus.libadblockplus.FilterEngine; | 
 |   32 import org.adblockplus.libadblockplus.FilterEngine.ContentType; | 
 |   33 import org.adblockplus.libadblockplus.JsEngine; | 
 |   34 import org.adblockplus.libadblockplus.JsValue; | 
 |   35 import org.adblockplus.libadblockplus.LogSystem; | 
 |   36 import org.adblockplus.libadblockplus.Notification; | 
 |   37 import org.adblockplus.libadblockplus.ShowNotificationCallback; | 
 |   38 import org.adblockplus.libadblockplus.Subscription; | 
 |   39 import org.adblockplus.libadblockplus.UpdateAvailableCallback; | 
 |   40 import org.adblockplus.libadblockplus.UpdateCheckDoneCallback; | 
 |   41  | 
 |   42 import android.content.Context; | 
 |   43 import android.content.pm.PackageInfo; | 
 |   44 import android.content.pm.PackageManager.NameNotFoundException; | 
 |   45 import android.os.Build.VERSION; | 
 |   46 import android.util.Log; | 
 |   47  | 
 |   48 public final class AdblockEngine | 
 |   49 { | 
 |   50   private static final String TAG = Utils.getTag(AdblockEngine.class); | 
 |   51  | 
 |   52   private final Context context; | 
 |   53  | 
 |   54   /* | 
 |   55    * The fields below are volatile because: | 
 |   56    * | 
 |   57    * I encountered JNI related bugs/crashes caused by JNI backed Java objects. I
     t seemed that under | 
 |   58    * certain conditions the objects were optimized away which resulted in crashe
     s when trying to | 
 |   59    * release the object, sometimes even on access. | 
 |   60    * | 
 |   61    * The only solution that really worked was to declare the variables holding t
     he references | 
 |   62    * volatile, this seems to prevent the JNI from 'optimizing away' those object
     s (as a volatile | 
 |   63    * variable might be changed at any time from any thread). | 
 |   64    */ | 
 |   65   private volatile JsEngine jsEngine; | 
 |   66   private volatile FilterEngine filterEngine; | 
 |   67   private volatile LogSystem logSystem; | 
 |   68   private volatile AndroidWebRequest webRequest; | 
 |   69   private volatile UpdateAvailableCallback updateAvailableCallback; | 
 |   70   private volatile UpdateCheckDoneCallback updateCheckDoneCallback; | 
 |   71   private volatile FilterChangeCallback filterChangeCallback; | 
 |   72   private volatile ShowNotificationCallback showNotificationCallback; | 
 |   73   private final boolean elemhideEnabled; | 
 |   74  | 
 |   75   private AdblockEngine(final Context context, final boolean enableElemhide) | 
 |   76   { | 
 |   77     this.context = context; | 
 |   78     this.elemhideEnabled = enableElemhide; | 
 |   79   } | 
 |   80  | 
 |   81   public static AppInfo generateAppInfo(final Context context, boolean developme
     ntBuild) | 
 |   82   { | 
 |   83     String version = "0"; | 
 |   84     try | 
 |   85     { | 
 |   86       final PackageInfo info = context.getPackageManager().getPackageInfo(contex
     t.getPackageName(), 0); | 
 |   87       version = info.versionName; | 
 |   88       if (developmentBuild) | 
 |   89         version += "." + info.versionCode; | 
 |   90     } | 
 |   91     catch (final NameNotFoundException e) | 
 |   92     { | 
 |   93       Log.e(TAG, "Failed to get the application version number", e); | 
 |   94     } | 
 |   95     final String sdkVersion = String.valueOf(VERSION.SDK_INT); | 
 |   96     final String locale = Locale.getDefault().toString().replace('_', '-'); | 
 |   97  | 
 |   98     return AppInfo.builder() | 
 |   99         .setVersion(version) | 
 |  100         .setApplicationVersion(sdkVersion) | 
 |  101         .setLocale(locale) | 
 |  102         .setDevelopmentBuild(developmentBuild) | 
 |  103         .build(); | 
 |  104   } | 
 |  105  | 
 |  106   public static AdblockEngine create(final Context context, final AppInfo appInf
     o, | 
 |  107                                      final String basePath, boolean enableElemhi
     de, | 
 |  108                                      UpdateAvailableCallback updateAvailableCall
     back, | 
 |  109                                      UpdateCheckDoneCallback updateCheckDoneCall
     back, | 
 |  110                                      ShowNotificationCallback showNotificationCa
     llback, | 
 |  111                                      FilterChangeCallback filterChangeCallback) | 
 |  112   { | 
 |  113     Log.w(TAG, "Create"); | 
 |  114  | 
 |  115     final AdblockEngine engine = new AdblockEngine(context, enableElemhide); | 
 |  116  | 
 |  117     engine.jsEngine = new JsEngine(appInfo); | 
 |  118     engine.jsEngine.setDefaultFileSystem(basePath); | 
 |  119  | 
 |  120     engine.logSystem = new AndroidLogSystem(); | 
 |  121     engine.jsEngine.setLogSystem(engine.logSystem); | 
 |  122  | 
 |  123     engine.webRequest = new AndroidWebRequest(enableElemhide); | 
 |  124     engine.jsEngine.setWebRequest(engine.webRequest); | 
 |  125  | 
 |  126     engine.filterEngine = new FilterEngine(engine.jsEngine); | 
 |  127  | 
 |  128     engine.updateAvailableCallback = updateAvailableCallback; | 
 |  129     if (engine.updateAvailableCallback != null) | 
 |  130     { | 
 |  131       engine.filterEngine.setUpdateAvailableCallback(updateAvailableCallback); | 
 |  132     } | 
 |  133  | 
 |  134     engine.updateCheckDoneCallback = updateCheckDoneCallback; | 
 |  135  | 
 |  136     engine.showNotificationCallback = showNotificationCallback; | 
 |  137     if (engine.showNotificationCallback != null) | 
 |  138     { | 
 |  139       engine.filterEngine.setShowNotificationCallback(showNotificationCallback); | 
 |  140     } | 
 |  141  | 
 |  142     engine.filterChangeCallback = filterChangeCallback; | 
 |  143     if (engine.filterChangeCallback != null) | 
 |  144     { | 
 |  145       engine.filterEngine.setFilterChangeCallback(filterChangeCallback); | 
 |  146     } | 
 |  147  | 
 |  148     engine.webRequest.updateSubscriptionURLs(engine.filterEngine); | 
 |  149  | 
 |  150     return engine; | 
 |  151   } | 
 |  152  | 
 |  153   public static AdblockEngine create(final Context context, final AppInfo appInf
     o, | 
 |  154                                      final String basePath, boolean elemhideEnab
     led) | 
 |  155   { | 
 |  156     return create(context, appInfo, basePath, elemhideEnabled, null, null, null,
      null); | 
 |  157   } | 
 |  158  | 
 |  159   public void dispose() | 
 |  160   { | 
 |  161     Log.w(TAG, "Dispose"); | 
 |  162  | 
 |  163     if (this.logSystem != null) | 
 |  164     { | 
 |  165       this.logSystem.dispose(); | 
 |  166       this.logSystem = null; | 
 |  167     } | 
 |  168  | 
 |  169     if (this.webRequest != null) | 
 |  170     { | 
 |  171       this.webRequest.dispose(); | 
 |  172       this.webRequest = null; | 
 |  173     } | 
 |  174  | 
 |  175     if (this.updateAvailableCallback != null) | 
 |  176     { | 
 |  177       if (this.filterEngine != null) | 
 |  178       { | 
 |  179         this.filterEngine.removeUpdateAvailableCallback(); | 
 |  180       } | 
 |  181  | 
 |  182       this.updateAvailableCallback.dispose(); | 
 |  183       this.updateAvailableCallback = null; | 
 |  184     } | 
 |  185  | 
 |  186     if (this.updateCheckDoneCallback != null) | 
 |  187     { | 
 |  188       this.updateCheckDoneCallback.dispose(); | 
 |  189       this.updateCheckDoneCallback = null; | 
 |  190     } | 
 |  191  | 
 |  192     if (this.filterChangeCallback != null) | 
 |  193     { | 
 |  194       if (this.filterEngine != null) | 
 |  195       { | 
 |  196         this.filterEngine.removeFilterChangeCallback(); | 
 |  197       } | 
 |  198  | 
 |  199       this.filterChangeCallback.dispose(); | 
 |  200       this.filterChangeCallback = null; | 
 |  201     } | 
 |  202  | 
 |  203     if (this.showNotificationCallback != null) | 
 |  204     { | 
 |  205       if (this.filterEngine != null) | 
 |  206       { | 
 |  207         this.filterEngine.removeShowNotificationCallback(); | 
 |  208       } | 
 |  209  | 
 |  210       this.showNotificationCallback.dispose(); | 
 |  211       this.showNotificationCallback = null; | 
 |  212     } | 
 |  213  | 
 |  214     // Safe disposing (just in case) | 
 |  215     if (this.filterEngine != null) | 
 |  216     { | 
 |  217       this.filterEngine.dispose(); | 
 |  218       this.filterEngine = null; | 
 |  219     } | 
 |  220  | 
 |  221     if (this.jsEngine != null) | 
 |  222     { | 
 |  223       this.jsEngine.dispose(); | 
 |  224       this.jsEngine = null; | 
 |  225     } | 
 |  226   } | 
 |  227  | 
 |  228   public boolean isFirstRun() | 
 |  229   { | 
 |  230     return this.filterEngine.isFirstRun(); | 
 |  231   } | 
 |  232  | 
 |  233   public boolean isElemhideEnabled() | 
 |  234   { | 
 |  235     return this.elemhideEnabled; | 
 |  236   } | 
 |  237  | 
 |  238   private static org.adblockplus.libadblockplus.android.Subscription convertJsSu
     bscription(final Subscription jsSubscription) | 
 |  239   { | 
 |  240     final org.adblockplus.libadblockplus.android.Subscription subscription = | 
 |  241       new org.adblockplus.libadblockplus.android.Subscription(); | 
 |  242  | 
 |  243     subscription.title = jsSubscription.getProperty("title").toString(); | 
 |  244     subscription.url = jsSubscription.getProperty("url").toString(); | 
 |  245  | 
 |  246     return subscription; | 
 |  247   } | 
 |  248  | 
 |  249   private static org.adblockplus.libadblockplus.android.Subscription[] convertJs
     Subscriptions( | 
 |  250     final List<Subscription> jsSubscriptions) | 
 |  251   { | 
 |  252     final org.adblockplus.libadblockplus.android.Subscription[] subscriptions = | 
 |  253       new org.adblockplus.libadblockplus.android.Subscription[jsSubscriptions.si
     ze()]; | 
 |  254  | 
 |  255     for (int i = 0; i < subscriptions.length; i++) | 
 |  256     { | 
 |  257       subscriptions[i] = convertJsSubscription(jsSubscriptions.get(i)); | 
 |  258     } | 
 |  259  | 
 |  260     return subscriptions; | 
 |  261   } | 
 |  262  | 
 |  263   public org.adblockplus.libadblockplus.android.Subscription[] getRecommendedSub
     scriptions() | 
 |  264   { | 
 |  265     return convertJsSubscriptions(this.filterEngine.fetchAvailableSubscriptions(
     )); | 
 |  266   } | 
 |  267  | 
 |  268   public org.adblockplus.libadblockplus.android.Subscription[] getListedSubscrip
     tions() | 
 |  269   { | 
 |  270     return convertJsSubscriptions(this.filterEngine.getListedSubscriptions()); | 
 |  271   } | 
 |  272  | 
 |  273   public void clearSubscriptions() | 
 |  274   { | 
 |  275     for (final Subscription s : this.filterEngine.getListedSubscriptions()) | 
 |  276     { | 
 |  277       s.removeFromList(); | 
 |  278     } | 
 |  279   } | 
 |  280  | 
 |  281   public void setSubscription(final String url) | 
 |  282   { | 
 |  283     clearSubscriptions(); | 
 |  284  | 
 |  285     final Subscription sub = this.filterEngine.getSubscription(url); | 
 |  286     if (sub != null) | 
 |  287     { | 
 |  288       sub.addToList(); | 
 |  289     } | 
 |  290   } | 
 |  291  | 
 |  292   public void setSubscriptions(Collection<String> urls) | 
 |  293   { | 
 |  294     clearSubscriptions(); | 
 |  295  | 
 |  296     for (String eachUrl : urls) | 
 |  297     { | 
 |  298       final Subscription sub = this.filterEngine.getSubscription(eachUrl); | 
 |  299       if (sub != null) | 
 |  300       { | 
 |  301         sub.addToList(); | 
 |  302       } | 
 |  303     } | 
 |  304   } | 
 |  305  | 
 |  306   public void refreshSubscriptions() | 
 |  307   { | 
 |  308     for (final Subscription s : this.filterEngine.getListedSubscriptions()) | 
 |  309     { | 
 |  310       s.updateFilters(); | 
 |  311     } | 
 |  312   } | 
 |  313  | 
 |  314   public boolean isAcceptableAdsEnabled() | 
 |  315   { | 
 |  316     final String url = getAcceptableAdsSubscriptionURL(); | 
 |  317     List<Subscription> subscriptions = this.filterEngine.getListedSubscriptions(
     ); | 
 |  318     for (Subscription eachSubscription : subscriptions) | 
 |  319     { | 
 |  320       if (eachSubscription.getProperty("url").toString().equals(url)) | 
 |  321       { | 
 |  322         return true; | 
 |  323       } | 
 |  324     } | 
 |  325     return false; | 
 |  326   } | 
 |  327  | 
 |  328   private volatile boolean enabled = true; | 
 |  329  | 
 |  330   public void setEnabled(final boolean enabled) | 
 |  331   { | 
 |  332     this.enabled = enabled; | 
 |  333   } | 
 |  334  | 
 |  335   public boolean isEnabled() | 
 |  336   { | 
 |  337     return enabled; | 
 |  338   } | 
 |  339  | 
 |  340   public String getAcceptableAdsSubscriptionURL() | 
 |  341   { | 
 |  342     return this.filterEngine.getPref("subscriptions_exceptionsurl").toString(); | 
 |  343   } | 
 |  344  | 
 |  345   public void setAcceptableAdsEnabled(final boolean enabled) | 
 |  346   { | 
 |  347     final String url = getAcceptableAdsSubscriptionURL(); | 
 |  348     final Subscription sub = this.filterEngine.getSubscription(url); | 
 |  349     if (sub != null) | 
 |  350     { | 
 |  351       if (enabled) | 
 |  352       { | 
 |  353         sub.addToList(); | 
 |  354       } | 
 |  355       else | 
 |  356       { | 
 |  357         sub.removeFromList(); | 
 |  358       } | 
 |  359     } | 
 |  360   } | 
 |  361  | 
 |  362   public String getDocumentationLink() | 
 |  363   { | 
 |  364     return this.filterEngine.getPref("documentation_link").toString(); | 
 |  365   } | 
 |  366  | 
 |  367   public boolean matches(final String fullUrl, final ContentType contentType, fi
     nal String[] referrerChainArray) | 
 |  368   { | 
 |  369     if (!enabled) | 
 |  370     { | 
 |  371       return false; | 
 |  372     } | 
 |  373  | 
 |  374     final Filter filter = this.filterEngine.matches(fullUrl, contentType, referr
     erChainArray); | 
 |  375  | 
 |  376     if (filter == null) | 
 |  377     { | 
 |  378       return false; | 
 |  379     } | 
 |  380  | 
 |  381     // hack: if there is no referrer, block only if filter is domain-specific | 
 |  382     // (to re-enable in-app ads blocking, proposed on 12.11.2012 Monday meeting) | 
 |  383     // (documentUrls contains the referrers on Android) | 
 |  384     try | 
 |  385     { | 
 |  386       if (referrerChainArray.length == 0 && (filter.getProperty("text").toString
     ()).contains("||")) | 
 |  387       { | 
 |  388         return false; | 
 |  389       } | 
 |  390     } catch (NullPointerException e) { | 
 |  391     } | 
 |  392  | 
 |  393     return filter.getType() != Filter.Type.EXCEPTION; | 
 |  394   } | 
 |  395  | 
 |  396   public boolean isDocumentWhitelisted(final String url, final String[] referrer
     ChainArray) | 
 |  397   { | 
 |  398     return this.filterEngine.isDocumentWhitelisted(url, referrerChainArray); | 
 |  399   } | 
 |  400  | 
 |  401   public boolean isDomainWhitelisted(final String url, final String[] referrerCh
     ainArray) | 
 |  402   { | 
 |  403     if (whitelistedDomains == null) | 
 |  404     { | 
 |  405       return false; | 
 |  406     } | 
 |  407  | 
 |  408     // using Set to remove duplicates | 
 |  409     Set<String> referrersAndResourceUrls = new HashSet<String>(); | 
 |  410     if (referrerChainArray != null) | 
 |  411     { | 
 |  412       referrersAndResourceUrls.addAll(Arrays.asList(referrerChainArray)); | 
 |  413     } | 
 |  414     referrersAndResourceUrls.add(url); | 
 |  415  | 
 |  416     for (String eachUrl : referrersAndResourceUrls) | 
 |  417     { | 
 |  418       if (whitelistedDomains.contains(filterEngine.getHostFromURL(eachUrl))) | 
 |  419       { | 
 |  420         return true; | 
 |  421       } | 
 |  422     } | 
 |  423  | 
 |  424     return false; | 
 |  425   } | 
 |  426  | 
 |  427   public boolean isElemhideWhitelisted(final String url, final String[] referrer
     ChainArray) | 
 |  428   { | 
 |  429     return this.filterEngine.isElemhideWhitelisted(url, referrerChainArray); | 
 |  430   } | 
 |  431  | 
 |  432   public List<String> getElementHidingSelectors(final String url, final String d
     omain, final String[] referrerChainArray) | 
 |  433   { | 
 |  434     /* | 
 |  435      * Issue 3364 (https://issues.adblockplus.org/ticket/3364) introduced the | 
 |  436      * feature to re-enabled element hiding. | 
 |  437      * | 
 |  438      * Nothing changes for Adblock Plus for Android, as `this.elemhideEnabled` | 
 |  439      * is `false`, which results in an empty list being returned and converted | 
 |  440      * into a `(String[])null` in AdblockPlus.java, which is the only place | 
 |  441      * this function here is called from Adblock Plus for Android. | 
 |  442      * | 
 |  443      * If element hiding is enabled, then this function now first checks for | 
 |  444      * possible whitelisting of either the document or element hiding for | 
 |  445      * the given URL and returns an empty list if so. This is needed to | 
 |  446      * ensure correct functioning of e.g. acceptable ads. | 
 |  447      */ | 
 |  448     if (!this.enabled | 
 |  449         || !this.elemhideEnabled | 
 |  450         || this.isDomainWhitelisted(url, referrerChainArray) | 
 |  451         || this.isDocumentWhitelisted(url, referrerChainArray) | 
 |  452         || this.isElemhideWhitelisted(url, referrerChainArray)) | 
 |  453     { | 
 |  454       return new ArrayList<String>(); | 
 |  455     } | 
 |  456     return this.filterEngine.getElementHidingSelectors(domain); | 
 |  457   } | 
 |  458  | 
 |  459   public void checkForUpdates() | 
 |  460   { | 
 |  461     this.filterEngine.forceUpdateCheck(this.updateCheckDoneCallback); | 
 |  462   } | 
 |  463  | 
 |  464   public FilterEngine getFilterEngine() | 
 |  465   { | 
 |  466     return this.filterEngine; | 
 |  467   } | 
 |  468  | 
 |  469   private List<String> whitelistedDomains; | 
 |  470  | 
 |  471   public void setWhitelistedDomains(List<String> domains) | 
 |  472   { | 
 |  473     this.whitelistedDomains = domains; | 
 |  474   } | 
 |  475  | 
 |  476   public List<String> getWhitelistedDomains() | 
 |  477   { | 
 |  478     return whitelistedDomains; | 
 |  479   } | 
 |  480 } | 
| OLD | NEW |