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.IDLE_SCREEN_AVAILABLE_EVENT; 20 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT; 21 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT; 22 23 import android.app.Activity; 24 import android.app.ActivityManager; 25 import android.app.PendingIntent; 26 import android.app.backup.BackupManager; 27 import android.compat.annotation.UnsupportedAppUsage; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.PackageManager; 33 import android.content.pm.ResolveInfo; 34 import android.content.res.Resources.NotFoundException; 35 import android.os.AsyncResult; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.HandlerThread; 39 import android.os.LocaleList; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.os.RemoteException; 43 import android.telephony.SubscriptionInfo; 44 import android.telephony.SubscriptionManager; 45 import android.telephony.TelephonyManager; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.telephony.CommandsInterface; 49 import com.android.internal.telephony.PhoneConstants; 50 import com.android.internal.telephony.ProxyController; 51 import com.android.internal.telephony.SmsController; 52 import com.android.internal.telephony.subscription.SubscriptionManagerService; 53 import com.android.internal.telephony.uicc.IccCardStatus.CardState; 54 import com.android.internal.telephony.uicc.IccFileHandler; 55 import com.android.internal.telephony.uicc.IccRecords; 56 import com.android.internal.telephony.uicc.IccRefreshResponse; 57 import com.android.internal.telephony.uicc.IccUtils; 58 import com.android.internal.telephony.uicc.UiccCard; 59 import com.android.internal.telephony.uicc.UiccCardApplication; 60 import com.android.internal.telephony.uicc.UiccController; 61 import com.android.internal.telephony.uicc.UiccProfile; 62 63 import java.io.ByteArrayOutputStream; 64 import java.util.List; 65 import java.util.Locale; 66 67 class RilMessage { 68 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 69 int mId; 70 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 71 Object mData; 72 ResultCode mResCode; 73 74 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) RilMessage(int msgId, String rawData)75 RilMessage(int msgId, String rawData) { 76 mId = msgId; 77 mData = rawData; 78 } 79 RilMessage(RilMessage other)80 RilMessage(RilMessage other) { 81 mId = other.mId; 82 mData = other.mData; 83 mResCode = other.mResCode; 84 } 85 } 86 87 /** 88 * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL 89 * and application. 90 * 91 * {@hide} 92 */ 93 public class CatService extends Handler implements AppInterface { 94 private static final boolean DBG = false; 95 96 // Class members 97 private static IccRecords mIccRecords; 98 private static UiccCardApplication mUiccApplication; 99 100 // Service members. 101 // Protects singleton instance lazy initialization. 102 @UnsupportedAppUsage 103 private static final Object sInstanceLock = new Object(); 104 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 105 private static CatService[] sInstance = null; 106 @UnsupportedAppUsage 107 private CommandsInterface mCmdIf; 108 @UnsupportedAppUsage 109 private Context mContext; 110 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 111 private CatCmdMessage mCurrntCmd = null; 112 @UnsupportedAppUsage 113 private CatCmdMessage mMenuCmd = null; 114 115 @UnsupportedAppUsage 116 private RilMessageDecoder mMsgDecoder = null; 117 @UnsupportedAppUsage 118 private boolean mStkAppInstalled = false; 119 120 @UnsupportedAppUsage 121 private UiccController mUiccController; 122 private CardState mCardState = CardState.CARDSTATE_ABSENT; 123 124 // Service constants. 125 protected static final int MSG_ID_SESSION_END = 1; 126 protected static final int MSG_ID_PROACTIVE_COMMAND = 2; 127 protected static final int MSG_ID_EVENT_NOTIFY = 3; 128 protected static final int MSG_ID_CALL_SETUP = 4; 129 static final int MSG_ID_REFRESH = 5; 130 static final int MSG_ID_RESPONSE = 6; 131 static final int MSG_ID_SIM_READY = 7; 132 133 protected static final int MSG_ID_ICC_CHANGED = 8; 134 protected static final int MSG_ID_ALPHA_NOTIFY = 9; 135 136 static final int MSG_ID_RIL_MSG_DECODED = 10; 137 138 // Events to signal SIM presence or absent in the device. 139 private static final int MSG_ID_ICC_RECORDS_LOADED = 20; 140 141 //Events to signal SIM REFRESH notificatations 142 private static final int MSG_ID_ICC_REFRESH = 30; 143 144 private static final int DEV_ID_KEYPAD = 0x01; 145 private static final int DEV_ID_DISPLAY = 0x02; 146 private static final int DEV_ID_UICC = 0x81; 147 private static final int DEV_ID_TERMINAL = 0x82; 148 private static final int DEV_ID_NETWORK = 0x83; 149 150 static final String STK_DEFAULT = "Default Message"; 151 152 private static final String SMS_DELIVERY_ACTION = 153 "com.android.internal.telephony.cat.SMS_DELIVERY_ACTION"; 154 private static final String SMS_SENT_ACTION = 155 "com.android.internal.telephony.cat.SMS_SENT_ACTION"; 156 157 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 158 private int mSlotId; 159 private static HandlerThread sCatServiceThread; 160 161 /* For multisim catservice should not be singleton */ CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir, Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId, Looper looper)162 private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir, 163 Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId, 164 Looper looper) { 165 //creating new thread to avoid deadlock conditions with the framework thread. 166 super(looper); 167 if (ci == null || ca == null || ir == null || context == null || fh == null 168 || uiccProfile == null) { 169 throw new NullPointerException( 170 "Service: Input parameters must not be null"); 171 } 172 mCmdIf = ci; 173 mContext = context; 174 mSlotId = slotId; 175 176 // Get the RilMessagesDecoder for decoding the messages. 177 mMsgDecoder = RilMessageDecoder.getInstance(this, fh, context, slotId); 178 if (null == mMsgDecoder) { 179 CatLog.d(this, "Null RilMessageDecoder instance"); 180 return; 181 } 182 mMsgDecoder.start(); 183 184 // Register ril events handling. 185 mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null); 186 mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); 187 mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null); 188 mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null); 189 //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null); 190 191 mCmdIf.registerForIccRefresh(this, MSG_ID_ICC_REFRESH, null); 192 mCmdIf.setOnCatCcAlphaNotify(this, MSG_ID_ALPHA_NOTIFY, null); 193 194 mIccRecords = ir; 195 mUiccApplication = ca; 196 197 // Register for SIM ready event. 198 mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null); 199 CatLog.d(this, "registerForRecordsLoaded slotid=" + mSlotId + " instance:" + this); 200 201 202 mUiccController = UiccController.getInstance(); 203 mUiccController.registerForIccChanged(this, MSG_ID_ICC_CHANGED, null); 204 205 // Check if STK application is available 206 mStkAppInstalled = isStkAppInstalled(); 207 208 CatLog.d(this, "Running CAT service on Slotid: " + mSlotId + 209 ". STK app installed:" + mStkAppInstalled); 210 211 mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_DELIVERY_ACTION)); 212 mContext.registerReceiver(mSmsBroadcastReceiver, new IntentFilter(SMS_SENT_ACTION)); 213 } 214 215 /** 216 * Used for instantiating the Service from the Card. 217 * 218 * @param ci CommandsInterface object 219 * @param context phone app context 220 * @param ic Icc card 221 * @param slotId to know the index of card 222 * @return The only Service object in the system 223 */ getInstance(CommandsInterface ci, Context context, UiccProfile uiccProfile, int slotId)224 public static CatService getInstance(CommandsInterface ci, 225 Context context, UiccProfile uiccProfile, int slotId) { 226 if (sCatServiceThread == null) { 227 sCatServiceThread = new HandlerThread("CatServiceThread"); 228 sCatServiceThread.start(); 229 } 230 UiccCardApplication ca = null; 231 IccFileHandler fh = null; 232 IccRecords ir = null; 233 if (uiccProfile != null) { 234 /* Since Cat is not tied to any application, but rather is Uicc application 235 * in itself - just get first FileHandler and IccRecords object 236 */ 237 ca = uiccProfile.getApplicationIndex(0); 238 if (ca != null) { 239 fh = ca.getIccFileHandler(); 240 ir = ca.getIccRecords(); 241 } 242 } 243 244 synchronized (sInstanceLock) { 245 if (sInstance == null) { 246 int simCount = TelephonyManager.getDefault().getSupportedModemCount(); 247 sInstance = new CatService[simCount]; 248 for (int i = 0; i < simCount; i++) { 249 sInstance[i] = null; 250 } 251 } 252 if (sInstance[slotId] == null) { 253 if (ci == null || ca == null || ir == null || context == null || fh == null 254 || uiccProfile == null) { 255 return null; 256 } 257 sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId, 258 sCatServiceThread.getLooper()); 259 } else if ((ir != null) && (mIccRecords != ir)) { 260 if (mIccRecords != null) { 261 mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]); 262 } 263 264 mIccRecords = ir; 265 mUiccApplication = ca; 266 267 mIccRecords.registerForRecordsLoaded(sInstance[slotId], 268 MSG_ID_ICC_RECORDS_LOADED, null); 269 CatLog.d(sInstance[slotId], "registerForRecordsLoaded slotid=" + slotId 270 + " instance:" + sInstance[slotId]); 271 } 272 return sInstance[slotId]; 273 } 274 } 275 276 @UnsupportedAppUsage 277 @Override dispose()278 public void dispose() { 279 synchronized (sInstanceLock) { 280 CatLog.d(this, "Disposing CatService object"); 281 mIccRecords.unregisterForRecordsLoaded(this); 282 283 // Clean up stk icon if dispose is called 284 broadcastCardStateAndIccRefreshResp(CardState.CARDSTATE_ABSENT, null); 285 286 mCmdIf.unSetOnCatSessionEnd(this); 287 mCmdIf.unSetOnCatProactiveCmd(this); 288 mCmdIf.unSetOnCatEvent(this); 289 mCmdIf.unSetOnCatCallSetUp(this); 290 mCmdIf.unSetOnCatCcAlphaNotify(this); 291 292 mCmdIf.unregisterForIccRefresh(this); 293 if (mUiccController != null) { 294 mUiccController.unregisterForIccChanged(this); 295 mUiccController = null; 296 } 297 if (mMsgDecoder != null) { 298 mMsgDecoder.dispose(); 299 mMsgDecoder = null; 300 } 301 removeCallbacksAndMessages(null); 302 if (sInstance != null) { 303 if (mSlotId >= 0 && mSlotId < sInstance.length) { 304 sInstance[mSlotId] = null; 305 } else { 306 CatLog.d(this, "error: invaild slot id: " + mSlotId); 307 } 308 } 309 } 310 } 311 312 @Override finalize()313 protected void finalize() { 314 CatLog.d(this, "Service finalized"); 315 } 316 handleRilMsg(RilMessage rilMsg)317 private void handleRilMsg(RilMessage rilMsg) { 318 if (rilMsg == null) { 319 return; 320 } 321 322 // dispatch messages 323 CommandParams cmdParams = null; 324 switch (rilMsg.mId) { 325 case MSG_ID_EVENT_NOTIFY: 326 if (rilMsg.mResCode == ResultCode.OK) { 327 cmdParams = (CommandParams) rilMsg.mData; 328 if (cmdParams != null) { 329 handleCommand(cmdParams, false); 330 } 331 } 332 break; 333 case MSG_ID_PROACTIVE_COMMAND: 334 try { 335 cmdParams = (CommandParams) rilMsg.mData; 336 } catch (ClassCastException e) { 337 // for error handling : cast exception 338 CatLog.d(this, "Fail to parse proactive command"); 339 // Don't send Terminal Resp if command detail is not available 340 if (mCurrntCmd != null) { 341 sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD, 342 false, 0x00, null); 343 } 344 break; 345 } 346 if (cmdParams != null) { 347 if (rilMsg.mResCode == ResultCode.OK) { 348 handleCommand(cmdParams, true); 349 } else { 350 // for proactive commands that couldn't be decoded 351 // successfully respond with the code generated by the 352 // message decoder. 353 sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode, 354 false, 0, null); 355 } 356 } 357 break; 358 case MSG_ID_REFRESH: 359 cmdParams = (CommandParams) rilMsg.mData; 360 if (cmdParams != null) { 361 handleCommand(cmdParams, false); 362 } 363 break; 364 case MSG_ID_SESSION_END: 365 handleSessionEnd(); 366 break; 367 case MSG_ID_CALL_SETUP: 368 // prior event notify command supplied all the information 369 // needed for set up call processing. 370 break; 371 } 372 } 373 374 /** 375 * This function validates the events in SETUP_EVENT_LIST which are currently 376 * supported by the Android framework. In case of SETUP_EVENT_LIST has NULL events 377 * or no events, all the events need to be reset. 378 */ isSupportedSetupEventCommand(CatCmdMessage cmdMsg)379 private boolean isSupportedSetupEventCommand(CatCmdMessage cmdMsg) { 380 boolean flag = true; 381 382 for (int eventVal: cmdMsg.getSetEventList().eventList) { 383 CatLog.d(this,"Event: " + eventVal); 384 switch (eventVal) { 385 /* Currently android is supporting only the below events in SetupEventList 386 * Language Selection. */ 387 case IDLE_SCREEN_AVAILABLE_EVENT: 388 case LANGUAGE_SELECTION_EVENT: 389 case USER_ACTIVITY_EVENT: 390 break; 391 default: 392 flag = false; 393 } 394 } 395 return flag; 396 } 397 398 /** 399 * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command 400 * from RIL. 401 * Sends valid proactive command data to the application using intents. 402 * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is 403 * from RIL_UNSOL_STK_PROACTIVE_COMMAND. 404 */ handleCommand(CommandParams cmdParams, boolean isProactiveCmd)405 private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { 406 CatLog.d(this, cmdParams.getCommandType().name()); 407 408 // Log all proactive commands. 409 if (isProactiveCmd) { 410 UiccController.addLocalLog("CatService[" + mSlotId + "]: ProactiveCommand " + 411 " cmdParams=" + cmdParams); 412 } 413 414 CharSequence message; 415 ResultCode resultCode; 416 CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); 417 switch (cmdParams.getCommandType()) { 418 case SET_UP_MENU: 419 if (removeMenu(cmdMsg.getMenu())) { 420 mMenuCmd = null; 421 } else { 422 mMenuCmd = cmdMsg; 423 } 424 resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED 425 : ResultCode.OK; 426 sendTerminalResponse(cmdParams.mCmdDet, resultCode, false, 0, null); 427 break; 428 case DISPLAY_TEXT: 429 break; 430 case SET_UP_IDLE_MODE_TEXT: 431 resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED 432 : ResultCode.OK; 433 sendTerminalResponse(cmdParams.mCmdDet,resultCode, false, 0, null); 434 break; 435 case SET_UP_EVENT_LIST: 436 if (isSupportedSetupEventCommand(cmdMsg)) { 437 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 438 } else { 439 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, 440 false, 0, null); 441 } 442 break; 443 case PROVIDE_LOCAL_INFORMATION: 444 ResponseData resp; 445 switch (cmdParams.mCmdDet.commandQualifier) { 446 case CommandParamsFactory.DTTZ_SETTING: 447 resp = new DTTZResponseData(null); 448 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); 449 break; 450 case CommandParamsFactory.LANGUAGE_SETTING: 451 resp = new LanguageResponseData(Locale.getDefault().getLanguage()); 452 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); 453 break; 454 default: 455 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 456 } 457 // No need to start STK app here. 458 return; 459 case LAUNCH_BROWSER: 460 if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null) 461 && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { 462 message = mContext.getText(com.android.internal.R.string.launchBrowserDefault); 463 ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString(); 464 } 465 break; 466 case SELECT_ITEM: 467 case GET_INPUT: 468 case GET_INKEY: 469 break; 470 case REFRESH: 471 case RUN_AT: 472 if (STK_DEFAULT.equals(((DisplayTextParams)cmdParams).mTextMsg.text)) { 473 // Remove the default text which was temporarily added and shall not be shown 474 ((DisplayTextParams)cmdParams).mTextMsg.text = null; 475 } 476 break; 477 case SEND_SMS: 478 /* If cmdParams is an instanceof SendSMSParams , then it means config value 479 * config_stk_sms_send_support is true and the SMS should be sent by framework 480 */ 481 if (cmdParams instanceof SendSMSParams) { 482 String text = null, destAddr = null; 483 if (((SendSMSParams) cmdParams).mTextSmsMsg != null) { 484 text = ((SendSMSParams) cmdParams).mTextSmsMsg.text; 485 } 486 if (((SendSMSParams) cmdParams).mDestAddress != null) { 487 destAddr = ((SendSMSParams) cmdParams).mDestAddress.text; 488 } 489 if (text != null && destAddr != null) { 490 ProxyController proxyController = ProxyController.getInstance(mContext); 491 SubscriptionManager subscriptionManager = (SubscriptionManager) 492 mContext.getSystemService( 493 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 494 SubscriptionInfo subInfo = 495 subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( 496 mSlotId); 497 if (subInfo != null) { 498 sendStkSms(text, destAddr, subInfo.getSubscriptionId(), cmdParams, 499 proxyController); 500 } else { 501 sendTerminalResponse(cmdParams.mCmdDet, 502 ResultCode.CMD_DATA_NOT_UNDERSTOOD, false, 0x00, null); 503 CatLog.d(this, "Subscription info is null"); 504 } 505 } else { 506 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD, 507 false, 0x00, null); 508 CatLog.d(this, "Sms text or Destination Address is null"); 509 } 510 } else { 511 if ((((DisplayTextParams) cmdParams).mTextMsg.text != null) 512 && (((DisplayTextParams) cmdParams).mTextMsg.text.equals( 513 STK_DEFAULT))) { 514 message = mContext.getText(com.android.internal.R.string.sending); 515 ((DisplayTextParams) cmdParams).mTextMsg.text = message.toString(); 516 } 517 } 518 break; 519 case SEND_DTMF: 520 case SEND_SS: 521 case SEND_USSD: 522 if ((((DisplayTextParams)cmdParams).mTextMsg.text != null) 523 && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) { 524 message = mContext.getText(com.android.internal.R.string.sending); 525 ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString(); 526 } 527 break; 528 case PLAY_TONE: 529 break; 530 case SET_UP_CALL: 531 if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null) 532 && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { 533 message = mContext.getText(com.android.internal.R.string.SetupCallDefault); 534 ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString(); 535 } 536 break; 537 case LANGUAGE_NOTIFICATION: 538 String language = ((LanguageParams) cmdParams).mLanguage; 539 ResultCode result = ResultCode.OK; 540 if (language != null && language.length() > 0) { 541 try { 542 changeLanguage(language); 543 } catch (RemoteException e) { 544 result = ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS; 545 } 546 } 547 sendTerminalResponse(cmdParams.mCmdDet, result, false, 0, null); 548 return; 549 case OPEN_CHANNEL: 550 case CLOSE_CHANNEL: 551 case RECEIVE_DATA: 552 case SEND_DATA: 553 BIPClientParams cmd = (BIPClientParams) cmdParams; 554 /* Per 3GPP specification 102.223, 555 * if the alpha identifier is not provided by the UICC, 556 * the terminal MAY give information to the user 557 * noAlphaUsrCnf defines if you need to show user confirmation or not 558 */ 559 boolean noAlphaUsrCnf = false; 560 try { 561 noAlphaUsrCnf = mContext.getResources().getBoolean( 562 com.android.internal.R.bool.config_stkNoAlphaUsrCnf); 563 } catch (NotFoundException e) { 564 noAlphaUsrCnf = false; 565 } 566 if ((cmd.mTextMsg.text == null) && (cmd.mHasAlphaId || noAlphaUsrCnf)) { 567 CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id"); 568 // If alpha length is zero, we just respond with OK. 569 if (isProactiveCmd) { 570 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 571 } else if (cmdParams.getCommandType() == CommandType.OPEN_CHANNEL) { 572 mCmdIf.handleCallSetupRequestFromSim(true, null); 573 } 574 return; 575 } 576 // Respond with permanent failure to avoid retry if STK app is not present. 577 if (!mStkAppInstalled) { 578 CatLog.d(this, "No STK application found."); 579 if (isProactiveCmd) { 580 sendTerminalResponse(cmdParams.mCmdDet, 581 ResultCode.BEYOND_TERMINAL_CAPABILITY, 582 false, 0, null); 583 return; 584 } 585 } 586 /* 587 * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by 588 * either PROACTIVE_COMMAND or EVENT_NOTIFY. 589 * If PROACTIVE_COMMAND is used for those commands, send terminal 590 * response here. 591 */ 592 if (isProactiveCmd && 593 ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) || 594 (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) || 595 (cmdParams.getCommandType() == CommandType.SEND_DATA))) { 596 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 597 } 598 break; 599 default: 600 CatLog.d(this, "Unsupported command"); 601 return; 602 } 603 mCurrntCmd = cmdMsg; 604 broadcastCatCmdIntent(cmdMsg); 605 } 606 607 /** 608 * Used to send STK based sms via CATService 609 * @param text The message body 610 * @param destAddr The destination Address 611 * @param subId Subscription Id 612 * @param cmdParams Send SMS Command Params 613 * @param proxyController ProxyController 614 * @hide 615 */ sendStkSms(String text, String destAddr, int subId, CommandParams cmdParams, ProxyController proxyController)616 public void sendStkSms(String text, String destAddr, int subId, CommandParams cmdParams, 617 ProxyController proxyController) { 618 PendingIntent sentPendingIntent = PendingIntent.getBroadcast(mContext, 0, 619 new Intent(SMS_SENT_ACTION) 620 .putExtra("cmdDetails", cmdParams.mCmdDet) 621 .setPackage(mContext.getPackageName()), 622 PendingIntent.FLAG_MUTABLE); 623 PendingIntent deliveryPendingIntent = PendingIntent.getBroadcast(mContext, 0, 624 new Intent(SMS_DELIVERY_ACTION) 625 .putExtra("cmdDetails", cmdParams.mCmdDet) 626 .setPackage(mContext.getPackageName()), 627 PendingIntent.FLAG_MUTABLE); 628 SmsController smsController = proxyController.getSmsController(); 629 smsController.sendTextForSubscriber(subId, mContext.getOpPackageName(), 630 mContext.getAttributionTag(), destAddr, null, text, sentPendingIntent, 631 deliveryPendingIntent, false, 0L, true, true); 632 } 633 634 /** 635 * BroadcastReceiver class to handle error and success cases of 636 * SEND and DELIVERY pending intents used for sending of STK SMS 637 */ 638 @VisibleForTesting 639 public final BroadcastReceiver mSmsBroadcastReceiver = new BroadcastReceiver() { 640 641 @Override 642 public void onReceive(Context context, Intent intent) { 643 CommandDetails commandDetails = (CommandDetails) intent.getExtra("cmdDetails"); 644 if (intent.getAction().equals(SMS_SENT_ACTION)) { 645 int resultCode = getResultCode(); 646 ResultCode terminalResponseResultCode = ResultCode.NETWORK_CRNTLY_UNABLE_TO_PROCESS; 647 CatLog.d(this, "STK SMS errorCode : " + resultCode); 648 int additionalInfo = 0; 649 if (resultCode != Activity.RESULT_OK) { 650 /** 651 * The Terminal Response Result code is assigned as per Section 12.12.3 652 * and 12.12.5 TS 101.267. The Result code SMS_RP_ERROR is added in Ims Case 653 * and additional information is added as per RP-Cause Values in TS 124.011. 654 * The Result code NETWORK_CRNTLY_UNABLE_TO_PROCESS is added in non-Ims Case 655 * and additional information added as per cause values in TS 04.08. 656 */ 657 if (intent.hasExtra("ims") && intent.getBooleanExtra("ims", false)) { 658 terminalResponseResultCode = ResultCode.SMS_RP_ERROR; 659 //Additional information's 8th bit is 0 as per section 12.12.5 of TS 101.267 660 if (intent.hasExtra("errorCode")) { 661 additionalInfo = (int) intent.getExtra("errorCode"); 662 if ((additionalInfo & 0x80) != 0) additionalInfo = 0; 663 } 664 } else { 665 //Additional information's 8th bit is 1 as per section 12.12.3 of TS 101.267 666 if (intent.hasExtra("errorCode")) { 667 additionalInfo = (int) intent.getExtra("errorCode"); 668 additionalInfo |= 0x80; 669 } 670 } 671 CatLog.d(this, "Error delivering STK SMS errorCode : " + additionalInfo 672 + " terminalResponseResultCode = " + terminalResponseResultCode); 673 sendTerminalResponse(commandDetails, terminalResponseResultCode, 674 true, additionalInfo, null); 675 } else { 676 CatLog.d(this, " STK SMS sent successfully "); 677 } 678 } 679 if (intent.getAction().equals(SMS_DELIVERY_ACTION)) { 680 int resultCode = getResultCode(); 681 switch (resultCode) { 682 case Activity.RESULT_OK: 683 sendTerminalResponse(commandDetails, ResultCode.OK, false, 0, null); 684 CatLog.d(this, " STK SMS delivered successfully "); 685 break; 686 default: 687 CatLog.d(this, "Error delivering STK SMS : " + resultCode); 688 sendTerminalResponse(commandDetails, 689 ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS, false, 690 0, null); 691 } 692 } 693 } 694 }; 695 broadcastCatCmdIntent(CatCmdMessage cmdMsg)696 private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) { 697 Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); 698 intent.putExtra( "STK CMD", cmdMsg); 699 intent.putExtra("SLOT_ID", mSlotId); 700 intent.setComponent(AppInterface.getDefaultSTKApplication()); 701 CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId); 702 mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION); 703 } 704 705 /** 706 * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL. 707 * 708 */ handleSessionEnd()709 private void handleSessionEnd() { 710 CatLog.d(this, "SESSION END on "+ mSlotId); 711 712 mCurrntCmd = mMenuCmd; 713 Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION); 714 intent.putExtra("SLOT_ID", mSlotId); 715 intent.setComponent(AppInterface.getDefaultSTKApplication()); 716 mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION); 717 } 718 719 720 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) sendTerminalResponse(CommandDetails cmdDet, ResultCode resultCode, boolean includeAdditionalInfo, int additionalInfo, ResponseData resp)721 private void sendTerminalResponse(CommandDetails cmdDet, 722 ResultCode resultCode, boolean includeAdditionalInfo, 723 int additionalInfo, ResponseData resp) { 724 725 if (cmdDet == null) { 726 return; 727 } 728 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 729 730 Input cmdInput = null; 731 if (mCurrntCmd != null) { 732 cmdInput = mCurrntCmd.geInput(); 733 } 734 735 // command details 736 int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); 737 if (cmdDet.compRequired) { 738 tag |= 0x80; 739 } 740 buf.write(tag); 741 buf.write(0x03); // length 742 buf.write(cmdDet.commandNumber); 743 buf.write(cmdDet.typeOfCommand); 744 buf.write(cmdDet.commandQualifier); 745 746 // device identities 747 // According to TS102.223/TS31.111 section 6.8 Structure of 748 // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N, 749 // the ME should set the CR(comprehension required) flag to 750 // comprehension not required.(CR=0)" 751 // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N, 752 // the CR flag is not set. 753 tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 754 buf.write(tag); 755 buf.write(0x02); // length 756 buf.write(DEV_ID_TERMINAL); // source device id 757 buf.write(DEV_ID_UICC); // destination device id 758 759 // result 760 tag = ComprehensionTlvTag.RESULT.value(); 761 if (cmdDet.compRequired) { 762 tag |= 0x80; 763 } 764 buf.write(tag); 765 int length = includeAdditionalInfo ? 2 : 1; 766 buf.write(length); 767 buf.write(resultCode.value()); 768 769 // additional info 770 if (includeAdditionalInfo) { 771 buf.write(additionalInfo); 772 } 773 774 // Fill optional data for each corresponding command 775 if (resp != null) { 776 resp.format(buf); 777 } else { 778 encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); 779 } 780 781 byte[] rawData = buf.toByteArray(); 782 String hexString = IccUtils.bytesToHexString(rawData); 783 if (DBG) { 784 CatLog.d(this, "TERMINAL RESPONSE: " + hexString); 785 } 786 787 mCmdIf.sendTerminalResponse(hexString, null); 788 } 789 encodeOptionalTags(CommandDetails cmdDet, ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf)790 private void encodeOptionalTags(CommandDetails cmdDet, 791 ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) { 792 CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 793 if (cmdType != null) { 794 switch (cmdType) { 795 case GET_INPUT: 796 case GET_INKEY: 797 // Please refer to the clause 6.8.21 of ETSI 102.223. 798 // The terminal shall supply the command execution duration 799 // when it issues TERMINAL RESPONSE for GET INKEY command with variable timeout. 800 // GET INPUT command should also be handled in the same manner. 801 if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) && 802 (cmdInput != null) && (cmdInput.duration != null)) { 803 getInKeyResponse(buf, cmdInput); 804 } 805 break; 806 case PROVIDE_LOCAL_INFORMATION: 807 if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) && 808 (resultCode.value() == ResultCode.OK.value())) { 809 getPliResponse(buf); 810 } 811 break; 812 default: 813 CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet); 814 break; 815 } 816 } else { 817 CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet); 818 } 819 } 820 getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput)821 private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) { 822 int tag = ComprehensionTlvTag.DURATION.value(); 823 824 buf.write(tag); 825 buf.write(0x02); // length 826 buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds) 827 buf.write(cmdInput.duration.timeInterval); // Time Duration 828 } 829 getPliResponse(ByteArrayOutputStream buf)830 private void getPliResponse(ByteArrayOutputStream buf) { 831 // Locale Language Setting 832 final String lang = Locale.getDefault().getLanguage(); 833 834 if (lang != null) { 835 // tag 836 int tag = ComprehensionTlvTag.LANGUAGE.value(); 837 buf.write(tag); 838 ResponseData.writeLength(buf, lang.length()); 839 buf.write(lang.getBytes(), 0, lang.length()); 840 } 841 } 842 sendMenuSelection(int menuId, boolean helpRequired)843 private void sendMenuSelection(int menuId, boolean helpRequired) { 844 845 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 846 847 // tag 848 int tag = BerTlv.BER_MENU_SELECTION_TAG; 849 buf.write(tag); 850 851 // length 852 buf.write(0x00); // place holder 853 854 // device identities 855 tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 856 buf.write(tag); 857 buf.write(0x02); // length 858 buf.write(DEV_ID_KEYPAD); // source device id 859 buf.write(DEV_ID_UICC); // destination device id 860 861 // item identifier 862 tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value(); 863 buf.write(tag); 864 buf.write(0x01); // length 865 buf.write(menuId); // menu identifier chosen 866 867 // help request 868 if (helpRequired) { 869 tag = ComprehensionTlvTag.HELP_REQUEST.value(); 870 buf.write(tag); 871 buf.write(0x00); // length 872 } 873 874 byte[] rawData = buf.toByteArray(); 875 876 // write real length 877 int len = rawData.length - 2; // minus (tag + length) 878 rawData[1] = (byte) len; 879 880 String hexString = IccUtils.bytesToHexString(rawData); 881 882 mCmdIf.sendEnvelope(hexString, null); 883 } 884 eventDownload(int event, int sourceId, int destinationId, byte[] additionalInfo, boolean oneShot)885 private void eventDownload(int event, int sourceId, int destinationId, 886 byte[] additionalInfo, boolean oneShot) { 887 888 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 889 890 // tag 891 int tag = BerTlv.BER_EVENT_DOWNLOAD_TAG; 892 buf.write(tag); 893 894 // length 895 buf.write(0x00); // place holder, assume length < 128. 896 897 // event list 898 tag = 0x80 | ComprehensionTlvTag.EVENT_LIST.value(); 899 buf.write(tag); 900 buf.write(0x01); // length 901 buf.write(event); // event value 902 903 // device identities 904 tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 905 buf.write(tag); 906 buf.write(0x02); // length 907 buf.write(sourceId); // source device id 908 buf.write(destinationId); // destination device id 909 910 /* 911 * Check for type of event download to be sent to UICC - Browser 912 * termination,Idle screen available, User activity, Language selection 913 * etc as mentioned under ETSI TS 102 223 section 7.5 914 */ 915 916 /* 917 * Currently the below events are supported: 918 * Language Selection Event. 919 * Other event download commands should be encoded similar way 920 */ 921 /* TODO: eventDownload should be extended for other Envelope Commands */ 922 switch (event) { 923 case IDLE_SCREEN_AVAILABLE_EVENT: 924 CatLog.d(sInstance, " Sending Idle Screen Available event download to ICC"); 925 break; 926 case LANGUAGE_SELECTION_EVENT: 927 CatLog.d(sInstance, " Sending Language Selection event download to ICC"); 928 tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value(); 929 buf.write(tag); 930 // Language length should be 2 byte 931 buf.write(0x02); 932 break; 933 case USER_ACTIVITY_EVENT: 934 break; 935 default: 936 break; 937 } 938 939 // additional information 940 if (additionalInfo != null) { 941 for (byte b : additionalInfo) { 942 buf.write(b); 943 } 944 } 945 946 byte[] rawData = buf.toByteArray(); 947 948 // write real length 949 int len = rawData.length - 2; // minus (tag + length) 950 rawData[1] = (byte) len; 951 952 String hexString = IccUtils.bytesToHexString(rawData); 953 954 CatLog.d(this, "ENVELOPE COMMAND: " + hexString); 955 956 mCmdIf.sendEnvelope(hexString, null); 957 } 958 959 /** 960 * Used by application to get an AppInterface object. 961 * 962 * @return The only Service object in the system 963 */ 964 //TODO Need to take care for MSIM getInstance()965 public static AppInterface getInstance() { 966 int slotId = PhoneConstants.DEFAULT_SLOT_INDEX; 967 if (SubscriptionManagerService.getInstance() != null) { 968 slotId = SubscriptionManagerService.getInstance().getSlotIndex( 969 SubscriptionManagerService.getInstance().getDefaultSubId()); 970 } 971 return getInstance(null, null, null, slotId); 972 } 973 974 /** 975 * Used by application to get an AppInterface object. 976 * 977 * @return The only Service object in the system 978 */ getInstance(int slotId)979 public static AppInterface getInstance(int slotId) { 980 return getInstance(null, null, null, slotId); 981 } 982 983 @Override handleMessage(Message msg)984 public void handleMessage(Message msg) { 985 CatLog.d(this, "handleMessage[" + msg.what + "]"); 986 987 switch (msg.what) { 988 case MSG_ID_SESSION_END: 989 case MSG_ID_PROACTIVE_COMMAND: 990 case MSG_ID_EVENT_NOTIFY: 991 case MSG_ID_REFRESH: 992 CatLog.d(this, "ril message arrived,slotid:" + mSlotId); 993 String data = null; 994 if (msg.obj != null) { 995 AsyncResult ar = (AsyncResult) msg.obj; 996 if (ar != null && ar.result != null) { 997 try { 998 data = (String) ar.result; 999 } catch (ClassCastException e) { 1000 break; 1001 } 1002 } 1003 } 1004 if (mMsgDecoder != null) { 1005 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); 1006 } else { 1007 CatLog.e(this, "Error in handleMessage (" + msg.what + ") mMsgDecoder is NULL"); 1008 } 1009 break; 1010 case MSG_ID_CALL_SETUP: 1011 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null)); 1012 break; 1013 case MSG_ID_ICC_RECORDS_LOADED: 1014 break; 1015 case MSG_ID_RIL_MSG_DECODED: 1016 handleRilMsg((RilMessage) msg.obj); 1017 break; 1018 case MSG_ID_RESPONSE: 1019 handleCmdResponse((CatResponseMessage) msg.obj); 1020 break; 1021 case MSG_ID_ICC_CHANGED: 1022 CatLog.d(this, "MSG_ID_ICC_CHANGED"); 1023 updateIccAvailability(); 1024 break; 1025 case MSG_ID_ICC_REFRESH: 1026 if (msg.obj != null) { 1027 AsyncResult ar = (AsyncResult) msg.obj; 1028 if (ar != null && ar.result != null) { 1029 broadcastCardStateAndIccRefreshResp(CardState.CARDSTATE_PRESENT, 1030 (IccRefreshResponse) ar.result); 1031 } else { 1032 CatLog.d(this,"Icc REFRESH with exception: " + ar.exception); 1033 } 1034 } else { 1035 CatLog.d(this, "IccRefresh Message is null"); 1036 } 1037 break; 1038 case MSG_ID_ALPHA_NOTIFY: 1039 CatLog.d(this, "Received CAT CC Alpha message from card"); 1040 if (msg.obj != null) { 1041 AsyncResult ar = (AsyncResult) msg.obj; 1042 if (ar != null && ar.result != null) { 1043 broadcastAlphaMessage((String)ar.result); 1044 } else { 1045 CatLog.d(this, "CAT Alpha message: ar.result is null"); 1046 } 1047 } else { 1048 CatLog.d(this, "CAT Alpha message: msg.obj is null"); 1049 } 1050 break; 1051 default: 1052 throw new AssertionError("Unrecognized CAT command: " + msg.what); 1053 } 1054 } 1055 1056 /** 1057 ** This function sends a CARD status (ABSENT, PRESENT, REFRESH) to STK_APP. 1058 ** This is triggered during ICC_REFRESH or CARD STATE changes. In case 1059 ** REFRESH, additional information is sent in 'refresh_result' 1060 ** 1061 **/ broadcastCardStateAndIccRefreshResp(CardState cardState, IccRefreshResponse iccRefreshState)1062 private void broadcastCardStateAndIccRefreshResp(CardState cardState, 1063 IccRefreshResponse iccRefreshState) { 1064 Intent intent = new Intent(AppInterface.CAT_ICC_STATUS_CHANGE); 1065 boolean cardPresent = (cardState == CardState.CARDSTATE_PRESENT); 1066 1067 if (iccRefreshState != null) { 1068 //This case is when MSG_ID_ICC_REFRESH is received. 1069 intent.putExtra(AppInterface.REFRESH_RESULT, iccRefreshState.refreshResult); 1070 CatLog.d(this, "Sending IccResult with Result: " 1071 + iccRefreshState.refreshResult); 1072 } 1073 1074 // This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true). 1075 intent.putExtra(AppInterface.CARD_STATUS, cardPresent); 1076 intent.setComponent(AppInterface.getDefaultSTKApplication()); 1077 intent.putExtra("SLOT_ID", mSlotId); 1078 CatLog.d(this, "Sending Card Status: " 1079 + cardState + " " + "cardPresent: " + cardPresent + "SLOT_ID: " + mSlotId); 1080 mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION); 1081 } 1082 broadcastAlphaMessage(String alphaString)1083 private void broadcastAlphaMessage(String alphaString) { 1084 CatLog.d(this, "Broadcasting CAT Alpha message from card: " + alphaString); 1085 Intent intent = new Intent(AppInterface.CAT_ALPHA_NOTIFY_ACTION); 1086 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1087 intent.putExtra(AppInterface.ALPHA_STRING, alphaString); 1088 intent.putExtra("SLOT_ID", mSlotId); 1089 intent.setComponent(AppInterface.getDefaultSTKApplication()); 1090 mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION); 1091 } 1092 1093 @Override onCmdResponse(CatResponseMessage resMsg)1094 public synchronized void onCmdResponse(CatResponseMessage resMsg) { 1095 if (resMsg == null) { 1096 return; 1097 } 1098 // queue a response message. 1099 Message msg = obtainMessage(MSG_ID_RESPONSE, resMsg); 1100 msg.sendToTarget(); 1101 } 1102 validateResponse(CatResponseMessage resMsg)1103 private boolean validateResponse(CatResponseMessage resMsg) { 1104 boolean validResponse = false; 1105 if ((resMsg.mCmdDet.typeOfCommand == CommandType.SET_UP_EVENT_LIST.value()) 1106 || (resMsg.mCmdDet.typeOfCommand == CommandType.SET_UP_MENU.value())) { 1107 CatLog.d(this, "CmdType: " + resMsg.mCmdDet.typeOfCommand); 1108 validResponse = true; 1109 } else if (mCurrntCmd != null) { 1110 validResponse = resMsg.mCmdDet.compareTo(mCurrntCmd.mCmdDet); 1111 CatLog.d(this, "isResponse for last valid cmd: " + validResponse); 1112 } 1113 return validResponse; 1114 } 1115 removeMenu(Menu menu)1116 private boolean removeMenu(Menu menu) { 1117 try { 1118 if (menu.items.size() == 1 && menu.items.get(0) == null) { 1119 return true; 1120 } 1121 } catch (NullPointerException e) { 1122 CatLog.d(this, "Unable to get Menu's items size"); 1123 return true; 1124 } 1125 return false; 1126 } 1127 handleCmdResponse(CatResponseMessage resMsg)1128 private void handleCmdResponse(CatResponseMessage resMsg) { 1129 // Make sure the response details match the last valid command. An invalid 1130 // response is a one that doesn't have a corresponding proactive command 1131 // and sending it can "confuse" the baseband/ril. 1132 // One reason for out of order responses can be UI glitches. For example, 1133 // if the application launch an activity, and that activity is stored 1134 // by the framework inside the history stack. That activity will be 1135 // available for relaunch using the latest application dialog 1136 // (long press on the home button). Relaunching that activity can send 1137 // the same command's result again to the CatService and can cause it to 1138 // get out of sync with the SIM. This can happen in case of 1139 // non-interactive type Setup Event List and SETUP_MENU proactive commands. 1140 // Stk framework would have already sent Terminal Response to Setup Event 1141 // List and SETUP_MENU proactive commands. After sometime Stk app will send 1142 // Envelope Command/Event Download. In which case, the response details doesn't 1143 // match with last valid command (which are not related). 1144 // However, we should allow Stk framework to send the message to ICC. 1145 if (!validateResponse(resMsg)) { 1146 return; 1147 } 1148 ResponseData resp = null; 1149 boolean helpRequired = false; 1150 CommandDetails cmdDet = resMsg.getCmdDetails(); 1151 AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 1152 1153 switch (resMsg.mResCode) { 1154 case HELP_INFO_REQUIRED: 1155 helpRequired = true; 1156 // fall through 1157 case OK: 1158 case PRFRMD_WITH_PARTIAL_COMPREHENSION: 1159 case PRFRMD_WITH_MISSING_INFO: 1160 case PRFRMD_WITH_ADDITIONAL_EFS_READ: 1161 case PRFRMD_ICON_NOT_DISPLAYED: 1162 case PRFRMD_MODIFIED_BY_NAA: 1163 case PRFRMD_LIMITED_SERVICE: 1164 case PRFRMD_WITH_MODIFICATION: 1165 case PRFRMD_NAA_NOT_ACTIVE: 1166 case PRFRMD_TONE_NOT_PLAYED: 1167 case LAUNCH_BROWSER_ERROR: 1168 case TERMINAL_CRNTLY_UNABLE_TO_PROCESS: 1169 switch (type) { 1170 case SET_UP_MENU: 1171 helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED; 1172 sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired); 1173 return; 1174 case SELECT_ITEM: 1175 resp = new SelectItemResponseData(resMsg.mUsersMenuSelection); 1176 break; 1177 case GET_INPUT: 1178 case GET_INKEY: 1179 Input input = mCurrntCmd.geInput(); 1180 if (!input.yesNo) { 1181 // when help is requested there is no need to send the text 1182 // string object. 1183 if (!helpRequired) { 1184 resp = new GetInkeyInputResponseData(resMsg.mUsersInput, 1185 input.ucs2, input.packed); 1186 } 1187 } else { 1188 resp = new GetInkeyInputResponseData( 1189 resMsg.mUsersYesNoSelection); 1190 } 1191 break; 1192 case DISPLAY_TEXT: 1193 if (resMsg.mResCode == ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS) { 1194 // For screenbusy case there will be addtional information in the terminal 1195 // response. And the value of the additional information byte is 0x01. 1196 resMsg.setAdditionalInfo(0x01); 1197 } else { 1198 resMsg.mIncludeAdditionalInfo = false; 1199 resMsg.mAdditionalInfo = 0; 1200 } 1201 break; 1202 case LAUNCH_BROWSER: 1203 if (resMsg.mResCode == ResultCode.LAUNCH_BROWSER_ERROR) { 1204 // Additional info for Default URL unavailable. 1205 resMsg.setAdditionalInfo(0x04); 1206 } else { 1207 resMsg.mIncludeAdditionalInfo = false; 1208 resMsg.mAdditionalInfo = 0; 1209 } 1210 break; 1211 // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR 1212 case OPEN_CHANNEL: 1213 case SET_UP_CALL: 1214 mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null); 1215 // No need to send terminal response for SET UP CALL. The user's 1216 // confirmation result is send back using a dedicated ril message 1217 // invoked by the CommandInterface call above. 1218 mCurrntCmd = null; 1219 return; 1220 case SET_UP_EVENT_LIST: 1221 if (IDLE_SCREEN_AVAILABLE_EVENT == resMsg.mEventValue) { 1222 eventDownload(resMsg.mEventValue, DEV_ID_DISPLAY, DEV_ID_UICC, 1223 resMsg.mAddedInfo, false); 1224 } else { 1225 eventDownload(resMsg.mEventValue, DEV_ID_TERMINAL, DEV_ID_UICC, 1226 resMsg.mAddedInfo, false); 1227 } 1228 // No need to send the terminal response after event download. 1229 return; 1230 default: 1231 break; 1232 } 1233 break; 1234 case BACKWARD_MOVE_BY_USER: 1235 case USER_NOT_ACCEPT: 1236 // if the user dismissed the alert dialog for a 1237 // setup call/open channel, consider that as the user 1238 // rejecting the call. Use dedicated API for this, rather than 1239 // sending a terminal response. 1240 if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) { 1241 mCmdIf.handleCallSetupRequestFromSim(false, null); 1242 mCurrntCmd = null; 1243 return; 1244 } else { 1245 resp = null; 1246 } 1247 break; 1248 case NO_RESPONSE_FROM_USER: 1249 // No need to send terminal response for SET UP CALL on user timeout, 1250 // instead use dedicated API 1251 if (type == CommandType.SET_UP_CALL) { 1252 mCmdIf.handleCallSetupRequestFromSim(false, null); 1253 mCurrntCmd = null; 1254 return; 1255 } 1256 case UICC_SESSION_TERM_BY_USER: 1257 resp = null; 1258 break; 1259 default: 1260 return; 1261 } 1262 sendTerminalResponse(cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo, 1263 resMsg.mAdditionalInfo, resp); 1264 mCurrntCmd = null; 1265 } 1266 1267 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isStkAppInstalled()1268 private boolean isStkAppInstalled() { 1269 Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); 1270 PackageManager pm = mContext.getPackageManager(); 1271 List<ResolveInfo> broadcastReceivers = 1272 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); 1273 int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1274 1275 return (numReceiver > 0); 1276 } 1277 update(CommandsInterface ci, Context context, UiccProfile uiccProfile)1278 public void update(CommandsInterface ci, 1279 Context context, UiccProfile uiccProfile) { 1280 UiccCardApplication ca = null; 1281 IccRecords ir = null; 1282 1283 if (uiccProfile != null) { 1284 /* Since Cat is not tied to any application, but rather is Uicc application 1285 * in itself - just get first FileHandler and IccRecords object 1286 */ 1287 ca = uiccProfile.getApplicationIndex(0); 1288 if (ca != null) { 1289 ir = ca.getIccRecords(); 1290 } 1291 } 1292 1293 synchronized (sInstanceLock) { 1294 if ((ir != null) && (mIccRecords != ir)) { 1295 if (mIccRecords != null) { 1296 mIccRecords.unregisterForRecordsLoaded(this); 1297 } 1298 1299 CatLog.d(this, 1300 "Reinitialize the Service with SIMRecords and UiccCardApplication"); 1301 mIccRecords = ir; 1302 mUiccApplication = ca; 1303 1304 // re-Register for SIM ready event. 1305 mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null); 1306 CatLog.d(this, "registerForRecordsLoaded slotid=" + mSlotId + " instance:" + this); 1307 } 1308 } 1309 } 1310 updateIccAvailability()1311 void updateIccAvailability() { 1312 if (null == mUiccController) { 1313 return; 1314 } 1315 1316 CardState newState = CardState.CARDSTATE_ABSENT; 1317 UiccCard newCard = mUiccController.getUiccCard(mSlotId); 1318 if (newCard != null) { 1319 newState = newCard.getCardState(); 1320 } 1321 CardState oldState = mCardState; 1322 mCardState = newState; 1323 CatLog.d(this,"New Card State = " + newState + " " + "Old Card State = " + oldState); 1324 if (oldState == CardState.CARDSTATE_PRESENT && 1325 newState != CardState.CARDSTATE_PRESENT) { 1326 broadcastCardStateAndIccRefreshResp(newState, null); 1327 } else if (oldState != CardState.CARDSTATE_PRESENT && 1328 newState == CardState.CARDSTATE_PRESENT) { 1329 // Card moved to PRESENT STATE. 1330 mCmdIf.reportStkServiceIsRunning(null); 1331 } 1332 } 1333 changeLanguage(String language)1334 private void changeLanguage(String language) throws RemoteException { 1335 // get locale list, combined with language locale and default locale list. 1336 LocaleList defaultLocaleList = LocaleList.getDefault(); 1337 Locale[] locales = new Locale[defaultLocaleList.size() + 1]; 1338 locales[0] = new Locale(language); 1339 for (int i = 0; i < defaultLocaleList.size(); i++) { 1340 locales[i+1] = defaultLocaleList.get(i); 1341 } 1342 mContext.getSystemService(ActivityManager.class).setDeviceLocales(new LocaleList(locales)); 1343 BackupManager.dataChanged("com.android.providers.settings"); 1344 } 1345 } 1346