CommandParamsFactory.java revision 5767ae41f516736b666062091d4f1948eccca8f4
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony.cat; 18 19import android.graphics.Bitmap; 20import android.os.Handler; 21import android.os.Message; 22 23import com.android.internal.telephony.GsmAlphabet; 24import com.android.internal.telephony.uicc.IccFileHandler; 25 26import java.util.Iterator; 27import java.util.List; 28import static com.android.internal.telephony.cat.CatCmdMessage. 29 SetupEventListConstants.USER_ACTIVITY_EVENT; 30import static com.android.internal.telephony.cat.CatCmdMessage. 31 SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT; 32import static com.android.internal.telephony.cat.CatCmdMessage. 33 SetupEventListConstants.LANGUAGE_SELECTION_EVENT; 34import static com.android.internal.telephony.cat.CatCmdMessage. 35 SetupEventListConstants.BROWSER_TERMINATION_EVENT; 36import static com.android.internal.telephony.cat.CatCmdMessage. 37 SetupEventListConstants.BROWSING_STATUS_EVENT; 38/** 39 * Factory class, used for decoding raw byte arrays, received from baseband, 40 * into a CommandParams object. 41 * 42 */ 43class CommandParamsFactory extends Handler { 44 private static CommandParamsFactory sInstance = null; 45 private IconLoader mIconLoader; 46 private CommandParams mCmdParams = null; 47 private int mIconLoadState = LOAD_NO_ICON; 48 private RilMessageDecoder mCaller = null; 49 private boolean mloadIcon = false; 50 51 // constants 52 static final int MSG_ID_LOAD_ICON_DONE = 1; 53 54 // loading icons state parameters. 55 static final int LOAD_NO_ICON = 0; 56 static final int LOAD_SINGLE_ICON = 1; 57 static final int LOAD_MULTI_ICONS = 2; 58 59 // Command Qualifier values for refresh command 60 static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00; 61 static final int REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02; 62 static final int REFRESH_NAA_INIT = 0x03; 63 static final int REFRESH_UICC_RESET = 0x04; 64 65 // Command Qualifier values for PLI command 66 static final int DTTZ_SETTING = 0x03; 67 static final int LANGUAGE_SETTING = 0x04; 68 69 // As per TS 102.223 Annex C, Structure of CAT communications, 70 // the APDU length can be max 255 bytes. This leaves only 239 bytes for user 71 // input string. CMD details TLV + Device IDs TLV + Result TLV + Other 72 // details of TextString TLV not including user input take 16 bytes. 73 // 74 // If UCS2 encoding is used, maximum 118 UCS2 chars can be encoded in 238 bytes. 75 // Each UCS2 char takes 2 bytes. Byte Order Mask(BOM), 0xFEFF takes 2 bytes. 76 // 77 // If GSM 7 bit default(use 8 bits to represent a 7 bit char) format is used, 78 // maximum 239 chars can be encoded in 239 bytes since each char takes 1 byte. 79 // 80 // No issues for GSM 7 bit packed format encoding. 81 82 private static final int MAX_GSM7_DEFAULT_CHARS = 239; 83 private static final int MAX_UCS2_CHARS = 118; 84 85 static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, 86 IccFileHandler fh) { 87 if (sInstance != null) { 88 return sInstance; 89 } 90 if (fh != null) { 91 return new CommandParamsFactory(caller, fh); 92 } 93 return null; 94 } 95 96 private CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh) { 97 mCaller = caller; 98 mIconLoader = IconLoader.getInstance(this, fh); 99 } 100 101 private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) { 102 CommandDetails cmdDet = null; 103 104 if (ctlvs != null) { 105 // Search for the Command Details object. 106 ComprehensionTlv ctlvCmdDet = searchForTag( 107 ComprehensionTlvTag.COMMAND_DETAILS, ctlvs); 108 if (ctlvCmdDet != null) { 109 try { 110 cmdDet = ValueParser.retrieveCommandDetails(ctlvCmdDet); 111 } catch (ResultException e) { 112 CatLog.d(this, 113 "processCommandDetails: Failed to procees command details e=" + e); 114 } 115 } 116 } 117 return cmdDet; 118 } 119 120 void make(BerTlv berTlv) { 121 if (berTlv == null) { 122 return; 123 } 124 // reset global state parameters. 125 mCmdParams = null; 126 mIconLoadState = LOAD_NO_ICON; 127 // only proactive command messages are processed. 128 if (berTlv.getTag() != BerTlv.BER_PROACTIVE_COMMAND_TAG) { 129 sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); 130 return; 131 } 132 boolean cmdPending = false; 133 List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs(); 134 // process command dtails from the tlv list. 135 CommandDetails cmdDet = processCommandDetails(ctlvs); 136 if (cmdDet == null) { 137 sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); 138 return; 139 } 140 141 // extract command type enumeration from the raw value stored inside 142 // the Command Details object. 143 AppInterface.CommandType cmdType = AppInterface.CommandType 144 .fromInt(cmdDet.typeOfCommand); 145 if (cmdType == null) { 146 // This PROACTIVE COMMAND is presently not handled. Hence set 147 // result code as BEYOND_TERMINAL_CAPABILITY in TR. 148 mCmdParams = new CommandParams(cmdDet); 149 sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); 150 return; 151 } 152 153 // proactive command length is incorrect. 154 if (!berTlv.isLengthValid()) { 155 mCmdParams = new CommandParams(cmdDet); 156 sendCmdParams(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 157 return; 158 } 159 160 try { 161 switch (cmdType) { 162 case SET_UP_MENU: 163 cmdPending = processSelectItem(cmdDet, ctlvs); 164 break; 165 case SELECT_ITEM: 166 cmdPending = processSelectItem(cmdDet, ctlvs); 167 break; 168 case DISPLAY_TEXT: 169 cmdPending = processDisplayText(cmdDet, ctlvs); 170 break; 171 case SET_UP_IDLE_MODE_TEXT: 172 cmdPending = processSetUpIdleModeText(cmdDet, ctlvs); 173 break; 174 case GET_INKEY: 175 cmdPending = processGetInkey(cmdDet, ctlvs); 176 break; 177 case GET_INPUT: 178 cmdPending = processGetInput(cmdDet, ctlvs); 179 break; 180 case SEND_DTMF: 181 case SEND_SMS: 182 case SEND_SS: 183 case SEND_USSD: 184 cmdPending = processEventNotify(cmdDet, ctlvs); 185 break; 186 case GET_CHANNEL_STATUS: 187 case SET_UP_CALL: 188 cmdPending = processSetupCall(cmdDet, ctlvs); 189 break; 190 case REFRESH: 191 processRefresh(cmdDet, ctlvs); 192 cmdPending = false; 193 break; 194 case LAUNCH_BROWSER: 195 cmdPending = processLaunchBrowser(cmdDet, ctlvs); 196 break; 197 case PLAY_TONE: 198 cmdPending = processPlayTone(cmdDet, ctlvs); 199 break; 200 case SET_UP_EVENT_LIST: 201 cmdPending = processSetUpEventList(cmdDet, ctlvs); 202 break; 203 case PROVIDE_LOCAL_INFORMATION: 204 cmdPending = processProvideLocalInfo(cmdDet, ctlvs); 205 break; 206 case OPEN_CHANNEL: 207 case CLOSE_CHANNEL: 208 case RECEIVE_DATA: 209 case SEND_DATA: 210 cmdPending = processBIPClient(cmdDet, ctlvs); 211 break; 212 default: 213 // unsupported proactive commands 214 mCmdParams = new CommandParams(cmdDet); 215 sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); 216 return; 217 } 218 } catch (ResultException e) { 219 CatLog.d(this, "make: caught ResultException e=" + e); 220 mCmdParams = new CommandParams(cmdDet); 221 sendCmdParams(e.result()); 222 return; 223 } 224 if (!cmdPending) { 225 sendCmdParams(ResultCode.OK); 226 } 227 } 228 229 @Override 230 public void handleMessage(Message msg) { 231 switch (msg.what) { 232 case MSG_ID_LOAD_ICON_DONE: 233 sendCmdParams(setIcons(msg.obj)); 234 break; 235 } 236 } 237 238 private ResultCode setIcons(Object data) { 239 Bitmap[] icons = null; 240 int iconIndex = 0; 241 242 if (data == null) { 243 CatLog.d(this, "Optional Icon data is NULL"); 244 mCmdParams.mLoadIconFailed = true; 245 mloadIcon = false; 246 /** In case of icon load fail consider the 247 ** received proactive command as valid (sending RESULT OK) as 248 ** The result code, 'PRFRMD_ICON_NOT_DISPLAYED' will be added in the 249 ** terminal response by CatService/StkAppService if needed based on 250 ** the value of mLoadIconFailed. 251 */ 252 return ResultCode.OK; 253 } 254 switch(mIconLoadState) { 255 case LOAD_SINGLE_ICON: 256 mCmdParams.setIcon((Bitmap) data); 257 break; 258 case LOAD_MULTI_ICONS: 259 icons = (Bitmap[]) data; 260 // set each item icon. 261 for (Bitmap icon : icons) { 262 mCmdParams.setIcon(icon); 263 if (icon == null && mloadIcon) { 264 CatLog.d(this, "Optional Icon data is NULL while loading multi icons"); 265 mCmdParams.mLoadIconFailed = true; 266 } 267 } 268 break; 269 } 270 return ResultCode.OK; 271 } 272 273 private void sendCmdParams(ResultCode resCode) { 274 mCaller.sendMsgParamsDecoded(resCode, mCmdParams); 275 } 276 277 /** 278 * Search for a COMPREHENSION-TLV object with the given tag from a list 279 * 280 * @param tag A tag to search for 281 * @param ctlvs List of ComprehensionTlv objects used to search in 282 * 283 * @return A ComprehensionTlv object that has the tag value of {@code tag}. 284 * If no object is found with the tag, null is returned. 285 */ 286 private ComprehensionTlv searchForTag(ComprehensionTlvTag tag, 287 List<ComprehensionTlv> ctlvs) { 288 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 289 return searchForNextTag(tag, iter); 290 } 291 292 /** 293 * Search for the next COMPREHENSION-TLV object with the given tag from a 294 * list iterated by {@code iter}. {@code iter} points to the object next to 295 * the found object when this method returns. Used for searching the same 296 * list for similar tags, usually item id. 297 * 298 * @param tag A tag to search for 299 * @param iter Iterator for ComprehensionTlv objects used for search 300 * 301 * @return A ComprehensionTlv object that has the tag value of {@code tag}. 302 * If no object is found with the tag, null is returned. 303 */ 304 private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag, 305 Iterator<ComprehensionTlv> iter) { 306 int tagValue = tag.value(); 307 while (iter.hasNext()) { 308 ComprehensionTlv ctlv = iter.next(); 309 if (ctlv.getTag() == tagValue) { 310 return ctlv; 311 } 312 } 313 return null; 314 } 315 316 /** 317 * Processes DISPLAY_TEXT proactive command from the SIM card. 318 * 319 * @param cmdDet Command Details container object. 320 * @param ctlvs List of ComprehensionTlv objects following Command Details 321 * object and Device Identities object within the proactive command 322 * @return true if the command is processing is pending and additional 323 * asynchronous processing is required. 324 * @throws ResultException 325 */ 326 private boolean processDisplayText(CommandDetails cmdDet, 327 List<ComprehensionTlv> ctlvs) 328 throws ResultException { 329 330 CatLog.d(this, "process DisplayText"); 331 332 TextMessage textMsg = new TextMessage(); 333 IconId iconId = null; 334 335 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 336 ctlvs); 337 if (ctlv != null) { 338 textMsg.text = ValueParser.retrieveTextString(ctlv); 339 } 340 // If the tlv object doesn't exist or the it is a null object reply 341 // with command not understood. 342 if (textMsg.text == null) { 343 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 344 } 345 346 ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs); 347 if (ctlv != null) { 348 textMsg.responseNeeded = false; 349 } 350 // parse icon identifier 351 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 352 if (ctlv != null) { 353 iconId = ValueParser.retrieveIconId(ctlv); 354 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 355 } 356 // parse tone duration 357 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 358 if (ctlv != null) { 359 textMsg.duration = ValueParser.retrieveDuration(ctlv); 360 } 361 362 // Parse command qualifier parameters. 363 textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0; 364 textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0; 365 366 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 367 368 if (iconId != null) { 369 mloadIcon = true; 370 mIconLoadState = LOAD_SINGLE_ICON; 371 mIconLoader.loadIcon(iconId.recordNumber, this 372 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 373 return true; 374 } 375 return false; 376 } 377 378 /** 379 * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card. 380 * 381 * @param cmdDet Command Details container object. 382 * @param ctlvs List of ComprehensionTlv objects following Command Details 383 * object and Device Identities object within the proactive command 384 * @return true if the command is processing is pending and additional 385 * asynchronous processing is required. 386 * @throws ResultException 387 */ 388 private boolean processSetUpIdleModeText(CommandDetails cmdDet, 389 List<ComprehensionTlv> ctlvs) throws ResultException { 390 391 CatLog.d(this, "process SetUpIdleModeText"); 392 393 TextMessage textMsg = new TextMessage(); 394 IconId iconId = null; 395 396 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 397 ctlvs); 398 if (ctlv != null) { 399 textMsg.text = ValueParser.retrieveTextString(ctlv); 400 } 401 402 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 403 if (ctlv != null) { 404 iconId = ValueParser.retrieveIconId(ctlv); 405 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 406 } 407 408 /* 409 * If the tlv object doesn't contain text and the icon is not self 410 * explanatory then reply with command not understood. 411 */ 412 413 if (textMsg.text == null && iconId != null && !textMsg.iconSelfExplanatory) { 414 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 415 } 416 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 417 418 if (iconId != null) { 419 mloadIcon = true; 420 mIconLoadState = LOAD_SINGLE_ICON; 421 mIconLoader.loadIcon(iconId.recordNumber, this 422 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 423 return true; 424 } 425 return false; 426 } 427 428 /** 429 * Processes GET_INKEY proactive command from the SIM card. 430 * 431 * @param cmdDet Command Details container object. 432 * @param ctlvs List of ComprehensionTlv objects following Command Details 433 * object and Device Identities object within the proactive command 434 * @return true if the command is processing is pending and additional 435 * asynchronous processing is required. 436 * @throws ResultException 437 */ 438 private boolean processGetInkey(CommandDetails cmdDet, 439 List<ComprehensionTlv> ctlvs) throws ResultException { 440 441 CatLog.d(this, "process GetInkey"); 442 443 Input input = new Input(); 444 IconId iconId = null; 445 446 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 447 ctlvs); 448 if (ctlv != null) { 449 input.text = ValueParser.retrieveTextString(ctlv); 450 } else { 451 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 452 } 453 // parse icon identifier 454 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 455 if (ctlv != null) { 456 iconId = ValueParser.retrieveIconId(ctlv); 457 } 458 459 // parse duration 460 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 461 if (ctlv != null) { 462 input.duration = ValueParser.retrieveDuration(ctlv); 463 } 464 465 input.minLen = 1; 466 input.maxLen = 1; 467 468 input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; 469 input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; 470 input.yesNo = (cmdDet.commandQualifier & 0x04) != 0; 471 input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 472 input.echo = true; 473 474 mCmdParams = new GetInputParams(cmdDet, input); 475 476 if (iconId != null) { 477 mloadIcon = true; 478 mIconLoadState = LOAD_SINGLE_ICON; 479 mIconLoader.loadIcon(iconId.recordNumber, this 480 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 481 return true; 482 } 483 return false; 484 } 485 486 /** 487 * Processes GET_INPUT proactive command from the SIM card. 488 * 489 * @param cmdDet Command Details container object. 490 * @param ctlvs List of ComprehensionTlv objects following Command Details 491 * object and Device Identities object within the proactive command 492 * @return true if the command is processing is pending and additional 493 * asynchronous processing is required. 494 * @throws ResultException 495 */ 496 private boolean processGetInput(CommandDetails cmdDet, 497 List<ComprehensionTlv> ctlvs) throws ResultException { 498 499 CatLog.d(this, "process GetInput"); 500 501 Input input = new Input(); 502 IconId iconId = null; 503 504 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 505 ctlvs); 506 if (ctlv != null) { 507 input.text = ValueParser.retrieveTextString(ctlv); 508 } else { 509 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 510 } 511 512 ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs); 513 if (ctlv != null) { 514 try { 515 byte[] rawValue = ctlv.getRawValue(); 516 int valueIndex = ctlv.getValueIndex(); 517 input.minLen = rawValue[valueIndex] & 0xff; 518 input.maxLen = rawValue[valueIndex + 1] & 0xff; 519 } catch (IndexOutOfBoundsException e) { 520 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 521 } 522 } else { 523 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 524 } 525 526 ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs); 527 if (ctlv != null) { 528 input.defaultText = ValueParser.retrieveTextString(ctlv); 529 } 530 // parse icon identifier 531 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 532 if (ctlv != null) { 533 iconId = ValueParser.retrieveIconId(ctlv); 534 } 535 536 input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; 537 input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; 538 input.echo = (cmdDet.commandQualifier & 0x04) == 0; 539 input.packed = (cmdDet.commandQualifier & 0x08) != 0; 540 input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 541 542 // Truncate the maxLen if it exceeds the max number of chars that can 543 // be encoded. Limit depends on DCS in Command Qualifier. 544 if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) { 545 CatLog.d(this, "UCS2: received maxLen = " + input.maxLen + 546 ", truncating to " + MAX_UCS2_CHARS); 547 input.maxLen = MAX_UCS2_CHARS; 548 } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) { 549 CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen + 550 ", truncating to " + MAX_GSM7_DEFAULT_CHARS); 551 input.maxLen = MAX_GSM7_DEFAULT_CHARS; 552 } 553 554 mCmdParams = new GetInputParams(cmdDet, input); 555 556 if (iconId != null) { 557 mloadIcon = true; 558 mIconLoadState = LOAD_SINGLE_ICON; 559 mIconLoader.loadIcon(iconId.recordNumber, this 560 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 561 return true; 562 } 563 return false; 564 } 565 566 /** 567 * Processes REFRESH proactive command from the SIM card. 568 * 569 * @param cmdDet Command Details container object. 570 * @param ctlvs List of ComprehensionTlv objects following Command Details 571 * object and Device Identities object within the proactive command 572 */ 573 private boolean processRefresh(CommandDetails cmdDet, 574 List<ComprehensionTlv> ctlvs) { 575 576 CatLog.d(this, "process Refresh"); 577 578 // REFRESH proactive command is rerouted by the baseband and handled by 579 // the telephony layer. IDLE TEXT should be removed for a REFRESH command 580 // with "initialization" or "reset" 581 switch (cmdDet.commandQualifier) { 582 case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE: 583 case REFRESH_NAA_INIT_AND_FILE_CHANGE: 584 case REFRESH_NAA_INIT: 585 case REFRESH_UICC_RESET: 586 mCmdParams = new DisplayTextParams(cmdDet, null); 587 break; 588 } 589 return false; 590 } 591 592 /** 593 * Processes SELECT_ITEM proactive command from the SIM card. 594 * 595 * @param cmdDet Command Details container object. 596 * @param ctlvs List of ComprehensionTlv objects following Command Details 597 * object and Device Identities object within the proactive command 598 * @return true if the command is processing is pending and additional 599 * asynchronous processing is required. 600 * @throws ResultException 601 */ 602 private boolean processSelectItem(CommandDetails cmdDet, 603 List<ComprehensionTlv> ctlvs) throws ResultException { 604 605 CatLog.d(this, "process SelectItem"); 606 607 Menu menu = new Menu(); 608 IconId titleIconId = null; 609 ItemsIconId itemsIconId = null; 610 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 611 612 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, 613 ctlvs); 614 if (ctlv != null) { 615 menu.title = ValueParser.retrieveAlphaId(ctlv); 616 } 617 618 while (true) { 619 ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); 620 if (ctlv != null) { 621 menu.items.add(ValueParser.retrieveItem(ctlv)); 622 } else { 623 break; 624 } 625 } 626 627 // We must have at least one menu item. 628 if (menu.items.size() == 0) { 629 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 630 } 631 632 ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); 633 if (ctlv != null) { 634 // CAT items are listed 1...n while list start at 0, need to 635 // subtract one. 636 menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1; 637 } 638 639 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 640 if (ctlv != null) { 641 mIconLoadState = LOAD_SINGLE_ICON; 642 titleIconId = ValueParser.retrieveIconId(ctlv); 643 menu.titleIconSelfExplanatory = titleIconId.selfExplanatory; 644 } 645 646 ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs); 647 if (ctlv != null) { 648 mIconLoadState = LOAD_MULTI_ICONS; 649 itemsIconId = ValueParser.retrieveItemsIconId(ctlv); 650 menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory; 651 } 652 653 boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0; 654 if (presentTypeSpecified) { 655 if ((cmdDet.commandQualifier & 0x02) == 0) { 656 menu.presentationType = PresentationType.DATA_VALUES; 657 } else { 658 menu.presentationType = PresentationType.NAVIGATION_OPTIONS; 659 } 660 } 661 menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0; 662 menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 663 664 mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null); 665 666 // Load icons data if needed. 667 switch(mIconLoadState) { 668 case LOAD_NO_ICON: 669 return false; 670 case LOAD_SINGLE_ICON: 671 mloadIcon = true; 672 mIconLoader.loadIcon(titleIconId.recordNumber, this 673 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 674 break; 675 case LOAD_MULTI_ICONS: 676 int[] recordNumbers = itemsIconId.recordNumbers; 677 if (titleIconId != null) { 678 // Create a new array for all the icons (title and items). 679 recordNumbers = new int[itemsIconId.recordNumbers.length + 1]; 680 recordNumbers[0] = titleIconId.recordNumber; 681 System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers, 682 1, itemsIconId.recordNumbers.length); 683 } 684 mloadIcon = true; 685 mIconLoader.loadIcons(recordNumbers, this 686 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 687 break; 688 } 689 return true; 690 } 691 692 /** 693 * Processes EVENT_NOTIFY message from baseband. 694 * 695 * @param cmdDet Command Details container object. 696 * @param ctlvs List of ComprehensionTlv objects following Command Details 697 * object and Device Identities object within the proactive command 698 * @return true if the command is processing is pending and additional 699 * asynchronous processing is required. 700 */ 701 private boolean processEventNotify(CommandDetails cmdDet, 702 List<ComprehensionTlv> ctlvs) throws ResultException { 703 704 CatLog.d(this, "process EventNotify"); 705 706 TextMessage textMsg = new TextMessage(); 707 IconId iconId = null; 708 709 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, 710 ctlvs); 711 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 712 713 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 714 if (ctlv != null) { 715 iconId = ValueParser.retrieveIconId(ctlv); 716 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 717 } 718 719 textMsg.responseNeeded = false; 720 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 721 722 if (iconId != null) { 723 mloadIcon = true; 724 mIconLoadState = LOAD_SINGLE_ICON; 725 mIconLoader.loadIcon(iconId.recordNumber, this 726 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 727 return true; 728 } 729 return false; 730 } 731 732 /** 733 * Processes SET_UP_EVENT_LIST proactive command from the SIM card. 734 * 735 * @param cmdDet Command Details object retrieved. 736 * @param ctlvs List of ComprehensionTlv objects following Command Details 737 * object and Device Identities object within the proactive command 738 * @return false. This function always returns false meaning that the command 739 * processing is not pending and additional asynchronous processing 740 * is not required. 741 */ 742 private boolean processSetUpEventList(CommandDetails cmdDet, 743 List<ComprehensionTlv> ctlvs) { 744 745 CatLog.d(this, "process SetUpEventList"); 746 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, ctlvs); 747 if (ctlv != null) { 748 try { 749 byte[] rawValue = ctlv.getRawValue(); 750 int valueIndex = ctlv.getValueIndex(); 751 int valueLen = ctlv.getLength(); 752 int[] eventList = new int[valueLen]; 753 int eventValue = -1; 754 int i = 0; 755 while (valueLen > 0) { 756 eventValue = rawValue[valueIndex] & 0xff; 757 valueIndex++; 758 valueLen--; 759 760 switch (eventValue) { 761 case USER_ACTIVITY_EVENT: 762 case IDLE_SCREEN_AVAILABLE_EVENT: 763 case LANGUAGE_SELECTION_EVENT: 764 case BROWSER_TERMINATION_EVENT: 765 case BROWSING_STATUS_EVENT: 766 eventList[i] = eventValue; 767 i++; 768 break; 769 default: 770 break; 771 } 772 773 } 774 mCmdParams = new SetEventListParams(cmdDet, eventList); 775 } catch (IndexOutOfBoundsException e) { 776 CatLog.e(this, " IndexOutofBoundException in processSetUpEventList"); 777 } 778 } 779 return false; 780 } 781 782 /** 783 * Processes LAUNCH_BROWSER proactive command from the SIM card. 784 * 785 * @param cmdDet Command Details container object. 786 * @param ctlvs List of ComprehensionTlv objects following Command Details 787 * object and Device Identities object within the proactive command 788 * @return true if the command is processing is pending and additional 789 * asynchronous processing is required. 790 * @throws ResultException 791 */ 792 private boolean processLaunchBrowser(CommandDetails cmdDet, 793 List<ComprehensionTlv> ctlvs) throws ResultException { 794 795 CatLog.d(this, "process LaunchBrowser"); 796 797 TextMessage confirmMsg = new TextMessage(); 798 IconId iconId = null; 799 String url = null; 800 801 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs); 802 if (ctlv != null) { 803 try { 804 byte[] rawValue = ctlv.getRawValue(); 805 int valueIndex = ctlv.getValueIndex(); 806 int valueLen = ctlv.getLength(); 807 if (valueLen > 0) { 808 url = GsmAlphabet.gsm8BitUnpackedToString(rawValue, 809 valueIndex, valueLen); 810 } else { 811 url = null; 812 } 813 } catch (IndexOutOfBoundsException e) { 814 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 815 } 816 } 817 818 // parse alpha identifier. 819 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 820 confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); 821 822 // parse icon identifier 823 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 824 if (ctlv != null) { 825 iconId = ValueParser.retrieveIconId(ctlv); 826 confirmMsg.iconSelfExplanatory = iconId.selfExplanatory; 827 } 828 829 // parse command qualifier value. 830 LaunchBrowserMode mode; 831 switch (cmdDet.commandQualifier) { 832 case 0x00: 833 default: 834 mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED; 835 break; 836 case 0x02: 837 mode = LaunchBrowserMode.USE_EXISTING_BROWSER; 838 break; 839 case 0x03: 840 mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER; 841 break; 842 } 843 844 mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode); 845 846 if (iconId != null) { 847 mIconLoadState = LOAD_SINGLE_ICON; 848 mIconLoader.loadIcon(iconId.recordNumber, this 849 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 850 return true; 851 } 852 return false; 853 } 854 855 /** 856 * Processes PLAY_TONE proactive command from the SIM card. 857 * 858 * @param cmdDet Command Details container object. 859 * @param ctlvs List of ComprehensionTlv objects following Command Details 860 * object and Device Identities object within the proactive command 861 * @return true if the command is processing is pending and additional 862 * asynchronous processing is required.t 863 * @throws ResultException 864 */ 865 private boolean processPlayTone(CommandDetails cmdDet, 866 List<ComprehensionTlv> ctlvs) throws ResultException { 867 868 CatLog.d(this, "process PlayTone"); 869 870 Tone tone = null; 871 TextMessage textMsg = new TextMessage(); 872 Duration duration = null; 873 IconId iconId = null; 874 875 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs); 876 if (ctlv != null) { 877 // Nothing to do for null objects. 878 if (ctlv.getLength() > 0) { 879 try { 880 byte[] rawValue = ctlv.getRawValue(); 881 int valueIndex = ctlv.getValueIndex(); 882 int toneVal = rawValue[valueIndex]; 883 tone = Tone.fromInt(toneVal); 884 } catch (IndexOutOfBoundsException e) { 885 throw new ResultException( 886 ResultCode.CMD_DATA_NOT_UNDERSTOOD); 887 } 888 } 889 } 890 // parse alpha identifier 891 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 892 if (ctlv != null) { 893 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 894 } 895 // parse tone duration 896 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 897 if (ctlv != null) { 898 duration = ValueParser.retrieveDuration(ctlv); 899 } 900 // parse icon identifier 901 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 902 if (ctlv != null) { 903 iconId = ValueParser.retrieveIconId(ctlv); 904 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 905 } 906 907 boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00; 908 909 textMsg.responseNeeded = false; 910 mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate); 911 912 if (iconId != null) { 913 mIconLoadState = LOAD_SINGLE_ICON; 914 mIconLoader.loadIcon(iconId.recordNumber, this 915 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 916 return true; 917 } 918 return false; 919 } 920 921 /** 922 * Processes SETUP_CALL proactive command from the SIM card. 923 * 924 * @param cmdDet Command Details object retrieved from the proactive command 925 * object 926 * @param ctlvs List of ComprehensionTlv objects following Command Details 927 * object and Device Identities object within the proactive command 928 * @return true if the command is processing is pending and additional 929 * asynchronous processing is required. 930 */ 931 private boolean processSetupCall(CommandDetails cmdDet, 932 List<ComprehensionTlv> ctlvs) throws ResultException { 933 CatLog.d(this, "process SetupCall"); 934 935 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 936 ComprehensionTlv ctlv = null; 937 // User confirmation phase message. 938 TextMessage confirmMsg = new TextMessage(); 939 // Call set up phase message. 940 TextMessage callMsg = new TextMessage(); 941 IconId confirmIconId = null; 942 IconId callIconId = null; 943 944 // get confirmation message string. 945 ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); 946 confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); 947 948 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 949 if (ctlv != null) { 950 confirmIconId = ValueParser.retrieveIconId(ctlv); 951 confirmMsg.iconSelfExplanatory = confirmIconId.selfExplanatory; 952 } 953 954 // get call set up message string. 955 ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); 956 if (ctlv != null) { 957 callMsg.text = ValueParser.retrieveAlphaId(ctlv); 958 } 959 960 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 961 if (ctlv != null) { 962 callIconId = ValueParser.retrieveIconId(ctlv); 963 callMsg.iconSelfExplanatory = callIconId.selfExplanatory; 964 } 965 966 mCmdParams = new CallSetupParams(cmdDet, confirmMsg, callMsg); 967 968 if (confirmIconId != null || callIconId != null) { 969 mIconLoadState = LOAD_MULTI_ICONS; 970 int[] recordNumbers = new int[2]; 971 recordNumbers[0] = confirmIconId != null 972 ? confirmIconId.recordNumber : -1; 973 recordNumbers[1] = callIconId != null ? callIconId.recordNumber 974 : -1; 975 976 mIconLoader.loadIcons(recordNumbers, this 977 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 978 return true; 979 } 980 return false; 981 } 982 983 private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) 984 throws ResultException { 985 CatLog.d(this, "process ProvideLocalInfo"); 986 switch (cmdDet.commandQualifier) { 987 case DTTZ_SETTING: 988 CatLog.d(this, "PLI [DTTZ_SETTING]"); 989 mCmdParams = new CommandParams(cmdDet); 990 break; 991 case LANGUAGE_SETTING: 992 CatLog.d(this, "PLI [LANGUAGE_SETTING]"); 993 mCmdParams = new CommandParams(cmdDet); 994 break; 995 default: 996 CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported"); 997 mCmdParams = new CommandParams(cmdDet); 998 throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY); 999 } 1000 return false; 1001 } 1002 1003 private boolean processBIPClient(CommandDetails cmdDet, 1004 List<ComprehensionTlv> ctlvs) throws ResultException { 1005 AppInterface.CommandType commandType = 1006 AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 1007 if (commandType != null) { 1008 CatLog.d(this, "process "+ commandType.name()); 1009 } 1010 1011 TextMessage textMsg = new TextMessage(); 1012 IconId iconId = null; 1013 ComprehensionTlv ctlv = null; 1014 boolean has_alpha_id = false; 1015 1016 // parse alpha identifier 1017 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 1018 if (ctlv != null) { 1019 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 1020 CatLog.d(this, "alpha TLV text=" + textMsg.text); 1021 has_alpha_id = true; 1022 } 1023 1024 // parse icon identifier 1025 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 1026 if (ctlv != null) { 1027 iconId = ValueParser.retrieveIconId(ctlv); 1028 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 1029 } 1030 1031 textMsg.responseNeeded = false; 1032 mCmdParams = new BIPClientParams(cmdDet, textMsg, has_alpha_id); 1033 1034 if (iconId != null) { 1035 mIconLoadState = LOAD_SINGLE_ICON; 1036 mIconLoader.loadIcon(iconId.recordNumber, obtainMessage(MSG_ID_LOAD_ICON_DONE)); 1037 return true; 1038 } 1039 return false; 1040 } 1041 1042 public void dispose() { 1043 mIconLoader.dispose(); 1044 mIconLoader = null; 1045 mCmdParams = null; 1046 mCaller = null; 1047 sInstance = null; 1048 } 1049} 1050