FileDocCategorySizeDatePackage
AtParser.javaAPI DocAndroid 1.5 API13774Wed May 06 22:41:54 BST 2009android.bluetooth

AtParser

public class AtParser extends Object
An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard.

Conforment with the subset of V.250 required for implementation of the Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP specifications. Also implements some V.250 features not required by Bluetooth - such as chained commands.

Command handlers are registered with an AtParser object. These handlers are invoked when command lines are processed by AtParser's process() method.

The AtParser object accepts a new command line to parse via its process() method. It breaks each command line into one or more commands. Each command is parsed for name, type, and (optional) arguments, and an appropriate external handler method is called through the AtCommandHandler interface. The command types are

  • Basic Command. For example "ATDT1234567890". Basic command names are a single character (e.g. "D"), and everything following this character is passed to the handler as a string argument (e.g. "T1234567890").
  • Action Command. For example "AT+CIMI". The command name is "CIMI", and there are no arguments for action commands.
  • Read Command. For example "AT+VGM?". The command name is "VGM", and there are no arguments for get commands.
  • Set Command. For example "AT+VGM=14". The command name is "VGM", and there is a single integer argument in this case. In the general case then can be zero or more arguments (comma deliminated) each of integer or string form.
  • Test Command. For example "AT+VGM=?. No arguments.
In V.250 the last four command types are known as Extended Commands, and they are used heavily in Bluetooth.

Basic commands cannot be chained in this implementation. For Bluetooth headset/handsfree use this is acceptable, because they only use the basic commands ATA and ATD, which are not allowed to be chained. For general V.250 use we would need to improve this class to allow Basic command chaining - however its tricky to get right becuase there is no deliminator for Basic command chaining.

Extended commands can be chained. For example:

AT+VGM?;+VGM=14;+CIMI

This is equivalent to:

AT+VGM? AT+VGM=14 AT+CIMI Except that only one final result code is return (although several intermediate responses may be returned), and as soon as one command in the chain fails the rest are abandonded.

Handlers are registered by there command name via register(Char c, ...) or register(String s, ...). Handlers for Basic command should be registered by the basic command character, and handlers for Extended commands should be registered by String.

Refer to:

  • ITU-T Recommendation V.250
  • ETSI TS 127.007 (AT Comannd set for User Equipment, 3GPP TS 27.007)
  • Bluetooth Headset Profile Spec (K6)
  • Bluetooth Handsfree Profile Spec (HFP 1.5)
hide

Fields Summary
private static final int
TYPE_ACTION
private static final int
TYPE_READ
private static final int
TYPE_SET
private static final int
TYPE_TEST
private HashMap
mExtHandlers
private HashMap
mBasicHandlers
private String
mLastInput
Constructors Summary
public AtParser()
Create a new AtParser.

No handlers are registered.

  // for "A/" (repeat last command) support

                 
      
        mBasicHandlers = new HashMap<Character, AtCommandHandler>();
        mExtHandlers = new HashMap<String, AtCommandHandler>();
        mLastInput = "";
    
Methods Summary
private static java.lang.Stringclean(java.lang.String input)
Strip input of whitespace and force Uppercase - except sections inside quotes. Also fixes unmatched quotes (by appending a quote). Double quotes " are the only quotes allowed by V.250

        StringBuilder out = new StringBuilder(input.length());

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c == '"") {
                int j = input.indexOf('"", i + 1 );  // search for closing "
                if (j == -1) {  // unmatched ", insert one.
                    out.append(input.substring(i, input.length()));
                    out.append('"");
                    break;
                }
                out.append(input.substring(i, j + 1));
                i = j;
            } else if (c != ' ") {
                out.append(Character.toUpperCase(c));
            }
        }

        return out.toString();
    
private static intfindChar(char ch, java.lang.String input, int fromIndex)
Find a character ch, ignoring quoted sections. Return input.length() if not found.

        for (int i = fromIndex; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c == '"") {
                i = input.indexOf('"", i + 1);
                if (i == -1) {
                    return input.length();
                }
            } else if (c == ch) {
                return i;
            }
        }
        return input.length();
    
