1 /* 2 * Copyright (C) 2006 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.app; 18 19 import android.content.ActivityNotFoundException; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.ActivityInfo; 25 import android.content.res.Configuration; 26 import android.os.Bundle; 27 import android.os.PerformanceCollector; 28 import android.os.RemoteException; 29 import android.os.Debug; 30 import android.os.IBinder; 31 import android.os.MessageQueue; 32 import android.os.Process; 33 import android.os.SystemClock; 34 import android.os.ServiceManager; 35 import android.util.AndroidRuntimeException; 36 import android.util.Config; 37 import android.util.Log; 38 import android.view.IWindowManager; 39 import android.view.KeyCharacterMap; 40 import android.view.KeyEvent; 41 import android.view.MotionEvent; 42 import android.view.ViewConfiguration; 43 import android.view.Window; 44 import android.view.inputmethod.InputMethodManager; 45 46 import java.io.File; 47 import java.util.ArrayList; 48 import java.util.List; 49 50 51 /** 52 * Base class for implementing application instrumentation code. When running 53 * with instrumentation turned on, this class will be instantiated for you 54 * before any of the application code, allowing you to monitor all of the 55 * interaction the system has with the application. An Instrumentation 56 * implementation is described to the system through an AndroidManifest.xml's 57 * <instrumentation> tag. 58 */ 59 public class Instrumentation { 60 /** 61 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 62 * identifies the class that is writing the report. This can be used to provide more structured 63 * logging or reporting capabilities in the IInstrumentationWatcher. 64 */ 65 public static final String REPORT_KEY_IDENTIFIER = "id"; 66 /** 67 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 68 * identifies a string which can simply be printed to the output stream. Using these streams 69 * provides a "pretty printer" version of the status & final packets. Any bundles including 70 * this key should also include the complete set of raw key/value pairs, so that the 71 * instrumentation can also be launched, and results collected, by an automated system. 72 */ 73 public static final String REPORT_KEY_STREAMRESULT = "stream"; 74 75 private static final String TAG = "Instrumentation"; 76 77 private final Object mSync = new Object(); 78 private ActivityThread mThread = null; 79 private MessageQueue mMessageQueue = null; 80 private Context mInstrContext; 81 private Context mAppContext; 82 private ComponentName mComponent; 83 private Thread mRunner; 84 private List<ActivityWaiter> mWaitingActivities; 85 private List<ActivityMonitor> mActivityMonitors; 86 private IInstrumentationWatcher mWatcher; 87 private boolean mAutomaticPerformanceSnapshots = false; 88 private PerformanceCollector mPerformanceCollector; 89 private Bundle mPerfMetrics = new Bundle(); 90 91 public Instrumentation() { 92 } 93 94 /** 95 * Called when the instrumentation is starting, before any application code 96 * has been loaded. Usually this will be implemented to simply call 97 * {@link #start} to begin the instrumentation thread, which will then 98 * continue execution in {@link #onStart}. 99 * 100 * <p>If you do not need your own thread -- that is you are writing your 101 * instrumentation to be completely asynchronous (returning to the event 102 * loop so that the application can run), you can simply begin your 103 * instrumentation here, for example call {@link Context#startActivity} to 104 * begin the appropriate first activity of the application. 105 * 106 * @param arguments Any additional arguments that were supplied when the 107 * instrumentation was started. 108 */ 109 public void onCreate(Bundle arguments) { 110 } 111 112 /** 113 * Create and start a new thread in which to run instrumentation. This new 114 * thread will call to {@link #onStart} where you can implement the 115 * instrumentation. 116 */ 117 public void start() { 118 if (mRunner != null) { 119 throw new RuntimeException("Instrumentation already started"); 120 } 121 mRunner = new InstrumentationThread("Instr: " + getClass().getName()); 122 mRunner.start(); 123 } 124 125 /** 126 * Method where the instrumentation thread enters execution. This allows 127 * you to run your instrumentation code in a separate thread than the 128 * application, so that it can perform blocking operation such as 129 * {@link #sendKeySync} or {@link #startActivitySync}. 130 * 131 * <p>You will typically want to call finish() when this function is done, 132 * to end your instrumentation. 133 */ 134 public void onStart() { 135 } 136 137 /** 138 * This is called whenever the system captures an unhandled exception that 139 * was thrown by the application. The default implementation simply 140 * returns false, allowing normal system handling of the exception to take 141 * place. 142 * 143 * @param obj The client object that generated the exception. May be an 144 * Application, Activity, BroadcastReceiver, Service, or null. 145 * @param e The exception that was thrown. 146 * 147 * @return To allow normal system exception process to occur, return false. 148 * If true is returned, the system will proceed as if the exception 149 * didn't happen. 150 */ 151 public boolean onException(Object obj, Throwable e) { 152 return false; 153 } 154 155 /** 156 * Provide a status report about the application. 157 * 158 * @param resultCode Current success/failure of instrumentation. 159 * @param results Any results to send back to the code that started the instrumentation. 160 */ 161 public void sendStatus(int resultCode, Bundle results) { 162 if (mWatcher != null) { 163 try { 164 mWatcher.instrumentationStatus(mComponent, resultCode, results); 165 } 166 catch (RemoteException e) { 167 mWatcher = null; 168 } 169 } 170 } 171 172 /** 173 * Terminate instrumentation of the application. This will cause the 174 * application process to exit, removing this instrumentation from the next 175 * time the application is started. 176 * 177 * @param resultCode Overall success/failure of instrumentation. 178 * @param results Any results to send back to the code that started the 179 * instrumentation. 180 */ 181 public void finish(int resultCode, Bundle results) { 182 if (mAutomaticPerformanceSnapshots) { 183 endPerformanceSnapshot(); 184 } 185 if (mPerfMetrics != null) { 186 results.putAll(mPerfMetrics); 187 } 188 mThread.finishInstrumentation(resultCode, results); 189 } 190 191 public void setAutomaticPerformanceSnapshots() { 192 mAutomaticPerformanceSnapshots = true; 193 mPerformanceCollector = new PerformanceCollector(); 194 } 195 196 public void startPerformanceSnapshot() { 197 if (!isProfiling()) { 198 mPerformanceCollector.beginSnapshot(null); 199 } 200 } 201 202 public void endPerformanceSnapshot() { 203 if (!isProfiling()) { 204 mPerfMetrics = mPerformanceCollector.endSnapshot(); 205 } 206 } 207 208 /** 209 * Called when the instrumented application is stopping, after all of the 210 * normal application cleanup has occurred. 211 */ 212 public void onDestroy() { 213 } 214 215 /** 216 * Return the Context of this instrumentation's package. Note that this is 217 * often different than the Context of the application being 218 * instrumentated, since the instrumentation code often lives is a 219 * different package than that of the application it is running against. 220 * See {@link #getTargetContext} to retrieve a Context for the target 221 * application. 222 * 223 * @return The instrumentation's package context. 224 * 225 * @see #getTargetContext 226 */ 227 public Context getContext() { 228 return mInstrContext; 229 } 230 231 /** 232 * Returns complete component name of this instrumentation. 233 * 234 * @return Returns the complete component name for this instrumentation. 235 */ 236 public ComponentName getComponentName() { 237 return mComponent; 238 } 239 240 /** 241 * Return a Context for the target application being instrumented. Note 242 * that this is often different than the Context of the instrumentation 243 * code, since the instrumentation code often lives is a different package 244 * than that of the application it is running against. See 245 * {@link #getContext} to retrieve a Context for the instrumentation code. 246 * 247 * @return A Context in the target application. 248 * 249 * @see #getContext 250 */ 251 public Context getTargetContext() { 252 return mAppContext; 253 } 254 255 /** 256 * Check whether this instrumentation was started with profiling enabled. 257 * 258 * @return Returns true if profiling was enabled when starting, else false. 259 */ 260 public boolean isProfiling() { 261 return mThread.isProfiling(); 262 } 263 264 /** 265 * This method will start profiling if isProfiling() returns true. You should 266 * only call this method if you set the handleProfiling attribute in the 267 * manifest file for this Instrumentation to true. 268 */ 269 public void startProfiling() { 270 if (mThread.isProfiling()) { 271 File file = new File(mThread.getProfileFilePath()); 272 file.getParentFile().mkdirs(); 273 Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); 274 } 275 } 276 277 /** 278 * Stops profiling if isProfiling() returns true. 279 */ 280 public void stopProfiling() { 281 if (mThread.isProfiling()) { 282 Debug.stopMethodTracing(); 283 } 284 } 285 286 /** 287 * Force the global system in or out of touch mode. This can be used if 288 * your instrumentation relies on the UI being in one more or the other 289 * when it starts. 290 * 291 * @param inTouch Set to true to be in touch mode, false to be in 292 * focus mode. 293 */ 294 public void setInTouchMode(boolean inTouch) { 295 try { 296 IWindowManager.Stub.asInterface( 297 ServiceManager.getService("window")).setInTouchMode(inTouch); 298 } catch (RemoteException e) { 299 // Shouldn't happen! 300 } 301 } 302 303 /** 304 * Schedule a callback for when the application's main thread goes idle 305 * (has no more events to process). 306 * 307 * @param recipient Called the next time the thread's message queue is 308 * idle. 309 */ 310 public void waitForIdle(Runnable recipient) { 311 mMessageQueue.addIdleHandler(new Idler(recipient)); 312 mThread.getHandler().post(new EmptyRunnable()); 313 } 314 315 /** 316 * Synchronously wait for the application to be idle. Can not be called 317 * from the main application thread -- use {@link #start} to execute 318 * instrumentation in its own thread. 319 */ 320 public void waitForIdleSync() { 321 validateNotAppThread(); 322 Idler idler = new Idler(null); 323 mMessageQueue.addIdleHandler(idler); 324 mThread.getHandler().post(new EmptyRunnable()); 325 idler.waitForIdle(); 326 } 327 328 /** 329 * Execute a call on the application's main thread, blocking until it is 330 * complete. Useful for doing things that are not thread-safe, such as 331 * looking at or modifying the view hierarchy. 332 * 333 * @param runner The code to run on the main thread. 334 */ 335 public void runOnMainSync(Runnable runner) { 336 validateNotAppThread(); 337 SyncRunnable sr = new SyncRunnable(runner); 338 mThread.getHandler().post(sr); 339 sr.waitForComplete(); 340 } 341 342 /** 343 * Start a new activity and wait for it to begin running before returning. 344 * In addition to being synchronous, this method as some semantic 345 * differences from the standard {@link Context#startActivity} call: the 346 * activity component is resolved before talking with the activity manager 347 * (its class name is specified in the Intent that this method ultimately 348 * starts), and it does not allow you to start activities that run in a 349 * different process. In addition, if the given Intent resolves to 350 * multiple activities, instead of displaying a dialog for the user to 351 * select an activity, an exception will be thrown. 352 * 353 * <p>The function returns as soon as the activity goes idle following the 354 * call to its {@link Activity#onCreate}. Generally this means it has gone 355 * through the full initialization including {@link Activity#onResume} and 356 * drawn and displayed its initial window. 357 * 358 * @param intent Description of the activity to start. 359 * 360 * @see Context#startActivity 361 */ 362 public Activity startActivitySync(Intent intent) { 363 validateNotAppThread(); 364 365 synchronized (mSync) { 366 intent = new Intent(intent); 367 368 ActivityInfo ai = intent.resolveActivityInfo( 369 getTargetContext().getPackageManager(), 0); 370 if (ai == null) { 371 throw new RuntimeException("Unable to resolve activity for: " + intent); 372 } 373 String myProc = mThread.getProcessName(); 374 if (!ai.processName.equals(myProc)) { 375 // todo: if this intent is ambiguous, look here to see if 376 // there is a single match that is in our package. 377 throw new RuntimeException("Intent in process " 378 + myProc + " resolved to different process " 379 + ai.processName + ": " + intent); 380 } 381 382 intent.setComponent(new ComponentName( 383 ai.applicationInfo.packageName, ai.name)); 384 final ActivityWaiter aw = new ActivityWaiter(intent); 385 386 if (mWaitingActivities == null) { 387 mWaitingActivities = new ArrayList(); 388 } 389 mWaitingActivities.add(aw); 390 391 getTargetContext().startActivity(intent); 392 393 do { 394 try { 395 mSync.wait(); 396 } catch (InterruptedException e) { 397 } 398 } while (mWaitingActivities.contains(aw)); 399 400 return aw.activity; 401 } 402 } 403 404 /** 405 * Information about a particular kind of Intent that is being monitored. 406 * An instance of this class is added to the 407 * current instrumentation through {@link #addMonitor}; after being added, 408 * when a new activity is being started the monitor will be checked and, if 409 * matching, its hit count updated and (optionally) the call stopped and a 410 * canned result returned. 411 * 412 * <p>An ActivityMonitor can also be used to look for the creation of an 413 * activity, through the {@link #waitForActivity} method. This will return 414 * after a matching activity has been created with that activity object. 415 */ 416 public static class ActivityMonitor { 417 private final IntentFilter mWhich; 418 private final String mClass; 419 private final ActivityResult mResult; 420 private final boolean mBlock; 421 422 423 // This is protected by 'Instrumentation.this.mSync'. 424 /*package*/ int mHits = 0; 425 426 // This is protected by 'this'. 427 /*package*/ Activity mLastActivity = null; 428 429 /** 430 * Create a new ActivityMonitor that looks for a particular kind of 431 * intent to be started. 432 * 433 * @param which The set of intents this monitor is responsible for. 434 * @param result A canned result to return if the monitor is hit; can 435 * be null. 436 * @param block Controls whether the monitor should block the activity 437 * start (returning its canned result) or let the call 438 * proceed. 439 * 440 * @see Instrumentation#addMonitor 441 */ 442 public ActivityMonitor( 443 IntentFilter which, ActivityResult result, boolean block) { 444 mWhich = which; 445 mClass = null; 446 mResult = result; 447 mBlock = block; 448 } 449 450 /** 451 * Create a new ActivityMonitor that looks for a specific activity 452 * class to be started. 453 * 454 * @param cls The activity class this monitor is responsible for. 455 * @param result A canned result to return if the monitor is hit; can 456 * be null. 457 * @param block Controls whether the monitor should block the activity 458 * start (returning its canned result) or let the call 459 * proceed. 460 * 461 * @see Instrumentation#addMonitor 462 */ 463 public ActivityMonitor( 464 String cls, ActivityResult result, boolean block) { 465 mWhich = null; 466 mClass = cls; 467 mResult = result; 468 mBlock = block; 469 } 470 471 /** 472 * Retrieve the filter associated with this ActivityMonitor. 473 */ 474 public final IntentFilter getFilter() { 475 return mWhich; 476 } 477 478 /** 479 * Retrieve the result associated with this ActivityMonitor, or null if 480 * none. 481 */ 482 public final ActivityResult getResult() { 483 return mResult; 484 } 485 486 /** 487 * Check whether this monitor blocks activity starts (not allowing the 488 * actual activity to run) or allows them to execute normally. 489 */ 490 public final boolean isBlocking() { 491 return mBlock; 492 } 493 494 /** 495 * Retrieve the number of times the monitor has been hit so far. 496 */ 497 public final int getHits() { 498 return mHits; 499 } 500 501 /** 502 * Retrieve the most recent activity class that was seen by this 503 * monitor. 504 */ 505 public final Activity getLastActivity() { 506 return mLastActivity; 507 } 508 509 /** 510 * Block until an Activity is created that matches this monitor, 511 * returning the resulting activity. 512 * 513 * @return Activity 514 */ 515 public final Activity waitForActivity() { 516 synchronized (this) { 517 while (mLastActivity == null) { 518 try { 519 wait(); 520 } catch (InterruptedException e) { 521 } 522 } 523 Activity res = mLastActivity; 524 mLastActivity = null; 525 return res; 526 } 527 } 528 529 /** 530 * Block until an Activity is created that matches this monitor, 531 * returning the resulting activity or till the timeOut period expires. 532 * If the timeOut expires before the activity is started, return null. 533 * 534 * @param timeOut Time to wait before the activity is created. 535 * 536 * @return Activity 537 */ 538 public final Activity waitForActivityWithTimeout(long timeOut) { 539 synchronized (this) { 540 try { 541 wait(timeOut); 542 } catch (InterruptedException e) { 543 } 544 if (mLastActivity == null) { 545 return null; 546 } else { 547 Activity res = mLastActivity; 548 mLastActivity = null; 549 return res; 550 } 551 } 552 } 553 554 final boolean match(Context who, 555 Activity activity, 556 Intent intent) { 557 synchronized (this) { 558 if (mWhich != null 559 && mWhich.match(who.getContentResolver(), intent, 560 true, "Instrumentation") < 0) { 561 return false; 562 } 563 if (mClass != null) { 564 String cls = null; 565 if (activity != null) { 566 cls = activity.getClass().getName(); 567 } else if (intent.getComponent() != null) { 568 cls = intent.getComponent().getClassName(); 569 } 570 if (cls == null || !mClass.equals(cls)) { 571 return false; 572 } 573 } 574 if (activity != null) { 575 mLastActivity = activity; 576 notifyAll(); 577 } 578 return true; 579 } 580 } 581 } 582 583 /** 584 * Add a new {@link ActivityMonitor} that will be checked whenever an 585 * activity is started. The monitor is added 586 * after any existing ones; the monitor will be hit only if none of the 587 * existing monitors can themselves handle the Intent. 588 * 589 * @param monitor The new ActivityMonitor to see. 590 * 591 * @see #addMonitor(IntentFilter, ActivityResult, boolean) 592 * @see #checkMonitorHit 593 */ 594 public void addMonitor(ActivityMonitor monitor) { 595 synchronized (mSync) { 596 if (mActivityMonitors == null) { 597 mActivityMonitors = new ArrayList(); 598 } 599 mActivityMonitors.add(monitor); 600 } 601 } 602 603 /** 604 * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 605 * creates an intent filter matching {@link ActivityMonitor} for you and 606 * returns it. 607 * 608 * @param filter The set of intents this monitor is responsible for. 609 * @param result A canned result to return if the monitor is hit; can 610 * be null. 611 * @param block Controls whether the monitor should block the activity 612 * start (returning its canned result) or let the call 613 * proceed. 614 * 615 * @return The newly created and added activity monitor. 616 * 617 * @see #addMonitor(ActivityMonitor) 618 * @see #checkMonitorHit 619 */ 620 public ActivityMonitor addMonitor( 621 IntentFilter filter, ActivityResult result, boolean block) { 622 ActivityMonitor am = new ActivityMonitor(filter, result, block); 623 addMonitor(am); 624 return am; 625 } 626 627 /** 628 * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 629 * creates a class matching {@link ActivityMonitor} for you and returns it. 630 * 631 * @param cls The activity class this monitor is responsible for. 632 * @param result A canned result to return if the monitor is hit; can 633 * be null. 634 * @param block Controls whether the monitor should block the activity 635 * start (returning its canned result) or let the call 636 * proceed. 637 * 638 * @return The newly created and added activity monitor. 639 * 640 * @see #addMonitor(ActivityMonitor) 641 * @see #checkMonitorHit 642 */ 643 public ActivityMonitor addMonitor( 644 String cls, ActivityResult result, boolean block) { 645 ActivityMonitor am = new ActivityMonitor(cls, result, block); 646 addMonitor(am); 647 return am; 648 } 649 650 /** 651 * Test whether an existing {@link ActivityMonitor} has been hit. If the 652 * monitor has been hit at least <var>minHits</var> times, then it will be 653 * removed from the activity monitor list and true returned. Otherwise it 654 * is left as-is and false is returned. 655 * 656 * @param monitor The ActivityMonitor to check. 657 * @param minHits The minimum number of hits required. 658 * 659 * @return True if the hit count has been reached, else false. 660 * 661 * @see #addMonitor 662 */ 663 public boolean checkMonitorHit(ActivityMonitor monitor, int minHits) { 664 waitForIdleSync(); 665 synchronized (mSync) { 666 if (monitor.getHits() < minHits) { 667 return false; 668 } 669 mActivityMonitors.remove(monitor); 670 } 671 return true; 672 } 673 674 /** 675 * Wait for an existing {@link ActivityMonitor} to be hit. Once the 676 * monitor has been hit, it is removed from the activity monitor list and 677 * the first created Activity object that matched it is returned. 678 * 679 * @param monitor The ActivityMonitor to wait for. 680 * 681 * @return The Activity object that matched the monitor. 682 */ 683 public Activity waitForMonitor(ActivityMonitor monitor) { 684 Activity activity = monitor.waitForActivity(); 685 synchronized (mSync) { 686 mActivityMonitors.remove(monitor); 687 } 688 return activity; 689 } 690 691 /** 692 * Wait for an existing {@link ActivityMonitor} to be hit till the timeout 693 * expires. Once the monitor has been hit, it is removed from the activity 694 * monitor list and the first created Activity object that matched it is 695 * returned. If the timeout expires, a null object is returned. 696 * 697 * @param monitor The ActivityMonitor to wait for. 698 * @param timeOut The timeout value in secs. 699 * 700 * @return The Activity object that matched the monitor. 701 */ 702 public Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) { 703 Activity activity = monitor.waitForActivityWithTimeout(timeOut); 704 synchronized (mSync) { 705 mActivityMonitors.remove(monitor); 706 } 707 return activity; 708 } 709 710 /** 711 * Remove an {@link ActivityMonitor} that was previously added with 712 * {@link #addMonitor}. 713 * 714 * @param monitor The monitor to remove. 715 * 716 * @see #addMonitor 717 */ 718 public void removeMonitor(ActivityMonitor monitor) { 719 synchronized (mSync) { 720 mActivityMonitors.remove(monitor); 721 } 722 } 723 724 /** 725 * Execute a particular menu item. 726 * 727 * @param targetActivity The activity in question. 728 * @param id The identifier associated with the menu item. 729 * @param flag Additional flags, if any. 730 * @return Whether the invocation was successful (for example, it could be 731 * false if item is disabled). 732 */ 733 public boolean invokeMenuActionSync(Activity targetActivity, 734 int id, int flag) { 735 class MenuRunnable implements Runnable { 736 private final Activity activity; 737 private final int identifier; 738 private final int flags; 739 boolean returnValue; 740 741 public MenuRunnable(Activity _activity, int _identifier, 742 int _flags) { 743 activity = _activity; 744 identifier = _identifier; 745 flags = _flags; 746 } 747 748 public void run() { 749 Window win = activity.getWindow(); 750 751 returnValue = win.performPanelIdentifierAction( 752 Window.FEATURE_OPTIONS_PANEL, 753 identifier, 754 flags); 755 } 756 757 } 758 MenuRunnable mr = new MenuRunnable(targetActivity, id, flag); 759 runOnMainSync(mr); 760 return mr.returnValue; 761 } 762 763 /** 764 * Show the context menu for the currently focused view and executes a 765 * particular context menu item. 766 * 767 * @param targetActivity The activity in question. 768 * @param id The identifier associated with the context menu item. 769 * @param flag Additional flags, if any. 770 * @return Whether the invocation was successful (for example, it could be 771 * false if item is disabled). 772 */ 773 public boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) { 774 validateNotAppThread(); 775 776 // Bring up context menu for current focus. 777 // It'd be nice to do this through code, but currently ListView depends on 778 // long press to set metadata for its selected child 779 780 final KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); 781 sendKeySync(downEvent); 782 783 // Need to wait for long press 784 waitForIdleSync(); 785 try { 786 Thread.sleep(ViewConfiguration.getLongPressTimeout()); 787 } catch (InterruptedException e) { 788 Log.e(TAG, "Could not sleep for long press timeout", e); 789 return false; 790 } 791 792 final KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); 793 sendKeySync(upEvent); 794 795 // Wait for context menu to appear 796 waitForIdleSync(); 797 798 class ContextMenuRunnable implements Runnable { 799 private final Activity activity; 800 private final int identifier; 801 private final int flags; 802 boolean returnValue; 803 804 public ContextMenuRunnable(Activity _activity, int _identifier, 805 int _flags) { 806 activity = _activity; 807 identifier = _identifier; 808 flags = _flags; 809 } 810 811 public void run() { 812 Window win = activity.getWindow(); 813 returnValue = win.performContextMenuIdentifierAction( 814 identifier, 815 flags); 816 } 817 818 } 819 820 ContextMenuRunnable cmr = new ContextMenuRunnable(targetActivity, id, flag); 821 runOnMainSync(cmr); 822 return cmr.returnValue; 823 } 824 825 /** 826 * Sends the key events corresponding to the text to the app being 827 * instrumented. 828 * 829 * @param text The text to be sent. 830 */ 831 public void sendStringSync(String text) { 832 if (text == null) { 833 return; 834 } 835 KeyCharacterMap keyCharacterMap = 836 KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); 837 838 KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray()); 839 840 if (events != null) { 841 for (int i = 0; i < events.length; i++) { 842 sendKeySync(events[i]); 843 } 844 } 845 } 846 847 /** 848 * Send a key event to the currently focused window/view and wait for it to 849 * be processed. Finished at some point after the recipient has returned 850 * from its event processing, though it may <em>not</em> have completely 851 * finished reacting from the event -- for example, if it needs to update 852 * its display as a result, it may still be in the process of doing that. 853 * 854 * @param event The event to send to the current focus. 855 */ 856 public void sendKeySync(KeyEvent event) { 857 validateNotAppThread(); 858 try { 859 (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))) 860 .injectKeyEvent(event, true); 861 } catch (RemoteException e) { 862 } 863 } 864 865 /** 866 * Sends an up and down key event sync to the currently focused window. 867 * 868 * @param key The integer keycode for the event. 869 */ 870 public void sendKeyDownUpSync(int key) { 871 sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, key)); 872 sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, key)); 873 } 874 875 /** 876 * Higher-level method for sending both the down and up key events for a 877 * particular character key code. Equivalent to creating both KeyEvent 878 * objects by hand and calling {@link #sendKeySync}. The event appears 879 * as if it came from keyboard 0, the built in one. 880 * 881 * @param keyCode The key code of the character to send. 882 */ 883 public void sendCharacterSync(int keyCode) { 884 sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 885 sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 886 } 887 888 /** 889 * Dispatch a pointer event. Finished at some point after the recipient has 890 * returned from its event processing, though it may <em>not</em> have 891 * completely finished reacting from the event -- for example, if it needs 892 * to update its display as a result, it may still be in the process of 893 * doing that. 894 * 895 * @param event A motion event describing the pointer action. (As noted in 896 * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 897 * {@link SystemClock#uptimeMillis()} as the timebase. 898 */ 899 public void sendPointerSync(MotionEvent event) { 900 validateNotAppThread(); 901 try { 902 (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))) 903 .injectPointerEvent(event, true); 904 } catch (RemoteException e) { 905 } 906 } 907 908 /** 909 * Dispatch a trackball event. Finished at some point after the recipient has 910 * returned from its event processing, though it may <em>not</em> have 911 * completely finished reacting from the event -- for example, if it needs 912 * to update its display as a result, it may still be in the process of 913 * doing that. 914 * 915 * @param event A motion event describing the trackball action. (As noted in 916 * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 917 * {@link SystemClock#uptimeMillis()} as the timebase. 918 */ 919 public void sendTrackballEventSync(MotionEvent event) { 920 validateNotAppThread(); 921 try { 922 (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))) 923 .injectTrackballEvent(event, true); 924 } catch (RemoteException e) { 925 } 926 } 927 928 /** 929 * Perform instantiation of the process's {@link Application} object. The 930 * default implementation provides the normal system behavior. 931 * 932 * @param cl The ClassLoader with which to instantiate the object. 933 * @param className The name of the class implementing the Application 934 * object. 935 * @param context The context to initialize the application with 936 * 937 * @return The newly instantiated Application object. 938 */ 939 public Application newApplication(ClassLoader cl, String className, Context context) 940 throws InstantiationException, IllegalAccessException, 941 ClassNotFoundException { 942 return newApplication(cl.loadClass(className), context); 943 } 944 945 /** 946 * Perform instantiation of the process's {@link Application} object. The 947 * default implementation provides the normal system behavior. 948 * 949 * @param clazz The class used to create an Application object from. 950 * @param context The context to initialize the application with 951 * 952 * @return The newly instantiated Application object. 953 */ 954 static public Application newApplication(Class<?> clazz, Context context) 955 throws InstantiationException, IllegalAccessException, 956 ClassNotFoundException { 957 Application app = (Application)clazz.newInstance(); 958 app.attach(context); 959 return app; 960 } 961 962 /** 963 * Perform calling of the application's {@link Application#onCreate} 964 * method. The default implementation simply calls through to that method. 965 * 966 * @param app The application being created. 967 */ 968 public void callApplicationOnCreate(Application app) { 969 app.onCreate(); 970 } 971 972 /** 973 * Perform instantiation of an {@link Activity} object. This method is intended for use with 974 * unit tests, such as android.test.ActivityUnitTestCase. The activity will be useable 975 * locally but will be missing some of the linkages necessary for use within the sytem. 976 * 977 * @param clazz The Class of the desired Activity 978 * @param context The base context for the activity to use 979 * @param token The token for this activity to communicate with 980 * @param application The application object (if any) 981 * @param intent The intent that started this Activity 982 * @param info ActivityInfo from the manifest 983 * @param title The title, typically retrieved from the ActivityInfo record 984 * @param parent The parent Activity (if any) 985 * @param id The embedded Id (if any) 986 * @param lastNonConfigurationInstance Arbitrary object that will be 987 * available via {@link Activity#getLastNonConfigurationInstance() 988 * Activity.getLastNonConfigurationInstance()}. 989 * @return Returns the instantiated activity 990 * @throws InstantiationException 991 * @throws IllegalAccessException 992 */ 993 public Activity newActivity(Class<?> clazz, Context context, 994 IBinder token, Application application, Intent intent, ActivityInfo info, 995 CharSequence title, Activity parent, String id, 996 Object lastNonConfigurationInstance) throws InstantiationException, 997 IllegalAccessException { 998 Activity activity = (Activity)clazz.newInstance(); 999 ActivityThread aThread = null; 1000 activity.attach(context, aThread, this, token, application, intent, info, title, 1001 parent, id, lastNonConfigurationInstance, new Configuration()); 1002 return activity; 1003 } 1004 1005 /** 1006 * Perform instantiation of the process's {@link Activity} object. The 1007 * default implementation provides the normal system behavior. 1008 * 1009 * @param cl The ClassLoader with which to instantiate the object. 1010 * @param className The name of the class implementing the Activity 1011 * object. 1012 * @param intent The Intent object that specified the activity class being 1013 * instantiated. 1014 * 1015 * @return The newly instantiated Activity object. 1016 */ 1017 public Activity newActivity(ClassLoader cl, String className, 1018 Intent intent) 1019 throws InstantiationException, IllegalAccessException, 1020 ClassNotFoundException { 1021 return (Activity)cl.loadClass(className).newInstance(); 1022 } 1023 1024 /** 1025 * Perform calling of an activity's {@link Activity#onCreate} 1026 * method. The default implementation simply calls through to that method. 1027 * 1028 * @param activity The activity being created. 1029 * @param icicle The previously frozen state (or null) to pass through to 1030 * onCreate(). 1031 */ 1032 public void callActivityOnCreate(Activity activity, Bundle icicle) { 1033 if (mWaitingActivities != null) { 1034 synchronized (mSync) { 1035 final int N = mWaitingActivities.size(); 1036 for (int i=0; i<N; i++) { 1037 final ActivityWaiter aw = mWaitingActivities.get(i); 1038 final Intent intent = aw.intent; 1039 if (intent.filterEquals(activity.getIntent())) { 1040 aw.activity = activity; 1041 mMessageQueue.addIdleHandler(new ActivityGoing(aw)); 1042 } 1043 } 1044 } 1045 } 1046 1047 activity.onCreate(icicle); 1048 1049 if (mActivityMonitors != null) { 1050 synchronized (mSync) { 1051 final int N = mActivityMonitors.size(); 1052 for (int i=0; i<N; i++) { 1053 final ActivityMonitor am = mActivityMonitors.get(i); 1054 am.match(activity, activity, activity.getIntent()); 1055 } 1056 } 1057 } 1058 } 1059 1060 public void callActivityOnDestroy(Activity activity) { 1061 if (mWaitingActivities != null) { 1062 synchronized (mSync) { 1063 final int N = mWaitingActivities.size(); 1064 for (int i=0; i<N; i++) { 1065 final ActivityWaiter aw = mWaitingActivities.get(i); 1066 final Intent intent = aw.intent; 1067 if (intent.filterEquals(activity.getIntent())) { 1068 aw.activity = activity; 1069 mMessageQueue.addIdleHandler(new ActivityGoing(aw)); 1070 } 1071 } 1072 } 1073 } 1074 1075 activity.onDestroy(); 1076 1077 if (mActivityMonitors != null) { 1078 synchronized (mSync) { 1079 final int N = mActivityMonitors.size(); 1080 for (int i=0; i<N; i++) { 1081 final ActivityMonitor am = mActivityMonitors.get(i); 1082 am.match(activity, activity, activity.getIntent()); 1083 } 1084 } 1085 } 1086 } 1087 1088 /** 1089 * Perform calling of an activity's {@link Activity#onRestoreInstanceState} 1090 * method. The default implementation simply calls through to that method. 1091 * 1092 * @param activity The activity being restored. 1093 * @param savedInstanceState The previously saved state being restored. 1094 */ 1095 public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { 1096 activity.performRestoreInstanceState(savedInstanceState); 1097 } 1098 1099 /** 1100 * Perform calling of an activity's {@link Activity#onPostCreate} method. 1101 * The default implementation simply calls through to that method. 1102 * 1103 * @param activity The activity being created. 1104 * @param icicle The previously frozen state (or null) to pass through to 1105 * onPostCreate(). 1106 */ 1107 public void callActivityOnPostCreate(Activity activity, Bundle icicle) { 1108 activity.onPostCreate(icicle); 1109 } 1110 1111 /** 1112 * Perform calling of an activity's {@link Activity#onNewIntent} 1113 * method. The default implementation simply calls through to that method. 1114 * 1115 * @param activity The activity receiving a new Intent. 1116 * @param intent The new intent being received. 1117 */ 1118 public void callActivityOnNewIntent(Activity activity, Intent intent) { 1119 activity.onNewIntent(intent); 1120 } 1121 1122 /** 1123 * Perform calling of an activity's {@link Activity#onStart} 1124 * method. The default implementation simply calls through to that method. 1125 * 1126 * @param activity The activity being started. 1127 */ 1128 public void callActivityOnStart(Activity activity) { 1129 activity.onStart(); 1130 } 1131 1132 /** 1133 * Perform calling of an activity's {@link Activity#onRestart} 1134 * method. The default implementation simply calls through to that method. 1135 * 1136 * @param activity The activity being restarted. 1137 */ 1138 public void callActivityOnRestart(Activity activity) { 1139 activity.onRestart(); 1140 } 1141 1142 /** 1143 * Perform calling of an activity's {@link Activity#onResume} method. The 1144 * default implementation simply calls through to that method. 1145 * 1146 * @param activity The activity being resumed. 1147 */ 1148 public void callActivityOnResume(Activity activity) { 1149 activity.onResume(); 1150 1151 if (mActivityMonitors != null) { 1152 synchronized (mSync) { 1153 final int N = mActivityMonitors.size(); 1154 for (int i=0; i<N; i++) { 1155 final ActivityMonitor am = mActivityMonitors.get(i); 1156 am.match(activity, activity, activity.getIntent()); 1157 } 1158 } 1159 } 1160 } 1161 1162 /** 1163 * Perform calling of an activity's {@link Activity#onStop} 1164 * method. The default implementation simply calls through to that method. 1165 * 1166 * @param activity The activity being stopped. 1167 */ 1168 public void callActivityOnStop(Activity activity) { 1169 activity.onStop(); 1170 } 1171 1172 /** 1173 * Perform calling of an activity's {@link Activity#onPause} method. The 1174 * default implementation simply calls through to that method. 1175 * 1176 * @param activity The activity being saved. 1177 * @param outState The bundle to pass to the call. 1178 */ 1179 public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) { 1180 activity.performSaveInstanceState(outState); 1181 } 1182 1183 /** 1184 * Perform calling of an activity's {@link Activity#onPause} method. The 1185 * default implementation simply calls through to that method. 1186 * 1187 * @param activity The activity being paused. 1188 */ 1189 public void callActivityOnPause(Activity activity) { 1190 activity.performPause(); 1191 } 1192 1193 /** 1194 * Perform calling of an activity's {@link Activity#onUserLeaveHint} method. 1195 * The default implementation simply calls through to that method. 1196 * 1197 * @param activity The activity being notified that the user has navigated away 1198 */ 1199 public void callActivityOnUserLeaving(Activity activity) { 1200 activity.performUserLeaving(); 1201 } 1202 1203 /* 1204 * Starts allocation counting. This triggers a gc and resets the counts. 1205 */ 1206 public void startAllocCounting() { 1207 // Before we start trigger a GC and reset the debug counts. Run the 1208 // finalizers and another GC before starting and stopping the alloc 1209 // counts. This will free up any objects that were just sitting around 1210 // waiting for their finalizers to be run. 1211 Runtime.getRuntime().gc(); 1212 Runtime.getRuntime().runFinalization(); 1213 Runtime.getRuntime().gc(); 1214 1215 Debug.resetAllCounts(); 1216 1217 // start the counts 1218 Debug.startAllocCounting(); 1219 } 1220 1221 /* 1222 * Stops allocation counting. 1223 */ 1224 public void stopAllocCounting() { 1225 Runtime.getRuntime().gc(); 1226 Runtime.getRuntime().runFinalization(); 1227 Runtime.getRuntime().gc(); 1228 Debug.stopAllocCounting(); 1229 } 1230 1231 /** 1232 * If Results already contains Key, it appends Value to the key's ArrayList 1233 * associated with the key. If the key doesn't already exist in results, it 1234 * adds the key/value pair to results. 1235 */ 1236 private void addValue(String key, int value, Bundle results) { 1237 if (results.containsKey(key)) { 1238 List<Integer> list = results.getIntegerArrayList(key); 1239 if (list != null) { 1240 list.add(value); 1241 } 1242 } else { 1243 ArrayList<Integer> list = new ArrayList<Integer>(); 1244 list.add(value); 1245 results.putIntegerArrayList(key, list); 1246 } 1247 } 1248 1249 /** 1250 * Returns a bundle with the current results from the allocation counting. 1251 */ 1252 public Bundle getAllocCounts() { 1253 Bundle results = new Bundle(); 1254 results.putLong("global_alloc_count", Debug.getGlobalAllocCount()); 1255 results.putLong("global_alloc_size", Debug.getGlobalAllocSize()); 1256 results.putLong("global_freed_count", Debug.getGlobalFreedCount()); 1257 results.putLong("global_freed_size", Debug.getGlobalFreedSize()); 1258 results.putLong("gc_invocation_count", Debug.getGlobalGcInvocationCount()); 1259 return results; 1260 } 1261 1262 /** 1263 * Returns a bundle with the counts for various binder counts for this process. Currently the only two that are 1264 * reported are the number of send and the number of received transactions. 1265 */ 1266 public Bundle getBinderCounts() { 1267 Bundle results = new Bundle(); 1268 results.putLong("sent_transactions", Debug.getBinderSentTransactions()); 1269 results.putLong("received_transactions", Debug.getBinderReceivedTransactions()); 1270 return results; 1271 } 1272 1273 /** 1274 * Description of a Activity execution result to return to the original 1275 * activity. 1276 */ 1277 public static final class ActivityResult { 1278 /** 1279 * Create a new activity result. See {@link Activity#setResult} for 1280 * more information. 1281 * 1282 * @param resultCode The result code to propagate back to the 1283 * originating activity, often RESULT_CANCELED or RESULT_OK 1284 * @param resultData The data to propagate back to the originating 1285 * activity. 1286 */ 1287 public ActivityResult(int resultCode, Intent resultData) { 1288 mResultCode = resultCode; 1289 mResultData = resultData; 1290 } 1291 1292 /** 1293 * Retrieve the result code contained in this result. 1294 */ 1295 public int getResultCode() { 1296 return mResultCode; 1297 } 1298 1299 /** 1300 * Retrieve the data contained in this result. 1301 */ 1302 public Intent getResultData() { 1303 return mResultData; 1304 } 1305 1306 private final int mResultCode; 1307 private final Intent mResultData; 1308 } 1309 1310 /** 1311 * Execute a startActivity call made by the application. The default 1312 * implementation takes care of updating any active {@link ActivityMonitor} 1313 * objects and dispatches this call to the system activity manager; you can 1314 * override this to watch for the application to start an activity, and 1315 * modify what happens when it does. 1316 * 1317 * <p>This method returns an {@link ActivityResult} object, which you can 1318 * use when intercepting application calls to avoid performing the start 1319 * activity action but still return the result the application is 1320 * expecting. To do this, override this method to catch the call to start 1321 * activity so that it returns a new ActivityResult containing the results 1322 * you would like the application to see, and don't call up to the super 1323 * class. Note that an application is only expecting a result if 1324 * <var>requestCode</var> is >= 0. 1325 * 1326 * <p>This method throws {@link android.content.ActivityNotFoundException} 1327 * if there was no Activity found to run the given Intent. 1328 * 1329 * @param who The Context from which the activity is being started. 1330 * @param contextThread The main thread of the Context from which the activity 1331 * is being started. 1332 * @param token Internal token identifying to the system who is starting 1333 * the activity; may be null. 1334 * @param target Which activity is perform the start (and thus receiving 1335 * any result); may be null if this call is not being made 1336 * from an activity. 1337 * @param intent The actual Intent to start. 1338 * @param requestCode Identifier for this request's result; less than zero 1339 * if the caller is not expecting a result. 1340 * 1341 * @return To force the return of a particular result, return an 1342 * ActivityResult object containing the desired data; otherwise 1343 * return null. The default implementation always returns null. 1344 * 1345 * @throws android.content.ActivityNotFoundException 1346 * 1347 * @see Activity#startActivity(Intent) 1348 * @see Activity#startActivityForResult(Intent, int) 1349 * @see Activity#startActivityFromChild 1350 * 1351 * {@hide} 1352 */ 1353 public ActivityResult execStartActivity( 1354 Context who, IBinder contextThread, IBinder token, Activity target, 1355 Intent intent, int requestCode) { 1356 IApplicationThread whoThread = (IApplicationThread) contextThread; 1357 if (mActivityMonitors != null) { 1358 synchronized (mSync) { 1359 final int N = mActivityMonitors.size(); 1360 for (int i=0; i<N; i++) { 1361 final ActivityMonitor am = mActivityMonitors.get(i); 1362 if (am.match(who, null, intent)) { 1363 am.mHits++; 1364 if (am.isBlocking()) { 1365 return requestCode >= 0 ? am.getResult() : null; 1366 } 1367 break; 1368 } 1369 } 1370 } 1371 } 1372 try { 1373 int result = ActivityManagerNative.getDefault() 1374 .startActivity(whoThread, intent, 1375 intent.resolveTypeIfNeeded(who.getContentResolver()), 1376 null, 0, token, target != null ? target.mEmbeddedID : null, 1377 requestCode, false, false); 1378 checkStartActivityResult(result, intent); 1379 } catch (RemoteException e) { 1380 } 1381 return null; 1382 } 1383 1384 /*package*/ final void init(ActivityThread thread, 1385 Context instrContext, Context appContext, ComponentName component, 1386 IInstrumentationWatcher watcher) { 1387 mThread = thread; 1388 mMessageQueue = mThread.getLooper().myQueue(); 1389 mInstrContext = instrContext; 1390 mAppContext = appContext; 1391 mComponent = component; 1392 mWatcher = watcher; 1393 } 1394 1395 /*package*/ static void checkStartActivityResult(int res, Object intent) { 1396 if (res >= IActivityManager.START_SUCCESS) { 1397 return; 1398 } 1399 1400 switch (res) { 1401 case IActivityManager.START_INTENT_NOT_RESOLVED: 1402 case IActivityManager.START_CLASS_NOT_FOUND: 1403 if (intent instanceof Intent && ((Intent)intent).getComponent() != null) 1404 throw new ActivityNotFoundException( 1405 "Unable to find explicit activity class " 1406 + ((Intent)intent).getComponent().toShortString() 1407 + "; have you declared this activity in your AndroidManifest.xml?"); 1408 throw new ActivityNotFoundException( 1409 "No Activity found to handle " + intent); 1410 case IActivityManager.START_PERMISSION_DENIED: 1411 throw new SecurityException("Not allowed to start activity " 1412 + intent); 1413 case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 1414 throw new AndroidRuntimeException( 1415 "FORWARD_RESULT_FLAG used while also requesting a result"); 1416 case IActivityManager.START_NOT_ACTIVITY: 1417 throw new IllegalArgumentException( 1418 "PendingIntent is not an activity"); 1419 default: 1420 throw new AndroidRuntimeException("Unknown error code " 1421 + res + " when starting " + intent); 1422 } 1423 } 1424 1425 private final void validateNotAppThread() { 1426 if (ActivityThread.currentActivityThread() != null) { 1427 throw new RuntimeException( 1428 "This method can not be called from the main application thread"); 1429 } 1430 } 1431 1432 private final class InstrumentationThread extends Thread { 1433 public InstrumentationThread(String name) { 1434 super(name); 1435 } 1436 public void run() { 1437 IActivityManager am = ActivityManagerNative.getDefault(); 1438 try { 1439 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 1440 } catch (RuntimeException e) { 1441 Log.w(TAG, "Exception setting priority of instrumentation thread " 1442 + Process.myTid(), e); 1443 } 1444 if (mAutomaticPerformanceSnapshots) { 1445 startPerformanceSnapshot(); 1446 } 1447 onStart(); 1448 } 1449 } 1450 1451 private static final class EmptyRunnable implements Runnable { 1452 public void run() { 1453 } 1454 } 1455 1456 private static final class SyncRunnable implements Runnable { 1457 private final Runnable mTarget; 1458 private boolean mComplete; 1459 1460 public SyncRunnable(Runnable target) { 1461 mTarget = target; 1462 } 1463 1464 public void run() { 1465 mTarget.run(); 1466 synchronized (this) { 1467 mComplete = true; 1468 notifyAll(); 1469 } 1470 } 1471 1472 public void waitForComplete() { 1473 synchronized (this) { 1474 while (!mComplete) { 1475 try { 1476 wait(); 1477 } catch (InterruptedException e) { 1478 } 1479 } 1480 } 1481 } 1482 } 1483 1484 private static final class ActivityWaiter { 1485 public final Intent intent; 1486 public Activity activity; 1487 1488 public ActivityWaiter(Intent _intent) { 1489 intent = _intent; 1490 } 1491 } 1492 1493 private final class ActivityGoing implements MessageQueue.IdleHandler { 1494 private final ActivityWaiter mWaiter; 1495 1496 public ActivityGoing(ActivityWaiter waiter) { 1497 mWaiter = waiter; 1498 } 1499 1500 public final boolean queueIdle() { 1501 synchronized (mSync) { 1502 mWaitingActivities.remove(mWaiter); 1503 mSync.notifyAll(); 1504 } 1505 return false; 1506 } 1507 } 1508 1509 private static final class Idler implements MessageQueue.IdleHandler { 1510 private final Runnable mCallback; 1511 private boolean mIdle; 1512 1513 public Idler(Runnable callback) { 1514 mCallback = callback; 1515 mIdle = false; 1516 } 1517 1518 public final boolean queueIdle() { 1519 if (mCallback != null) { 1520 mCallback.run(); 1521 } 1522 synchronized (this) { 1523 mIdle = true; 1524 notifyAll(); 1525 } 1526 return false; 1527 } 1528 1529 public void waitForIdle() { 1530 synchronized (this) { 1531 while (!mIdle) { 1532 try { 1533 wait(); 1534 } catch (InterruptedException e) { 1535 } 1536 } 1537 } 1538 } 1539 } 1540 } 1541