Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit.cts;
     18 
     19 import android.content.Context;
     20 import android.cts.util.LocationUtils;
     21 import android.cts.util.NullWebViewUtils;
     22 import android.cts.util.PollingCheck;
     23 import android.graphics.Bitmap;
     24 import android.location.Criteria;
     25 import android.location.Location;
     26 import android.location.LocationListener;
     27 import android.location.LocationManager;
     28 import android.location.LocationProvider;
     29 import android.os.Bundle;
     30 import android.os.Looper;
     31 import android.os.SystemClock;
     32 import android.test.ActivityInstrumentationTestCase2;
     33 import android.webkit.CookieManager;
     34 import android.webkit.CookieSyncManager;
     35 import android.webkit.GeolocationPermissions;
     36 import android.webkit.JavascriptInterface;
     37 import android.webkit.WebChromeClient;
     38 import android.webkit.WebResourceResponse;
     39 import android.webkit.WebView;
     40 import android.webkit.WebViewClient;
     41 import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
     42 import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
     43 
     44 import java.io.ByteArrayInputStream;
     45 import java.io.UnsupportedEncodingException;
     46 import java.util.concurrent.Callable;
     47 import java.util.Date;
     48 import java.util.List;
     49 import java.util.Random;
     50 import java.util.regex.Matcher;
     51 import java.util.regex.Pattern;
     52 import java.util.Set;
     53 import java.util.TreeSet;
     54 
     55 import junit.framework.Assert;
     56 
     57 public class GeolocationTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
     58 
     59     // TODO Write additional tests to cover:
     60     // - test that the errors are correct
     61     // - test that use of gps and network location is correct
     62 
     63     // The URLs does not matter since the tests will intercept the load, but it has to be a real
     64     // url, and different domains.
     65     private static final String URL_1 = "http://www.example.com";
     66     private static final String URL_2 = "http://www.example.org";
     67 
     68     private static final String JS_INTERFACE_NAME = "Android";
     69     private static final int POLLING_TIMEOUT = 60 * 1000;
     70     private static final int LOCATION_THREAD_UPDATE_WAIT_MS = 250;
     71 
     72     // static HTML page always injected instead of the url loaded
     73     private static final String RAW_HTML =
     74             "<!DOCTYPE html>\n" +
     75             "<html>\n" +
     76             "  <head>\n" +
     77             "    <title>Geolocation</title>\n" +
     78             "    <script>\n" +
     79             "      function gotPos(position) {\n" +
     80             "        " + JS_INTERFACE_NAME + ".gotLocation();\n" +
     81             "      }\n" +
     82             "      function initiate_getCurrentPosition() {\n" +
     83             "        navigator.geolocation.getCurrentPosition(\n" +
     84             "            gotPos,\n" +
     85             "            handle_errors,\n" +
     86             "            {maximumAge:1000});\n" +
     87             "      }\n" +
     88             "      function handle_errors(error) {\n" +
     89             "        switch(error.code) {\n" +
     90             "          case error.PERMISSION_DENIED:\n" +
     91             "            " + JS_INTERFACE_NAME + ".errorDenied(); break;\n" +
     92             "          case error.POSITION_UNAVAILABLE:\n" +
     93             "            " + JS_INTERFACE_NAME + ".errorUnavailable(); break;\n" +
     94             "          case error.TIMEOUT:\n" +
     95             "            " + JS_INTERFACE_NAME + ".errorTimeout(); break;\n" +
     96             "          default: break;\n" +
     97             "        }\n" +
     98             "      }\n" +
     99             "    </script>\n" +
    100             "  </head>\n" +
    101             "  <body onload=\"initiate_getCurrentPosition();\">\n" +
    102             "  </body>\n" +
    103             "</html>";
    104 
    105     private JavascriptStatusReceiver mJavascriptStatusReceiver;
    106     private LocationManager mLocationManager;
    107     private WebViewOnUiThread mOnUiThread;
    108     private Thread mLocationUpdateThread;
    109     private volatile boolean mLocationUpdateThreadExitRequested;
    110     private List<String> mProviders;
    111 
    112     public GeolocationTest() throws Exception {
    113         super("com.android.cts.webkit", WebViewCtsActivity.class);
    114     }
    115 
    116     // Both this test and WebViewOnUiThread need to override some of the methods on WebViewClient,
    117     // so this test sublclasses the WebViewClient from WebViewOnUiThread
    118     private static class InterceptClient extends WaitForLoadedClient {
    119 
    120         public InterceptClient(WebViewOnUiThread webViewOnUiThread) throws Exception {
    121             super(webViewOnUiThread);
    122         }
    123 
    124         @Override
    125         public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
    126             // Intercept all page loads with the same geolocation enabled page
    127             try {
    128                 return new WebResourceResponse("text/html", "utf-8",
    129                     new ByteArrayInputStream(RAW_HTML.getBytes("UTF-8")));
    130             } catch(java.io.UnsupportedEncodingException e) {
    131                 return null;
    132             }
    133         }
    134     }
    135 
    136     @Override
    137     protected void setUp() throws Exception {
    138         super.setUp();
    139 
    140         LocationUtils.registerMockLocationProvider(getInstrumentation(), true);
    141         WebView webview = getActivity().getWebView();
    142 
    143         if (webview != null) {
    144             // Set up a WebView with JavaScript and Geolocation enabled
    145             final String GEO_DIR = "geo_test";
    146             mOnUiThread = new WebViewOnUiThread(this, webview);
    147             mOnUiThread.getSettings().setJavaScriptEnabled(true);
    148             mOnUiThread.getSettings().setGeolocationEnabled(true);
    149             mOnUiThread.getSettings().setGeolocationDatabasePath(
    150                     getActivity().getApplicationContext().getDir(GEO_DIR, 0).getPath());
    151 
    152             // Add a JsInterface to report back to the test when a location is received
    153             mJavascriptStatusReceiver = new JavascriptStatusReceiver();
    154             mOnUiThread.addJavascriptInterface(mJavascriptStatusReceiver, JS_INTERFACE_NAME);
    155 
    156             // Always intercept all loads with the same geolocation test page
    157             mOnUiThread.setWebViewClient(new InterceptClient(mOnUiThread));
    158             // Clear all permissions before each test
    159             GeolocationPermissions.getInstance().clearAll();
    160             // Cache this mostly because the lookup is two lines of code
    161             mLocationManager = (LocationManager)getActivity().getApplicationContext()
    162                     .getSystemService(Context.LOCATION_SERVICE);
    163             // Add a test provider before each test to inject a location
    164             mProviders = mLocationManager.getAllProviders();
    165             for (String provider : mProviders) {
    166                 // Can't mock passive provider.
    167                 if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
    168                     mProviders.remove(provider);
    169                     break;
    170                 }
    171             }
    172             mProviders.add(LocationManager.FUSED_PROVIDER);
    173             addTestProviders();
    174         }
    175     }
    176 
    177     @Override
    178     protected void tearDown() throws Exception {
    179         stopUpdateLocationThread();
    180         if (mProviders != null) {
    181             // Remove the test provider after each test
    182             for (String provider : mProviders) {
    183                 try {
    184                     // Work around b/11446702 by clearing the test provider before removing it
    185                     mLocationManager.clearTestProviderEnabled(provider);
    186                     mLocationManager.removeTestProvider(provider);
    187                 } catch (IllegalArgumentException e) {} // Not much to do about this
    188             }
    189         }
    190         LocationUtils.registerMockLocationProvider(getInstrumentation(), false);
    191 
    192         if (mOnUiThread != null) {
    193             mOnUiThread.cleanUp();
    194         }
    195         // This will null all member and static variables
    196         super.tearDown();
    197     }
    198 
    199     private void addTestProviders() {
    200         for (String providerName : mProviders) {
    201             LocationProvider provider = mLocationManager.getProvider(providerName);
    202             mLocationManager.addTestProvider(provider.getName(),
    203                     provider.requiresNetwork(), //requiresNetwork,
    204                     provider.requiresSatellite(), // requiresSatellite,
    205                     provider.requiresCell(),  // requiresCell,
    206                     provider.hasMonetaryCost(), // hasMonetaryCost,
    207                     provider.supportsAltitude(), // supportsAltitude,
    208                     provider.supportsSpeed(), // supportsSpeed,
    209                     provider.supportsBearing(), // supportsBearing,
    210                     provider.getPowerRequirement(), // powerRequirement
    211                     provider.getAccuracy()); // accuracy
    212             mLocationManager.setTestProviderEnabled(provider.getName(), true);
    213         }
    214     }
    215 
    216     private void startUpdateLocationThread() {
    217         // Only start the thread once
    218         if (mLocationUpdateThread == null) {
    219             mLocationUpdateThreadExitRequested = false;
    220             mLocationUpdateThread = new Thread() {
    221                 @Override
    222                 public void run() {
    223                     while (!mLocationUpdateThreadExitRequested) {
    224                         try {
    225                             Thread.sleep(LOCATION_THREAD_UPDATE_WAIT_MS);
    226                         } catch(Exception e) {
    227                             // Do nothing, an extra update is no problem
    228                         }
    229                         updateLocation();
    230                     }
    231                 }
    232             };
    233             mLocationUpdateThread.start();
    234         }
    235     }
    236 
    237     private void stopUpdateLocationThread() {
    238         // Only stop the thread if it was started
    239         if (mLocationUpdateThread != null) {
    240             mLocationUpdateThreadExitRequested = true;
    241             try {
    242                 mLocationUpdateThread.join();
    243             } catch (InterruptedException e) {
    244                 // Do nothing
    245             }
    246             mLocationUpdateThread = null;
    247         }
    248     }
    249 
    250     // Update location with a fixed latitude and longtitude, sets the time to the current time.
    251     private void updateLocation() {
    252         for (int i = 0; i < mProviders.size(); i++) {
    253             Location location = new Location(mProviders.get(i));
    254             location.setLatitude(40);
    255             location.setLongitude(40);
    256             location.setAccuracy(1.0f);
    257             location.setTime(java.lang.System.currentTimeMillis());
    258             location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
    259             mLocationManager.setTestProviderLocation(mProviders.get(i), location);
    260         }
    261     }
    262 
    263     // Need to set the location just after loading the url. Setting it after each load instead of
    264     // using a maximum age.
    265     private void loadUrlAndUpdateLocation(String url) {
    266         mOnUiThread.loadUrlAndWaitForCompletion(url);
    267         startUpdateLocationThread();
    268     }
    269 
    270     // WebChromeClient that accepts each location for one load. WebChromeClient is used in
    271     // WebViewOnUiThread to detect when the page is loaded, so subclassing the one used there.
    272     private static class TestSimpleGeolocationRequestWebChromeClient
    273                 extends WaitForProgressClient {
    274         private boolean mReceivedRequest = false;
    275         private final boolean mAccept;
    276         private final boolean mRetain;
    277 
    278         public TestSimpleGeolocationRequestWebChromeClient(
    279                 WebViewOnUiThread webViewOnUiThread, boolean accept, boolean retain) {
    280             super(webViewOnUiThread);
    281             this.mAccept = accept;
    282             this.mRetain = retain;
    283         }
    284 
    285         @Override
    286         public void onGeolocationPermissionsShowPrompt(
    287                 String origin, GeolocationPermissions.Callback callback) {
    288             mReceivedRequest = true;
    289             callback.invoke(origin, mAccept, mRetain);
    290         }
    291     }
    292 
    293     // Test loading a page and accepting the domain for one load
    294     public void testSimpleGeolocationRequestAcceptOnce() throws Exception {
    295         if (!NullWebViewUtils.isWebViewAvailable()) {
    296             return;
    297         }
    298         final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptOnce =
    299                 new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, false);
    300         mOnUiThread.setWebChromeClient(chromeClientAcceptOnce);
    301         loadUrlAndUpdateLocation(URL_1);
    302         Callable<Boolean> receivedRequest = new Callable<Boolean>() {
    303             @Override
    304             public Boolean call() {
    305                 return chromeClientAcceptOnce.mReceivedRequest;
    306             }
    307         };
    308         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    309         Callable<Boolean> receivedLocation = new Callable<Boolean>() {
    310             @Override
    311             public Boolean call() {
    312                 return mJavascriptStatusReceiver.mHasPosition;
    313             }
    314         };
    315         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
    316         chromeClientAcceptOnce.mReceivedRequest = false;
    317         // Load URL again, should receive callback again
    318         loadUrlAndUpdateLocation(URL_1);
    319         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    320         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
    321     }
    322 
    323     private static class OriginCheck extends PollingCheck implements
    324             android.webkit.ValueCallback<Set<String>> {
    325 
    326         private boolean mReceived = false;
    327         private final Set<String> mExpectedValue;
    328         private Set<String> mReceivedValue = null;
    329 
    330         public OriginCheck(Set<String> val) {
    331             mExpectedValue = val;
    332         }
    333 
    334         @Override
    335         protected boolean check() {
    336             if (!mReceived) return false;
    337             if (mExpectedValue.equals(mReceivedValue)) return true;
    338             if (mExpectedValue.size() != mReceivedValue.size()) return false;
    339             // Origins can have different strings even if they represent the same origin,
    340             // for example http://www.example.com is the same origin as http://www.example.com/
    341             // and they are both valid representations
    342             for (String origin : mReceivedValue) {
    343                 if (mExpectedValue.contains(origin)) continue;
    344                 if (origin.endsWith("/")) {
    345                     if (mExpectedValue.contains(origin.substring(0, origin.length() - 1))) {
    346                         continue;
    347                     }
    348                 } else {
    349                     if (mExpectedValue.contains(origin + "/")) continue;
    350                 }
    351                 return false;
    352             }
    353             return true;
    354         }
    355         @Override
    356         public void onReceiveValue(Set<String> value) {
    357             mReceived = true;
    358             mReceivedValue = value;
    359         }
    360     }
    361 
    362     // Class that waits and checks for a particular value being received
    363     private static class BooleanCheck extends PollingCheck implements
    364             android.webkit.ValueCallback<Boolean> {
    365 
    366         private boolean mReceived = false;
    367         private final boolean mExpectedValue;
    368         private boolean mReceivedValue;
    369 
    370         public BooleanCheck(boolean val) {
    371             mExpectedValue = val;
    372         }
    373 
    374         @Override
    375         protected boolean check() {
    376             return mReceived && mReceivedValue == mExpectedValue;
    377         }
    378 
    379         @Override
    380         public void onReceiveValue(Boolean value) {
    381             mReceived = true;
    382             mReceivedValue = value;
    383         }
    384     }
    385 
    386     // Test loading a page and retaining the domain forever
    387     public void testSimpleGeolocationRequestAcceptAlways() throws Exception {
    388         if (!NullWebViewUtils.isWebViewAvailable()) {
    389             return;
    390         }
    391         final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptAlways =
    392                 new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, true);
    393         mOnUiThread.setWebChromeClient(chromeClientAcceptAlways);
    394         // Load url once, and the callback should accept the domain for all future loads
    395         loadUrlAndUpdateLocation(URL_1);
    396         Callable<Boolean> receivedRequest = new Callable<Boolean>() {
    397             @Override
    398             public Boolean call() {
    399                 return chromeClientAcceptAlways.mReceivedRequest;
    400             }
    401         };
    402         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    403         Callable<Boolean> receivedLocation = new Callable<Boolean>() {
    404             @Override
    405             public Boolean call() {
    406                 return mJavascriptStatusReceiver.mHasPosition;
    407             }
    408         };
    409         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
    410         chromeClientAcceptAlways.mReceivedRequest = false;
    411         mJavascriptStatusReceiver.clearState();
    412         // Load the same URL again
    413         loadUrlAndUpdateLocation(URL_1);
    414         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
    415         // Assert prompt for geolocation permission is not called the second time
    416         assertFalse(chromeClientAcceptAlways.mReceivedRequest);
    417         // Check that the permission is in GeolocationPermissions
    418         BooleanCheck trueCheck = new BooleanCheck(true);
    419         GeolocationPermissions.getInstance().getAllowed(URL_1, trueCheck);
    420         trueCheck.run();
    421         Set<String> acceptedOrigins = new TreeSet<String>();
    422         acceptedOrigins.add(URL_1);
    423         OriginCheck originCheck = new OriginCheck(acceptedOrigins);
    424         GeolocationPermissions.getInstance().getOrigins(originCheck);
    425         originCheck.run();
    426 
    427         // URL_2 should get a prompt
    428         chromeClientAcceptAlways.mReceivedRequest = false;
    429         loadUrlAndUpdateLocation(URL_2);
    430         // Checking the callback for geolocation permission prompt is called
    431         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    432         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
    433         acceptedOrigins.add(URL_2);
    434         originCheck = new OriginCheck(acceptedOrigins);
    435         GeolocationPermissions.getInstance().getOrigins(originCheck);
    436         originCheck.run();
    437         // Remove a domain manually that was added by the callback
    438         GeolocationPermissions.getInstance().clear(URL_1);
    439         acceptedOrigins.remove(URL_1);
    440         originCheck = new OriginCheck(acceptedOrigins);
    441         GeolocationPermissions.getInstance().getOrigins(originCheck);
    442         originCheck.run();
    443     }
    444 
    445     // Test the GeolocationPermissions API
    446     public void testGeolocationPermissions() {
    447         if (!NullWebViewUtils.isWebViewAvailable()) {
    448             return;
    449         }
    450         Set<String> acceptedOrigins = new TreeSet<String>();
    451         BooleanCheck falseCheck = new BooleanCheck(false);
    452         GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
    453         falseCheck.run();
    454         OriginCheck originCheck = new OriginCheck(acceptedOrigins);
    455         GeolocationPermissions.getInstance().getOrigins(originCheck);
    456         originCheck.run();
    457 
    458         // Remove a domain that has not been allowed
    459         GeolocationPermissions.getInstance().clear(URL_2);
    460         acceptedOrigins.remove(URL_2);
    461         originCheck = new OriginCheck(acceptedOrigins);
    462         GeolocationPermissions.getInstance().getOrigins(originCheck);
    463         originCheck.run();
    464 
    465         // Add a domain
    466         acceptedOrigins.add(URL_2);
    467         GeolocationPermissions.getInstance().allow(URL_2);
    468         originCheck = new OriginCheck(acceptedOrigins);
    469         GeolocationPermissions.getInstance().getOrigins(originCheck);
    470         originCheck.run();
    471         BooleanCheck trueCheck = new BooleanCheck(true);
    472         GeolocationPermissions.getInstance().getAllowed(URL_2, trueCheck);
    473         trueCheck.run();
    474 
    475         // Add a domain
    476         acceptedOrigins.add(URL_1);
    477         GeolocationPermissions.getInstance().allow(URL_1);
    478         originCheck = new OriginCheck(acceptedOrigins);
    479         GeolocationPermissions.getInstance().getOrigins(originCheck);
    480         originCheck.run();
    481 
    482         // Remove a domain that has been allowed
    483         GeolocationPermissions.getInstance().clear(URL_2);
    484         acceptedOrigins.remove(URL_2);
    485         originCheck = new OriginCheck(acceptedOrigins);
    486         GeolocationPermissions.getInstance().getOrigins(originCheck);
    487         originCheck.run();
    488         falseCheck = new BooleanCheck(false);
    489         GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
    490         falseCheck.run();
    491 
    492         // Try to clear all domains
    493         GeolocationPermissions.getInstance().clearAll();
    494         acceptedOrigins.clear();
    495         originCheck = new OriginCheck(acceptedOrigins);
    496         GeolocationPermissions.getInstance().getOrigins(originCheck);
    497         originCheck.run();
    498 
    499         // Add a domain
    500         acceptedOrigins.add(URL_1);
    501         GeolocationPermissions.getInstance().allow(URL_1);
    502         originCheck = new OriginCheck(acceptedOrigins);
    503         GeolocationPermissions.getInstance().getOrigins(originCheck);
    504         originCheck.run();
    505     }
    506 
    507     // Test loading pages and checks rejecting once and recjecting the domain forever
    508     public void testSimpleGeolocationRequestReject() throws Exception {
    509         if (!NullWebViewUtils.isWebViewAvailable()) {
    510             return;
    511         }
    512         final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectOnce =
    513                 new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, false);
    514         mOnUiThread.setWebChromeClient(chromeClientRejectOnce);
    515         // Load url once, and the callback should reject it once
    516         mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
    517         Callable<Boolean> receivedRequest = new Callable<Boolean>() {
    518             @Override
    519             public Boolean call() {
    520                 return chromeClientRejectOnce.mReceivedRequest;
    521             }
    522         };
    523         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    524         Callable<Boolean> locationDenied = new Callable<Boolean>() {
    525             @Override
    526             public Boolean call() {
    527                 return mJavascriptStatusReceiver.mDenied;
    528             }
    529         };
    530         PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
    531         // Same result should happen on next run
    532         chromeClientRejectOnce.mReceivedRequest = false;
    533         mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
    534         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    535         PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
    536 
    537         // Try to reject forever
    538         final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectAlways =
    539             new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, true);
    540         mOnUiThread.setWebChromeClient(chromeClientRejectAlways);
    541         mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
    542         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    543         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
    544         // second load should now not get a prompt
    545         chromeClientRejectAlways.mReceivedRequest = false;
    546         mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
    547         PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
    548         PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
    549 
    550         // Test if it gets added to origins
    551         Set<String> acceptedOrigins = new TreeSet<String>();
    552         acceptedOrigins.add(URL_2);
    553         OriginCheck domainCheck = new OriginCheck(acceptedOrigins);
    554         GeolocationPermissions.getInstance().getOrigins(domainCheck);
    555         domainCheck.run();
    556         // And now check that getAllowed returns false
    557         BooleanCheck falseCheck = new BooleanCheck(false);
    558         GeolocationPermissions.getInstance().getAllowed(URL_1, falseCheck);
    559         falseCheck.run();
    560     }
    561 
    562     // Object added to the page via AddJavascriptInterface() that is used by the test Javascript to
    563     // notify back to Java when a location or error is received.
    564     public final static class JavascriptStatusReceiver {
    565         public volatile boolean mHasPosition = false;
    566         public volatile boolean mDenied = false;
    567         public volatile boolean mUnavailable = false;
    568         public volatile boolean mTimeout = false;
    569 
    570         public void clearState() {
    571             mHasPosition = false;
    572             mDenied = false;
    573             mUnavailable = false;
    574             mTimeout = false;
    575         }
    576 
    577         @JavascriptInterface
    578         public void errorDenied() {
    579             mDenied = true;
    580         }
    581 
    582         @JavascriptInterface
    583         public void errorUnavailable() {
    584             mUnavailable = true;
    585         }
    586 
    587         @JavascriptInterface
    588         public void errorTimeout() {
    589             mTimeout = true;
    590         }
    591 
    592         @JavascriptInterface
    593         public void gotLocation() {
    594             mHasPosition = true;
    595         }
    596     }
    597 }
    598