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