private static intfindEndExtendedName(java.lang.String input, int index)
Return the index of the end of character after the last characeter in the extended command name. Uses the V.250 spec for allowed command names.

        for (int i = index; i < input.length(); i++) {
            char c = input.charAt(i);

            // V.250 defines the following chars as legal extended command
            // names
            if (isAtoZ(c)) continue;
            if (c >= '0" && c <= '9") continue;
            switch (c) {
            case '!":
            case '%":
            case '-":
            case '.":
            case '/":
            case ':":
            case '_":
                continue;
            default:
                return i;
            }
        }
        return input.length();
    
private static java.lang.Object[]generateArgs(java.lang.String input)
Break an argument string into individual arguments (comma deliminated). Integer arguments are turned into Integer objects. Otherwise a String object is used.

        int i = 0;
        int j;
        ArrayList<Object> out = new ArrayList<Object>();
        while (i <= input.length()) {
            j = findChar(',", input, i);

            String arg = input.substring(i, j);
            try {
                out.add(new Integer(arg));
            } catch (NumberFormatException e) {
                out.add(arg);
            }

            i = j + 1; // move past comma
        }
        return out.toArray();
    
private static booleanisAtoZ(char c)

        return (c >= 'A" && c <= 'Z");
    
public android.bluetooth.AtCommandResultprocess(java.lang.String raw_input)
Processes an incoming AT command line.

This method will invoke zero or one command handler methods for each command in the command line.

param
raw_input The AT input, without EOL deliminator (e.g. ).
return
Result object for this command line. This can be converted to a String[] response with toStrings().

        String input = clean(raw_input);

        // Handle "A/" (repeat previous line)
        if (input.regionMatches(0, "A/", 0, 2)) {
            input = new String(mLastInput);
        } else {
            mLastInput = new String(input);
        }

        // Handle empty line - no response necessary
        if (input.equals("")) {
            // Return []
            return new AtCommandResult(AtCommandResult.UNSOLICITED);
        }

        // Anything else deserves an error
        if (!input.regionMatches(0, "AT", 0, 2)) {
            // Return ["ERROR"]
            return new AtCommandResult(AtCommandResult.ERROR);
        }

        // Ok we have a command that starts with AT. Process it
        int index = 2;
        AtCommandResult result =
                new AtCommandResult(AtCommandResult.UNSOLICITED);
        while (index < input.length()) {
            char c = input.charAt(index);

            if (isAtoZ(c)) {
                // Option 1: Basic Command
                // Pass the rest of the line as is to the handler. Do not
                // look for any more commands on this line.
                String args = input.substring(index + 1);
                if (mBasicHandlers.containsKey((Character)c)) {
                    result.addResult(mBasicHandlers.get(
                            (Character)c).handleBasicCommand(args));
                    return result;
                } else {
                    // no handler
                    result.addResult(
                            new AtCommandResult(AtCommandResult.ERROR));
                    return result;
                }
                // control never reaches here
            }

            if (c == '+") {
                // Option 2: Extended Command
                // Search for first non-name character. Shortcircuit if we dont
                // handle this command name.
                int i = findEndExtendedName(input, index + 1);
                String commandName = input.substring(index, i);
                if (!mExtHandlers.containsKey(commandName)) {
                    // no handler
                    result.addResult(
                            new AtCommandResult(AtCommandResult.ERROR));
                    return result;
                }
                AtCommandHandler handler = mExtHandlers.get(commandName);

                // Search for end of this command - this is usually the end of
                // line
                int endIndex = findChar(';", input, index);

                // Determine what type of command this is.
                // Default to TYPE_ACTION if we can't find anything else
                // obvious.
                int type;

                if (i >= endIndex) {
                    type = TYPE_ACTION;
                } else if (input.charAt(i) == '?") {
                    type = TYPE_READ;
                } else if (input.charAt(i) == '=") {
                    if (i + 1 < endIndex) {
                        if (input.charAt(i + 1) == '?") {
                            type = TYPE_TEST;
                        } else {
                            type = TYPE_SET;
                        }
                    } else {
                        type = TYPE_SET;
                    }
                } else {
                    type = TYPE_ACTION;
                }

                // Call this command. Short-circuit as soon as a command fails
                switch (type) {
                case TYPE_ACTION:
                    result.addResult(handler.handleActionCommand());
                    break;
                case TYPE_READ:
                    result.addResult(handler.handleReadCommand());
                    break;
                case TYPE_TEST:
                    result.addResult(handler.handleTestCommand());
                    break;
                case TYPE_SET:
                    Object[] args =
                            generateArgs(input.substring(i + 1, endIndex));
                    result.addResult(handler.handleSetCommand(args));
                    break;
                }
                if (result.getResultCode() != AtCommandResult.OK) {
                    return result;   // short-circuit
                }

                index = endIndex;
            } else {
                // Can't tell if this is a basic or extended command.
                // Push forwards and hope we hit something.
                index++;
            }
        }
        // Finished processing (and all results were ok)
        return result;
    
public voidregister(java.lang.Character command, android.bluetooth.AtCommandHandler handler)
Register a Basic command handler.

Basic command handlers are later called via their handleBasicCommand(String args) method.

param
command Command name - a single character
param
handler Handler to register

        mBasicHandlers.put(command, handler);
    
public voidregister(java.lang.String command, android.bluetooth.AtCommandHandler handler)
Register an Extended command handler.

Extended command handlers are later called via:

  • handleActionCommand()
  • handleGetCommand()
  • handleSetCommand()
  • handleTestCommand()
Only one method will be called for each command processed.

param
command Command name - can be multiple characters
param
handler Handler to register

        mExtHandlers.put(command, handler);