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 17 package com.android.internal.telephony.cat; 18 19 import android.graphics.Bitmap; 20 import android.os.Handler; 21 import android.os.Message; 22 23 import com.android.internal.telephony.GsmAlphabet; 24 import com.android.internal.telephony.uicc.IccFileHandler; 25 26 import java.util.Iterator; 27 import java.util.List; 28 import static com.android.internal.telephony.cat.CatCmdMessage. 29 SetupEventListConstants.USER_ACTIVITY_EVENT; 30 import static com.android.internal.telephony.cat.CatCmdMessage. 31 SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT; 32 import static com.android.internal.telephony.cat.CatCmdMessage. 33 SetupEventListConstants.LANGUAGE_SELECTION_EVENT; 34 import static com.android.internal.telephony.cat.CatCmdMessage. 35 SetupEventListConstants.BROWSER_TERMINATION_EVENT; 36 import 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 */ 43 class 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 getInstance(RilMessageDecoder caller, IccFileHandler fh)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 CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh)96 private CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh) { 97 mCaller = caller; 98 mIconLoader = IconLoader.getInstance(this, fh); 99 } 100 processCommandDetails(List<ComprehensionTlv> ctlvs)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 make(BerTlv berTlv)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 handleMessage(Message msg)230 public void handleMessage(Message msg) { 231 switch (msg.what) { 232 case MSG_ID_LOAD_ICON_DONE: 233 if (mIconLoader != null) { 234 sendCmdParams(setIcons(msg.obj)); 235 } 236 break; 237 } 238 } 239 setIcons(Object data)240 private ResultCode setIcons(Object data) { 241 Bitmap[] icons = null; 242 int iconIndex = 0; 243 244 if (data == null) { 245 CatLog.d(this, "Optional Icon data is NULL"); 246 mCmdParams.mLoadIconFailed = true; 247 mloadIcon = false; 248 /** In case of icon load fail consider the 249 ** received proactive command as valid (sending RESULT OK) as 250 ** The result code, 'PRFRMD_ICON_NOT_DISPLAYED' will be added in the 251 ** terminal response by CatService/StkAppService if needed based on 252 ** the value of mLoadIconFailed. 253 */ 254 return ResultCode.OK; 255 } 256 switch(mIconLoadState) { 257 case LOAD_SINGLE_ICON: 258 mCmdParams.setIcon((Bitmap) data); 259 break; 260 case LOAD_MULTI_ICONS: 261 icons = (Bitmap[]) data; 262 // set each item icon. 263 for (Bitmap icon : icons) { 264 mCmdParams.setIcon(icon); 265 if (icon == null && mloadIcon) { 266 CatLog.d(this, "Optional Icon data is NULL while loading multi icons"); 267 mCmdParams.mLoadIconFailed = true; 268 } 269 } 270 break; 271 } 272 return ResultCode.OK; 273 } 274 sendCmdParams(ResultCode resCode)275 private void sendCmdParams(ResultCode resCode) { 276 mCaller.sendMsgParamsDecoded(resCode, mCmdParams); 277 } 278 279 /** 280 * Search for a COMPREHENSION-TLV object with the given tag from a list 281 * 282 * @param tag A tag to search for 283 * @param ctlvs List of ComprehensionTlv objects used to search in 284 * 285 * @return A ComprehensionTlv object that has the tag value of {@code tag}. 286 * If no object is found with the tag, null is returned. 287 */ searchForTag(ComprehensionTlvTag tag, List<ComprehensionTlv> ctlvs)288 private ComprehensionTlv searchForTag(ComprehensionTlvTag tag, 289 List<ComprehensionTlv> ctlvs) { 290 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 291 return searchForNextTag(tag, iter); 292 } 293 294 /** 295 * Search for the next COMPREHENSION-TLV object with the given tag from a 296 * list iterated by {@code iter}. {@code iter} points to the object next to 297 * the found object when this method returns. Used for searching the same 298 * list for similar tags, usually item id. 299 * 300 * @param tag A tag to search for 301 * @param iter Iterator for ComprehensionTlv objects used for search 302 * 303 * @return A ComprehensionTlv object that has the tag value of {@code tag}. 304 * If no object is found with the tag, null is returned. 305 */ searchForNextTag(ComprehensionTlvTag tag, Iterator<ComprehensionTlv> iter)306 private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag, 307 Iterator<ComprehensionTlv> iter) { 308 int tagValue = tag.value(); 309 while (iter.hasNext()) { 310 ComprehensionTlv ctlv = iter.next(); 311 if (ctlv.getTag() == tagValue) { 312 return ctlv; 313 } 314 } 315 return null; 316 } 317 318 /** 319 * Processes DISPLAY_TEXT proactive command from the SIM card. 320 * 321 * @param cmdDet Command Details container object. 322 * @param ctlvs List of ComprehensionTlv objects following Command Details 323 * object and Device Identities object within the proactive command 324 * @return true if the command is processing is pending and additional 325 * asynchronous processing is required. 326 * @throws ResultException 327 */ processDisplayText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)328 private boolean processDisplayText(CommandDetails cmdDet, 329 List<ComprehensionTlv> ctlvs) 330 throws ResultException { 331 332 CatLog.d(this, "process DisplayText"); 333 334 TextMessage textMsg = new TextMessage(); 335 IconId iconId = null; 336 337 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 338 ctlvs); 339 if (ctlv != null) { 340 textMsg.text = ValueParser.retrieveTextString(ctlv); 341 } 342 // If the tlv object doesn't exist or the it is a null object reply 343 // with command not understood. 344 if (textMsg.text == null) { 345 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 346 } 347 348 ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs); 349 if (ctlv != null) { 350 textMsg.responseNeeded = false; 351 } 352 // parse icon identifier 353 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 354 if (ctlv != null) { 355 iconId = ValueParser.retrieveIconId(ctlv); 356 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 357 } 358 // parse tone duration 359 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 360 if (ctlv != null) { 361 textMsg.duration = ValueParser.retrieveDuration(ctlv); 362 } 363 364 // Parse command qualifier parameters. 365 textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0; 366 textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0; 367 368 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 369 370 if (iconId != null) { 371 mloadIcon = true; 372 mIconLoadState = LOAD_SINGLE_ICON; 373 mIconLoader.loadIcon(iconId.recordNumber, this 374 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 375 return true; 376 } 377 return false; 378 } 379 380 /** 381 * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card. 382 * 383 * @param cmdDet Command Details container object. 384 * @param ctlvs List of ComprehensionTlv objects following Command Details 385 * object and Device Identities object within the proactive command 386 * @return true if the command is processing is pending and additional 387 * asynchronous processing is required. 388 * @throws ResultException 389 */ processSetUpIdleModeText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)390 private boolean processSetUpIdleModeText(CommandDetails cmdDet, 391 List<ComprehensionTlv> ctlvs) throws ResultException { 392 393 CatLog.d(this, "process SetUpIdleModeText"); 394 395 TextMessage textMsg = new TextMessage(); 396 IconId iconId = null; 397 398 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 399 ctlvs); 400 if (ctlv != null) { 401 textMsg.text = ValueParser.retrieveTextString(ctlv); 402 } 403 404 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 405 if (ctlv != null) { 406 iconId = ValueParser.retrieveIconId(ctlv); 407 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 408 } 409 410 /* 411 * If the tlv object doesn't contain text and the icon is not self 412 * explanatory then reply with command not understood. 413 */ 414 415 if (textMsg.text == null && iconId != null && !textMsg.iconSelfExplanatory) { 416 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 417 } 418 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 419 420 if (iconId != null) { 421 mloadIcon = true; 422 mIconLoadState = LOAD_SINGLE_ICON; 423 mIconLoader.loadIcon(iconId.recordNumber, this 424 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 425 return true; 426 } 427 return false; 428 } 429 430 /** 431 * Processes GET_INKEY proactive command from the SIM card. 432 * 433 * @param cmdDet Command Details container object. 434 * @param ctlvs List of ComprehensionTlv objects following Command Details 435 * object and Device Identities object within the proactive command 436 * @return true if the command is processing is pending and additional 437 * asynchronous processing is required. 438 * @throws ResultException 439 */ processGetInkey(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)440 private boolean processGetInkey(CommandDetails cmdDet, 441 List<ComprehensionTlv> ctlvs) throws ResultException { 442 443 CatLog.d(this, "process GetInkey"); 444 445 Input input = new Input(); 446 IconId iconId = null; 447 448 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 449 ctlvs); 450 if (ctlv != null) { 451 input.text = ValueParser.retrieveTextString(ctlv); 452 } else { 453 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 454 } 455 // parse icon identifier 456 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 457 if (ctlv != null) { 458 iconId = ValueParser.retrieveIconId(ctlv); 459 input.iconSelfExplanatory = iconId.selfExplanatory; 460 } 461 462 // parse duration 463 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 464 if (ctlv != null) { 465 input.duration = ValueParser.retrieveDuration(ctlv); 466 } 467 468 input.minLen = 1; 469 input.maxLen = 1; 470 471 input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; 472 input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; 473 input.yesNo = (cmdDet.commandQualifier & 0x04) != 0; 474 input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 475 input.echo = true; 476 477 mCmdParams = new GetInputParams(cmdDet, input); 478 479 if (iconId != null) { 480 mloadIcon = true; 481 mIconLoadState = LOAD_SINGLE_ICON; 482 mIconLoader.loadIcon(iconId.recordNumber, this 483 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 484 return true; 485 } 486 return false; 487 } 488 489 /** 490 * Processes GET_INPUT proactive command from the SIM card. 491 * 492 * @param cmdDet Command Details container object. 493 * @param ctlvs List of ComprehensionTlv objects following Command Details 494 * object and Device Identities object within the proactive command 495 * @return true if the command is processing is pending and additional 496 * asynchronous processing is required. 497 * @throws ResultException 498 */ processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)499 private boolean processGetInput(CommandDetails cmdDet, 500 List<ComprehensionTlv> ctlvs) throws ResultException { 501 502 CatLog.d(this, "process GetInput"); 503 504 Input input = new Input(); 505 IconId iconId = null; 506 507 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, 508 ctlvs); 509 if (ctlv != null) { 510 input.text = ValueParser.retrieveTextString(ctlv); 511 } else { 512 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 513 } 514 515 ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs); 516 if (ctlv != null) { 517 try { 518 byte[] rawValue = ctlv.getRawValue(); 519 int valueIndex = ctlv.getValueIndex(); 520 input.minLen = rawValue[valueIndex] & 0xff; 521 input.maxLen = rawValue[valueIndex + 1] & 0xff; 522 } catch (IndexOutOfBoundsException e) { 523 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 524 } 525 } else { 526 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 527 } 528 529 ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs); 530 if (ctlv != null) { 531 input.defaultText = ValueParser.retrieveTextString(ctlv); 532 } 533 // parse icon identifier 534 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 535 if (ctlv != null) { 536 iconId = ValueParser.retrieveIconId(ctlv); 537 input.iconSelfExplanatory = iconId.selfExplanatory; 538 } 539 540 input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; 541 input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; 542 input.echo = (cmdDet.commandQualifier & 0x04) == 0; 543 input.packed = (cmdDet.commandQualifier & 0x08) != 0; 544 input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 545 546 // Truncate the maxLen if it exceeds the max number of chars that can 547 // be encoded. Limit depends on DCS in Command Qualifier. 548 if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) { 549 CatLog.d(this, "UCS2: received maxLen = " + input.maxLen + 550 ", truncating to " + MAX_UCS2_CHARS); 551 input.maxLen = MAX_UCS2_CHARS; 552 } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) { 553 CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen + 554 ", truncating to " + MAX_GSM7_DEFAULT_CHARS); 555 input.maxLen = MAX_GSM7_DEFAULT_CHARS; 556 } 557 558 mCmdParams = new GetInputParams(cmdDet, input); 559 560 if (iconId != null) { 561 mloadIcon = true; 562 mIconLoadState = LOAD_SINGLE_ICON; 563 mIconLoader.loadIcon(iconId.recordNumber, this 564 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 565 return true; 566 } 567 return false; 568 } 569 570 /** 571 * Processes REFRESH proactive command from the SIM card. 572 * 573 * @param cmdDet Command Details container object. 574 * @param ctlvs List of ComprehensionTlv objects following Command Details 575 * object and Device Identities object within the proactive command 576 */ processRefresh(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)577 private boolean processRefresh(CommandDetails cmdDet, 578 List<ComprehensionTlv> ctlvs) { 579 580 CatLog.d(this, "process Refresh"); 581 582 // REFRESH proactive command is rerouted by the baseband and handled by 583 // the telephony layer. IDLE TEXT should be removed for a REFRESH command 584 // with "initialization" or "reset" 585 switch (cmdDet.commandQualifier) { 586 case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE: 587 case REFRESH_NAA_INIT_AND_FILE_CHANGE: 588 case REFRESH_NAA_INIT: 589 case REFRESH_UICC_RESET: 590 mCmdParams = new DisplayTextParams(cmdDet, null); 591 break; 592 } 593 return false; 594 } 595 596 /** 597 * Processes SELECT_ITEM proactive command from the SIM card. 598 * 599 * @param cmdDet Command Details container object. 600 * @param ctlvs List of ComprehensionTlv objects following Command Details 601 * object and Device Identities object within the proactive command 602 * @return true if the command is processing is pending and additional 603 * asynchronous processing is required. 604 * @throws ResultException 605 */ processSelectItem(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)606 private boolean processSelectItem(CommandDetails cmdDet, 607 List<ComprehensionTlv> ctlvs) throws ResultException { 608 609 CatLog.d(this, "process SelectItem"); 610 611 Menu menu = new Menu(); 612 IconId titleIconId = null; 613 ItemsIconId itemsIconId = null; 614 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 615 616 AppInterface.CommandType cmdType = AppInterface.CommandType 617 .fromInt(cmdDet.typeOfCommand); 618 619 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, 620 ctlvs); 621 if (ctlv != null) { 622 menu.title = ValueParser.retrieveAlphaId(ctlv); 623 } else if (cmdType == AppInterface.CommandType.SET_UP_MENU) { 624 // According to spec ETSI TS 102 223 section 6.10.3, the 625 // Alpha ID is mandatory (and also part of minimum set of 626 // elements required) for SET_UP_MENU. If it is not received 627 // by ME, then ME should respond with "error: missing minimum 628 // information" and not "command performed successfully". 629 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 630 } 631 632 while (true) { 633 ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); 634 if (ctlv != null) { 635 menu.items.add(ValueParser.retrieveItem(ctlv)); 636 } else { 637 break; 638 } 639 } 640 641 // We must have at least one menu item. 642 if (menu.items.size() == 0) { 643 throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); 644 } 645 646 ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); 647 if (ctlv != null) { 648 // CAT items are listed 1...n while list start at 0, need to 649 // subtract one. 650 menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1; 651 } 652 653 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 654 if (ctlv != null) { 655 mIconLoadState = LOAD_SINGLE_ICON; 656 titleIconId = ValueParser.retrieveIconId(ctlv); 657 menu.titleIconSelfExplanatory = titleIconId.selfExplanatory; 658 } 659 660 ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs); 661 if (ctlv != null) { 662 mIconLoadState = LOAD_MULTI_ICONS; 663 itemsIconId = ValueParser.retrieveItemsIconId(ctlv); 664 menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory; 665 } 666 667 boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0; 668 if (presentTypeSpecified) { 669 if ((cmdDet.commandQualifier & 0x02) == 0) { 670 menu.presentationType = PresentationType.DATA_VALUES; 671 } else { 672 menu.presentationType = PresentationType.NAVIGATION_OPTIONS; 673 } 674 } 675 menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0; 676 menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; 677 678 mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null); 679 680 // Load icons data if needed. 681 switch(mIconLoadState) { 682 case LOAD_NO_ICON: 683 return false; 684 case LOAD_SINGLE_ICON: 685 mloadIcon = true; 686 mIconLoader.loadIcon(titleIconId.recordNumber, this 687 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 688 break; 689 case LOAD_MULTI_ICONS: 690 int[] recordNumbers = itemsIconId.recordNumbers; 691 if (titleIconId != null) { 692 // Create a new array for all the icons (title and items). 693 recordNumbers = new int[itemsIconId.recordNumbers.length + 1]; 694 recordNumbers[0] = titleIconId.recordNumber; 695 System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers, 696 1, itemsIconId.recordNumbers.length); 697 } 698 mloadIcon = true; 699 mIconLoader.loadIcons(recordNumbers, this 700 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 701 break; 702 } 703 return true; 704 } 705 706 /** 707 * Processes EVENT_NOTIFY message from baseband. 708 * 709 * @param cmdDet Command Details container object. 710 * @param ctlvs List of ComprehensionTlv objects following Command Details 711 * object and Device Identities object within the proactive command 712 * @return true if the command is processing is pending and additional 713 * asynchronous processing is required. 714 */ processEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)715 private boolean processEventNotify(CommandDetails cmdDet, 716 List<ComprehensionTlv> ctlvs) throws ResultException { 717 718 CatLog.d(this, "process EventNotify"); 719 720 TextMessage textMsg = new TextMessage(); 721 IconId iconId = null; 722 723 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, 724 ctlvs); 725 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 726 727 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 728 if (ctlv != null) { 729 iconId = ValueParser.retrieveIconId(ctlv); 730 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 731 } 732 733 textMsg.responseNeeded = false; 734 mCmdParams = new DisplayTextParams(cmdDet, textMsg); 735 736 if (iconId != null) { 737 mloadIcon = true; 738 mIconLoadState = LOAD_SINGLE_ICON; 739 mIconLoader.loadIcon(iconId.recordNumber, this 740 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 741 return true; 742 } 743 return false; 744 } 745 746 /** 747 * Processes SET_UP_EVENT_LIST proactive command from the SIM card. 748 * 749 * @param cmdDet Command Details object retrieved. 750 * @param ctlvs List of ComprehensionTlv objects following Command Details 751 * object and Device Identities object within the proactive command 752 * @return false. This function always returns false meaning that the command 753 * processing is not pending and additional asynchronous processing 754 * is not required. 755 */ processSetUpEventList(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)756 private boolean processSetUpEventList(CommandDetails cmdDet, 757 List<ComprehensionTlv> ctlvs) { 758 759 CatLog.d(this, "process SetUpEventList"); 760 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, ctlvs); 761 if (ctlv != null) { 762 try { 763 byte[] rawValue = ctlv.getRawValue(); 764 int valueIndex = ctlv.getValueIndex(); 765 int valueLen = ctlv.getLength(); 766 int[] eventList = new int[valueLen]; 767 int eventValue = -1; 768 int i = 0; 769 while (valueLen > 0) { 770 eventValue = rawValue[valueIndex] & 0xff; 771 valueIndex++; 772 valueLen--; 773 774 switch (eventValue) { 775 case USER_ACTIVITY_EVENT: 776 case IDLE_SCREEN_AVAILABLE_EVENT: 777 case LANGUAGE_SELECTION_EVENT: 778 case BROWSER_TERMINATION_EVENT: 779 case BROWSING_STATUS_EVENT: 780 eventList[i] = eventValue; 781 i++; 782 break; 783 default: 784 break; 785 } 786 787 } 788 mCmdParams = new SetEventListParams(cmdDet, eventList); 789 } catch (IndexOutOfBoundsException e) { 790 CatLog.e(this, " IndexOutofBoundException in processSetUpEventList"); 791 } 792 } 793 return false; 794 } 795 796 /** 797 * Processes LAUNCH_BROWSER proactive command from the SIM card. 798 * 799 * @param cmdDet Command Details container object. 800 * @param ctlvs List of ComprehensionTlv objects following Command Details 801 * object and Device Identities object within the proactive command 802 * @return true if the command is processing is pending and additional 803 * asynchronous processing is required. 804 * @throws ResultException 805 */ processLaunchBrowser(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)806 private boolean processLaunchBrowser(CommandDetails cmdDet, 807 List<ComprehensionTlv> ctlvs) throws ResultException { 808 809 CatLog.d(this, "process LaunchBrowser"); 810 811 TextMessage confirmMsg = new TextMessage(); 812 IconId iconId = null; 813 String url = null; 814 815 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs); 816 if (ctlv != null) { 817 try { 818 byte[] rawValue = ctlv.getRawValue(); 819 int valueIndex = ctlv.getValueIndex(); 820 int valueLen = ctlv.getLength(); 821 if (valueLen > 0) { 822 url = GsmAlphabet.gsm8BitUnpackedToString(rawValue, 823 valueIndex, valueLen); 824 } else { 825 url = null; 826 } 827 } catch (IndexOutOfBoundsException e) { 828 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); 829 } 830 } 831 832 // parse alpha identifier. 833 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 834 confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); 835 836 // parse icon identifier 837 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 838 if (ctlv != null) { 839 iconId = ValueParser.retrieveIconId(ctlv); 840 confirmMsg.iconSelfExplanatory = iconId.selfExplanatory; 841 } 842 843 // parse command qualifier value. 844 LaunchBrowserMode mode; 845 switch (cmdDet.commandQualifier) { 846 case 0x00: 847 default: 848 mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED; 849 break; 850 case 0x02: 851 mode = LaunchBrowserMode.USE_EXISTING_BROWSER; 852 break; 853 case 0x03: 854 mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER; 855 break; 856 } 857 858 mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode); 859 860 if (iconId != null) { 861 mIconLoadState = LOAD_SINGLE_ICON; 862 mIconLoader.loadIcon(iconId.recordNumber, this 863 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 864 return true; 865 } 866 return false; 867 } 868 869 /** 870 * Processes PLAY_TONE proactive command from the SIM card. 871 * 872 * @param cmdDet Command Details container object. 873 * @param ctlvs List of ComprehensionTlv objects following Command Details 874 * object and Device Identities object within the proactive command 875 * @return true if the command is processing is pending and additional 876 * asynchronous processing is required.t 877 * @throws ResultException 878 */ processPlayTone(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)879 private boolean processPlayTone(CommandDetails cmdDet, 880 List<ComprehensionTlv> ctlvs) throws ResultException { 881 882 CatLog.d(this, "process PlayTone"); 883 884 Tone tone = null; 885 TextMessage textMsg = new TextMessage(); 886 Duration duration = null; 887 IconId iconId = null; 888 889 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs); 890 if (ctlv != null) { 891 // Nothing to do for null objects. 892 if (ctlv.getLength() > 0) { 893 try { 894 byte[] rawValue = ctlv.getRawValue(); 895 int valueIndex = ctlv.getValueIndex(); 896 int toneVal = rawValue[valueIndex]; 897 tone = Tone.fromInt(toneVal); 898 } catch (IndexOutOfBoundsException e) { 899 throw new ResultException( 900 ResultCode.CMD_DATA_NOT_UNDERSTOOD); 901 } 902 } 903 } 904 // parse alpha identifier 905 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 906 if (ctlv != null) { 907 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 908 } 909 // parse tone duration 910 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); 911 if (ctlv != null) { 912 duration = ValueParser.retrieveDuration(ctlv); 913 } 914 // parse icon identifier 915 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 916 if (ctlv != null) { 917 iconId = ValueParser.retrieveIconId(ctlv); 918 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 919 } 920 921 boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00; 922 923 textMsg.responseNeeded = false; 924 mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate); 925 926 if (iconId != null) { 927 mIconLoadState = LOAD_SINGLE_ICON; 928 mIconLoader.loadIcon(iconId.recordNumber, this 929 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 930 return true; 931 } 932 return false; 933 } 934 935 /** 936 * Processes SETUP_CALL proactive command from the SIM card. 937 * 938 * @param cmdDet Command Details object retrieved from the proactive command 939 * object 940 * @param ctlvs List of ComprehensionTlv objects following Command Details 941 * object and Device Identities object within the proactive command 942 * @return true if the command is processing is pending and additional 943 * asynchronous processing is required. 944 */ processSetupCall(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)945 private boolean processSetupCall(CommandDetails cmdDet, 946 List<ComprehensionTlv> ctlvs) throws ResultException { 947 CatLog.d(this, "process SetupCall"); 948 949 Iterator<ComprehensionTlv> iter = ctlvs.iterator(); 950 ComprehensionTlv ctlv = null; 951 // User confirmation phase message. 952 TextMessage confirmMsg = new TextMessage(); 953 // Call set up phase message. 954 TextMessage callMsg = new TextMessage(); 955 IconId confirmIconId = null; 956 IconId callIconId = null; 957 958 // get confirmation message string. 959 ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); 960 confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); 961 962 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 963 if (ctlv != null) { 964 confirmIconId = ValueParser.retrieveIconId(ctlv); 965 confirmMsg.iconSelfExplanatory = confirmIconId.selfExplanatory; 966 } 967 968 // get call set up message string. 969 ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter); 970 if (ctlv != null) { 971 callMsg.text = ValueParser.retrieveAlphaId(ctlv); 972 } 973 974 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 975 if (ctlv != null) { 976 callIconId = ValueParser.retrieveIconId(ctlv); 977 callMsg.iconSelfExplanatory = callIconId.selfExplanatory; 978 } 979 980 mCmdParams = new CallSetupParams(cmdDet, confirmMsg, callMsg); 981 982 if (confirmIconId != null || callIconId != null) { 983 mIconLoadState = LOAD_MULTI_ICONS; 984 int[] recordNumbers = new int[2]; 985 recordNumbers[0] = confirmIconId != null 986 ? confirmIconId.recordNumber : -1; 987 recordNumbers[1] = callIconId != null ? callIconId.recordNumber 988 : -1; 989 990 mIconLoader.loadIcons(recordNumbers, this 991 .obtainMessage(MSG_ID_LOAD_ICON_DONE)); 992 return true; 993 } 994 return false; 995 } 996 processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)997 private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) 998 throws ResultException { 999 CatLog.d(this, "process ProvideLocalInfo"); 1000 switch (cmdDet.commandQualifier) { 1001 case DTTZ_SETTING: 1002 CatLog.d(this, "PLI [DTTZ_SETTING]"); 1003 mCmdParams = new CommandParams(cmdDet); 1004 break; 1005 case LANGUAGE_SETTING: 1006 CatLog.d(this, "PLI [LANGUAGE_SETTING]"); 1007 mCmdParams = new CommandParams(cmdDet); 1008 break; 1009 default: 1010 CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported"); 1011 mCmdParams = new CommandParams(cmdDet); 1012 throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY); 1013 } 1014 return false; 1015 } 1016 processBIPClient(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1017 private boolean processBIPClient(CommandDetails cmdDet, 1018 List<ComprehensionTlv> ctlvs) throws ResultException { 1019 AppInterface.CommandType commandType = 1020 AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 1021 if (commandType != null) { 1022 CatLog.d(this, "process "+ commandType.name()); 1023 } 1024 1025 TextMessage textMsg = new TextMessage(); 1026 IconId iconId = null; 1027 ComprehensionTlv ctlv = null; 1028 boolean has_alpha_id = false; 1029 1030 // parse alpha identifier 1031 ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); 1032 if (ctlv != null) { 1033 textMsg.text = ValueParser.retrieveAlphaId(ctlv); 1034 CatLog.d(this, "alpha TLV text=" + textMsg.text); 1035 has_alpha_id = true; 1036 } 1037 1038 // parse icon identifier 1039 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); 1040 if (ctlv != null) { 1041 iconId = ValueParser.retrieveIconId(ctlv); 1042 textMsg.iconSelfExplanatory = iconId.selfExplanatory; 1043 } 1044 1045 textMsg.responseNeeded = false; 1046 mCmdParams = new BIPClientParams(cmdDet, textMsg, has_alpha_id); 1047 1048 if (iconId != null) { 1049 mIconLoadState = LOAD_SINGLE_ICON; 1050 mIconLoader.loadIcon(iconId.recordNumber, obtainMessage(MSG_ID_LOAD_ICON_DONE)); 1051 return true; 1052 } 1053 return false; 1054 } 1055 dispose()1056 public void dispose() { 1057 mIconLoader.dispose(); 1058 mIconLoader = null; 1059 mCmdParams = null; 1060 mCaller = null; 1061 sInstance = null; 1062 } 1063 } 1064