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