summaryrefslogtreecommitdiff
path: root/core/java/com/android/internal/util/MessageUtils.java
blob: e733c30ae84a074c4d1d0158eb5dd2f251d546e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * 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
     * {@code prefixes}.
     *
     * @param classes the classes to examine.
     * @param prefixes only consider fields names starting with one of these prefixes.
     * @return a {@link SparseArray} mapping integer constants to their names.
     */
    public static SparseArray<String> findMessageNames(Class[] classes, String[] prefixes) {
        SparseArray<String> 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.
     * @return a {@link SparseArray} mapping integer constants to their names.
     */
    public static SparseArray<String> findMessageNames(Class[] classNames) {
        return findMessageNames(classNames, DEFAULT_PREFIXES);
    }
}