CatService.java revision 22d85a8e3a575a6d01d2c788587971657dfe20c6
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
17package com.android.internal.telephony.cat;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.os.AsyncResult;
24import android.os.Handler;
25import android.os.HandlerThread;
26import android.os.Message;
27import android.os.SystemProperties;
28
29import com.android.internal.telephony.CommandsInterface;
30import com.android.internal.telephony.uicc.IccFileHandler;
31import com.android.internal.telephony.uicc.IccRecords;
32import com.android.internal.telephony.uicc.IccUtils;
33import com.android.internal.telephony.uicc.UiccCard;
34import com.android.internal.telephony.uicc.UiccCardApplication;
35
36
37import java.io.ByteArrayOutputStream;
38import java.util.List;
39import java.util.Locale;
40
41class RilMessage {
42    int mId;
43    Object mData;
44    ResultCode mResCode;
45
46    RilMessage(int msgId, String rawData) {
47        mId = msgId;
48        mData = rawData;
49    }
50
51    RilMessage(RilMessage other) {
52        mId = other.mId;
53        mData = other.mData;
54        mResCode = other.mResCode;
55    }
56}
57
58/**
59 * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL
60 * and application.
61 *
62 * {@hide}
63 */
64public class CatService extends Handler implements AppInterface {
65    private static final boolean DBG = false;
66
67    // Class members
68    private static IccRecords mIccRecords;
69    private static UiccCardApplication mUiccApplication;
70
71    // Service members.
72    // Protects singleton instance lazy initialization.
73    private static final Object sInstanceLock = new Object();
74    private static CatService sInstance;
75    private CommandsInterface mCmdIf;
76    private Context mContext;
77    private CatCmdMessage mCurrntCmd = null;
78    private CatCmdMessage mMenuCmd = null;
79
80    private RilMessageDecoder mMsgDecoder = null;
81    private boolean mStkAppInstalled = false;
82
83    // Service constants.
84    static final int MSG_ID_SESSION_END              = 1;
85    static final int MSG_ID_PROACTIVE_COMMAND        = 2;
86    static final int MSG_ID_EVENT_NOTIFY             = 3;
87    static final int MSG_ID_CALL_SETUP               = 4;
88    static final int MSG_ID_REFRESH                  = 5;
89    static final int MSG_ID_RESPONSE                 = 6;
90    static final int MSG_ID_SIM_READY                = 7;
91
92    static final int MSG_ID_RIL_MSG_DECODED          = 10;
93
94    // Events to signal SIM presence or absent in the device.
95    private static final int MSG_ID_ICC_RECORDS_LOADED       = 20;
96
97    private static final int DEV_ID_KEYPAD      = 0x01;
98    private static final int DEV_ID_UICC        = 0x81;
99    private static final int DEV_ID_TERMINAL    = 0x82;
100    static final String STK_DEFAULT = "Defualt Message";
101
102    /* Intentionally private for singleton */
103    private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
104            Context context, IccFileHandler fh, UiccCard ic) {
105        if (ci == null || ca == null || ir == null || context == null || fh == null
106                || ic == null) {
107            throw new NullPointerException(
108                    "Service: Input parameters must not be null");
109        }
110        mCmdIf = ci;
111        mContext = context;
112
113        // Get the RilMessagesDecoder for decoding the messages.
114        mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
115
116        // Register ril events handling.
117        mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
118        mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
119        mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
120        mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
121        //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);
122
123        mIccRecords = ir;
124        mUiccApplication = ca;
125
126        // Register for SIM ready event.
127        mUiccApplication.registerForReady(this, MSG_ID_SIM_READY, null);
128        mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
129
130        // Check if STK application is availalbe
131        mStkAppInstalled = isStkAppInstalled();
132
133        CatLog.d(this, "Running CAT service. STK app installed:" + mStkAppInstalled);
134    }
135
136    public void dispose() {
137        mIccRecords.unregisterForRecordsLoaded(this);
138        mCmdIf.unSetOnCatSessionEnd(this);
139        mCmdIf.unSetOnCatProactiveCmd(this);
140        mCmdIf.unSetOnCatEvent(this);
141        mCmdIf.unSetOnCatCallSetUp(this);
142
143        removeCallbacksAndMessages(null);
144    }
145
146    @Override
147    protected void finalize() {
148        CatLog.d(this, "Service finalized");
149    }
150
151    private void handleRilMsg(RilMessage rilMsg) {
152        if (rilMsg == null) {
153            return;
154        }
155
156        // dispatch messages
157        CommandParams cmdParams = null;
158        switch (rilMsg.mId) {
159        case MSG_ID_EVENT_NOTIFY:
160            if (rilMsg.mResCode == ResultCode.OK) {
161                cmdParams = (CommandParams) rilMsg.mData;
162                if (cmdParams != null) {
163                    handleCommand(cmdParams, false);
164                }
165            }
166            break;
167        case MSG_ID_PROACTIVE_COMMAND:
168            try {
169                cmdParams = (CommandParams) rilMsg.mData;
170            } catch (ClassCastException e) {
171                // for error handling : cast exception
172                CatLog.d(this, "Fail to parse proactive command");
173                // Don't send Terminal Resp if command detail is not available
174                if (mCurrntCmd != null) {
175                    sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
176                                     false, 0x00, null);
177                }
178                break;
179            }
180            if (cmdParams != null) {
181                if (rilMsg.mResCode == ResultCode.OK) {
182                    handleCommand(cmdParams, true);
183                } else {
184                    // for proactive commands that couldn't be decoded
185                    // successfully respond with the code generated by the
186                    // message decoder.
187                    sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode,
188                            false, 0, null);
189                }
190            }
191            break;
192        case MSG_ID_REFRESH:
193            cmdParams = (CommandParams) rilMsg.mData;
194            if (cmdParams != null) {
195                handleCommand(cmdParams, false);
196            }
197            break;
198        case MSG_ID_SESSION_END:
199            handleSessionEnd();
200            break;
201        case MSG_ID_CALL_SETUP:
202            // prior event notify command supplied all the information
203            // needed for set up call processing.
204            break;
205        }
206    }
207
208    /**
209     * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command
210     * from RIL.
211     * Sends valid proactive command data to the application using intents.
212     * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is
213     * from RIL_UNSOL_STK_PROACTIVE_COMMAND.
214     */
215    private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) {
216        CatLog.d(this, cmdParams.getCommandType().name());
217
218        CharSequence message;
219        CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
220        switch (cmdParams.getCommandType()) {
221            case SET_UP_MENU:
222                if (removeMenu(cmdMsg.getMenu())) {
223                    mMenuCmd = null;
224                } else {
225                    mMenuCmd = cmdMsg;
226                }
227                sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
228                break;
229            case DISPLAY_TEXT:
230                // when application is not required to respond, send an immediate response.
231                if (!cmdMsg.geTextMessage().responseNeeded) {
232                    sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
233                }
234                break;
235            case REFRESH:
236                // ME side only handles refresh commands which meant to remove IDLE
237                // MODE TEXT.
238                cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
239                break;
240            case SET_UP_IDLE_MODE_TEXT:
241                sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
242                break;
243            case PROVIDE_LOCAL_INFORMATION:
244                ResponseData resp;
245                switch (cmdParams.mCmdDet.commandQualifier) {
246                    case CommandParamsFactory.DTTZ_SETTING:
247                        resp = new DTTZResponseData(null);
248                        sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp);
249                        break;
250                    case CommandParamsFactory.LANGUAGE_SETTING:
251                        resp = new LanguageResponseData(Locale.getDefault().getLanguage());
252                        sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp);
253                        break;
254                    default:
255                        sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
256                }
257                // No need to start STK app here.
258                return;
259            case LAUNCH_BROWSER:
260                if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null)
261                        && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) {
262                    message = mContext.getText(com.android.internal.R.string.launchBrowserDefault);
263                    ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString();
264                }
265                break;
266            case SELECT_ITEM:
267            case GET_INPUT:
268            case GET_INKEY:
269                break;
270            case SEND_DTMF:
271            case SEND_SMS:
272            case SEND_SS:
273            case SEND_USSD:
274                if ((((DisplayTextParams)cmdParams).mTextMsg.text != null)
275                        && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) {
276                    message = mContext.getText(com.android.internal.R.string.sending);
277                    ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString();
278                }
279                break;
280            case PLAY_TONE:
281                break;
282            case SET_UP_CALL:
283                if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null)
284                        && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) {
285                    message = mContext.getText(com.android.internal.R.string.SetupCallDefault);
286                    ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString();
287                }
288                break;
289            case OPEN_CHANNEL:
290            case CLOSE_CHANNEL:
291            case RECEIVE_DATA:
292            case SEND_DATA:
293                BIPClientParams cmd = (BIPClientParams) cmdParams;
294                if (cmd.mHasAlphaId && (cmd.mTextMsg.text == null)) {
295                    CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id");
296                    // If alpha length is zero, we just respond with OK.
297                    if (isProactiveCmd) {
298                        sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
299                    }
300                    return;
301                }
302                // Respond with permanent failure to avoid retry if STK app is not present.
303                if (!mStkAppInstalled) {
304                    CatLog.d(this, "No STK application found.");
305                    if (isProactiveCmd) {
306                        sendTerminalResponse(cmdParams.mCmdDet,
307                                             ResultCode.BEYOND_TERMINAL_CAPABILITY,
308                                             false, 0, null);
309                        return;
310                    }
311                }
312                /*
313                 * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by
314                 * either PROACTIVE_COMMAND or EVENT_NOTIFY.
315                 * If PROACTIVE_COMMAND is used for those commands, send terminal
316                 * response here.
317                 */
318                if (isProactiveCmd &&
319                    ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) ||
320                     (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) ||
321                     (cmdParams.getCommandType() == CommandType.SEND_DATA))) {
322                    sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
323                }
324                break;
325            default:
326                CatLog.d(this, "Unsupported command");
327                return;
328        }
329        mCurrntCmd = cmdMsg;
330        Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
331        intent.putExtra("STK CMD", cmdMsg);
332        mContext.sendBroadcast(intent);
333    }
334
335    /**
336     * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL.
337     *
338     */
339    private void handleSessionEnd() {
340        CatLog.d(this, "SESSION END");
341
342        mCurrntCmd = mMenuCmd;
343        Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
344        mContext.sendBroadcast(intent);
345    }
346
347    private void sendTerminalResponse(CommandDetails cmdDet,
348            ResultCode resultCode, boolean includeAdditionalInfo,
349            int additionalInfo, ResponseData resp) {
350
351        if (cmdDet == null) {
352            return;
353        }
354        ByteArrayOutputStream buf = new ByteArrayOutputStream();
355
356        Input cmdInput = null;
357        if (mCurrntCmd != null) {
358            cmdInput = mCurrntCmd.geInput();
359        }
360
361        // command details
362        int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
363        if (cmdDet.compRequired) {
364            tag |= 0x80;
365        }
366        buf.write(tag);
367        buf.write(0x03); // length
368        buf.write(cmdDet.commandNumber);
369        buf.write(cmdDet.typeOfCommand);
370        buf.write(cmdDet.commandQualifier);
371
372        // device identities
373        // According to TS102.223/TS31.111 section 6.8 Structure of
374        // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
375        // the ME should set the CR(comprehension required) flag to
376        // comprehension not required.(CR=0)"
377        // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
378        // the CR flag is not set.
379        tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
380        buf.write(tag);
381        buf.write(0x02); // length
382        buf.write(DEV_ID_TERMINAL); // source device id
383        buf.write(DEV_ID_UICC); // destination device id
384
385        // result
386        tag = 0x80 | ComprehensionTlvTag.RESULT.value();
387        buf.write(tag);
388        int length = includeAdditionalInfo ? 2 : 1;
389        buf.write(length);
390        buf.write(resultCode.value());
391
392        // additional info
393        if (includeAdditionalInfo) {
394            buf.write(additionalInfo);
395        }
396
397        // Fill optional data for each corresponding command
398        if (resp != null) {
399            resp.format(buf);
400        } else {
401            encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
402        }
403
404        byte[] rawData = buf.toByteArray();
405        String hexString = IccUtils.bytesToHexString(rawData);
406        if (DBG) {
407            CatLog.d(this, "TERMINAL RESPONSE: " + hexString);
408        }
409
410        mCmdIf.sendTerminalResponse(hexString, null);
411    }
412
413    private void encodeOptionalTags(CommandDetails cmdDet,
414            ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
415        CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
416        if (cmdType != null) {
417            switch (cmdType) {
418                case GET_INKEY:
419                    // ETSI TS 102 384,27.22.4.2.8.4.2.
420                    // If it is a response for GET_INKEY command and the response timeout
421                    // occured, then add DURATION TLV for variable timeout case.
422                    if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
423                        (cmdInput != null) && (cmdInput.duration != null)) {
424                        getInKeyResponse(buf, cmdInput);
425                    }
426                    break;
427                case PROVIDE_LOCAL_INFORMATION:
428                    if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
429                        (resultCode.value() == ResultCode.OK.value())) {
430                        getPliResponse(buf);
431                    }
432                    break;
433                default:
434                    CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet);
435                    break;
436            }
437        } else {
438            CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet);
439        }
440    }
441
442    private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
443        int tag = ComprehensionTlvTag.DURATION.value();
444
445        buf.write(tag);
446        buf.write(0x02); // length
447        buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
448        buf.write(cmdInput.duration.timeInterval); // Time Duration
449    }
450
451    private void getPliResponse(ByteArrayOutputStream buf) {
452
453        // Locale Language Setting
454        String lang = SystemProperties.get("persist.sys.language");
455
456        if (lang != null) {
457            // tag
458            int tag = ComprehensionTlvTag.LANGUAGE.value();
459            buf.write(tag);
460            ResponseData.writeLength(buf, lang.length());
461            buf.write(lang.getBytes(), 0, lang.length());
462        }
463    }
464
465    private void sendMenuSelection(int menuId, boolean helpRequired) {
466
467        ByteArrayOutputStream buf = new ByteArrayOutputStream();
468
469        // tag
470        int tag = BerTlv.BER_MENU_SELECTION_TAG;
471        buf.write(tag);
472
473        // length
474        buf.write(0x00); // place holder
475
476        // device identities
477        tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
478        buf.write(tag);
479        buf.write(0x02); // length
480        buf.write(DEV_ID_KEYPAD); // source device id
481        buf.write(DEV_ID_UICC); // destination device id
482
483        // item identifier
484        tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value();
485        buf.write(tag);
486        buf.write(0x01); // length
487        buf.write(menuId); // menu identifier chosen
488
489        // help request
490        if (helpRequired) {
491            tag = ComprehensionTlvTag.HELP_REQUEST.value();
492            buf.write(tag);
493            buf.write(0x00); // length
494        }
495
496        byte[] rawData = buf.toByteArray();
497
498        // write real length
499        int len = rawData.length - 2; // minus (tag + length)
500        rawData[1] = (byte) len;
501
502        String hexString = IccUtils.bytesToHexString(rawData);
503
504        mCmdIf.sendEnvelope(hexString, null);
505    }
506
507    /**
508     * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor.
509     *
510     * @param ci CommandsInterface object
511     * @param context phone app context
512     * @param ic Icc card
513     * @return The only Service object in the system
514     */
515    public static CatService getInstance(CommandsInterface ci,
516            Context context, UiccCard ic) {
517        UiccCardApplication ca = null;
518        IccFileHandler fh = null;
519        IccRecords ir = null;
520        if (ic != null) {
521            /* Since Cat is not tied to any application, but rather is Uicc application
522             * in itself - just get first FileHandler and IccRecords object
523             */
524            ca = ic.getApplicationIndex(0);
525            if (ca != null) {
526                fh = ca.getIccFileHandler();
527                ir = ca.getIccRecords();
528            }
529        }
530        synchronized (sInstanceLock) {
531            if (sInstance == null) {
532                if (ci == null || ca == null || ir == null || context == null || fh == null
533                        || ic == null) {
534                    return null;
535                }
536                HandlerThread thread = new HandlerThread("Cat Telephony service");
537                thread.start();
538                sInstance = new CatService(ci, ca, ir, context, fh, ic);
539                CatLog.d(sInstance, "NEW sInstance");
540            } else if ((ir != null) && (mIccRecords != ir)) {
541                if (mIccRecords != null) {
542                    mIccRecords.unregisterForRecordsLoaded(sInstance);
543                }
544
545                if (mUiccApplication != null) {
546                    mUiccApplication.unregisterForReady(sInstance);
547                }
548                CatLog.d(sInstance,
549                        "Reinitialize the Service with SIMRecords and UiccCardApplication");
550                mIccRecords = ir;
551                mUiccApplication = ca;
552
553                // re-Register for SIM ready event.
554                mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
555                mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null);
556                CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
557            } else {
558                CatLog.d(sInstance, "Return current sInstance");
559            }
560            return sInstance;
561        }
562    }
563
564    /**
565     * Used by application to get an AppInterface object.
566     *
567     * @return The only Service object in the system
568     */
569    public static AppInterface getInstance() {
570        return getInstance(null, null, null);
571    }
572
573    @Override
574    public void handleMessage(Message msg) {
575
576        switch (msg.what) {
577        case MSG_ID_SESSION_END:
578        case MSG_ID_PROACTIVE_COMMAND:
579        case MSG_ID_EVENT_NOTIFY:
580        case MSG_ID_REFRESH:
581            CatLog.d(this, "ril message arrived");
582            String data = null;
583            if (msg.obj != null) {
584                AsyncResult ar = (AsyncResult) msg.obj;
585                if (ar != null && ar.result != null) {
586                    try {
587                        data = (String) ar.result;
588                    } catch (ClassCastException e) {
589                        break;
590                    }
591                }
592            }
593            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
594            break;
595        case MSG_ID_CALL_SETUP:
596            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
597            break;
598        case MSG_ID_ICC_RECORDS_LOADED:
599            break;
600        case MSG_ID_RIL_MSG_DECODED:
601            handleRilMsg((RilMessage) msg.obj);
602            break;
603        case MSG_ID_RESPONSE:
604            handleCmdResponse((CatResponseMessage) msg.obj);
605            break;
606        case MSG_ID_SIM_READY:
607            CatLog.d(this, "SIM ready. Reporting STK service running now...");
608            mCmdIf.reportStkServiceIsRunning(null);
609            break;
610        default:
611            throw new AssertionError("Unrecognized CAT command: " + msg.what);
612        }
613    }
614
615    @Override
616    public synchronized void onCmdResponse(CatResponseMessage resMsg) {
617        if (resMsg == null) {
618            return;
619        }
620        // queue a response message.
621        Message msg = obtainMessage(MSG_ID_RESPONSE, resMsg);
622        msg.sendToTarget();
623    }
624
625    private boolean validateResponse(CatResponseMessage resMsg) {
626        if (mCurrntCmd != null) {
627            return (resMsg.mCmdDet.compareTo(mCurrntCmd.mCmdDet));
628        }
629        return false;
630    }
631
632    private boolean removeMenu(Menu menu) {
633        try {
634            if (menu.items.size() == 1 && menu.items.get(0) == null) {
635                return true;
636            }
637        } catch (NullPointerException e) {
638            CatLog.d(this, "Unable to get Menu's items size");
639            return true;
640        }
641        return false;
642    }
643
644    private void handleCmdResponse(CatResponseMessage resMsg) {
645        // Make sure the response details match the last valid command. An invalid
646        // response is a one that doesn't have a corresponding proactive command
647        // and sending it can "confuse" the baseband/ril.
648        // One reason for out of order responses can be UI glitches. For example,
649        // if the application launch an activity, and that activity is stored
650        // by the framework inside the history stack. That activity will be
651        // available for relaunch using the latest application dialog
652        // (long press on the home button). Relaunching that activity can send
653        // the same command's result again to the CatService and can cause it to
654        // get out of sync with the SIM.
655        if (!validateResponse(resMsg)) {
656            return;
657        }
658        ResponseData resp = null;
659        boolean helpRequired = false;
660        CommandDetails cmdDet = resMsg.getCmdDetails();
661
662        switch (resMsg.mResCode) {
663        case HELP_INFO_REQUIRED:
664            helpRequired = true;
665            // fall through
666        case OK:
667        case PRFRMD_WITH_PARTIAL_COMPREHENSION:
668        case PRFRMD_WITH_MISSING_INFO:
669        case PRFRMD_WITH_ADDITIONAL_EFS_READ:
670        case PRFRMD_ICON_NOT_DISPLAYED:
671        case PRFRMD_MODIFIED_BY_NAA:
672        case PRFRMD_LIMITED_SERVICE:
673        case PRFRMD_WITH_MODIFICATION:
674        case PRFRMD_NAA_NOT_ACTIVE:
675        case PRFRMD_TONE_NOT_PLAYED:
676        case TERMINAL_CRNTLY_UNABLE_TO_PROCESS:
677            switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
678            case SET_UP_MENU:
679                helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED;
680                sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired);
681                return;
682            case SELECT_ITEM:
683                resp = new SelectItemResponseData(resMsg.mUsersMenuSelection);
684                break;
685            case GET_INPUT:
686            case GET_INKEY:
687                Input input = mCurrntCmd.geInput();
688                if (!input.yesNo) {
689                    // when help is requested there is no need to send the text
690                    // string object.
691                    if (!helpRequired) {
692                        resp = new GetInkeyInputResponseData(resMsg.mUsersInput,
693                                input.ucs2, input.packed);
694                    }
695                } else {
696                    resp = new GetInkeyInputResponseData(
697                            resMsg.mUsersYesNoSelection);
698                }
699                break;
700            case DISPLAY_TEXT:
701            case LAUNCH_BROWSER:
702                break;
703            case SET_UP_CALL:
704                mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null);
705                // No need to send terminal response for SET UP CALL. The user's
706                // confirmation result is send back using a dedicated ril message
707                // invoked by the CommandInterface call above.
708                mCurrntCmd = null;
709                return;
710            default:
711                break;
712            }
713            break;
714        case NO_RESPONSE_FROM_USER:
715        case UICC_SESSION_TERM_BY_USER:
716        case BACKWARD_MOVE_BY_USER:
717        case USER_NOT_ACCEPT:
718            resp = null;
719            break;
720        default:
721            return;
722        }
723        sendTerminalResponse(cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo,
724                resMsg.mAdditionalInfo, resp);
725        mCurrntCmd = null;
726    }
727
728    private boolean isStkAppInstalled() {
729        Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
730        PackageManager pm = mContext.getPackageManager();
731        List<ResolveInfo> broadcastReceivers =
732                            pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA);
733        int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size();
734
735        return (numReceiver > 0);
736    }
737}
738