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