Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2457a3e authored by Lorenzo Colitti's avatar Lorenzo Colitti
Browse files

Add a MessageUtils class to convert int constants to strings.

This class uses reflection to find accessible static integer
members in a specified list of classes and returns a SparseArray
mapping the integers to their names. This will allow us to
replace various 400-line switch statements with a simple
array access.

Change-Id: I3607e6389a423cde0bd83270c00b3c863ae1bb29
parent 0d0f0c7f
Loading
Loading
Loading
Loading
+127 −0
Original line number Diff line number Diff line
/*
 * 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;

/**
 * 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<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) {
                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<String> findMessageNames(Class[] classNames) {
        return findMessageNames(classNames, DEFAULT_PREFIXES);
    }
}
+8 −27
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.net.dhcp;
import com.android.internal.util.HexDump;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.StateMachine;

import android.app.AlarmManager;
@@ -34,7 +35,6 @@ import android.net.NetworkUtils;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -42,16 +42,15 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
import android.util.Log;
import android.util.SparseArray;
import android.util.TimeUtils;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.Thread;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
@@ -133,6 +132,11 @@ public class DhcpClient extends StateMachine {
    private static final int CMD_TIMEOUT          = PRIVATE_BASE + 3;
    private static final int CMD_ONESHOT_TIMEOUT  = PRIVATE_BASE + 4;

    // For message logging.
    private static final Class[] sMessageClasses = { DhcpClient.class };
    private static final SparseArray<String> sMessageNames =
            MessageUtils.findMessageNames(sMessageClasses);

    // DHCP parameters that we request.
    private static final byte[] REQUESTED_PARAMS = new byte[] {
        DHCP_SUBNET_MASK,
@@ -497,30 +501,7 @@ public class DhcpClient extends StateMachine {
        }

        private String messageName(int what) {
            switch (what) {
                case CMD_START_DHCP:
                    return "CMD_START_DHCP";
                case CMD_STOP_DHCP:
                    return "CMD_STOP_DHCP";
                case CMD_RENEW_DHCP:
                    return "CMD_RENEW_DHCP";
                case CMD_PRE_DHCP_ACTION:
                    return "CMD_PRE_DHCP_ACTION";
                case CMD_PRE_DHCP_ACTION_COMPLETE:
                    return "CMD_PRE_DHCP_ACTION_COMPLETE";
                case CMD_POST_DHCP_ACTION:
                    return "CMD_POST_DHCP_ACTION";
                case CMD_KICK:
                    return "CMD_KICK";
                case CMD_RECEIVED_PACKET:
                    return "CMD_RECEIVED_PACKET";
                case CMD_TIMEOUT:
                    return "CMD_TIMEOUT";
                case CMD_ONESHOT_TIMEOUT:
                    return "CMD_ONESHOT_TIMEOUT";
                default:
                    return Integer.toString(what);
            }
            return sMessageNames.get(what, Integer.toString(what));
        }

        private String messageToString(Message message) {
+9 −20
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.net.ip;

import com.android.internal.util.MessageUtils;

import android.content.Context;
import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
@@ -30,6 +32,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -61,6 +64,11 @@ public class IpManager extends StateMachine {
    private static final boolean DBG = true;
    private static final boolean VDBG = false;

    // For message logging.
    private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class };
    private static final SparseArray<String> sWhatToString =
            MessageUtils.findMessageNames(sMessageClasses);

    /**
     * Callbacks for handling IpManager events.
     */
@@ -306,26 +314,7 @@ public class IpManager extends StateMachine {

    @Override
    protected String getWhatToString(int what) {
        // TODO: Investigate switching to reflection.
        switch (what) {
            case CMD_STOP:
                return "CMD_STOP";
            case CMD_START:
                return "CMD_START";
            case CMD_CONFIRM:
                return "CMD_CONFIRM";
            case EVENT_PRE_DHCP_ACTION_COMPLETE:
                return "EVENT_PRE_DHCP_ACTION_COMPLETE";
            case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
                return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
            case DhcpClient.CMD_PRE_DHCP_ACTION:
                return "DhcpClient.CMD_PRE_DHCP_ACTION";
            case DhcpClient.CMD_POST_DHCP_ACTION:
                return "DhcpClient.CMD_POST_DHCP_ACTION";
            case DhcpClient.CMD_ON_QUIT:
                return "DhcpClient.CMD_ON_QUIT";
        }
        return "UNKNOWN:" + Integer.toString(what);
        return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
    }

    @Override