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