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