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