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.annotation.IntDef; 20 import android.content.ActivityNotFoundException; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.ActivityInfo; 26 import android.content.res.Configuration; 27 import android.hardware.input.InputManager; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.Debug; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.MessageQueue; 34 import android.os.PerformanceCollector; 35 import android.os.PersistableBundle; 36 import android.os.Process; 37 import android.os.RemoteException; 38 import android.os.ServiceManager; 39 import android.os.SystemClock; 40 import android.os.TestLooperManager; 41 import android.os.UserHandle; 42 import android.util.AndroidRuntimeException; 43 import android.util.Log; 44 import android.view.IWindowManager; 45 import android.view.InputDevice; 46 import android.view.KeyCharacterMap; 47 import android.view.KeyEvent; 48 import android.view.MotionEvent; 49 import android.view.ViewConfiguration; 50 import android.view.Window; 51 import com.android.internal.content.ReferrerIntent; 52 53 import java.io.File; 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.util.ArrayList; 57 import java.util.List; 58 59 /** 60 * Base class for implementing application instrumentation code. When running 61 * with instrumentation turned on, this class will be instantiated for you 62 * before any of the application code, allowing you to monitor all of the 63 * interaction the system has with the application. An Instrumentation 64 * implementation is described to the system through an AndroidManifest.xml's 65 * <instrumentation> tag. 66 */ 67 public class Instrumentation { 68 69 /** 70 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 71 * identifies the class that is writing the report. This can be used to provide more structured 72 * logging or reporting capabilities in the IInstrumentationWatcher. 73 */ 74 public static final String REPORT_KEY_IDENTIFIER = "id"; 75 /** 76 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 77 * identifies a string which can simply be printed to the output stream. Using these streams 78 * provides a "pretty printer" version of the status & final packets. Any bundles including 79 * this key should also include the complete set of raw key/value pairs, so that the 80 * instrumentation can also be launched, and results collected, by an automated system. 81 */ 82 public static final String REPORT_KEY_STREAMRESULT = "stream"; 83 84 private static final String TAG = "Instrumentation"; 85 86 /** 87 * @hide 88 */ 89 @Retention(RetentionPolicy.SOURCE) 90 @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}) 91 public @interface UiAutomationFlags {}; 92 93 94 private final Object mSync = new Object(); 95 private ActivityThread mThread = null; 96 private MessageQueue mMessageQueue = null; 97 private Context mInstrContext; 98 private Context mAppContext; 99 private ComponentName mComponent; 100 private Thread mRunner; 101 private List<ActivityWaiter> mWaitingActivities; 102 private List<ActivityMonitor> mActivityMonitors; 103 private IInstrumentationWatcher mWatcher; 104 private IUiAutomationConnection mUiAutomationConnection; 105 private boolean mAutomaticPerformanceSnapshots = false; 106 private PerformanceCollector mPerformanceCollector; 107 private Bundle mPerfMetrics = new Bundle(); 108 private UiAutomation mUiAutomation; 109 110 public Instrumentation() { 111 } 112 113 /** 114 * Called for methods that shouldn't be called by standard apps and 115 * should only be used in instrumentation environments. This is not 116 * security feature as these classes will still be accessible through 117 * reflection, but it will serve as noticeable discouragement from 118 * doing such a thing. 119 */ 120 private void checkInstrumenting(String method) { 121 // Check if we have an instrumentation context, as init should only get called by 122 // the system in startup processes that are being instrumented. 123 if (mInstrContext == null) { 124 throw new RuntimeException(method + 125 " cannot be called outside of instrumented processes"); 126 } 127 } 128 129 /** 130 * Called when the instrumentation is starting, before any application code 131 * has been loaded. Usually this will be implemented to simply call 132 * {@link #start} to begin the instrumentation thread, which will then 133 * continue execution in {@link #onStart}. 134 * 135 * <p>If you do not need your own thread -- that is you are writing your 136 * instrumentation to be completely asynchronous (returning to the event 137 * loop so that the application can run), you can simply begin your 138 * instrumentation here, for example call {@link Context#startActivity} to 139 * begin the appropriate first activity of the application. 140 * 141 * @param arguments Any additional arguments that were supplied when the 142 * instrumentation was started. 143 */ 144 public void onCreate(Bundle arguments) { 145 } 146 147 /** 148 * Create and start a new thread in which to run instrumentation. This new 149 * thread will call to {@link #onStart} where you can implement the 150 * instrumentation. 151 */ 152 public void start() { 153 if (mRunner != null) { 154 throw new RuntimeException("Instrumentation already started"); 155 } 156 mRunner = new InstrumentationThread("Instr: " + getClass().getName()); 157 mRunner.start(); 158 } 159 160 /** 161 * Method where the instrumentation thread enters execution. This allows 162 * you to run your instrumentation code in a separate thread than the 163 * application, so that it can perform blocking operation such as 164 * {@link #sendKeySync} or {@link #startActivitySync}. 165 * 166 * <p>You will typically want to call finish() when this function is done, 167 * to end your instrumentation. 168 */ 169 public void onStart() { 170 } 171 172 /** 173 * This is called whenever the system captures an unhandled exception that 174 * was thrown by the application. The default implementation simply 175 * returns false, allowing normal system handling of the exception to take 176 * place. 177 * 178 * @param obj The client object that generated the exception. May be an 179 * Application, Activity, BroadcastReceiver, Service, or null. 180 * @param e The exception that was thrown. 181 * 182 * @return To allow normal system exception process to occur, return false. 183 * If true is returned, the system will proceed as if the exception 184 * didn't happen. 185 */ 186 public boolean onException(Object obj, Throwable e) { 187 return false; 188 } 189 190 /** 191 * Provide a status report about the application. 192 * 193 * @param resultCode Current success/failure of instrumentation. 194 * @param results Any results to send back to the code that started the instrumentation. 195 */ 196 public void sendStatus(int resultCode, Bundle results) { 197 if (mWatcher != null) { 198 try { 199 mWatcher.instrumentationStatus(mComponent, resultCode, results); 200 } 201 catch (RemoteException e) { 202 mWatcher = null; 203 } 204 } 205 } 206 207 /** 208 * Report some results in the middle of instrumentation execution. Later results (including 209 * those provided by {@link #finish}) will be combined with {@link Bundle#putAll}. 210 */ 211 public void addResults(Bundle results) { 212 IActivityManager am = ActivityManager.getService(); 213 try { 214 am.addInstrumentationResults(mThread.getApplicationThread(), results); 215 } catch (RemoteException ex) { 216 throw ex.rethrowFromSystemServer(); 217 } 218 } 219 220 /** 221 * Terminate instrumentation of the application. This will cause the 222 * application process to exit, removing this instrumentation from the next 223 * time the application is started. If multiple processes are currently running 224 * for this instrumentation, all of those processes will be killed. 225 * 226 * @param resultCode Overall success/failure of instrumentation. 227 * @param results Any results to send back to the code that started the 228 * instrumentation. 229 */ 230 public void finish(int resultCode, Bundle results) { 231 if (mAutomaticPerformanceSnapshots) { 232 endPerformanceSnapshot(); 233 } 234 if (mPerfMetrics != null) { 235 if (results == null) { 236 results = new Bundle(); 237 } 238 results.putAll(mPerfMetrics); 239 } 240 if ((mUiAutomation != null) && !mUiAutomation.isDestroyed()) { 241 mUiAutomation.disconnect(); 242 mUiAutomation = null; 243 } 244 mThread.finishInstrumentation(resultCode, results); 245 } 246 247 public void setAutomaticPerformanceSnapshots() { 248 mAutomaticPerformanceSnapshots = true; 249 mPerformanceCollector = new PerformanceCollector(); 250 } 251 252 public void startPerformanceSnapshot() { 253 if (!isProfiling()) { 254 mPerformanceCollector.beginSnapshot(null); 255 } 256 } 257 258 public void endPerformanceSnapshot() { 259 if (!isProfiling()) { 260 mPerfMetrics = mPerformanceCollector.endSnapshot(); 261 } 262 } 263 264 /** 265 * Called when the instrumented application is stopping, after all of the 266 * normal application cleanup has occurred. 267 */ 268 public void onDestroy() { 269 } 270 271 /** 272 * Return the Context of this instrumentation's package. Note that this is 273 * often different than the Context of the application being 274 * instrumentated, since the instrumentation code often lives is a 275 * different package than that of the application it is running against. 276 * See {@link #getTargetContext} to retrieve a Context for the target 277 * application. 278 * 279 * @return The instrumentation's package context. 280 * 281 * @see #getTargetContext 282 */ 283 public Context getContext() { 284 return mInstrContext; 285 } 286 287 /** 288 * Returns complete component name of this instrumentation. 289 * 290 * @return Returns the complete component name for this instrumentation. 291 */ 292 public ComponentName getComponentName() { 293 return mComponent; 294 } 295 296 /** 297 * Return a Context for the target application being instrumented. Note 298 * that this is often different than the Context of the instrumentation 299 * code, since the instrumentation code often lives is a different package 300 * than that of the application it is running against. See 301 * {@link #getContext} to retrieve a Context for the instrumentation code. 302 * 303 * @return A Context in the target application. 304 * 305 * @see #getContext 306 */ 307 public Context getTargetContext() { 308 return mAppContext; 309 } 310 311 /** 312 * Return the name of the process this instrumentation is running in. Note this should 313 * only be used for testing and debugging. If you are thinking about using this to, 314 * for example, conditionalize what is initialized in an Application class, it is strongly 315 * recommended to instead use lazy initialization (such as a getter for the state that 316 * only creates it when requested). This can greatly reduce the work your process does 317 * when created for secondary things, such as to receive a broadcast. 318 */ 319 public String getProcessName() { 320 return mThread.getProcessName(); 321 } 322 323 /** 324 * Check whether this instrumentation was started with profiling enabled. 325 * 326 * @return Returns true if profiling was enabled when starting, else false. 327 */ 328 public boolean isProfiling() { 329 return mThread.isProfiling(); 330 } 331 332 /** 333 * This method will start profiling if isProfiling() returns true. You should 334 * only call this method if you set the handleProfiling attribute in the 335 * manifest file for this Instrumentation to true. 336 */ 337 public void startProfiling() { 338 if (mThread.isProfiling()) { 339 File file = new File(mThread.getProfileFilePath()); 340 file.getParentFile().mkdirs(); 341 Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); 342 } 343 } 344 345 /** 346 * Stops profiling if isProfiling() returns true. 347 */ 348 public void stopProfiling() { 349 if (mThread.isProfiling()) { 350 Debug.stopMethodTracing(); 351 } 352 } 353 354 /** 355 * Force the global system in or out of touch mode. This can be used if 356 * your instrumentation relies on the UI being in one more or the other 357 * when it starts. 358 * 359 * @param inTouch Set to true to be in touch mode, false to be in 360 * focus mode. 361 */ 362 public void setInTouchMode(boolean inTouch) { 363 try { 364 IWindowManager.Stub.asInterface( 365 ServiceManager.getService("window")).setInTouchMode(inTouch); 366 } catch (RemoteException e) { 367 // Shouldn't happen! 368 } 369 } 370 371 /** 372 * Schedule a callback for when the application's main thread goes idle 373 * (has no more events to process). 374 * 375 * @param recipient Called the next time the thread's message queue is 376 * idle. 377 */ 378 public void waitForIdle(Runnable recipient) { 379 mMessageQueue.addIdleHandler(new Idler(recipient)); 380 mThread.getHandler().post(new EmptyRunnable()); 381 } 382 383 /** 384 * Synchronously wait for the application to be idle. Can not be called 385 * from the main application thread -- use {@link #start} to execute 386 * instrumentation in its own thread. 387 */ 388 public void waitForIdleSync() { 389 validateNotAppThread(); 390 Idler idler = new Idler(null); 391 mMessageQueue.addIdleHandler(idler); 392 mThread.getHandler().post(new EmptyRunnable()); 393 idler.waitForIdle(); 394 } 395 396 /** 397 * Execute a call on the application's main thread, blocking until it is 398 * complete. Useful for doing things that are not thread-safe, such as 399 * looking at or modifying the view hierarchy. 400 * 401 * @param runner The code to run on the main thread. 402 */ 403 public void runOnMainSync(Runnable runner) { 404 validateNotAppThread(); 405 SyncRunnable sr = new SyncRunnable(runner); 406 mThread.getHandler().post(sr); 407 sr.waitForComplete(); 408 } 409 410 /** 411 * Start a new activity and wait for it to begin running before returning. 412 * In addition to being synchronous, this method as some semantic 413 * differences from the standard {@link Context#startActivity} call: the 414 * activity component is resolved before talking with the activity manager 415 * (its class name is specified in the Intent that this method ultimately 416 * starts), and it does not allow you to start activities that run in a 417 * different process. In addition, if the given Intent resolves to 418 * multiple activities, instead of displaying a dialog for the user to 419 * select an activity, an exception will be thrown. 420 * 421 * <p>The function returns as soon as the activity goes idle following the 422 * call to its {@link Activity#onCreate}. Generally this means it has gone 423 * through the full initialization including {@link Activity#onResume} and 424 * drawn and displayed its initial window. 425 * 426 * @param intent Description of the activity to start. 427 * 428 * @see Context#startActivity 429 */ 430 public Activity startActivitySync(Intent intent) { 431 validateNotAppThread(); 432 433 synchronized (mSync) { 434 intent = new Intent(intent); 435 436 ActivityInfo ai = intent.resolveActivityInfo( 437 getTargetContext().getPackageManager(), 0); 438 if (ai == null) { 439 throw new RuntimeException("Unable to resolve activity for: " + intent); 440 } 441 String myProc = mThread.getProcessName(); 442 if (!ai.processName.equals(myProc)) { 443 // todo: if this intent is ambiguous, look here to see if 444 // there is a single match that is in our package. 445 throw new RuntimeException("Intent in process " 446 + myProc + " resolved to different process " 447 + ai.processName + ": " + intent); 448 } 449 450 intent.setComponent(new ComponentName( 451 ai.applicationInfo.packageName, ai.name)); 452 final ActivityWaiter aw = new ActivityWaiter(intent); 453 454 if (mWaitingActivities == null) { 455 mWaitingActivities = new ArrayList(); 456 } 457 mWaitingActivities.add(aw); 458 459 getTargetContext().startActivity(intent); 460 461 do { 462 try { 463 mSync.wait(); 464 } catch (InterruptedException e) { 465 } 466 } while (mWaitingActivities.contains(aw)); 467 468 return aw.activity; 469 } 470 } 471 472 /** 473 * Information about a particular kind of Intent that is being monitored. 474 * An instance of this class is added to the 475 * current instrumentation through {@link #addMonitor}; after being added, 476 * when a new activity is being started the monitor will be checked and, if 477 * matching, its hit count updated and (optionally) the call stopped and a 478 * canned result returned. 479 * 480 * <p>An ActivityMonitor can also be used to look for the creation of an 481 * activity, through the {@link #waitForActivity} method. This will return 482 * after a matching activity has been created with that activity object. 483 */ 484 public static class ActivityMonitor { 485 private final IntentFilter mWhich; 486 private final String mClass; 487 private final ActivityResult mResult; 488 private final boolean mBlock; 489 private final boolean mIgnoreMatchingSpecificIntents; 490 491 492 // This is protected by 'Instrumentation.this.mSync'. 493 /*package*/ int mHits = 0; 494 495 // This is protected by 'this'. 496 /*package*/ Activity mLastActivity = null; 497 498 /** 499 * Create a new ActivityMonitor that looks for a particular kind of 500 * intent to be started. 501 * 502 * @param which The set of intents this monitor is responsible for. 503 * @param result A canned result to return if the monitor is hit; can 504 * be null. 505 * @param block Controls whether the monitor should block the activity 506 * start (returning its canned result) or let the call 507 * proceed. 508 * 509 * @see Instrumentation#addMonitor 510 */ 511 public ActivityMonitor( 512 IntentFilter which, ActivityResult result, boolean block) { 513 mWhich = which; 514 mClass = null; 515 mResult = result; 516 mBlock = block; 517 mIgnoreMatchingSpecificIntents = false; 518 } 519 520 /** 521 * Create a new ActivityMonitor that looks for a specific activity 522 * class to be started. 523 * 524 * @param cls The activity class this monitor is responsible for. 525 * @param result A canned result to return if the monitor is hit; can 526 * be null. 527 * @param block Controls whether the monitor should block the activity 528 * start (returning its canned result) or let the call 529 * proceed. 530 * 531 * @see Instrumentation#addMonitor 532 */ 533 public ActivityMonitor( 534 String cls, ActivityResult result, boolean block) { 535 mWhich = null; 536 mClass = cls; 537 mResult = result; 538 mBlock = block; 539 mIgnoreMatchingSpecificIntents = false; 540 } 541 542 /** 543 * Create a new ActivityMonitor that can be used for intercepting any activity to be 544 * started. 545 * 546 * <p> When an activity is started, {@link #onStartActivity(Intent)} will be called on 547 * instances created using this constructor to see if it is a hit. 548 * 549 * @see #onStartActivity(Intent) 550 */ 551 public ActivityMonitor() { 552 mWhich = null; 553 mClass = null; 554 mResult = null; 555 mBlock = false; 556 mIgnoreMatchingSpecificIntents = true; 557 } 558 559 /** 560 * @return true if this monitor is used for intercepting any started activity by calling 561 * into {@link #onStartActivity(Intent)}, false if this monitor is only used 562 * for specific intents corresponding to the intent filter or activity class 563 * passed in the constructor. 564 */ 565 final boolean ignoreMatchingSpecificIntents() { 566 return mIgnoreMatchingSpecificIntents; 567 } 568 569 /** 570 * Retrieve the filter associated with this ActivityMonitor. 571 */ 572 public final IntentFilter getFilter() { 573 return mWhich; 574 } 575 576 /** 577 * Retrieve the result associated with this ActivityMonitor, or null if 578 * none. 579 */ 580 public final ActivityResult getResult() { 581 return mResult; 582 } 583 584 /** 585 * Check whether this monitor blocks activity starts (not allowing the 586 * actual activity to run) or allows them to execute normally. 587 */ 588 public final boolean isBlocking() { 589 return mBlock; 590 } 591 592 /** 593 * Retrieve the number of times the monitor has been hit so far. 594 */ 595 public final int getHits() { 596 return mHits; 597 } 598 599 /** 600 * Retrieve the most recent activity class that was seen by this 601 * monitor. 602 */ 603 public final Activity getLastActivity() { 604 return mLastActivity; 605 } 606 607 /** 608 * Block until an Activity is created that matches this monitor, 609 * returning the resulting activity. 610 * 611 * @return Activity 612 */ 613 public final Activity waitForActivity() { 614 synchronized (this) { 615 while (mLastActivity == null) { 616 try { 617 wait(); 618 } catch (InterruptedException e) { 619 } 620 } 621 Activity res = mLastActivity; 622 mLastActivity = null; 623 return res; 624 } 625 } 626 627 /** 628 * Block until an Activity is created that matches this monitor, 629 * returning the resulting activity or till the timeOut period expires. 630 * If the timeOut expires before the activity is started, return null. 631 * 632 * @param timeOut Time to wait in milliseconds before the activity is created. 633 * 634 * @return Activity 635 */ 636 public final Activity waitForActivityWithTimeout(long timeOut) { 637 synchronized (this) { 638 if (mLastActivity == null) { 639 try { 640 wait(timeOut); 641 } catch (InterruptedException e) { 642 } 643 } 644 if (mLastActivity == null) { 645 return null; 646 } else { 647 Activity res = mLastActivity; 648 mLastActivity = null; 649 return res; 650 } 651 } 652 } 653 654 /** 655 * Used for intercepting any started activity. 656 * 657 * <p> A non-null return value here will be considered a hit for this monitor. 658 * By default this will return {@code null} and subclasses can override this to return 659 * a non-null value if the intent needs to be intercepted. 660 * 661 * <p> Whenever a new activity is started, this method will be called on instances created 662 * using {@link #Instrumentation.ActivityMonitor()} to check if there is a match. In case 663 * of a match, the activity start will be blocked and the returned result will be used. 664 * 665 * @param intent The intent used for starting the activity. 666 * @return The {@link ActivityResult} that needs to be used in case of a match. 667 */ 668 public ActivityResult onStartActivity(Intent intent) { 669 return null; 670 } 671 672 final boolean match(Context who, 673 Activity activity, 674 Intent intent) { 675 if (mIgnoreMatchingSpecificIntents) { 676 return false; 677 } 678 synchronized (this) { 679 if (mWhich != null 680 && mWhich.match(who.getContentResolver(), intent, 681 true, "Instrumentation") < 0) { 682 return false; 683 } 684 if (mClass != null) { 685 String cls = null; 686 if (activity != null) { 687 cls = activity.getClass().getName(); 688 } else if (intent.getComponent() != null) { 689 cls = intent.getComponent().getClassName(); 690 } 691 if (cls == null || !mClass.equals(cls)) { 692 return false; 693 } 694 } 695 if (activity != null) { 696 mLastActivity = activity; 697 notifyAll(); 698 } 699 return true; 700 } 701 } 702 } 703 704 /** 705 * Add a new {@link ActivityMonitor} that will be checked whenever an 706 * activity is started. The monitor is added 707 * after any existing ones; the monitor will be hit only if none of the 708 * existing monitors can themselves handle the Intent. 709 * 710 * @param monitor The new ActivityMonitor to see. 711 * 712 * @see #addMonitor(IntentFilter, ActivityResult, boolean) 713 * @see #checkMonitorHit 714 */ 715 public void addMonitor(ActivityMonitor monitor) { 716 synchronized (mSync) { 717 if (mActivityMonitors == null) { 718 mActivityMonitors = new ArrayList(); 719 } 720 mActivityMonitors.add(monitor); 721 } 722 } 723 724 /** 725 * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 726 * creates an intent filter matching {@link ActivityMonitor} for you and 727 * returns it. 728 * 729 * @param filter The set of intents this monitor is responsible for. 730 * @param result A canned result to return if the monitor is hit; can 731 * be null. 732 * @param block Controls whether the monitor should block the activity 733 * start (returning its canned result) or let the call 734 * proceed. 735 * 736 * @return The newly created and added activity monitor. 737 * 738 * @see #addMonitor(ActivityMonitor) 739 * @see #checkMonitorHit 740 */ 741 public ActivityMonitor addMonitor( 742 IntentFilter filter, ActivityResult result, boolean block) { 743 ActivityMonitor am = new ActivityMonitor(filter, result, block); 744 addMonitor(am); 745 return am; 746 } 747 748 /** 749 * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 750 * creates a class matching {@link ActivityMonitor} for you and returns it. 751 * 752 * @param cls The activity class this monitor is responsible for. 753 * @param result A canned result to return if the monitor is hit; can 754 * be null. 755 * @param block Controls whether the monitor should block the activity 756 * start (returning its canned result) or let the call 757 * proceed. 758 * 759 * @return The newly created and added activity monitor. 760 * 761 * @see #addMonitor(ActivityMonitor) 762 * @see #checkMonitorHit 763 */ 764 public ActivityMonitor addMonitor( 765 String cls, ActivityResult result, boolean block) { 766 ActivityMonitor am = new ActivityMonitor(cls, result, block); 767 addMonitor(am); 768 return am; 769 } 770 771 /** 772 * Test whether an existing {@link ActivityMonitor} has been hit. If the 773 * monitor has been hit at least <var>minHits</var> times, then it will be 774 * removed from the activity monitor list and true returned. Otherwise it 775 * is left as-is and false is returned. 776 * 777 * @param monitor The ActivityMonitor to check. 778 * @param minHits The minimum number of hits required. 779 * 780 * @return True if the hit count has been reached, else false. 781 * 782 * @see #addMonitor 783 */ 784 public boolean checkMonitorHit(ActivityMonitor monitor, int minHits) { 785 waitForIdleSync(); 786 synchronized (mSync) { 787 if (monitor.getHits() < minHits) { 788 return false; 789 } 790 mActivityMonitors.remove(monitor); 791 } 792 return true; 793 } 794 795 /** 796 * Wait for an existing {@link ActivityMonitor} to be hit. Once the 797 * monitor has been hit, it is removed from the activity monitor list and 798 * the first created Activity object that matched it is returned. 799 * 800 * @param monitor The ActivityMonitor to wait for. 801 * 802 * @return The Activity object that matched the monitor. 803 */ 804 public Activity waitForMonitor(ActivityMonitor monitor) { 805 Activity activity = monitor.waitForActivity(); 806 synchronized (mSync) { 807 mActivityMonitors.remove(monitor); 808 } 809 return activity; 810 } 811 812 /** 813 * Wait for an existing {@link ActivityMonitor} to be hit till the timeout 814 * expires. Once the monitor has been hit, it is removed from the activity 815 * monitor list and the first created Activity object that matched it is 816 * returned. If the timeout expires, a null object is returned. 817 * 818 * @param monitor The ActivityMonitor to wait for. 819 * @param timeOut The timeout value in secs. 820 * 821 * @return The Activity object that matched the monitor. 822 */ 823 public Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) { 824 Activity activity = monitor.waitForActivityWithTimeout(timeOut); 825 synchronized (mSync) { 826 mActivityMonitors.remove(monitor); 827 } 828 return activity; 829 } 830 831 /** 832 * Remove an {@link ActivityMonitor} that was previously added with 833 * {@link #addMonitor}. 834 * 835 * @param monitor The monitor to remove. 836 * 837 * @see #addMonitor 838 */ 839 public void removeMonitor(ActivityMonitor monitor) { 840 synchronized (mSync) { 841 mActivityMonitors.remove(monitor); 842 } 843 } 844 845 /** 846 * Execute a particular menu item. 847 * 848 * @param targetActivity The activity in question. 849 * @param id The identifier associated with the menu item. 850 * @param flag Additional flags, if any. 851 * @return Whether the invocation was successful (for example, it could be 852 * false if item is disabled). 853 */ 854 public boolean invokeMenuActionSync(Activity targetActivity, 855 int id, int flag) { 856 class MenuRunnable implements Runnable { 857 private final Activity activity; 858 private final int identifier; 859 private final int flags; 860 boolean returnValue; 861 862 public MenuRunnable(Activity _activity, int _identifier, 863 int _flags) { 864 activity = _activity; 865 identifier = _identifier; 866 flags = _flags; 867 } 868 869 public void run() { 870 Window win = activity.getWindow(); 871 872 returnValue = win.performPanelIdentifierAction( 873 Window.FEATURE_OPTIONS_PANEL, 874 identifier, 875 flags); 876 } 877 878 } 879 MenuRunnable mr = new MenuRunnable(targetActivity, id, flag); 880 runOnMainSync(mr); 881 return mr.returnValue; 882 } 883 884 /** 885 * Show the context menu for the currently focused view and executes a 886 * particular context menu item. 887 * 888 * @param targetActivity The activity in question. 889 * @param id The identifier associated with the context menu item. 890 * @param flag Additional flags, if any. 891 * @return Whether the invocation was successful (for example, it could be 892 * false if item is disabled). 893 */ 894 public boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) { 895 validateNotAppThread(); 896 897 // Bring up context menu for current focus. 898 // It'd be nice to do this through code, but currently ListView depends on 899 // long press to set metadata for its selected child 900 901 final KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); 902 sendKeySync(downEvent); 903 904 // Need to wait for long press 905 waitForIdleSync(); 906 try { 907 Thread.sleep(ViewConfiguration.getLongPressTimeout()); 908 } catch (InterruptedException e) { 909 Log.e(TAG, "Could not sleep for long press timeout", e); 910 return false; 911 } 912 913 final KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); 914 sendKeySync(upEvent); 915 916 // Wait for context menu to appear 917 waitForIdleSync(); 918 919 class ContextMenuRunnable implements Runnable { 920 private final Activity activity; 921 private final int identifier; 922 private final int flags; 923 boolean returnValue; 924 925 public ContextMenuRunnable(Activity _activity, int _identifier, 926 int _flags) { 927 activity = _activity; 928 identifier = _identifier; 929 flags = _flags; 930 } 931 932 public void run() { 933 Window win = activity.getWindow(); 934 returnValue = win.performContextMenuIdentifierAction( 935 identifier, 936 flags); 937 } 938 939 } 940 941 ContextMenuRunnable cmr = new ContextMenuRunnable(targetActivity, id, flag); 942 runOnMainSync(cmr); 943 return cmr.returnValue; 944 } 945 946 /** 947 * Sends the key events corresponding to the text to the app being 948 * instrumented. 949 * 950 * @param text The text to be sent. 951 */ 952 public void sendStringSync(String text) { 953 if (text == null) { 954 return; 955 } 956 KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); 957 958 KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray()); 959 960 if (events != null) { 961 for (int i = 0; i < events.length; i++) { 962 // We have to change the time of an event before injecting it because 963 // all KeyEvents returned by KeyCharacterMap.getEvents() have the same 964 // time stamp and the system rejects too old events. Hence, it is 965 // possible for an event to become stale before it is injected if it 966 // takes too long to inject the preceding ones. 967 sendKeySync(KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(), 0)); 968 } 969 } 970 } 971 972 /** 973 * Send a key event to the currently focused window/view and wait for it to 974 * be processed. Finished at some point after the recipient has returned 975 * from its event processing, though it may <em>not</em> have completely 976 * finished reacting from the event -- for example, if it needs to update 977 * its display as a result, it may still be in the process of doing that. 978 * 979 * @param event The event to send to the current focus. 980 */ 981 public void sendKeySync(KeyEvent event) { 982 validateNotAppThread(); 983 984 long downTime = event.getDownTime(); 985 long eventTime = event.getEventTime(); 986 int action = event.getAction(); 987 int code = event.getKeyCode(); 988 int repeatCount = event.getRepeatCount(); 989 int metaState = event.getMetaState(); 990 int deviceId = event.getDeviceId(); 991 int scancode = event.getScanCode(); 992 int source = event.getSource(); 993 int flags = event.getFlags(); 994 if (source == InputDevice.SOURCE_UNKNOWN) { 995 source = InputDevice.SOURCE_KEYBOARD; 996 } 997 if (eventTime == 0) { 998 eventTime = SystemClock.uptimeMillis(); 999 } 1000 if (downTime == 0) { 1001 downTime = eventTime; 1002 } 1003 KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState, 1004 deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source); 1005 InputManager.getInstance().injectInputEvent(newEvent, 1006 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 1007 } 1008 1009 /** 1010 * Sends an up and down key event sync to the currently focused window. 1011 * 1012 * @param key The integer keycode for the event. 1013 */ 1014 public void sendKeyDownUpSync(int key) { 1015 sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, key)); 1016 sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, key)); 1017 } 1018 1019 /** 1020 * Higher-level method for sending both the down and up key events for a 1021 * particular character key code. Equivalent to creating both KeyEvent 1022 * objects by hand and calling {@link #sendKeySync}. The event appears 1023 * as if it came from keyboard 0, the built in one. 1024 * 1025 * @param keyCode The key code of the character to send. 1026 */ 1027 public void sendCharacterSync(int keyCode) { 1028 sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 1029 sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 1030 } 1031 1032 /** 1033 * Dispatch a pointer event. Finished at some point after the recipient has 1034 * returned from its event processing, though it may <em>not</em> have 1035 * completely finished reacting from the event -- for example, if it needs 1036 * to update its display as a result, it may still be in the process of 1037 * doing that. 1038 * 1039 * @param event A motion event describing the pointer action. (As noted in 1040 * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 1041 * {@link SystemClock#uptimeMillis()} as the timebase. 1042 */ 1043 public void sendPointerSync(MotionEvent event) { 1044 validateNotAppThread(); 1045 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) { 1046 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 1047 } 1048 InputManager.getInstance().injectInputEvent(event, 1049 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 1050 } 1051 1052 /** 1053 * Dispatch a trackball event. Finished at some point after the recipient has 1054 * returned from its event processing, though it may <em>not</em> have 1055 * completely finished reacting from the event -- for example, if it needs 1056 * to update its display as a result, it may still be in the process of 1057 * doing that. 1058 * 1059 * @param event A motion event describing the trackball action. (As noted in 1060 * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 1061 * {@link SystemClock#uptimeMillis()} as the timebase. 1062 */ 1063 public void sendTrackballEventSync(MotionEvent event) { 1064 validateNotAppThread(); 1065 if ((event.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == 0) { 1066 event.setSource(InputDevice.SOURCE_TRACKBALL); 1067 } 1068 InputManager.getInstance().injectInputEvent(event, 1069 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); 1070 } 1071 1072 /** 1073 * Perform instantiation of the process's {@link Application} object. The 1074 * default implementation provides the normal system behavior. 1075 * 1076 * @param cl The ClassLoader with which to instantiate the object. 1077 * @param className The name of the class implementing the Application 1078 * object. 1079 * @param context The context to initialize the application with 1080 * 1081 * @return The newly instantiated Application object. 1082 */ 1083 public Application newApplication(ClassLoader cl, String className, Context context) 1084 throws InstantiationException, IllegalAccessException, 1085 ClassNotFoundException { 1086 return newApplication(cl.loadClass(className), context); 1087 } 1088 1089 /** 1090 * Perform instantiation of the process's {@link Application} object. The 1091 * default implementation provides the normal system behavior. 1092 * 1093 * @param clazz The class used to create an Application object from. 1094 * @param context The context to initialize the application with 1095 * 1096 * @return The newly instantiated Application object. 1097 */ 1098 static public Application newApplication(Class<?> clazz, Context context) 1099 throws InstantiationException, IllegalAccessException, 1100 ClassNotFoundException { 1101 Application app = (Application)clazz.newInstance(); 1102 app.attach(context); 1103 return app; 1104 } 1105 1106 /** 1107 * Perform calling of the application's {@link Application#onCreate} 1108 * method. The default implementation simply calls through to that method. 1109 * 1110 * <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}. 1111 * Often instrumentation tests start their test thread in onCreate(); you 1112 * need to be careful of races between these. (Well between it and 1113 * everything else, but let's start here.) 1114 * 1115 * @param app The application being created. 1116 */ 1117 public void callApplicationOnCreate(Application app) { 1118 app.onCreate(); 1119 } 1120 1121 /** 1122 * Perform instantiation of an {@link Activity} object. This method is intended for use with 1123 * unit tests, such as android.test.ActivityUnitTestCase. The activity will be useable 1124 * locally but will be missing some of the linkages necessary for use within the system. 1125 * 1126 * @param clazz The Class of the desired Activity 1127 * @param context The base context for the activity to use 1128 * @param token The token for this activity to communicate with 1129 * @param application The application object (if any) 1130 * @param intent The intent that started this Activity 1131 * @param info ActivityInfo from the manifest 1132 * @param title The title, typically retrieved from the ActivityInfo record 1133 * @param parent The parent Activity (if any) 1134 * @param id The embedded Id (if any) 1135 * @param lastNonConfigurationInstance Arbitrary object that will be 1136 * available via {@link Activity#getLastNonConfigurationInstance() 1137 * Activity.getLastNonConfigurationInstance()}. 1138 * @return Returns the instantiated activity 1139 * @throws InstantiationException 1140 * @throws IllegalAccessException 1141 */ 1142 public Activity newActivity(Class<?> clazz, Context context, 1143 IBinder token, Application application, Intent intent, ActivityInfo info, 1144 CharSequence title, Activity parent, String id, 1145 Object lastNonConfigurationInstance) throws InstantiationException, 1146 IllegalAccessException { 1147 Activity activity = (Activity)clazz.newInstance(); 1148 ActivityThread aThread = null; 1149 activity.attach(context, aThread, this, token, 0 /* ident */, application, intent, 1150 info, title, parent, id, 1151 (Activity.NonConfigurationInstances)lastNonConfigurationInstance, 1152 new Configuration(), null /* referrer */, null /* voiceInteractor */, 1153 null /* window */, null /* activityConfigCallback */); 1154 return activity; 1155 } 1156 1157 /** 1158 * Perform instantiation of the process's {@link Activity} object. The 1159 * default implementation provides the normal system behavior. 1160 * 1161 * @param cl The ClassLoader with which to instantiate the object. 1162 * @param className The name of the class implementing the Activity 1163 * object. 1164 * @param intent The Intent object that specified the activity class being 1165 * instantiated. 1166 * 1167 * @return The newly instantiated Activity object. 1168 */ 1169 public Activity newActivity(ClassLoader cl, String className, 1170 Intent intent) 1171 throws InstantiationException, IllegalAccessException, 1172 ClassNotFoundException { 1173 return (Activity)cl.loadClass(className).newInstance(); 1174 } 1175 1176 private void prePerformCreate(Activity activity) { 1177 if (mWaitingActivities != null) { 1178 synchronized (mSync) { 1179 final int N = mWaitingActivities.size(); 1180 for (int i=0; i<N; i++) { 1181 final ActivityWaiter aw = mWaitingActivities.get(i); 1182 final Intent intent = aw.intent; 1183 if (intent.filterEquals(activity.getIntent())) { 1184 aw.activity = activity; 1185 mMessageQueue.addIdleHandler(new ActivityGoing(aw)); 1186 } 1187 } 1188 } 1189 } 1190 } 1191 1192 private void postPerformCreate(Activity activity) { 1193 if (mActivityMonitors != null) { 1194 synchronized (mSync) { 1195 final int N = mActivityMonitors.size(); 1196 for (int i=0; i<N; i++) { 1197 final ActivityMonitor am = mActivityMonitors.get(i); 1198 am.match(activity, activity, activity.getIntent()); 1199 } 1200 } 1201 } 1202 } 1203 1204 /** 1205 * Perform calling of an activity's {@link Activity#onCreate} 1206 * method. The default implementation simply calls through to that method. 1207 * 1208 * @param activity The activity being created. 1209 * @param icicle The previously frozen state (or null) to pass through to onCreate(). 1210 */ 1211 public void callActivityOnCreate(Activity activity, Bundle icicle) { 1212 prePerformCreate(activity); 1213 activity.performCreate(icicle); 1214 postPerformCreate(activity); 1215 } 1216 1217 /** 1218 * Perform calling of an activity's {@link Activity#onCreate} 1219 * method. The default implementation simply calls through to that method. 1220 * @param activity The activity being created. 1221 * @param icicle The previously frozen state (or null) to pass through to 1222 * @param persistentState The previously persisted state (or null) 1223 */ 1224 public void callActivityOnCreate(Activity activity, Bundle icicle, 1225 PersistableBundle persistentState) { 1226 prePerformCreate(activity); 1227 activity.performCreate(icicle, persistentState); 1228 postPerformCreate(activity); 1229 } 1230 1231 public void callActivityOnDestroy(Activity activity) { 1232 // TODO: the following block causes intermittent hangs when using startActivity 1233 // temporarily comment out until root cause is fixed (bug 2630683) 1234 // if (mWaitingActivities != null) { 1235 // synchronized (mSync) { 1236 // final int N = mWaitingActivities.size(); 1237 // for (int i=0; i<N; i++) { 1238 // final ActivityWaiter aw = mWaitingActivities.get(i); 1239 // final Intent intent = aw.intent; 1240 // if (intent.filterEquals(activity.getIntent())) { 1241 // aw.activity = activity; 1242 // mMessageQueue.addIdleHandler(new ActivityGoing(aw)); 1243 // } 1244 // } 1245 // } 1246 // } 1247 1248 activity.performDestroy(); 1249 } 1250 1251 /** 1252 * Perform calling of an activity's {@link Activity#onRestoreInstanceState} 1253 * method. The default implementation simply calls through to that method. 1254 * 1255 * @param activity The activity being restored. 1256 * @param savedInstanceState The previously saved state being restored. 1257 */ 1258 public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { 1259 activity.performRestoreInstanceState(savedInstanceState); 1260 } 1261 1262 /** 1263 * Perform calling of an activity's {@link Activity#onRestoreInstanceState} 1264 * method. The default implementation simply calls through to that method. 1265 * 1266 * @param activity The activity being restored. 1267 * @param savedInstanceState The previously saved state being restored. 1268 * @param persistentState The previously persisted state (or null) 1269 */ 1270 public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState, 1271 PersistableBundle persistentState) { 1272 activity.performRestoreInstanceState(savedInstanceState, persistentState); 1273 } 1274 1275 /** 1276 * Perform calling of an activity's {@link Activity#onPostCreate} method. 1277 * The default implementation simply calls through to that method. 1278 * 1279 * @param activity The activity being created. 1280 * @param icicle The previously frozen state (or null) to pass through to 1281 * onPostCreate(). 1282 */ 1283 public void callActivityOnPostCreate(Activity activity, Bundle icicle) { 1284 activity.onPostCreate(icicle); 1285 } 1286 1287 /** 1288 * Perform calling of an activity's {@link Activity#onPostCreate} method. 1289 * The default implementation simply calls through to that method. 1290 * 1291 * @param activity The activity being created. 1292 * @param icicle The previously frozen state (or null) to pass through to 1293 * onPostCreate(). 1294 */ 1295 public void callActivityOnPostCreate(Activity activity, Bundle icicle, 1296 PersistableBundle persistentState) { 1297 activity.onPostCreate(icicle, persistentState); 1298 } 1299 1300 /** 1301 * Perform calling of an activity's {@link Activity#onNewIntent} 1302 * method. The default implementation simply calls through to that method. 1303 * 1304 * @param activity The activity receiving a new Intent. 1305 * @param intent The new intent being received. 1306 */ 1307 public void callActivityOnNewIntent(Activity activity, Intent intent) { 1308 activity.onNewIntent(intent); 1309 } 1310 1311 /** 1312 * @hide 1313 */ 1314 public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) { 1315 final String oldReferrer = activity.mReferrer; 1316 try { 1317 if (intent != null) { 1318 activity.mReferrer = intent.mReferrer; 1319 } 1320 callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null); 1321 } finally { 1322 activity.mReferrer = oldReferrer; 1323 } 1324 } 1325 1326 /** 1327 * Perform calling of an activity's {@link Activity#onStart} 1328 * method. The default implementation simply calls through to that method. 1329 * 1330 * @param activity The activity being started. 1331 */ 1332 public void callActivityOnStart(Activity activity) { 1333 activity.onStart(); 1334 } 1335 1336 /** 1337 * Perform calling of an activity's {@link Activity#onRestart} 1338 * method. The default implementation simply calls through to that method. 1339 * 1340 * @param activity The activity being restarted. 1341 */ 1342 public void callActivityOnRestart(Activity activity) { 1343 activity.onRestart(); 1344 } 1345 1346 /** 1347 * Perform calling of an activity's {@link Activity#onResume} method. The 1348 * default implementation simply calls through to that method. 1349 * 1350 * @param activity The activity being resumed. 1351 */ 1352 public void callActivityOnResume(Activity activity) { 1353 activity.mResumed = true; 1354 activity.onResume(); 1355 1356 if (mActivityMonitors != null) { 1357 synchronized (mSync) { 1358 final int N = mActivityMonitors.size(); 1359 for (int i=0; i<N; i++) { 1360 final ActivityMonitor am = mActivityMonitors.get(i); 1361 am.match(activity, activity, activity.getIntent()); 1362 } 1363 } 1364 } 1365 } 1366 1367 /** 1368 * Perform calling of an activity's {@link Activity#onStop} 1369 * method. The default implementation simply calls through to that method. 1370 * 1371 * @param activity The activity being stopped. 1372 */ 1373 public void callActivityOnStop(Activity activity) { 1374 activity.onStop(); 1375 } 1376 1377 /** 1378 * Perform calling of an activity's {@link Activity#onSaveInstanceState} 1379 * method. The default implementation simply calls through to that method. 1380 * 1381 * @param activity The activity being saved. 1382 * @param outState The bundle to pass to the call. 1383 */ 1384 public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) { 1385 activity.performSaveInstanceState(outState); 1386 } 1387 1388 /** 1389 * Perform calling of an activity's {@link Activity#onSaveInstanceState} 1390 * method. The default implementation simply calls through to that method. 1391 * @param activity The activity being saved. 1392 * @param outState The bundle to pass to the call. 1393 * @param outPersistentState The persistent bundle to pass to the call. 1394 */ 1395 public void callActivityOnSaveInstanceState(Activity activity, Bundle outState, 1396 PersistableBundle outPersistentState) { 1397 activity.performSaveInstanceState(outState, outPersistentState); 1398 } 1399 1400 /** 1401 * Perform calling of an activity's {@link Activity#onPause} method. The 1402 * default implementation simply calls through to that method. 1403 * 1404 * @param activity The activity being paused. 1405 */ 1406 public void callActivityOnPause(Activity activity) { 1407 activity.performPause(); 1408 } 1409 1410 /** 1411 * Perform calling of an activity's {@link Activity#onUserLeaveHint} method. 1412 * The default implementation simply calls through to that method. 1413 * 1414 * @param activity The activity being notified that the user has navigated away 1415 */ 1416 public void callActivityOnUserLeaving(Activity activity) { 1417 activity.performUserLeaving(); 1418 } 1419 1420 /* 1421 * Starts allocation counting. This triggers a gc and resets the counts. 1422 * 1423 * @deprecated Accurate counting is a burden on the runtime and may be removed. 1424 */ 1425 @Deprecated 1426 public void startAllocCounting() { 1427 // Before we start trigger a GC and reset the debug counts. Run the 1428 // finalizers and another GC before starting and stopping the alloc 1429 // counts. This will free up any objects that were just sitting around 1430 // waiting for their finalizers to be run. 1431 Runtime.getRuntime().gc(); 1432 Runtime.getRuntime().runFinalization(); 1433 Runtime.getRuntime().gc(); 1434 1435 Debug.resetAllCounts(); 1436 1437 // start the counts 1438 Debug.startAllocCounting(); 1439 } 1440 1441 /* 1442 * Stops allocation counting. 1443 * 1444 * @deprecated Accurate counting is a burden on the runtime and may be removed. 1445 */ 1446 @Deprecated 1447 public void stopAllocCounting() { 1448 Runtime.getRuntime().gc(); 1449 Runtime.getRuntime().runFinalization(); 1450 Runtime.getRuntime().gc(); 1451 Debug.stopAllocCounting(); 1452 } 1453 1454 /** 1455 * If Results already contains Key, it appends Value to the key's ArrayList 1456 * associated with the key. If the key doesn't already exist in results, it 1457 * adds the key/value pair to results. 1458 */ 1459 private void addValue(String key, int value, Bundle results) { 1460 if (results.containsKey(key)) { 1461 List<Integer> list = results.getIntegerArrayList(key); 1462 if (list != null) { 1463 list.add(value); 1464 } 1465 } else { 1466 ArrayList<Integer> list = new ArrayList<Integer>(); 1467 list.add(value); 1468 results.putIntegerArrayList(key, list); 1469 } 1470 } 1471 1472 /** 1473 * Returns a bundle with the current results from the allocation counting. 1474 */ 1475 public Bundle getAllocCounts() { 1476 Bundle results = new Bundle(); 1477 results.putLong("global_alloc_count", Debug.getGlobalAllocCount()); 1478 results.putLong("global_alloc_size", Debug.getGlobalAllocSize()); 1479 results.putLong("global_freed_count", Debug.getGlobalFreedCount()); 1480 results.putLong("global_freed_size", Debug.getGlobalFreedSize()); 1481 results.putLong("gc_invocation_count", Debug.getGlobalGcInvocationCount()); 1482 return results; 1483 } 1484 1485 /** 1486 * Returns a bundle with the counts for various binder counts for this process. Currently the only two that are 1487 * reported are the number of send and the number of received transactions. 1488 */ 1489 public Bundle getBinderCounts() { 1490 Bundle results = new Bundle(); 1491 results.putLong("sent_transactions", Debug.getBinderSentTransactions()); 1492 results.putLong("received_transactions", Debug.getBinderReceivedTransactions()); 1493 return results; 1494 } 1495 1496 /** 1497 * Description of a Activity execution result to return to the original 1498 * activity. 1499 */ 1500 public static final class ActivityResult { 1501 /** 1502 * Create a new activity result. See {@link Activity#setResult} for 1503 * more information. 1504 * 1505 * @param resultCode The result code to propagate back to the 1506 * originating activity, often RESULT_CANCELED or RESULT_OK 1507 * @param resultData The data to propagate back to the originating 1508 * activity. 1509 */ 1510 public ActivityResult(int resultCode, Intent resultData) { 1511 mResultCode = resultCode; 1512 mResultData = resultData; 1513 } 1514 1515 /** 1516 * Retrieve the result code contained in this result. 1517 */ 1518 public int getResultCode() { 1519 return mResultCode; 1520 } 1521 1522 /** 1523 * Retrieve the data contained in this result. 1524 */ 1525 public Intent getResultData() { 1526 return mResultData; 1527 } 1528 1529 private final int mResultCode; 1530 private final Intent mResultData; 1531 } 1532 1533 /** 1534 * Execute a startActivity call made by the application. The default 1535 * implementation takes care of updating any active {@link ActivityMonitor} 1536 * objects and dispatches this call to the system activity manager; you can 1537 * override this to watch for the application to start an activity, and 1538 * modify what happens when it does. 1539 * 1540 * <p>This method returns an {@link ActivityResult} object, which you can 1541 * use when intercepting application calls to avoid performing the start 1542 * activity action but still return the result the application is 1543 * expecting. To do this, override this method to catch the call to start 1544 * activity so that it returns a new ActivityResult containing the results 1545 * you would like the application to see, and don't call up to the super 1546 * class. Note that an application is only expecting a result if 1547 * <var>requestCode</var> is >= 0. 1548 * 1549 * <p>This method throws {@link android.content.ActivityNotFoundException} 1550 * if there was no Activity found to run the given Intent. 1551 * 1552 * @param who The Context from which the activity is being started. 1553 * @param contextThread The main thread of the Context from which the activity 1554 * is being started. 1555 * @param token Internal token identifying to the system who is starting 1556 * the activity; may be null. 1557 * @param target Which activity is performing the start (and thus receiving 1558 * any result); may be null if this call is not being made 1559 * from an activity. 1560 * @param intent The actual Intent to start. 1561 * @param requestCode Identifier for this request's result; less than zero 1562 * if the caller is not expecting a result. 1563 * @param options Addition options. 1564 * 1565 * @return To force the return of a particular result, return an 1566 * ActivityResult object containing the desired data; otherwise 1567 * return null. The default implementation always returns null. 1568 * 1569 * @throws android.content.ActivityNotFoundException 1570 * 1571 * @see Activity#startActivity(Intent) 1572 * @see Activity#startActivityForResult(Intent, int) 1573 * @see Activity#startActivityFromChild 1574 * 1575 * {@hide} 1576 */ 1577 public ActivityResult execStartActivity( 1578 Context who, IBinder contextThread, IBinder token, Activity target, 1579 Intent intent, int requestCode, Bundle options) { 1580 IApplicationThread whoThread = (IApplicationThread) contextThread; 1581 Uri referrer = target != null ? target.onProvideReferrer() : null; 1582 if (referrer != null) { 1583 intent.putExtra(Intent.EXTRA_REFERRER, referrer); 1584 } 1585 if (mActivityMonitors != null) { 1586 synchronized (mSync) { 1587 final int N = mActivityMonitors.size(); 1588 for (int i=0; i<N; i++) { 1589 final ActivityMonitor am = mActivityMonitors.get(i); 1590 ActivityResult result = null; 1591 if (am.ignoreMatchingSpecificIntents()) { 1592 result = am.onStartActivity(intent); 1593 } 1594 if (result != null) { 1595 am.mHits++; 1596 return result; 1597 } else if (am.match(who, null, intent)) { 1598 am.mHits++; 1599 if (am.isBlocking()) { 1600 return requestCode >= 0 ? am.getResult() : null; 1601 } 1602 break; 1603 } 1604 } 1605 } 1606 } 1607 try { 1608 intent.migrateExtraStreamToClipData(); 1609 intent.prepareToLeaveProcess(who); 1610 int result = ActivityManager.getService() 1611 .startActivity(whoThread, who.getBasePackageName(), intent, 1612 intent.resolveTypeIfNeeded(who.getContentResolver()), 1613 token, target != null ? target.mEmbeddedID : null, 1614 requestCode, 0, null, options); 1615 checkStartActivityResult(result, intent); 1616 } catch (RemoteException e) { 1617 throw new RuntimeException("Failure from system", e); 1618 } 1619 return null; 1620 } 1621 1622 /** 1623 * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, 1624 * but accepts an array of activities to be started. Note that active 1625 * {@link ActivityMonitor} objects only match against the first activity in 1626 * the array. 1627 * 1628 * {@hide} 1629 */ 1630 public void execStartActivities(Context who, IBinder contextThread, 1631 IBinder token, Activity target, Intent[] intents, Bundle options) { 1632 execStartActivitiesAsUser(who, contextThread, token, target, intents, options, 1633 UserHandle.myUserId()); 1634 } 1635 1636 /** 1637 * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, 1638 * but accepts an array of activities to be started. Note that active 1639 * {@link ActivityMonitor} objects only match against the first activity in 1640 * the array. 1641 * 1642 * {@hide} 1643 */ 1644 public void execStartActivitiesAsUser(Context who, IBinder contextThread, 1645 IBinder token, Activity target, Intent[] intents, Bundle options, 1646 int userId) { 1647 IApplicationThread whoThread = (IApplicationThread) contextThread; 1648 if (mActivityMonitors != null) { 1649 synchronized (mSync) { 1650 final int N = mActivityMonitors.size(); 1651 for (int i=0; i<N; i++) { 1652 final ActivityMonitor am = mActivityMonitors.get(i); 1653 ActivityResult result = null; 1654 if (am.ignoreMatchingSpecificIntents()) { 1655 result = am.onStartActivity(intents[0]); 1656 } 1657 if (result != null) { 1658 am.mHits++; 1659 return; 1660 } else if (am.match(who, null, intents[0])) { 1661 am.mHits++; 1662 if (am.isBlocking()) { 1663 return; 1664 } 1665 break; 1666 } 1667 } 1668 } 1669 } 1670 try { 1671 String[] resolvedTypes = new String[intents.length]; 1672 for (int i=0; i<intents.length; i++) { 1673 intents[i].migrateExtraStreamToClipData(); 1674 intents[i].prepareToLeaveProcess(who); 1675 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); 1676 } 1677 int result = ActivityManager.getService() 1678 .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes, 1679 token, options, userId); 1680 checkStartActivityResult(result, intents[0]); 1681 } catch (RemoteException e) { 1682 throw new RuntimeException("Failure from system", e); 1683 } 1684 } 1685 1686 /** 1687 * Like {@link #execStartActivity(android.content.Context, android.os.IBinder, 1688 * android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)}, 1689 * but for calls from a {#link Fragment}. 1690 * 1691 * @param who The Context from which the activity is being started. 1692 * @param contextThread The main thread of the Context from which the activity 1693 * is being started. 1694 * @param token Internal token identifying to the system who is starting 1695 * the activity; may be null. 1696 * @param target Which element is performing the start (and thus receiving 1697 * any result). 1698 * @param intent The actual Intent to start. 1699 * @param requestCode Identifier for this request's result; less than zero 1700 * if the caller is not expecting a result. 1701 * 1702 * @return To force the return of a particular result, return an 1703 * ActivityResult object containing the desired data; otherwise 1704 * return null. The default implementation always returns null. 1705 * 1706 * @throws android.content.ActivityNotFoundException 1707 * 1708 * @see Activity#startActivity(Intent) 1709 * @see Activity#startActivityForResult(Intent, int) 1710 * @see Activity#startActivityFromChild 1711 * 1712 * {@hide} 1713 */ 1714 public ActivityResult execStartActivity( 1715 Context who, IBinder contextThread, IBinder token, String target, 1716 Intent intent, int requestCode, Bundle options) { 1717 IApplicationThread whoThread = (IApplicationThread) contextThread; 1718 if (mActivityMonitors != null) { 1719 synchronized (mSync) { 1720 final int N = mActivityMonitors.size(); 1721 for (int i=0; i<N; i++) { 1722 final ActivityMonitor am = mActivityMonitors.get(i); 1723 ActivityResult result = null; 1724 if (am.ignoreMatchingSpecificIntents()) { 1725 result = am.onStartActivity(intent); 1726 } 1727 if (result != null) { 1728 am.mHits++; 1729 return result; 1730 } else if (am.match(who, null, intent)) { 1731 am.mHits++; 1732 if (am.isBlocking()) { 1733 return requestCode >= 0 ? am.getResult() : null; 1734 } 1735 break; 1736 } 1737 } 1738 } 1739 } 1740 try { 1741 intent.migrateExtraStreamToClipData(); 1742 intent.prepareToLeaveProcess(who); 1743 int result = ActivityManager.getService() 1744 .startActivity(whoThread, who.getBasePackageName(), intent, 1745 intent.resolveTypeIfNeeded(who.getContentResolver()), 1746 token, target, requestCode, 0, null, options); 1747 checkStartActivityResult(result, intent); 1748 } catch (RemoteException e) { 1749 throw new RuntimeException("Failure from system", e); 1750 } 1751 return null; 1752 } 1753 1754 /** 1755 * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, 1756 * but for starting as a particular user. 1757 * 1758 * @param who The Context from which the activity is being started. 1759 * @param contextThread The main thread of the Context from which the activity 1760 * is being started. 1761 * @param token Internal token identifying to the system who is starting 1762 * the activity; may be null. 1763 * @param target Which fragment is performing the start (and thus receiving 1764 * any result). 1765 * @param intent The actual Intent to start. 1766 * @param requestCode Identifier for this request's result; less than zero 1767 * if the caller is not expecting a result. 1768 * 1769 * @return To force the return of a particular result, return an 1770 * ActivityResult object containing the desired data; otherwise 1771 * return null. The default implementation always returns null. 1772 * 1773 * @throws android.content.ActivityNotFoundException 1774 * 1775 * @see Activity#startActivity(Intent) 1776 * @see Activity#startActivityForResult(Intent, int) 1777 * @see Activity#startActivityFromChild 1778 * 1779 * {@hide} 1780 */ 1781 public ActivityResult execStartActivity( 1782 Context who, IBinder contextThread, IBinder token, String resultWho, 1783 Intent intent, int requestCode, Bundle options, UserHandle user) { 1784 IApplicationThread whoThread = (IApplicationThread) contextThread; 1785 if (mActivityMonitors != null) { 1786 synchronized (mSync) { 1787 final int N = mActivityMonitors.size(); 1788 for (int i=0; i<N; i++) { 1789 final ActivityMonitor am = mActivityMonitors.get(i); 1790 ActivityResult result = null; 1791 if (am.ignoreMatchingSpecificIntents()) { 1792 result = am.onStartActivity(intent); 1793 } 1794 if (result != null) { 1795 am.mHits++; 1796 return result; 1797 } else if (am.match(who, null, intent)) { 1798 am.mHits++; 1799 if (am.isBlocking()) { 1800 return requestCode >= 0 ? am.getResult() : null; 1801 } 1802 break; 1803 } 1804 } 1805 } 1806 } 1807 try { 1808 intent.migrateExtraStreamToClipData(); 1809 intent.prepareToLeaveProcess(who); 1810 int result = ActivityManager.getService() 1811 .startActivityAsUser(whoThread, who.getBasePackageName(), intent, 1812 intent.resolveTypeIfNeeded(who.getContentResolver()), 1813 token, resultWho, 1814 requestCode, 0, null, options, user.getIdentifier()); 1815 checkStartActivityResult(result, intent); 1816 } catch (RemoteException e) { 1817 throw new RuntimeException("Failure from system", e); 1818 } 1819 return null; 1820 } 1821 1822 /** 1823 * Special version! 1824 * @hide 1825 */ 1826 public ActivityResult execStartActivityAsCaller( 1827 Context who, IBinder contextThread, IBinder token, Activity target, 1828 Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, 1829 int userId) { 1830 IApplicationThread whoThread = (IApplicationThread) contextThread; 1831 if (mActivityMonitors != null) { 1832 synchronized (mSync) { 1833 final int N = mActivityMonitors.size(); 1834 for (int i=0; i<N; i++) { 1835 final ActivityMonitor am = mActivityMonitors.get(i); 1836 ActivityResult result = null; 1837 if (am.ignoreMatchingSpecificIntents()) { 1838 result = am.onStartActivity(intent); 1839 } 1840 if (result != null) { 1841 am.mHits++; 1842 return result; 1843 } else if (am.match(who, null, intent)) { 1844 am.mHits++; 1845 if (am.isBlocking()) { 1846 return requestCode >= 0 ? am.getResult() : null; 1847 } 1848 break; 1849 } 1850 } 1851 } 1852 } 1853 try { 1854 intent.migrateExtraStreamToClipData(); 1855 intent.prepareToLeaveProcess(who); 1856 int result = ActivityManager.getService() 1857 .startActivityAsCaller(whoThread, who.getBasePackageName(), intent, 1858 intent.resolveTypeIfNeeded(who.getContentResolver()), 1859 token, target != null ? target.mEmbeddedID : null, 1860 requestCode, 0, null, options, ignoreTargetSecurity, userId); 1861 checkStartActivityResult(result, intent); 1862 } catch (RemoteException e) { 1863 throw new RuntimeException("Failure from system", e); 1864 } 1865 return null; 1866 } 1867 1868 /** 1869 * Special version! 1870 * @hide 1871 */ 1872 public void execStartActivityFromAppTask( 1873 Context who, IBinder contextThread, IAppTask appTask, 1874 Intent intent, Bundle options) { 1875 IApplicationThread whoThread = (IApplicationThread) contextThread; 1876 if (mActivityMonitors != null) { 1877 synchronized (mSync) { 1878 final int N = mActivityMonitors.size(); 1879 for (int i=0; i<N; i++) { 1880 final ActivityMonitor am = mActivityMonitors.get(i); 1881 ActivityResult result = null; 1882 if (am.ignoreMatchingSpecificIntents()) { 1883 result = am.onStartActivity(intent); 1884 } 1885 if (result != null) { 1886 am.mHits++; 1887 return; 1888 } else if (am.match(who, null, intent)) { 1889 am.mHits++; 1890 if (am.isBlocking()) { 1891 return; 1892 } 1893 break; 1894 } 1895 } 1896 } 1897 } 1898 try { 1899 intent.migrateExtraStreamToClipData(); 1900 intent.prepareToLeaveProcess(who); 1901 int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(), 1902 intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options); 1903 checkStartActivityResult(result, intent); 1904 } catch (RemoteException e) { 1905 throw new RuntimeException("Failure from system", e); 1906 } 1907 return; 1908 } 1909 1910 /*package*/ final void init(ActivityThread thread, 1911 Context instrContext, Context appContext, ComponentName component, 1912 IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) { 1913 mThread = thread; 1914 mMessageQueue = mThread.getLooper().myQueue(); 1915 mInstrContext = instrContext; 1916 mAppContext = appContext; 1917 mComponent = component; 1918 mWatcher = watcher; 1919 mUiAutomationConnection = uiAutomationConnection; 1920 } 1921 1922 /** @hide */ 1923 public static void checkStartActivityResult(int res, Object intent) { 1924 if (!ActivityManager.isStartResultFatalError(res)) { 1925 return; 1926 } 1927 1928 switch (res) { 1929 case ActivityManager.START_INTENT_NOT_RESOLVED: 1930 case ActivityManager.START_CLASS_NOT_FOUND: 1931 if (intent instanceof Intent && ((Intent)intent).getComponent() != null) 1932 throw new ActivityNotFoundException( 1933 "Unable to find explicit activity class " 1934 + ((Intent)intent).getComponent().toShortString() 1935 + "; have you declared this activity in your AndroidManifest.xml?"); 1936 throw new ActivityNotFoundException( 1937 "No Activity found to handle " + intent); 1938 case ActivityManager.START_PERMISSION_DENIED: 1939 throw new SecurityException("Not allowed to start activity " 1940 + intent); 1941 case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 1942 throw new AndroidRuntimeException( 1943 "FORWARD_RESULT_FLAG used while also requesting a result"); 1944 case ActivityManager.START_NOT_ACTIVITY: 1945 throw new IllegalArgumentException( 1946 "PendingIntent is not an activity"); 1947 case ActivityManager.START_NOT_VOICE_COMPATIBLE: 1948 throw new SecurityException( 1949 "Starting under voice control not allowed for: " + intent); 1950 case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION: 1951 throw new IllegalStateException( 1952 "Session calling startVoiceActivity does not match active session"); 1953 case ActivityManager.START_VOICE_HIDDEN_SESSION: 1954 throw new IllegalStateException( 1955 "Cannot start voice activity on a hidden session"); 1956 case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION: 1957 throw new IllegalStateException( 1958 "Session calling startAssistantActivity does not match active session"); 1959 case ActivityManager.START_ASSISTANT_HIDDEN_SESSION: 1960 throw new IllegalStateException( 1961 "Cannot start assistant activity on a hidden session"); 1962 case ActivityManager.START_CANCELED: 1963 throw new AndroidRuntimeException("Activity could not be started for " 1964 + intent); 1965 default: 1966 throw new AndroidRuntimeException("Unknown error code " 1967 + res + " when starting " + intent); 1968 } 1969 } 1970 1971 private final void validateNotAppThread() { 1972 if (Looper.myLooper() == Looper.getMainLooper()) { 1973 throw new RuntimeException( 1974 "This method can not be called from the main application thread"); 1975 } 1976 } 1977 1978 /** 1979 * Gets the {@link UiAutomation} instance with no flags set. 1980 * <p> 1981 * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation} 1982 * work across application boundaries while the APIs exposed by the instrumentation 1983 * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will 1984 * not allow you to inject the event in an app different from the instrumentation 1985 * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)} 1986 * will work regardless of the current application. 1987 * </p> 1988 * <p> 1989 * A typical test case should be using either the {@link UiAutomation} or 1990 * {@link Instrumentation} APIs. Using both APIs at the same time is not 1991 * a mistake by itself but a client has to be aware of the APIs limitations. 1992 * </p> 1993 * <p> 1994 * Equivalent to {@code getUiAutomation(0)}. If a {@link UiAutomation} exists with different 1995 * flags, the flags on that instance will be changed, and then it will be returned. 1996 * </p> 1997 * @return The UI automation instance. 1998 * 1999 * @see UiAutomation 2000 */ 2001 public UiAutomation getUiAutomation() { 2002 return getUiAutomation(0); 2003 } 2004 2005 /** 2006 * Gets the {@link UiAutomation} instance with flags set. 2007 * <p> 2008 * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation} 2009 * work across application boundaries while the APIs exposed by the instrumentation 2010 * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will 2011 * not allow you to inject the event in an app different from the instrumentation 2012 * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)} 2013 * will work regardless of the current application. 2014 * </p> 2015 * <p> 2016 * A typical test case should be using either the {@link UiAutomation} or 2017 * {@link Instrumentation} APIs. Using both APIs at the same time is not 2018 * a mistake by itself but a client has to be aware of the APIs limitations. 2019 * </p> 2020 * <p> 2021 * If a {@link UiAutomation} exists with different flags, the flags on that instance will be 2022 * changed, and then it will be returned. 2023 * </p> 2024 * 2025 * @param flags The flags to be passed to the UiAutomation, for example 2026 * {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}. 2027 * 2028 * @return The UI automation instance. 2029 * 2030 * @see UiAutomation 2031 */ 2032 public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { 2033 boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed()); 2034 2035 if (mUiAutomationConnection != null) { 2036 if (!mustCreateNewAutomation && (mUiAutomation.getFlags() == flags)) { 2037 return mUiAutomation; 2038 } 2039 if (mustCreateNewAutomation) { 2040 mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), 2041 mUiAutomationConnection); 2042 } else { 2043 mUiAutomation.disconnect(); 2044 } 2045 mUiAutomation.connect(flags); 2046 return mUiAutomation; 2047 } 2048 return null; 2049 } 2050 2051 /** 2052 * Takes control of the execution of messages on the specified looper until 2053 * {@link TestLooperManager#release} is called. 2054 */ 2055 public TestLooperManager acquireLooperManager(Looper looper) { 2056 checkInstrumenting("acquireLooperManager"); 2057 return new TestLooperManager(looper); 2058 } 2059 2060 private final class InstrumentationThread extends Thread { 2061 public InstrumentationThread(String name) { 2062 super(name); 2063 } 2064 public void run() { 2065 try { 2066 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 2067 } catch (RuntimeException e) { 2068 Log.w(TAG, "Exception setting priority of instrumentation thread " 2069 + Process.myTid(), e); 2070 } 2071 if (mAutomaticPerformanceSnapshots) { 2072 startPerformanceSnapshot(); 2073 } 2074 onStart(); 2075 } 2076 } 2077 2078 private static final class EmptyRunnable implements Runnable { 2079 public void run() { 2080 } 2081 } 2082 2083 private static final class SyncRunnable implements Runnable { 2084 private final Runnable mTarget; 2085 private boolean mComplete; 2086 2087 public SyncRunnable(Runnable target) { 2088 mTarget = target; 2089 } 2090 2091 public void run() { 2092 mTarget.run(); 2093 synchronized (this) { 2094 mComplete = true; 2095 notifyAll(); 2096 } 2097 } 2098 2099 public void waitForComplete() { 2100 synchronized (this) { 2101 while (!mComplete) { 2102 try { 2103 wait(); 2104 } catch (InterruptedException e) { 2105 } 2106 } 2107 } 2108 } 2109 } 2110 2111 private static final class ActivityWaiter { 2112 public final Intent intent; 2113 public Activity activity; 2114 2115 public ActivityWaiter(Intent _intent) { 2116 intent = _intent; 2117 } 2118 } 2119 2120 private final class ActivityGoing implements MessageQueue.IdleHandler { 2121 private final ActivityWaiter mWaiter; 2122 2123 public ActivityGoing(ActivityWaiter waiter) { 2124 mWaiter = waiter; 2125 } 2126 2127 public final boolean queueIdle() { 2128 synchronized (mSync) { 2129 mWaitingActivities.remove(mWaiter); 2130 mSync.notifyAll(); 2131 } 2132 return false; 2133 } 2134 } 2135 2136 private static final class Idler implements MessageQueue.IdleHandler { 2137 private final Runnable mCallback; 2138 private boolean mIdle; 2139 2140 public Idler(Runnable callback) { 2141 mCallback = callback; 2142 mIdle = false; 2143 } 2144 2145 public final boolean queueIdle() { 2146 if (mCallback != null) { 2147 mCallback.run(); 2148 } 2149 synchronized (this) { 2150 mIdle = true; 2151 notifyAll(); 2152 } 2153 return false; 2154 } 2155 2156 public void waitForIdle() { 2157 synchronized (this) { 2158 while (!mIdle) { 2159 try { 2160 wait(); 2161 } catch (InterruptedException e) { 2162 } 2163 } 2164 } 2165 } 2166 } 2167 } 2168