1b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine/* 2b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Copyright (C) 2012 The Android Open Source Project 3b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 4b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Licensed under the Apache License, Version 2.0 (the "License"); 5b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * you may not use this file except in compliance with the License. 6b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * You may obtain a copy of the License at 7b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 8b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * http://www.apache.org/licenses/LICENSE-2.0 9b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 10b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Unless required by applicable law or agreed to in writing, software 11b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * distributed under the License is distributed on an "AS IS" BASIS, 12b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * See the License for the specific language governing permissions and 14b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * limitations under the License. 15b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 16b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavinepackage com.android.uiautomator.core; 17b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 18b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport android.util.Log; 19b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 20b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.io.File; 21b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.io.FileNotFoundException; 22b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.io.PrintWriter; 23b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.text.SimpleDateFormat; 24b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.util.ArrayList; 25b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.util.Arrays; 26b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.util.Date; 27b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.util.List; 28b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavineimport java.util.Locale; 29b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 30b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine/** 31b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Class that creates traces of the calls to the UiAutomator API and outputs the 32b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * traces either to logcat or a logfile. Each public method in the UiAutomator 33b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * that needs to be traced should include a call to Tracer.trace in the 34b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * beginning. Tracing is turned off by defualt and needs to be enabled 35b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * explicitly. 36b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @hide 37b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 38b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavinepublic class Tracer { 39b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final String UNKNOWN_METHOD_STRING = "(unknown method)"; 40b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final String UIAUTOMATOR_PACKAGE = "com.android.uiautomator.core"; 41b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final int CALLER_LOCATION = 6; 42b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final int METHOD_TO_TRACE_LOCATION = 5; 43b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final int MIN_STACK_TRACE_LENGTH = 7; 44b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 45b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 46b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Enum that determines where the trace output goes. It can go to either 47b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * logcat, log file or both. 48b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 49b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public enum Mode { 50b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine NONE, 51b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine FILE, 52b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine LOGCAT, 53b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine ALL 54b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 55b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 56b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private interface TracerSink { 57b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void log(String message); 58b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 59b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void close(); 60b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 61b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 62b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private class FileSink implements TracerSink { 63b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private PrintWriter mOut; 64b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private SimpleDateFormat mDateFormat; 65b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 66b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public FileSink(File file) throws FileNotFoundException { 67b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mOut = new PrintWriter(file); 68b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); 69b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 70b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 71b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void log(String message) { 72b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mOut.printf("%s %s\n", mDateFormat.format(new Date()), message); 73b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 74b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 75b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void close() { 76b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mOut.close(); 77b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 78b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 79b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 80b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private class LogcatSink implements TracerSink { 81b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 82b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static final String LOGCAT_TAG = "UiAutomatorTrace"; 83b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 84b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void log(String message) { 85b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine Log.i(LOGCAT_TAG, message); 86b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 87b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 88b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void close() { 89b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine // nothing is needed 90b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 91b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 92b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 93b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private Mode mCurrentMode = Mode.NONE; 94b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private List<TracerSink> mSinks = new ArrayList<TracerSink>(); 95b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private File mOutputFile; 96b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 97b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static Tracer mInstance = null; 98b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 99b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 100b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Returns a reference to an instance of the tracer. Useful to set the 101b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * parameters before the trace is collected. 102b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 103b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @return 104b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 105b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public static Tracer getInstance() { 106b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (mInstance == null) { 107b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mInstance = new Tracer(); 108b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 109b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return mInstance; 110b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 111b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 112b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 113b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Sets where the trace output will go. Can be either be logcat or a file or 114b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * both. Setting this to NONE will turn off tracing. 115b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 116b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @param mode 117b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 118b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void setOutputMode(Mode mode) { 119b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine closeSinks(); 120b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mCurrentMode = mode; 121b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine try { 122b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine switch (mode) { 123b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine case FILE: 124b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (mOutputFile == null) { 125b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine throw new IllegalArgumentException("Please provide a filename before " + 126b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine "attempting write trace to a file"); 127b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 128b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mSinks.add(new FileSink(mOutputFile)); 129b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine break; 130b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine case LOGCAT: 131b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mSinks.add(new LogcatSink()); 132b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine break; 133b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine case ALL: 134b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mSinks.add(new LogcatSink()); 135b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (mOutputFile == null) { 136b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine throw new IllegalArgumentException("Please provide a filename before " + 137b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine "attempting write trace to a file"); 138b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 139b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mSinks.add(new FileSink(mOutputFile)); 140b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine break; 141b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine default: 142b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine break; 143b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 144b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } catch (FileNotFoundException e) { 145b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine Log.w("Tracer", "Could not open log file: " + e.getMessage()); 146b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 147b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 148b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 149b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private void closeSinks() { 150b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine for (TracerSink sink : mSinks) { 151b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine sink.close(); 152b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 153b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mSinks.clear(); 154b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 155b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 156b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 157b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Sets the name of the log file where tracing output will be written if the 158b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * tracer is set to write to a file. 159b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 160b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @param filename name of the log file. 161b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 162b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public void setOutputFilename(String filename) { 163b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine mOutputFile = new File(filename); 164b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 165b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 166b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private void doTrace(Object[] arguments) { 167b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (mCurrentMode == Mode.NONE) { 168b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return; 169b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 170b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 171b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine String caller = getCaller(); 172b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (caller == null) { 173b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return; 174b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 175b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 176b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine log(String.format("%s (%s)", caller, join(", ", arguments))); 177b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 178b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 179b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private void log(String message) { 180b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine for (TracerSink sink : mSinks) { 181b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine sink.log(message); 182b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 183b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 184b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 185b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 186b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Queries whether the tracing is enabled. 187b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @return true if tracing is enabled, false otherwise. 188b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 189b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public boolean isTracingEnabled() { 190b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return mCurrentMode != Mode.NONE; 191b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 192b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 193b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 194b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Public methods in the UiAutomator should call this function to generate a 195b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * trace. The trace will include the method thats is being called, it's 196b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * arguments and where in the user's code the method is called from. If a 197b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * public method is called internally from UIAutomator then this will not 198b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * output a trace entry. Only calls from outise the UiAutomator package will 199b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * produce output. 200b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 201b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Special note about array arguments. You can safely pass arrays of reference types 202b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * to this function. Like String[] or Integer[]. The trace function will print their 203b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * contents by calling toString() on each of the elements. This will not work for 204b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * array of primitive types like int[] or float[]. Before passing them to this function 205b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * convert them to arrays of reference types manually. Example: convert int[] to Integer[]. 206b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 207b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @param arguments arguments of the method being traced. 208b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 209b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine public static void trace(Object... arguments) { 210b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine Tracer.getInstance().doTrace(arguments); 211b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 212b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 213b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static String join(String separator, Object[] strings) { 214b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (strings.length == 0) 215b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return ""; 216b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 217b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine StringBuilder builder = new StringBuilder(objectToString(strings[0])); 218b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine for (int i = 1; i < strings.length; i++) { 219b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine builder.append(separator); 220b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine builder.append(objectToString(strings[i])); 221b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 222b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return builder.toString(); 223b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 224b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 225b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 226b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * Special toString method to handle arrays. If the argument is a normal object then this will 227b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * return normal output of obj.toString(). If the argument is an array this will return a 228b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * string representation of the elements of the array. 229b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 230b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * This method will not work for arrays of primitive types. Arrays of primitive types are 231b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * expected to be converted manually by the caller. If the array is not converter then 232b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * this function will only output "[...]" instead of the contents of the array. 233b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 234b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @param obj object to convert to a string 235b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @return String representation of the object. 236b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 237b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static String objectToString(Object obj) { 238b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (obj.getClass().isArray()) { 239b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (obj instanceof Object[]) { 240b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return Arrays.deepToString((Object[])obj); 241b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } else { 242b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return "[...]"; 243b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 244b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } else { 245b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return obj.toString(); 246b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 247b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 248b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 249b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine /** 250b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * This method outputs which UiAutomator method was called and where in the 251b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * user code it was called from. If it can't deside which method is called 252b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * it will output "(unknown method)". If the method was called from inside 253b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * the UiAutomator then it returns null. 254b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * 255b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * @return name of the method called and where it was called from. Null if 256b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine * method was called from inside UiAutomator. 257b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine */ 258b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine private static String getCaller() { 259b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine StackTraceElement stackTrace[] = Thread.currentThread().getStackTrace(); 260b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (stackTrace.length < MIN_STACK_TRACE_LENGTH) { 261b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return UNKNOWN_METHOD_STRING; 262b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 263b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 264b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine StackTraceElement caller = stackTrace[METHOD_TO_TRACE_LOCATION]; 265b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine StackTraceElement previousCaller = stackTrace[CALLER_LOCATION]; 266b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 267b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (previousCaller.getClassName().startsWith(UIAUTOMATOR_PACKAGE)) { 268b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return null; 269b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 270b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 271b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine int indexOfDot = caller.getClassName().lastIndexOf('.'); 272b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (indexOfDot < 0) { 273b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine indexOfDot = 0; 274b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 275b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 276b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine if (indexOfDot + 1 >= caller.getClassName().length()) { 277b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return UNKNOWN_METHOD_STRING; 278b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 279b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine 280b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine String shortClassName = caller.getClassName().substring(indexOfDot + 1); 281b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine return String.format("%s.%s from %s() at %s:%d", shortClassName, caller.getMethodName(), 282b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine previousCaller.getMethodName(), previousCaller.getFileName(), 283b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine previousCaller.getLineNumber()); 284b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine } 285b04f3c526835516b098227342e741b7ce944c2f3Maxim Siniavine} 286