am 720f95b6: am b64737f2: (-s ours) am b00e7012: DO NOT MERGE: Add proper @hide\'s for mismatched released APIs

* commit '720f95b64533b88ca6f53ec89c8b7a9fe936af42':
  DO NOT MERGE: Add proper @hide's for mismatched released APIs
diff --git a/uiautomator/library/Android.mk b/uiautomator/library/Android.mk
index a9ad065..d4b745a 100644
--- a/uiautomator/library/Android.mk
+++ b/uiautomator/library/Android.mk
@@ -19,7 +19,7 @@
 uiautomator.core_src_files := $(call all-java-files-under, src)
 uiautomator.core_java_libraries := android.test.runner core-junit
 
-INTERNAL_UIAUTOMATOR_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/uiautomator_api.txt
+uiautomator_internal_api_file := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/uiautomator_api.txt
 
 ###############################################
 include $(CLEAR_VARS)
@@ -40,7 +40,7 @@
 LOCAL_DROIDDOC_OPTIONS:= \
     -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_uiautomator_intermediates/src \
     -stubpackages com.android.uiautomator.core:com.android.uiautomator.testrunner \
-    -api $(INTERNAL_UIAUTOMATOR_API_FILE) \
+    -api $(uiautomator_internal_api_file) \
     -nodocs
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
@@ -49,7 +49,8 @@
 LOCAL_MODULE := uiautomator-stubs
 
 include $(BUILD_DROIDDOC)
-uiautomator-stubs-stamp := $(full_target)
+uiautomator_stubs_stamp := $(full_target)
+$(uiautomator_internal_api_file) : $(full_target)
 
 ###############################################
 # Build the stub source files into a jar.
@@ -59,10 +60,63 @@
 LOCAL_SOURCE_FILES_ALL_GENERATED := true
 include $(BUILD_STATIC_JAVA_LIBRARY)
 # Make sure to run droiddoc first to generate the stub source files.
