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