19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.bluetooth;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.*;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard.
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
25f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby * Conformant with the subset of V.250 required for implementation of the
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * specifications. Also implements some V.250 features not required by
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Bluetooth - such as chained commands.<p>
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Command handlers are registered with an AtParser object. These handlers are
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * invoked when command lines are processed by AtParser's process() method.<p>
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The AtParser object accepts a new command line to parse via its process()
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * method. It breaks each command line into one or more commands. Each command
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is parsed for name, type, and (optional) arguments, and an appropriate
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * external handler method is called through the AtCommandHandler interface.
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The command types are<ul>
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Basic Command. For example "ATDT1234567890". Basic command names are a
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * single character (e.g. "D"), and everything following this character is
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * passed to the handler as a string argument (e.g. "T1234567890").
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Action Command. For example "AT+CIMI". The command name is "CIMI", and
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * there are no arguments for action commands.
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Read Command. For example "AT+VGM?". The command name is "VGM", and there
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * are no arguments for get commands.
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Set Command. For example "AT+VGM=14". The command name is "VGM", and
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * there is a single integer argument in this case. In the general case then
48f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby * can be zero or more arguments (comma delimited) each of integer or string
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * form.
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Test Command. For example "AT+VGM=?. No arguments.
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </ul>
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * In V.250 the last four command types are known as Extended Commands, and
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * they are used heavily in Bluetooth.<p>
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Basic commands cannot be chained in this implementation. For Bluetooth
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * headset/handsfree use this is acceptable, because they only use the basic
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * commands ATA and ATD, which are not allowed to be chained. For general V.250
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * use we would need to improve this class to allow Basic command chaining -
60f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby * however it's tricky to get right because there is no delimiter for Basic
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * command chaining.<p>
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Extended commands can be chained. For example:<p>
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * AT+VGM?;+VGM=14;+CIMI<p>
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This is equivalent to:<p>
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * AT+VGM?
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * AT+VGM=14
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * AT+CIMI
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Except that only one final result code is return (although several
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * intermediate responses may be returned), and as soon as one command in the
71f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby * chain fails the rest are abandoned.<p>
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Handlers are registered by there command name via register(Char c, ...) or
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * register(String s, ...). Handlers for Basic command should be registered by
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the basic command character, and handlers for Extended commands should be
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * registered by String.<p>
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Refer to:<ul>
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>ITU-T Recommendation V.250
80f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby * <li>ETSI TS 127.007  (AT Command set for User Equipment, 3GPP TS 27.007)
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Bluetooth Headset Profile Spec (K6)
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <li>Bluetooth Handsfree Profile Spec (HFP 1.5)
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </ul>
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class AtParser {
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Extended command type enumeration, only used internally
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TYPE_ACTION = 0;   // AT+FOO
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TYPE_READ = 1;     // AT+FOO?
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TYPE_SET = 2;      // AT+FOO=
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TYPE_TEST = 3;     // AT+FOO=?
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private HashMap<String, AtCommandHandler> mExtHandlers;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private HashMap<Character, AtCommandHandler> mBasicHandlers;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mLastInput;  // for "A/" (repeat last command) support
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create a new AtParser.<p>
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * No handlers are registered.
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public AtParser() {
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBasicHandlers = new HashMap<Character, AtCommandHandler>();
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mExtHandlers = new HashMap<String, AtCommandHandler>();
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLastInput = "";
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Register a Basic command handler.<p>
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Basic command handlers are later called via their
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>handleBasicCommand(String args)</code> method.
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param  command Command name - a single character
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param  handler Handler to register
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void register(Character command, AtCommandHandler handler) {
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBasicHandlers.put(command, handler);
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Register an Extended command handler.<p>
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Extended command handlers are later called via:<ul>
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <li><code>handleActionCommand()</code>
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <li><code>handleGetCommand()</code>
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <li><code>handleSetCommand()</code>
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <li><code>handleTestCommand()</code>
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * </ul>
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one method will be called for each command processed.
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param  command Command name - can be multiple characters
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param  handler Handler to register
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void register(String command, AtCommandHandler handler) {
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mExtHandlers.put(command, handler);
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Strip input of whitespace and force Uppercase - except sections inside
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * quotes. Also fixes unmatched quotes (by appending a quote). Double
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * quotes " are the only quotes allowed by V.250
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private String clean(String input) {
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder out = new StringBuilder(input.length());
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < input.length(); i++) {
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = input.charAt(i);
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '"') {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int j = input.indexOf('"', i + 1 );  // search for closing "
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (j == -1) {  // unmatched ", insert one.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append(input.substring(i, input.length()));
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append('"');
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append(input.substring(i, j + 1));
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = j;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c != ' ') {
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append(Character.toUpperCase(c));
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out.toString();
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private boolean isAtoZ(char c) {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (c >= 'A' && c <= 'Z');
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Find a character ch, ignoring quoted sections.
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return input.length() if not found.
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private int findChar(char ch, String input, int fromIndex) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = fromIndex; i < input.length(); i++) {
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = input.charAt(i);
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '"') {
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i = input.indexOf('"', i + 1);
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i == -1) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return input.length();
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == ch) {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return input.length();
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
188f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby     * Break an argument string into individual arguments (comma delimited).
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Integer arguments are turned into Integer objects. Otherwise a String
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * object is used.
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private Object[] generateArgs(String input) {
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = 0;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ArrayList<Object> out = new ArrayList<Object>();
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i <= input.length()) {
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            j = findChar(',', input, i);
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String arg = input.substring(i, j);
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.add(new Integer(arg));
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.add(arg);
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            i = j + 1; // move past comma
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return out.toArray();
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
212f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby     * Return the index of the end of character after the last character in
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the extended command name. Uses the V.250 spec for allowed command
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * names.
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private int findEndExtendedName(String input, int index) {
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = index; i < input.length(); i++) {
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = input.charAt(i);
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // V.250 defines the following chars as legal extended command
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // names
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (isAtoZ(c)) continue;
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c >= '0' && c <= '9') continue;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (c) {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '!':
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '%':
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '-':
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '.':
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '/':
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case ':':
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '_':
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return input.length();
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Processes an incoming AT command line.<p>
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will invoke zero or one command handler methods for each
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * command in the command line.<p>
244f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby     * @param raw_input The AT input, without EOL delimiter (e.g. <CR>).
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return          Result object for this command line. This can be
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *                  converted to a String[] response with toStrings().
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public AtCommandResult process(String raw_input) {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String input = clean(raw_input);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Handle "A/" (repeat previous line)
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (input.regionMatches(0, "A/", 0, 2)) {
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            input = new String(mLastInput);
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastInput = new String(input);
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Handle empty line - no response necessary
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (input.equals("")) {
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Return []
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new AtCommandResult(AtCommandResult.UNSOLICITED);
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Anything else deserves an error
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!input.regionMatches(0, "AT", 0, 2)) {
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Return ["ERROR"]
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new AtCommandResult(AtCommandResult.ERROR);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Ok we have a command that starts with AT. Process it
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int index = 2;
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        AtCommandResult result =
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new AtCommandResult(AtCommandResult.UNSOLICITED);
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (index < input.length()) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = input.charAt(index);
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (isAtoZ(c)) {
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Option 1: Basic Command
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Pass the rest of the line as is to the handler. Do not
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // look for any more commands on this line.
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String args = input.substring(index + 1);
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mBasicHandlers.containsKey((Character)c)) {
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(mBasicHandlers.get(
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            (Character)c).handleBasicCommand(args));
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return result;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // no handler
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            new AtCommandResult(AtCommandResult.ERROR));
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return result;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // control never reaches here
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '+') {
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Option 2: Extended Command
297f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby                // Search for first non-name character. Short-circuit if
298f51eadaf1f83abfe16a609a4ded6d789494689b2Jake Hamby                // we don't handle this command name.
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int i = findEndExtendedName(input, index + 1);
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String commandName = input.substring(index, i);
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mExtHandlers.containsKey(commandName)) {
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // no handler
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            new AtCommandResult(AtCommandResult.ERROR));
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return result;
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                AtCommandHandler handler = mExtHandlers.get(commandName);
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Search for end of this command - this is usually the end of
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // line
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int endIndex = findChar(';', input, index);
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Determine what type of command this is.
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Default to TYPE_ACTION if we can't find anything else
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // obvious.
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int type;
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (i >= endIndex) {
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    type = TYPE_ACTION;
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (input.charAt(i) == '?') {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    type = TYPE_READ;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (input.charAt(i) == '=') {
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (i + 1 < endIndex) {
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (input.charAt(i + 1) == '?') {
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            type = TYPE_TEST;
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            type = TYPE_SET;
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        type = TYPE_SET;
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    type = TYPE_ACTION;
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Call this command. Short-circuit as soon as a command fails
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (type) {
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPE_ACTION:
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(handler.handleActionCommand());
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPE_READ:
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(handler.handleReadCommand());
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPE_TEST:
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(handler.handleTestCommand());
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPE_SET:
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Object[] args =
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            generateArgs(input.substring(i + 1, endIndex));
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    result.addResult(handler.handleSetCommand(args));
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (result.getResultCode() != AtCommandResult.OK) {
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return result;   // short-circuit
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                index = endIndex;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Can't tell if this is a basic or extended command.
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Push forwards and hope we hit something.
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                index++;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Finished processing (and all results were ok)
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
368