118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/*
218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Copyright (C) 2012 The Android Open Source Project
318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * you may not use this file except in compliance with the License.
618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * You may obtain a copy of the License at
718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
1018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Unless required by applicable law or agreed to in writing, software
1118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
1218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * See the License for the specific language governing permissions and
1418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * limitations under the License.
1518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
1618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupackage com.android.uiautomator.core;
1718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
1818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Log;
1918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
2018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.File;
2118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.FileNotFoundException;
2218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.io.PrintWriter;
2318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.text.SimpleDateFormat;
2418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.ArrayList;
2518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.Arrays;
2618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.Date;
2718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.List;
2818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport java.util.Locale;
2918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/**
3118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Class that creates traces of the calls to the UiAutomator API and outputs the
3218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * traces either to logcat or a logfile. Each public method in the UiAutomator
3318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * that needs to be traced should include a call to Tracer.trace in the
3418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * beginning. Tracing is turned off by defualt and needs to be enabled
3518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * explicitly.
3618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @hide
3718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
3818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupublic class Tracer {
3918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final String UNKNOWN_METHOD_STRING = "(unknown method)";
4018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final String UIAUTOMATOR_PACKAGE = "com.android.uiautomator.core";
4118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int CALLER_LOCATION = 6;
4218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int METHOD_TO_TRACE_LOCATION = 5;
4318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int MIN_STACK_TRACE_LENGTH = 7;
4418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
4518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
4618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Enum that determines where the trace output goes. It can go to either
4718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * logcat, log file or both.
4818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
4918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public enum Mode {
5018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        NONE,
5118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        FILE,
5218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        LOGCAT,
5318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        ALL
5418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
5518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private interface TracerSink {
5718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void log(String message);
5818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
5918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void close();
6018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
6118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private class FileSink implements TracerSink {
6318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        private PrintWriter mOut;
6418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        private SimpleDateFormat mDateFormat;
6518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public FileSink(File file) throws FileNotFoundException {
6718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mOut = new PrintWriter(file);
6818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
6918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
7018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
7118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void log(String message) {
7218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mOut.printf("%s %s\n", mDateFormat.format(new Date()), message);
7318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
7418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
7518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void close() {
7618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mOut.close();
7718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
7818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
7918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private class LogcatSink implements TracerSink {
8118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        private static final String LOGCAT_TAG = "UiAutomatorTrace";
8318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void log(String message) {
8518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.i(LOGCAT_TAG, message);
8618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
8718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        public void close() {
8918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // nothing is needed
9018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
9118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
9218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
9318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private Mode mCurrentMode = Mode.NONE;
9418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private List<TracerSink> mSinks = new ArrayList<TracerSink>();
9518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private File mOutputFile;
9618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
9718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static Tracer mInstance = null;
9818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
9918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
10018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Returns a reference to an instance of the tracer. Useful to set the
10118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * parameters before the trace is collected.
10218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
10318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return
10418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
10518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public static Tracer getInstance() {
10618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (mInstance == null) {
10718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            mInstance = new Tracer();
10818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
10918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mInstance;
11018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
11118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
11218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
11318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Sets where the trace output will go. Can be either be logcat or a file or
11418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * both. Setting this to NONE will turn off tracing.
11518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
11618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param mode
11718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
11818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void setOutputMode(Mode mode) {
11918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        closeSinks();
12018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mCurrentMode = mode;
12118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        try {
12218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            switch (mode) {
12318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                case FILE:
12418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    if (mOutputFile == null) {
12518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        throw new IllegalArgumentException("Please provide a filename before " +
12618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                                "attempting write trace to a file");
12718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    }
12818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    mSinks.add(new FileSink(mOutputFile));
12918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    break;
13018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                case LOGCAT:
13118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    mSinks.add(new LogcatSink());
13218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    break;
13318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                case ALL:
13418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    mSinks.add(new LogcatSink());
13518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    if (mOutputFile == null) {
13618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                        throw new IllegalArgumentException("Please provide a filename before " +
13718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                                "attempting write trace to a file");
13818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    }
13918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    mSinks.add(new FileSink(mOutputFile));
14018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    break;
14118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                default:
14218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    break;
14318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
14418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } catch (FileNotFoundException e) {
14518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.w("Tracer", "Could not open log file: " + e.getMessage());
14618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
14718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
14818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
14918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private void closeSinks() {
15018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (TracerSink sink : mSinks) {
15118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            sink.close();
15218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
15318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mSinks.clear();
15418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
15518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
15618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
15718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Sets the name of the log file where tracing output will be written if the
15818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * tracer is set to write to a file.
15918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
16018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param filename name of the log file.
16118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
16218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public void setOutputFilename(String filename) {
16318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mOutputFile = new File(filename);
16418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
16518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
16618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private void doTrace(Object[] arguments) {
16718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (mCurrentMode == Mode.NONE) {
16818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return;
16918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
17018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
17118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        String caller = getCaller();
17218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (caller == null) {
17318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return;
17418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
17518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
17618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        log(String.format("%s (%s)", caller, join(", ", arguments)));
17718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
17818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
17918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private void log(String message) {
18018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (TracerSink sink : mSinks) {
18118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            sink.log(message);
18218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
18318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
18418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
18518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
18618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Queries whether the tracing is enabled.
18718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if tracing is enabled, false otherwise.
18818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
18918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean isTracingEnabled() {
19018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mCurrentMode != Mode.NONE;
19118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
19218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
19318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
19418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Public methods in the UiAutomator should call this function to generate a
19518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * trace. The trace will include the method thats is being called, it's
19618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * arguments and where in the user's code the method is called from. If a
19718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * public method is called internally from UIAutomator then this will not
19818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * output a trace entry. Only calls from outise the UiAutomator package will
19918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * produce output.
20018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
20118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Special note about array arguments. You can safely pass arrays of reference types
20218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * to this function. Like String[] or Integer[]. The trace function will print their
20318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * contents by calling toString() on each of the elements. This will not work for
20418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * array of primitive types like int[] or float[]. Before passing them to this function
20518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * convert them to arrays of reference types manually. Example: convert int[] to Integer[].
20618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
20718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param arguments arguments of the method being traced.
20818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
20918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public static void trace(Object... arguments) {
21018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.getInstance().doTrace(arguments);
21118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
21218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
21318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static String join(String separator, Object[] strings) {
21418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (strings.length == 0)
21518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return "";
21618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
21718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        StringBuilder builder = new StringBuilder(objectToString(strings[0]));
21818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for (int i = 1; i < strings.length; i++) {
21918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            builder.append(separator);
22018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            builder.append(objectToString(strings[i]));
22118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
22218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return builder.toString();
22318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
22418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
22518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
22618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Special toString method to handle arrays. If the argument is a normal object then this will
22718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * return normal output of obj.toString(). If the argument is an array this will return a
22818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * string representation of the elements of the array.
22918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
23018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * This method will not work for arrays of primitive types. Arrays of primitive types are
23118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * expected to be converted manually by the caller. If the array is not converter then
23218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * this function will only output "[...]" instead of the contents of the array.
23318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
23418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param obj object to convert to a string
23518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return String representation of the object.
23618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
23718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static String objectToString(Object obj) {
23818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (obj.getClass().isArray()) {
23918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (obj instanceof Object[]) {
24018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return Arrays.deepToString((Object[])obj);
24118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            } else {
24218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return "[...]";
24318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
24418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
24518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return obj.toString();
24618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
24718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
24818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
24918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
25018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * This method outputs which UiAutomator method was called and where in the
25118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * user code it was called from. If it can't deside which method is called
25218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * it will output "(unknown method)". If the method was called from inside
25318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the UiAutomator then it returns null.
25418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
25518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return name of the method called and where it was called from. Null if
25618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *         method was called from inside UiAutomator.
25718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
25818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static String getCaller() {
25918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        StackTraceElement stackTrace[] = Thread.currentThread().getStackTrace();
26018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (stackTrace.length < MIN_STACK_TRACE_LENGTH) {
26118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return UNKNOWN_METHOD_STRING;
26218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
26318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
26418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        StackTraceElement caller = stackTrace[METHOD_TO_TRACE_LOCATION];
26518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        StackTraceElement previousCaller = stackTrace[CALLER_LOCATION];
26618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
26718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (previousCaller.getClassName().startsWith(UIAUTOMATOR_PACKAGE)) {
26818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return null;
26918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
27018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
27118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int indexOfDot = caller.getClassName().lastIndexOf('.');
27218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (indexOfDot < 0) {
27318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            indexOfDot = 0;
27418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
27518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
27618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (indexOfDot + 1 >= caller.getClassName().length()) {
27718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return UNKNOWN_METHOD_STRING;
27818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
27918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
28018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        String shortClassName = caller.getClassName().substring(indexOfDot + 1);
28118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return String.format("%s.%s from %s() at %s:%d", shortClassName, caller.getMethodName(),
28218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                previousCaller.getMethodName(), previousCaller.getFileName(),
28318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                previousCaller.getLineNumber());
28418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
28518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu}
286