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