blob: d98c14be8361a49ae045ea1e674d079c0e08fbfd [file] [log] [blame]
The Android Open Source Project9d9730a2009-03-03 19:32:37 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.stk;
18
Kazuhiro Ondo764167c2011-10-21 16:05:05 -050019import android.app.AlertDialog;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080020import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.app.Service;
24import android.content.Context;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -050025import android.content.DialogInterface;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080026import android.content.Intent;
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +010027import android.graphics.Bitmap;
28import android.graphics.BitmapFactory;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080029import android.net.Uri;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.IBinder;
33import android.os.Looper;
34import android.os.Message;
Wink Saville56469d52009-04-02 01:37:03 -070035import android.telephony.TelephonyManager;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080036import android.view.Gravity;
37import android.view.LayoutInflater;
38import android.view.View;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -050039import android.view.Window;
40import android.view.WindowManager;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080041import android.widget.ImageView;
42import android.widget.RemoteViews;
43import android.widget.TextView;
44import android.widget.Toast;
45
Alex Yakavenkad41f1d92010-07-12 14:13:13 -070046import com.android.internal.telephony.cat.AppInterface;
47import com.android.internal.telephony.cat.Menu;
48import com.android.internal.telephony.cat.Item;
Pierre Fröjd97503262010-11-08 13:59:36 +010049import com.android.internal.telephony.cat.Input;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -070050import com.android.internal.telephony.cat.ResultCode;
51import com.android.internal.telephony.cat.CatCmdMessage;
52import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings;
53import com.android.internal.telephony.cat.CatLog;
54import com.android.internal.telephony.cat.CatResponseMessage;
55import com.android.internal.telephony.cat.TextMessage;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080056
57import 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.
Wink Saville79085fc2009-06-09 10:27:23 -070062 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080063 */
64public 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;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -070071 private CatCmdMessage mMainCmd = null;
72 private CatCmdMessage mCurrentCmd = null;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080073 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";
Kazuhiro Ondo764167c2011-10-21 16:05:05 -050099 static final String CHOICE = "choice";
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800100
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;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500114 static final int RES_ID_CHOICE = 15;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800115
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
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500121 static final int YES = 1;
122 static final int NO = 0;
123
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800124 private static final String PACKAGE_NAME = "com.android.stk";
Wink Saville79085fc2009-06-09 10:27:23 -0700125 private static final String MENU_ACTIVITY_NAME =
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800126 PACKAGE_NAME + ".StkMenuActivity";
Wink Saville79085fc2009-06-09 10:27:23 -0700127 private static final String INPUT_ACTIVITY_NAME =
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800128 PACKAGE_NAME + ".StkInputActivity";
Wink Saville79085fc2009-06-09 10:27:23 -0700129
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800130 // Notification id used to display Idle Mode text in NotificationManager.
131 private static final int STK_NOTIFICATION_ID = 333;
Wink Saville79085fc2009-06-09 10:27:23 -0700132
133 // Inner class used for queuing telephony messages (proactive commands,
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800134 // session end) while the service is busy processing a previous message.
135 private class DelayedCmd {
136 // members
137 int id;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700138 CatCmdMessage msg;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800139
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700140 DelayedCmd(int id, CatCmdMessage msg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800141 this.id = id;
142 this.msg = msg;
143 }
144 }
145
146 @Override
147 public void onCreate() {
148 // Initialize members
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800149 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) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800160
Banavathu, Srinivas Naik87cda962012-07-17 15:32:44 +0530161 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");
duho.roae291582013-09-03 12:08:35 +0900167 StkAppInstaller.unInstall(mContext);
Banavathu, Srinivas Naik87cda962012-07-17 15:32:44 +0530168 return;
169 }
170
171 waitForLooper();
John Wang62acae42009-10-08 11:20:23 -0700172 // onStart() method can be passed a null intent
173 // TODO: replace onStart() with onStartCommand()
174 if (intent == null) {
175 return;
176 }
177
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800178 Bundle args = intent.getExtras();
John Wang62acae42009-10-08 11:20:23 -0700179
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800180 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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700270 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800271 // 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 //
Wink Saville79085fc2009-06-09 10:27:23 -0700275 // Informative commands can be handled immediately without any delay.
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800276 // 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;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700284 handleCmd((CatCmdMessage) msg.obj);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800285 } else {
286 mCmdsQ.addLast(new DelayedCmd(OP_CMD,
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700287 (CatCmdMessage) msg.obj));
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800288 }
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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700313 CatLog.d(this, "OP_BOOT_COMPLETED");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800314 if (mMainCmd == null) {
315 StkAppInstaller.unInstall(mContext);
316 }
317 break;
318 case OP_DELAYED_MSG:
319 handleDelayedCmd();
320 break;
321 }
322 }
323 }
324
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700325 private boolean isCmdInteractive(CatCmdMessage cmd) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800326 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:
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500333 case CLOSE_CHANNEL:
334 case RECEIVE_DATA:
335 case SEND_DATA:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800336 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;
Wink Saville79085fc2009-06-09 10:27:23 -0700365 // In case of SET UP MENU command which removed the app, don't
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800366 // 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
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700385 private void handleCmd(CatCmdMessage cmdMsg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800386 if (cmdMsg == null) {
387 return;
388 }
389 // save local reference for state tracking.
390 mCurrentCmd = cmdMsg;
391 boolean waitForUsersResponse = true;
392
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700393 CatLog.d(this, cmdMsg.getCmdType().name());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800394 switch (cmdMsg.getCmdType()) {
395 case DISPLAY_TEXT:
396 TextMessage msg = cmdMsg.geTextMessage();
397 responseNeeded = msg.responseNeeded;
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200398 waitForUsersResponse = msg.responseNeeded;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800399 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()) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700417 CatLog.d(this, "Uninstall App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800418 mCurrentMenu = null;
419 StkAppInstaller.unInstall(mContext);
420 } else {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700421 CatLog.d(this, "Install App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800422 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;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500452 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 }
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700473 /*
474 * Display indication in the form of a toast to the user if required.
475 */
476 launchEventMessage();
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500477 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800478 }
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 }
Alex Yakavenkad8e2ecd2012-04-20 17:10:15 -0700493 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
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700502 CatResponseMessage resMsg = new CatResponseMessage(mCurrentCmd);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800503
504 // set result code
505 boolean helpRequired = args.getBoolean(HELP, false);
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700506 boolean confirmed = false;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800507
508 switch(args.getInt(RES_ID)) {
509 case RES_ID_MENU_SELECTION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700510 CatLog.d(this, "RES_ID_MENU_SELECTION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800511 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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700526 CatLog.d(this, "RES_ID_INPUT");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800527 String input = args.getString(INPUT);
Pierre Fröjd97503262010-11-08 13:59:36 +0100528 Input cmdInput = mCurrentCmd.geInput();
529 if (cmdInput != null && cmdInput.yesNo) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800530 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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700543 CatLog.d(this, "RES_ID_CONFIRM");
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700544 confirmed = args.getBoolean(CONFIRMATION);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800545 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) {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100562 launchEventMessage(mCurrentCmd.getCallSettings().callMsg);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800563 }
564 break;
565 }
566 break;
567 case RES_ID_DONE:
568 resMsg.setResultCode(ResultCode.OK);
569 break;
570 case RES_ID_BACKWARD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700571 CatLog.d(this, "RES_ID_BACKWARD");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800572 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
573 break;
574 case RES_ID_END_SESSION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700575 CatLog.d(this, "RES_ID_END_SESSION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800576 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
577 break;
578 case RES_ID_TIMEOUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700579 CatLog.d(this, "RES_ID_TIMEOUT");
Naveen Kallad5176892009-11-30 12:45:29 -0800580 // 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 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800591 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500592 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);
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700598 confirmed = true;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500599 break;
600 case NO:
601 resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
602 break;
603 }
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700604
605 if (mCurrentCmd.getCmdType().value() == AppInterface.CommandType.OPEN_CHANNEL
606 .value()) {
607 resMsg.setConfirmation(confirmed);
608 }
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500609 break;
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700610
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800611 default:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700612 CatLog.d(this, "Unknown result id");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800613 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.
Wink Saville79085fc2009-06-09 10:27:23 -0700626 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800627 * @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
duho.ro76abd4b2013-09-22 08:36:35 +0900666 | Intent.FLAG_ACTIVITY_CLEAR_TOP
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800667 | 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() {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100675 launchEventMessage(mCurrentCmd.geTextMessage());
676 }
677
678 private void launchEventMessage(TextMessage msg) {
John Josephb22e7d82009-12-15 14:53:17 -0800679 if (msg == null || msg.text == null) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800680 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 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800720
Abhishek Adappa840c82f2013-02-26 10:19:49 -0800721 Intent intent = null;
722 Uri data = null;
David Brown7c03cfe2011-10-20 15:36:12 -0700723
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800724 if (settings.url != null) {
dujin.cha486c1d02011-11-02 22:14:25 +0900725 CatLog.d(this, "settings.url = " + settings.url);
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900726 if ((settings.url.startsWith("http://") || (settings.url.startsWith("https://")))) {
dujin.cha486c1d02011-11-02 22:14:25 +0900727 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 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800733 }
Abhishek Adappa840c82f2013-02-26 10:19:49 -0800734 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 }
David Brown7c03cfe2011-10-20 15:36:12 -0700744
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800745 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
746 switch (settings.mode) {
747 case USE_EXISTING_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800748 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
749 break;
750 case LAUNCH_NEW_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800751 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.
Wink Saville79085fc2009-06-09 10:27:23 -0700760 // this is good for scenarios where a related DISPLAY TEXT command is
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800761 // followed immediately.
762 try {
763 Thread.sleep(10000);
764 } catch (InterruptedException e) {}
765 }
766
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800767 private void launchIdleText() {
768 TextMessage msg = mCurrentCmd.geTextMessage();
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900769
Wink Saville046db4b2011-11-01 20:42:54 -0700770 if (msg == null) {
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900771 CatLog.d(this, "mCurrent.getTextMessage is NULL");
772 mNotificationManager.cancel(STK_NOTIFICATION_ID);
Wink Saville046db4b2011-11-01 20:42:54 -0700773 return;
774 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800775 if (msg.text == null) {
776 mNotificationManager.cancel(STK_NOTIFICATION_ID);
777 } else {
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100778 PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800779 new Intent(mContext, StkAppService.class), 0);
780
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100781 final Notification.Builder notificationBuilder = new Notification.Builder(
782 StkAppService.this);
Christopher Posselwhite2ad2ab72013-05-27 07:51:06 +0200783 if (mMainCmd != null && mMainCmd.getMenu() != null) {
784 notificationBuilder.setContentTitle(mMainCmd.getMenu().title);
785 } else {
786 notificationBuilder.setContentTitle("");
787 }
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100788 notificationBuilder
789 .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
790 notificationBuilder.setContentIntent(pendingIntent);
791 notificationBuilder.setOngoing(true);
792 // Set text and icon for the status bar and notification body.
793 if (!msg.iconSelfExplanatory) {
794 notificationBuilder.setContentText(msg.text);
Christopher Posselwhite2ad2ab72013-05-27 07:51:06 +0200795 notificationBuilder.setTicker(msg.text);
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100796 }
797 if (msg.icon != null) {
798 notificationBuilder.setLargeIcon(msg.icon);
799 } else {
800 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this
801 .getResources().getSystem(),
802 com.android.internal.R.drawable.stat_notify_sim_toolkit);
803 notificationBuilder.setLargeIcon(bitmapIcon);
804 }
805
806 mNotificationManager.notify(STK_NOTIFICATION_ID, notificationBuilder.build());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800807 }
808 }
809
810 private void launchToneDialog() {
811 Intent newIntent = new Intent(this, ToneDialog.class);
812 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
813 | Intent.FLAG_ACTIVITY_NO_HISTORY
814 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
815 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
816 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
817 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
818 startActivity(newIntent);
819 }
820
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500821 private void launchOpenChannelDialog() {
822 TextMessage msg = mCurrentCmd.geTextMessage();
823 if (msg == null) {
824 CatLog.d(this, "msg is null, return here");
825 return;
826 }
827
828 msg.title = getResources().getString(R.string.stk_dialog_title);
829 if (msg.text == null) {
830 msg.text = getResources().getString(R.string.default_open_channel_msg);
831 }
832
833 final AlertDialog dialog = new AlertDialog.Builder(mContext)
834 .setIconAttribute(android.R.attr.alertDialogIcon)
835 .setTitle(msg.title)
836 .setMessage(msg.text)
837 .setCancelable(false)
838 .setPositiveButton(getResources().getString(R.string.stk_dialog_accept),
839 new DialogInterface.OnClickListener() {
840 public void onClick(DialogInterface dialog, int which) {
841 Bundle args = new Bundle();
842 args.putInt(RES_ID, RES_ID_CHOICE);
843 args.putInt(CHOICE, YES);
844 Message message = mServiceHandler.obtainMessage();
845 message.arg1 = OP_RESPONSE;
846 message.obj = args;
847 mServiceHandler.sendMessage(message);
848 }
849 })
850 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
851 new DialogInterface.OnClickListener() {
852 public void onClick(DialogInterface dialog, int which) {
853 Bundle args = new Bundle();
854 args.putInt(RES_ID, RES_ID_CHOICE);
855 args.putInt(CHOICE, NO);
856 Message message = mServiceHandler.obtainMessage();
857 message.arg1 = OP_RESPONSE;
858 message.obj = args;
859 mServiceHandler.sendMessage(message);
860 }
861 })
862 .create();
863
864 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
865 if (!mContext.getResources().getBoolean(
866 com.android.internal.R.bool.config_sf_slowBlur)) {
867 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
868 }
869
870 dialog.show();
871 }
872
873 private void launchTransientEventMessage() {
874 TextMessage msg = mCurrentCmd.geTextMessage();
875 if (msg == null) {
876 CatLog.d(this, "msg is null, return here");
877 return;
878 }
879
880 msg.title = getResources().getString(R.string.stk_dialog_title);
881
882 final AlertDialog dialog = new AlertDialog.Builder(mContext)
883 .setIconAttribute(android.R.attr.alertDialogIcon)
884 .setTitle(msg.title)
885 .setMessage(msg.text)
886 .setCancelable(false)
887 .setPositiveButton(getResources().getString(android.R.string.ok),
888 new DialogInterface.OnClickListener() {
889 public void onClick(DialogInterface dialog, int which) {
890 }
891 })
892 .create();
893
894 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
895 if (!mContext.getResources().getBoolean(
896 com.android.internal.R.bool.config_sf_slowBlur)) {
897 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
898 }
899
900 dialog.show();
901 }
902
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800903 private String getItemName(int itemId) {
904 Menu menu = mCurrentCmd.getMenu();
905 if (menu == null) {
906 return null;
907 }
908 for (Item item : menu.items) {
909 if (item.id == itemId) {
910 return item.text;
911 }
912 }
913 return null;
914 }
915
916 private boolean removeMenu() {
917 try {
Wink Saville79085fc2009-06-09 10:27:23 -0700918 if (mCurrentMenu.items.size() == 1 &&
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800919 mCurrentMenu.items.get(0) == null) {
920 return true;
921 }
922 } catch (NullPointerException e) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700923 CatLog.d(this, "Unable to get Menu's items size");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800924 return true;
925 }
926 return false;
927 }
928}