CatService.java revision 2f48cebb0f3169da9d9325f7b4884872280599e5
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        this.mId = other.mId;
53        this.mData = other.mData;
54        this.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
66    // Class members
67    private static IccRecords mIccRecords;
68    private static UiccCardApplication mUiccApplication;
69
70    // Service members.
71    // Protects singleton instance lazy initialization.
72    private static final Object sInstanceLock = new Object();
73    private static CatService sInstance;
74    private CommandsInterface mCmdIf;
75    private Context mContext;
76    private CatCmdMessage mCurrntCmd = null;
77    private CatCmdMessage mMenuCmd = null;
78
79    private RilMessageDecoder mMsgDecoder = null;
80    private boolean mStkAppInstalled = false;
81
82    // Service constants.
83    static final int MSG_ID_SESSION_END              = 1;
84    static final int MSG_ID_PROACTIVE_COMMAND        = 2;
85    static final int MSG_ID_EVENT_NOTIFY             = 3;
86    static final int MSG_ID_CALL_SETUP               = 4;
87    static final int MSG_ID_REFRESH                  = 5;
88    static final int MSG_ID_RESPONSE                 = 6;
89    static final int MSG_ID_SIM_READY                = 7;
90
91    static final int MSG_ID_RIL_MSG_DECODED          = 10;
92
93    // Events to signal SIM presence or absent in the device.
94    private static final int MSG_ID_ICC_RECORDS_LOADED       = 20;
95
96    private static final int DEV_ID_KEYPAD      = 0x01;
97    private static final int DEV_ID_DISPLAY     = 0x02;
98    private static final int DEV_ID_EARPIECE    = 0x03;
99    private static final int DEV_ID_UICC        = 0x81;
100    private static final int DEV_ID_TERMINAL    = 0x82;
101    private static final int DEV_ID_NETWORK     = 0x83;
102
103    static final String STK_DEFAULT = "Default Message";
104
105    /* Intentionally private for singleton */
106    private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
107            Context context, IccFileHandler fh, UiccCard ic) {
108        if (ci == null || ca == null || ir == null || context == null || fh == null
109                || ic == null) {
110            throw new NullPointerException(
111                    "Service: Input parameters must not be null");
112        }
113        mCmdIf = ci;
114        mContext = context;
115
116        // Get the RilMessagesDecoder for decoding the messages.
117        mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
118
119        // Register ril events handling.
120        mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
121        mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
122        mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
123        mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
124        //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);
125
126        mIccRecords = ir;
127        mUiccApplication = ca;
128
129        // Register for SIM ready event.
130        mUiccApplication.registerForReady(this, MSG_ID_SIM_READY, null);
131        mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
132
133        // Check if STK application is availalbe
134        mStkAppInstalled = isStkAppInstalled();
135
136        CatLog.d(this, "Running CAT service. STK app installed:" + mStkAppInstalled);
137    }
138
139    public void dispose() {
140        mIccRecords.unregisterForRecordsLoaded(this);
141        mCmdIf.unSetOnCatSessionEnd(this);
142        mCmdIf.unSetOnCatProactiveCmd(this);
143        mCmdIf.unSetOnCatEvent(this);
144        mCmdIf.unSetOnCatCallSetUp(this);
145
146        this.removeCallbacksAndMessages(null);
147    }
148
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.cmdDet, 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.cmdDet, 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.cmdDet, 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.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
241                break;
242            case SET_UP_IDLE_MODE_TEXT:
243                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
244                break;
245            case PROVIDE_LOCAL_INFORMATION:
246                ResponseData resp;
247                switch (cmdParams.cmdDet.commandQualifier) {
248                    case CommandParamsFactory.DTTZ_SETTING:
249                        resp = new DTTZResponseData(null);
250                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
251                        break;
252                    case CommandParamsFactory.LANGUAGE_SETTING:
253                        resp = new LanguageResponseData(Locale.getDefault().getLanguage());
254                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
255                        break;
256                    default:
257                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
258                }
259                // No need to start STK app here.
260                return;
261            case LAUNCH_BROWSER:
262                if ((((LaunchBrowserParams) cmdParams).confirmMsg.text != null)
263                        && (((LaunchBrowserParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) {
264                    message = mContext.getText(com.android.internal.R.string.launchBrowserDefault);
265                    ((LaunchBrowserParams) cmdParams).confirmMsg.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).textMsg.text != null)
277                        && (((DisplayTextParams)cmdParams).textMsg.text.equals(STK_DEFAULT))) {
278                    message = mContext.getText(com.android.internal.R.string.sending);
279                    ((DisplayTextParams)cmdParams).textMsg.text = message.toString();
280                }
281                break;
282            case PLAY_TONE:
283                break;
284            case SET_UP_CALL:
285                if ((((CallSetupParams) cmdParams).confirmMsg.text != null)
286                        && (((CallSetupParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) {
287                    message = mContext.getText(com.android.internal.R.string.SetupCallDefault);
288                    ((CallSetupParams) cmdParams).confirmMsg.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.bHasAlphaId && (cmd.textMsg.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.cmdDet, 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.cmdDet,
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.cmdDet, 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 = 0x80 | ComprehensionTlvTag.RESULT.value();
389        buf.write(tag);
390        int length = includeAdditionalInfo ? 2 : 1;
391        buf.write(length);
392        buf.write(resultCode.value());
393
394        // additional info
395        if (includeAdditionalInfo) {
396            buf.write(additionalInfo);
397        }
398
399        // Fill optional data for each corresponding command
400        if (resp != null) {
401            resp.format(buf);
402        } else {
403            encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
404        }
405
406        byte[] rawData = buf.toByteArray();
407        String hexString = IccUtils.bytesToHexString(rawData);
408        if (false) {
409            CatLog.d(this, "TERMINAL RESPONSE: " + hexString);
410        }
411
412        mCmdIf.sendTerminalResponse(hexString, null);
413    }
414
415    private void encodeOptionalTags(CommandDetails cmdDet,
416            ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
417        CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
418        if (cmdType != null) {
419            switch (cmdType) {
420                case GET_INKEY:
421                    // ETSI TS 102 384,27.22.4.2.8.4.2.
422                    // If it is a response for GET_INKEY command and the response timeout
423                    // occured, then add DURATION TLV for variable timeout case.
424                    if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
425                        (cmdInput != null) && (cmdInput.duration != null)) {
426                        getInKeyResponse(buf, cmdInput);
427                    }
428                    break;
429                case PROVIDE_LOCAL_INFORMATION:
430                    if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
431                        (resultCode.value() == ResultCode.OK.value())) {
432                        getPliResponse(buf);
433                    }
434                    break;
435                default:
436                    CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet);
437                    break;
438            }
439        } else {
440            CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet);
441        }
442    }
443
444    private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
445        int tag = ComprehensionTlvTag.DURATION.value();
446
447        buf.write(tag);
448        buf.write(0x02); // length
449        buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
450        buf.write(cmdInput.duration.timeInterval); // Time Duration
451    }
452
453    private void getPliResponse(ByteArrayOutputStream buf) {
454
455        // Locale Language Setting
456        String lang = SystemProperties.get("persist.sys.language");
457
458        if (lang != null) {
459            // tag
460            int tag = ComprehensionTlvTag.LANGUAGE.value();
461            buf.write(tag);
462            ResponseData.writeLength(buf, lang.length());
463            buf.write(lang.getBytes(), 0, lang.length());
464        }
465    }
466
467    private void sendMenuSelection(int menuId, boolean helpRequired) {
468
469        ByteArrayOutputStream buf = new ByteArrayOutputStream();
470
471        // tag
472        int tag = BerTlv.BER_MENU_SELECTION_TAG;
473        buf.write(tag);
474
475        // length
476        buf.write(0x00); // place holder
477
478        // device identities
479        tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
480        buf.write(tag);
481        buf.write(0x02); // length
482        buf.write(DEV_ID_KEYPAD); // source device id
483        buf.write(DEV_ID_UICC); // destination device id
484
485        // item identifier
486        tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value();
487        buf.write(tag);
488        buf.write(0x01); // length
489        buf.write(menuId); // menu identifier chosen
490
491        // help request
492        if (helpRequired) {
493            tag = ComprehensionTlvTag.HELP_REQUEST.value();
494            buf.write(tag);
495            buf.write(0x00); // length
496        }
497
498        byte[] rawData = buf.toByteArray();
499
500        // write real length
501        int len = rawData.length - 2; // minus (tag + length)
502        rawData[1] = (byte) len;
503
504        String hexString = IccUtils.bytesToHexString(rawData);
505
506        mCmdIf.sendEnvelope(hexString, null);
507    }
508
509    private void eventDownload(int event, int sourceId, int destinationId,
510            byte[] additionalInfo, boolean oneShot) {
511
512        ByteArrayOutputStream buf = new ByteArrayOutputStream();
513
514        // tag
515        int tag = BerTlv.BER_EVENT_DOWNLOAD_TAG;
516        buf.write(tag);
517
518        // length
519        buf.write(0x00); // place holder, assume length < 128.
520
521        // event list
522        tag = 0x80 | ComprehensionTlvTag.EVENT_LIST.value();
523        buf.write(tag);
524        buf.write(0x01); // length
525        buf.write(event); // event value
526
527        // device identities
528        tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
529        buf.write(tag);
530        buf.write(0x02); // length
531        buf.write(sourceId); // source device id
532        buf.write(destinationId); // destination device id
533
534        // additional information
535        if (additionalInfo != null) {
536            for (byte b : additionalInfo) {
537                buf.write(b);
538            }
539        }
540
541        byte[] rawData = buf.toByteArray();
542
543        // write real length
544        int len = rawData.length - 2; // minus (tag + length)
545        rawData[1] = (byte) len;
546
547        String hexString = IccUtils.bytesToHexString(rawData);
548
549        mCmdIf.sendEnvelope(hexString, null);
550    }
551
552    /**
553     * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor.
554     *
555     * @param ci CommandsInterface object
556     * @param ir IccRecords object
557     * @param context phone app context
558     * @param fh Icc file handler
559     * @param ic Icc card
560     * @return The only Service object in the system
561     */
562    public static CatService getInstance(CommandsInterface ci,
563            Context context, UiccCard ic) {
564        UiccCardApplication ca = null;
565        IccFileHandler fh = null;
566        IccRecords ir = null;
567        if (ic != null) {
568            /* Since Cat is not tied to any application, but rather is Uicc application
569             * in itself - just get first FileHandler and IccRecords object
570             */
571            ca = ic.getApplicationIndex(0);
572            if (ca != null) {
573                fh = ca.getIccFileHandler();
574                ir = ca.getIccRecords();
575            }
576        }
577        synchronized (sInstanceLock) {
578            if (sInstance == null) {
579                if (ci == null || ca == null || ir == null || context == null || fh == null
580                        || ic == null) {
581                    return null;
582                }
583                HandlerThread thread = new HandlerThread("Cat Telephony service");
584                thread.start();
585                sInstance = new CatService(ci, ca, ir, context, fh, ic);
586                CatLog.d(sInstance, "NEW sInstance");
587            } else if ((ir != null) && (mIccRecords != ir)) {
588                if (mIccRecords != null) {
589                    mIccRecords.unregisterForRecordsLoaded(sInstance);
590                }
591
592                if (mUiccApplication != null) {
593                    mUiccApplication.unregisterForReady(sInstance);
594                }
595                CatLog.d(sInstance,
596                        "Reinitialize the Service with SIMRecords and UiccCardApplication");
597                mIccRecords = ir;
598                mUiccApplication = ca;
599
600                // re-Register for SIM ready event.
601                mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
602                mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null);
603                CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
604            } else {
605                CatLog.d(sInstance, "Return current sInstance");
606            }
607            return sInstance;
608        }
609    }
610
611    /**
612     * Used by application to get an AppInterface object.
613     *
614     * @return The only Service object in the system
615     */
616    public static AppInterface getInstance() {
617        return getInstance(null, null, null);
618    }
619
620    @Override
621    public void handleMessage(Message msg) {
622
623        switch (msg.what) {
624        case MSG_ID_SESSION_END:
625        case MSG_ID_PROACTIVE_COMMAND:
626        case MSG_ID_EVENT_NOTIFY:
627        case MSG_ID_REFRESH:
628            CatLog.d(this, "ril message arrived");
629            String data = null;
630            if (msg.obj != null) {
631                AsyncResult ar = (AsyncResult) msg.obj;
632                if (ar != null && ar.result != null) {
633                    try {
634                        data = (String) ar.result;
635                    } catch (ClassCastException e) {
636                        break;
637                    }
638                }
639            }
640            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
641            break;
642        case MSG_ID_CALL_SETUP:
643            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
644            break;
645        case MSG_ID_ICC_RECORDS_LOADED:
646            break;
647        case MSG_ID_RIL_MSG_DECODED:
648            handleRilMsg((RilMessage) msg.obj);
649            break;
650        case MSG_ID_RESPONSE:
651            handleCmdResponse((CatResponseMessage) msg.obj);
652            break;
653        case MSG_ID_SIM_READY:
654            CatLog.d(this, "SIM ready. Reporting STK service running now...");
655            mCmdIf.reportStkServiceIsRunning(null);
656            break;
657        default:
658            throw new AssertionError("Unrecognized CAT command: " + msg.what);
659        }
660    }
661
662    public synchronized void onCmdResponse(CatResponseMessage resMsg) {
663        if (resMsg == null) {
664            return;
665        }
666        // queue a response message.
667        Message msg = this.obtainMessage(MSG_ID_RESPONSE, resMsg);
668        msg.sendToTarget();
669    }
670
671    private boolean validateResponse(CatResponseMessage resMsg) {
672        if (mCurrntCmd != null) {
673            return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet));
674        }
675        return false;
676    }
677
678    private boolean removeMenu(Menu menu) {
679        try {
680            if (menu.items.size() == 1 && menu.items.get(0) == null) {
681                return true;
682            }
683        } catch (NullPointerException e) {
684            CatLog.d(this, "Unable to get Menu's items size");
685            return true;
686        }
687        return false;
688    }
689
690    private void handleCmdResponse(CatResponseMessage resMsg) {
691        // Make sure the response details match the last valid command. An invalid
692        // response is a one that doesn't have a corresponding proactive command
693        // and sending it can "confuse" the baseband/ril.
694        // One reason for out of order responses can be UI glitches. For example,
695        // if the application launch an activity, and that activity is stored
696        // by the framework inside the history stack. That activity will be
697        // available for relaunch using the latest application dialog
698        // (long press on the home button). Relaunching that activity can send
699        // the same command's result again to the CatService and can cause it to
700        // get out of sync with the SIM.
701        if (!validateResponse(resMsg)) {
702            return;
703        }
704        ResponseData resp = null;
705        boolean helpRequired = false;
706        CommandDetails cmdDet = resMsg.getCmdDetails();
707
708        switch (resMsg.resCode) {
709        case HELP_INFO_REQUIRED:
710            helpRequired = true;
711            // fall through
712        case OK:
713        case PRFRMD_WITH_PARTIAL_COMPREHENSION:
714        case PRFRMD_WITH_MISSING_INFO:
715        case PRFRMD_WITH_ADDITIONAL_EFS_READ:
716        case PRFRMD_ICON_NOT_DISPLAYED:
717        case PRFRMD_MODIFIED_BY_NAA:
718        case PRFRMD_LIMITED_SERVICE:
719        case PRFRMD_WITH_MODIFICATION:
720        case PRFRMD_NAA_NOT_ACTIVE:
721        case PRFRMD_TONE_NOT_PLAYED:
722        case TERMINAL_CRNTLY_UNABLE_TO_PROCESS:
723            switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
724            case SET_UP_MENU:
725                helpRequired = resMsg.resCode == ResultCode.HELP_INFO_REQUIRED;
726                sendMenuSelection(resMsg.usersMenuSelection, helpRequired);
727                return;
728            case SELECT_ITEM:
729                resp = new SelectItemResponseData(resMsg.usersMenuSelection);
730                break;
731            case GET_INPUT:
732            case GET_INKEY:
733                Input input = mCurrntCmd.geInput();
734                if (!input.yesNo) {
735                    // when help is requested there is no need to send the text
736                    // string object.
737                    if (!helpRequired) {
738                        resp = new GetInkeyInputResponseData(resMsg.usersInput,
739                                input.ucs2, input.packed);
740                    }
741                } else {
742                    resp = new GetInkeyInputResponseData(
743                            resMsg.usersYesNoSelection);
744                }
745                break;
746            case DISPLAY_TEXT:
747            case LAUNCH_BROWSER:
748                break;
749            case SET_UP_CALL:
750                mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null);
751                // No need to send terminal response for SET UP CALL. The user's
752                // confirmation result is send back using a dedicated ril message
753                // invoked by the CommandInterface call above.
754                mCurrntCmd = null;
755                return;
756            }
757            break;
758        case NO_RESPONSE_FROM_USER:
759        case UICC_SESSION_TERM_BY_USER:
760        case BACKWARD_MOVE_BY_USER:
761        case USER_NOT_ACCEPT:
762            resp = null;
763            break;
764        default:
765            return;
766        }
767        sendTerminalResponse(cmdDet, resMsg.resCode, resMsg.includeAdditionalInfo,
768                resMsg.additionalInfo, resp);
769        mCurrntCmd = null;
770    }
771
772    private boolean isStkAppInstalled() {
773        Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
774        PackageManager pm = mContext.getPackageManager();
775        List<ResolveInfo> broadcastReceivers =
776                            pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA);
777        int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size();
778
779        return (numReceiver > 0);
780    }
781}
782