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