alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (AtParser.java)

This example Android source code file (AtParser.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, arraylist, at+foo, atcommandhandler, atcommandresult, atparser, bluetooth, hashmap, integer, object, string, type_action, type_read, type_set, type_test, util, z

The AtParser.java Android example source code

/*
 * Copyright (C) 2007 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 android.bluetooth;

import android.bluetooth.AtCommandHandler;
import android.bluetooth.AtCommandResult;

import java.util.*;

/**
 * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard.
 * <p>
 *
 * 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.<p>
 *
 * Command handlers are registered with an AtParser object. These handlers are
 * invoked when command lines are processed by AtParser's process() method.<p>
 *
 * 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<ul>
 * <li>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").
 * <li>Action Command. For example "AT+CIMI". The command name is "CIMI", and
 * there are no arguments for action commands.
 * <li>Read Command. For example "AT+VGM?". The command name is "VGM", and there
 * are no arguments for get commands.
 * <li>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.
 * <li>Test Command. For example "AT+VGM=?. No arguments.
 * </ul>
 *
 * In V.250 the last four command types are known as Extended Commands, and
 * they are used heavily in Bluetooth.<p>
 *
 * 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.<p>
 *
 * Extended commands can be chained. For example:<p>
 * AT+VGM?;+VGM=14;+CIMI<p>
 * This is equivalent to:<p>
 * 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.<p>
 *
 * 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.<p>
 *
 * Refer to:<ul>
 * <li>ITU-T Recommendation V.250
 * <li>ETSI TS 127.007  (AT Comannd set for User Equipment, 3GPP TS 27.007)
 * <li>Bluetooth Headset Profile Spec (K6)
 * <li>Bluetooth Handsfree Profile Spec (HFP 1.5)
 * </ul>
 * @hide
 */
public class AtParser {

    // Extended command type enumeration, only used internally
    private static final int TYPE_ACTION = 0;   // AT+FOO
    private static final int TYPE_READ = 1;     // AT+FOO?
    private static final int TYPE_SET = 2;      // AT+FOO=
    private static final int TYPE_TEST = 3;     // AT+FOO=?

    private HashMap<String, AtCommandHandler> mExtHandlers;
    private HashMap<Character, AtCommandHandler> mBasicHandlers;

    private String mLastInput;  // for "A/" (repeat last command) support

    /**
     * Create a new AtParser.<p>
     * No handlers are registered.
     */
    public AtParser() {
        mBasicHandlers = new HashMap<Character, AtCommandHandler>();
        mExtHandlers = new HashMap<String, AtCommandHandler>();
        mLastInput = "";
    }

    /**
     * Register a Basic command handler.<p>
     * Basic command handlers are later called via their
     * <code>handleBasicCommand(String args) method.
     * @param  command Command name - a single character
     * @param  handler Handler to register
     */
    public void register(Character command, AtCommandHandler handler) {
        mBasicHandlers.put(command, handler);
    }

    /**
     * Register an Extended command handler.<p>
     * Extended command handlers are later called via:<ul>
     * <li>handleActionCommand()
     * <li>handleGetCommand()
     * <li>handleSetCommand()
     * <li>handleTestCommand()
     * </ul>
     * Only one method will be called for each command processed.
     * @param  command Command name - can be multiple characters
     * @param  handler Handler to register
     */
    public void register(String command, AtCommandHandler handler) {
        mExtHandlers.put(command, handler);
    }


    /**
     * 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
     */
    static private String clean(String input) {
        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();
    }

    static private boolean isAtoZ(char c) {
        return (c >= 'A' && c <= 'Z');
    }

    /**
     * Find a character ch, ignoring quoted sections.
     * Return input.length() if not found.
     */
    static private int findChar(char ch, String input, int fromIndex) {
        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();
    }

    /**
     * Break an argument string into individual arguments (comma deliminated).
     * Integer arguments are turned into Integer objects. Otherwise a String
     * object is used.
     */
    static private Object[] generateArgs(String input) {
        int i = 0;
        int j;
        ArrayList<Object> out = new ArrayList();
        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();
    }

    /**
     * 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.
     */
    static private int findEndExtendedName(String input, int index) {
        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();
    }

    /**
     * Processes an incoming AT command line.<p>
     * This method will invoke zero or one command handler methods for each
     * command in the command line.<p>
     * @param raw_input The AT input, without EOL deliminator (e.g. <CR>).
     * @return          Result object for this command line. This can be
     *                  converted to a String[] response with toStrings().
     */
    public AtCommandResult process(String raw_input) {
        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;
    }
}

Other Android examples (source code examples)

Here is a short list of links related to this Android AtParser.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.