Add sources for API 35

Downloaded from https://dl.google.com/android/repository/source-35_r01.zip
using SdkManager in Studio

Test: None
Change-Id: I83f78aa820b66edfdc9f8594d17bc7b6cacccec1
diff --git a/android-35/android/app/Instrumentation.java b/android-35/android/app/Instrumentation.java
new file mode 100644
index 0000000..db216b1
--- /dev/null
+++ b/android-35/android/app/Instrumentation.java
@@ -0,0 +1,2687 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManagerGlobal;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.os.PerformanceCollector;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.TestLooperManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base class for implementing application instrumentation code.  When running
+ * with instrumentation turned on, this class will be instantiated for you
+ * before any of the application code, allowing you to monitor all of the
+ * interaction the system has with the application.  An Instrumentation
+ * implementation is described to the system through an AndroidManifest.xml's
+ * <instrumentation> tag.
+ */
[email protected]
+public class Instrumentation {
+
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies the class that is writing the report.  This can be used to provide more structured
+     * logging or reporting capabilities in the IInstrumentationWatcher.
+     */
+    public static final String REPORT_KEY_IDENTIFIER = "id";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key 
+     * identifies a string which can simply be printed to the output stream.  Using these streams
+     * provides a "pretty printer" version of the status & final packets.  Any bundles including 
+     * this key should also include the complete set of raw key/value pairs, so that the
+     * instrumentation can also be launched, and results collected, by an automated system.
+     */
+    public static final String REPORT_KEY_STREAMRESULT = "stream";
+
+    private static final String TAG = "Instrumentation";
+
+    private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
+
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    // If set, will print the stack trace for activity starts within the process
+    static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+            SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES,
+            UiAutomation.FLAG_DONT_USE_ACCESSIBILITY})
+    public @interface UiAutomationFlags {};
+
+
+    private final Object mSync = new Object();
+    private ActivityThread mThread = null;
+    private MessageQueue mMessageQueue = null;
+    private Context mInstrContext;
+    private Context mAppContext;
+    private ComponentName mComponent;
+    private Thread mRunner;
+    private List<ActivityWaiter> mWaitingActivities;
+    private List<ActivityMonitor> mActivityMonitors;
+    private IInstrumentationWatcher mWatcher;
+    private IUiAutomationConnection mUiAutomationConnection;
+    private boolean mAutomaticPerformanceSnapshots = false;
+    private PerformanceCollector mPerformanceCollector;
+    private Bundle mPerfMetrics = new Bundle();
+    private UiAutomation mUiAutomation;
+    private final Object mAnimationCompleteLock = new Object();
+
+    @android.ravenwood.annotation.RavenwoodKeep
+    public Instrumentation() {
+    }
+
+    /**
+     * Called for methods that shouldn't be called by standard apps and
+     * should only be used in instrumentation environments. This is not
+     * security feature as these classes will still be accessible through
+     * reflection, but it will serve as noticeable discouragement from
+     * doing such a thing.
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    private void checkInstrumenting(String method) {
+        // Check if we have an instrumentation context, as init should only get called by
+        // the system in startup processes that are being instrumented.
+        if (mInstrContext == null) {
+            throw new RuntimeException(method +
+                    " cannot be called outside of instrumented processes");
+        }
+    }
+
+    /**
+     * Returns if it is being called in an instrumentation environment.
+     *
+     * @hide
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public boolean isInstrumenting() {
+        // Check if we have an instrumentation context, as init should only get called by
+        // the system in startup processes that are being instrumented.
+        if (mInstrContext == null) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Called when the instrumentation is starting, before any application code
+     * has been loaded.  Usually this will be implemented to simply call
+     * {@link #start} to begin the instrumentation thread, which will then
+     * continue execution in {@link #onStart}.
+     * 
+     * <p>If you do not need your own thread -- that is you are writing your
+     * instrumentation to be completely asynchronous (returning to the event
+     * loop so that the application can run), you can simply begin your
+     * instrumentation here, for example call {@link Context#startActivity} to
+     * begin the appropriate first activity of the application. 
+     *  
+     * @param arguments Any additional arguments that were supplied when the 
+     *                  instrumentation was started.
+     */
+    public void onCreate(Bundle arguments) {
+    }
+
+    /**
+     * Create and start a new thread in which to run instrumentation.  This new
+     * thread will call to {@link #onStart} where you can implement the
+     * instrumentation.
+     */
+    public void start() {
+        if (mRunner != null) {
+            throw new RuntimeException("Instrumentation already started");
+        }
+        mRunner = new InstrumentationThread("Instr: " + getClass().getName());
+        mRunner.start();
+    }
+
+    /**
+     * Method where the instrumentation thread enters execution.  This allows
+     * you to run your instrumentation code in a separate thread than the
+     * application, so that it can perform blocking operation such as
+     * {@link #sendKeySync} or {@link #startActivitySync}.
+     * 
+     * <p>You will typically want to call finish() when this function is done,
+     * to end your instrumentation.
+     */
+    public void onStart() {
+    }
+
+    /**
+     * This is called whenever the system captures an unhandled exception that
+     * was thrown by the application.  The default implementation simply
+     * returns false, allowing normal system handling of the exception to take
+     * place.
+     * 
+     * @param obj The client object that generated the exception.  May be an
+     *            Application, Activity, BroadcastReceiver, Service, or null.
+     * @param e The exception that was thrown.
+     *  
+     * @return To allow normal system exception process to occur, return false.
+     *         If true is returned, the system will proceed as if the exception
+     *         didn't happen.
+     */
+    public boolean onException(Object obj, Throwable e) {
+        return false;
+    }
+
+    /**
+     * Provide a status report about the application.
+     *  
+     * @param resultCode Current success/failure of instrumentation. 
+     * @param results Any results to send back to the code that started the instrumentation.
+     */
+    public void sendStatus(int resultCode, Bundle results) {
+        if (mWatcher != null) {
+            try {
+                mWatcher.instrumentationStatus(mComponent, resultCode, results);
+            }
+            catch (RemoteException e) {
+                mWatcher = null;
+            }
+        }
+    }
+
+    /**
+     * Report some results in the middle of instrumentation execution.  Later results (including
+     * those provided by {@link #finish}) will be combined with {@link Bundle#putAll}.
+     */
+    public void addResults(Bundle results) {
+        IActivityManager am = ActivityManager.getService();
+        try {
+            am.addInstrumentationResults(mThread.getApplicationThread(), results);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Terminate instrumentation of the application.  This will cause the
+     * application process to exit, removing this instrumentation from the next
+     * time the application is started.  If multiple processes are currently running
+     * for this instrumentation, all of those processes will be killed.
+     *  
+     * @param resultCode Overall success/failure of instrumentation. 
+     * @param results Any results to send back to the code that started the 
+     *                instrumentation.
+     */
+    public void finish(int resultCode, Bundle results) {
+        if (mAutomaticPerformanceSnapshots) {
+            endPerformanceSnapshot();
+        }
+        if (mPerfMetrics != null) {
+            if (results == null) {
+                results = new Bundle();
+            }
+            results.putAll(mPerfMetrics);
+        }
+        if ((mUiAutomation != null) && !mUiAutomation.isDestroyed()) {
+            mUiAutomation.disconnect();
+            mUiAutomation = null;
+        }
+        mThread.finishInstrumentation(resultCode, results);
+    }
+    
+    public void setAutomaticPerformanceSnapshots() {
+        mAutomaticPerformanceSnapshots = true;
+        mPerformanceCollector = new PerformanceCollector();
+    }
+
+    public void startPerformanceSnapshot() {
+        if (!isProfiling()) {
+            mPerformanceCollector.beginSnapshot(null);
+        }
+    }
+    
+    public void endPerformanceSnapshot() {
+        if (!isProfiling()) {
+            mPerfMetrics = mPerformanceCollector.endSnapshot();
+        }
+    }
+    
+    /**
+     * Called when the instrumented application is stopping, after all of the
+     * normal application cleanup has occurred.
+     */
+    public void onDestroy() {
+    }
+
+    /**
+     * Return the Context of this instrumentation's package.  Note that this is
+     * often different than the Context of the application being
+     * instrumentated, since the instrumentation code often lives is a
+     * different package than that of the application it is running against.
+     * See {@link #getTargetContext} to retrieve a Context for the target
+     * application.
+     * 
+     * @return The instrumentation's package context.
+     * 
+     * @see #getTargetContext
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public Context getContext() {
+        return mInstrContext;
+    }
+
+    /**
+     * Returns complete component name of this instrumentation.
+     * 
+     * @return Returns the complete component name for this instrumentation.
+     */
+    public ComponentName getComponentName() {
+        return mComponent;
+    }
+    
+    /**
+     * Return a Context for the target application being instrumented.  Note
+     * that this is often different than the Context of the instrumentation
+     * code, since the instrumentation code often lives is a different package
+     * than that of the application it is running against. See
+     * {@link #getContext} to retrieve a Context for the instrumentation code.
+     * 
+     * @return A Context in the target application.
+     * 
+     * @see #getContext
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public Context getTargetContext() {
+        return mAppContext;
+    }
+
+    /**
+     * Return the name of the process this instrumentation is running in.  Note this should
+     * only be used for testing and debugging.  If you are thinking about using this to,
+     * for example, conditionalize what is initialized in an Application class, it is strongly
+     * recommended to instead use lazy initialization (such as a getter for the state that
+     * only creates it when requested).  This can greatly reduce the work your process does
+     * when created for secondary things, such as to receive a broadcast.
+     */
+    public String getProcessName() {
+        return mThread.getProcessName();
+    }
+
+    /**
+     * Check whether this instrumentation was started with profiling enabled.
+     * 
+     * @return Returns true if profiling was enabled when starting, else false.
+     */
+    public boolean isProfiling() {
+        return mThread.isProfiling();
+    }
+
+    /**
+     * This method will start profiling if isProfiling() returns true. You should
+     * only call this method if you set the handleProfiling attribute in the 
+     * manifest file for this Instrumentation to true.  
+     */
+    public void startProfiling() {
+        if (mThread.isProfiling()) {
+            File file = new File(mThread.getProfileFilePath());
+            file.getParentFile().mkdirs();
+            Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+        }
+    }
+
+    /**
+     * Stops profiling if isProfiling() returns true.
+     */
+    public void stopProfiling() {
+        if (mThread.isProfiling()) {
+            Debug.stopMethodTracing();
+        }
+    }
+
+    /**
+     * Force the global system in or out of touch mode. This can be used if your
+     * instrumentation relies on the UI being in one more or the other when it starts.
+     *
+     * <p><b>Note:</b> Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method
+     * will only take effect if the instrumentation was sourced from a process with
+     * {@code MODIFY_TOUCH_MODE_STATE} internal permission granted (shell already have it).
+     *
+     * @param inTouch Set to true to be in touch mode, false to be in focus mode.
+     */
+    public void setInTouchMode(boolean inTouch) {
+        try {
+            IWindowManager.Stub.asInterface(
+                    ServiceManager.getService("window")).setInTouchModeOnAllDisplays(inTouch);
+        } catch (RemoteException e) {
+            // Shouldn't happen!
+        }
+    }
+
+    /**
+     * Resets the {@link #setInTouchMode touch mode} to the device default.
+     */
+    public void resetInTouchMode() {
+        final boolean defaultInTouchMode = getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_defaultInTouchMode);
+        setInTouchMode(defaultInTouchMode);
+    }
+
+    /**
+     * Schedule a callback for when the application's main thread goes idle
+     * (has no more events to process).
+     *
+     * @param recipient Called the next time the thread's message queue is
+     *                  idle.
+     */
+    public void waitForIdle(Runnable recipient) {
+        mMessageQueue.addIdleHandler(new Idler(recipient));
+        mThread.getHandler().post(new EmptyRunnable());
+    }
+
+    /**
+     * Synchronously wait for the application to be idle.  Can not be called
+     * from the main application thread -- use {@link #start} to execute
+     * instrumentation in its own thread.
+     */
+    public void waitForIdleSync() {
+        validateNotAppThread();
+        Idler idler = new Idler(null);
+        mMessageQueue.addIdleHandler(idler);
+        mThread.getHandler().post(new EmptyRunnable());
+        idler.waitForIdle();
+    }
+
+    private void waitForEnterAnimationComplete(Activity activity) {
+        synchronized (mAnimationCompleteLock) {
+            long timeout = 5000;
+            try {
+                // We need to check that this specified Activity completed the animation, not just
+                // any Activity. If it was another Activity, then decrease the timeout by how long
+                // it's already waited and wait for the thread to wakeup again.
+                while (timeout > 0 && !activity.mEnterAnimationComplete) {
+                    long startTime = System.currentTimeMillis();
+                    mAnimationCompleteLock.wait(timeout);
+                    long totalTime = System.currentTimeMillis() - startTime;
+                    timeout -= totalTime;
+                }
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    /** @hide */
+    public void onEnterAnimationComplete() {
+        synchronized (mAnimationCompleteLock) {
+            mAnimationCompleteLock.notifyAll();
+        }
+    }
+
+    /**
+     * Execute a call on the application's main thread, blocking until it is
+     * complete.  Useful for doing things that are not thread-safe, such as
+     * looking at or modifying the view hierarchy.
+     * 
+     * @param runner The code to run on the main thread.
+     */
+    public void runOnMainSync(Runnable runner) {
+        validateNotAppThread();
+        SyncRunnable sr = new SyncRunnable(runner);
+        mThread.getHandler().post(sr);
+        sr.waitForComplete();
+    }
+
+    boolean isSdkSandboxAllowedToStartActivities() {
+        return Process.isSdkSandbox()
+                && mThread != null
+                && mThread.mBoundApplication != null
+                && mThread.mBoundApplication.isSdkInSandbox
+                && getContext() != null
+                && (getContext()
+                                .checkSelfPermission(
+                                        android.Manifest.permission
+                                                .START_ACTIVITIES_FROM_SDK_SANDBOX)
+                        == PackageManager.PERMISSION_GRANTED);
+    }
+
+    /**
+     * Activity name resolution for CTS-in-SdkSandbox tests requires some adjustments. Intents
+     * generated using {@link Context#getPackageName()} use the SDK sandbox package name in the
+     * component field instead of the test package name. An SDK-in-sandbox test attempting to launch
+     * an activity in the test package will encounter name resolution errors when resolving the
+     * activity name in the SDK sandbox package.
+     *
+     * <p>This function replaces the package name of the input intent component to allow activities
+     * belonging to a CTS-in-sandbox test to resolve correctly.
+     *
+     * @param intent the intent to modify to allow CTS-in-sandbox activity resolution.
+     */
+    private void adjustIntentForCtsInSdkSandboxInstrumentation(@NonNull Intent intent) {
+        if (mComponent != null
+                && intent.getComponent() != null
+                && getContext()
+                        .getPackageManager()
+                        .getSdkSandboxPackageName()
+                        .equals(intent.getComponent().getPackageName())) {
+            // Resolve the intent target for the test package, not for the sandbox package.
+            intent.setComponent(
+                    new ComponentName(
+                            mComponent.getPackageName(), intent.getComponent().getClassName()));
+        }
+        // We match the intent identifier against the running instrumentations for the sandbox.
+        intent.setIdentifier(mComponent.getPackageName());
+    }
+
+    private ActivityInfo resolveActivityInfoForCtsInSandbox(@NonNull Intent intent) {
+        adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        ActivityInfo ai = intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
+        if (ai != null) {
+            ai.processName = mThread.getProcessName();
+        }
+        return ai;
+    }
+
+    /**
+     * Start a new activity and wait for it to begin running before returning.
+     * In addition to being synchronous, this method as some semantic
+     * differences from the standard {@link Context#startActivity} call: the
+     * activity component is resolved before talking with the activity manager
+     * (its class name is specified in the Intent that this method ultimately
+     * starts), and it does not allow you to start activities that run in a
+     * different process.  In addition, if the given Intent resolves to
+     * multiple activities, instead of displaying a dialog for the user to
+     * select an activity, an exception will be thrown.
+     *
+     * <p>The function returns as soon as the activity goes idle following the
+     * call to its {@link Activity#onCreate}.  Generally this means it has gone
+     * through the full initialization including {@link Activity#onResume} and
+     * drawn and displayed its initial window.
+     *
+     * @param intent Description of the activity to start.
+     *
+     * @see Context#startActivity
+     * @see #startActivitySync(Intent, Bundle)
+     */
+    public Activity startActivitySync(Intent intent) {
+        return startActivitySync(intent, null /* options */);
+    }
+
+    /**
+     * Start a new activity and wait for it to begin running before returning.
+     * In addition to being synchronous, this method as some semantic
+     * differences from the standard {@link Context#startActivity} call: the
+     * activity component is resolved before talking with the activity manager
+     * (its class name is specified in the Intent that this method ultimately
+     * starts), and it does not allow you to start activities that run in a
+     * different process.  In addition, if the given Intent resolves to
+     * multiple activities, instead of displaying a dialog for the user to
+     * select an activity, an exception will be thrown.
+     *
+     * <p>The function returns as soon as the activity goes idle following the
+     * call to its {@link Activity#onCreate}.  Generally this means it has gone
+     * through the full initialization including {@link Activity#onResume} and
+     * drawn and displayed its initial window.
+     *
+     * @param intent Description of the activity to start.
+     * @param options Additional options for how the Activity should be started.
+     * May be null if there are no options.  See {@link android.app.ActivityOptions}
+     * for how to build the Bundle supplied here; there are no supported definitions
+     * for building it manually.
+     *
+     * @see Context#startActivity(Intent, Bundle)
+     */
+    @NonNull
+    public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: intent=" + intent + " options=" + options, new Throwable());
+        }
+        validateNotAppThread();
+
+        final Activity activity;
+        synchronized (mSync) {
+            intent = new Intent(intent);
+
+            ActivityInfo ai =
+                    isSdkSandboxAllowedToStartActivities()
+                            ? resolveActivityInfoForCtsInSandbox(intent)
+                            : intent.resolveActivityInfo(getTargetContext().getPackageManager(), 0);
+            if (ai == null) {
+                throw new RuntimeException("Unable to resolve activity for: " + intent);
+            }
+            String myProc = mThread.getProcessName();
+            if (!ai.processName.equals(myProc)) {
+                // todo: if this intent is ambiguous, look here to see if
+                // there is a single match that is in our package.
+                throw new RuntimeException("Intent in process "
+                        + myProc + " resolved to different process "
+                        + ai.processName + ": " + intent);
+            }
+
+            intent.setComponent(new ComponentName(
+                    ai.applicationInfo.packageName, ai.name));
+            final ActivityWaiter aw = new ActivityWaiter(intent);
+
+            if (mWaitingActivities == null) {
+                mWaitingActivities = new ArrayList();
+            }
+            mWaitingActivities.add(aw);
+
+            getTargetContext().startActivity(intent, options);
+
+            do {
+                try {
+                    mSync.wait();
+                } catch (InterruptedException e) {
+                }
+            } while (mWaitingActivities.contains(aw));
+            activity = aw.activity;
+        }
+
+        // Do not call this method within mSync, lest it could block the main thread.
+        waitForEnterAnimationComplete(activity);
+
+        // Apply an empty transaction to ensure SF has a chance to update before
+        // the Activity is ready (b/138263890).
+        try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+            t.apply(true);
+        }
+        return activity;
+    }
+
+    /**
+     * Information about a particular kind of Intent that is being monitored.
+     * An instance of this class is added to the 
+     * current instrumentation through {@link #addMonitor}; after being added, 
+     * when a new activity is being started the monitor will be checked and, if 
+     * matching, its hit count updated and (optionally) the call stopped and a 
+     * canned result returned.
+     * 
+     * <p>An ActivityMonitor can also be used to look for the creation of an
+     * activity, through the {@link #waitForActivity} method.  This will return
+     * after a matching activity has been created with that activity object.
+     */
+    public static class ActivityMonitor {
+        private final IntentFilter mWhich;
+        private final String mClass;
+        private final ActivityResult mResult;
+        private final boolean mBlock;
+        private final boolean mIgnoreMatchingSpecificIntents;
+
+
+        // This is protected by 'Instrumentation.this.mSync'.
+        /*package*/ int mHits = 0;
+
+        // This is protected by 'this'.
+        /*package*/ Activity mLastActivity = null;
+
+        /**
+         * Create a new ActivityMonitor that looks for a particular kind of 
+         * intent to be started.
+         *
+         * @param which The set of intents this monitor is responsible for.
+         * @param result A canned result to return if the monitor is hit; can 
+         *               be null.
+         * @param block Controls whether the monitor should block the activity 
+         *              start (returning its canned result) or let the call
+         *              proceed.
+         *  
+         * @see Instrumentation#addMonitor 
+         */
+        public ActivityMonitor(
+            IntentFilter which, ActivityResult result, boolean block) {
+            mWhich = which;
+            mClass = null;
+            mResult = result;
+            mBlock = block;
+            mIgnoreMatchingSpecificIntents = false;
+        }
+
+        /**
+         * Create a new ActivityMonitor that looks for a specific activity 
+         * class to be started. 
+         *  
+         * @param cls The activity class this monitor is responsible for.
+         * @param result A canned result to return if the monitor is hit; can 
+         *               be null.
+         * @param block Controls whether the monitor should block the activity 
+         *              start (returning its canned result) or let the call
+         *              proceed.
+         *  
+         * @see Instrumentation#addMonitor 
+         */
+        public ActivityMonitor(
+            String cls, ActivityResult result, boolean block) {
+            mWhich = null;
+            mClass = cls;
+            mResult = result;
+            mBlock = block;
+            mIgnoreMatchingSpecificIntents = false;
+        }
+
+        /**
+         * Create a new ActivityMonitor that can be used for intercepting any activity to be
+         * started.
+         *
+         * <p> When an activity is started, {@link #onStartActivity(Intent)} will be called on
+         * instances created using this constructor to see if it is a hit.
+         *
+         * @see #onStartActivity(Intent)
+         */
+        public ActivityMonitor() {
+            mWhich = null;
+            mClass = null;
+            mResult = null;
+            mBlock = false;
+            mIgnoreMatchingSpecificIntents = true;
+        }
+
+        /**
+         * @return true if this monitor is used for intercepting any started activity by calling
+         *         into {@link #onStartActivity(Intent)}, false if this monitor is only used
+         *         for specific intents corresponding to the intent filter or activity class
+         *         passed in the constructor.
+         */
+        final boolean ignoreMatchingSpecificIntents() {
+            return mIgnoreMatchingSpecificIntents;
+        }
+
+        /**
+         * Retrieve the filter associated with this ActivityMonitor.
+         */
+        public final IntentFilter getFilter() {
+            return mWhich;
+        }
+
+        /**
+         * Retrieve the result associated with this ActivityMonitor, or null if 
+         * none. 
+         */
+        public final ActivityResult getResult() {
+            return mResult;
+        }
+
+        /**
+         * Check whether this monitor blocks activity starts (not allowing the 
+         * actual activity to run) or allows them to execute normally. 
+         */
+        public final boolean isBlocking() {
+            return mBlock;
+        }
+
+        /**
+         * Retrieve the number of times the monitor has been hit so far.
+         */
+        public final int getHits() {
+            return mHits;
+        }
+
+        /**
+         * Retrieve the most recent activity class that was seen by this 
+         * monitor. 
+         */
+        public final Activity getLastActivity() {
+            return mLastActivity;
+        }
+
+        /**
+         * Block until an Activity is created that matches this monitor, 
+         * returning the resulting activity. 
+         * 
+         * @return Activity
+         */
+        public final Activity waitForActivity() {
+            synchronized (this) {
+                while (mLastActivity == null) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                Activity res = mLastActivity;
+                mLastActivity = null;
+                return res;
+            }
+        }
+
+        /**
+         * Block until an Activity is created that matches this monitor, 
+         * returning the resulting activity or till the timeOut period expires.
+         * If the timeOut expires before the activity is started, return null. 
+         * 
+         * @param timeOut Time to wait in milliseconds before the activity is created.
+         * 
+         * @return Activity
+         */
+        public final Activity waitForActivityWithTimeout(long timeOut) {
+            synchronized (this) {
+                if (mLastActivity == null) {
+                    try {
+                        wait(timeOut);
+                    } catch (InterruptedException e) {
+                    }
+                }
+                if (mLastActivity == null) {
+                    return null;
+                } else {
+                    Activity res = mLastActivity;
+                    mLastActivity = null;
+                    return res;
+                }
+            }
+        }
+
+        /**
+         * This overload is used for notifying the {@link android.window.TaskFragmentOrganizer}
+         * implementation internally about started activities.
+         *
+         * @see #onStartActivity(Intent)
+         * @hide
+         */
+        public ActivityResult onStartActivity(@NonNull Context who, @NonNull Intent intent,
+                @NonNull Bundle options) {
+            return onStartActivity(intent);
+        }
+
+        /**
+         * Used for intercepting any started activity.
+         *
+         * <p> A non-null return value here will be considered a hit for this monitor.
+         * By default this will return {@code null} and subclasses can override this to return
+         * a non-null value if the intent needs to be intercepted.
+         *
+         * <p> Whenever a new activity is started, this method will be called on instances created
+         * using {@link #ActivityMonitor()} to check if there is a match. In case
+         * of a match, the activity start will be blocked and the returned result will be used.
+         *
+         * @param intent The intent used for starting the activity.
+         * @return The {@link ActivityResult} that needs to be used in case of a match.
+         */
+        public ActivityResult onStartActivity(Intent intent) {
+            return null;
+        }
+
+        /**
+         * This is called after starting an Activity and provides the result code that defined in
+         * {@link ActivityManager}, like {@link ActivityManager#START_SUCCESS}.
+         *
+         * @param result the result code that returns after starting an Activity.
+         * @param bOptions the bundle generated from {@link ActivityOptions} that originally
+         *                 being used to start the Activity.
+         * @hide
+         */
+        public void onStartActivityResult(int result, @NonNull Bundle bOptions) {}
+
+        final boolean match(Context who,
+                            Activity activity,
+                            Intent intent) {
+            if (mIgnoreMatchingSpecificIntents) {
+                return false;
+            }
+            synchronized (this) {
+                if (mWhich != null
+                    && mWhich.match(who.getContentResolver(), intent,
+                                    true, "Instrumentation") < 0) {
+                    return false;
+                }
+                if (mClass != null) {
+                    String cls = null;
+                    if (activity != null) {
+                        cls = activity.getClass().getName();
+                    } else if (intent.getComponent() != null) {
+                        cls = intent.getComponent().getClassName();
+                    }
+                    if (cls == null || !mClass.equals(cls)) {
+                        return false;
+                    }
+                }
+                if (activity != null) {
+                    mLastActivity = activity;
+                    notifyAll();
+                }
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Add a new {@link ActivityMonitor} that will be checked whenever an 
+     * activity is started.  The monitor is added 
+     * after any existing ones; the monitor will be hit only if none of the 
+     * existing monitors can themselves handle the Intent. 
+     *  
+     * @param monitor The new ActivityMonitor to see. 
+     *  
+     * @see #addMonitor(IntentFilter, ActivityResult, boolean) 
+     * @see #checkMonitorHit 
+     */
+    public void addMonitor(ActivityMonitor monitor) {
+        synchronized (mSync) {
+            if (mActivityMonitors == null) {
+                mActivityMonitors = new ArrayList();
+            }
+            mActivityMonitors.add(monitor);
+        }
+    }
+
+    /**
+     * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 
+     * creates an intent filter matching {@link ActivityMonitor} for you and 
+     * returns it. 
+     *  
+     * @param filter The set of intents this monitor is responsible for.
+     * @param result A canned result to return if the monitor is hit; can 
+     *               be null.
+     * @param block Controls whether the monitor should block the activity 
+     *              start (returning its canned result) or let the call
+     *              proceed.
+     * 
+     * @return The newly created and added activity monitor. 
+     *  
+     * @see #addMonitor(ActivityMonitor) 
+     * @see #checkMonitorHit 
+     */
+    public ActivityMonitor addMonitor(
+        IntentFilter filter, ActivityResult result, boolean block) {
+        ActivityMonitor am = new ActivityMonitor(filter, result, block);
+        addMonitor(am);
+        return am;
+    }
+
+    /**
+     * A convenience wrapper for {@link #addMonitor(ActivityMonitor)} that 
+     * creates a class matching {@link ActivityMonitor} for you and returns it.
+     *  
+     * @param cls The activity class this monitor is responsible for.
+     * @param result A canned result to return if the monitor is hit; can 
+     *               be null.
+     * @param block Controls whether the monitor should block the activity 
+     *              start (returning its canned result) or let the call
+     *              proceed.
+     * 
+     * @return The newly created and added activity monitor. 
+     *  
+     * @see #addMonitor(ActivityMonitor) 
+     * @see #checkMonitorHit 
+     */
+    public ActivityMonitor addMonitor(
+        String cls, ActivityResult result, boolean block) {
+        ActivityMonitor am = new ActivityMonitor(cls, result, block);
+        addMonitor(am);
+        return am;
+    }
+
+    /**
+     * Test whether an existing {@link ActivityMonitor} has been hit.  If the 
+     * monitor has been hit at least <var>minHits</var> times, then it will be 
+     * removed from the activity monitor list and true returned.  Otherwise it 
+     * is left as-is and false is returned. 
+     *  
+     * @param monitor The ActivityMonitor to check.
+     * @param minHits The minimum number of hits required.
+     * 
+     * @return True if the hit count has been reached, else false. 
+     *  
+     * @see #addMonitor 
+     */
+    public boolean checkMonitorHit(ActivityMonitor monitor, int minHits) {
+        waitForIdleSync();
+        synchronized (mSync) {
+            if (monitor.getHits() < minHits) {
+                return false;
+            }
+            mActivityMonitors.remove(monitor);
+        }
+        return true;
+    }
+
+    /**
+     * Wait for an existing {@link ActivityMonitor} to be hit.  Once the 
+     * monitor has been hit, it is removed from the activity monitor list and 
+     * the first created Activity object that matched it is returned.
+     *  
+     * @param monitor The ActivityMonitor to wait for.
+     * 
+     * @return The Activity object that matched the monitor.
+     */
+    public Activity waitForMonitor(ActivityMonitor monitor) {
+        Activity activity = monitor.waitForActivity();
+        synchronized (mSync) {
+            mActivityMonitors.remove(monitor);
+        }
+        return activity;
+    }
+
+    /**
+     * Wait for an existing {@link ActivityMonitor} to be hit till the timeout
+     * expires.  Once the monitor has been hit, it is removed from the activity 
+     * monitor list and the first created Activity object that matched it is 
+     * returned.  If the timeout expires, a null object is returned. 
+     *
+     * @param monitor The ActivityMonitor to wait for.
+     * @param timeOut The timeout value in milliseconds.
+     *
+     * @return The Activity object that matched the monitor.
+     */
+    public Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) {
+        Activity activity = monitor.waitForActivityWithTimeout(timeOut);
+        synchronized (mSync) {
+            mActivityMonitors.remove(monitor);
+        }
+        return activity;
+    }
+    
+    /**
+     * Remove an {@link ActivityMonitor} that was previously added with 
+     * {@link #addMonitor}.
+     *  
+     * @param monitor The monitor to remove.
+     *  
+     * @see #addMonitor 
+     */
+    public void removeMonitor(ActivityMonitor monitor) {
+        synchronized (mSync) {
+            mActivityMonitors.remove(monitor);
+        }
+    }
+
+    /**
+     * Execute a particular menu item.
+     * 
+     * @param targetActivity The activity in question.
+     * @param id The identifier associated with the menu item.
+     * @param flag Additional flags, if any.
+     * @return Whether the invocation was successful (for example, it could be
+     *         false if item is disabled).
+     */
+    public boolean invokeMenuActionSync(Activity targetActivity, 
+                                    int id, int flag) {
+        class MenuRunnable implements Runnable {
+            private final Activity activity;
+            private final int identifier;
+            private final int flags;
+            boolean returnValue;
+            
+            public MenuRunnable(Activity _activity, int _identifier,
+                                    int _flags) {
+                activity = _activity;
+                identifier = _identifier;
+                flags = _flags;
+            }
+            
+            public void run() {
+                Window win = activity.getWindow();
+                
+                returnValue = win.performPanelIdentifierAction(
+                            Window.FEATURE_OPTIONS_PANEL,
+                            identifier, 
+                            flags);                
+            }
+            
+        }        
+        MenuRunnable mr = new MenuRunnable(targetActivity, id, flag);
+        runOnMainSync(mr);
+        return mr.returnValue;
+    }
+
+    /**
+     * Show the context menu for the currently focused view and executes a
+     * particular context menu item.
+     * 
+     * @param targetActivity The activity in question.
+     * @param id The identifier associated with the context menu item.
+     * @param flag Additional flags, if any.
+     * @return Whether the invocation was successful (for example, it could be
+     *         false if item is disabled).
+     */
+    public boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) {
+        validateNotAppThread();
+        
+        // Bring up context menu for current focus.
+        // It'd be nice to do this through code, but currently ListView depends on
+        //   long press to set metadata for its selected child
+        
+        final KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); 
+        sendKeySync(downEvent);
+
+        // Need to wait for long press
+        waitForIdleSync();
+        try {
+            Thread.sleep(ViewConfiguration.getLongPressTimeout());
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Could not sleep for long press timeout", e);
+            return false;
+        }
+
+        final KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); 
+        sendKeySync(upEvent);
+
+        // Wait for context menu to appear
+        waitForIdleSync();
+        
+        class ContextMenuRunnable implements Runnable {
+            private final Activity activity;
+            private final int identifier;
+            private final int flags;
+            boolean returnValue;
+            
+            public ContextMenuRunnable(Activity _activity, int _identifier,
+                                    int _flags) {
+                activity = _activity;
+                identifier = _identifier;
+                flags = _flags;
+            }
+            
+            public void run() {
+                Window win = activity.getWindow();
+                returnValue = win.performContextMenuIdentifierAction(
+                            identifier, 
+                            flags);                
+            }
+            
+        }        
+        
+        ContextMenuRunnable cmr = new ContextMenuRunnable(targetActivity, id, flag);
+        runOnMainSync(cmr);
+        return cmr.returnValue;
+    }
+    
+    /**
+     * Sends the key events that result in the given text being typed into the currently focused
+     * window, and waits for it to be processed.
+     *
+     * @param text The text to be sent.
+     * @see #sendKeySync(KeyEvent)
+     */
+    public void sendStringSync(String text) {
+        if (text == null) {
+            return;
+        }
+        KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+
+        KeyEvent[] events = keyCharacterMap.getEvents(text.toCharArray());
+
+        if (events != null) {
+            for (int i = 0; i < events.length; i++) {
+                // We have to change the time of an event before injecting it because
+                // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
+                // time stamp and the system rejects too old events. Hence, it is
+                // possible for an event to become stale before it is injected if it
+                // takes too long to inject the preceding ones.
+                sendKeySync(KeyEvent.changeTimeRepeat(events[i], SystemClock.uptimeMillis(), 0));
+            }
+        }
+    }
+
+    /**
+     * Sends a key event to the currently focused window, and waits for it to be processed.
+     * <p>
+     * This method blocks until the recipient has finished handling the event. Note that the
+     * recipient may <em>not</em> have completely finished reacting from the event when this method
+     * returns. For example, it may still be in the process of updating its display or UI contents
+     * upon reacting to the injected event.
+     *
+     * @param event The event to send to the current focus.
+     */
+    public void sendKeySync(KeyEvent event) {
+        validateNotAppThread();
+
+        long downTime = event.getDownTime();
+        long eventTime = event.getEventTime();
+        int source = event.getSource();
+        if (source == InputDevice.SOURCE_UNKNOWN) {
+            source = InputDevice.SOURCE_KEYBOARD;
+        }
+        if (eventTime == 0) {
+            eventTime = SystemClock.uptimeMillis();
+        }
+        if (downTime == 0) {
+            downTime = eventTime;
+        }
+        KeyEvent newEvent = new KeyEvent(event);
+        newEvent.setTime(downTime, eventTime);
+        newEvent.setSource(source);
+        newEvent.setFlags(event.getFlags() | KeyEvent.FLAG_FROM_SYSTEM);
+        setDisplayIfNeeded(newEvent);
+
+        InputManagerGlobal.getInstance().injectInputEvent(newEvent,
+                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+    }
+
+    private void setDisplayIfNeeded(KeyEvent event) {
+        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+            return;
+        }
+        // In devices that support visible background users visible, the display id must be set to
+        // reflect the display the user was started visible on, otherwise the event would be sent to
+        // the main display (which would most likely fail the test).
+        int eventDisplayId = event.getDisplayId();
+        if (eventDisplayId != Display.INVALID_DISPLAY) {
+            if (VERBOSE) {
+                Log.v(TAG, "setDisplayIfNeeded(" + event + "): not changing display id as it's "
+                        + "explicitly set to " + eventDisplayId);
+            }
+            return;
+        }
+
+        UserManager userManager = mInstrContext.getSystemService(UserManager.class);
+        int userDisplayId = userManager.getMainDisplayIdAssignedToUser();
+        if (VERBOSE) {
+            Log.v(TAG, "setDisplayIfNeeded(" + event + "): eventDisplayId=" + eventDisplayId
+                    + ", user=" + mInstrContext.getUser() + ", userDisplayId=" + userDisplayId);
+        }
+        if (userDisplayId == Display.INVALID_DISPLAY) {
+            Log.e(TAG, "setDisplayIfNeeded(" + event + "): UserManager returned INVALID_DISPLAY as "
+                    + "display assigned to user " + mInstrContext.getUser());
+            return;
+
+        }
+
+        event.setDisplayId(userDisplayId);
+    }
+
+    /**
+     * Sends up and down key events with the given key code to the currently focused window, and
+     * waits for it to be processed.
+     * 
+     * @param keyCode The key code for the events to send.
+     * @see #sendKeySync(KeyEvent)
+     */
+    public void sendKeyDownUpSync(int keyCode) {
+        sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+        sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+    }
+
+    /**
+     * Sends up and down key events with the given key code to the currently focused window, and
+     * waits for it to be processed.
+     * <p>
+     * Equivalent to {@link #sendKeyDownUpSync(int)}.
+     *
+     * @param keyCode The key code of the character to send.
+     * @see #sendKeySync(KeyEvent)
+     */
+    public void sendCharacterSync(int keyCode) {
+        sendKeyDownUpSync(keyCode);
+    }
+
+    /**
+     * Dispatches a pointer event into a window owned by the instrumented application, and waits for
+     * it to be processed.
+     * <p>
+     * If the motion event being injected is targeted at a window that is not owned by the
+     * instrumented application, the input injection will fail. See {@link #getUiAutomation()} for
+     * injecting events into all windows.
+     * <p>
+     * This method blocks until the recipient has finished handling the event. Note that the
+     * recipient may <em>not</em> have completely finished reacting from the event when this method
+     * returns. For example, it may still be in the process of updating its display or UI contents
+     * upon reacting to the injected event.
+     * 
+     * @param event A motion event describing the pointer action.  (As noted in 
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     * {@link SystemClock#uptimeMillis()} as the timebase.
+     */
+    public void sendPointerSync(MotionEvent event) {
+        validateNotAppThread();
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        }
+
+        syncInputTransactionsAndInjectEventIntoSelf(event);
+    }
+
+    private void syncInputTransactionsAndInjectEventIntoSelf(MotionEvent event) {
+        final boolean syncBefore = event.getAction() == MotionEvent.ACTION_DOWN
+                || event.isFromSource(InputDevice.SOURCE_MOUSE);
+        final boolean syncAfter = event.getAction() == MotionEvent.ACTION_UP;
+
+        try {
+            if (syncBefore) {
+                WindowManagerGlobal.getWindowManagerService()
+                        .syncInputTransactions(true /*waitForAnimations*/);
+            }
+
+            // Direct the injected event into windows owned by the instrumentation target.
+            InputManagerGlobal.getInstance().injectInputEvent(
+                    event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH, Process.myUid());
+
+            if (syncAfter) {
+                WindowManagerGlobal.getWindowManagerService()
+                        .syncInputTransactions(true /*waitForAnimations*/);
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Dispatches a trackball event into the currently focused window, and waits for it to be
+     * processed.
+     * <p>
+     * This method blocks until the recipient has finished handling the event. Note that the
+     * recipient may <em>not</em> have completely finished reacting from the event when this method
+     * returns. For example, it may still be in the process of updating its display or UI contents
+     * upon reacting to the injected event.
+     *
+     * @param event A motion event describing the trackball action.  (As noted in 
+     * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use 
+     * {@link SystemClock#uptimeMillis()} as the timebase.
+     */
+    public void sendTrackballEventSync(MotionEvent event) {
+        validateNotAppThread();
+        if (!event.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
+            event.setSource(InputDevice.SOURCE_TRACKBALL);
+        }
+        InputManagerGlobal.getInstance().injectInputEvent(event,
+                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+    }
+
+    /**
+     * Perform instantiation of the process's {@link Application} object.  The
+     * default implementation provides the normal system behavior.
+     * 
+     * @param cl The ClassLoader with which to instantiate the object.
+     * @param className The name of the class implementing the Application
+     *                  object.
+     * @param context The context to initialize the application with
+     * 
+     * @return The newly instantiated Application object.
+     */
+    public Application newApplication(ClassLoader cl, String className, Context context)
+            throws InstantiationException, IllegalAccessException, 
+            ClassNotFoundException {
+        Application app = getFactory(context.getPackageName())
+                .instantiateApplication(cl, className);
+        app.attach(context);
+        return app;
+    }
+    
+    /**
+     * Perform instantiation of the process's {@link Application} object.  The
+     * default implementation provides the normal system behavior.
+     * 
+     * @param clazz The class used to create an Application object from.
+     * @param context The context to initialize the application with
+     * 
+     * @return The newly instantiated Application object.
+     */
+    static public Application newApplication(Class<?> clazz, Context context)
+            throws InstantiationException, IllegalAccessException, 
+            ClassNotFoundException {
+        Application app = (Application)clazz.newInstance();
+        app.attach(context);
+        return app;
+    }
+
+    /**
+     * Perform calling of the application's {@link Application#onCreate}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
+     * Often instrumentation tests start their test thread in onCreate(); you
+     * need to be careful of races between these.  (Well between it and
+     * everything else, but let's start here.)
+     *
+     * @param app The application being created.
+     */
+    public void callApplicationOnCreate(Application app) {
+        app.onCreate();
+    }
+    
+    /**
+     * Perform instantiation of an {@link Activity} object.  This method is intended for use with
+     * unit tests, such as android.test.ActivityUnitTestCase.  The activity will be useable
+     * locally but will be missing some of the linkages necessary for use within the system.
+     * 
+     * @param clazz The Class of the desired Activity
+     * @param context The base context for the activity to use
+     * @param token The token for this activity to communicate with
+     * @param application The application object (if any)
+     * @param intent The intent that started this Activity
+     * @param info ActivityInfo from the manifest
+     * @param title The title, typically retrieved from the ActivityInfo record
+     * @param parent The parent Activity (if any)
+     * @param id The embedded Id (if any)
+     * @param lastNonConfigurationInstance Arbitrary object that will be
+     * available via {@link Activity#getLastNonConfigurationInstance()
+     * Activity.getLastNonConfigurationInstance()}.
+     * @return Returns the instantiated activity
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    public Activity newActivity(Class<?> clazz, Context context, 
+            IBinder token, Application application, Intent intent, ActivityInfo info, 
+            CharSequence title, Activity parent, String id,
+            Object lastNonConfigurationInstance) throws InstantiationException,
+            IllegalAccessException {
+        Activity activity = (Activity)clazz.newInstance();
+        ActivityThread aThread = null;
+        // Activity.attach expects a non-null Application Object.
+        if (application == null) {
+            application = new Application();
+        }
+        activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
+                info, title, parent, id,
+                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
+                new Configuration(), null /* referrer */, null /* voiceInteractor */,
+                null /* window */, null /* activityCallback */, null /* assistToken */,
+                null /* shareableActivityToken */, null /* initialCallerInfoAccessToken */);
+        return activity;
+    }
+
+    /**
+     * Perform instantiation of the process's {@link Activity} object.  The
+     * default implementation provides the normal system behavior.
+     * 
+     * @param cl The ClassLoader with which to instantiate the object.
+     * @param className The name of the class implementing the Activity
+     *                  object.
+     * @param intent The Intent object that specified the activity class being
+     *               instantiated.
+     * 
+     * @return The newly instantiated Activity object.
+     */
+    public Activity newActivity(ClassLoader cl, String className,
+            Intent intent)
+            throws InstantiationException, IllegalAccessException,
+            ClassNotFoundException {
+        String pkg = intent != null && intent.getComponent() != null
+                ? intent.getComponent().getPackageName() : null;
+        return getFactory(pkg).instantiateActivity(cl, className, intent);
+    }
+
+    private AppComponentFactory getFactory(String pkg) {
+        if (pkg == null) {
+            Log.e(TAG, "No pkg specified, disabling AppComponentFactory");
+            return AppComponentFactory.DEFAULT;
+        }
+        if (mThread == null) {
+            Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation,"
+                    + " disabling AppComponentFactory", new Throwable());
+            return AppComponentFactory.DEFAULT;
+        }
+        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
+        // This is in the case of starting up "android".
+        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
+        return apk.getAppFactory();
+    }
+
+    /**
+     * This should be called before {@link #checkStartActivityResult(int, Object)}, because
+     * exceptions might be thrown while checking the results.
+     */
+    private void notifyStartActivityResult(int result, @Nullable Bundle options) {
+        if (mActivityMonitors == null) {
+            return;
+        }
+        synchronized (mSync) {
+            final int size = mActivityMonitors.size();
+            for (int i = 0; i < size; i++) {
+                final ActivityMonitor am = mActivityMonitors.get(i);
+                if (am.ignoreMatchingSpecificIntents()) {
+                    if (options == null) {
+                        options = ActivityOptions.makeBasic().toBundle();
+                    }
+                    am.onStartActivityResult(result, options);
+                }
+            }
+        }
+    }
+
+    private void prePerformCreate(Activity activity) {
+        if (mWaitingActivities != null) {
+            synchronized (mSync) {
+                final int N = mWaitingActivities.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityWaiter aw = mWaitingActivities.get(i);
+                    final Intent intent = aw.intent;
+                    if (intent.filterEquals(activity.getIntent())) {
+                        aw.activity = activity;
+                        mMessageQueue.addIdleHandler(new ActivityGoing(aw));
+                    }
+                }
+            }
+        }
+    }
+
+    private void postPerformCreate(Activity activity) {
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    am.match(activity, activity, activity.getIntent());
+                }
+            }
+        }
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onCreate}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being created.
+     * @param icicle The previously frozen state (or null) to pass through to onCreate().
+     */
+    public void callActivityOnCreate(Activity activity, Bundle icicle) {
+        prePerformCreate(activity);
+        activity.performCreate(icicle);
+        postPerformCreate(activity);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onCreate}
+     * method.  The default implementation simply calls through to that method.
+     *  @param activity The activity being created.
+     * @param icicle The previously frozen state (or null) to pass through to
+     * @param persistentState The previously persisted state (or null)
+     */
+    public void callActivityOnCreate(Activity activity, Bundle icicle,
+            PersistableBundle persistentState) {
+        prePerformCreate(activity);
+        activity.performCreate(icicle, persistentState);
+        postPerformCreate(activity);
+    }
+    
+    public void callActivityOnDestroy(Activity activity) {
+      // TODO: the following block causes intermittent hangs when using startActivity
+      // temporarily comment out until root cause is fixed (bug 2630683)
+//      if (mWaitingActivities != null) {
+//          synchronized (mSync) {
+//              final int N = mWaitingActivities.size();
+//              for (int i=0; i<N; i++) {
+//                  final ActivityWaiter aw = mWaitingActivities.get(i);
+//                  final Intent intent = aw.intent;
+//                  if (intent.filterEquals(activity.getIntent())) {
+//                      aw.activity = activity;
+//                      mMessageQueue.addIdleHandler(new ActivityGoing(aw));
+//                  }
+//              }
+//          }
+//      }
+      
+      activity.performDestroy();
+  }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being restored.
+     * @param savedInstanceState The previously saved state being restored.
+     */
+    public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+            @NonNull Bundle savedInstanceState) {
+        activity.performRestoreInstanceState(savedInstanceState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being restored.
+     * @param savedInstanceState The previously saved state being restored (or null).
+     * @param persistentState The previously persisted state (or null)
+     */
+    public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
+        activity.performRestoreInstanceState(savedInstanceState, persistentState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onPostCreate} method.
+     * The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being created.
+     * @param savedInstanceState The previously saved state (or null) to pass through to
+     *               onPostCreate().
+     */
+    public void callActivityOnPostCreate(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState) {
+        activity.onPostCreate(savedInstanceState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onPostCreate} method.
+     * The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being created.
+     * @param savedInstanceState The previously frozen state (or null) to pass through to
+     *               onPostCreate().
+     * @param persistentState The previously persisted state (or null)
+     */
+    public void callActivityOnPostCreate(@NonNull Activity activity,
+            @Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
+        activity.onPostCreate(savedInstanceState, persistentState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onNewIntent}
+     * method.  The default implementation simply calls through to that method.
+     * 
+     * @param activity The activity receiving a new Intent.
+     * @param intent The new intent being received.
+     */
+    public void callActivityOnNewIntent(Activity activity, Intent intent) {
+        if (android.security.Flags.contentUriPermissionApis()) {
+            activity.performNewIntent(intent, new ComponentCaller(activity.getActivityToken(),
+                    /* callerToken */ null));
+        } else {
+            activity.performNewIntent(intent);
+        }
+    }
+
+    /**
+     * Same as {@link #callActivityOnNewIntent(Activity, Intent)}, but with an extra parameter for
+     * the {@link ComponentCaller} instance associated with the app that sent the intent.
+     *
+     * @param activity The activity receiving a new Intent.
+     * @param intent The new intent being received.
+     * @param caller The {@link ComponentCaller} instance that launched the activity with the new
+     *               intent.
+     */
+    @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+    public void callActivityOnNewIntent(@NonNull Activity activity, @NonNull Intent intent,
+            @NonNull ComponentCaller caller) {
+        activity.performNewIntent(intent, caller);
+    }
+
+    /**
+     * @hide
+     */
+    @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+    public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent,
+            @NonNull ComponentCaller caller) {
+        internalCallActivityOnNewIntent(activity, intent, caller);
+    }
+
+    @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+    private void internalCallActivityOnNewIntent(Activity activity, ReferrerIntent intent,
+            @NonNull ComponentCaller caller) {
+        final String oldReferrer = activity.mReferrer;
+        try {
+            if (intent != null) {
+                activity.mReferrer = intent.mReferrer;
+            }
+            Intent newIntent = intent != null ? new Intent(intent) : null;
+            callActivityOnNewIntent(activity, newIntent, caller);
+        } finally {
+            activity.mReferrer = oldReferrer;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) {
+        if (android.security.Flags.contentUriPermissionApis()) {
+            internalCallActivityOnNewIntent(activity, intent, new ComponentCaller(
+                    activity.getActivityToken(), /* callerToken */ null));
+        } else {
+            final String oldReferrer = activity.mReferrer;
+            try {
+                if (intent != null) {
+                    activity.mReferrer = intent.mReferrer;
+                }
+                callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null);
+            } finally {
+                activity.mReferrer = oldReferrer;
+            }
+        }
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onStart}
+     * method.  The default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being started.
+     */
+    public void callActivityOnStart(Activity activity) {
+        activity.onStart();
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onRestart}
+     * method.  The default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being restarted.
+     */
+    public void callActivityOnRestart(Activity activity) {
+        activity.onRestart();
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onResume} method.  The
+     * default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being resumed.
+     */
+    public void callActivityOnResume(Activity activity) {
+        activity.mResumed = true;
+        activity.onResume();
+        
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    am.match(activity, activity, activity.getIntent());
+                }
+            }
+        }
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onStop}
+     * method.  The default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being stopped.
+     */
+    public void callActivityOnStop(Activity activity) {
+        activity.onStop();
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onSaveInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being saved.
+     * @param outState The bundle to pass to the call.
+     */
+    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+            @NonNull Bundle outState) {
+        activity.performSaveInstanceState(outState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onSaveInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *  @param activity The activity being saved.
+     * @param outState The bundle to pass to the call.
+     * @param outPersistentState The persistent bundle to pass to the call.
+     */
+    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+            @NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
+        activity.performSaveInstanceState(outState, outPersistentState);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onPause} method.  The
+     * default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being paused.
+     */
+    public void callActivityOnPause(Activity activity) {
+        activity.performPause();
+    }
+    
+    /**
+     * Perform calling of an activity's {@link Activity#onUserLeaveHint} method.
+     * The default implementation simply calls through to that method.
+     * 
+     * @param activity The activity being notified that the user has navigated away
+     */
+    public void callActivityOnUserLeaving(Activity activity) {
+        activity.performUserLeaving();
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onPictureInPictureRequested} method.
+     * The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being notified that picture-in-picture is being requested.
+     */
+    public void callActivityOnPictureInPictureRequested(@NonNull Activity activity) {
+        activity.onPictureInPictureRequested();
+    }
+    
+    /*
+     * Starts allocation counting. This triggers a gc and resets the counts.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public void startAllocCounting() {
+        // Before we start trigger a GC and reset the debug counts. Run the 
+        // finalizers and another GC before starting and stopping the alloc
+        // counts. This will free up any objects that were just sitting around 
+        // waiting for their finalizers to be run.
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+
+        Debug.resetAllCounts();
+        
+        // start the counts
+        Debug.startAllocCounting();
+    }
+    
+    /*
+     * Stops allocation counting.
+     *
+     * @deprecated Accurate counting is a burden on the runtime and may be removed.
+     */
+    @Deprecated
+    public void stopAllocCounting() {
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+        Debug.stopAllocCounting();
+    }
+    
+    /**
+     * If Results already contains Key, it appends Value to the key's ArrayList
+     * associated with the key. If the key doesn't already exist in results, it
+     * adds the key/value pair to results.
+     */
+    private void addValue(String key, int value, Bundle results) {
+        if (results.containsKey(key)) {
+            List<Integer> list = results.getIntegerArrayList(key);
+            if (list != null) {
+                list.add(value);
+            }
+        } else {
+            ArrayList<Integer> list = new ArrayList<Integer>();
+            list.add(value);
+            results.putIntegerArrayList(key, list);
+        }
+    }
+
+    /**
+     * Returns a bundle with the current results from the allocation counting.
+     */
+    public Bundle getAllocCounts() {
+        Bundle results = new Bundle();
+        results.putLong("global_alloc_count", Debug.getGlobalAllocCount());
+        results.putLong("global_alloc_size", Debug.getGlobalAllocSize());
+        results.putLong("global_freed_count", Debug.getGlobalFreedCount());
+        results.putLong("global_freed_size", Debug.getGlobalFreedSize());
+        results.putLong("gc_invocation_count", Debug.getGlobalGcInvocationCount());    
+        return results;
+    }
+
+    /**
+     * Returns a bundle with the counts for various binder counts for this process. Currently the only two that are
+     * reported are the number of send and the number of received transactions.
+     */
+    public Bundle getBinderCounts() {
+        Bundle results = new Bundle();
+        results.putLong("sent_transactions", Debug.getBinderSentTransactions());
+        results.putLong("received_transactions", Debug.getBinderReceivedTransactions());
+        return results;
+    }
+    
+    /**
+     * Description of a Activity execution result to return to the original
+     * activity.
+     */
+    public static final class ActivityResult {
+        /**
+         * Create a new activity result.  See {@link Activity#setResult} for 
+         * more information. 
+         *  
+         * @param resultCode The result code to propagate back to the
+         * originating activity, often RESULT_CANCELED or RESULT_OK
+         * @param resultData The data to propagate back to the originating
+         * activity.
+         */
+        public ActivityResult(int resultCode, Intent resultData) {
+            mResultCode = resultCode;
+            mResultData = resultData;
+        }
+
+        /**
+         * Retrieve the result code contained in this result.
+         */
+        public int getResultCode() {
+            return mResultCode;
+        }
+
+        /**
+         * Retrieve the data contained in this result.
+         */
+        public Intent getResultData() {
+            return mResultData;
+        }
+
+        private final int mResultCode;
+        private final Intent mResultData;
+    }
+
+    /**
+     * Execute a startActivity call made by the application.  The default 
+     * implementation takes care of updating any active {@link ActivityMonitor}
+     * objects and dispatches this call to the system activity manager; you can
+     * override this to watch for the application to start an activity, and 
+     * modify what happens when it does. 
+     *
+     * <p>This method returns an {@link ActivityResult} object, which you can 
+     * use when intercepting application calls to avoid performing the start 
+     * activity action but still return the result the application is 
+     * expecting.  To do this, override this method to catch the call to start 
+     * activity so that it returns a new ActivityResult containing the results 
+     * you would like the application to see, and don't call up to the super 
+     * class.  Note that an application is only expecting a result if 
+     * <var>requestCode</var> is &gt;= 0.
+     *
+     * <p>This method throws {@link android.content.ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+     *
+     * @param who The Context from which the activity is being started.
+     * @param contextThread The main thread of the Context from which the activity
+     *                      is being started.
+     * @param token Internal token identifying to the system who is starting 
+     *              the activity; may be null.
+     * @param target Which activity is performing the start (and thus receiving 
+     *               any result); may be null if this call is not being made
+     *               from an activity.
+     * @param intent The actual Intent to start.
+     * @param requestCode Identifier for this request's result; less than zero 
+     *                    if the caller is not expecting a result.
+     * @param options Addition options.
+     *
+     * @return To force the return of a particular result, return an 
+     *         ActivityResult object containing the desired data; otherwise
+     *         return null.  The default implementation always returns null.
+     *
+     * @throws android.content.ActivityNotFoundException
+     *
+     * @see Activity#startActivity(Intent)
+     * @see Activity#startActivityForResult(Intent, int)
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public ActivityResult execStartActivity(
+            Context who, IBinder contextThread, IBinder token, Activity target,
+            Intent intent, int requestCode, Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: who=" + who + " source=" + target + " intent=" + intent
+                    + " requestCode=" + requestCode + " options=" + options, new Throwable());
+        }
+        Objects.requireNonNull(intent);
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        Uri referrer = target != null ? target.onProvideReferrer() : null;
+        if (referrer != null) {
+            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+        }
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return requestCode >= 0 ? am.getResult() : null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            intent.migrateExtraStreamToClipData(who);
+            intent.prepareToLeaveProcess(who);
+            int result = ActivityTaskManager.getService().startActivity(whoThread,
+                    who.getOpPackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
+                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+        return null;
+    }
+
+    /**
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
+     * but accepts an array of activities to be started.  Note that active
+     * {@link ActivityMonitor} objects only match against the first activity in
+     * the array.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public void execStartActivities(Context who, IBinder contextThread,
+            IBinder token, Activity target, Intent[] intents, Bundle options) {
+        execStartActivitiesAsUser(who, contextThread, token, target, intents, options,
+                who.getUserId());
+    }
+
+    /**
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
+     * but accepts an array of activities to be started.  Note that active
+     * {@link ActivityMonitor} objects only match against the first activity in
+     * the array.
+     *
+     * @return The corresponding flag {@link ActivityManager#START_CANCELED},
+     *         {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was
+     *         successful.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int execStartActivitiesAsUser(Context who, IBinder contextThread,
+            IBinder token, Activity target, Intent[] intents, Bundle options,
+            int userId) {
+        if (DEBUG_START_ACTIVITY) {
+            StringJoiner joiner = new StringJoiner(", ");
+            for (Intent i : intents) {
+                joiner.add(i.toString());
+            }
+            Log.d(TAG, "startActivities: who=" + who + " source=" + target + " userId=" + userId
+                    + " intents=[" + joiner + "] options=" + options, new Throwable());
+        }
+        Objects.requireNonNull(intents);
+        for (int i = intents.length - 1; i >= 0; i--) {
+            Objects.requireNonNull(intents[i]);
+        }
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            for (Intent intent : intents) {
+                adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+            }
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intents[0], options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return ActivityManager.START_CANCELED;
+                    } else if (am.match(who, null, intents[0])) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return ActivityManager.START_CANCELED;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            String[] resolvedTypes = new String[intents.length];
+            for (int i=0; i<intents.length; i++) {
+                intents[i].migrateExtraStreamToClipData(who);
+                intents[i].prepareToLeaveProcess(who);
+                resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
+            }
+            int result = ActivityTaskManager.getService().startActivities(whoThread,
+                    who.getOpPackageName(), who.getAttributionTag(), intents, resolvedTypes,
+                    token, options, userId);
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intents[0]);
+            return result;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+    }
+
+    /**
+     * Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
+     * android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
+     * but for calls from a {@link Fragment}.
+     *
+     * @param who The Context from which the activity is being started.
+     * @param contextThread The main thread of the Context from which the activity
+     *                      is being started.
+     * @param token Internal token identifying to the system who is starting
+     *              the activity; may be null.
+     * @param target Which element is performing the start (and thus receiving
+     *               any result).
+     * @param intent The actual Intent to start.
+     * @param requestCode Identifier for this request's result; less than zero
+     *                    if the caller is not expecting a result.
+     *
+     * @return To force the return of a particular result, return an
+     *         ActivityResult object containing the desired data; otherwise
+     *         return null.  The default implementation always returns null.
+     *
+     * @throws android.content.ActivityNotFoundException
+     *
+     * @see Activity#startActivity(Intent)
+     * @see Activity#startActivityForResult(Intent, int)
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public ActivityResult execStartActivity(
+        Context who, IBinder contextThread, IBinder token, String target,
+        Intent intent, int requestCode, Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: who=" + who + " target=" + target
+                    + " intent=" + intent + " requestCode=" + requestCode
+                    + " options=" + options, new Throwable());
+        }
+        Objects.requireNonNull(intent);
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return requestCode >= 0 ? am.getResult() : null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            intent.migrateExtraStreamToClipData(who);
+            intent.prepareToLeaveProcess(who);
+            int result = ActivityTaskManager.getService().startActivity(whoThread,
+                    who.getOpPackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
+                    requestCode, 0, null, options);
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+        return null;
+    }
+
+    /**
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
+     * but for starting as a particular user.
+     *
+     * @param who The Context from which the activity is being started.
+     * @param contextThread The main thread of the Context from which the activity
+     *                      is being started.
+     * @param token Internal token identifying to the system who is starting
+     *              the activity; may be null.
+     * @param target Which fragment is performing the start (and thus receiving
+     *               any result).
+     * @param intent The actual Intent to start.
+     * @param requestCode Identifier for this request's result; less than zero
+     *                    if the caller is not expecting a result.
+     *
+     * @return To force the return of a particular result, return an
+     *         ActivityResult object containing the desired data; otherwise
+     *         return null.  The default implementation always returns null.
+     *
+     * @throws android.content.ActivityNotFoundException
+     *
+     * @see Activity#startActivity(Intent)
+     * @see Activity#startActivityForResult(Intent, int)
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public ActivityResult execStartActivity(
+            Context who, IBinder contextThread, IBinder token, String resultWho,
+            Intent intent, int requestCode, Bundle options, UserHandle user) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: who=" + who + " user=" + user + " intent=" + intent
+                    + " requestCode=" + requestCode + " resultWho=" + resultWho
+                    + " options=" + options, new Throwable());
+        }
+        Objects.requireNonNull(intent);
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return requestCode >= 0 ? am.getResult() : null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            intent.migrateExtraStreamToClipData(who);
+            intent.prepareToLeaveProcess(who);
+            int result = ActivityTaskManager.getService().startActivityAsUser(whoThread,
+                    who.getOpPackageName(), who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho,
+                    requestCode, 0, null, options, user.getIdentifier());
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+        return null;
+    }
+
+    /**
+     * Special version!
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ActivityResult execStartActivityAsCaller(
+            Context who, IBinder contextThread, IBinder token, Activity target,
+            Intent intent, int requestCode, Bundle options,
+            boolean ignoreTargetSecurity, int userId) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: who=" + who + " source=" + target + " userId=" + userId
+                    + " intent=" + intent + " requestCode=" + requestCode
+                    + " ignoreTargetSecurity=" + ignoreTargetSecurity + " options=" + options,
+                    new Throwable());
+        }
+        Objects.requireNonNull(intent);
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return requestCode >= 0 ? am.getResult() : null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            intent.migrateExtraStreamToClipData(who);
+            intent.prepareToLeaveProcess(who);
+            int result = ActivityTaskManager.getService()
+                    .startActivityAsCaller(whoThread, who.getOpPackageName(), intent,
+                            intent.resolveTypeIfNeeded(who.getContentResolver()),
+                            token, target != null ? target.mEmbeddedID : null,
+                            requestCode, 0, null, options,
+                            ignoreTargetSecurity, userId);
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+        return null;
+    }
+
+    /**
+     * Special version!
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void execStartActivityFromAppTask(
+            Context who, IBinder contextThread, IAppTask appTask,
+            Intent intent, Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(TAG, "startActivity: who=" + who + " intent=" + intent
+                    + " options=" + options, new Throwable());
+        }
+        Objects.requireNonNull(intent);
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (isSdkSandboxAllowedToStartActivities()) {
+            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
+        }
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return;
+                    } else if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            intent.migrateExtraStreamToClipData(who);
+            intent.prepareToLeaveProcess(who);
+            int result = appTask.startActivity(whoThread.asBinder(), who.getOpPackageName(),
+                    who.getAttributionTag(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+            notifyStartActivityResult(result, options);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+        return;
+    }
+
+    /*package*/ final void init(ActivityThread thread,
+            Context instrContext, Context appContext, ComponentName component, 
+            IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {
+        mThread = thread;
+        mMessageQueue = mThread.getLooper().myQueue();
+        mInstrContext = instrContext;
+        mAppContext = appContext;
+        mComponent = component;
+        mWatcher = watcher;
+        mUiAutomationConnection = uiAutomationConnection;
+    }
+
+    /**
+     * Only sets the ActivityThread up, keeps everything else null because app is not being
+     * instrumented.
+     */
+    final void basicInit(ActivityThread thread) {
+        mThread = thread;
+    }
+
+    /**
+     * Only sets the Context up, keeps everything else null.
+     *
+     * @hide
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public final void basicInit(Context context) {
+        mInstrContext = context;
+        mAppContext = context;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static void checkStartActivityResult(int res, Object intent) {
+        if (!ActivityManager.isStartResultFatalError(res)) {
+            return;
+        }
+
+        switch (res) {
+            case ActivityManager.START_INTENT_NOT_RESOLVED:
+            case ActivityManager.START_CLASS_NOT_FOUND:
+                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
+                    throw new ActivityNotFoundException(
+                            "Unable to find explicit activity class "
+                            + ((Intent)intent).getComponent().toShortString()
+                            + "; have you declared this activity in your AndroidManifest.xml"
+                            + ", or does your intent not match its declared <intent-filter>?");
+                throw new ActivityNotFoundException(
+                        "No Activity found to handle " + intent);
+            case ActivityManager.START_PERMISSION_DENIED:
+                throw new SecurityException("Not allowed to start activity "
+                        + intent);
+            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+                throw new AndroidRuntimeException(
+                        "FORWARD_RESULT_FLAG used while also requesting a result");
+            case ActivityManager.START_NOT_ACTIVITY:
+                throw new IllegalArgumentException(
+                        "PendingIntent is not an activity");
+            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+                throw new SecurityException(
+                        "Starting under voice control not allowed for: " + intent);
+            case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
+                throw new IllegalStateException(
+                        "Session calling startVoiceActivity does not match active session");
+            case ActivityManager.START_VOICE_HIDDEN_SESSION:
+                throw new IllegalStateException(
+                        "Cannot start voice activity on a hidden session");
+            case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
+                throw new IllegalStateException(
+                        "Session calling startAssistantActivity does not match active session");
+            case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
+                throw new IllegalStateException(
+                        "Cannot start assistant activity on a hidden session");
+            case ActivityManager.START_CANCELED:
+                throw new AndroidRuntimeException("Activity could not be started for "
+                        + intent);
+            default:
+                throw new AndroidRuntimeException("Unknown error code "
+                        + res + " when starting " + intent);
+        }
+    }
+
+    private final void validateNotAppThread() {
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            throw new RuntimeException(
+                "This method can not be called from the main application thread");
+        }
+    }
+
+    /**
+     * Gets the {@link UiAutomation} instance with no flags set.
+     * <p>
+     * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation}
+     * work across application boundaries while the APIs exposed by the instrumentation
+     * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will
+     * not allow you to inject the event in an app different from the instrumentation
+     * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)}
+     * will work regardless of the current application.
+     * </p>
+     * <p>
+     * A typical test case should be using either the {@link UiAutomation} or
+     * {@link Instrumentation} APIs. Using both APIs at the same time is not
+     * a mistake by itself but a client has to be aware of the APIs limitations.
+     * </p>
+     * <p>
+     * Equivalent to {@code getUiAutomation(0)}. If a {@link UiAutomation} exists with different
+     * flags, the flags on that instance will be changed, and then it will be returned.
+     * </p>
+     * <p>
+     * Compatibility mode: This method is infallible for apps targeted for
+     * {@link Build.VERSION_CODES#R} and earlier versions; for apps targeted for later versions, it
+     * will return null if {@link UiAutomation} fails to connect. The caller can check the return
+     * value and retry on error.
+     * </p>
+     *
+     * @return The UI automation instance.
+     *
+     * @see UiAutomation
+     */
+    public UiAutomation getUiAutomation() {
+        return getUiAutomation(0);
+    }
+
+    /**
+     * Gets the {@link UiAutomation} instance with flags set.
+     * <p>
+     * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation}
+     * work across application boundaries while the APIs exposed by the instrumentation
+     * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will
+     * not allow you to inject the event in an app different from the instrumentation
+     * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)}
+     * will work regardless of the current application.
+     * </p>
+     * <p>
+     * A typical test case should be using either the {@link UiAutomation} or
+     * {@link Instrumentation} APIs. Using both APIs at the same time is not
+     * a mistake by itself but a client has to be aware of the APIs limitations.
+     * </p>
+     * <p>
+     * If a {@link UiAutomation} exists with different flags, the flags on that instance will be
+     * changed, and then it will be returned.
+     * </p>
+     * <p>
+     * Compatibility mode: This method is infallible for apps targeted for
+     * {@link Build.VERSION_CODES#R} and earlier versions; for apps targeted for later versions, it
+     * will return null if {@link UiAutomation} fails to connect. The caller can check the return
+     * value and retry on error.
+     * </p>
+     *
+     * @param flags The flags to be passed to the UiAutomation, for example
+     *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES},
+     *        {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY}.
+     *
+     * @return The UI automation instance.
+     *
+     * @see UiAutomation
+     */
+    public UiAutomation getUiAutomation(@UiAutomationFlags int flags) {
+        boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed());
+
+        if (mUiAutomationConnection != null) {
+            if (!mustCreateNewAutomation && (mUiAutomation.getFlags() == flags)) {
+                return mUiAutomation;
+            }
+            if (mustCreateNewAutomation) {
+                mUiAutomation = new UiAutomation(getTargetContext(), mUiAutomationConnection);
+            } else {
+                mUiAutomation.disconnect();
+            }
+            if (getTargetContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R) {
+                mUiAutomation.connect(flags);
+                return mUiAutomation;
+            }
+            final long startUptime = SystemClock.uptimeMillis();
+            try {
+                mUiAutomation.connectWithTimeout(flags, CONNECT_TIMEOUT_MILLIS);
+                return mUiAutomation;
+            } catch (TimeoutException e) {
+                final long waited = SystemClock.uptimeMillis() - startUptime;
+                Log.e(TAG, "Unable to connect to UiAutomation. Waited for " + waited + " ms", e);
+                mUiAutomation.destroy();
+                mUiAutomation = null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Takes control of the execution of messages on the specified looper until
+     * {@link TestLooperManager#release} is called.
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public TestLooperManager acquireLooperManager(Looper looper) {
+        checkInstrumenting("acquireLooperManager");
+        return new TestLooperManager(looper);
+    }
+
+    private final class InstrumentationThread extends Thread {
+        public InstrumentationThread(String name) {
+            super(name);
+        }
+        public void run() {
+            try {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
+            } catch (RuntimeException e) {
+                Log.w(TAG, "Exception setting priority of instrumentation thread "
+                        + Process.myTid(), e);
+            }
+            if (mAutomaticPerformanceSnapshots) {
+                startPerformanceSnapshot();
+            }
+            onStart();
+        }
+    }
+
+    private static final class EmptyRunnable implements Runnable {
+        public void run() {
+        }
+    }
+
+    private static final class SyncRunnable implements Runnable {
+        private final Runnable mTarget;
+        private boolean mComplete;
+
+        public SyncRunnable(Runnable target) {
+            mTarget = target;
+        }
+
+        public void run() {
+            mTarget.run();
+            synchronized (this) {
+                mComplete = true;
+                notifyAll();
+            }
+        }
+
+        public void waitForComplete() {
+            synchronized (this) {
+                while (!mComplete) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+    }
+
+    private static final class ActivityWaiter {
+        public final Intent intent;
+        public Activity activity;
+
+        public ActivityWaiter(Intent _intent) {
+            intent = _intent;
+        }
+    }
+
+    private final class ActivityGoing implements MessageQueue.IdleHandler {
+        private final ActivityWaiter mWaiter;
+
+        public ActivityGoing(ActivityWaiter waiter) {
+            mWaiter = waiter;
+        }
+
+        public final boolean queueIdle() {
+            synchronized (mSync) {
+                mWaitingActivities.remove(mWaiter);
+                mSync.notifyAll();
+            }
+            return false;
+        }
+    }
+
+    private static final class Idler implements MessageQueue.IdleHandler {
+        private final Runnable mCallback;
+        private boolean mIdle;
+
+        public Idler(Runnable callback) {
+            mCallback = callback;
+            mIdle = false;
+        }
+
+        public final boolean queueIdle() {
+            if (mCallback != null) {
+                mCallback.run();
+            }
+            synchronized (this) {
+                mIdle = true;
+                notifyAll();
+            }
+            return false;
+        }
+
+        public void waitForIdle() {
+            synchronized (this) {
+                while (!mIdle) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+    }
+}