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