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