1 /* 2 * Copyright (C) 2007 Esmertec AG. 3 * Copyright (C) 2007 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.im.imps; 19 20 import java.io.BufferedReader; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.InputStreamReader; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.Map.Entry; 27 import java.util.regex.Matcher; 28 import java.util.regex.Pattern; 29 30 import com.android.im.imps.Primitive.TransactionMode; 31 32 /** 33 * PTS/SMS encoded IMPS messages parser. Only response transactions and 34 * server initiated requests are supported. 35 */ 36 public class PtsPrimitiveParser implements PrimitiveParser { 37 38 // WVaaBBcccDD <parameters> 39 // aa - version number; 12 for 1.2, 13 for 1.3; "XX" for version discovery 40 // BB - message type, case insensitive 41 // ccc - transaction id in range 0-999 without preceding zero 42 // DD - multiple SMSes identifier 43 private static final Pattern sPreamplePattern = 44 Pattern.compile("\\AWV(\\d{2})(\\p{Alpha}{2})(\\d{1,3})(\\p{Alpha}{2})?(\\z| .*)"); 45 46 private char mReadBuf[] = new char[256]; 47 private StringBuilder mStringBuf = new StringBuilder(); 48 private int mPos; 49 50 private static int UNCERTAIN_GROUP_SIZE = -1; 51 parse(InputStream in)52 public Primitive parse(InputStream in) throws ParserException, IOException { 53 // assuming PTS data is always short 54 BufferedReader reader = new BufferedReader( 55 new InputStreamReader(in, "UTF-8"), 128); 56 mStringBuf.setLength(0); 57 mPos = 0; 58 int len; 59 while ((len = reader.read(mReadBuf)) != -1) { 60 mStringBuf.append(mReadBuf, 0, len); 61 } 62 return parsePrim(); 63 } 64 parsePrim()65 private Primitive parsePrim() throws ParserException 66 { 67 Matcher m = sPreamplePattern.matcher(mStringBuf); 68 if (!m.matches()) { 69 throw new ParserException("Invalid PTS encoded message"); 70 } 71 72 Primitive p = new Primitive(); 73 74 // TODO: handle WV version in m.group(1) 75 76 String type = m.group(2).toUpperCase(); 77 String transactionType = PtsCodes.getTransaction(type); 78 if (transactionType == null) { 79 throw new ParserException("Unrecognized transaction code " + type); 80 } 81 p.setContentElement(transactionType); 82 83 if (PtsCodes.isServerRequestCode(type)) { 84 p.setTransactionMode(TransactionMode.Request); 85 } else { 86 p.setTransactionMode(TransactionMode.Response); 87 } 88 89 p.setTransactionId(m.group(3)); 90 mPos = m.start(5); 91 92 if (mPos < mStringBuf.length()) { 93 match(' '); 94 95 HashMap<String, ParamValue> params = parseParams(); 96 for (Entry<String, ParamValue> param : params.entrySet()) { 97 translateParam(p, param.getKey(), param.getValue()); 98 } 99 } 100 return p; 101 } 102 103 private static HashMap<String, Integer> sInfoElemTypeMap; 104 private static final int ELEM_OTHER_SIMPLE = 0; 105 private static final int ELEM_SESSION_ID = 1; 106 private static final int ELEM_RESULT = 2; 107 private static final int ELEM_ALL_FUNCTIONS = 3; 108 private static final int ELEM_NOT_AVAIL_FUNCS = 4; 109 private static final int ELEM_CAPABILITY_LIST = 5; 110 private static final int ELEM_CONTACT_LIST = 6; 111 private static final int ELEM_DEFAULT_CONTACT_LIST = 7; 112 private static final int ELEM_USER_NICK_LIST = 8; 113 private static final int ELEM_CONTACT_LIST_PROPS = 9; 114 private static final int ELEM_PRESENCE = 10; 115 116 /* 117 private static final int ELEM_RESULT_CLIST = 3; 118 private static final int ELEM_RESULT_DOMAIN = 4; 119 private static final int ELEM_RESULT_GROUP = 5; 120 private static final int ELEM_RESULT_MSGID = 6; 121 private static final int ELEM_RESULT_SCRNAME = 7; 122 private static final int ELEM_RESULT_USER = 8; 123 */ 124 125 static { 126 sInfoElemTypeMap = new HashMap<String, Integer>(); sInfoElemTypeMap.put(PtsCodes.SessionID, ELEM_SESSION_ID)127 sInfoElemTypeMap.put(PtsCodes.SessionID, ELEM_SESSION_ID); sInfoElemTypeMap.put(PtsCodes.Status, ELEM_RESULT)128 sInfoElemTypeMap.put(PtsCodes.Status, ELEM_RESULT); sInfoElemTypeMap.put(PtsCodes.NotAvailableFunctions, ELEM_NOT_AVAIL_FUNCS)129 sInfoElemTypeMap.put(PtsCodes.NotAvailableFunctions, ELEM_NOT_AVAIL_FUNCS); sInfoElemTypeMap.put(PtsCodes.AllFunctions, ELEM_ALL_FUNCTIONS)130 sInfoElemTypeMap.put(PtsCodes.AllFunctions, ELEM_ALL_FUNCTIONS); sInfoElemTypeMap.put(PtsCodes.AgreedCapabilityList, ELEM_CAPABILITY_LIST)131 sInfoElemTypeMap.put(PtsCodes.AgreedCapabilityList, ELEM_CAPABILITY_LIST); sInfoElemTypeMap.put(PtsCodes.ContactList, ELEM_CONTACT_LIST)132 sInfoElemTypeMap.put(PtsCodes.ContactList, ELEM_CONTACT_LIST); sInfoElemTypeMap.put(PtsCodes.DefaultContactList, ELEM_DEFAULT_CONTACT_LIST)133 sInfoElemTypeMap.put(PtsCodes.DefaultContactList, ELEM_DEFAULT_CONTACT_LIST); sInfoElemTypeMap.put(PtsCodes.UserNickList, ELEM_USER_NICK_LIST)134 sInfoElemTypeMap.put(PtsCodes.UserNickList, ELEM_USER_NICK_LIST); sInfoElemTypeMap.put(PtsCodes.ContactListProps, ELEM_CONTACT_LIST_PROPS)135 sInfoElemTypeMap.put(PtsCodes.ContactListProps, ELEM_CONTACT_LIST_PROPS); sInfoElemTypeMap.put(PtsCodes.Presence, ELEM_PRESENCE)136 sInfoElemTypeMap.put(PtsCodes.Presence, ELEM_PRESENCE); 137 } 138 translateParam(Primitive p, String elemCode, ParamValue elemValue)139 private static void translateParam(Primitive p, String elemCode, 140 ParamValue elemValue) throws ParserException { 141 int type; 142 elemCode = elemCode.toUpperCase(); 143 144 // FIXME: Should be refactored when we had concrete situation of the null value case 145 if (elemValue == null) { 146 throw new ParserException("Parameter " + elemCode + " must have value."); 147 } 148 149 if (sInfoElemTypeMap.containsKey(elemCode)) { 150 type = sInfoElemTypeMap.get(elemCode); 151 /* 152 if (type == ELEM_RESULT_CLIST && p.getType().equals(ImpsTags.Login_Response)) { 153 // Fix up DigestSchema which shares a same code with 154 // ContactListID. It appears only in Login_Response. 155 type = ELEM_OTHER_SIMPLE; 156 } 157 */ 158 } else { 159 type = ELEM_OTHER_SIMPLE; 160 } 161 162 switch (type) { 163 case ELEM_SESSION_ID: 164 if (elemValue.mStrValue == null) { 165 throw new ParserException("Element SessionID must have string value!"); 166 } 167 168 if (p.getType().equals(ImpsTags.Login_Response)) { 169 p.addElement(ImpsTags.SessionID, elemValue.mStrValue); 170 } else { 171 p.setSession(elemValue.mStrValue); 172 } 173 break; 174 175 case ELEM_RESULT: 176 // ST=<StatusCode> 177 // ST=(<StatusCode>,<Description>) 178 PrimitiveElement result = p.addElement(ImpsTags.Result); 179 180 if (elemValue.mStrValue != null) { 181 result.addChild(ImpsTags.Code, elemValue.mStrValue); 182 } else { 183 checkGroupValue(elemValue.mValueGroup, 2); 184 185 result.addChild(ImpsTags.Code, elemValue.mValueGroup.get(0).mStrValue); 186 result.addChild(ImpsTags.Description, elemValue.mValueGroup.get(1).mStrValue); 187 } 188 break; 189 190 case ELEM_ALL_FUNCTIONS: 191 case ELEM_NOT_AVAIL_FUNCS: 192 p.addElement(translateServiceTree(elemCode, elemValue)); 193 break; 194 195 case ELEM_CAPABILITY_LIST: 196 p.addElement(translateCapabilityList(elemValue)); 197 break; 198 199 case ELEM_CONTACT_LIST: 200 if (elemValue.mStrValue != null) { 201 p.addElement(ImpsTags.ContactList, elemValue.mStrValue); 202 } else { 203 checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE); 204 for (ParamValue value : elemValue.mValueGroup) { 205 p.addElement(ImpsTags.ContactList, value.mStrValue); 206 } 207 } 208 break; 209 210 case ELEM_DEFAULT_CONTACT_LIST: 211 if (elemValue.mStrValue == null) { 212 throw new ParserException("Deafult Contact List must have string value!"); 213 } 214 215 p.addElement(ImpsTags.DefaultContactList, elemValue.mStrValue); 216 break; 217 218 case ELEM_USER_NICK_LIST: 219 { 220 checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE); 221 222 PrimitiveElement nicklistElem = p.addElement(ImpsTags.NickList); 223 224 int groupSize = elemValue.mValueGroup.size(); 225 for (int i = 0; i < groupSize; i++) { 226 ArrayList<ParamValue> valueGroup = elemValue.mValueGroup.get(i).mValueGroup; 227 checkGroupValue(valueGroup, 2); 228 229 String nickname = valueGroup.get(0).mStrValue; 230 String address = valueGroup.get(1).mStrValue; 231 if (nickname == null || address == null) { 232 throw new ParserException("Null value found for NickName: " + nickname 233 + "-" + address); 234 } 235 236 PrimitiveElement nicknameElem = nicklistElem.addChild(ImpsTags.NickName); 237 nicknameElem.addChild(ImpsTags.Name, "".equals(nickname) ? null : nickname); 238 nicknameElem.addChild(ImpsTags.UserID, address); 239 } 240 } 241 break; 242 243 case ELEM_CONTACT_LIST_PROPS: 244 { 245 checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE); 246 247 PrimitiveElement propertiesElem = p.addElement(ImpsTags.ContactListProperties); 248 249 int groupSize = elemValue.mValueGroup.size(); 250 for (int i = 0; i < groupSize; i++) { 251 ArrayList<ParamValue> valueGroup = elemValue.mValueGroup.get(i).mValueGroup; 252 checkGroupValue(valueGroup, 2); 253 254 String name = valueGroup.get(0).mStrValue; 255 String value = valueGroup.get(1).mStrValue; 256 if (name == null || value == null) { 257 throw new ParserException("Null value found for property: " + name + "-" + value); 258 } 259 260 if (PtsCodes.DisplayName.equals(name)) { 261 name = ImpsConstants.DisplayName; 262 } else if (PtsCodes.Default.equals(name)) { 263 name = ImpsConstants.Default; 264 } else { 265 throw new ParserException("Unrecognized property " + name); 266 } 267 268 PrimitiveElement propertyElem = propertiesElem.addChild(ImpsTags.Property); 269 propertyElem.addChild(ImpsTags.Name, name); 270 propertyElem.addChild(ImpsTags.Value, value); 271 } 272 } 273 break; 274 275 case ELEM_PRESENCE: 276 //PR=(<UserID>[,<PresenceSubList>]) 277 //PR=((<UserID>[,<PresenceSubList>]),(<UserID>[,<PresenceSubList>])) 278 checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE); 279 280 if (elemValue.mValueGroup.size() == 1) { 281 // PR=(<UserID>) 282 ParamValue value = elemValue.mValueGroup.get(0); 283 if (value.mStrValue != null) { 284 p.addElement(ImpsTags.Presence).addChild(ImpsTags.UserID, value.mStrValue); 285 } else { 286 // workaround for OZ server 287 p.addElement(translatePresence(value.mValueGroup)); 288 } 289 290 } else { 291 if (elemValue.mValueGroup.get(0).mStrValue == null) { 292 // PR=((<UserID>[,<PresenceSubList>]),(<UserID>[,<PresenceSubList>])) 293 int groupSize = elemValue.mValueGroup.size(); 294 for (int i = 0; i < groupSize; i++) { 295 ParamValue value = elemValue.mValueGroup.get(i); 296 if (value.mStrValue != null) { 297 p.addElement(ImpsTags.Presence).addChild(ImpsTags.UserID, value.mStrValue); 298 } else { 299 p.addElement(translatePresence(value.mValueGroup)); 300 } 301 } 302 } else { 303 // PR=(<UserID>,<PresenceSubList>) 304 p.addElement(translatePresence(elemValue.mValueGroup)); 305 } 306 } 307 break; 308 309 case ELEM_OTHER_SIMPLE: 310 p.addElement(translateSimpleElem(elemCode, elemValue)); 311 break; 312 313 default: 314 throw new ParserException("Unsupported element " + elemValue); 315 } 316 } 317 translatePresence(ArrayList<ParamValue> valueGroup)318 private static PrimitiveElement translatePresence(ArrayList<ParamValue> valueGroup) 319 throws ParserException { 320 checkGroupValue(valueGroup, UNCERTAIN_GROUP_SIZE); 321 322 PrimitiveElement presence = new PrimitiveElement(ImpsTags.Presence); 323 if (valueGroup.get(0).mStrValue == null) { 324 throw new ParserException("UserID must have string value!"); 325 } 326 presence.addChild(ImpsTags.UserID, valueGroup.get(0).mStrValue); 327 328 if (valueGroup.size() > 1) { 329 // has presence sub list 330 presence.addChild(translatePresenceSubList(valueGroup.get(1))); 331 } 332 333 return presence; 334 } 335 translatePresenceSubList(ParamValue value)336 private static PrimitiveElement translatePresenceSubList(ParamValue value) 337 throws ParserException { 338 checkGroupValue(value.mValueGroup, UNCERTAIN_GROUP_SIZE); 339 340 PrimitiveElement presenceSubList = new PrimitiveElement(ImpsTags.PresenceSubList); 341 342 int groupSize = value.mValueGroup.size(); 343 for (int i = 0; i < groupSize; i++) { 344 ParamValue v = value.mValueGroup.get(i); 345 if (v.mStrValue != null) { 346 throw new ParserException("Unexpected string value for presence attribute"); 347 } 348 349 presenceSubList.addChild(translatePresenceAttribute(v.mValueGroup)); 350 } 351 352 return presenceSubList; 353 } 354 355 // <attribute>[,<qualifier>][,<value>] 356 // <attribute>[,<qualifier>,<sub-attribute>] translatePresenceAttribute( ArrayList<ParamValue> valueGroup)357 private static PrimitiveElement translatePresenceAttribute( 358 ArrayList<ParamValue> valueGroup) throws ParserException { 359 String type = valueGroup.get(0).mStrValue; 360 if (type == null) { 361 return null; 362 } 363 364 String tag = PtsCodes.getPresenceAttributeElement(type); 365 if (tag == null) { 366 return null; 367 } 368 369 PrimitiveElement paElem = new PrimitiveElement(tag); 370 if (valueGroup.size() == 2) { 371 // no qualifier 372 translateAttributeValue(paElem, valueGroup.get(1), false); 373 }else if (valueGroup.size() == 3) { 374 // has qualifier, and it should has no group value 375 ParamValue qualifierValue = valueGroup.get(1); 376 if (qualifierValue.mStrValue == null) { 377 throw new ParserException("Qualifier value can't be group value!"); 378 } 379 380 if (!"".equals(qualifierValue.mStrValue)) { 381 paElem.addChild(ImpsTags.Qualifier, qualifierValue.mStrValue); 382 } 383 384 translateAttributeValue(paElem, valueGroup.get(2), true); 385 } else { 386 return null; 387 } 388 389 return paElem; 390 } 391 translateAttributeValue(PrimitiveElement paElem, ParamValue v, boolean hasQualifier)392 private static void translateAttributeValue(PrimitiveElement paElem, 393 ParamValue v, boolean hasQualifier) throws ParserException { 394 if (v.mStrValue == null) { 395 // sub-attribute as value 396 checkGroupValue(v.mValueGroup, UNCERTAIN_GROUP_SIZE); 397 if (v.mValueGroup.get(0).mStrValue != null) { 398 paElem.addChild(translatePresenceAttribute(v.mValueGroup)); 399 } else { 400 int groupSize = v.mValueGroup.size(); 401 for (int i = 0; i < groupSize; i++) { 402 ParamValue value = v.mValueGroup.get(i); 403 if (value.mStrValue != null) { 404 throw new ParserException("Presence Attribute value error!"); 405 } 406 407 checkGroupValue(value.mValueGroup, UNCERTAIN_GROUP_SIZE); 408 paElem.addChild(translatePresenceAttribute(value.mValueGroup)); 409 } 410 } 411 } else { 412 // single simple value 413 if (hasQualifier) { 414 paElem.addChild(ImpsTags.PresenceValue, PtsCodes.getPAValue(v.mStrValue)); 415 } else { 416 paElem.setContents(PtsCodes.getPAValue(v.mStrValue)); 417 } 418 } 419 } 420 checkGroupValue(ArrayList<ParamValue> valueGroup, int expectedGroupSize)421 private static void checkGroupValue(ArrayList<ParamValue> valueGroup, 422 int expectedGroupSize) throws ParserException { 423 if (valueGroup == null 424 || (expectedGroupSize != UNCERTAIN_GROUP_SIZE 425 && valueGroup.size() != expectedGroupSize)) { 426 throw new ParserException("Invalid group value!"); 427 } 428 429 int groupSize = valueGroup.size(); 430 for (int i = 0; i < groupSize; i++) { 431 if (valueGroup.get(i) == null) { 432 throw new ParserException("Invalid group value!"); 433 } 434 } 435 } 436 translateCapabilityList(ParamValue elemValue)437 private static PrimitiveElement translateCapabilityList(ParamValue elemValue) 438 throws ParserException { 439 PrimitiveElement elem = new PrimitiveElement(ImpsTags.AgreedCapabilityList); 440 ArrayList<ParamValue> params = elemValue.mValueGroup; 441 if (params != null) { 442 checkGroupValue(params, UNCERTAIN_GROUP_SIZE); 443 int paramsSize = params.size(); 444 for (int i = 0; i < paramsSize; i++) { 445 ArrayList<ParamValue> capElemGroup = params.get(i).mValueGroup; 446 checkGroupValue(capElemGroup, 2); 447 448 String capElemCode = capElemGroup.get(0).mStrValue; 449 String capElemName; 450 if (capElemCode == null 451 || (capElemName = PtsCodes.getCapElement(capElemCode)) == null) { 452 throw new ParserException("Unknown capability element " 453 + capElemCode); 454 } 455 String capElemValue = capElemGroup.get(1).mStrValue; 456 if (capElemValue == null) { 457 throw new ParserException("Illegal capability value for " 458 + capElemCode); 459 } 460 capElemValue = PtsCodes.getCapValue(capElemValue); 461 462 elem.addChild(capElemName, capElemValue); 463 } 464 } 465 return elem; 466 } 467 translateServiceTree(String elemCode, ParamValue elemValue)468 private static PrimitiveElement translateServiceTree(String elemCode, 469 ParamValue elemValue) throws ParserException { 470 String elemName = PtsCodes.getElement(elemCode); 471 PrimitiveElement elem = new PrimitiveElement(elemName); 472 // TODO: translate the service tree. 473 return elem; 474 } 475 translateSimpleElem(String elemCode, ParamValue value)476 private static PrimitiveElement translateSimpleElem(String elemCode, ParamValue value) 477 throws ParserException { 478 String elemName = PtsCodes.getElement(elemCode); 479 if (elemName == null) { 480 throw new ParserException("Unrecognized parameter " + elemCode); 481 } 482 483 PrimitiveElement elem = new PrimitiveElement(elemName); 484 if (value.mStrValue != null) { 485 elem.setContents(value.mStrValue); 486 } else { 487 throw new ParserException("Don't know how to handle parameters for " 488 + elemName); 489 } 490 491 return elem; 492 } 493 parseParams()494 private HashMap<String, ParamValue> parseParams() throws ParserException { 495 int pos = mPos; 496 StringBuilder buf = mStringBuf; 497 int len = buf.length(); 498 HashMap<String, ParamValue> ret = new HashMap<String, ParamValue>(); 499 500 String paramName; 501 ParamValue paramValue; 502 503 while (pos < len) { 504 int nameStart = pos; 505 while (pos < len) { 506 char ch = buf.charAt(pos); 507 if (ch == ' ' || ch == '=') { 508 break; 509 } 510 pos++; 511 } 512 if (nameStart == pos) { 513 throw new ParserException("Missing parameter name near " + pos); 514 } 515 paramName = buf.substring(nameStart, pos); 516 if (pos < len && buf.charAt(pos) == '=') { 517 pos++; 518 mPos = pos; 519 paramValue = parseParamValue(); 520 pos = mPos; 521 } else { 522 paramValue = null; 523 } 524 ret.put(paramName, paramValue); 525 526 if (pos < len) { 527 // more parameters ahead 528 match(' '); 529 pos = mPos; 530 } 531 } 532 533 return ret; 534 } 535 parseParamValue()536 private ParamValue parseParamValue() throws ParserException { 537 int pos = mPos; 538 StringBuilder buf = mStringBuf; 539 int len = buf.length(); 540 541 if (pos == len) { 542 throw new ParserException("Missing parameter value near " + pos); 543 } 544 ParamValue value = new ParamValue(); 545 546 char ch = buf.charAt(pos); 547 if (ch == '(') { 548 // value list 549 pos++; 550 ArrayList<ParamValue> valueGroup = new ArrayList<ParamValue>(); 551 while (pos < len) { 552 mPos = pos; 553 valueGroup.add(parseParamValue()); 554 pos = mPos; 555 if (pos == len) { 556 throw new ParserException("Unexpected parameter end"); 557 } 558 if (buf.charAt(pos) != ',') { 559 break; 560 } 561 pos++; 562 } 563 mPos = pos; 564 match(')'); 565 if (valueGroup.isEmpty()) { 566 throw new ParserException("Empty value group near " + mPos); 567 } 568 value.mValueGroup = valueGroup; 569 } else { 570 // single value 571 if (ch == '"') { 572 // quoted value 573 pos++; 574 StringBuilder escapedValue = new StringBuilder(); 575 boolean quotedEnd = false; 576 while (pos < len) { 577 ch = buf.charAt(pos); 578 pos++; 579 if (ch == '"') { 580 if (pos < len && buf.charAt(pos) == '"') { 581 // "doubled" quote 582 pos++; 583 } else { 584 quotedEnd = true; 585 break; 586 } 587 } 588 escapedValue.append(ch); 589 } 590 if (!quotedEnd) { 591 throw new ParserException("Unexpected quoted parameter end"); 592 } 593 value.mStrValue = escapedValue.toString(); 594 } else { 595 int valueStart = pos; 596 while (pos < len) { 597 ch = buf.charAt(pos); 598 if (ch == ',' || ch == ')' || ch == ' ') { 599 break; 600 } 601 if ("\"(=&".indexOf(ch) != -1) { 602 throw new ParserException("Special character " + ch 603 + " must be quoted"); 604 } 605 pos++; 606 } 607 value.mStrValue = buf.substring(valueStart, pos); 608 } 609 mPos = pos; 610 } 611 612 return value; 613 } 614 match(char c)615 private void match(char c) throws ParserException { 616 if (mStringBuf.charAt(mPos) != c) { 617 throw new ParserException("Expected " + c + " at pos " + mPos); 618 } 619 mPos++; 620 } 621 622 /** 623 * Detect if this short message is a PTS encoded WV-primitive. 624 */ isPtsPrimitive(CharSequence msg)625 public static boolean isPtsPrimitive(CharSequence msg) 626 { 627 if (msg == null) { 628 return false; 629 } 630 Matcher m = sPreamplePattern.matcher(msg); 631 return m.matches(); 632 } 633 634 static final class ParamValue { 635 public String mStrValue; 636 public ArrayList<ParamValue> mValueGroup; 637 } 638 } 639