Allow for declaring a test as a monkey type test

This enables users to write tests that act like monkey and
at the same time allowing apps to react as if an android
monkey test is running.

bug: 8588857
Change-Id: I43ca663c2aac43658f5bb39eabe2d904d1e9009a
diff --git a/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/RunTestCommand.java b/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/RunTestCommand.java
index 7320207..65611ab 100644
--- a/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/RunTestCommand.java
+++ b/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/RunTestCommand.java
@@ -51,9 +51,10 @@
     private static final int ARG_FAIL_RUNNER = -4;
     private static final int ARG_FAIL_UNSUPPORTED = -99;
 
-    private Bundle mParams = new Bundle();
-    private List<String> mTestClasses = new ArrayList<String>();
+    private final Bundle mParams = new Bundle();
+    private final List<String> mTestClasses = new ArrayList<String>();
     private boolean mDebug;
+    private boolean mMonkey = false;
     private String mRunnerClassName;
     private UiAutomatorTestRunner mRunner;
 
@@ -87,7 +88,7 @@
                 System.exit(ARG_FAIL_NO_CLASS);
             }
         }
-        getRunner().run(mTestClasses, mParams, mDebug);
+        getRunner().run(mTestClasses, mParams, mDebug, mMonkey);
     }
 
     private int parseArgs(String[] args) {
@@ -124,6 +125,8 @@
                 } else {
                     return ARG_FAIL_INCOMPLETE_C;
                 }
+            } else if (args[i].equals("--monkey")) {
+                mMonkey = true;
             } else if (args[i].equals("-s")) {
                 mParams.putString(OUTPUT_FORMAT_KEY, OUTPUT_SIMPLE);
             } else {
diff --git a/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index eaaef14..5c48672 100644
--- a/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -56,6 +56,15 @@
      */
     void initialize(Bundle params) {
         mParams = params;
+
+        // check if this is a monkey test mode
+        String monkeyVal = mParams.getString("monkey");
+        if (monkeyVal != null) {
+            // only if the monkey key is specified, we alter the state of monkey
+            // else we should leave things as they are.
+            getInstrumentation().getUiAutomation().setRunAsMonkey(Boolean.valueOf(monkeyVal));
+        }
+
         UiDevice.getInstance().initialize(new InstrumentationUiAutomatorBridge(
                 getInstrumentation().getContext(),
                 getInstrumentation().getUiAutomation()));
diff --git a/uiautomator/library/core-src/com/android/uiautomator/core/UiAutomatorBridge.java b/uiautomator/library/core-src/com/android/uiautomator/core/UiAutomatorBridge.java
index 3b9887d..59dd450 100644
--- a/uiautomator/library/core-src/com/android/uiautomator/core/UiAutomatorBridge.java
+++ b/uiautomator/library/core-src/com/android/uiautomator/core/UiAutomatorBridge.java
@@ -5,6 +5,7 @@
 import android.app.UiAutomation.AccessibilityEventFilter;
 import android.app.UiAutomation.OnAccessibilityEventListener;
 import android.graphics.Bitmap;
+import android.util.AndroidException;
 import android.util.Log;
 import android.view.Display;
 import android.view.InputEvent;
@@ -58,6 +59,23 @@
         return mQueryController;
     }
 
+    /**
+     * Enable or disable monkey test run mode.
+     *
+     * Setting test as "monkey" indicates to some applications that a test framework is
+     * running and specifically as a "monkey" type. Such applications may choose
+     * then not to perform actions that do submits so to avoid allowing monkey tests
+     * from doing harm or performing annoying actions such as dialing 911 or posting messages
+     * to public forums, etc.
+     *
+     * @param b True to set as monkey test. False to set as regular functional test (default).
+     * @throws AndroidException
+     */
+
+    public void setRunAsMonkey(boolean b) throws AndroidException {
+        mUiAutomation.setRunAsMonkey(b);
+    }
+
     public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) {
         mUiAutomation.setOnAccessibilityEventListener(listener);
     }
diff --git a/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
index 24c7c98..e06024f 100644
--- a/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
+++ b/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
@@ -1,6 +1,7 @@
 package com.android.uiautomator.core;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManager;
 import android.app.UiAutomation;
 import android.app.UiAutomationConnection;
 import android.os.HandlerThread;
@@ -26,6 +27,21 @@
         mUiAutomation.connect();
     }
 
