blob: 83971736a2bf298946eabd6d081210205e754b79 [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");
167 return;
168 }
169
170 waitForLooper();
John Wang62acae42009-10-08 11:20:23 -0700171 // onStart() method can be passed a null intent
172 // TODO: replace onStart() with onStartCommand()
173 if (intent == null) {
174 return;
175 }
176
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800177 Bundle args = intent.getExtras();
John Wang62acae42009-10-08 11:20:23 -0700178
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800179 if (args == null) {
180 return;
181 }
182
183 Message msg = mServiceHandler.obtainMessage();
184 msg.arg1 = args.getInt(OPCODE);
185 switch(msg.arg1) {
186 case OP_CMD:
187 msg.obj = args.getParcelable(CMD_MSG);
188 break;
189 case OP_RESPONSE:
190 msg.obj = args;
191 /* falls through */
192 case OP_LAUNCH_APP:
193 case OP_END_SESSION:
194 case OP_BOOT_COMPLETED:
195 break;
196 default:
197 return;
198 }
199 mServiceHandler.sendMessage(msg);
200 }
201
202 @Override
203 public void onDestroy() {
204 waitForLooper();
205 mServiceLooper.quit();
206 }
207
208 @Override
209 public IBinder onBind(Intent intent) {
210 return null;
211 }
212
213 public void run() {
214 Looper.prepare();
215
216 mServiceLooper = Looper.myLooper();
217 mServiceHandler = new ServiceHandler();
218
219 Looper.loop();
220 }
221
222 /*
223 * Package api used by StkMenuActivity to indicate if its on the foreground.
224 */
225 void indicateMenuVisibility(boolean visibility) {
226 mMenuIsVisibile = visibility;
227 }
228
229 /*
230 * Package api used by StkMenuActivity to get its Menu parameter.
231 */
232 Menu getMenu() {
233 return mCurrentMenu;
234 }
235
236 /*
237 * Package api used by UI Activities and Dialogs to communicate directly
238 * with the service to deliver state information and parameters.
239 */
240 static StkAppService getInstance() {
241 return sInstance;
242 }
243
244 private void waitForLooper() {
245 while (mServiceHandler == null) {
246 synchronized (this) {
247 try {
248 wait(100);
249 } catch (InterruptedException e) {
250 }
251 }
252 }
253 }
254
255 private final class ServiceHandler extends Handler {
256 @Override
257 public void handleMessage(Message msg) {
258 int opcode = msg.arg1;
259
260 switch (opcode) {
261 case OP_LAUNCH_APP:
262 if (mMainCmd == null) {
263 // nothing todo when no SET UP MENU command didn't arrive.
264 return;
265 }
266 launchMenuActivity(null);
267 break;
268 case OP_CMD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700269 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800270 // There are two types of commands:
271 // 1. Interactive - user's response is required.
272 // 2. Informative - display a message, no interaction with the user.
273 //
Wink Saville79085fc2009-06-09 10:27:23 -0700274 // Informative commands can be handled immediately without any delay.
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800275 // Interactive commands can't override each other. So if a command
276 // is already in progress, we need to queue the next command until
277 // the user has responded or a timeout expired.
278 if (!isCmdInteractive(cmdMsg)) {
279 handleCmd(cmdMsg);
280 } else {
281 if (!mCmdInProgress) {
282 mCmdInProgress = true;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700283 handleCmd((CatCmdMessage) msg.obj);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800284 } else {
285 mCmdsQ.addLast(new DelayedCmd(OP_CMD,
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700286 (CatCmdMessage) msg.obj));
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800287 }
288 }
289 break;
290 case OP_RESPONSE:
291 if (responseNeeded) {
292 handleCmdResponse((Bundle) msg.obj);
293 }
294 // call delayed commands if needed.
295 if (mCmdsQ.size() != 0) {
296 callDelayedMsg();
297 } else {
298 mCmdInProgress = false;
299 }
300 // reset response needed state var to its original value.
301 responseNeeded = true;
302 break;
303 case OP_END_SESSION:
304 if (!mCmdInProgress) {
305 mCmdInProgress = true;
306 handleSessionEnd();
307 } else {
308 mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null));
309 }
310 break;
311 case OP_BOOT_COMPLETED:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700312 CatLog.d(this, "OP_BOOT_COMPLETED");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800313 if (mMainCmd == null) {
314 StkAppInstaller.unInstall(mContext);
315 }
316 break;
317 case OP_DELAYED_MSG:
318 handleDelayedCmd();
319 break;
320 }
321 }
322 }
323
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700324 private boolean isCmdInteractive(CatCmdMessage cmd) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800325 switch (cmd.getCmdType()) {
326 case SEND_DTMF:
327 case SEND_SMS:
328 case SEND_SS:
329 case SEND_USSD:
330 case SET_UP_IDLE_MODE_TEXT:
331 case SET_UP_MENU:
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500332 case CLOSE_CHANNEL:
333 case RECEIVE_DATA:
334 case SEND_DATA:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800335 return false;
336 }
337
338 return true;
339 }
340
341 private void handleDelayedCmd() {
342 if (mCmdsQ.size() != 0) {
343 DelayedCmd cmd = mCmdsQ.poll();
344 switch (cmd.id) {
345 case OP_CMD:
346 handleCmd(cmd.msg);
347 break;
348 case OP_END_SESSION:
349 handleSessionEnd();
350 break;
351 }
352 }
353 }
354
355 private void callDelayedMsg() {
356 Message msg = mServiceHandler.obtainMessage();
357 msg.arg1 = OP_DELAYED_MSG;
358 mServiceHandler.sendMessage(msg);
359 }
360
361 private void handleSessionEnd() {
362 mCurrentCmd = mMainCmd;
363 lastSelectedItem = null;
Wink Saville79085fc2009-06-09 10:27:23 -0700364 // In case of SET UP MENU command which removed the app, don't
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800365 // update the current menu member.
366 if (mCurrentMenu != null && mMainCmd != null) {
367 mCurrentMenu = mMainCmd.getMenu();
368 }
369 if (mMenuIsVisibile) {
370 launchMenuActivity(null);
371 }
372 if (mCmdsQ.size() != 0) {
373 callDelayedMsg();
374 } else {
375 mCmdInProgress = false;
376 }
377 // In case a launch browser command was just confirmed, launch that url.
378 if (launchBrowser) {
379 launchBrowser = false;
380 launchBrowser(mBrowserSettings);
381 }
382 }
383
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700384 private void handleCmd(CatCmdMessage cmdMsg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800385 if (cmdMsg == null) {
386 return;
387 }
388 // save local reference for state tracking.
389 mCurrentCmd = cmdMsg;
390 boolean waitForUsersResponse = true;
391
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700392 CatLog.d(this, cmdMsg.getCmdType().name());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800393 switch (cmdMsg.getCmdType()) {
394 case DISPLAY_TEXT:
395 TextMessage msg = cmdMsg.geTextMessage();
396 responseNeeded = msg.responseNeeded;
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200397 waitForUsersResponse = msg.responseNeeded;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800398 if (lastSelectedItem != null) {
399 msg.title = lastSelectedItem;
400 } else if (mMainCmd != null){
401 msg.title = mMainCmd.getMenu().title;
402 } else {
403 // TODO: get the carrier name from the SIM
404 msg.title = "";
405 }
406 launchTextDialog();
407 break;
408 case SELECT_ITEM:
409 mCurrentMenu = cmdMsg.getMenu();
410 launchMenuActivity(cmdMsg.getMenu());
411 break;
412 case SET_UP_MENU:
413 mMainCmd = mCurrentCmd;
414 mCurrentMenu = cmdMsg.getMenu();
415 if (removeMenu()) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700416 CatLog.d(this, "Uninstall App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800417 mCurrentMenu = null;
418 StkAppInstaller.unInstall(mContext);
419 } else {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700420 CatLog.d(this, "Install App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800421 StkAppInstaller.install(mContext);
422 }
423 if (mMenuIsVisibile) {
424 launchMenuActivity(null);
425 }
426 break;
427 case GET_INPUT:
428 case GET_INKEY:
429 launchInputActivity();
430 break;
431 case SET_UP_IDLE_MODE_TEXT:
432 waitForUsersResponse = false;
433 launchIdleText();
434 break;
435 case SEND_DTMF:
436 case SEND_SMS:
437 case SEND_SS:
438 case SEND_USSD:
439 waitForUsersResponse = false;
440 launchEventMessage();
441 break;
442 case LAUNCH_BROWSER:
443 launchConfirmationDialog(mCurrentCmd.geTextMessage());
444 break;
445 case SET_UP_CALL:
446 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg);
447 break;
448 case PLAY_TONE:
449 launchToneDialog();
450 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500451 case OPEN_CHANNEL:
452 launchOpenChannelDialog();
453 break;
454 case CLOSE_CHANNEL:
455 case RECEIVE_DATA:
456 case SEND_DATA:
457 TextMessage m = mCurrentCmd.geTextMessage();
458
459 if ((m != null) && (m.text == null)) {
460 switch(cmdMsg.getCmdType()) {
461 case CLOSE_CHANNEL:
462 m.text = getResources().getString(R.string.default_close_channel_msg);
463 break;
464 case RECEIVE_DATA:
465 m.text = getResources().getString(R.string.default_receive_data_msg);
466 break;
467 case SEND_DATA:
468 m.text = getResources().getString(R.string.default_send_data_msg);
469 break;
470 }
471 }
472 launchTransientEventMessage();
473 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800474 }
475
476 if (!waitForUsersResponse) {
477 if (mCmdsQ.size() != 0) {
478 callDelayedMsg();
479 } else {
480 mCmdInProgress = false;
481 }
482 }
483 }
484
485 private void handleCmdResponse(Bundle args) {
486 if (mCurrentCmd == null) {
487 return;
488 }
Alex Yakavenkad8e2ecd2012-04-20 17:10:15 -0700489 if (mStkService == null) {
490 mStkService = com.android.internal.telephony.cat.CatService.getInstance();
491 if (mStkService == null) {
492 // This should never happen (we should be responding only to a message
493 // that arrived from StkService). It has to exist by this time
494 throw new RuntimeException("mStkService is null when we need to send response");
495 }
496 }
497
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700498 CatResponseMessage resMsg = new CatResponseMessage(mCurrentCmd);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800499
500 // set result code
501 boolean helpRequired = args.getBoolean(HELP, false);
502
503 switch(args.getInt(RES_ID)) {
504 case RES_ID_MENU_SELECTION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700505 CatLog.d(this, "RES_ID_MENU_SELECTION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800506 int menuSelection = args.getInt(MENU_SELECTION);
507 switch(mCurrentCmd.getCmdType()) {
508 case SET_UP_MENU:
509 case SELECT_ITEM:
510 lastSelectedItem = getItemName(menuSelection);
511 if (helpRequired) {
512 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
513 } else {
514 resMsg.setResultCode(ResultCode.OK);
515 }
516 resMsg.setMenuSelection(menuSelection);
517 break;
518 }
519 break;
520 case RES_ID_INPUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700521 CatLog.d(this, "RES_ID_INPUT");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800522 String input = args.getString(INPUT);
Pierre Fröjd97503262010-11-08 13:59:36 +0100523 Input cmdInput = mCurrentCmd.geInput();
524 if (cmdInput != null && cmdInput.yesNo) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800525 boolean yesNoSelection = input
526 .equals(StkInputActivity.YES_STR_RESPONSE);
527 resMsg.setYesNo(yesNoSelection);
528 } else {
529 if (helpRequired) {
530 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
531 } else {
532 resMsg.setResultCode(ResultCode.OK);
533 resMsg.setInput(input);
534 }
535 }
536 break;
537 case RES_ID_CONFIRM:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700538 CatLog.d(this, "RES_ID_CONFIRM");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800539 boolean confirmed = args.getBoolean(CONFIRMATION);
540 switch (mCurrentCmd.getCmdType()) {
541 case DISPLAY_TEXT:
542 resMsg.setResultCode(confirmed ? ResultCode.OK
543 : ResultCode.UICC_SESSION_TERM_BY_USER);
544 break;
545 case LAUNCH_BROWSER:
546 resMsg.setResultCode(confirmed ? ResultCode.OK
547 : ResultCode.UICC_SESSION_TERM_BY_USER);
548 if (confirmed) {
549 launchBrowser = true;
550 mBrowserSettings = mCurrentCmd.getBrowserSettings();
551 }
552 break;
553 case SET_UP_CALL:
554 resMsg.setResultCode(ResultCode.OK);
555 resMsg.setConfirmation(confirmed);
556 if (confirmed) {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100557 launchEventMessage(mCurrentCmd.getCallSettings().callMsg);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800558 }
559 break;
560 }
561 break;
562 case RES_ID_DONE:
563 resMsg.setResultCode(ResultCode.OK);
564 break;
565 case RES_ID_BACKWARD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700566 CatLog.d(this, "RES_ID_BACKWARD");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800567 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
568 break;
569 case RES_ID_END_SESSION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700570 CatLog.d(this, "RES_ID_END_SESSION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800571 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
572 break;
573 case RES_ID_TIMEOUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700574 CatLog.d(this, "RES_ID_TIMEOUT");
Naveen Kallad5176892009-11-30 12:45:29 -0800575 // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
576 // Clear message after delay, successful) expects result code OK.
577 // If the command qualifier specifies no user response is required
578 // then send OK instead of NO_RESPONSE_FROM_USER
579 if ((mCurrentCmd.getCmdType().value() == AppInterface.CommandType.DISPLAY_TEXT
580 .value())
581 && (mCurrentCmd.geTextMessage().userClear == false)) {
582 resMsg.setResultCode(ResultCode.OK);
583 } else {
584 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
585 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800586 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500587 case RES_ID_CHOICE:
588 int choice = args.getInt(CHOICE);
589 CatLog.d(this, "User Choice=" + choice);
590 switch (choice) {
591 case YES:
592 resMsg.setResultCode(ResultCode.OK);
593 break;
594 case NO:
595 resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
596 break;
597 }
598 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800599 default:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700600 CatLog.d(this, "Unknown result id");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800601 return;
602 }
603 mStkService.onCmdResponse(resMsg);
604 }
605
606 /**
607 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
608 *
609 * @param userAction If the userAction is yes then we always return 0 otherwise
610 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
611 * then we are the foreground app and we'll return 0 as from our perspective a
612 * user action did cause. If it's false than we aren't the foreground app and
613 * FLAG_ACTIVITY_NO_USER_ACTION is returned.
Wink Saville79085fc2009-06-09 10:27:23 -0700614 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800615 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
616 */
617 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) {
618 return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ?
619 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
620 }
621
622 private void launchMenuActivity(Menu menu) {
623 Intent newIntent = new Intent(Intent.ACTION_VIEW);
624 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
625 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
626 | Intent.FLAG_ACTIVITY_CLEAR_TOP;
627 if (menu == null) {
628 // We assume this was initiated by the user pressing the tool kit icon
629 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
630
631 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
632 } else {
633 // We don't know and we'll let getFlagActivityNoUserAction decide.
634 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
635
636 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
637 }
638 newIntent.setFlags(intentFlags);
639 mContext.startActivity(newIntent);
640 }
641
642 private void launchInputActivity() {
643 Intent newIntent = new Intent(Intent.ACTION_VIEW);
644 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
645 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
646 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME);
647 newIntent.putExtra("INPUT", mCurrentCmd.geInput());
648 mContext.startActivity(newIntent);
649 }
650
651 private void launchTextDialog() {
652 Intent newIntent = new Intent(this, StkDialogActivity.class);
653 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200654 | Intent.FLAG_ACTIVITY_CLEAR_TASK
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800655 | Intent.FLAG_ACTIVITY_NO_HISTORY
656 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
657 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
658 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
659 startActivity(newIntent);
660 }
661
662 private void launchEventMessage() {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100663 launchEventMessage(mCurrentCmd.geTextMessage());
664 }
665
666 private void launchEventMessage(TextMessage msg) {
John Josephb22e7d82009-12-15 14:53:17 -0800667 if (msg == null || msg.text == null) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800668 return;
669 }
670 Toast toast = new Toast(mContext.getApplicationContext());
671 LayoutInflater inflate = (LayoutInflater) mContext
672 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
673 View v = inflate.inflate(R.layout.stk_event_msg, null);
674 TextView tv = (TextView) v
675 .findViewById(com.android.internal.R.id.message);
676 ImageView iv = (ImageView) v
677 .findViewById(com.android.internal.R.id.icon);
678 if (msg.icon != null) {
679 iv.setImageBitmap(msg.icon);
680 } else {
681 iv.setVisibility(View.GONE);
682 }
683 if (!msg.iconSelfExplanatory) {
684 tv.setText(msg.text);
685 }
686
687 toast.setView(v);
688 toast.setDuration(Toast.LENGTH_LONG);
689 toast.setGravity(Gravity.BOTTOM, 0, 0);
690 toast.show();
691 }
692
693 private void launchConfirmationDialog(TextMessage msg) {
694 msg.title = lastSelectedItem;
695 Intent newIntent = new Intent(this, StkDialogActivity.class);
696 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
697 | Intent.FLAG_ACTIVITY_NO_HISTORY
698 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
699 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
700 newIntent.putExtra("TEXT", msg);
701 startActivity(newIntent);
702 }
703
704 private void launchBrowser(BrowserSettings settings) {
705 if (settings == null) {
706 return;
707 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800708
David Brown7c03cfe2011-10-20 15:36:12 -0700709 Intent intent = new Intent(Intent.ACTION_VIEW);
710
711 Uri data;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800712 if (settings.url != null) {
dujin.cha486c1d02011-11-02 22:14:25 +0900713 CatLog.d(this, "settings.url = " + settings.url);
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900714 if ((settings.url.startsWith("http://") || (settings.url.startsWith("https://")))) {
dujin.cha486c1d02011-11-02 22:14:25 +0900715 data = Uri.parse(settings.url);
716 } else {
717 String modifiedUrl = "http://" + settings.url;
718 CatLog.d(this, "modifiedUrl = " + modifiedUrl);
719 data = Uri.parse(modifiedUrl);
720 }
David Brown7c03cfe2011-10-20 15:36:12 -0700721 } else {
722 // If no URL specified, just bring up the "home page".
723 //
724 // (Note we need to specify *something* in the intent's data field
725 // here, since if you fire off a VIEW intent with no data at all
726 // you'll get an activity chooser rather than the browser. There's
727 // no specific URI that means "use the default home page", so
728 // instead let's just explicitly bring up http://google.com.)
729 data = Uri.parse("http://google.com/");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800730 }
731 intent.setData(data);
David Brown7c03cfe2011-10-20 15:36:12 -0700732
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800733 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
734 switch (settings.mode) {
735 case USE_EXISTING_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800736 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
737 break;
738 case LAUNCH_NEW_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800739 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
740 break;
741 case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
742 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
743 break;
744 }
745 // start browser activity
746 startActivity(intent);
747 // a small delay, let the browser start, before processing the next command.
Wink Saville79085fc2009-06-09 10:27:23 -0700748 // this is good for scenarios where a related DISPLAY TEXT command is
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800749 // followed immediately.
750 try {
751 Thread.sleep(10000);
752 } catch (InterruptedException e) {}
753 }
754
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800755 private void launchIdleText() {
756 TextMessage msg = mCurrentCmd.geTextMessage();
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900757
Wink Saville046db4b2011-11-01 20:42:54 -0700758 if (msg == null) {
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900759 CatLog.d(this, "mCurrent.getTextMessage is NULL");
760 mNotificationManager.cancel(STK_NOTIFICATION_ID);
Wink Saville046db4b2011-11-01 20:42:54 -0700761 return;
762 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800763 if (msg.text == null) {
764 mNotificationManager.cancel(STK_NOTIFICATION_ID);
765 } else {
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100766 PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800767 new Intent(mContext, StkAppService.class), 0);
768
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100769 final Notification.Builder notificationBuilder = new Notification.Builder(
770 StkAppService.this);
771 notificationBuilder.setContentTitle("");
772 notificationBuilder
773 .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
774 notificationBuilder.setContentIntent(pendingIntent);
775 notificationBuilder.setOngoing(true);
776 // Set text and icon for the status bar and notification body.
777 if (!msg.iconSelfExplanatory) {
778 notificationBuilder.setContentText(msg.text);
779 }
780 if (msg.icon != null) {
781 notificationBuilder.setLargeIcon(msg.icon);
782 } else {
783 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this
784 .getResources().getSystem(),
785 com.android.internal.R.drawable.stat_notify_sim_toolkit);
786 notificationBuilder.setLargeIcon(bitmapIcon);
787 }
788
789 mNotificationManager.notify(STK_NOTIFICATION_ID, notificationBuilder.build());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800790 }
791 }
792
793 private void launchToneDialog() {
794 Intent newIntent = new Intent(this, ToneDialog.class);
795 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
796 | Intent.FLAG_ACTIVITY_NO_HISTORY
797 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
798 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
799 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
800 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
801 startActivity(newIntent);
802 }
803
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500804 private void launchOpenChannelDialog() {
805 TextMessage msg = mCurrentCmd.geTextMessage();
806 if (msg == null) {
807 CatLog.d(this, "msg is null, return here");
808 return;
809 }
810
811 msg.title = getResources().getString(R.string.stk_dialog_title);
812 if (msg.text == null) {
813 msg.text = getResources().getString(R.string.default_open_channel_msg);
814 }
815
816 final AlertDialog dialog = new AlertDialog.Builder(mContext)
817 .setIconAttribute(android.R.attr.alertDialogIcon)
818 .setTitle(msg.title)
819 .setMessage(msg.text)
820 .setCancelable(false)
821 .setPositiveButton(getResources().getString(R.string.stk_dialog_accept),
822 new DialogInterface.OnClickListener() {
823 public void onClick(DialogInterface dialog, int which) {
824 Bundle args = new Bundle();
825 args.putInt(RES_ID, RES_ID_CHOICE);
826 args.putInt(CHOICE, YES);
827 Message message = mServiceHandler.obtainMessage();
828 message.arg1 = OP_RESPONSE;
829 message.obj = args;
830 mServiceHandler.sendMessage(message);
831 }
832 })
833 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
834 new DialogInterface.OnClickListener() {
835 public void onClick(DialogInterface dialog, int which) {
836 Bundle args = new Bundle();
837 args.putInt(RES_ID, RES_ID_CHOICE);
838 args.putInt(CHOICE, NO);
839 Message message = mServiceHandler.obtainMessage();
840 message.arg1 = OP_RESPONSE;
841 message.obj = args;
842 mServiceHandler.sendMessage(message);
843 }
844 })
845 .create();
846
847 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
848 if (!mContext.getResources().getBoolean(
849 com.android.internal.R.bool.config_sf_slowBlur)) {
850 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
851 }
852
853 dialog.show();
854 }
855
856 private void launchTransientEventMessage() {
857 TextMessage msg = mCurrentCmd.geTextMessage();
858 if (msg == null) {
859 CatLog.d(this, "msg is null, return here");
860 return;
861 }
862
863 msg.title = getResources().getString(R.string.stk_dialog_title);
864
865 final AlertDialog dialog = new AlertDialog.Builder(mContext)
866 .setIconAttribute(android.R.attr.alertDialogIcon)
867 .setTitle(msg.title)
868 .setMessage(msg.text)
869 .setCancelable(false)
870 .setPositiveButton(getResources().getString(android.R.string.ok),
871 new DialogInterface.OnClickListener() {
872 public void onClick(DialogInterface dialog, int which) {
873 }
874 })
875 .create();
876
877 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
878 if (!mContext.getResources().getBoolean(
879 com.android.internal.R.bool.config_sf_slowBlur)) {
880 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
881 }
882
883 dialog.show();
884 }
885
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800886 private String getItemName(int itemId) {
887 Menu menu = mCurrentCmd.getMenu();
888 if (menu == null) {
889 return null;
890 }
891 for (Item item : menu.items) {
892 if (item.id == itemId) {
893 return item.text;
894 }
895 }
896 return null;
897 }
898
899 private boolean removeMenu() {
900 try {
Wink Saville79085fc2009-06-09 10:27:23 -0700901 if (mCurrentMenu.items.size() == 1 &&
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800902 mCurrentMenu.items.get(0) == null) {
903 return true;
904 }
905 } catch (NullPointerException e) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700906 CatLog.d(this, "Unable to get Menu's items size");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800907 return true;
908 }
909 return false;
910 }
911}