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