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