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