blob: 48e3f67ca1cf6b2ad3a9a91145de42d1b47a0344 [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
Alex Yakavenkad8e2ecd2012-04-20 17:10:15 -0700147 // This can return null if StkService is not yet instantiated, but it's ok
148 // If this is null we will do getInstance before we need to use this
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700149 mStkService = com.android.internal.telephony.cat.CatService
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800150 .getInstance();
Wink Saville36eddd52009-05-29 13:56:28 -0700151
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800152 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
162 public void onStart(Intent intent, int startId) {
163 waitForLooper();
164
John Wang62acae42009-10-08 11:20:23 -0700165 // onStart() method can be passed a null intent
166 // TODO: replace onStart() with onStartCommand()
167 if (intent == null) {
168 return;
169 }
170
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800171 Bundle args = intent.getExtras();
John Wang62acae42009-10-08 11:20:23 -0700172
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800173 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
197 public void onDestroy() {
198 waitForLooper();
199 mServiceLooper.quit();
200 }
201
202 @Override
203 public IBinder onBind(Intent intent) {
204 return null;
205 }
206
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 */
219 void indicateMenuVisibility(boolean visibility) {
220 mMenuIsVisibile = visibility;
221 }
222
223 /*
224 * Package api used by StkMenuActivity to get its Menu parameter.
225 */
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 */
234 static StkAppService getInstance() {
235 return sInstance;
236 }
237
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
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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700263 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800264 // 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 //
Wink Saville79085fc2009-06-09 10:27:23 -0700268 // Informative commands can be handled immediately without any delay.
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800269 // 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;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700277 handleCmd((CatCmdMessage) msg.obj);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800278 } else {
279 mCmdsQ.addLast(new DelayedCmd(OP_CMD,
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700280 (CatCmdMessage) msg.obj));
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800281 }
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:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700306 CatLog.d(this, "OP_BOOT_COMPLETED");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800307 if (mMainCmd == null) {
308 StkAppInstaller.unInstall(mContext);
309 }
310 break;
311 case OP_DELAYED_MSG:
312 handleDelayedCmd();
313 break;
314 }
315 }
316 }
317
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700318 private boolean isCmdInteractive(CatCmdMessage cmd) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800319 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:
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500326 case CLOSE_CHANNEL:
327 case RECEIVE_DATA:
328 case SEND_DATA:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800329 return false;
330 }
331
332 return true;
333 }
334
335 private void handleDelayedCmd() {
336 if (mCmdsQ.size() != 0) {
337 DelayedCmd cmd = mCmdsQ.poll();
338 switch (cmd.id) {
339 case OP_CMD:
340 handleCmd(cmd.msg);
341 break;
342 case OP_END_SESSION:
343 handleSessionEnd();
344 break;
345 }
346 }
347 }
348
349 private void callDelayedMsg() {
350 Message msg = mServiceHandler.obtainMessage();
351 msg.arg1 = OP_DELAYED_MSG;
352 mServiceHandler.sendMessage(msg);
353 }
354
355 private void handleSessionEnd() {
356 mCurrentCmd = mMainCmd;
357 lastSelectedItem = null;
Wink Saville79085fc2009-06-09 10:27:23 -0700358 // In case of SET UP MENU command which removed the app, don't
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800359 // update the current menu member.
360 if (mCurrentMenu != null && mMainCmd != null) {
361 mCurrentMenu = mMainCmd.getMenu();
362 }
363 if (mMenuIsVisibile) {
364 launchMenuActivity(null);
365 }
366 if (mCmdsQ.size() != 0) {
367 callDelayedMsg();
368 } else {
369 mCmdInProgress = false;
370 }
371 // In case a launch browser command was just confirmed, launch that url.
372 if (launchBrowser) {
373 launchBrowser = false;
374 launchBrowser(mBrowserSettings);
375 }
376 }
377
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700378 private void handleCmd(CatCmdMessage cmdMsg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800379 if (cmdMsg == null) {
380 return;
381 }
382 // save local reference for state tracking.
383 mCurrentCmd = cmdMsg;
384 boolean waitForUsersResponse = true;
385
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700386 CatLog.d(this, cmdMsg.getCmdType().name());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800387 switch (cmdMsg.getCmdType()) {
388 case DISPLAY_TEXT:
389 TextMessage msg = cmdMsg.geTextMessage();
390 responseNeeded = msg.responseNeeded;
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200391 waitForUsersResponse = msg.responseNeeded;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800392 if (lastSelectedItem != null) {
393 msg.title = lastSelectedItem;
394 } else if (mMainCmd != null){
395 msg.title = mMainCmd.getMenu().title;
396 } else {
397 // TODO: get the carrier name from the SIM
398 msg.title = "";
399 }
400 launchTextDialog();
401 break;
402 case SELECT_ITEM:
403 mCurrentMenu = cmdMsg.getMenu();
404 launchMenuActivity(cmdMsg.getMenu());
405 break;
406 case SET_UP_MENU:
407 mMainCmd = mCurrentCmd;
408 mCurrentMenu = cmdMsg.getMenu();
409 if (removeMenu()) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700410 CatLog.d(this, "Uninstall App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800411 mCurrentMenu = null;
412 StkAppInstaller.unInstall(mContext);
413 } else {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700414 CatLog.d(this, "Install App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800415 StkAppInstaller.install(mContext);
416 }
417 if (mMenuIsVisibile) {
418 launchMenuActivity(null);
419 }
420 break;
421 case GET_INPUT:
422 case GET_INKEY:
423 launchInputActivity();
424 break;
425 case SET_UP_IDLE_MODE_TEXT:
426 waitForUsersResponse = false;
427 launchIdleText();
428 break;
429 case SEND_DTMF:
430 case SEND_SMS:
431 case SEND_SS:
432 case SEND_USSD:
433 waitForUsersResponse = false;
434 launchEventMessage();
435 break;
436 case LAUNCH_BROWSER:
437 launchConfirmationDialog(mCurrentCmd.geTextMessage());
438 break;
439 case SET_UP_CALL:
440 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg);
441 break;
442 case PLAY_TONE:
443 launchToneDialog();
444 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500445 case OPEN_CHANNEL:
446 launchOpenChannelDialog();
447 break;
448 case CLOSE_CHANNEL:
449 case RECEIVE_DATA:
450 case SEND_DATA:
451 TextMessage m = mCurrentCmd.geTextMessage();
452
453 if ((m != null) && (m.text == null)) {
454 switch(cmdMsg.getCmdType()) {
455 case CLOSE_CHANNEL:
456 m.text = getResources().getString(R.string.default_close_channel_msg);
457 break;
458 case RECEIVE_DATA:
459 m.text = getResources().getString(R.string.default_receive_data_msg);
460 break;
461 case SEND_DATA:
462 m.text = getResources().getString(R.string.default_send_data_msg);
463 break;
464 }
465 }
466 launchTransientEventMessage();
467 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800468 }
469
470 if (!waitForUsersResponse) {
471 if (mCmdsQ.size() != 0) {
472 callDelayedMsg();
473 } else {
474 mCmdInProgress = false;
475 }
476 }
477 }
478
479 private void handleCmdResponse(Bundle args) {
480 if (mCurrentCmd == null) {
481 return;
482 }
Alex Yakavenkad8e2ecd2012-04-20 17:10:15 -0700483 if (mStkService == null) {
484 mStkService = com.android.internal.telephony.cat.CatService.getInstance();
485 if (mStkService == null) {
486 // This should never happen (we should be responding only to a message
487 // that arrived from StkService). It has to exist by this time
488 throw new RuntimeException("mStkService is null when we need to send response");
489 }
490 }
491
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700492 CatResponseMessage resMsg = new CatResponseMessage(mCurrentCmd);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800493
494 // set result code
495 boolean helpRequired = args.getBoolean(HELP, false);
496
497 switch(args.getInt(RES_ID)) {
498 case RES_ID_MENU_SELECTION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700499 CatLog.d(this, "RES_ID_MENU_SELECTION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800500 int menuSelection = args.getInt(MENU_SELECTION);
501 switch(mCurrentCmd.getCmdType()) {
502 case SET_UP_MENU:
503 case SELECT_ITEM:
504 lastSelectedItem = getItemName(menuSelection);
505 if (helpRequired) {
506 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
507 } else {
508 resMsg.setResultCode(ResultCode.OK);
509 }
510 resMsg.setMenuSelection(menuSelection);
511 break;
512 }
513 break;
514 case RES_ID_INPUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700515 CatLog.d(this, "RES_ID_INPUT");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800516 String input = args.getString(INPUT);
Pierre Fröjd97503262010-11-08 13:59:36 +0100517 Input cmdInput = mCurrentCmd.geInput();
518 if (cmdInput != null && cmdInput.yesNo) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800519 boolean yesNoSelection = input
520 .equals(StkInputActivity.YES_STR_RESPONSE);
521 resMsg.setYesNo(yesNoSelection);
522 } else {
523 if (helpRequired) {
524 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
525 } else {
526 resMsg.setResultCode(ResultCode.OK);
527 resMsg.setInput(input);
528 }
529 }
530 break;
531 case RES_ID_CONFIRM:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700532 CatLog.d(this, "RES_ID_CONFIRM");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800533 boolean confirmed = args.getBoolean(CONFIRMATION);
534 switch (mCurrentCmd.getCmdType()) {
535 case DISPLAY_TEXT:
536 resMsg.setResultCode(confirmed ? ResultCode.OK
537 : ResultCode.UICC_SESSION_TERM_BY_USER);
538 break;
539 case LAUNCH_BROWSER:
540 resMsg.setResultCode(confirmed ? ResultCode.OK
541 : ResultCode.UICC_SESSION_TERM_BY_USER);
542 if (confirmed) {
543 launchBrowser = true;
544 mBrowserSettings = mCurrentCmd.getBrowserSettings();
545 }
546 break;
547 case SET_UP_CALL:
548 resMsg.setResultCode(ResultCode.OK);
549 resMsg.setConfirmation(confirmed);
550 if (confirmed) {
551 launchCallMsg();
552 }
553 break;
554 }
555 break;
556 case RES_ID_DONE:
557 resMsg.setResultCode(ResultCode.OK);
558 break;
559 case RES_ID_BACKWARD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700560 CatLog.d(this, "RES_ID_BACKWARD");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800561 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
562 break;
563 case RES_ID_END_SESSION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700564 CatLog.d(this, "RES_ID_END_SESSION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800565 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
566 break;
567 case RES_ID_TIMEOUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700568 CatLog.d(this, "RES_ID_TIMEOUT");
Naveen Kallad5176892009-11-30 12:45:29 -0800569 // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
570 // Clear message after delay, successful) expects result code OK.
571 // If the command qualifier specifies no user response is required
572 // then send OK instead of NO_RESPONSE_FROM_USER
573 if ((mCurrentCmd.getCmdType().value() == AppInterface.CommandType.DISPLAY_TEXT
574 .value())
575 && (mCurrentCmd.geTextMessage().userClear == false)) {
576 resMsg.setResultCode(ResultCode.OK);
577 } else {
578 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
579 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800580 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500581 case RES_ID_CHOICE:
582 int choice = args.getInt(CHOICE);
583 CatLog.d(this, "User Choice=" + choice);
584 switch (choice) {
585 case YES:
586 resMsg.setResultCode(ResultCode.OK);
587 break;
588 case NO:
589 resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
590 break;
591 }
592 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800593 default:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700594 CatLog.d(this, "Unknown result id");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800595 return;
596 }
597 mStkService.onCmdResponse(resMsg);
598 }
599
600 /**
601 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
602 *
603 * @param userAction If the userAction is yes then we always return 0 otherwise
604 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
605 * then we are the foreground app and we'll return 0 as from our perspective a
606 * user action did cause. If it's false than we aren't the foreground app and
607 * FLAG_ACTIVITY_NO_USER_ACTION is returned.
Wink Saville79085fc2009-06-09 10:27:23 -0700608 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800609 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
610 */
611 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) {
612 return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ?
613 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
614 }
615
616 private void launchMenuActivity(Menu menu) {
617 Intent newIntent = new Intent(Intent.ACTION_VIEW);
618 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
619 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
620 | Intent.FLAG_ACTIVITY_CLEAR_TOP;
621 if (menu == null) {
622 // We assume this was initiated by the user pressing the tool kit icon
623 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
624
625 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
626 } else {
627 // We don't know and we'll let getFlagActivityNoUserAction decide.
628 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
629
630 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
631 }
632 newIntent.setFlags(intentFlags);
633 mContext.startActivity(newIntent);
634 }
635
636 private void launchInputActivity() {
637 Intent newIntent = new Intent(Intent.ACTION_VIEW);
638 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
639 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
640 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME);
641 newIntent.putExtra("INPUT", mCurrentCmd.geInput());
642 mContext.startActivity(newIntent);
643 }
644
645 private void launchTextDialog() {
646 Intent newIntent = new Intent(this, StkDialogActivity.class);
647 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200648 | Intent.FLAG_ACTIVITY_CLEAR_TASK
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800649 | Intent.FLAG_ACTIVITY_NO_HISTORY
650 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
651 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
652 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
653 startActivity(newIntent);
654 }
655
656 private void launchEventMessage() {
657 TextMessage msg = mCurrentCmd.geTextMessage();
John Josephb22e7d82009-12-15 14:53:17 -0800658 if (msg == null || msg.text == null) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800659 return;
660 }
661 Toast toast = new Toast(mContext.getApplicationContext());
662 LayoutInflater inflate = (LayoutInflater) mContext
663 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
664 View v = inflate.inflate(R.layout.stk_event_msg, null);
665 TextView tv = (TextView) v
666 .findViewById(com.android.internal.R.id.message);
667 ImageView iv = (ImageView) v
668 .findViewById(com.android.internal.R.id.icon);
669 if (msg.icon != null) {
670 iv.setImageBitmap(msg.icon);
671 } else {
672 iv.setVisibility(View.GONE);
673 }
674 if (!msg.iconSelfExplanatory) {
675 tv.setText(msg.text);
676 }
677
678 toast.setView(v);
679 toast.setDuration(Toast.LENGTH_LONG);
680 toast.setGravity(Gravity.BOTTOM, 0, 0);
681 toast.show();
682 }
683
684 private void launchConfirmationDialog(TextMessage msg) {
685 msg.title = lastSelectedItem;
686 Intent newIntent = new Intent(this, StkDialogActivity.class);
687 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
688 | Intent.FLAG_ACTIVITY_NO_HISTORY
689 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
690 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
691 newIntent.putExtra("TEXT", msg);
692 startActivity(newIntent);
693 }
694
695 private void launchBrowser(BrowserSettings settings) {
696 if (settings == null) {
697 return;
698 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800699
David Brown7c03cfe2011-10-20 15:36:12 -0700700 Intent intent = new Intent(Intent.ACTION_VIEW);
701
702 Uri data;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800703 if (settings.url != null) {
dujin.cha486c1d02011-11-02 22:14:25 +0900704 CatLog.d(this, "settings.url = " + settings.url);
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900705 if ((settings.url.startsWith("http://") || (settings.url.startsWith("https://")))) {
dujin.cha486c1d02011-11-02 22:14:25 +0900706 data = Uri.parse(settings.url);
707 } else {
708 String modifiedUrl = "http://" + settings.url;
709 CatLog.d(this, "modifiedUrl = " + modifiedUrl);
710 data = Uri.parse(modifiedUrl);
711 }
David Brown7c03cfe2011-10-20 15:36:12 -0700712 } else {
713 // If no URL specified, just bring up the "home page".
714 //
715 // (Note we need to specify *something* in the intent's data field
716 // here, since if you fire off a VIEW intent with no data at all
717 // you'll get an activity chooser rather than the browser. There's
718 // no specific URI that means "use the default home page", so
719 // instead let's just explicitly bring up http://google.com.)
720 data = Uri.parse("http://google.com/");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800721 }
722 intent.setData(data);
David Brown7c03cfe2011-10-20 15:36:12 -0700723
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800724 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
725 switch (settings.mode) {
726 case USE_EXISTING_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800727 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
728 break;
729 case LAUNCH_NEW_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800730 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
731 break;
732 case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
733 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
734 break;
735 }
736 // start browser activity
737 startActivity(intent);
738 // a small delay, let the browser start, before processing the next command.
Wink Saville79085fc2009-06-09 10:27:23 -0700739 // this is good for scenarios where a related DISPLAY TEXT command is
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800740 // followed immediately.
741 try {
742 Thread.sleep(10000);
743 } catch (InterruptedException e) {}
744 }
745
746 private void launchCallMsg() {
747 TextMessage msg = mCurrentCmd.getCallSettings().callMsg;
748 if (msg.text == null || msg.text.length() == 0) {
749 return;
750 }
751 msg.title = lastSelectedItem;
752
753 Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text,
754 Toast.LENGTH_LONG);
755 toast.setGravity(Gravity.BOTTOM, 0, 0);
756 toast.show();
757 }
758
759 private void launchIdleText() {
760 TextMessage msg = mCurrentCmd.geTextMessage();
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900761
Wink Saville046db4b2011-11-01 20:42:54 -0700762 if (msg == null) {
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900763 CatLog.d(this, "mCurrent.getTextMessage is NULL");
764 mNotificationManager.cancel(STK_NOTIFICATION_ID);
Wink Saville046db4b2011-11-01 20:42:54 -0700765 return;
766 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800767 if (msg.text == null) {
768 mNotificationManager.cancel(STK_NOTIFICATION_ID);
769 } else {
770 Notification notification = new Notification();
771 RemoteViews contentView = new RemoteViews(
772 PACKAGE_NAME,
773 com.android.internal.R.layout.status_bar_latest_event_content);
774
775 notification.flags |= Notification.FLAG_NO_CLEAR;
776 notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit;
777 // Set text and icon for the status bar and notification body.
778 if (!msg.iconSelfExplanatory) {
779 notification.tickerText = msg.text;
780 contentView.setTextViewText(com.android.internal.R.id.text,
781 msg.text);
782 }
783 if (msg.icon != null) {
784 contentView.setImageViewBitmap(com.android.internal.R.id.icon,
785 msg.icon);
786 } else {
787 contentView
788 .setImageViewResource(
789 com.android.internal.R.id.icon,
790 com.android.internal.R.drawable.stat_notify_sim_toolkit);
791 }
792 notification.contentView = contentView;
793 notification.contentIntent = PendingIntent.getService(mContext, 0,
794 new Intent(mContext, StkAppService.class), 0);
795
796 mNotificationManager.notify(STK_NOTIFICATION_ID, notification);
797 }
798 }
799
800 private void launchToneDialog() {
801 Intent newIntent = new Intent(this, ToneDialog.class);
802 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
803 | Intent.FLAG_ACTIVITY_NO_HISTORY
804 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
805 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
806 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
807 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
808 startActivity(newIntent);
809 }
810
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500811 private void launchOpenChannelDialog() {
812 TextMessage msg = mCurrentCmd.geTextMessage();
813 if (msg == null) {
814 CatLog.d(this, "msg is null, return here");
815 return;
816 }
817
818 msg.title = getResources().getString(R.string.stk_dialog_title);
819 if (msg.text == null) {
820 msg.text = getResources().getString(R.string.default_open_channel_msg);
821 }
822
823 final AlertDialog dialog = new AlertDialog.Builder(mContext)
824 .setIconAttribute(android.R.attr.alertDialogIcon)
825 .setTitle(msg.title)
826 .setMessage(msg.text)
827 .setCancelable(false)
828 .setPositiveButton(getResources().getString(R.string.stk_dialog_accept),
829 new DialogInterface.OnClickListener() {
830 public void onClick(DialogInterface dialog, int which) {
831 Bundle args = new Bundle();
832 args.putInt(RES_ID, RES_ID_CHOICE);
833 args.putInt(CHOICE, YES);
834 Message message = mServiceHandler.obtainMessage();
835 message.arg1 = OP_RESPONSE;
836 message.obj = args;
837 mServiceHandler.sendMessage(message);
838 }
839 })
840 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
841 new DialogInterface.OnClickListener() {
842 public void onClick(DialogInterface dialog, int which) {
843 Bundle args = new Bundle();
844 args.putInt(RES_ID, RES_ID_CHOICE);
845 args.putInt(CHOICE, NO);
846 Message message = mServiceHandler.obtainMessage();
847 message.arg1 = OP_RESPONSE;
848 message.obj = args;
849 mServiceHandler.sendMessage(message);
850 }
851 })
852 .create();
853
854 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
855 if (!mContext.getResources().getBoolean(
856 com.android.internal.R.bool.config_sf_slowBlur)) {
857 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
858 }
859
860 dialog.show();
861 }
862
863 private void launchTransientEventMessage() {
864 TextMessage msg = mCurrentCmd.geTextMessage();
865 if (msg == null) {
866 CatLog.d(this, "msg is null, return here");
867 return;
868 }
869
870 msg.title = getResources().getString(R.string.stk_dialog_title);
871
872 final AlertDialog dialog = new AlertDialog.Builder(mContext)
873 .setIconAttribute(android.R.attr.alertDialogIcon)
874 .setTitle(msg.title)
875 .setMessage(msg.text)
876 .setCancelable(false)
877 .setPositiveButton(getResources().getString(android.R.string.ok),
878 new DialogInterface.OnClickListener() {
879 public void onClick(DialogInterface dialog, int which) {
880 }
881 })
882 .create();
883
884 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
885 if (!mContext.getResources().getBoolean(
886 com.android.internal.R.bool.config_sf_slowBlur)) {
887 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
888 }
889
890 dialog.show();
891 }
892
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800893 private String getItemName(int itemId) {
894 Menu menu = mCurrentCmd.getMenu();
895 if (menu == null) {
896 return null;
897 }
898 for (Item item : menu.items) {
899 if (item.id == itemId) {
900 return item.text;
901 }
902 }
903 return null;
904 }
905
906 private boolean removeMenu() {
907 try {
Wink Saville79085fc2009-06-09 10:27:23 -0700908 if (mCurrentMenu.items.size() == 1 &&
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800909 mCurrentMenu.items.get(0) == null) {
910 return true;
911 }
912 } catch (NullPointerException e) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700913 CatLog.d(this, "Unable to get Menu's items size");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800914 return true;
915 }
916 return false;
917 }
918}