TestShellActivity.java revision 2eea5f3dc8f96059a60a97052c20004b640eb1c3
1/* 2 * Copyright (C) 2007 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 17package com.android.dumprendertree; 18 19import com.android.dumprendertree.forwarder.ForwardService; 20 21import android.app.Activity; 22import android.app.AlertDialog; 23import android.content.Context; 24import android.content.DialogInterface; 25import android.content.Intent; 26import android.content.DialogInterface.OnClickListener; 27import android.graphics.Bitmap; 28import android.net.http.SslError; 29import android.os.Bundle; 30import android.os.Handler; 31import android.os.Message; 32import android.util.Log; 33import android.view.ViewGroup; 34import android.webkit.GeolocationPermissions; 35import android.webkit.HttpAuthHandler; 36import android.webkit.JsPromptResult; 37import android.webkit.JsResult; 38import android.webkit.SslErrorHandler; 39import android.webkit.WebChromeClient; 40import android.webkit.WebSettings; 41import android.webkit.WebStorage; 42import android.webkit.WebView; 43import android.webkit.WebViewClient; 44import android.widget.LinearLayout; 45 46import java.io.BufferedReader; 47import java.io.File; 48import java.io.FileOutputStream; 49import java.io.FileReader; 50import java.io.IOException; 51import java.net.MalformedURLException; 52import java.net.URL; 53import java.util.HashMap; 54import java.util.Map; 55import java.util.Vector; 56 57public class TestShellActivity extends Activity implements LayoutTestController { 58 59 static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP} 60 61 public class AsyncHandler extends Handler { 62 @Override 63 public void handleMessage(Message msg) { 64 if (msg.what == MSG_TIMEOUT) { 65 mTimedOut = true; 66 if(mCallback != null) 67 mCallback.timedOut(mWebView.getUrl()); 68 requestWebKitData(); 69 return; 70 } else if (msg.what == MSG_WEBKIT_DATA) { 71 TestShellActivity.this.dump(mTimedOut, (String)msg.obj); 72 return; 73 } 74 75 super.handleMessage(msg); 76 } 77 } 78 79 public void requestWebKitData() { 80 Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA); 81 82 if (mRequestedWebKitData) 83 throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl()); 84 85 mRequestedWebKitData = true; 86 switch (mDumpDataType) { 87 case DUMP_AS_TEXT: 88 mWebView.documentAsText(callback); 89 break; 90 case EXT_REPR: 91 mWebView.externalRepresentation(callback); 92 break; 93 default: 94 finished(); 95 break; 96 } 97 } 98 99 public void clearCache() { 100 mWebView.freeMemory(); 101 } 102 103 @Override 104 protected void onCreate(Bundle icicle) { 105 super.onCreate(icicle); 106 107 LinearLayout contentView = new LinearLayout(this); 108 contentView.setOrientation(LinearLayout.VERTICAL); 109 setContentView(contentView); 110 111 mWebView = new WebView(this); 112 mEventSender = new WebViewEventSender(mWebView); 113 mCallbackProxy = new CallbackProxy(mEventSender, this); 114 115 mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); 116 mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); 117 setupWebViewForLayoutTests(mWebView, mCallbackProxy); 118 119 contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f)); 120 121 mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 122 123 // Expose window.gc function to JavaScript. JSC build exposes 124 // this function by default, but V8 requires the flag to turn it on. 125 // WebView::setJsFlags is noop in JSC build. 126 mWebView.setJsFlags("--expose_gc"); 127 128 mHandler = new AsyncHandler(); 129 130 Intent intent = getIntent(); 131 if (intent != null) { 132 executeIntent(intent); 133 } 134 } 135 136 @Override 137 protected void onNewIntent(Intent intent) { 138 super.onNewIntent(intent); 139 executeIntent(intent); 140 } 141 142 private void executeIntent(Intent intent) { 143 resetTestStatus(); 144 if (!Intent.ACTION_VIEW.equals(intent.getAction())) { 145 return; 146 } 147 148 mTestUrl = intent.getStringExtra(TEST_URL); 149 if (mTestUrl == null) { 150 mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST); 151 if(mUiAutoTestPath != null) { 152 beginUiAutoTest(); 153 } 154 return; 155 } 156 157 mResultFile = intent.getStringExtra(RESULT_FILE); 158 mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0); 159 160 Log.v(LOGTAG, " Loading " + mTestUrl); 161 mWebView.loadUrl(mTestUrl); 162 163 if (mTimeoutInMillis > 0) { 164 // Create a timeout timer 165 Message m = mHandler.obtainMessage(MSG_TIMEOUT); 166 mHandler.sendMessageDelayed(m, mTimeoutInMillis); 167 } 168 } 169 170 private void beginUiAutoTest() { 171 try { 172 mTestListReader = new BufferedReader( 173 new FileReader(mUiAutoTestPath)); 174 } catch (IOException ioe) { 175 Log.e(LOGTAG, "Failed to open test list for read.", ioe); 176 finishUiAutoTest(); 177 return; 178 } 179 moveToNextTest(); 180 } 181 182 private void finishUiAutoTest() { 183 try { 184 if(mTestListReader != null) 185 mTestListReader.close(); 186 } catch (IOException ioe) { 187 Log.w(LOGTAG, "Failed to close test list file.", ioe); 188 } 189 ForwardService.getForwardService().stopForwardService(); 190 finished(); 191 } 192 193 private void moveToNextTest() { 194 String url = null; 195 try { 196 url = mTestListReader.readLine(); 197 } catch (IOException ioe) { 198 Log.e(LOGTAG, "Failed to read next test.", ioe); 199 finishUiAutoTest(); 200 return; 201 } 202 if (url == null) { 203 mUiAutoTestPath = null; 204 finishUiAutoTest(); 205 AlertDialog.Builder builder = new AlertDialog.Builder(this); 206 builder.setMessage("All tests finished. Exit?") 207 .setCancelable(false) 208 .setPositiveButton("Yes", new OnClickListener(){ 209 public void onClick(DialogInterface dialog, int which) { 210 TestShellActivity.this.finish(); 211 } 212 }) 213 .setNegativeButton("No", new OnClickListener(){ 214 public void onClick(DialogInterface dialog, int which) { 215 dialog.cancel(); 216 } 217 }); 218 builder.create().show(); 219 return; 220 } 221 Intent intent = new Intent(Intent.ACTION_VIEW); 222 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 223 intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url)); 224 intent.putExtra(TIMEOUT_IN_MILLIS, 10000); 225 executeIntent(intent); 226 } 227 228 @Override 229 protected void onStop() { 230 super.onStop(); 231 mWebView.stopLoading(); 232 } 233 234 @Override 235 protected void onDestroy() { 236 super.onDestroy(); 237 mWebView.destroy(); 238 mWebView = null; 239 } 240 241 @Override 242 public void onLowMemory() { 243 super.onLowMemory(); 244 Log.e(LOGTAG, "Low memory, clearing caches"); 245 mWebView.freeMemory(); 246 } 247 248 // Dump the page 249 public void dump(boolean timeout, String webkitData) { 250 mDumpWebKitData = true; 251 if (mResultFile == null || mResultFile.length() == 0) { 252 finished(); 253 return; 254 } 255 256 try { 257 File parentDir = new File(mResultFile).getParentFile(); 258 if (!parentDir.exists()) { 259 parentDir.mkdirs(); 260 } 261 262 FileOutputStream os = new FileOutputStream(mResultFile); 263 if (timeout) { 264 Log.w("Layout test: Timeout", mResultFile); 265 os.write(TIMEOUT_STR.getBytes()); 266 os.write('\n'); 267 } 268 if (mDumpTitleChanges) 269 os.write(mTitleChanges.toString().getBytes()); 270 if (mDialogStrings != null) 271 os.write(mDialogStrings.toString().getBytes()); 272 mDialogStrings = null; 273 if (mDatabaseCallbackStrings != null) 274 os.write(mDatabaseCallbackStrings.toString().getBytes()); 275 mDatabaseCallbackStrings = null; 276 if (mConsoleMessages != null) 277 os.write(mConsoleMessages.toString().getBytes()); 278 mConsoleMessages = null; 279 if (webkitData != null) 280 os.write(webkitData.getBytes()); 281 os.flush(); 282 os.close(); 283 } catch (IOException ex) { 284 Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage()); 285 } 286 287 finished(); 288 } 289 290 public void setCallback(TestShellCallback callback) { 291 mCallback = callback; 292 } 293 294 public boolean finished() { 295 if (canMoveToNextTest()) { 296 mHandler.removeMessages(MSG_TIMEOUT); 297 if (mUiAutoTestPath != null) { 298 //don't really finish here 299 moveToNextTest(); 300 } else { 301 if (mCallback != null) { 302 mCallback.finished(); 303 } 304 } 305 return true; 306 } 307 return false; 308 } 309 310 public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) { 311 mDefaultDumpDataType = defaultDumpDataType; 312 } 313 314 // ....................................... 315 // LayoutTestController Functions 316 public void dumpAsText() { 317 mDumpDataType = DumpDataType.DUMP_AS_TEXT; 318 if (mWebView != null) { 319 String url = mWebView.getUrl(); 320 Log.v(LOGTAG, "dumpAsText called: "+url); 321 } 322 } 323 324 public void waitUntilDone() { 325 mWaitUntilDone = true; 326 String url = mWebView.getUrl(); 327 Log.v(LOGTAG, "waitUntilDone called: " + url); 328 } 329 330 public void notifyDone() { 331 String url = mWebView.getUrl(); 332 Log.v(LOGTAG, "notifyDone called: " + url); 333 if (mWaitUntilDone) { 334 mWaitUntilDone = false; 335 mChromeClient.onProgressChanged(mWebView, 101); 336 } 337 } 338 339 public void display() { 340 mWebView.invalidate(); 341 } 342 343 public void clearBackForwardList() { 344 mWebView.clearHistory(); 345 346 } 347 348 public void dumpBackForwardList() { 349 //printf("\n============== Back Forward List ==============\n"); 350 // mWebHistory 351 //printf("===============================================\n"); 352 353 } 354 355 public void dumpChildFrameScrollPositions() { 356 // TODO Auto-generated method stub 357 358 } 359 360 public void dumpEditingCallbacks() { 361 // TODO Auto-generated method stub 362 363 } 364 365 public void dumpSelectionRect() { 366 // TODO Auto-generated method stub 367 368 } 369 370 public void dumpTitleChanges() { 371 if (!mDumpTitleChanges) { 372 mTitleChanges = new StringBuffer(); 373 } 374 mDumpTitleChanges = true; 375 } 376 377 public void keepWebHistory() { 378 if (!mKeepWebHistory) { 379 mWebHistory = new Vector(); 380 } 381 mKeepWebHistory = true; 382 } 383 384 public void queueBackNavigation(int howfar) { 385 // TODO Auto-generated method stub 386 387 } 388 389 public void queueForwardNavigation(int howfar) { 390 // TODO Auto-generated method stub 391 392 } 393 394 public void queueLoad(String Url, String frameTarget) { 395 // TODO Auto-generated method stub 396 397 } 398 399 public void queueReload() { 400 mWebView.reload(); 401 } 402 403 public void queueScript(String scriptToRunInCurrentContext) { 404 mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); 405 } 406 407 public void repaintSweepHorizontally() { 408 // TODO Auto-generated method stub 409 410 } 411 412 public void setAcceptsEditing(boolean b) { 413 // TODO Auto-generated method stub 414 415 } 416 417 public void setMainFrameIsFirstResponder(boolean b) { 418 // TODO Auto-generated method stub 419 420 } 421 422 public void setWindowIsKey(boolean b) { 423 // This is meant to show/hide the window. The best I can find 424 // is setEnabled() 425 mWebView.setEnabled(b); 426 } 427 428 public void testRepaint() { 429 mWebView.invalidate(); 430 } 431 432 public void dumpDatabaseCallbacks() { 433 Log.v(LOGTAG, "dumpDatabaseCallbacks called."); 434 mDumpDatabaseCallbacks = true; 435 } 436 437 public void setCanOpenWindows() { 438 Log.v(LOGTAG, "setCanOpenWindows called."); 439 mCanOpenWindows = true; 440 } 441 442 /** 443 * Sets the Geolocation permission state to be used for all future requests. 444 */ 445 public void setGeolocationPermission(boolean allow) { 446 mGeolocationPermissionSet = true; 447 mGeolocationPermission = allow; 448 } 449 450 private final WebViewClient mViewClient = new WebViewClient(){ 451 @Override 452 public void onPageFinished(WebView view, String url) { 453 Log.v(LOGTAG, "onPageFinished, url=" + url); 454 mPageFinished = true; 455 // Calling finished() will check if we've met all the conditions for completing 456 // this test and move to the next one if we are ready. 457 if (finished()) { 458 return; 459 } 460 super.onPageFinished(view, url); 461 } 462 463 @Override 464 public void onPageStarted(WebView view, String url, Bitmap favicon) { 465 Log.v(LOGTAG, "onPageStarted, url=" + url); 466 mPageFinished = false; 467 super.onPageStarted(view, url, favicon); 468 } 469 470 @Override 471 public void onReceivedError(WebView view, int errorCode, String description, 472 String failingUrl) { 473 Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode 474 + ", desc=" + description + ", url=" + failingUrl); 475 super.onReceivedError(view, errorCode, description, failingUrl); 476 } 477 478 @Override 479 public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, 480 String host, String realm) { 481 handler.cancel(); 482 } 483 484 @Override 485 public void onReceivedSslError(WebView view, SslErrorHandler handler, 486 SslError error) { 487 handler.proceed(); 488 } 489 }; 490 491 492 private final WebChromeClient mChromeClient = new WebChromeClient() { 493 @Override 494 public void onProgressChanged(WebView view, int newProgress) { 495 496 // notifyDone calls this with 101%. We only want to update this flag if this 497 // is the real call from WebCore. 498 if (newProgress == 100) { 499 mOneHundredPercentComplete = true; 500 } 501 502 // With the flag updated, we can now proceed as normal whether the progress update came from 503 // WebCore or notifyDone. 504 if (newProgress >= 100) { 505 // finished() will check if we are ready to move to the next test and do so if we are. 506 if (finished()) { 507 return; 508 } 509 510 if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) { 511 String url = mWebView.getUrl(); 512 Log.v(LOGTAG, "Finished: "+ url); 513 requestWebKitData(); 514 } else { 515 String url = mWebView.getUrl(); 516 if (mTimedOut) { 517 Log.v(LOGTAG, "Timed out before finishing: " + url); 518 } else if (mWaitUntilDone) { 519 Log.v(LOGTAG, "Waiting for notifyDone: " + url); 520 } else if (mRequestedWebKitData) { 521 Log.v(LOGTAG, "Requested webkit data ready: " + url); 522 } 523 } 524 } 525 } 526 527 @Override 528 public void onReceivedTitle(WebView view, String title) { 529 if (title.length() > 30) 530 title = "..."+title.substring(title.length()-30); 531 setTitle(title); 532 if (mDumpTitleChanges) { 533 mTitleChanges.append("TITLE CHANGED: "); 534 mTitleChanges.append(title); 535 mTitleChanges.append("\n"); 536 } 537 } 538 539 @Override 540 public boolean onJsAlert(WebView view, String url, String message, 541 JsResult result) { 542 if (mDialogStrings == null) { 543 mDialogStrings = new StringBuffer(); 544 } 545 mDialogStrings.append("ALERT: "); 546 mDialogStrings.append(message); 547 mDialogStrings.append('\n'); 548 result.confirm(); 549 return true; 550 } 551 552 @Override 553 public boolean onJsConfirm(WebView view, String url, String message, 554 JsResult result) { 555 if (mDialogStrings == null) { 556 mDialogStrings = new StringBuffer(); 557 } 558 mDialogStrings.append("CONFIRM: "); 559 mDialogStrings.append(message); 560 mDialogStrings.append('\n'); 561 result.confirm(); 562 return true; 563 } 564 565 @Override 566 public boolean onJsPrompt(WebView view, String url, String message, 567 String defaultValue, JsPromptResult result) { 568 if (mDialogStrings == null) { 569 mDialogStrings = new StringBuffer(); 570 } 571 mDialogStrings.append("PROMPT: "); 572 mDialogStrings.append(message); 573 mDialogStrings.append(", default text: "); 574 mDialogStrings.append(defaultValue); 575 mDialogStrings.append('\n'); 576 result.confirm(); 577 return true; 578 } 579 580 @Override 581 public boolean onJsTimeout() { 582 Log.v(LOGTAG, "JavaScript timeout"); 583 return false; 584 } 585 586 @Override 587 public void onExceededDatabaseQuota(String url_str, 588 String databaseIdentifier, long currentQuota, 589 long estimatedSize, long totalUsedQuota, 590 WebStorage.QuotaUpdater callback) { 591 if (mDumpDatabaseCallbacks) { 592 if (mDatabaseCallbackStrings == null) { 593 mDatabaseCallbackStrings = new StringBuffer(); 594 } 595 596 String protocol = ""; 597 String host = ""; 598 int port = 0; 599 600 try { 601 URL url = new URL(url_str); 602 protocol = url.getProtocol(); 603 host = url.getHost(); 604 if (url.getPort() > -1) { 605 port = url.getPort(); 606 } 607 } catch (MalformedURLException e) {} 608 609 String databaseCallbackString = 610 "UI DELEGATE DATABASE CALLBACK: " + 611 "exceededDatabaseQuotaForSecurityOrigin:{" + protocol + 612 ", " + host + ", " + port + "} database:" + 613 databaseIdentifier + "\n"; 614 Log.v(LOGTAG, "LOG: "+databaseCallbackString); 615 mDatabaseCallbackStrings.append(databaseCallbackString); 616 } 617 // Give 5MB more quota. 618 callback.updateQuota(currentQuota + 1024 * 1024 * 5); 619 } 620 621 /** 622 * Instructs the client to show a prompt to ask the user to set the 623 * Geolocation permission state for the specified origin. 624 */ 625 @Override 626 public void onGeolocationPermissionsShowPrompt(String origin, 627 GeolocationPermissions.Callback callback) { 628 if (mGeolocationPermissionSet) { 629 callback.invoke(origin, mGeolocationPermission, false); 630 } 631 } 632 633 @Override 634 public void addMessageToConsole(String message, int lineNumber, 635 String sourceID) { 636 if (mConsoleMessages == null) { 637 mConsoleMessages = new StringBuffer(); 638 } 639 String consoleMessage = "CONSOLE MESSAGE: line " 640 + lineNumber +": "+ message +"\n"; 641 mConsoleMessages.append(consoleMessage); 642 Log.v(LOGTAG, "LOG: "+consoleMessage); 643 } 644 645 @Override 646 public boolean onCreateWindow(WebView view, boolean dialog, 647 boolean userGesture, Message resultMsg) { 648 if (!mCanOpenWindows) { 649 return false; 650 } 651 652 // We never display the new window, just create the view and 653 // allow it's content to execute and be recorded by the test 654 // runner. 655 656 HashMap<String, Object> jsIfaces = new HashMap<String, Object>(); 657 jsIfaces.put("layoutTestController", mCallbackProxy); 658 jsIfaces.put("eventSender", mCallbackProxy); 659 WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces); 660 setupWebViewForLayoutTests(newWindowView, mCallbackProxy); 661 WebView.WebViewTransport transport = 662 (WebView.WebViewTransport) resultMsg.obj; 663 transport.setWebView(newWindowView); 664 resultMsg.sendToTarget(); 665 return true; 666 } 667 }; 668 669 private static class NewWindowWebView extends WebView { 670 public NewWindowWebView(Context context, Map<String, Object> jsIfaces) { 671 super(context, null, 0, jsIfaces); 672 } 673 } 674 675 private void resetTestStatus() { 676 mWaitUntilDone = false; 677 mDumpDataType = mDefaultDumpDataType; 678 mTimedOut = false; 679 mDumpTitleChanges = false; 680 mRequestedWebKitData = false; 681 mDumpDatabaseCallbacks = false; 682 mCanOpenWindows = false; 683 mEventSender.resetMouse(); 684 mPageFinished = false; 685 mOneHundredPercentComplete = false; 686 mDumpWebKitData = false; 687 } 688 689 private boolean canMoveToNextTest() { 690 return (mDumpWebKitData && mOneHundredPercentComplete && mPageFinished && !mWaitUntilDone) || mTimedOut; 691 } 692 693 private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { 694 if (webview == null) { 695 return; 696 } 697 698 WebSettings settings = webview.getSettings(); 699 settings.setAppCacheEnabled(true); 700 settings.setAppCachePath(getApplicationContext().getCacheDir().getPath()); 701 settings.setAppCacheMaxSize(Long.MAX_VALUE); 702 settings.setJavaScriptEnabled(true); 703 settings.setJavaScriptCanOpenWindowsAutomatically(true); 704 settings.setSupportMultipleWindows(true); 705 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); 706 settings.setDatabaseEnabled(true); 707 settings.setDatabasePath(getDir("databases",0).getAbsolutePath()); 708 settings.setDomStorageEnabled(true); 709 settings.setWorkersEnabled(false); 710 711 webview.setWebChromeClient(mChromeClient); 712 webview.setWebViewClient(mViewClient); 713 } 714 715 private WebView mWebView; 716 private WebViewEventSender mEventSender; 717 private AsyncHandler mHandler; 718 private TestShellCallback mCallback; 719 720 private CallbackProxy mCallbackProxy; 721 722 private String mTestUrl; 723 private String mResultFile; 724 private int mTimeoutInMillis; 725 private String mUiAutoTestPath; 726 private BufferedReader mTestListReader; 727 728 // States 729 private boolean mTimedOut; 730 private boolean mRequestedWebKitData; 731 private boolean mFinishedRunning; 732 733 // Layout test controller variables. 734 private DumpDataType mDumpDataType; 735 private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR; 736 private boolean mWaitUntilDone; 737 private boolean mDumpTitleChanges; 738 private StringBuffer mTitleChanges; 739 private StringBuffer mDialogStrings; 740 private boolean mKeepWebHistory; 741 private Vector mWebHistory; 742 private boolean mDumpDatabaseCallbacks; 743 private StringBuffer mDatabaseCallbackStrings; 744 private StringBuffer mConsoleMessages; 745 private boolean mCanOpenWindows; 746 747 private boolean mPageFinished = false; 748 private boolean mDumpWebKitData = false; 749 private boolean mOneHundredPercentComplete = false; 750 751 static final String TIMEOUT_STR = "**Test timeout"; 752 753 static final int MSG_TIMEOUT = 0; 754 static final int MSG_WEBKIT_DATA = 1; 755 756 static final String LOGTAG="TestShell"; 757 758 static final String TEST_URL = "TestUrl"; 759 static final String RESULT_FILE = "ResultFile"; 760 static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis"; 761 static final String UI_AUTO_TEST = "UiAutoTest"; 762 763 private boolean mGeolocationPermissionSet; 764 private boolean mGeolocationPermission; 765} 766