blob: af73a16e012a3fdd43370417658a83389c216c95 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010019import android.annotation.UnsupportedAppUsage;
Mathew Inwood31755f92018-12-20 13:53:36 +000020import android.os.Build;
Dianne Hackbornb9a5e4a2015-03-03 17:04:12 -080021import java.io.PrintWriter;
Jeff Sharkey48877892015-03-18 11:27:19 -070022import java.lang.reflect.Field;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import java.lang.reflect.InvocationTargetException;
Jeff Sharkey48877892015-03-18 11:27:19 -070024import java.lang.reflect.Method;
25import java.lang.reflect.Modifier;
Elliott Hughescb64d432013-08-02 10:00:44 -070026import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027
28/**
Jesse Wilsona0f8bc52011-02-24 10:44:33 -080029 * <p>Various utilities for debugging and logging.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030 */
31public class DebugUtils {
Jesse Wilsona0f8bc52011-02-24 10:44:33 -080032 /** @hide */ public DebugUtils() {}
33
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034 /**
35 * <p>Filters objects against the <code>ANDROID_OBJECT_FILTER</code>
36 * environment variable. This environment variable can filter objects
37 * based on their class name and attribute values.</p>
38 *
39 * <p>Here is the syntax for <code>ANDROID_OBJECT_FILTER</code>:</p>
40 *
41 * <p><code>ClassName@attribute1=value1@attribute2=value2...</code></p>
42 *
43 * <p>Examples:</p>
44 * <ul>
45 * <li>Select TextView instances: <code>TextView</code></li>
46 * <li>Select TextView instances of text "Loading" and bottom offset of 22:
47 * <code>TextView@text=Loading.*@bottom=22</code></li>
48 * </ul>
49 *
50 * <p>The class name and the values are regular expressions.</p>
51 *
52 * <p>This class is useful for debugging and logging purpose:</p>
53 * <pre>
Dave Bort34f1ca72009-04-13 17:18:45 -070054 * if (DEBUG) {
55 * if (DebugUtils.isObjectSelected(childView) && LOGV_ENABLED) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 * Log.v(TAG, "Object " + childView + " logged!");
57 * }
58 * }
59 * </pre>
60 *
61 * <p><strong>NOTE</strong>: This method is very expensive as it relies
62 * heavily on regular expressions and reflection. Calls to this method
63 * should always be stripped out of the release binaries and avoided
64 * as much as possible in debug mode.</p>
65 *
66 * @param object any object to match against the ANDROID_OBJECT_FILTER
67 * environement variable
68 * @return true if object is selected by the ANDROID_OBJECT_FILTER
69 * environment variable, false otherwise
70 */
71 public static boolean isObjectSelected(Object object) {
72 boolean match = false;
73 String s = System.getenv("ANDROID_OBJECT_FILTER");
74 if (s != null && s.length() > 0) {
75 String[] selectors = s.split("@");
76 // first selector == class name
77 if (object.getClass().getSimpleName().matches(selectors[0])) {
78 // check potential attributes
79 for (int i = 1; i < selectors.length; i++) {
80 String[] pair = selectors[i].split("=");
81 Class<?> klass = object.getClass();
82 try {
83 Method declaredMethod = null;
84 Class<?> parent = klass;
85 do {
86 declaredMethod = parent.getDeclaredMethod("get" +
Elliott Hughescb64d432013-08-02 10:00:44 -070087 pair[0].substring(0, 1).toUpperCase(Locale.ROOT) +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 pair[0].substring(1),
89 (Class[]) null);
90 } while ((parent = klass.getSuperclass()) != null &&
91 declaredMethod == null);
92
93 if (declaredMethod != null) {
94 Object value = declaredMethod
95 .invoke(object, (Object[])null);
96 match |= (value != null ?
97 value.toString() : "null").matches(pair[1]);
98 }
99 } catch (NoSuchMethodException e) {
100 e.printStackTrace();
101 } catch (IllegalAccessException e) {
102 e.printStackTrace();
103 } catch (InvocationTargetException e) {
104 e.printStackTrace();
105 }
106 }
107 }
108 }
109 return match;
110 }
111
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800112 /** @hide */
Mathew Inwood31755f92018-12-20 13:53:36 +0000113 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800114 public static void buildShortClassTag(Object cls, StringBuilder out) {
115 if (cls == null) {
116 out.append("null");
117 } else {
118 String simpleName = cls.getClass().getSimpleName();
119 if (simpleName == null || simpleName.isEmpty()) {
120 simpleName = cls.getClass().getName();
121 int end = simpleName.lastIndexOf('.');
122 if (end > 0) {
123 simpleName = simpleName.substring(end+1);
124 }
125 }
126 out.append(simpleName);
127 out.append('{');
128 out.append(Integer.toHexString(System.identityHashCode(cls)));
129 }
130 }
131
Dianne Hackbornb9a5e4a2015-03-03 17:04:12 -0800132 /** @hide */
133 public static void printSizeValue(PrintWriter pw, long number) {
134 float result = number;
135 String suffix = "";
136 if (result > 900) {
137 suffix = "KB";
138 result = result / 1024;
139 }
140 if (result > 900) {
141 suffix = "MB";
142 result = result / 1024;
143 }
144 if (result > 900) {
145 suffix = "GB";
146 result = result / 1024;
147 }
148 if (result > 900) {
149 suffix = "TB";
150 result = result / 1024;
151 }
152 if (result > 900) {
153 suffix = "PB";
154 result = result / 1024;
155 }
156 String value;
157 if (result < 1) {
158 value = String.format("%.2f", result);
159 } else if (result < 10) {
160 value = String.format("%.1f", result);
161 } else if (result < 100) {
162 value = String.format("%.0f", result);
163 } else {
164 value = String.format("%.0f", result);
165 }
166 pw.print(value);
167 pw.print(suffix);
168 }
169
170 /** @hide */
171 public static String sizeValueToString(long number, StringBuilder outBuilder) {
172 if (outBuilder == null) {
173 outBuilder = new StringBuilder(32);
174 }
175 float result = number;
176 String suffix = "";
177 if (result > 900) {
178 suffix = "KB";
179 result = result / 1024;
180 }
181 if (result > 900) {
182 suffix = "MB";
183 result = result / 1024;
184 }
185 if (result > 900) {
186 suffix = "GB";
187 result = result / 1024;
188 }
189 if (result > 900) {
190 suffix = "TB";
191 result = result / 1024;
192 }
193 if (result > 900) {
194 suffix = "PB";
195 result = result / 1024;
196 }
197 String value;
198 if (result < 1) {
199 value = String.format("%.2f", result);
200 } else if (result < 10) {
201 value = String.format("%.1f", result);
202 } else if (result < 100) {
203 value = String.format("%.0f", result);
204 } else {
205 value = String.format("%.0f", result);
206 }
207 outBuilder.append(value);
208 outBuilder.append(suffix);
209 return outBuilder.toString();
210 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700211
212 /**
213 * Use prefixed constants (static final values) on given class to turn value
214 * into human-readable string.
215 *
216 * @hide
217 */
218 public static String valueToString(Class<?> clazz, String prefix, int value) {
219 for (Field field : clazz.getDeclaredFields()) {
220 final int modifiers = field.getModifiers();
221 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
222 && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
223 try {
224 if (value == field.getInt(null)) {
Eugene Susla8f012412018-03-16 14:35:31 -0700225 return constNameWithoutPrefix(prefix, field);
Jeff Sharkey48877892015-03-18 11:27:19 -0700226 }
227 } catch (IllegalAccessException ignored) {
228 }
229 }
230 }
231 return Integer.toString(value);
232 }
233
234 /**
235 * Use prefixed constants (static final values) on given class to turn flags
236 * into human-readable string.
237 *
238 * @hide
239 */
240 public static String flagsToString(Class<?> clazz, String prefix, int flags) {
241 final StringBuilder res = new StringBuilder();
Eugene Susla8f012412018-03-16 14:35:31 -0700242 boolean flagsWasZero = flags == 0;
Jeff Sharkey48877892015-03-18 11:27:19 -0700243
244 for (Field field : clazz.getDeclaredFields()) {
245 final int modifiers = field.getModifiers();
246 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
247 && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
248 try {
249 final int value = field.getInt(null);
Eugene Susla8f012412018-03-16 14:35:31 -0700250 if (value == 0 && flagsWasZero) {
251 return constNameWithoutPrefix(prefix, field);
252 }
Jeff Sharkey48877892015-03-18 11:27:19 -0700253 if ((flags & value) != 0) {
254 flags &= ~value;
Eugene Susla8f012412018-03-16 14:35:31 -0700255 res.append(constNameWithoutPrefix(prefix, field)).append('|');
Jeff Sharkey48877892015-03-18 11:27:19 -0700256 }
257 } catch (IllegalAccessException ignored) {
258 }
259 }
260 }
261 if (flags != 0 || res.length() == 0) {
262 res.append(Integer.toHexString(flags));
263 } else {
264 res.deleteCharAt(res.length() - 1);
265 }
266 return res.toString();
267 }
Eugene Susla8f012412018-03-16 14:35:31 -0700268
269 private static String constNameWithoutPrefix(String prefix, Field field) {
270 return field.getName().substring(prefix.length());
271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272}