+    /**
+     * Enable or disable monkey test mode.
+     *
+     * Setting test as "monkey" indicates to some applications that a test framework is
+     * running as a "monkey" type. Such applications may choose not to perform actions that
+     * do submits so to avoid allowing monkey tests from doing harm or performing annoying
+     * actions such as dialing 911 or posting messages to public forums, etc.
+     *
+     * @param isSet True to set as monkey test. False to set as regular functional test (default).
+     * @see {@link ActivityManager#isUserAMonkey()}
+     */
+    public void setRunAsMonkey(boolean isSet) {
+        mUiAutomation.setRunAsMonkey(isSet);
+    }
+
     public void disconnect() {
         if (!mHandlerThread.isAlive()) {
             throw new IllegalStateException("Already disconnected!");
diff --git a/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java b/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
index 1b751da..0fdc984 100644
--- a/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
+++ b/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
@@ -33,6 +33,13 @@
 import com.android.uiautomator.core.UiAutomationShellWrapper;
 import com.android.uiautomator.core.UiDevice;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.Test;
 import junit.framework.TestCase;
@@ -41,13 +48,6 @@
 import junit.runner.BaseTestRunner;
 import junit.textui.ResultPrinter;
 
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * @hide
  */
@@ -60,21 +60,22 @@
     private static final String HANDLER_THREAD_NAME = "UiAutomatorHandlerThread";
 
     private boolean mDebug;
+    private boolean mMonkey;
     private Bundle mParams = null;
     private UiDevice mUiDevice;
     private List<String> mTestClasses = null;
-    private FakeInstrumentationWatcher mWatcher = new FakeInstrumentationWatcher();
-    private IAutomationSupport mAutomationSupport = new IAutomationSupport() {
+    private final FakeInstrumentationWatcher mWatcher = new FakeInstrumentationWatcher();
+    private final IAutomationSupport mAutomationSupport = new IAutomationSupport() {
         @Override
         public void sendStatus(int resultCode, Bundle status) {
             mWatcher.instrumentationStatus(null, resultCode, status);
         }
     };
-    private List<TestListener> mTestListeners = new ArrayList<TestListener>();
+    private final List<TestListener> mTestListeners = new ArrayList<TestListener>();
 
     private HandlerThread mHandlerThread;
 
-    public void run(List<String> testClasses, Bundle params, boolean debug) {
+    public void run(List<String> testClasses, Bundle params, boolean debug, boolean monkey) {
         Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
             @Override
             public void uncaughtException(Thread thread, Throwable ex) {
@@ -91,6 +92,7 @@
         mTestClasses = testClasses;
         mParams = params;
         mDebug = debug;
+        mMonkey = monkey;
         start();
         System.exit(EXIT_OK);
     }
@@ -114,6 +116,7 @@
         mHandlerThread.start();
         UiAutomationShellWrapper automationWrapper = new UiAutomationShellWrapper();
         automationWrapper.connect();
+        automationWrapper.setRunAsMonkey(mMonkey);
         mUiDevice = UiDevice.getInstance();
         mUiDevice.initialize(new ShellUiAutomatorBridge(automationWrapper.getUiAutomation()));
         List<TestCase> testCases = collector.getTestCases();
@@ -163,6 +166,7 @@
             long runTime = SystemClock.uptimeMillis() - startTime;
             resultPrinter.print(testRunResult, runTime, testRunOutput);
             automationWrapper.disconnect();
+            automationWrapper.setRunAsMonkey(false);
             mHandlerThread.quit();
         }
     }
@@ -170,7 +174,7 @@
     // copy & pasted from com.android.commands.am.Am
     private class FakeInstrumentationWatcher implements IInstrumentationWatcher {
 
-        private boolean mRawMode = true;
+        private final boolean mRawMode = true;
 
         @Override
         public IBinder asBinder() {
@@ -250,9 +254,9 @@
         int mTestResultCode = 0;
         String mTestClass = null;
 
-        private SimpleResultPrinter mPrinter;
-        private ByteArrayOutputStream mStream;
-        private PrintStream mWriter;
+        private final SimpleResultPrinter mPrinter;
+        private final ByteArrayOutputStream mStream;
+        private final PrintStream mWriter;
 
         public WatcherResultPrinter(int numTests) {
             mResultTemplate = new Bundle();
@@ -340,6 +344,7 @@
             mPrinter.endTest(test);
         }
 
+        @Override
         public void print(TestResult result, long runTime, Bundle testOutput) {
             mPrinter.print(result, runTime, testOutput);
             testOutput.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
@@ -350,6 +355,7 @@
             mAutomationSupport.sendStatus(Activity.RESULT_OK, testOutput);
         }
 
+        @Override
         public void printUnexpectedError(Throwable t) {
             mWriter.println(String.format("Test run aborted due to unexpected exception: %s",
                     t.getMessage()));
@@ -362,12 +368,13 @@
      * used when default UiAutomator output is too verbose.
      */
     private class SimpleResultPrinter extends ResultPrinter implements ResultReporter {
-        private boolean mFullOutput;
+        private final boolean mFullOutput;
         public SimpleResultPrinter(PrintStream writer, boolean fullOutput) {
             super(writer);
             mFullOutput = fullOutput;
         }
 
+        @Override
         public void print(TestResult result, long runTime, Bundle testOutput) {
             printHeader(runTime);
             if (mFullOutput) {
@@ -377,6 +384,7 @@
             printFooter(result);
         }
 
+        @Override
         public void printUnexpectedError(Throwable t) {
             if (mFullOutput) {
                 getWriter().printf("Test run aborted due to unexpected exeption: %s",