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