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