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