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