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