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