Home | History | Annotate | Download | only in stk
      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 
     17 package com.android.stk;
     18 
     19 import android.app.Notification;
     20 import android.app.NotificationManager;
     21 import android.app.PendingIntent;
     22 import android.app.Service;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.os.IBinder;
     29 import android.os.Looper;
     30 import android.os.Message;
     31 import android.telephony.TelephonyManager;
     32 import android.view.Gravity;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.widget.ImageView;
     36 import android.widget.RemoteViews;
     37 import android.widget.TextView;
     38 import android.widget.Toast;
     39 
     40 import com.android.internal.telephony.gsm.stk.AppInterface;
     41 import com.android.internal.telephony.gsm.stk.Menu;
     42 import com.android.internal.telephony.gsm.stk.Item;
     43 import com.android.internal.telephony.gsm.stk.ResultCode;
     44 import com.android.internal.telephony.gsm.stk.StkCmdMessage;
     45 import com.android.internal.telephony.gsm.stk.StkCmdMessage.BrowserSettings;
     46 import com.android.internal.telephony.gsm.stk.StkLog;
     47 import com.android.internal.telephony.gsm.stk.StkResponseMessage;
     48 import com.android.internal.telephony.gsm.stk.TextMessage;
     49 
     50 import 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  */
     57 public 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 || msg.text == 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