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