StkAppService.java revision d517689fb08e95c8e5a23385b5744ae5af2e4678
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.stk;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.app.Service;
23import android.content.Context;
24import android.content.Intent;
25import android.net.Uri;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Looper;
30import android.os.Message;
31import android.telephony.TelephonyManager;
32import android.view.Gravity;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.widget.ImageView;
36import android.widget.RemoteViews;
37import android.widget.TextView;
38import android.widget.Toast;
39
40import com.android.internal.telephony.gsm.stk.AppInterface;
41import com.android.internal.telephony.gsm.stk.Menu;
42import com.android.internal.telephony.gsm.stk.Item;
43import com.android.internal.telephony.gsm.stk.ResultCode;
44import com.android.internal.telephony.gsm.stk.StkCmdMessage;
45import com.android.internal.telephony.gsm.stk.StkCmdMessage.BrowserSettings;
46import com.android.internal.telephony.gsm.stk.StkLog;
47import com.android.internal.telephony.gsm.stk.StkResponseMessage;
48import com.android.internal.telephony.gsm.stk.TextMessage;
49
50import java.util.LinkedList;
51
52/**
53 * SIM toolkit application level service. Interacts with Telephopny messages,
54 * application's launch and user input from STK UI elements.
55 *
56 */
57public class StkAppService extends Service implements Runnable {
58
59    // members
60    private volatile Looper mServiceLooper;
61    private volatile ServiceHandler mServiceHandler;
62    private AppInterface mStkService;
63    private Context mContext = null;
64    private StkCmdMessage mMainCmd = null;
65    private StkCmdMessage mCurrentCmd = null;
66    private Menu mCurrentMenu = null;
67    private String lastSelectedItem = null;
68    private boolean mMenuIsVisibile = false;
69    private boolean responseNeeded = true;
70    private boolean mCmdInProgress = false;
71    private NotificationManager mNotificationManager = null;
72    private LinkedList<DelayedCmd> mCmdsQ = null;
73    private boolean launchBrowser = false;
74    private BrowserSettings mBrowserSettings = null;
75    static StkAppService sInstance = null;
76
77    // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when
78    // creating an intent.
79    private enum InitiatedByUserAction {
80        yes,            // The action was started via a user initiated action
81        unknown,        // Not known for sure if user initated the action
82    }
83
84    // constants
85    static final String OPCODE = "op";
86    static final String CMD_MSG = "cmd message";
87    static final String RES_ID = "response id";
88    static final String MENU_SELECTION = "menu selection";
89    static final String INPUT = "input";
90    static final String HELP = "help";
91    static final String CONFIRMATION = "confirm";
92
93    // operations ids for different service functionality.
94    static final int OP_CMD = 1;
95    static final int OP_RESPONSE = 2;
96    static final int OP_LAUNCH_APP = 3;
97    static final int OP_END_SESSION = 4;
98    static final int OP_BOOT_COMPLETED = 5;
99    private static final int OP_DELAYED_MSG = 6;
100
101    // Response ids
102    static final int RES_ID_MENU_SELECTION = 11;
103    static final int RES_ID_INPUT = 12;
104    static final int RES_ID_CONFIRM = 13;
105    static final int RES_ID_DONE = 14;
106
107    static final int RES_ID_TIMEOUT = 20;
108    static final int RES_ID_BACKWARD = 21;
109    static final int RES_ID_END_SESSION = 22;
110    static final int RES_ID_EXIT = 23;
111
112    private static final String PACKAGE_NAME = "com.android.stk";
113    private static final String MENU_ACTIVITY_NAME =
114                                        PACKAGE_NAME + ".StkMenuActivity";
115    private static final String INPUT_ACTIVITY_NAME =
116                                        PACKAGE_NAME + ".StkInputActivity";
117
118    // Notification id used to display Idle Mode text in NotificationManager.
119    private static final int STK_NOTIFICATION_ID = 333;
120
121    // Inner class used for queuing telephony messages (proactive commands,
122    // session end) while the service is busy processing a previous message.
123    private class DelayedCmd {
124        // members
125        int id;
126        StkCmdMessage msg;
127
128        DelayedCmd(int id, StkCmdMessage msg) {
129            this.id = id;
130            this.msg = msg;
131        }
132    }
133
134    @Override
135    public void onCreate() {
136        // Initialize members
137        mStkService = com.android.internal.telephony.gsm.stk.StkService
138                .getInstance();
139
140        // NOTE mStkService is a singleton and continues to exist even if the GSMPhone is disposed
141        //   after the radio technology change from GSM to CDMA so the PHONE_TYPE_CDMA check is
142        //   needed. In case of switching back from CDMA to GSM the GSMPhone constructor updates
143        //   the instance. (TODO: test).
144        if ((mStkService == null)
145                && (TelephonyManager.getDefault().getPhoneType()
146                                != TelephonyManager.PHONE_TYPE_CDMA)) {
147            StkLog.d(this, " Unable to get Service handle");
148            return;
149        }
150
151        mCmdsQ = new LinkedList<DelayedCmd>();
152        Thread serviceThread = new Thread(null, this, "Stk App Service");
153        serviceThread.start();
154        mContext = getBaseContext();
155        mNotificationManager = (NotificationManager) mContext
156                .getSystemService(Context.NOTIFICATION_SERVICE);
157        sInstance = this;
158    }
159
160    @Override
161    public void onStart(Intent intent, int startId) {
162        waitForLooper();
163
164        // onStart() method can be passed a null intent
165        // TODO: replace onStart() with onStartCommand()
166        if (intent == null) {
167            return;
168        }
169
170        Bundle args = intent.getExtras();
171
172        if (args == null) {
173            return;
174        }
175
176        Message msg = mServiceHandler.obtainMessage();
177        msg.arg1 = args.getInt(OPCODE);
178        switch(msg.arg1) {
179        case OP_CMD:
180            msg.obj = args.getParcelable(CMD_MSG);
181            break;
182        case OP_RESPONSE:
183            msg.obj = args;
184            /* falls through */
185        case OP_LAUNCH_APP:
186        case OP_END_SESSION:
187        case OP_BOOT_COMPLETED:
188            break;
189        default:
190            return;
191        }
192        mServiceHandler.sendMessage(msg);
193    }
194
195    @Override
196    public void onDestroy() {
197        waitForLooper();
198        mServiceLooper.quit();
199    }
200
201    @Override
202    public IBinder onBind(Intent intent) {
203        return null;
204    }
205
206    public void run() {
207        Looper.prepare();
208
209        mServiceLooper = Looper.myLooper();
210        mServiceHandler = new ServiceHandler();
211
212        Looper.loop();
213    }
214
215    /*
216     * Package api used by StkMenuActivity to indicate if its on the foreground.
217     */
218    void indicateMenuVisibility(boolean visibility) {
219        mMenuIsVisibile = visibility;
220    }
221
222    /*
223     * Package api used by StkMenuActivity to get its Menu parameter.
224     */
225    Menu getMenu() {
226        return mCurrentMenu;
227    }
228
229    /*
230     * Package api used by UI Activities and Dialogs to communicate directly
231     * with the service to deliver state information and parameters.
232     */
233    static StkAppService getInstance() {
234        return sInstance;
235    }
236
237    private void waitForLooper() {
238        while (mServiceHandler == null) {
239            synchronized (this) {
240                try {
241                    wait(100);
242                } catch (InterruptedException e) {
243                }
244            }
245        }
246    }
247
248    private final class ServiceHandler extends Handler {
249        @Override
250        public void handleMessage(Message msg) {
251            int opcode = msg.arg1;
252
253            switch (opcode) {
254            case OP_LAUNCH_APP:
255                if (mMainCmd == null) {
256                    // nothing todo when no SET UP MENU command didn't arrive.
257                    return;
258                }
259                launchMenuActivity(null);
260                break;
261            case OP_CMD:
262                StkCmdMessage cmdMsg = (StkCmdMessage) msg.obj;
263                // There are two types of commands:
264                // 1. Interactive - user's response is required.
265                // 2. Informative - display a message, no interaction with the user.
266                //
267                // Informative commands can be handled immediately without any delay.
268                // Interactive commands can't override each other. So if a command
269                // is already in progress, we need to queue the next command until
270                // the user has responded or a timeout expired.
271                if (!isCmdInteractive(cmdMsg)) {
272                    handleCmd(cmdMsg);
273                } else {
274                    if (!mCmdInProgress) {
275                        mCmdInProgress = true;
276                        handleCmd((StkCmdMessage) msg.obj);
277                    } else {
278                        mCmdsQ.addLast(new DelayedCmd(OP_CMD,
279                                (StkCmdMessage) msg.obj));
280                    }
281                }
282                break;
283            case OP_RESPONSE:
284                if (responseNeeded) {
285                    handleCmdResponse((Bundle) msg.obj);
286                }
287                // call delayed commands if needed.
288                if (mCmdsQ.size() != 0) {
289                    callDelayedMsg();
290                } else {
291                    mCmdInProgress = false;
292                }
293                // reset response needed state var to its original value.
294                responseNeeded = true;
295                break;
296            case OP_END_SESSION:
297                if (!mCmdInProgress) {
298                    mCmdInProgress = true;
299                    handleSessionEnd();
300                } else {
301                    mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null));
302                }
303                break;
304            case OP_BOOT_COMPLETED:
305                StkLog.d(this, "OP_BOOT_COMPLETED");
306                if (mMainCmd == null) {
307                    StkAppInstaller.unInstall(mContext);
308                }
309                break;
310            case OP_DELAYED_MSG:
311                handleDelayedCmd();
312                break;
313            }
314        }
315    }
316
317    private boolean isCmdInteractive(StkCmdMessage cmd) {
318        switch (cmd.getCmdType()) {
319        case SEND_DTMF:
320        case SEND_SMS:
321        case SEND_SS:
322        case SEND_USSD:
323        case SET_UP_IDLE_MODE_TEXT:
324        case SET_UP_MENU:
325            return false;
326        }
327
328        return true;
329    }
330
331    private void handleDelayedCmd() {
332        if (mCmdsQ.size() != 0) {
333            DelayedCmd cmd = mCmdsQ.poll();
334            switch (cmd.id) {
335            case OP_CMD:
336                handleCmd(cmd.msg);
337                break;
338            case OP_END_SESSION:
339                handleSessionEnd();
340                break;
341            }
342        }
343    }
344
345    private void callDelayedMsg() {
346        Message msg = mServiceHandler.obtainMessage();
347        msg.arg1 = OP_DELAYED_MSG;
348        mServiceHandler.sendMessage(msg);
349    }
350
351    private void handleSessionEnd() {
352        mCurrentCmd = mMainCmd;
353        lastSelectedItem = null;
354        // In case of SET UP MENU command which removed the app, don't
355        // update the current menu member.
356        if (mCurrentMenu != null && mMainCmd != null) {
357            mCurrentMenu = mMainCmd.getMenu();
358        }
359        if (mMenuIsVisibile) {
360            launchMenuActivity(null);
361        }
362        if (mCmdsQ.size() != 0) {
363            callDelayedMsg();
364        } else {
365            mCmdInProgress = false;
366        }
367        // In case a launch browser command was just confirmed, launch that url.
368        if (launchBrowser) {
369            launchBrowser = false;
370            launchBrowser(mBrowserSettings);
371        }
372    }
373
374    private void handleCmd(StkCmdMessage cmdMsg) {
375        if (cmdMsg == null) {
376            return;
377        }
378        // save local reference for state tracking.
379        mCurrentCmd = cmdMsg;
380        boolean waitForUsersResponse = true;
381
382        StkLog.d(this, cmdMsg.getCmdType().name());
383        switch (cmdMsg.getCmdType()) {
384        case DISPLAY_TEXT:
385            TextMessage msg = cmdMsg.geTextMessage();
386            responseNeeded = msg.responseNeeded;
387            if (lastSelectedItem != null) {
388                msg.title = lastSelectedItem;
389            } else if (mMainCmd != null){
390                msg.title = mMainCmd.getMenu().title;
391            } else {
392                // TODO: get the carrier name from the SIM
393                msg.title = "";
394            }
395            launchTextDialog();
396            break;
397        case SELECT_ITEM:
398            mCurrentMenu = cmdMsg.getMenu();
399            launchMenuActivity(cmdMsg.getMenu());
400            break;
401        case SET_UP_MENU:
402            mMainCmd = mCurrentCmd;
403            mCurrentMenu = cmdMsg.getMenu();
404            if (removeMenu()) {
405                StkLog.d(this, "Uninstall App");
406                mCurrentMenu = null;
407                StkAppInstaller.unInstall(mContext);
408            } else {
409                StkLog.d(this, "Install App");
410                StkAppInstaller.install(mContext);
411            }
412            if (mMenuIsVisibile) {
413                launchMenuActivity(null);
414            }
415            break;
416        case GET_INPUT:
417        case GET_INKEY:
418            launchInputActivity();
419            break;
420        case SET_UP_IDLE_MODE_TEXT:
421            waitForUsersResponse = false;
422            launchIdleText();
423            break;
424        case SEND_DTMF:
425        case SEND_SMS:
426        case SEND_SS:
427        case SEND_USSD:
428            waitForUsersResponse = false;
429            launchEventMessage();
430            break;
431        case LAUNCH_BROWSER:
432            launchConfirmationDialog(mCurrentCmd.geTextMessage());
433            break;
434        case SET_UP_CALL:
435            launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg);
436            break;
437        case PLAY_TONE:
438            launchToneDialog();
439            break;
440        }
441
442        if (!waitForUsersResponse) {
443            if (mCmdsQ.size() != 0) {
444                callDelayedMsg();
445            } else {
446                mCmdInProgress = false;
447            }
448        }
449    }
450
451    private void handleCmdResponse(Bundle args) {
452        if (mCurrentCmd == null) {
453            return;
454        }
455        StkResponseMessage resMsg = new StkResponseMessage(mCurrentCmd);
456
457        // set result code
458        boolean helpRequired = args.getBoolean(HELP, false);
459
460        switch(args.getInt(RES_ID)) {
461        case RES_ID_MENU_SELECTION:
462            StkLog.d(this, "RES_ID_MENU_SELECTION");
463            int menuSelection = args.getInt(MENU_SELECTION);
464            switch(mCurrentCmd.getCmdType()) {
465            case SET_UP_MENU:
466            case SELECT_ITEM:
467                lastSelectedItem = getItemName(menuSelection);
468                if (helpRequired) {
469                    resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
470                } else {
471                    resMsg.setResultCode(ResultCode.OK);
472                }
473                resMsg.setMenuSelection(menuSelection);
474                break;
475            }
476            break;
477        case RES_ID_INPUT:
478            StkLog.d(this, "RES_ID_INPUT");
479            String input = args.getString(INPUT);
480            if (mCurrentCmd.geInput().yesNo) {
481                boolean yesNoSelection = input
482                        .equals(StkInputActivity.YES_STR_RESPONSE);
483                resMsg.setYesNo(yesNoSelection);
484            } else {
485                if (helpRequired) {
486                    resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
487                } else {
488                    resMsg.setResultCode(ResultCode.OK);
489                    resMsg.setInput(input);
490                }
491            }
492            break;
493        case RES_ID_CONFIRM:
494            StkLog.d(this, "RES_ID_CONFIRM");
495            boolean confirmed = args.getBoolean(CONFIRMATION);
496            switch (mCurrentCmd.getCmdType()) {
497            case DISPLAY_TEXT:
498                resMsg.setResultCode(confirmed ? ResultCode.OK
499                        : ResultCode.UICC_SESSION_TERM_BY_USER);
500                break;
501            case LAUNCH_BROWSER:
502                resMsg.setResultCode(confirmed ? ResultCode.OK
503                        : ResultCode.UICC_SESSION_TERM_BY_USER);
504                if (confirmed) {
505                    launchBrowser = true;
506                    mBrowserSettings = mCurrentCmd.getBrowserSettings();
507                }
508                break;
509            case SET_UP_CALL:
510                resMsg.setResultCode(ResultCode.OK);
511                resMsg.setConfirmation(confirmed);
512                if (confirmed) {
513                    launchCallMsg();
514                }
515                break;
516            }
517            break;
518        case RES_ID_DONE:
519            resMsg.setResultCode(ResultCode.OK);
520            break;
521        case RES_ID_BACKWARD:
522            StkLog.d(this, "RES_ID_BACKWARD");
523            resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
524            break;
525        case RES_ID_END_SESSION:
526            StkLog.d(this, "RES_ID_END_SESSION");
527            resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
528            break;
529        case RES_ID_TIMEOUT:
530            StkLog.d(this, "RES_ID_TIMEOUT");
531            // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
532            // Clear message after delay, successful) expects result code OK.
533            // If the command qualifier specifies no user response is required
534            // then send OK instead of NO_RESPONSE_FROM_USER
535            if ((mCurrentCmd.getCmdType().value() == AppInterface.CommandType.DISPLAY_TEXT
536                    .value())
537                    && (mCurrentCmd.geTextMessage().userClear == false)) {
538                resMsg.setResultCode(ResultCode.OK);
539            } else {
540                resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
541            }
542            break;
543        default:
544            StkLog.d(this, "Unknown result id");
545            return;
546        }
547        mStkService.onCmdResponse(resMsg);
548    }
549
550    /**
551     * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
552     *
553     * @param userAction If the userAction is yes then we always return 0 otherwise
554     * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
555     * then we are the foreground app and we'll return 0 as from our perspective a
556     * user action did cause. If it's false than we aren't the foreground app and
557     * FLAG_ACTIVITY_NO_USER_ACTION is returned.
558     *
559     * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
560     */
561    private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) {
562        return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ?
563                                                    0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
564    }
565
566    private void launchMenuActivity(Menu menu) {
567        Intent newIntent = new Intent(Intent.ACTION_VIEW);
568        newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
569        int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
570                | Intent.FLAG_ACTIVITY_CLEAR_TOP;
571        if (menu == null) {
572            // We assume this was initiated by the user pressing the tool kit icon
573            intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
574
575            newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
576        } else {
577            // We don't know and we'll let getFlagActivityNoUserAction decide.
578            intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
579
580            newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
581        }
582        newIntent.setFlags(intentFlags);
583        mContext.startActivity(newIntent);
584    }
585
586    private void launchInputActivity() {
587        Intent newIntent = new Intent(Intent.ACTION_VIEW);
588        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
589                            | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
590        newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME);
591        newIntent.putExtra("INPUT", mCurrentCmd.geInput());
592        mContext.startActivity(newIntent);
593    }
594
595    private void launchTextDialog() {
596        Intent newIntent = new Intent(this, StkDialogActivity.class);
597        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
598                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
599                | Intent.FLAG_ACTIVITY_NO_HISTORY
600                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
601                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
602        newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
603        startActivity(newIntent);
604    }
605
606    private void launchEventMessage() {
607        TextMessage msg = mCurrentCmd.geTextMessage();
608        if (msg == null) {
609            return;
610        }
611        Toast toast = new Toast(mContext.getApplicationContext());
612        LayoutInflater inflate = (LayoutInflater) mContext
613                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
614        View v = inflate.inflate(R.layout.stk_event_msg, null);
615        TextView tv = (TextView) v
616                .findViewById(com.android.internal.R.id.message);
617        ImageView iv = (ImageView) v
618                .findViewById(com.android.internal.R.id.icon);
619        if (msg.icon != null) {
620            iv.setImageBitmap(msg.icon);
621        } else {
622            iv.setVisibility(View.GONE);
623        }
624        if (!msg.iconSelfExplanatory) {
625            tv.setText(msg.text);
626        }
627
628        toast.setView(v);
629        toast.setDuration(Toast.LENGTH_LONG);
630        toast.setGravity(Gravity.BOTTOM, 0, 0);
631        toast.show();
632    }
633
634    private void launchConfirmationDialog(TextMessage msg) {
635        msg.title = lastSelectedItem;
636        Intent newIntent = new Intent(this, StkDialogActivity.class);
637        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
638                | Intent.FLAG_ACTIVITY_NO_HISTORY
639                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
640                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
641        newIntent.putExtra("TEXT", msg);
642        startActivity(newIntent);
643    }
644
645    private void launchBrowser(BrowserSettings settings) {
646        if (settings == null) {
647            return;
648        }
649        // Set browser launch mode
650        Intent intent = new Intent();
651        intent.setClassName("com.android.browser",
652                "com.android.browser.BrowserActivity");
653
654        // to launch home page, make sure that data Uri is null.
655        Uri data = null;
656        if (settings.url != null) {
657            data = Uri.parse(settings.url);
658        }
659        intent.setData(data);
660        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
661        switch (settings.mode) {
662        case USE_EXISTING_BROWSER:
663            intent.setAction(Intent.ACTION_VIEW);
664            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
665            break;
666        case LAUNCH_NEW_BROWSER:
667            intent.setAction(Intent.ACTION_VIEW);
668            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
669            break;
670        case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
671            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
672            break;
673        }
674        // start browser activity
675        startActivity(intent);
676        // a small delay, let the browser start, before processing the next command.
677        // this is good for scenarios where a related DISPLAY TEXT command is
678        // followed immediately.
679        try {
680            Thread.sleep(10000);
681        } catch (InterruptedException e) {}
682    }
683
684    private void launchCallMsg() {
685        TextMessage msg = mCurrentCmd.getCallSettings().callMsg;
686        if (msg.text == null || msg.text.length() == 0) {
687            return;
688        }
689        msg.title = lastSelectedItem;
690
691        Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text,
692                Toast.LENGTH_LONG);
693        toast.setGravity(Gravity.BOTTOM, 0, 0);
694        toast.show();
695    }
696
697    private void launchIdleText() {
698        TextMessage msg = mCurrentCmd.geTextMessage();
699        if (msg.text == null) {
700            mNotificationManager.cancel(STK_NOTIFICATION_ID);
701        } else {
702            Notification notification = new Notification();
703            RemoteViews contentView = new RemoteViews(
704                    PACKAGE_NAME,
705                    com.android.internal.R.layout.status_bar_latest_event_content);
706
707            notification.flags |= Notification.FLAG_NO_CLEAR;
708            notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit;
709            // Set text and icon for the status bar and notification body.
710            if (!msg.iconSelfExplanatory) {
711                notification.tickerText = msg.text;
712                contentView.setTextViewText(com.android.internal.R.id.text,
713                        msg.text);
714            }
715            if (msg.icon != null) {
716                contentView.setImageViewBitmap(com.android.internal.R.id.icon,
717                        msg.icon);
718            } else {
719                contentView
720                        .setImageViewResource(
721                                com.android.internal.R.id.icon,
722                                com.android.internal.R.drawable.stat_notify_sim_toolkit);
723            }
724            notification.contentView = contentView;
725            notification.contentIntent = PendingIntent.getService(mContext, 0,
726                    new Intent(mContext, StkAppService.class), 0);
727
728            mNotificationManager.notify(STK_NOTIFICATION_ID, notification);
729        }
730    }
731
732    private void launchToneDialog() {
733        Intent newIntent = new Intent(this, ToneDialog.class);
734        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
735                | Intent.FLAG_ACTIVITY_NO_HISTORY
736                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
737                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
738        newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
739        newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
740        startActivity(newIntent);
741    }
742
743    private String getItemName(int itemId) {
744        Menu menu = mCurrentCmd.getMenu();
745        if (menu == null) {
746            return null;
747        }
748        for (Item item : menu.items) {
749            if (item.id == itemId) {
750                return item.text;
751            }
752        }
753        return null;
754    }
755
756    private boolean removeMenu() {
757        try {
758            if (mCurrentMenu.items.size() == 1 &&
759                mCurrentMenu.items.get(0) == null) {
760                return true;
761            }
762        } catch (NullPointerException e) {
763            StkLog.d(this, "Unable to get Menu's items size");
764            return true;
765        }
766        return false;
767    }
768}
769