/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.util; import android.os.Message; import android.util.Log; import android.util.SparseArray; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /** * Static utility class for dealing with {@link Message} objects. */ public class MessageUtils { private static final String TAG = MessageUtils.class.getSimpleName(); private static final boolean DBG = false; /** Thrown when two different constants have the same value. */ public static class DuplicateConstantError extends Error { private DuplicateConstantError() {} public DuplicateConstantError(String name1, String name2, int value) { super(String.format("Duplicate constant value: both %s and %s = %d", name1, name2, value)); } } /** * Finds the names of integer constants. Searches the specified {@code classes}, looking for * accessible static integer fields whose names begin with one of the specified {@prefixes}. * * @param classes the classes to examine. * @prefixes only consider fields names starting with one of these prefixes. * @return a {@link SparseArray} mapping integer constants to their names. */ public static SparseArray findMessageNames(Class[] classes, String[] prefixes) { SparseArray messageNames = new SparseArray<>(); for (Class c : classes) { String className = c.getName(); if (DBG) Log.d(TAG, "Examining class " + className); Field[] fields; try { fields = c.getDeclaredFields(); } catch (SecurityException e) { Log.e(TAG, "Can't list fields of class " + className); continue; } for (Field field : fields) { int modifiers = field.getModifiers(); if (!Modifier.isStatic(modifiers) | !Modifier.isFinal(modifiers)) { continue; } String name = field.getName(); for (String prefix : prefixes) { // Does this look like a constant? if (!name.startsWith(prefix)) { continue; } try { // TODO: can we have the caller try to access the field instead, so we don't // expose constants it does not have access to? field.setAccessible(true); // Fetch the constant's value. int value; try { value = field.getInt(null); } catch (IllegalArgumentException | ExceptionInInitializerError e) { // The field is not an integer (or short or byte), or c's static // initializer failed and we have no idea what its value is. // Either way, give up on this field. break; } // Check for duplicate values. String previousName = messageNames.get(value); if (previousName != null && !previousName.equals(name)) { throw new DuplicateConstantError(name, previousName, value); } messageNames.put(value, name); if (DBG) { Log.d(TAG, String.format("Found constant: %s.%s = %d", className, name, value)); } } catch (SecurityException | IllegalAccessException e) { // Not allowed to make the field accessible, or no access. Ignore. continue; } } } } return messageNames; } /** * Default prefixes for constants. */ public static final String[] DEFAULT_PREFIXES = {"CMD_", "EVENT_"}; /** * Finds the names of integer constants. Searches the specified {@code classes}, looking for * accessible static integer values whose names begin with {@link #DEFAULT_PREFIXES}. * * @param classNames the classes to examine. * @prefixes only consider fields names starting with one of these prefixes. * @return a {@link SparseArray} mapping integer constants to their names. */ public static SparseArray findMessageNames(Class[] classNames) { return findMessageNames(classNames, DEFAULT_PREFIXES); } }