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