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