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 46 * {@code prefixes}. 47 * 48 * @param classes the classes to examine. 49 * @param prefixes only consider fields names starting with one of these prefixes. 50 * @return a {@link SparseArray} mapping integer constants to their names. 51 */ findMessageNames(Class[] classes, String[] prefixes)52 public static SparseArray<String> findMessageNames(Class[] classes, String[] prefixes) { 53 SparseArray<String> messageNames = new SparseArray<>(); 54 for (Class c : classes) { 55 String className = c.getName(); 56 if (DBG) Log.d(TAG, "Examining class " + className); 57 58 Field[] fields; 59 try { 60 fields = c.getDeclaredFields(); 61 } catch (SecurityException e) { 62 Log.e(TAG, "Can't list fields of class " + className); 63 continue; 64 } 65 66 for (Field field : fields) { 67 int modifiers = field.getModifiers(); 68 if (!Modifier.isStatic(modifiers) | !Modifier.isFinal(modifiers)) { 69 continue; 70 } 71 72 String name = field.getName(); 73 for (String prefix : prefixes) { 74 // Does this look like a constant? 75 if (!name.startsWith(prefix)) { 76 continue; 77 } 78 79 try { 80 // TODO: can we have the caller try to access the field instead, so we don't 81 // expose constants it does not have access to? 82 field.setAccessible(true); 83 84 // Fetch the constant's value. 85 int value; 86 try { 87 value = field.getInt(null); 88 } catch (IllegalArgumentException | ExceptionInInitializerError e) { 89 // The field is not an integer (or short or byte), or c's static 90 // initializer failed and we have no idea what its value is. 91 // Either way, give up on this field. 92 break; 93 } 94 95 // Check for duplicate values. 96 String previousName = messageNames.get(value); 97 if (previousName != null && !previousName.equals(name)) { 98 throw new DuplicateConstantError(name, previousName, value); 99 } 100 101 messageNames.put(value, name); 102 if (DBG) { 103 Log.d(TAG, String.format("Found constant: %s.%s = %d", 104 className, name, value)); 105 } 106 } catch (SecurityException | IllegalAccessException e) { 107 // Not allowed to make the field accessible, or no access. Ignore. 108 continue; 109 } 110 } 111 } 112 } 113 return messageNames; 114 } 115 116 /** 117 * Default prefixes for constants. 118 */ 119 public static final String[] DEFAULT_PREFIXES = {"CMD_", "EVENT_"}; 120 121 /** 122 * Finds the names of integer constants. Searches the specified {@code classes}, looking for 123 * accessible static integer values whose names begin with {@link #DEFAULT_PREFIXES}. 124 * 125 * @param classNames the classes to examine. 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