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