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