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