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