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