1 /*
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 
17 package android.util;
18 
19 import java.io.PrintWriter;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.util.Locale;
25 
26 /**
27  * <p>Various utilities for debugging and logging.</p>
28  */
29 public class DebugUtils {
DebugUtils()30     /** @hide */ public DebugUtils() {}
31 
32     /**
33      * <p>Filters objects against the <code>ANDROID_OBJECT_FILTER</code>
34      * environment variable. This environment variable can filter objects
35      * based on their class name and attribute values.</p>
36      *
37      * <p>Here is the syntax for <code>ANDROID_OBJECT_FILTER</code>:</p>
38      *
39      * <p><code>ClassName@attribute1=value1@attribute2=value2...</code></p>
40      *
41      * <p>Examples:</p>
42      * <ul>
43      * <li>Select TextView instances: <code>TextView</code></li>
44      * <li>Select TextView instances of text "Loading" and bottom offset of 22:
45      * <code>TextView@text=Loading.*@bottom=22</code></li>
46      * </ul>
47      *
48      * <p>The class name and the values are regular expressions.</p>
49      *
50      * <p>This class is useful for debugging and logging purpose:</p>
51      * <pre>
52      * if (DEBUG) {
53      *   if (DebugUtils.isObjectSelected(childView) && LOGV_ENABLED) {
54      *     Log.v(TAG, "Object " + childView + " logged!");
55      *   }
56      * }
57      * </pre>
58      *
59      * <p><strong>NOTE</strong>: This method is very expensive as it relies
60      * heavily on regular expressions and reflection. Calls to this method
61      * should always be stripped out of the release binaries and avoided
62      * as much as possible in debug mode.</p>
63      *
64      * @param object any object to match against the ANDROID_OBJECT_FILTER
65      *        environement variable
66      * @return true if object is selected by the ANDROID_OBJECT_FILTER
67      *         environment variable, false otherwise
68      */
isObjectSelected(Object object)69     public static boolean isObjectSelected(Object object) {
70         boolean match = false;
71         String s = System.getenv("ANDROID_OBJECT_FILTER");
72         if (s != null && s.length() > 0) {
73             String[] selectors = s.split("@");
74             // first selector == class name
75             if (object.getClass().getSimpleName().matches(selectors[0])) {
76                 // check potential attributes
77                 for (int i = 1; i < selectors.length; i++) {
78                     String[] pair = selectors[i].split("=");
79                     Class<?> klass = object.getClass();
80                     try {
81                         Method declaredMethod = null;
82                         Class<?> parent = klass;
83                         do {
84                             declaredMethod = parent.getDeclaredMethod("get" +
85                                     pair[0].substring(0, 1).toUpperCase(Locale.ROOT) +
86                                     pair[0].substring(1),
87                                     (Class[]) null);
88                         } while ((parent = klass.getSuperclass()) != null &&
89                                 declaredMethod == null);
90 
91                         if (declaredMethod != null) {
92                             Object value = declaredMethod
93                                     .invoke(object, (Object[])null);
94                             match |= (value != null ?
95                                     value.toString() : "null").matches(pair[1]);
96                         }
97                     } catch (NoSuchMethodException e) {
98                         e.printStackTrace();
99                     } catch (IllegalAccessException e) {
100                         e.printStackTrace();
101                     } catch (InvocationTargetException e) {
102                         e.printStackTrace();
103                     }
104                 }
105             }
106         }
107         return match;
108     }
109 
110     /** @hide */
buildShortClassTag(Object cls, StringBuilder out)111     public static void buildShortClassTag(Object cls, StringBuilder out) {
112         if (cls == null) {
113             out.append("null");
114         } else {
115             String simpleName = cls.getClass().getSimpleName();
116             if (simpleName == null || simpleName.isEmpty()) {
117                 simpleName = cls.getClass().getName();
118                 int end = simpleName.lastIndexOf('.');
119                 if (end > 0) {
120                     simpleName = simpleName.substring(end+1);
121                 }
122             }
123             out.append(simpleName);
124             out.append('{');
125             out.append(Integer.toHexString(System.identityHashCode(cls)));
126         }
127     }
128 
129     /** @hide */
printSizeValue(PrintWriter pw, long number)130     public static void printSizeValue(PrintWriter pw, long number) {
131         float result = number;
132         String suffix = "";
133         if (result > 900) {
134             suffix = "KB";
135             result = result / 1024;
136         }
137         if (result > 900) {
138             suffix = "MB";
139             result = result / 1024;
140         }
141         if (result > 900) {
142             suffix = "GB";
143             result = result / 1024;
144         }
145         if (result > 900) {
146             suffix = "TB";
147             result = result / 1024;
148         }
149         if (result > 900) {
150             suffix = "PB";
151             result = result / 1024;
152         }
153         String value;
154         if (result < 1) {
155             value = String.format("%.2f", result);
156         } else if (result < 10) {
157             value = String.format("%.1f", result);
158         } else if (result < 100) {
159             value = String.format("%.0f", result);
160         } else {
161             value = String.format("%.0f", result);
162         }
163         pw.print(value);
164         pw.print(suffix);
165     }
166 
167     /** @hide */
sizeValueToString(long number, StringBuilder outBuilder)168     public static String sizeValueToString(long number, StringBuilder outBuilder) {
169         if (outBuilder == null) {
170             outBuilder = new StringBuilder(32);
171         }
172         float result = number;
173         String suffix = "";
174         if (result > 900) {
175             suffix = "KB";
176             result = result / 1024;
177         }
178         if (result > 900) {
179             suffix = "MB";
180             result = result / 1024;
181         }
182         if (result > 900) {
183             suffix = "GB";
184             result = result / 1024;
185         }
186         if (result > 900) {
187             suffix = "TB";
188             result = result / 1024;
189         }
190         if (result > 900) {
191             suffix = "PB";
192             result = result / 1024;
193         }
194         String value;
195         if (result < 1) {
196             value = String.format("%.2f", result);
197         } else if (result < 10) {
198             value = String.format("%.1f", result);
199         } else if (result < 100) {
200             value = String.format("%.0f", result);
201         } else {
202             value = String.format("%.0f", result);
203         }
204         outBuilder.append(value);
205         outBuilder.append(suffix);
206         return outBuilder.toString();
207     }
208 
209     /**
210      * Use prefixed constants (static final values) on given class to turn value
211      * into human-readable string.
212      *
213      * @hide
214      */
valueToString(Class<?> clazz, String prefix, int value)215     public static String valueToString(Class<?> clazz, String prefix, int value) {
216         for (Field field : clazz.getDeclaredFields()) {
217             final int modifiers = field.getModifiers();
218             if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
219                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
220                 try {
221                     if (value == field.getInt(null)) {
222                         return field.getName().substring(prefix.length());
223                     }
224                 } catch (IllegalAccessException ignored) {
225                 }
226             }
227         }
228         return Integer.toString(value);
229     }
230 
231     /**
232      * Use prefixed constants (static final values) on given class to turn flags
233      * into human-readable string.
234      *
235      * @hide
236      */
flagsToString(Class<?> clazz, String prefix, int flags)237     public static String flagsToString(Class<?> clazz, String prefix, int flags) {
238         final StringBuilder res = new StringBuilder();
239 
240         for (Field field : clazz.getDeclaredFields()) {
241             final int modifiers = field.getModifiers();
242             if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
243                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
244                 try {
245                     final int value = field.getInt(null);
246                     if ((flags & value) != 0) {
247                         flags &= ~value;
248                         res.append(field.getName().substring(prefix.length())).append('|');
249                     }
250                 } catch (IllegalAccessException ignored) {
251                 }
252             }
253         }
254         if (flags != 0 || res.length() == 0) {
255             res.append(Integer.toHexString(flags));
256         } else {
257             res.deleteCharAt(res.length() - 1);
258         }
259         return res.toString();
260     }
261 }
262