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