-$(full_classes_compiled_jar) : $(uiautomator-stubs-stamp)
+$(full_classes_compiled_jar) : $(uiautomator_stubs_stamp)
+uiautomator_stubs_jar := $(full_classes_compiled_jar)
+
+###############################################
+# API check
+# Please refer to build/core/tasks/apicheck.mk.
+uiautomator_api_dir := frameworks/testing/uiautomator/api
+last_released_sdk_version := $(lastword $(call numerically_sort, \
+    $(filter-out current, \
+        $(patsubst $(uiautomator_api_dir)/%.txt,%, $(wildcard $(uiautomator_api_dir)/*.txt)) \
+    )))
+
+checkapi_last_error_level_flags := \
+    -hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 \
+    -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+    -error 16 -error 17 -error 18
+
+# Check that the API we're building hasn't broken the last-released SDK version.
+$(eval $(call check-api, \
+    uiautomator-checkapi-last, \
+    $(uiautomator_api_dir)/$(last_released_sdk_version).txt, \
+    $(uiautomator_internal_api_file), \
+    $(checkapi_last_error_level_flags), \
+    cat $(LOCAL_PATH)/apicheck_msg_last.txt, \
+    $(uiautomator_stubs_jar), \
+    $(uiautomator_stubs_stamp)))
+
+checkapi_current_error_level_flags := \
+    -error 2 -error 3 -error 4 -error 5 -error 6 \
+    -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+    -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+    -error 25
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+    uiautomator-checkapi-current, \
+    $(uiautomator_api_dir)/current.txt, \
+    $(uiautomator_internal_api_file), \
+    $(checkapi_current_error_level_flags), \
+    cat $(LOCAL_PATH)/apicheck_msg_current.txt, \
+    $(uiautomator_stubs_jar), \
+    $(uiautomator_stubs_stamp)))
+
+.PHONY: update-uiautomator-api
+update-uiautomator-api: PRIVATE_API_DIR := $(uiautomator_api_dir)
+update-uiautomator-api: $(uiautomator_internal_api_file) | $(ACP)
+	@echo Copying uiautomator current.txt
+	$(hide) $(ACP) $< $(PRIVATE_API_DIR)/current.txt
 
 ###############################################
 # clean up temp vars
 uiautomator.core_src_files :=
 uiautomator.core_java_libraries :=
-uiautomator-stubs-stamp :=
+uiautomator_stubs_stamp :=
+uiautomator_internal_api_file :=
+uiautomator_stubs_jar :=
+uiautomator_api_dir :=
+checkapi_last_error_level_flags :=
+checkapi_current_error_level_flags :=
diff --git a/uiautomator/library/apicheck_msg_current.txt b/uiautomator/library/apicheck_msg_current.txt
new file mode 100644
index 0000000..989248d
--- /dev/null
+++ b/uiautomator/library/apicheck_msg_current.txt
@@ -0,0 +1,17 @@
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+      errors above.
+
+   2) You can update current.txt by executing the following command:
+         make update-uiautomator-api
+
+      To submit the revised current.txt to the main Android repository,
+      you will need approval.
+******************************
+
+
+
diff --git a/uiautomator/library/apicheck_msg_last.txt b/uiautomator/library/apicheck_msg_last.txt
new file mode 100644
index 0000000..2993157
--- /dev/null
+++ b/uiautomator/library/apicheck_msg_last.txt
@@ -0,0 +1,7 @@
+
+******************************
+You have tried to change the API from what has been previously released in
+an SDK.  Please fix the errors listed above.
+******************************
+
+
diff --git a/uiautomator/library/src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/uiautomator/library/src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 65fcd28..10878e3 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -63,7 +63,8 @@
     /**
      * Using {@link AccessibilityNodeInfo} this method will walk the layout hierarchy
      * and generates an xml dump to the location specified by <code>dumpFile</code>
-     * @param info
+     * @param root The root accessibility node.
+     * @param dumpFile The file to dump to.
      */
     public static void dumpWindowToFile(AccessibilityNodeInfo root, File dumpFile) {
         if (root == null) {
@@ -137,11 +138,11 @@
      * only reduce noise from standard layout classes that may be falsely
      * configured to accept clicks and are also enabled.
      *
-     * @param n
-     * @return
+     * @param node
+     * @return true if node is excluded.
      */
-    private static boolean nafExcludedClass(AccessibilityNodeInfo n) {
-        String className = safeCharSeqToString(n.getClassName());
+    private static boolean nafExcludedClass(AccessibilityNodeInfo node) {
+        String className = safeCharSeqToString(node.getClassName());
         for(String excludedClassName : NAF_EXCLUDED_CLASSES) {
             if(className.endsWith(excludedClassName))
                 return true;
@@ -183,7 +184,7 @@
      * considered by this dumper as acceptable for accessibility.
      *
      * @param node
-     * @return
+     * @return false if node fails the check.
      */
     private static boolean childNafCheck(AccessibilityNodeInfo node) {
         int childCount = node.getChildCount();
diff --git a/uiautomator/library/src/com/android/uiautomator/core/InteractionController.java b/uiautomator/library/src/com/android/uiautomator/core/InteractionController.java
index 9a4df13..0fc6466 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/InteractionController.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/InteractionController.java
@@ -162,7 +162,7 @@
      * @param timeout
      * @param waitForAll boolean to indicate whether to wait for any or all events
      * @param eventTypes mask
-     * @return
+     * @return true if events are received, else false if timeout.
      */
     public boolean clickAndWaitForEvents(final int x, final int y, long timeout,
             boolean waitForAll, int eventTypes) {
@@ -180,7 +180,7 @@
                 }
             }
         };
-        return runAndWaitForEvents(command, timeout, waitForAll, eventTypes);
+        return runAndWaitForEvents(command, timeout, waitForAll, eventTypes) != null;
     }
 
     /**
@@ -188,9 +188,9 @@
      * @param command is a Runnable to execute before waiting for the event.
      * @param timeout
      * @param eventType
-     * @return
+     * @return The AccessibilityEvent if one is received, otherwise null.
      */
-    private boolean runAndWaitForEvent(Runnable command, long timeout, int eventType) {
+    private AccessibilityEvent runAndWaitForEvent(Runnable command, long timeout, int eventType) {
         return runAndWaitForEvents(command, timeout, false, eventType);
     }
 
@@ -202,11 +202,10 @@
      * @param timeout
      * @param waitForAll boolean to indicate whether to wait for any or all events
      * @param eventTypesMask
-     * @return
+     * @return The AccessibilityEvent if one is received, otherwise null.
      */
-    private boolean runAndWaitForEvents(Runnable command, long timeout, final boolean waitForAll,
-            final int eventTypesMask) {
-
+    private AccessibilityEvent runAndWaitForEvents(Runnable command, long timeout,
+            final boolean waitForAll, final int eventTypesMask) {
         if (eventTypesMask == 0)
             throw new IllegalArgumentException("events mask cannot be zero");
 
@@ -237,17 +236,18 @@
             }
         }
 
+        AccessibilityEvent event = null;
         try {
-            mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command,
+            event = mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command,
                     new EventPredicate(eventTypesMask), timeout);
         } catch (TimeoutException e) {
             Log.w(LOG_TAG, "runAndwaitForEvent timedout waiting for events: " + eventTypesMask);
-            return false;
+            return null;
         } catch (Exception e) {
             Log.e(LOG_TAG, "exception from executeCommandAndWaitForAccessibilityEvent", e);
-            return false;
+            return null;
         }
-        return true;
+        return event;
     }
 
     /**
@@ -255,14 +255,14 @@
      *
      * Most key presses will cause some UI change to occur. If the device is busy, this will
      * block until the device begins to process the key press at which point the call returns
-     * and normal wait for idle processing may begin. If no evens are detected for the
+     * and normal wait for idle processing may begin. If no events are detected for the
      * timeout period specified, the call will return anyway with false.
      *
      * @param keyCode
      * @param metaState
      * @param eventType
      * @param timeout
-     * @return
+     * @return true if events is received, otherwise false.
      */
     public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,
             final int eventType, long timeout) {
@@ -283,7 +283,7 @@
             }
         };
 
-        return runAndWaitForEvent(command, timeout, eventType);
+        return runAndWaitForEvent(command, timeout, eventType) != null;
     }
 
     /**
@@ -291,7 +291,7 @@
      * that require stressing the target.
      * @param x
      * @param y
-     * @return
+     * @return true if the click executed successfully
      */
     public boolean click(int x, int y) {
         Log.d(LOG_TAG, "click (" + x + ", " + y + ")");
@@ -376,8 +376,8 @@
      * @param downY
      * @param upX
      * @param upY
-     * @param duration
-     * @return true if the swipe and scrolling have been successfully completed.
+     * @param steps
+     * @return true if we are not at the beginning or end of the scrollable view.
      */
     public boolean scrollSwipe(final int downX, final int downY, final int upX, final int upY,
             final int steps) {
@@ -391,8 +391,34 @@
             }
         };
 
-        return runAndWaitForEvent(command, DEFAULT_SCROLL_EVENT_TIMEOUT_MILLIS,
-                AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        AccessibilityEvent event = runAndWaitForEvent(command,
+                DEFAULT_SCROLL_EVENT_TIMEOUT_MILLIS, AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        if (event == null) {
+            return false;
+        }
+        // AdapterViews have indices we can use to check for the beginning.
+        if (event.getFromIndex() != -1 && event.getToIndex() != -1 && event.getItemCount() != -1) {
+            boolean foundEnd = event.getFromIndex() == 0 ||
+                    (event.getItemCount() - 1) == event.getToIndex();
+            Log.d(LOG_TAG, "scrollSwipe reached scroll end: " + foundEnd);
+            return !foundEnd;
+        } else if (event.getScrollX() != -1 && event.getScrollY() != -1) {
+            // Determine if we are scrolling vertically or horizontally.
+            if (downX == upX) {
+                // Vertical
+                boolean foundEnd = event.getScrollY() == 0 ||
+                        event.getScrollY() == event.getMaxScrollY();
+                Log.d(LOG_TAG, "Vertical scrollSwipe reached scroll end: " + foundEnd);
+                return !foundEnd;
+            } else if (downY == upY) {
+                // Horizontal
+                boolean foundEnd = event.getScrollX() == 0 ||
+                        event.getScrollX() == event.getMaxScrollX();
+                Log.d(LOG_TAG, "Horizontal scrollSwipe reached scroll end: " + foundEnd);
+                return !foundEnd;
+            }
+        }
+        return event != null;
     }
 
     /**
@@ -401,8 +427,8 @@
      * @param downY
      * @param upX
      * @param upY
-     * @param duration
-     * @return
+     * @param steps
+     * @return true if the swipe executed successfully
      */
     public boolean swipe(int downX, int downY, int upX, int upY, int steps) {
         boolean ret = false;
diff --git a/uiautomator/library/src/com/android/uiautomator/core/QueryController.java b/uiautomator/library/src/com/android/uiautomator/core/QueryController.java
index d2938ac..f14b017 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/QueryController.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/QueryController.java
@@ -23,10 +23,8 @@
 import com.android.uiautomator.core.UiAutomatorBridge.AccessibilityEventListener;
 
 /**
- * The QuertController main purpose is to translate a {@link UiSelector} selectors to
- * {@link AccessibilityNodeInfo}. This is all this controller does. It is typically
- * created in conjunction with a {@link InteractionController} by {@link UiAutomationContext}
- * which owns both. {@link UiAutomationContext} is used by {@link UiBase} classes.
+ * The QueryController main purpose is to translate a {@link UiSelector} selectors to
+ * {@link AccessibilityNodeInfo}. This is all this controller does.
  */
 class QueryController {
 
@@ -88,7 +86,6 @@
      * Returns the last text selection reported by accessibility
      * event TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY. One way to cause
      * this event is using a DPad arrows to focus on UI elements.
-     * @return
      */
     public String getLastTraversedText() {
         mUiAutomatorBridge.waitForIdle();
@@ -115,7 +112,7 @@
         mPatternCounter = 0;
         mPatternIndexer = 0;
         mLogIndent = 0;
-        mLogParentIndent = 0;;
+        mLogParentIndent = 0;
     }
 
     /**
@@ -134,7 +131,7 @@
     /**
      * Main search method for translating By selectors to AccessibilityInfoNodes
      * @param selector
-     * @return
+     * @return AccessibilityNodeInfo
      */
     public AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector) {
         return findAccessibilityNodeInfo(selector, false);
@@ -212,7 +209,7 @@
      * @param selector
      * @param fromNode
      * @param isCounting
-     * @return
+     * @return AccessibilityNodeInfo
      */
     private AccessibilityNodeInfo translateCompoundSelector(UiSelector selector,
             AccessibilityNodeInfo fromNode, boolean isCounting) {
@@ -282,7 +279,6 @@
      * <p/>
      * @param selector
      * @param fromNode
-     * @param index
      * @return AccessibilityNodeInfo if found else null
      */
     private AccessibilityNodeInfo translateReqularSelector(UiSelector selector,
@@ -369,7 +365,7 @@
      * until the end of the tree.
      * @param subSelector
      * @param fromNode
-     * @param originalPattern
+     * @param isCounting
      * @return null of node is not found or if counting mode is true.
      * See {@link #translateCompoundSelector(UiSelector, AccessibilityNodeInfo, boolean)}
      */
diff --git a/uiautomator/library/src/com/android/uiautomator/core/Tracer.java b/uiautomator/library/src/com/android/uiautomator/core/Tracer.java
new file mode 100644
index 0000000..d574fc0
--- /dev/null
+++ b/uiautomator/library/src/com/android/uiautomator/core/Tracer.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2012 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 com.android.uiautomator.core;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Class that creates traces of the calls to the UiAutomator API and outputs the
+ * traces either to logcat or a logfile. Each public method in the UiAutomator
+ * that needs to be traced should include a call to Tracer.trace in the
+ * beginning. Tracing is turned off by defualt and needs to be enabled
+ * explicitly.
+ * @hide
+ */
+public class Tracer {
+    private static final String UNKNOWN_METHOD_STRING = "(unknown method)";
+    private static final String UIAUTOMATOR_PACKAGE = "com.android.uiautomator.core";
+    private static final int CALLER_LOCATION = 6;
+    private static final int METHOD_TO_TRACE_LOCATION = 5;
+    private static final int MIN_STACK_TRACE_LENGTH = 7;
+
+    /**
+     * Enum that determines where the trace output goes. It can go to either
+     * logcat, log file or both.
+     */
+    public enum Mode {
+        NONE,
+        FILE,
+        LOGCAT,
+        ALL
+    }
+
+    private interface TracerSink {
+        public void log(String message);
+
+        public void close();
+    }
+
+    private class FileSink implements TracerSink {
+        private PrintWriter mOut;
+        private SimpleDateFormat mDateFormat;
+
+        public FileSink(File file) throws FileNotFoundException {
+            mOut = new PrintWriter(file);
+            mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
+        }
+
+        public void log(String message) {
+            mOut.printf("%s %s\n", mDateFormat.format(new Date()), message);
+        }
+
+        public void close() {
+            mOut.close();
+        }
+    }
+
+    private class LogcatSink implements TracerSink {
+
+        private static final String LOGCAT_TAG = "UiAutomatorTrace";
+
+        public void log(String message) {
+            Log.i(LOGCAT_TAG, message);
+        }
+
+        public void close() {
+            // nothing is needed
+        }
+    }
+
+    private Mode mCurrentMode = Mode.NONE;
+    private List<TracerSink> mSinks = new ArrayList<TracerSink>();
+    private File mOutputFile;
+
+    private static Tracer mInstance = null;
+
+    /**
+     * Returns a reference to an instance of the tracer. Useful to set the
+     * parameters before the trace is collected.
+     *
+     * @return
+     */
+    public static Tracer getInstance() {
+        if (mInstance == null) {
+            mInstance = new Tracer();
+        }
+        return mInstance;
+    }
+
+    /**
+     * Sets where the trace output will go. Can be either be logcat or a file or
+     * both. Setting this to NONE will turn off tracing.
+     *
+     * @param mode
+     */
+    public void setOutputMode(Mode mode) {
+        closeSinks();
+        mCurrentMode = mode;
+        try {
+            switch (mode) {
+                case FILE:
+                    if (mOutputFile == null) {
+                        throw new IllegalArgumentException("Please provide a filename before " +
+                                "attempting write trace to a file");
+                    }
+                    mSinks.add(new FileSink(mOutputFile));
+                    break;
+                case LOGCAT:
+                    mSinks.add(new LogcatSink());
+                    break;
+                case ALL:
+                    mSinks.add(new LogcatSink());
+                    if (mOutputFile == null) {
+                        throw new IllegalArgumentException("Please provide a filename before " +
+                                "attempting write trace to a file");
+                    }
+                    mSinks.add(new FileSink(mOutputFile));
+                    break;
+                default:
+                    break;
+            }
+        } catch (FileNotFoundException e) {
+            Log.w("Tracer", "Could not open log file: " + e.getMessage());
+        }
+    }
+
+    private void closeSinks() {
+        for (TracerSink sink : mSinks) {
+            sink.close();
+        }
+        mSinks.clear();
+    }
+
+    /**
+     * Sets the name of the log file where tracing output will be written if the
+     * tracer is set to write to a file.
+     *
+     * @param filename name of the log file.
+     */
+    public void setOutputFilename(String filename) {
+        mOutputFile = new File(filename);
+    }
+
+    private void doTrace(Object[] arguments) {
+        if (mCurrentMode == Mode.NONE) {
+            return;
+        }
+
+        String caller = getCaller();
+        if (caller == null) {
+            return;
+        }
+
+        log(String.format("%s (%s)", caller, join(", ", arguments)));
+    }
+
+    private void log(String message) {
+        for (TracerSink sink : mSinks) {
+            sink.log(message);
+        }
+    }
+
+    /**
+     * Queries whether the tracing is enabled.
+     * @return true if tracing is enabled, false otherwise.
+     */
+    public boolean isTracingEnabled() {
+        return mCurrentMode != Mode.NONE;
+    }
+
+    /**
+     * Public methods in the UiAutomator should call this function to generate a
+     * trace. The trace will include the method thats is being called, it's
+     * arguments and where in the user's code the method is called from. If a
+     * public method is called internally from UIAutomator then this will not
+     * output a trace entry. Only calls from outise the UiAutomator package will
+     * produce output.
+     *
+     * Special note about array arguments. You can safely pass arrays of reference types
+     * to this function. Like String[] or Integer[]. The trace function will print their
+     * contents by calling toString() on each of the elements. This will not work for
+     * array of primitive types like int[] or float[]. Before passing them to this function
+     * convert them to arrays of reference types manually. Example: convert int[] to Integer[].
+     *
+     * @param arguments arguments of the method being traced.
+     */
+    public static void trace(Object... arguments) {
+        Tracer.getInstance().doTrace(arguments);
+    }
+
+    private static String join(String separator, Object[] strings) {
+        if (strings.length == 0)
+            return "";
+
+        StringBuilder builder = new StringBuilder(objectToString(strings[0]));
+        for (int i = 1; i < strings.length; i++) {
+            builder.append(separator);
+            builder.append(objectToString(strings[i]));
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Special toString method to handle arrays. If the argument is a normal object then this will
+     * return normal output of obj.toString(). If the argument is an array this will return a
+     * string representation of the elements of the array.
+     *
+     * This method will not work for arrays of primitive types. Arrays of primitive types are
+     * expected to be converted manually by the caller. If the array is not converter then
+     * this function will only output "[...]" instead of the contents of the array.
+     *
+     * @param obj object to convert to a string
+     * @return String representation of the object.
+     */
+    private static String objectToString(Object obj) {
+        if (obj.getClass().isArray()) {
+            if (obj instanceof Object[]) {
+                return Arrays.deepToString((Object[])obj);
+            } else {
+                return "[...]";
+            }
+        } else {
+            return obj.toString();
+        }
+    }
+
+    /**
+     * This method outputs which UiAutomator method was called and where in the
+     * user code it was called from. If it can't deside which method is called
+     * it will output "(unknown method)". If the method was called from inside
+     * the UiAutomator then it returns null.
+     *
+     * @return name of the method called and where it was called from. Null if
+     *         method was called from inside UiAutomator.
+     */
+    private static String getCaller() {
+        StackTraceElement stackTrace[] = Thread.currentThread().getStackTrace();
+        if (stackTrace.length < MIN_STACK_TRACE_LENGTH) {
+            return UNKNOWN_METHOD_STRING;
+        }
+
+        StackTraceElement caller = stackTrace[METHOD_TO_TRACE_LOCATION];
+        StackTraceElement previousCaller = stackTrace[CALLER_LOCATION];
+
+        if (previousCaller.getClassName().startsWith(UIAUTOMATOR_PACKAGE)) {
+            return null;
+        }
+
+        int indexOfDot = caller.getClassName().lastIndexOf('.');
+        if (indexOfDot < 0) {
+            indexOfDot = 0;
+        }
+
+        if (indexOfDot + 1 >= caller.getClassName().length()) {
+            return UNKNOWN_METHOD_STRING;
+        }
+
+        String shortClassName = caller.getClassName().substring(indexOfDot + 1);
+        return String.format("%s.%s from %s() at %s:%d", shortClassName, caller.getMethodName(),
+                previousCaller.getMethodName(), previousCaller.getFileName(),
+                previousCaller.getLineNumber());
+    }
+}
diff --git a/uiautomator/library/src/com/android/uiautomator/core/UiCollection.java b/uiautomator/library/src/com/android/uiautomator/core/UiCollection.java
index 20b6d9a..e15beb2 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/UiCollection.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/UiCollection.java
@@ -49,6 +49,7 @@
      */
     public UiObject getChildByDescription(UiSelector childPattern, String text)
             throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, text);
         if (text != null) {
             int count = getChildCount(childPattern);
             for (int x = 0; x < count; x++) {
@@ -82,6 +83,7 @@
      */
     public UiObject getChildByInstance(UiSelector childPattern, int instance)
             throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, instance);
         UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
                 UiSelector.patternBuilder(childPattern).instance(instance));
         return new UiObject(patternSelector);
@@ -105,7 +107,7 @@
      */
     public UiObject getChildByText(UiSelector childPattern, String text)
             throws UiObjectNotFoundException {
-
+        Tracer.trace(childPattern, text);
         if (text != null) {
             int count = getChildCount(childPattern);
             for (int x = 0; x < count; x++) {
@@ -135,6 +137,7 @@
      * @since API Level 16
      */
     public int getChildCount(UiSelector childPattern) {
+        Tracer.trace(childPattern);
         UiSelector patternSelector =
                 UiSelector.patternBuilder(getSelector(), UiSelector.patternBuilder(childPattern));
         return getQueryController().getPatternCount(patternSelector);
diff --git a/uiautomator/library/src/com/android/uiautomator/core/UiDevice.java b/uiautomator/library/src/com/android/uiautomator/core/UiDevice.java
index 09d8af3..b668bea 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/UiDevice.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/UiDevice.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
@@ -112,6 +113,7 @@
      * @hide
      */
     public Point getDisplaySizeDp() {
+        Tracer.trace();
         Display display = getDefaultDisplay();
         Point p = new Point();
         display.getSize(p);
@@ -134,6 +136,7 @@
      * @since API Level 17
      */
     public String getProductName() {
+        Tracer.trace();
         return Build.PRODUCT;
     }
 
@@ -153,6 +156,7 @@
      * @since API Level 16
      */
     public String getLastTraversedText() {
+        Tracer.trace();
         return mUiAutomationBridge.getQueryController().getLastTraversedText();
     }
 
@@ -162,6 +166,7 @@
      * @since API Level 16
      */
     public void clearLastTraversedText() {
+        Tracer.trace();
         mUiAutomationBridge.getQueryController().clearLastTraversedText();
     }
 
@@ -171,6 +176,7 @@
      * @since API Level 16
      */
     public boolean pressMenu() {
+        Tracer.trace();
         waitForIdle();
         return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
                 KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -183,6 +189,7 @@
      * @since API Level 16
      */
     public boolean pressBack() {
+        Tracer.trace();
         waitForIdle();
         return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
                 KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -195,6 +202,7 @@
      * @since API Level 16
      */
     public boolean pressHome() {
+        Tracer.trace();
         waitForIdle();
         return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
                 KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
@@ -207,6 +215,7 @@
      * @since API Level 16
      */
     public boolean pressSearch() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
     }
 
@@ -216,6 +225,7 @@
      * @since API Level 16
      */
     public boolean pressDPadCenter() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
     }
 
@@ -225,6 +235,7 @@
      * @since API Level 16
      */
     public boolean pressDPadDown() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
     }
 
@@ -234,6 +245,7 @@
      * @since API Level 16
      */
     public boolean pressDPadUp() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
     }
 
@@ -243,6 +255,7 @@
      * @since API Level 16
      */
     public boolean pressDPadLeft() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
     }
 
@@ -252,6 +265,7 @@
      * @since API Level 16
      */
     public boolean pressDPadRight() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
     }
 
@@ -261,6 +275,7 @@
      * @since API Level 16
      */
     public boolean pressDelete() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_DEL);
     }
 
@@ -270,6 +285,7 @@
      * @since API Level 16
      */
     public boolean pressEnter() {
+        Tracer.trace();
         return pressKeyCode(KeyEvent.KEYCODE_ENTER);
     }
 
@@ -281,6 +297,7 @@
      * @since API Level 16
      */
     public boolean pressKeyCode(int keyCode) {
+        Tracer.trace(keyCode);
         waitForIdle();
         return mUiAutomationBridge.getInteractionController().sendKey(keyCode, 0);
     }
@@ -295,6 +312,7 @@
      * @since API Level 16
      */
     public boolean pressKeyCode(int keyCode, int metaState) {
+        Tracer.trace(keyCode, metaState);
         waitForIdle();
         return mUiAutomationBridge.getInteractionController().sendKey(keyCode, metaState);
     }
@@ -307,6 +325,7 @@
      * @since API Level 16
      */
     public boolean pressRecentApps() throws RemoteException {
+        Tracer.trace();
         waitForIdle();
         final IStatusBarService statusBar = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -325,6 +344,7 @@
      * @since API Level 16
      */
     public int getDisplayWidth() {
+        Tracer.trace();
         Display display = getDefaultDisplay();
         Point p = new Point();
         display.getSize(p);
@@ -338,6 +358,7 @@
      * @since API Level 16
      */
     public int getDisplayHeight() {
+        Tracer.trace();
         Display display = getDefaultDisplay();
         Point p = new Point();
         display.getSize(p);
@@ -353,6 +374,7 @@
      * @since API Level 16
      */
     public boolean click(int x, int y) {
+        Tracer.trace(x, y);
         if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
             return (false);
         }
@@ -373,6 +395,7 @@
      * @since API Level 16
      */
     public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
+        Tracer.trace(startX, startY, endX, endY, steps);
         return mUiAutomationBridge.getInteractionController()
                 .scrollSwipe(startX, startY, endX, endY, steps);
     }
@@ -387,6 +410,7 @@
      * @since API Level 16
      */
     public boolean swipe(Point[] segments, int segmentSteps) {
+        Tracer.trace(segments, segmentSteps);
         return mUiAutomationBridge.getInteractionController().swipe(segments, segmentSteps);
     }
 
@@ -396,6 +420,7 @@
      * @since API Level 16
      */
     public void waitForIdle() {
+        Tracer.trace();
         waitForIdle(DEFAULT_TIMEOUT_MILLIS);
     }
 
@@ -404,8 +429,9 @@
      * @param timeout in milliseconds
      * @since API Level 16
      */
-    public void waitForIdle(long time) {
-        mUiAutomationBridge.waitForIdle(time);
+    public void waitForIdle(long timeout) {
+        Tracer.trace(timeout);
+        mUiAutomationBridge.waitForIdle(timeout);
     }
 
     /**
@@ -416,6 +442,7 @@
      */
     @Deprecated
     public String getCurrentActivityName() {
+        Tracer.trace();
         return mUiAutomationBridge.getQueryController().getCurrentActivityName();
     }
 
@@ -425,6 +452,7 @@
      * @since API Level 16
      */
     public String getCurrentPackageName() {
+        Tracer.trace();
         return mUiAutomationBridge.getQueryController().getCurrentPackageName();
     }
 
@@ -437,6 +465,7 @@
      * @since API Level 16
      */
     public void registerWatcher(String name, UiWatcher watcher) {
+        Tracer.trace(name, watcher);
         if (mInWatcherContext) {
             throw new IllegalStateException("Cannot register new watcher from within another");
         }
@@ -448,10 +477,10 @@
      *
      * See {@link #registerWatcher(String, UiWatcher)}
      * @param name used to register the UiWatcher
-     * @throws UiAutomationException
      * @since API Level 16
      */
     public void removeWatcher(String name) {
+        Tracer.trace(name);
         if (mInWatcherContext) {
             throw new IllegalStateException("Cannot remove a watcher from within another");
         }
@@ -464,6 +493,7 @@
      * @since API Level 16
      */
     public void runWatchers() {
+        Tracer.trace();
         if (mInWatcherContext) {
             return;
         }
@@ -493,6 +523,7 @@
      * @since API Level 16
      */
     public void resetWatcherTriggers() {
+        Tracer.trace();
         mWatchersTriggers.clear();
     }
 
@@ -508,6 +539,7 @@
      * @since API Level 16
      */
     public boolean hasWatcherTriggered(String watcherName) {
+        Tracer.trace(watcherName);
         return mWatchersTriggers.contains(watcherName);
     }
 
@@ -519,6 +551,7 @@
      * @since API Level 16
      */
     public boolean hasAnyWatcherTriggered() {
+        Tracer.trace();
         return mWatchersTriggers.size() > 0;
     }
 
@@ -527,6 +560,7 @@
      * @param watcherName
      */
     private void setWatcherTriggered(String watcherName) {
+        Tracer.trace(watcherName);
         if (!hasWatcherTriggered(watcherName)) {
             mWatchersTriggers.add(watcherName);
         }
@@ -539,6 +573,7 @@
      * @since API Level 17
      */
     public boolean isNaturalOrientation() {
+        Tracer.trace();
         Display display = getDefaultDisplay();
         return display.getRotation() == Surface.ROTATION_0 ||
                 display.getRotation() == Surface.ROTATION_180;
@@ -546,10 +581,10 @@
 
     /**
      * Returns the current rotation of the display, as defined in {@link Surface}
-     * @return
      * @since API Level 17
      */
     public int getDisplayRotation() {
+        Tracer.trace();
         return getDefaultDisplay().getRotation();
     }
 
@@ -560,6 +595,7 @@
      * @since API Level 16
      */
     public void freezeRotation() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().freezeRotation();
     }
 
@@ -570,6 +606,7 @@
      * @throws RemoteException
      */
     public void unfreezeRotation() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().unfreezeRotation();
     }
 
@@ -583,6 +620,7 @@
      * @since API Level 17
      */
     public void setOrientationLeft() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().setRotationLeft();
     }
 
@@ -596,6 +634,7 @@
      * @since API Level 17
      */
     public void setOrientationRight() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().setRotationRight();
     }
 
@@ -609,6 +648,7 @@
      * @since API Level 17
      */
     public void setOrientationNatural() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().setRotationNatural();
     }
 
@@ -622,6 +662,7 @@
      * @since API Level 16
      */
     public void wakeUp() throws RemoteException {
+        Tracer.trace();
         if(getAutomatorBridge().getInteractionController().wakeDevice()) {
             // sync delay to allow the window manager to start accepting input
             // after the device is awakened.
@@ -637,6 +678,7 @@
      * @since API Level 16
      */
     public boolean isScreenOn() throws RemoteException {
+        Tracer.trace();
         return getAutomatorBridge().getInteractionController().isScreenOn();
     }
 
@@ -648,6 +690,7 @@
      * @since API Level 16
      */
     public void sleep() throws RemoteException {
+        Tracer.trace();
         getAutomatorBridge().getInteractionController().sleepDevice();
     }
 
@@ -659,6 +702,7 @@
      * @since API Level 16
      */
     public void dumpWindowHierarchy(String fileName) {
+        Tracer.trace(fileName);
         AccessibilityNodeInfo root =
                 getAutomatorBridge().getQueryController().getAccessibilityRootNode();
         if(root != null) {
@@ -683,6 +727,7 @@
      * @since API Level 16
      */
     public boolean waitForWindowUpdate(final String packageName, long timeout) {
+        Tracer.trace(packageName, timeout);
         if (packageName != null) {
             if (!packageName.equals(getCurrentPackageName())) {
                 return false;
@@ -744,6 +789,7 @@
      * @since API Level 17
      */
     public boolean takeScreenshot(File storePath) {
+        Tracer.trace(storePath);
         return takeScreenshot(storePath, 1.0f, 90);
     }
 
@@ -759,6 +805,7 @@
      * @since API Level 17
      */
     public boolean takeScreenshot(File storePath, float scale, int quality) {
+        Tracer.trace(storePath, scale, quality);
         // This is from com.android.systemui.screenshot.GlobalScreenshot#takeScreenshot
         // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
         // only in the natural orientation of the device :!)
diff --git a/uiautomator/library/src/com/android/uiautomator/core/UiObject.java b/uiautomator/library/src/com/android/uiautomator/core/UiObject.java
index 42819e2..2bf6455 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/UiObject.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/UiObject.java
@@ -77,6 +77,7 @@
      * @since API Level 16
      */
     public final UiSelector getSelector() {
+        Tracer.trace();
         return new UiSelector(mSelector);
     }
 
@@ -109,6 +110,7 @@
      * @since API Level 16
      */
     public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
+        Tracer.trace(selector);
         return new UiObject(getSelector().childSelector(selector));
     }
 
@@ -124,6 +126,7 @@
      * @since API Level 16
      */
     public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
+        Tracer.trace(selector);
         return new UiObject(getSelector().fromParent(selector));
     }
 
@@ -136,6 +139,7 @@
      * @since API Level 16
      */
     public int getChildCount() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -147,7 +151,6 @@
      * Uses the member UiSelector properties to find a matching UI element reported in
      * the accessibility hierarchy.
      *
-     * @param selector {@link UiSelector}
      * @param timeout in milliseconds
      * @return AccessibilityNodeInfo if found else null
      * @since API Level 16
@@ -181,8 +184,8 @@
 
     /**
      * Perform the action on the UI element that is represented by this UiObject. Also see
-     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
-     * {@link #scrollForward()}.
+     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
+     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}.
      *
      * @param steps indicates the number of injected move steps into the system. Steps are
      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
@@ -191,6 +194,7 @@
      * @since API Level 16
      */
     public boolean swipeUp(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
         if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
             return false; // too small to swipe
@@ -201,10 +205,11 @@
 
     /**
      * Perform the action on the UI element that is represented by this object, Also see
-     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
-     * {@link #scrollForward()}. This method will perform the swipe gesture over any
-     * surface. The targeted UI element does not need to have the attribute
-     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
+     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
+     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
+     * perform the swipe gesture over any surface.  The targeted UI element does not need to have
+     * the attribute <code>scrollable</code> set to <code>true</code> for this operation to be
+     * performed.
      *
      * @param steps indicates the number of injected move steps into the system. Steps are
      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
@@ -213,6 +218,7 @@
      * @since API Level 16
      */
     public boolean swipeDown(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
         if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
             return false; // too small to swipe
@@ -223,10 +229,11 @@
 
     /**
      * Perform the action on the UI element that is represented by this object. Also see
-     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
-     * {@link #scrollForward()}. This method will perform the swipe gesture over any
-     * surface. The targeted UI element does not need to have the attribute
-     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
+     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
+     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
+     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
+     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
+     * performed.
      *
      * @param steps indicates the number of injected move steps into the system. Steps are
      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
@@ -235,6 +242,7 @@
      * @since API Level 16
      */
     public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
         if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
             return false; // too small to swipe
@@ -244,10 +252,11 @@
 
     /**
      * Perform the action on the UI element that is represented by this object. Also see
-     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
-     * {@link #scrollForward()}. This method will perform the swipe gesture over any
-     * surface. The targeted UI element does not need to have the attribute
-     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
+     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
+     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
+     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
+     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
+     * performed.
      *
      * @param steps indicates the number of injected move steps into the system. Steps are
      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
@@ -256,6 +265,7 @@
      * @since API Level 16
      */
     public boolean swipeRight(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Rect rect = getVisibleBounds();
         if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
             return false; // too small to swipe
@@ -299,7 +309,7 @@
      * adjustments should be made to the click coordinates.
      *
      * @param node
-     * @return
+     * @return The accessibility node info.
      */
     private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
         AccessibilityNodeInfo parent = node;
@@ -321,6 +331,7 @@
      * @since API Level 16
      */
     public boolean click() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -341,6 +352,7 @@
      * @since API Level 16
      */
     public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
+        Tracer.trace();
         return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
     }
 
@@ -360,6 +372,7 @@
      * @since API Level 16
      */
     public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
+        Tracer.trace(timeout);
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -373,10 +386,11 @@
      * Clicks the top and left corner of the UI element
      *
      * @return true on success
-     * @throws Exception
+     * @throws UiObjectNotFoundException
      * @since API Level 16
      */
     public boolean clickTopLeft() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -393,6 +407,7 @@
      * @since API Level 16
      */
     public boolean longClickBottomRight() throws UiObjectNotFoundException  {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -405,10 +420,11 @@
      * Clicks the bottom and right corner of the UI element
      *
      * @return true on success
-     * @throws Exception
+     * @throws UiObjectNotFoundException
      * @since API Level 16
      */
     public boolean clickBottomRight() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -425,6 +441,7 @@
      * @since API Level 16
      */
     public boolean longClick() throws UiObjectNotFoundException  {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -441,6 +458,7 @@
      * @since API Level 16
      */
     public boolean longClickTopLeft() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -457,6 +475,7 @@
      * @since API Level 16
      */
     public String getText() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -474,6 +493,7 @@
      * @since API Level 16
      */
     public String getContentDescription() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -499,6 +519,7 @@
      * @since API Level 16
      */
     public boolean setText(String text) throws UiObjectNotFoundException {
+        Tracer.trace(text);
         clearTextField();
         return getInteractionController().sendText(text);
     }
@@ -523,6 +544,7 @@
      * @since API Level 16
      */
     public void clearTextField() throws UiObjectNotFoundException {
+        Tracer.trace();
         // long click left + center
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
@@ -547,6 +569,7 @@
      * @since API Level 16
      */
     public boolean isChecked() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -562,6 +585,7 @@
      * @since API Level 16
      */
     public boolean isSelected() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -577,6 +601,7 @@
      * @since API Level 16
      */
     public boolean isCheckable() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -592,6 +617,7 @@
      * @since API Level 16
      */
     public boolean isEnabled() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -607,6 +633,7 @@
      * @since API Level 16
      */
     public boolean isClickable() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -622,6 +649,7 @@
      * @since API Level 16
      */
     public boolean isFocused() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -637,6 +665,7 @@
      * @since API Level 16
      */
     public boolean isFocusable() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -652,6 +681,7 @@
      * @since API Level 16
      */
     public boolean isScrollable() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -667,6 +697,7 @@
      * @since API Level 16
      */
     public boolean isLongClickable() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -682,6 +713,7 @@
      * @since API Level 16
      */
     public String getPackageName() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -697,10 +729,11 @@
      *
      * @return Rect
      * @throws UiObjectNotFoundException
-     * @see {@link #getBound()}
+     * @see {@link #getBounds()}
      * @since API Level 17
      */
     public Rect getVisibleBounds() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -716,6 +749,7 @@
      * @since API Level 16
      */
     public Rect getBounds() throws UiObjectNotFoundException {
+        Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
@@ -738,6 +772,7 @@
      * @since API Level 16
      */
     public boolean waitForExists(long timeout) {
+        Tracer.trace(timeout);
         if(findAccessibilityNodeInfo(timeout) != null) {
             return true;
         }
@@ -763,6 +798,7 @@
      * @since API Level 16
      */
     public boolean waitUntilGone(long timeout) {
+        Tracer.trace(timeout);
         long startMills = SystemClock.uptimeMillis();
         long currentMills = 0;
         while (currentMills <= timeout) {
@@ -787,6 +823,7 @@
      * @since API Level 16
      */
     public boolean exists() {
+        Tracer.trace();
         return waitForExists(0);
     }
 
diff --git a/uiautomator/library/src/com/android/uiautomator/core/UiScrollable.java b/uiautomator/library/src/com/android/uiautomator/core/UiScrollable.java
index 607ae6f..c128ac2 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/UiScrollable.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/UiScrollable.java
@@ -63,6 +63,7 @@
      * @since API Level 16
      */
     public UiScrollable setAsVerticalList() {
+        Tracer.trace();
         mIsVerticalList = true;
         return this;
     }
@@ -73,6 +74,7 @@
      * @since API Level 16
      */
     public UiScrollable setAsHorizontalList() {
+        Tracer.trace();
         mIsVerticalList = false;
         return this;
     }
@@ -110,6 +112,7 @@
     @Override
     public UiObject getChildByDescription(UiSelector childPattern, String text)
             throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, text);
         return getChildByDescription(childPattern, text, true);
     }
 
@@ -125,6 +128,7 @@
      */
     public UiObject getChildByDescription(UiSelector childPattern, String text,
             boolean allowScrollSearch) throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, text, allowScrollSearch);
         if (text != null) {
             if (allowScrollSearch) {
                 scrollIntoView(new UiSelector().descriptionContains(text));
@@ -148,6 +152,7 @@
     @Override
     public UiObject getChildByInstance(UiSelector childPattern, int instance)
             throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, instance);
         UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
                 UiSelector.patternBuilder(childPattern).instance(instance));
         return new UiObject(patternSelector);
@@ -172,6 +177,7 @@
     @Override
     public UiObject getChildByText(UiSelector childPattern, String text)
             throws UiObjectNotFoundException {
+        Tracer.trace(childPattern, text);
         return getChildByText(childPattern, text, true);
     }
 
@@ -187,7 +193,7 @@
      */
     public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
             throws UiObjectNotFoundException {
-
+        Tracer.trace(childPattern, text, allowScrollSearch);
         if (text != null) {
             if (allowScrollSearch) {
                 scrollIntoView(new UiSelector().text(text));
@@ -206,6 +212,7 @@
      * @since API Level 16
      */
     public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
+        Tracer.trace(text);
         return scrollIntoView(new UiSelector().description(text));
     }
 
@@ -218,6 +225,7 @@
      * @since API Level 16
      */
     public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
+        Tracer.trace(obj.getSelector());
         return scrollIntoView(obj.getSelector());
     }
 
@@ -230,6 +238,7 @@
      * @since API Level 16
      */
     public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
+        Tracer.trace(selector);
         // if we happen to be on top of the text we want then return here
         if (exists(getSelector().childSelector(selector))) {
             return (true);
@@ -261,34 +270,39 @@
      * @since API Level 16
      */
     public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
+        Tracer.trace(text);
         return scrollIntoView(new UiSelector().text(text));
     }
 
     /**
-     * {@link #getChildByDescription(String, boolean)} and {@link #getChildByText(String, boolean)}
-     * use an arguments that specifies if scrolling is allowed while searching for the UI element.
-     * The number of scrolls allowed to perform a search can be modified by this method.
-     * The current value can be read by calling {@link #getMaxSearchSwipes()}
+     * {@link #getChildByDescription(UiSelector, String)} and
+     * {@link #getChildByText(UiSelector, String)} use an arguments that specifies if scrolling is
+     * allowed while searching for the UI element.  The number of scrolls allowed to perform a
+     * search can be modified by this method.  The current value can be read by calling
+     * {@link #getMaxSearchSwipes()}
      *
      * @param swipes is the number of search swipes until abort
      * @return reference to itself
      * @since API Level 16
      */
     public UiScrollable setMaxSearchSwipes(int swipes) {
+        Tracer.trace(swipes);
         mMaxSearchSwipes = swipes;
         return this;
     }
 
     /**
-     * {@link #getChildByDescription(String, boolean)} and {@link #getChildByText(String, boolean)}
-     * use an arguments that specifies if scrolling is allowed while searching for the UI element.
-     * The number of scrolls currently allowed to perform a search can be read by this method.
+     * {@link #getChildByDescription(UiSelector, String)} and
+     * {@link #getChildByText(UiSelector, String)} use an arguments that specifies if scrolling is
+     * allowed while searching for the UI element.  The number of scrolls currently allowed to
+     * perform a search can be read by this method.
      * See {@link #setMaxSearchSwipes(int)}
      *
      * @return max value of the number of swipes currently allowed during a scroll search
      * @since API Level 16
      */
     public int getMaxSearchSwipes() {
+        Tracer.trace();
         return mMaxSearchSwipes;
     }
 
@@ -299,6 +313,7 @@
      * @since API Level 16
      */
     public boolean flingForward() throws UiObjectNotFoundException {
+        Tracer.trace();
         return scrollForward(FLING_STEPS);
     }
 
@@ -309,6 +324,7 @@
      * @since API Level 16
      */
     public boolean scrollForward() throws UiObjectNotFoundException {
+        Tracer.trace();
         return scrollForward(SCROLL_STEPS);
     }
 
@@ -324,12 +340,13 @@
      * @since API Level 16
      */
     public boolean scrollForward(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if(node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
         }
-        Rect rect = new Rect();;
+        Rect rect = new Rect();
         node.getBoundsInScreen(rect);
 
         int downX = 0;
@@ -365,6 +382,7 @@
      * @since API Level 16
      */
     public boolean flingBackward() throws UiObjectNotFoundException {
+        Tracer.trace();
         return scrollBackward(FLING_STEPS);
     }
 
@@ -375,6 +393,7 @@
      * @since API Level 16
      */
     public boolean scrollBackward() throws UiObjectNotFoundException {
+        Tracer.trace();
         return scrollBackward(SCROLL_STEPS);
     }
 
@@ -390,12 +409,13 @@
      * @since API Level 16
      */
     public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
+        Tracer.trace(steps);
         Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
         if (node == null) {
             throw new UiObjectNotFoundException(getSelector().toString());
         }
-        Rect rect = new Rect();;
+        Rect rect = new Rect();
         node.getBoundsInScreen(rect);
 
         int downX = 0;
@@ -436,6 +456,7 @@
      * @since API Level 16
      */
     public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes, steps);
         Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
         // protect against potential hanging and return after preset attempts
         for(int x = 0; x < maxSwipes; x++) {
@@ -454,6 +475,7 @@
      * @since API Level 16
      */
     public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes);
         return scrollToBeginning(maxSwipes, SCROLL_STEPS);
     }
 
@@ -465,6 +487,7 @@
      * @since API Level 16
      */
     public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes);
         return scrollToBeginning(maxSwipes, FLING_STEPS);
     }
 
@@ -478,6 +501,7 @@
      * @since API Level 16
      */
     public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes, steps);
         // protect against potential hanging and return after preset attempts
         for(int x = 0; x < maxSwipes; x++) {
             if(!scrollForward(steps)) {
@@ -488,13 +512,14 @@
     }
 
     /**
-     * See {@link UiScrollable#scrollToEnd(int, int)
+     * See {@link UiScrollable#scrollToEnd(int, int)}
      *
      * @param maxSwipes
      * @return true on scrolled else false
      * @since API Level 16
      */
     public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes);
         return scrollToEnd(maxSwipes, SCROLL_STEPS);
     }
 
@@ -506,6 +531,7 @@
      * @since API Level 16
      */
     public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
+        Tracer.trace(maxSwipes);
         return scrollToEnd(maxSwipes, FLING_STEPS);
     }
 
@@ -524,6 +550,7 @@
      * @since API Level 16
      */
     public double getSwipeDeadZonePercentage() {
+        Tracer.trace();
         return mSwipeDeadZonePercentage;
     }
 
@@ -543,6 +570,7 @@
      * @since API Level 16
      */
     public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
+        Tracer.trace(swipeDeadZonePercentage);
         mSwipeDeadZonePercentage = swipeDeadZonePercentage;
         return this;
     }
diff --git a/uiautomator/library/src/com/android/uiautomator/core/UiSelector.java b/uiautomator/library/src/com/android/uiautomator/core/UiSelector.java
index d240ce9..8963c38 100644
--- a/uiautomator/library/src/com/android/uiautomator/core/UiSelector.java
+++ b/uiautomator/library/src/com/android/uiautomator/core/UiSelector.java
@@ -121,7 +121,7 @@
      * The text for the widget must match exactly
      * with the string in your input argument.
      *
-     * @param regular expression
+     * @param regex a regular expression
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
@@ -176,7 +176,7 @@
      * Set the search criteria to match the class property
      * for a widget (for example, "android.widget.Button").
      *
-     * @param regular expression
+     * @param regex a regular expression
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
@@ -188,7 +188,7 @@
      * Set the search criteria to match the class property
      * for a widget (for example, "android.widget.Button").
      *
-     * @param class type
+     * @param type type
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
@@ -228,7 +228,7 @@
      * for the widget must match exactly
      * with the string in your input argument.
      *
-     * @param regular expression
+     * @param regex a regular expression
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
@@ -529,7 +529,7 @@
      * Set the search criteria to match the package name
      * of the application that contains the widget.
      *
-     * @param regular expression
+     * @param regex a regular expression
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
diff --git a/uiautomator/library/src/com/android/uiautomator/testrunner/IAutomationSupport.java b/uiautomator/library/src/com/android/uiautomator/testrunner/IAutomationSupport.java
index bea9e9a..f0c60d2 100644
--- a/uiautomator/library/src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/uiautomator/library/src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -28,7 +28,8 @@
     /**
      * Allows the running test cases to send out interim status
      *
-     * @param bundle status report, consisting of key value pairs
+     * @param resultCode
+     * @param status status report, consisting of key value pairs
      * @since API Level 16
      */
     public void sendStatus(int resultCode, Bundle status);
diff --git a/uiautomator/library/src/com/android/uiautomator/testrunner/TestCaseCollector.java b/uiautomator/library/src/com/android/uiautomator/testrunner/TestCaseCollector.java
index ae27838..8bc9914 100644
--- a/uiautomator/library/src/com/android/uiautomator/testrunner/TestCaseCollector.java
+++ b/uiautomator/library/src/com/android/uiautomator/testrunner/TestCaseCollector.java
@@ -59,7 +59,7 @@
      *
      * The class name may be in "<class name>#<method name>" format
      *
-     * @param classNames classes must be subclass of {@link UiAutomatorTestCase}
+     * @param className classes must be subclass of {@link UiAutomatorTestCase}
      * @throws ClassNotFoundException
      */
     public void addTestClass(String className) throws ClassNotFoundException {
@@ -96,8 +96,7 @@
 
     /**
      * Gets the list of added test cases so far
-     *
-     * @return
+     * @return a list of {@link TestCase}
      */
     public List<TestCase> getTestCases() {
         return Collections.unmodifiableList(mTestCases);
diff --git a/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index 926bff7..e7d961b 100644
--- a/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -85,7 +85,7 @@
     /**
      * Provides support for running tests to report interim status
      *
-     * @return
+     * @return IAutomationSupport
      * @since API Level 16
      */
     public IAutomationSupport getAutomationSupport() {
diff --git a/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java b/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
index 02553e6..a376aa0 100644
--- a/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
+++ b/uiautomator/library/src/com/android/uiautomator/testrunner/UiAutomatorTestRunner.java
@@ -26,6 +26,8 @@
 import android.test.RepetitiveTest;
 import android.util.Log;
 
+import com.android.uiautomator.core.Tracer;
+import com.android.uiautomator.core.Tracer.Mode;
 import com.android.uiautomator.core.UiDevice;
 
 import junit.framework.AssertionFailedError;
@@ -105,6 +107,21 @@
         Bundle testRunOutput = new Bundle();
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         PrintStream writer = new PrintStream(byteArrayOutputStream);
+
+        String traceType = mParams.getString("traceOutputMode");
+        if(traceType != null) {
+            Tracer.Mode mode = Tracer.Mode.valueOf(Tracer.Mode.class, traceType);
+            if (mode == Tracer.Mode.FILE || mode == Tracer.Mode.ALL) {
+                String filename = mParams.getString("traceLogFilename");
+                if (filename == null) {
+                    throw new RuntimeException("Name of log file not specified. " +
+                            "Please specify it using traceLogFilename parameter");
+                }
+                Tracer.getInstance().setOutputFilename(filename);
+            }
+            Tracer.getInstance().setOutputMode(mode);
+        }
+
         try {
             StringResultPrinter resultPrinter = new StringResultPrinter(writer);