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