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