CatService.java revision e287feac673ff68565b766e0e463d105fa9cef9d
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.IccFileHandler;
32import com.android.internal.telephony.IccRecords;
33import com.android.internal.telephony.UiccCard;
34import com.android.internal.telephony.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 = "Defualt 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                sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
176                                     false, 0x00, null);
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,
560            Context context, UiccCard ic) {
561        UiccCardApplication ca = null;
562        IccFileHandler fh = null;
563        IccRecords ir = null;
564        if (ic != null) {
565            /* Since Cat is not tied to any application, but rather is Uicc application
566             * in itself - just get first FileHandler and IccRecords object
567             */
568            ca = ic.getApplicationIndex(0);
569            if (ca != null) {
570                fh = ca.getIccFileHandler();
571                ir = ca.getIccRecords();
572            }
573        }
574        synchronized (sInstanceLock) {
575            if (sInstance == null) {
576                if (ci == null || ca == null || ir == null || context == null || fh == null
577                        || ic == null) {
578                    return null;
579                }
580                HandlerThread thread = new HandlerThread("Cat Telephony service");
581                thread.start();
582                sInstance = new CatService(ci, ca, ir, context, fh, ic);
583                CatLog.d(sInstance, "NEW sInstance");
584            } else if ((ir != null) && (mIccRecords != ir)) {
585                if (mIccRecords != null) {
586                    mIccRecords.unregisterForRecordsLoaded(sInstance);
587                }
588
589                if (mUiccApplication != null) {
590                    mUiccApplication.unregisterForReady(sInstance);
591                }
592                CatLog.d(sInstance,
593                        "Reinitialize the Service with SIMRecords and UiccCardApplication");
594                mIccRecords = ir;
595                mUiccApplication = ca;
596
597                // re-Register for SIM ready event.
598                mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
599                mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null);
600                CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
601            } else {
602                CatLog.d(sInstance, "Return current sInstance");
603            }
604            return sInstance;
605        }
606    }
607
608    /**
609     * Used by application to get an AppInterface object.
610     *
611     * @return The only Service object in the system
612     */
613    public static AppInterface getInstance() {
614        return getInstance(null, null, null);
615    }
616
617    @Override
618    public void handleMessage(Message msg) {
619
620        switch (msg.what) {
621        case MSG_ID_SESSION_END:
622        case MSG_ID_PROACTIVE_COMMAND:
623        case MSG_ID_EVENT_NOTIFY:
624        case MSG_ID_REFRESH:
625            CatLog.d(this, "ril message arrived");
626            String data = null;
627            if (msg.obj != null) {
628                AsyncResult ar = (AsyncResult) msg.obj;
629                if (ar != null && ar.result != null) {
630                    try {
631                        data = (String) ar.result;
632                    } catch (ClassCastException e) {
633                        break;
634                    }
635                }
636            }
637            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
638            break;
639        case MSG_ID_CALL_SETUP:
640            mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
641            break;
642        case MSG_ID_ICC_RECORDS_LOADED:
643            break;
644        case MSG_ID_RIL_MSG_DECODED:
645            handleRilMsg((RilMessage) msg.obj);
646            break;
647        case MSG_ID_RESPONSE:
648            handleCmdResponse((CatResponseMessage) msg.obj);
649            break;
650        case MSG_ID_SIM_READY:
651            CatLog.d(this, "SIM ready. Reporting STK service running now...");
652            mCmdIf.reportStkServiceIsRunning(null);
653            break;
654        default:
655            throw new AssertionError("Unrecognized CAT command: " + msg.what);
656        }
657    }
658
659    public synchronized void onCmdResponse(CatResponseMessage resMsg) {
660        if (resMsg == null) {
661            return;
662        }
663        // queue a response message.
664        Message msg = this.obtainMessage(MSG_ID_RESPONSE, resMsg);
665        msg.sendToTarget();
666    }
667
668    private boolean validateResponse(CatResponseMessage resMsg) {
669        if (mCurrntCmd != null) {
670            return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet));
671        }
672        return false;
673    }
674
675    private boolean removeMenu(Menu menu) {
676        try {
677            if (menu.items.size() == 1 && menu.items.get(0) == null) {
678                return true;
679            }
680        } catch (NullPointerException e) {
681            CatLog.d(this, "Unable to get Menu's items size");
682            return true;
683        }
684        return false;
685    }
686
687    private void handleCmdResponse(CatResponseMessage resMsg) {
688        // Make sure the response details match the last valid command. An invalid
689        // response is a one that doesn't have a corresponding proactive command
690        // and sending it can "confuse" the baseband/ril.
691        // One reason for out of order responses can be UI glitches. For example,
692        // if the application launch an activity, and that activity is stored
693        // by the framework inside the history stack. That activity will be
694        // available for relaunch using the latest application dialog
695        // (long press on the home button). Relaunching that activity can send
696        // the same command's result again to the CatService and can cause it to
697        // get out of sync with the SIM.
698        if (!validateResponse(resMsg)) {
699            return;
700        }
701        ResponseData resp = null;
702        boolean helpRequired = false;
703        CommandDetails cmdDet = resMsg.getCmdDetails();
704
705        switch (resMsg.resCode) {
706        case HELP_INFO_REQUIRED:
707            helpRequired = true;
708            // fall through
709        case OK:
710        case PRFRMD_WITH_PARTIAL_COMPREHENSION:
711        case PRFRMD_WITH_MISSING_INFO:
712        case PRFRMD_WITH_ADDITIONAL_EFS_READ:
713        case PRFRMD_ICON_NOT_DISPLAYED:
714        case PRFRMD_MODIFIED_BY_NAA:
715        case PRFRMD_LIMITED_SERVICE:
716        case PRFRMD_WITH_MODIFICATION:
717        case PRFRMD_NAA_NOT_ACTIVE:
718        case PRFRMD_TONE_NOT_PLAYED:
719            switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
720            case SET_UP_MENU:
721                helpRequired = resMsg.resCode == ResultCode.HELP_INFO_REQUIRED;
722                sendMenuSelection(resMsg.usersMenuSelection, helpRequired);
723                return;
724            case SELECT_ITEM:
725                resp = new SelectItemResponseData(resMsg.usersMenuSelection);
726                break;
727            case GET_INPUT:
728            case GET_INKEY:
729                Input input = mCurrntCmd.geInput();
730                if (!input.yesNo) {
731                    // when help is requested there is no need to send the text
732                    // string object.
733                    if (!helpRequired) {
734                        resp = new GetInkeyInputResponseData(resMsg.usersInput,
735                                input.ucs2, input.packed);
736                    }
737                } else {
738                    resp = new GetInkeyInputResponseData(
739                            resMsg.usersYesNoSelection);
740                }
741                break;
742            case DISPLAY_TEXT:
743            case LAUNCH_BROWSER:
744                break;
745            case SET_UP_CALL:
746                mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null);
747                // No need to send terminal response for SET UP CALL. The user's
748                // confirmation result is send back using a dedicated ril message
749                // invoked by the CommandInterface call above.
750                mCurrntCmd = null;
751                return;
752            }
753            break;
754        case NO_RESPONSE_FROM_USER:
755        case UICC_SESSION_TERM_BY_USER:
756        case BACKWARD_MOVE_BY_USER:
757        case USER_NOT_ACCEPT:
758            resp = null;
759            break;
760        default:
761            return;
762        }
763        sendTerminalResponse(cmdDet, resMsg.resCode, false, 0, resp);
764        mCurrntCmd = null;
765    }
766
767    private boolean isStkAppInstalled() {
768        Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
769        PackageManager pm = mContext.getPackageManager();
770        List<ResolveInfo> broadcastReceivers =
771                            pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA);
772        int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size();
773
774        return (numReceiver > 0);
775    }
776}
777