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