ViewDebug.java revision 95db2b20d7bc0aaf00b1d4418124f5cf0a755d74
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.content.Context;
20f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.content.res.Resources;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
22c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.graphics.Canvas;
23223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport android.graphics.Rect;
24c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.os.Debug;
25f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.os.Environment;
26f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.os.Looper;
27f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.os.Message;
289a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guyimport android.os.ParcelFileDescriptor;
29223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport android.os.RemoteException;
30648bee18a1ccd362445d562729250ff5910f16a0Romain Guyimport android.os.SystemClock;
31f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.util.DisplayMetrics;
32f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.util.Log;
33f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport android.util.Printer;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
35f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.io.BufferedOutputStream;
36f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.io.BufferedWriter;
37223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport java.io.ByteArrayOutputStream;
38f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.io.DataOutputStream;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
407eabe55db6b113f83c2cefcd06812648927de877Romain Guyimport java.io.FileDescriptor;
41f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.io.FileOutputStream;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileWriter;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream;
45f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.io.OutputStreamWriter;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.ElementType;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Retention;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.RetentionPolicy;
49f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.lang.annotation.Target;
50f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.lang.reflect.AccessibleObject;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Field;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.InvocationTargetException;
53f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.lang.reflect.Method;
549a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guyimport java.nio.ByteBuffer;
559a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guyimport java.nio.ByteOrder;
569a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guyimport java.nio.channels.FileChannel;
57f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.ArrayList;
58f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.HashMap;
59f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.LinkedList;
60f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.List;
61648bee18a1ccd362445d562729250ff5910f16a0Romain Guyimport java.util.Map;
62f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.concurrent.CountDownLatch;
63f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guyimport java.util.concurrent.TimeUnit;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Various debugging/tracing tools related to {@link View} and the view hierarchy.
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ViewDebug {
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Log tag used to log errors related to the consistency of the view hierarchy.
7113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
7213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
7313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
7413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final String CONSISTENCY_LOG_TAG = "ViewConsistency";
7513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
7613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
7713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check layout-related properties.
7813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
7913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
8013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
8113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_LAYOUT = 0x1;
8213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
8313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
8413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check drawing-related properties.
8513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
8613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
8713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
8813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_DRAWING = 0x2;
8913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
9013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view hierarchy tracing. Any invoker of
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_HIERARCHY = false;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view recycler tracing. Any invoker of
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_RECYCLER = false;
103a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles drawing times in the events log.
10613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
10713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
10813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
1095429e1d1026db8922478b715672e6c0703bbf4a9Romain Guy    public static final boolean DEBUG_PROFILE_DRAWING = false;
11013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
11113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
11213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles layout times in the events log.
11313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
11413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
11513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
1165429e1d1026db8922478b715672e6c0703bbf4a9Romain Guy    public static final boolean DEBUG_PROFILE_LAYOUT = false;
11713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
11813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
1192c095f367779ef32130c72849936a2e3013c8492Christopher Tate     * Enables detailed logging of drag/drop operations.
1202c095f367779ef32130c72849936a2e3013c8492Christopher Tate     * @hide
1212c095f367779ef32130c72849936a2e3013c8492Christopher Tate     */
122994ef9296a00523de1df424b4b760b4416ead58bChristopher Tate    public static final boolean DEBUG_DRAG = false;
1232c095f367779ef32130c72849936a2e3013c8492Christopher Tate
1242c095f367779ef32130c72849936a2e3013c8492Christopher Tate    /**
1254e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     * Enables logging of factors that affect the latency and responsiveness of an application.
1264e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     *
1274e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     * Logs the relative difference between the time an event was created and the time it
1284e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     * was delivered.
1294e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     *
13095db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown     * Logs the time spent waiting for Surface.lockCanvas(), Surface.unlockCanvasAndPost()
13195db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown     * or eglSwapBuffers().  This is time that the event loop spends blocked and unresponsive.
13295db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown     * Ideally, drawing and animations should be perfectly synchronized with VSYNC so that
13395db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown     * dequeuing and queueing buffers is instantaneous.
1344e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     *
13595db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown     * Logs the time spent in ViewRoot.performTraversals() and ViewRoot.performDraw().
1364e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     * @hide
1374e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown     */
1384e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown    public static final boolean DEBUG_LATENCY = false;
1394e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown
14095db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown    /** @hide */
14195db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown    public static final String DEBUG_LATENCY_TAG = "ViewLatency";
14295db2b20d7bc0aaf00b1d4418124f5cf0a755d74Jeff Brown
1434e91a180be46c0c7c3bf398d4df4cbe2404216b5Jeff Brown    /**
14413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <p>Enables or disables views consistency check. Even when this property is enabled,
14543a17654cf4bfe7f1ec22bd8b7b32daccdf27c09Joe Onorato     * view consistency checks happen only if {@link false} is set
14613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * to true. The value of this property can be configured externally in one of the
14713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * following files:</p>
14813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <ul>
14913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/system/debug.prop</li>
15013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/debug.prop</li>
15113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/data/debug.prop</li>
15213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * </ul>
15313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
15413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
15513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
15613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean consistencyCheckEnabled = false;
15713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
15813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped by
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view server. Only non-void methods with no arguments can be annotated
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by this annotation.
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface ExportedProperty {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When resolveId is true, and if the annotated field/method return value
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is an int, the value is converted to an Android's resource name.
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the property's value must be transformed into an Android
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *         resource name, false otherwise
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean resolveId() default false;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A mapping can be defined to map int values to specific strings. For
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * instance, View.getVisibility() returns 0, 4 or 8. However, these values
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * these human readable values:
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @ViewDebug.ExportedProperty(mapping = {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 8, to = "GONE")
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * })
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * public int getVisibility() { ...
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An array of int to String mappings
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see android.view.ViewDebug.IntToString
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IntToString[] mapping() default { };
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
197c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be defined to map array indices to specific strings.
198c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be used to see human readable values for the indices
199c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * of an array:
200c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
201c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
202809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(indexMapping = {
203c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "INVALID"),
204c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 1, to = "FIRST"),
205c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 2, to = "SECOND")
206c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * })
207c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * private int[] mElements;
208c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
209c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
210c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @return An array of int to String mappings
211c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
212c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see android.view.ViewDebug.IntToString
213c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see #mapping()
214c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         */
215c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        IntToString[] indexMapping() default { };
216c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
217c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        /**
218809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A flags mapping can be defined to map flags encoded in an integer to
219809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * specific strings. A mapping can be used to see human readable values
220809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * for the flags of an integer:
221809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
222809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
223809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(flagMapping = {
224809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"),
225809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"),
226809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * })
227809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * private int mFlags;
228809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
229809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
230809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A specified String is output when the following is true:
231a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         *
232809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An array of int to String mappings
233809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
234809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        FlagToString[] flagMapping() default { };
235809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
236809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When deep export is turned on, this property is not dumped. Instead, the
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * properties contained in this property are dumped. Each child property
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is prefixed with the name of this property.
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the properties of this property should be dumped
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
243a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * @see #prefix()
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean deepExport() default false;
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The prefix to use on child properties when deep export is enabled
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a prefix as a String
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #deepExport()
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String prefix() default "";
255bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
256bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev        /**
257bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * Specifies the category the property falls into, such as measurement,
258bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * layout, drawing, etc.
259bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         *
260bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * @return the category as String
261bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         */
262bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev        String category() default "";
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines a mapping from an int value to a String. Such a mapping can be used
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in a @ExportedProperty to provide more meaningful values to the end user.
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.view.ViewDebug.ExportedProperty
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.TYPE })
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface IntToString {
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The original int value to map to a String.
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary int value.
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int from();
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The String to use in place of the original int value.
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary non-null String.
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String to();
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
288809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
289809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    /**
290809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * Defines a mapping from an flag to a String. Such a mapping can be used
291809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * in a @ExportedProperty to provide more meaningful values to the end user.
292809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     *
293809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * @see android.view.ViewDebug.ExportedProperty
294809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     */
295809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Target({ ElementType.TYPE })
296809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Retention(RetentionPolicy.RUNTIME)
297809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    public @interface FlagToString {
298809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
299809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The mask to apply to the original value.
300809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
301809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary int value.
302809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
303809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int mask();
304809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
305809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
306809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The value to compare to the result of:
307809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <code>original value &amp; {@link #mask()}</code>.
308809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
309809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary value.
310809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
311809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int equals();
312809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
313809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
314809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The String to use in place of the original int value.
315809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
316809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary non-null String.
317809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
318809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        String name();
319809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
320809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
321809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * Indicates whether to output the flag when the test is true,
322809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * or false. Defaults to true.
323809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
324809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        boolean outputIf() default true;
325809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
326809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped when
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view is captured. Methods with this annotation must have no arguments
330f8a7ceaef2e7d5cd530c9426bde91b6fa9a40b75Andy Stadler     * and must return a valid type of data.
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface CapturedViewProperty {
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
336a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * When retrieveReturn is true, we need to retrieve second level methods
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
338a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * we will set retrieveReturn = true on the annotation of
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * myView.getFirstLevelMethod()
340a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * @return true if we need the second level methods
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
342a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        boolean retrieveReturn() default false;
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
344a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Maximum delay in ms after which we stop trying to capture a View's drawing
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_TIMEOUT = 4000;
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_DUMP = "DUMP";
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
355c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static final String REMOTE_PROFILE = "PROFILE";
356223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
357ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase    private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
361c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
362c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum HierarchyTraceType {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE,
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD,
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD_IN_PARENT,
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        REQUEST_LAYOUT,
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_LAYOUT,
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_MEASURE,
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        DRAW,
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BUILD_CACHE
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static BufferedWriter sHierarchyTraces;
3786dd005b48138708762bfade0081d031a2a4a3822Dianne Hackborn    private static ViewRootImpl sHierarhcyRoot;
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sHierarchyTracePrefix;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of recycler trace to output to the recycler traces file.
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum RecyclerTraceType {
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        NEW_VIEW,
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BIND_VIEW,
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_ACTIVE_HEAP,
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_SCRAP_HEAP,
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_SCRAP_HEAP,
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class RecyclerTrace {
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int view;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public RecyclerTraceType type;
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int position;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int indexOnScreen;
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View sRecyclerOwnerView;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<View> sRecyclerViews;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<RecyclerTrace> sRecyclerTraces;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sRecyclerTracePrefix;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
405f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage =
406f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            new ThreadLocal<LooperProfiler>();
407f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated Views.
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of Views instanciated in the current process.
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewInstanceCount() {
416c21550a8d1dfc9e5359fe994cb48049a0bd4c82cBrian Carlstrom        return Debug.countInstancesOfClass(View.class);
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
420c6cc0f8c19d9eccf408a443fa2bf668af261dcd0Joe Onorato     * Returns the number of instanciated ViewAncestors.
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
422c6cc0f8c19d9eccf408a443fa2bf668af261dcd0Joe Onorato     * @return The number of ViewAncestors instanciated in the current process.
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
42665b345fa22b878e141b8fd8ece9c208df00fa40fRomain Guy    public static long getViewRootImplCount() {
4276dd005b48138708762bfade0081d031a2a4a3822Dianne Hackborn        return Debug.countInstancesOfClass(ViewRootImpl.class);
428a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    }
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
431f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     * Starts profiling the looper associated with the current thread.
432f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     * You must call {@link #stopLooperProfiling} to end profiling
433f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     * and obtain the traces. Both methods must be invoked on the
434f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     * same thread.
435f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     *
4367eabe55db6b113f83c2cefcd06812648927de877Romain Guy     * @hide
437f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     */
4387eabe55db6b113f83c2cefcd06812648927de877Romain Guy    public static void startLooperProfiling(String path, FileDescriptor fileDescriptor) {
439f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        if (sLooperProfilerStorage.get() == null) {
4407eabe55db6b113f83c2cefcd06812648927de877Romain Guy            LooperProfiler profiler = new LooperProfiler(path, fileDescriptor);
441f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            sLooperProfilerStorage.set(profiler);
442f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            Looper.myLooper().setMessageLogging(profiler);
443f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
4447eabe55db6b113f83c2cefcd06812648927de877Romain Guy    }
445f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
446f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    /**
447f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     * Stops profiling the looper associated with the current thread.
448f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     *
4497eabe55db6b113f83c2cefcd06812648927de877Romain Guy     * @see #startLooperProfiling(String, java.io.FileDescriptor)
4507eabe55db6b113f83c2cefcd06812648927de877Romain Guy     *
4517eabe55db6b113f83c2cefcd06812648927de877Romain Guy     * @hide
452f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy     */
453f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    public static void stopLooperProfiling() {
454f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        LooperProfiler profiler = sLooperProfilerStorage.get();
455f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        if (profiler != null) {
456f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            sLooperProfilerStorage.remove();
457f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            Looper.myLooper().setMessageLogging(null);
458f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            profiler.save();
459f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
460f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    }
461f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
462f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    private static class LooperProfiler implements Looper.Profiler, Printer {
463f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        private static final String LOG_TAG = "LooperProfiler";
464f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
4659a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static final int TRACE_VERSION_NUMBER = 3;
4669a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static final int ACTION_EXIT_METHOD = 0x1;
4679a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static final int HEADER_SIZE = 32;
4689a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static final String HEADER_MAGIC = "SLOW";
4699a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static final short HEADER_RECORD_SIZE = (short) 14;
4709a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
471648bee18a1ccd362445d562729250ff5910f16a0Romain Guy        private final long mTraceWallStart;
472648bee18a1ccd362445d562729250ff5910f16a0Romain Guy        private final long mTraceThreadStart;
473648bee18a1ccd362445d562729250ff5910f16a0Romain Guy
474f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
475f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
4769a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private final HashMap<String, Integer> mTraceNames = new HashMap<String, Integer>(32);
4779a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private int mTraceId = 0;
478648bee18a1ccd362445d562729250ff5910f16a0Romain Guy
4797eabe55db6b113f83c2cefcd06812648927de877Romain Guy        private final String mPath;
4809a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private ParcelFileDescriptor mFileDescriptor;
4817eabe55db6b113f83c2cefcd06812648927de877Romain Guy
4827eabe55db6b113f83c2cefcd06812648927de877Romain Guy        LooperProfiler(String path, FileDescriptor fileDescriptor) {
4837eabe55db6b113f83c2cefcd06812648927de877Romain Guy            mPath = path;
4849a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            try {
4859a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                mFileDescriptor = ParcelFileDescriptor.dup(fileDescriptor);
4869a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            } catch (IOException e) {
4879a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
4889a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                throw new RuntimeException(e);
4899a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            }
490648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            mTraceWallStart = SystemClock.currentTimeMicro();
491648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            mTraceThreadStart = SystemClock.currentThreadTimeMicro();
492f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
493f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
494f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        @Override
495f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        public void println(String x) {
496f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            // Ignore messages
497f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
498f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
499f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        @Override
500648bee18a1ccd362445d562729250ff5910f16a0Romain Guy        public void profile(Message message, long wallStart, long wallTime,
501648bee18a1ccd362445d562729250ff5910f16a0Romain Guy                long threadStart, long threadTime) {
502f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            Entry entry = new Entry();
503648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            entry.traceId = getTraceId(message);
504f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            entry.wallStart = wallStart;
505f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            entry.wallTime = wallTime;
506648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            entry.threadStart = threadStart;
507f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            entry.threadTime = threadTime;
508f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
509f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            mTraces.add(entry);
510f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
511f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
5129a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private int getTraceId(Message message) {
513648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            String name = message.getTarget().getMessageName(message);
5149a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            Integer traceId = mTraceNames.get(name);
515648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            if (traceId == null) {
5169a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                traceId = mTraceId++ << 4;
517648bee18a1ccd362445d562729250ff5910f16a0Romain Guy                mTraceNames.put(name, traceId);
518648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            }
519648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            return traceId;
520648bee18a1ccd362445d562729250ff5910f16a0Romain Guy        }
521648bee18a1ccd362445d562729250ff5910f16a0Romain Guy
522f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        void save() {
523f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            // Don't block the UI thread
524f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            new Thread(new Runnable() {
525f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                @Override
526f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                public void run() {
527f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                    saveTraces();
528f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                }
5297eabe55db6b113f83c2cefcd06812648927de877Romain Guy            }, "LooperProfiler[" + mPath + "]").start();
530f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
531f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
532f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        private void saveTraces() {
5339a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            FileOutputStream fos = new FileOutputStream(mFileDescriptor.getFileDescriptor());
534f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
535f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
536f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            try {
5379a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                writeHeader(out, mTraceWallStart, mTraceNames, mTraces);
5389a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                out.flush();
539648bee18a1ccd362445d562729250ff5910f16a0Romain Guy
5409a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                writeTraces(fos, out.size(), mTraceWallStart, mTraceThreadStart, mTraces);
541f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
5427eabe55db6b113f83c2cefcd06812648927de877Romain Guy                Log.d(LOG_TAG, "Looper traces ready: " + mPath);
543f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            } catch (IOException e) {
5449a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
545f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            } finally {
546f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                try {
547f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                    out.close();
548f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                } catch (IOException e) {
5499a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
5509a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                }
5519a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                try {
5529a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                    mFileDescriptor.close();
5539a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                } catch (IOException e) {
5549a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                    Log.e(LOG_TAG, "Could not write trace file " + mPath, e);
555f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy                }
556f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            }
557f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
5589a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5599a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void writeTraces(FileOutputStream out, long offset, long wallStart,
5609a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                long threadStart, ArrayList<Entry> entries) throws IOException {
5619a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5629a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            FileChannel channel = out.getChannel();
5639a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5649a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            // Header
5659a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            ByteBuffer buffer = ByteBuffer.allocateDirect(HEADER_SIZE);
5669a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.put(HEADER_MAGIC.getBytes());
5679a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
5689a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.putShort((short) TRACE_VERSION_NUMBER);    // version
5699a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.putShort((short) HEADER_SIZE);             // offset to data
5709a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.putLong(wallStart);                        // start time in usec
5719a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.putShort(HEADER_RECORD_SIZE);              // size of a record in bytes
5729a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            // padding to 32 bytes
5739a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            for (int i = 0; i < HEADER_SIZE - 18; i++) {
5749a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.put((byte) 0);
5759a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            }
5769a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5779a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer.flip();
5789a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            channel.position(offset).write(buffer);
5799a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5809a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            buffer = ByteBuffer.allocateDirect(14).order(ByteOrder.LITTLE_ENDIAN);
5819a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            for (Entry entry : entries) {
5829a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putShort((short) 1);   // we simulate only one thread
5839a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt(entry.traceId); // entering method
5849a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt((int) (entry.threadStart - threadStart));
5859a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt((int) (entry.wallStart - wallStart));
5869a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5879a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.flip();
5889a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                channel.write(buffer);
5899a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.clear();
5909a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5919a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putShort((short) 1);
5929a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt(entry.traceId | ACTION_EXIT_METHOD); // exiting method
5939a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt((int) (entry.threadStart + entry.threadTime - threadStart));
5949a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.putInt((int) (entry.wallStart + entry.wallTime - wallStart));
5959a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
5969a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.flip();
5979a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                channel.write(buffer);
5989a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                buffer.clear();
5999a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            }
6009a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6019a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            channel.close();
6029a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        }
6039a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6049a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void writeHeader(DataOutputStream out, long start,
6059a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                HashMap<String, Integer> names, ArrayList<Entry> entries) throws IOException {
6069a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6079a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            Entry last = entries.get(entries.size() - 1);
6089a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            long wallTotal = (last.wallStart + last.wallTime) - start;
6099a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6109a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            startSection("version", out);
6119a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue(null, Integer.toString(TRACE_VERSION_NUMBER), out);
6129a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("data-file-overflow", "false", out);
6139a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("clock", "dual", out);
6149a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("elapsed-time-usec", Long.toString(wallTotal), out);
6159a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("num-method-calls", Integer.toString(entries.size()), out);
6169a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("clock-call-overhead-nsec", "1", out);
6179a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addValue("vm", "dalvik", out);
6189a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6199a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            startSection("threads", out);
6209a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addThreadId(1, "main", out);
6219a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6229a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            startSection("methods", out);
6239a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            addMethods(names, out);
6249a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6259a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            startSection("end", out);
626648bee18a1ccd362445d562729250ff5910f16a0Romain Guy        }
6279a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6289a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void addMethods(HashMap<String, Integer> names, DataOutputStream out)
6299a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                throws IOException {
6309a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6319a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            for (Map.Entry<String, Integer> name : names.entrySet()) {
6329a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                out.writeBytes(String.format("0x%08x\tEventQueue\t%s\t()V\tLooper\t-2\n",
6339a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                        name.getValue(), name.getKey()));
6349a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            }
6359a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        }
6369a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6379a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void addThreadId(int id, String name, DataOutputStream out)
6389a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                throws IOException {
639648bee18a1ccd362445d562729250ff5910f16a0Romain Guy
6409a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            out.writeBytes(Integer.toString(id) + '\t' + name + '\n');
6419a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        }
6429a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6439a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void addValue(String name, String value, DataOutputStream out)
6449a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                throws IOException {
6459a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6469a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            if (name != null) {
6479a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy                out.writeBytes(name + "=");
6489a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            }
6499a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            out.writeBytes(value + '\n');
6509a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        }
6519a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy
6529a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy        private static void startSection(String name, DataOutputStream out) throws IOException {
6539a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            out.writeBytes("*" + name + '\n');
654f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
655f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
656f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        static class Entry {
6579a8c5cefcab3d5dec6ff63f0e99553e1aa9a4af8Romain Guy            int traceId;
658f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            long wallStart;
659f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            long wallTime;
660648bee18a1ccd362445d562729250ff5910f16a0Romain Guy            long threadStart;
661f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy            long threadTime;
662f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy        }
663f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    }
664f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy
665f9284695e8c10dad4daf3d2c84f607483bcb56caRomain Guy    /**
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened recycler traces. The trace records the type of
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * recycler action performed on the supplied view as well as a number of parameters.
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param parameters parameters depending on the type of the trace
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, RecyclerTraceType type, int... parameters) {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sRecyclerViews.contains(view)) {
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sRecyclerViews.add(view);
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int index = sRecyclerViews.indexOf(view);
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RecyclerTrace trace = new RecyclerTrace();
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.view = index;
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.type = type;
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.position = parameters[0];
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.indexOnScreen = parameters[1];
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.add(trace);
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view recycler can be traced at the same time. After calling this method, any
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopRecyclerTracing()} is invoked before.
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose recycler must be traced
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopRecyclerTracing()
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startRecyclerTracing(String prefix, View view) {
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView != null) {
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTracePrefix = prefix;
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = view;
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = new ArrayList<View>();
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = new LinkedList<RecyclerTrace>();
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view recycer tracing.
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's recycler.
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all of the views used by the recycler of the view supplied to
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startRecyclerTracing(String, View)}.
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startRecyclerTracing(String, View)
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopRecyclerTracing() {
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startRecyclerTracing() before" +
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopRecyclerTracing()!");
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
756c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump.mkdirs();
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (View view : sRecyclerViews) {
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final String name = view.getClass().getName();
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(name);
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.newLine();
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler content");
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
7781afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            if (recyclerDump.exists()) {
77988b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                //noinspection ResultOfMethodCallIgnored
7801afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                recyclerDump.delete();
7811afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            }
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final FileOutputStream file = new FileOutputStream(recyclerDump);
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final DataOutputStream out = new DataOutputStream(file);
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (RecyclerTrace trace : sRecyclerTraces) {
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.view);
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.type.ordinal());
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.position);
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.indexOnScreen);
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.flush();
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler traces");
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews.clear();
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = null;
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.clear();
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = null;
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = null;
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened traces file. The trace contains the class name
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and instance's hashcode of the specified view as well as the supplied trace type.
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, HierarchyTraceType type) {
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarchyTraces == null) {
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(type.name());
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(' ');
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(view.getClass().getName());
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write('@');
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.newLine();
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view hierarchy can be traced at the same time. After calling this method, any
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopHierarchyTracing()} is invoked before.
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's hierarchy.
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose hierarchy must be traced
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopHierarchyTracing()
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startHierarchyTracing(String prefix, View view) {
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot != null) {
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
864c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTracePrefix = prefix;
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8776dd005b48138708762bfade0081d031a2a4a3822Dianne Hackborn        sHierarhcyRoot = (ViewRootImpl) view.getRootView().getParent();
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view hierarchy tracing. This method closes the file
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing the view hierarchy of the view supplied to
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startHierarchyTracing(String, View)}.
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
890a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * @see #startHierarchyTracing(String, View)
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopHierarchyTracing() {
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot == null || sHierarchyTraces == null) {
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startHierarchyTracing() before" +
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopHierarchyTracing()!");
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.close();
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not write view traces");
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTraces = null;
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
912c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out;
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View view = sHierarhcyRoot.getView();
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view instanceof ViewGroup) {
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ViewGroup group = (ViewGroup) view;
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dumpViewHierarchy(group, out, 0);
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e("View", "Could not dump view hierarchy");
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = null;
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
937a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void dispatchCommand(View view, String command, String parameters,
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OutputStream clientStream) throws IOException {
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Paranoid but safe...
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        view = view.getRootView();
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dump(view, clientStream);
946223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
947223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            captureLayers(view, new DataOutputStream(clientStream));
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final String[] params = parameters.split(" ");
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                capture(view, clientStream, params[0]);
952ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase            } else if (REMOTE_COMMAND_OUTPUT_DISPLAYLIST.equalsIgnoreCase(command)) {
953ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase                outputDisplayList(view, params[0]);
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                invalidate(view, params[0]);
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                requestLayout(view, params[0]);
958c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
959c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                profile(view, clientStream, params[0]);
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
964c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static View findView(View root, String parameter) {
965c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        // Look by type/hashcode
966c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (parameter.indexOf('@') != -1) {
967c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String[] ids = parameter.split("@");
968c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String className = ids[0];
969236092a36216c79507ec19eb207831810caced19Romain Guy            final int hashCode = (int) Long.parseLong(ids[1], 16);
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
971c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            View view = root.getRootView();
972c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view instanceof ViewGroup) {
973c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                return findView((ViewGroup) view, className, hashCode);
974c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
975c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
976c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            // Look by id
977c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int id = root.getResources().getIdentifier(parameter, null, null);
978c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return root.getRootView().findViewById(id);
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void invalidate(View root, String parameter) {
985c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            view.postInvalidate();
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void requestLayout(View root, String parameter) {
992c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    view.requestLayout();
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1002c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void profile(View root, OutputStream clientStream, String parameter)
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1005c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
1006c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        BufferedWriter out = null;
1007c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
1008c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
1009c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1010c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view != null) {
1011f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                profileViewAndChildren(view, out);
1012f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            } else {
1013f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.write("-1 -1 -1");
1014f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.newLine();
1015f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            }
1016f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            out.write("DONE.");
1017f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            out.newLine();
1018f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        } catch (Exception e) {
1019f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            android.util.Log.w("View", "Problem profiling the view:", e);
1020f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        } finally {
1021f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            if (out != null) {
1022f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.close();
1023f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            }
1024f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        }
1025f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev    }
1026c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1027f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev    private static void profileViewAndChildren(final View view, BufferedWriter out)
1028f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            throws IOException {
1029c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        profileViewAndChildren(view, out, true);
1030c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev    }
1031f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev
1032c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev    private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
1033c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            throws IOException {
1034f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev
1035c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationMeasure =
1036c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
1037c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        view, new ViewOperation<Void>() {
1038c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Void[] pre() {
1039c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                forceLayout(view);
1040c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return null;
1041c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1042c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1043c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            private void forceLayout(View view) {
1044c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.forceLayout();
1045c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                if (view instanceof ViewGroup) {
1046c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    ViewGroup group = (ViewGroup) view;
1047c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    final int count = group.getChildCount();
1048c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    for (int i = 0; i < count; i++) {
1049c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        forceLayout(group.getChildAt(i));
1050c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    }
1051c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                }
1052c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1053c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1054c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Void... data) {
1055c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
1056c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1057c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1058c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Void... data) {
1059c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1060c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        })
1061c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        : 0;
1062c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationLayout =
1063c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation(
1064c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        view, new ViewOperation<Void>() {
1065c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Void[] pre() {
1066c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return null;
1067c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1068c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1069c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Void... data) {
1070c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
1071c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1072c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1073c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Void... data) {
1074c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1075c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        }) : 0;
1076c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationDraw =
1077bef337f516eafaee8f3891e22688b74388570317Konstantin Lopyrev                (root || !view.willNotDraw() || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(
1078bef337f516eafaee8f3891e22688b74388570317Konstantin Lopyrev                        view,
1079c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        new ViewOperation<Object>() {
1080c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Object[] pre() {
1081c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                final DisplayMetrics metrics =
1082daf98e941e140e8739458126640183b9f296a2abChet Haase                                        (view != null && view.getResources() != null) ?
1083daf98e941e140e8739458126640183b9f296a2abChet Haase                                                view.getResources().getDisplayMetrics() : null;
1084daf98e941e140e8739458126640183b9f296a2abChet Haase                                final Bitmap bitmap = metrics != null ?
1085c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        Bitmap.createBitmap(metrics.widthPixels,
1086daf98e941e140e8739458126640183b9f296a2abChet Haase                                                metrics.heightPixels, Bitmap.Config.RGB_565) : null;
1087daf98e941e140e8739458126640183b9f296a2abChet Haase                                final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null;
1088c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return new Object[] {
1089c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        bitmap, canvas
1090c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                };
1091c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1092c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1093c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Object... data) {
1094daf98e941e140e8739458126640183b9f296a2abChet Haase                                if (data[1] != null) {
1095daf98e941e140e8739458126640183b9f296a2abChet Haase                                    view.draw((Canvas) data[1]);
1096daf98e941e140e8739458126640183b9f296a2abChet Haase                                }
1097c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1098c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1099c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Object... data) {
11006311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn                                if (data[1] != null) {
11016311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn                                    ((Canvas) data[1]).setBitmap(null);
11026311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn                                }
1103daf98e941e140e8739458126640183b9f296a2abChet Haase                                if (data[0] != null) {
1104daf98e941e140e8739458126640183b9f296a2abChet Haase                                    ((Bitmap) data[0]).recycle();
1105daf98e941e140e8739458126640183b9f296a2abChet Haase                                }
1106c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1107c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        }) : 0;
1108f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationMeasure));
1109f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(' ');
1110f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationLayout));
1111f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(' ');
1112f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationDraw));
1113f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.newLine();
1114f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        if (view instanceof ViewGroup) {
1115f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            ViewGroup group = (ViewGroup) view;
1116f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            final int count = group.getChildCount();
1117f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            for (int i = 0; i < count; i++) {
1118c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                profileViewAndChildren(group.getChildAt(i), out, false);
1119c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1120c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1121c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1122c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1123c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    interface ViewOperation<T> {
1124c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        T[] pre();
1125c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void run(T... data);
1126c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void post(T... data);
1127c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1128c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1129c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final CountDownLatch latch = new CountDownLatch(1);
1131c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final long[] duration = new long[1];
1132c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1133c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        view.post(new Runnable() {
1134c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            public void run() {
1135c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                try {
1136c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    T[] data = operation.pre();
1137c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    long start = Debug.threadCpuTimeNanos();
113888b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                    //noinspection unchecked
1139c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.run(data);
1140c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    duration[0] = Debug.threadCpuTimeNanos() - start;
114188b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                    //noinspection unchecked
1142c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.post(data);
1143c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } finally {
1144c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    latch.countDown();
1145c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1146c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1147c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        });
1148c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1149c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
1150c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
1151c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                Log.w("View", "Could not complete the profiling of the view " + view);
1152c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                return -1;
1153c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            }
1154c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (InterruptedException e) {
1155c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Log.w("View", "Could not complete the profiling of the view " + view);
1156c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Thread.currentThread().interrupt();
1157c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return -1;
1158c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1159c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1160c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return duration[0];
1161c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1162c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1163223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static void captureLayers(View root, final DataOutputStream clientStream)
1164223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            throws IOException {
1165223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1166223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        try {
1167223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Rect outRect = new Rect();
1168223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            try {
1169223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                root.mAttachInfo.mSession.getDisplayFrame(root.mAttachInfo.mWindow, outRect);
1170223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            } catch (RemoteException e) {
1171223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                // Ignore
1172223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1173223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1174223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(outRect.width());
1175223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(outRect.height());
1176223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
117765554f27855ce1764123604b061b10346f8b8404Romain Guy            captureViewLayer(root, clientStream, true);
1178223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1179223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.write(2);
1180223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        } finally {
1181223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.close();
1182223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1183223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
1184223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
118565554f27855ce1764123604b061b10346f8b8404Romain Guy    private static void captureViewLayer(View view, DataOutputStream clientStream, boolean visible)
1186223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            throws IOException {
1187223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
118865554f27855ce1764123604b061b10346f8b8404Romain Guy        final boolean localVisible = view.getVisibility() == View.VISIBLE && visible;
118965554f27855ce1764123604b061b10346f8b8404Romain Guy
1190223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        if ((view.mPrivateFlags & View.SKIP_DRAW) != View.SKIP_DRAW) {
1191223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            final int id = view.getId();
1192223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            String name = view.getClass().getSimpleName();
1193223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            if (id != View.NO_ID) {
1194223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                name = resolveId(view.getContext(), id).toString();
1195223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1196223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1197223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.write(1);
1198223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeUTF(name);
119965554f27855ce1764123604b061b10346f8b8404Romain Guy            clientStream.writeByte(localVisible ? 1 : 0);
1200223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1201223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            int[] position = new int[2];
1202223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            // XXX: Should happen on the UI thread
1203223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            view.getLocationInWindow(position);
1204223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1205223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(position[0]);
1206223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(position[1]);
1207223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.flush();
1208223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1209223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Bitmap b = performViewCapture(view, true);
1210223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            if (b != null) {
1211223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                ByteArrayOutputStream arrayOut = new ByteArrayOutputStream(b.getWidth() *
1212223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                        b.getHeight() * 2);
1213223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                b.compress(Bitmap.CompressFormat.PNG, 100, arrayOut);
1214223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                clientStream.writeInt(arrayOut.size());
1215223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                arrayOut.writeTo(clientStream);
1216223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1217223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.flush();
1218223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1219223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1220223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        if (view instanceof ViewGroup) {
1221223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            ViewGroup group = (ViewGroup) view;
1222223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            int count = group.getChildCount();
1223223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1224223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            for (int i = 0; i < count; i++) {
122565554f27855ce1764123604b061b10346f8b8404Romain Guy                captureViewLayer(group.getChildAt(i), clientStream, localVisible);
1226223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1227223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1228223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
1229223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1230ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase    private static void outputDisplayList(View root, String parameter) throws IOException {
1231ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase        final View view = findView(root, parameter);
12326dd005b48138708762bfade0081d031a2a4a3822Dianne Hackborn        view.getViewRootImpl().outputDisplayList(view);
1233ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase    }
1234ed30fd8e9a2d65ee5c8520de55b0089c219f390cChet Haase
1235c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void capture(View root, final OutputStream clientStream, String parameter)
1236c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1237c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1238c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View captureView = findView(root, parameter);
1239223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        Bitmap b = performViewCapture(captureView, false);
124043b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev
124143b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        if (b == null) {
1242223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Log.w("View", "Failed to create capture bitmap!");
124343b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            // Send an empty one so that it doesn't get stuck waiting for
124443b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            // something.
124543b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
124643b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        }
124743b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev
124843b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        BufferedOutputStream out = null;
124943b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        try {
125043b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            out = new BufferedOutputStream(clientStream, 32 * 1024);
125143b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b.compress(Bitmap.CompressFormat.PNG, 100, out);
125243b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            out.flush();
125343b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        } finally {
125443b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            if (out != null) {
125543b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev                out.close();
125643b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            }
125743b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b.recycle();
1258223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1259223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1261223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static Bitmap performViewCapture(final View captureView, final boolean skpiChildren) {
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (captureView != null) {
1263c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final CountDownLatch latch = new CountDownLatch(1);
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Bitmap[] cache = new Bitmap[1];
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1266223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            captureView.post(new Runnable() {
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
1269958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        cache[0] = captureView.createSnapshot(
1270223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                                Bitmap.Config.ARGB_8888, 0, skpiChildren);
1271958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                    } catch (OutOfMemoryError e) {
127288b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                        Log.w("View", "Out of memory for bitmap");
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        latch.countDown();
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
1281223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                return cache[0];
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InterruptedException e) {
1283c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Log.w("View", "Could not complete the capture of the view " + captureView);
1284c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Thread.currentThread().interrupt();
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1287223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1288223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        return null;
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dump(View root, OutputStream clientStream) throws IOException {
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out = null;
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
129438e951b62ed956501b11f0715e23e3bf70942f6dRomain Guy            out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View view = root.getRootView();
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ViewGroup group = (ViewGroup) view;
1298105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write("DONE.");
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
1302c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
1303c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem dumping the view:", e);
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (out != null) {
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View findView(ViewGroup group, String className, int hashCode) {
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isRequestedView(group, className, hashCode)) {
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return group;
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final View found = findView((ViewGroup) view, className, hashCode);
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (found != null) {
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return found;
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (isRequestedView(view, className, hashCode)) {
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return view;
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean isRequestedView(View view, String className, int hashCode) {
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1336105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            BufferedWriter out, int level) {
1338105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (!dumpViewWithProperties(context, group, out, level)) {
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
1346105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1348105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewWithProperties(context, view, out, level + 1);
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1353105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static boolean dumpViewWithProperties(Context context, View view,
1354105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, int level) {
1355105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(' ');
1364105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            dumpViewProperties(context, view, out);
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] getExportedPropertyFields(Class<?> klass) {
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sFieldsForClasses == null) {
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1377c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
1378c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
1379c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1380c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getDeclaredFields();
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(ExportedProperty.class)) {
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
139788b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] getExportedPropertyMethods(Class<?> klass) {
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sMethodsForClasses == null) {
1409c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
1410c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1411c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
1412c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1414c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getDeclaredMethods();
1424a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1427a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            final Method method = methods[i];
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
1429c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.isAnnotationPresent(ExportedProperty.class) &&
1430c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.getReturnType() != Void.class) {
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
143388b4f153e2be863d79f4d7f68af95e0f9375e4ecRomain Guy                sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1443105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1444105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out) throws IOException {
1445105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1446105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        dumpViewProperties(context, view, out, "");
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1450105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, String prefix) throws IOException {
1451105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        do {
1455105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportFields(context, view, out, klass, prefix);
1456105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportMethods(context, view, out, klass, prefix);
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            klass = klass.getSuperclass();
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } while (klass != Object.class);
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1460a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1461105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportMethods(Context context, Object view, BufferedWriter out,
1462105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = getExportedPropertyMethods(klass);
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // TODO: This should happen on the UI thread
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(view, (Object[]) null);
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
1474bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                final ExportedProperty property = sAnnotations.get(method);
147591a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                String categoryPrefix =
147691a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        property.category().length() != 0 ? property.category() + ":" : "";
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (returnType == int.class) {
1479bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1480105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = (Integer) methodValue;
1482105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        methodValue = resolveId(context, id);
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1484809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1485809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1486809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = (Integer) methodValue;
148791a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                            final String valuePrefix =
148891a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                    categoryPrefix + prefix + method.getName() + '_';
1489809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1490809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1491809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = (Integer) methodValue;
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            boolean mapped = false;
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapper = mapping[j];
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapper.from() == intValue) {
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    methodValue = mapper.to();
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mapped = true;
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (!mapped) {
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                methodValue = intValue;
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1511c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (returnType == int[].class) {
1512c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) methodValue;
151391a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                    final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
1514c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "()";
1515c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1516105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
1517bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1518bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                    // Probably want to return here, same as for fields.
1519bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                    return;
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!returnType.isPrimitive()) {
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
1522105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        dumpViewProperties(context, methodValue, out, prefix + property.prefix());
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
15259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
152791a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InvocationTargetException e) {
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1534105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportFields(Context context, Object view, BufferedWriter out,
1535105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
1536105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = getExportedPropertyFields(klass);
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = null;
15469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> type = field.getType();
1547bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                final ExportedProperty property = sAnnotations.get(field);
154891a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                String categoryPrefix =
154991a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        property.category().length() != 0 ? property.category() + ":" : "";
15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (type == int.class) {
1552bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1553105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = field.getInt(view);
1555105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        fieldValue = resolveId(context, id);
15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1557809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1558809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1559809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = field.getInt(view);
156091a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                            final String valuePrefix =
156191a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                    categoryPrefix + prefix + field.getName() + '_';
1562809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1563809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1564809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
15679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = field.getInt(view);
15689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
15699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
15709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapped = mapping[j];
15719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapped.from() == intValue) {
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    fieldValue = mapped.to();
15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (fieldValue == null) {
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                fieldValue = intValue;
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1582c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (type == int[].class) {
1583c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) field.get(view);
158491a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                    final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
1585c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "";
1586c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1587105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
1588c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1589c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // We exit here!
1590c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    return;
15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!type.isPrimitive()) {
15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
159391a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        dumpViewProperties(context, field.get(view), out, prefix
159491a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                + property.prefix());
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue == null) {
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fieldValue = field.get(view);
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
160391a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeEntry(BufferedWriter out, String prefix, String name,
1610c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String suffix, Object value) throws IOException {
1611c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1612c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(prefix);
1613c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(name);
1614c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(suffix);
1615c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write("=");
1616c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        writeValue(out, value);
1617c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(' ');
1618c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1619c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1620809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    private static void exportUnrolledFlags(BufferedWriter out, FlagToString[] mapping,
1621809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            int intValue, String prefix) throws IOException {
1622809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1623809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        final int count = mapping.length;
1624809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        for (int j = 0; j < count; j++) {
1625809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final FlagToString flagMapping = mapping[j];
1626809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final boolean ifTrue = flagMapping.outputIf();
16275bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final int maskResult = intValue & flagMapping.mask();
16285bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final boolean test = maskResult == flagMapping.equals();
1629809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            if ((test && ifTrue) || (!test && !ifTrue)) {
1630809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                final String name = flagMapping.name();
16315bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy                final String value = "0x" + Integer.toHexString(maskResult);
1632809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                writeEntry(out, prefix, name, "", value);
1633809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            }
1634809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        }
1635809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
1636809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1637105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportUnrolledArray(Context context, BufferedWriter out,
1638c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            ExportedProperty property, int[] array, String prefix, String suffix)
1639c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1640c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1641c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] indexMapping = property.indexMapping();
1642c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasIndexMapping = indexMapping.length > 0;
1643c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1644c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] mapping = property.mapping();
1645c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasMapping = mapping.length > 0;
1646c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1647105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final boolean resolveId = property.resolveId() && context != null;
1648c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final int valuesCount = array.length;
1649c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1650c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        for (int j = 0; j < valuesCount; j++) {
1651c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String name;
1652a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            String value = null;
1653c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1654c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int intValue = array[j];
1655c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1656c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            name = String.valueOf(j);
1657c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasIndexMapping) {
1658c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = indexMapping.length;
1659c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1660c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = indexMapping[k];
1661c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == j) {
1662c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        name = mapped.to();
1663c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1664c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1665c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1666c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1667c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1668c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasMapping) {
1669c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = mapping.length;
1670c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1671c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = mapping[k];
1672c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == intValue) {
1673c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        value = mapped.to();
1674c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1675c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1676c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1677c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1678c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1679c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (resolveId) {
1680a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                if (value == null) value = (String) resolveId(context, intValue);
1681a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            } else {
1682a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                value = String.valueOf(intValue);
1683c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1684c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1685c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            writeEntry(out, prefix, name, suffix, value);
1686c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1687c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1688c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1689237c1ceea36024cf4194212e713806e3ce8a1c49Romain Guy    static Object resolveId(Context context, int id) {
1690c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        Object fieldValue;
1691105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final Resources resources = context.getResources();
1692c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (id >= 0) {
1693c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            try {
1694c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = resources.getResourceTypeName(id) + '/' +
1695c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        resources.getResourceEntryName(id);
1696c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } catch (Resources.NotFoundException e) {
1697c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = "id/0x" + Integer.toHexString(id);
1698c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1699c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1700c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            fieldValue = "NO_ID";
1701c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1702c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return fieldValue;
1703c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1704c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1705c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeValue(BufferedWriter out, Object value) throws IOException {
1706c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (value != null) {
1707c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String output = value.toString().replace("\n", "\\n");
1708c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(String.valueOf(output.length()));
1709c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(",");
1710c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(output);
1711c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1712c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write("4,null");
1713c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1714c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1715c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
17169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!dumpView(group, out, level)) {
17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
17199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
17229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
17239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchy((ViewGroup) view, out, level + 1);
17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpView(view, out, level + 1);
17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean dumpView(Object view, BufferedWriter out, int level) {
17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
17349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
17389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
17409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
17419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
17429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
17439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
17449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
17469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
17499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewFieldsForClasses == null) {
17509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
17519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
17579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getFields();
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
17669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewMethodsForClasses == null) {
17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
17829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
17849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getMethods();
1790a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1793a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            final Method method = methods[i];
17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.isAnnotationPresent(CapturedViewProperty.class) &&
17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.getReturnType() != Void.class) {
17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
17989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
18039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
18069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1807a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1808a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    private static String capturedViewExportMethods(Object obj, Class<?> klass,
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String prefix) {
18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1814a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = capturedViewGetPropertyMethods(klass);
18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(obj, (Object[]) null);
18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
1824a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (property.retrieveReturn()) {
18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //we are interested in the second level data only
18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
1829a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                } else {
18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(prefix);
18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(method.getName());
18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("()=");
1833a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (methodValue != null) {
1835a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                        final String value = methodValue.toString().replace("\n", "\\n");
1836a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                        sb.append(value);
18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append("null");
18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("; ");
18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (IllegalAccessException e) {
1843a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                  //Exception IllegalAccess, it is OK here
18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (InvocationTargetException e) {
1846a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                  //Exception InvocationTarget, it is OK here
18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
1848a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy              }
1849a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        }
18509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
1854a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
18579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1858a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
18609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = capturedViewGetPropertyFields(klass);
18619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
18639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
18649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
18659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
18669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = field.get(obj);
18679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(prefix);
18699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(field.getName());
18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("=");
18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue != null) {
18739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final String value = fieldValue.toString().replace("\n", "\\n");
18749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(value);
18759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("null");
18779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(' ');
18799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
1880a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                //Exception IllegalAccess, it is OK here
18819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //we simply ignore this field
18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1886a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1888a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * Dump view info for id based instrument test generation
18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (and possibly further data analysis). The results are dumped
1890a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * to the log.
18919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag for log
18929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view for dump
18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1894a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    public static void dumpCapturedView(String tag, Object view) {
18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder(klass.getName() + ": ");
18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportFields(view, klass, ""));
1898a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        sb.append(capturedViewExportMethods(view, klass, ""));
1899a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        Log.d(tag, sb.toString());
19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1902