1 /* 2 * Copyright (C) 2016 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 com.android.internal.util; 18 19 import android.os.Message; 20 import android.util.Log; 21 import android.util.SparseArray; 22 23 import java.lang.reflect.Field; 24 import java.lang.reflect.Modifier; 25 26 /** 27 * Static utility class for dealing with {@link Message} objects. 28 */ 29 public class MessageUtils { 30 31 private static final String TAG = MessageUtils.class.getSimpleName(); 32 private static final boolean DBG = false; 33 34 /** Thrown when two different constants have the same value. */ 35 public static class DuplicateConstantError extends Error { DuplicateConstantError()36 private DuplicateConstantError() {} DuplicateConstantError(String name1, String name2, int value)37 public DuplicateConstantError(String name1, String name2, int value) { 38 super(String.format("Duplicate constant value: both %s and %s = %d", 39 name1, name2, value)); 40 } 41 } 42 43 /** 44 * Finds the names of integer constants. Searches the specified {@code classes}, looking for 45 * accessible static integer fields whose names begin with one of the specified {@prefixes}. 46 * 47 * @param classes the classes to examine. 48 * @prefixes only consider fields names starting with one of these prefixes. 49 * @return a {@link SparseArray} mapping integer constants to their names. 50 */ findMessageNames(Class[] classes, String[] prefixes)51 public static SparseArray<String> findMessageNames(Class[] classes, String[] prefixes) { 52 SparseArray<String> messageNames = new SparseArray<>(); 53 for (Class c : classes) { 54 String className = c.getName(); 55 if (DBG) Log.d(TAG, "Examining class " + className); 56 57 Field[] fields; 58 try { 59 fields = c.getDeclaredFields(); 60 } catch (SecurityException e) { 61 Log.e(TAG, "Can't list fields of class " + className); 62 continue; 63 } 64 65 for (Field field : fields) { 66 int modifiers = field.getModifiers(); 67 if (!Modifier.isStatic(modifiers) | !Modifier.isFinal(modifiers)) { 68 continue; 69 } 70 71 String name = field.getName(); 72 for (String prefix : prefixes) { 73 // Does this look like a constant? 74 if (!name.startsWith(prefix)) { 75 continue; 76 } 77 78 try { 79 // TODO: can we have the caller try to access the field instead, so we don't 80 // expose constants it does not have access to? 81 field.setAccessible(true); 82 83 // Fetch the constant's value. 84 int value; 85 try { 86 value = field.getInt(null); 87 } catch (IllegalArgumentException | ExceptionInInitializerError e) { 88 // The field is not an integer (or short or byte), or c's static 89 // initializer failed and we have no idea what its value is. 90 // Either way, give up on this field. 91 break; 92 } 93 94 // Check for duplicate values. 95 String previousName = messageNames.get(value); 96 if (previousName != null && !previousName.equals(name)) { 97 throw new DuplicateConstantError(name, previousName, value); 98 } 99 100 messageNames.put(value, name); 101 if (DBG) { 102 Log.d(TAG, String.format("Found constant: %s.%s = %d", 103 className, name, value)); 104 } 105 } catch (SecurityException | IllegalAccessException e) { 106 // Not allowed to make the field accessible, or no access. Ignore. 107 continue; 108 } 109 } 110 } 111 } 112 return messageNames; 113 } 114 115 /** 116 * Default prefixes for constants. 117 */ 118 public static final String[] DEFAULT_PREFIXES = {"CMD_", "EVENT_"}; 119 120 /** 121 * Finds the names of integer constants. Searches the specified {@code classes}, looking for 122 * accessible static integer values whose names begin with {@link #DEFAULT_PREFIXES}. 123 * 124 * @param classNames the classes to examine. 125 * @prefixes only consider fields names starting with one of these prefixes. 126 * @return a {@link SparseArray} mapping integer constants to their names. 127 */ findMessageNames(Class[] classNames)128 public static SparseArray<String> findMessageNames(Class[] classNames) { 129 return findMessageNames(classNames, DEFAULT_PREFIXES); 130 } 131 } 132 133