blob: 24d5b891a0a8c25c4bcce591b7b5acf95a41db37 [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;
Wink Saville94e982b2014-07-11 07:38:14 -070056import com.android.internal.telephony.uicc.IccRefreshResponse;
57import com.android.internal.telephony.uicc.IccCardStatus.CardState;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080058
59import java.util.LinkedList;
60
61/**
62 * SIM toolkit application level service. Interacts with Telephopny messages,
63 * application's launch and user input from STK UI elements.
Wink Saville79085fc2009-06-09 10:27:23 -070064 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080065 */
66public class StkAppService extends Service implements Runnable {
67
68 // members
69 private volatile Looper mServiceLooper;
70 private volatile ServiceHandler mServiceHandler;
71 private AppInterface mStkService;
72 private Context mContext = null;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -070073 private CatCmdMessage mMainCmd = null;
74 private CatCmdMessage mCurrentCmd = null;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -080075 private Menu mCurrentMenu = null;
76 private String lastSelectedItem = null;
77 private boolean mMenuIsVisibile = false;
78 private boolean responseNeeded = true;
79 private boolean mCmdInProgress = false;
80 private NotificationManager mNotificationManager = null;
81 private LinkedList<DelayedCmd> mCmdsQ = null;
82 private boolean launchBrowser = false;
83 private BrowserSettings mBrowserSettings = null;
84 static StkAppService sInstance = null;
85
86 // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when
87 // creating an intent.
88 private enum InitiatedByUserAction {
89 yes, // The action was started via a user initiated action
90 unknown, // Not known for sure if user initated the action
91 }
92
93 // constants
94 static final String OPCODE = "op";
95 static final String CMD_MSG = "cmd message";
96 static final String RES_ID = "response id";
97 static final String MENU_SELECTION = "menu selection";
98 static final String INPUT = "input";
99 static final String HELP = "help";
100 static final String CONFIRMATION = "confirm";
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500101 static final String CHOICE = "choice";
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800102
103 // operations ids for different service functionality.
104 static final int OP_CMD = 1;
105 static final int OP_RESPONSE = 2;
106 static final int OP_LAUNCH_APP = 3;
107 static final int OP_END_SESSION = 4;
108 static final int OP_BOOT_COMPLETED = 5;
109 private static final int OP_DELAYED_MSG = 6;
Wink Saville94e982b2014-07-11 07:38:14 -0700110 static final int OP_CARD_STATUS_CHANGED = 7;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800111
112 // Response ids
113 static final int RES_ID_MENU_SELECTION = 11;
114 static final int RES_ID_INPUT = 12;
115 static final int RES_ID_CONFIRM = 13;
116 static final int RES_ID_DONE = 14;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500117 static final int RES_ID_CHOICE = 15;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800118
119 static final int RES_ID_TIMEOUT = 20;
120 static final int RES_ID_BACKWARD = 21;
121 static final int RES_ID_END_SESSION = 22;
122 static final int RES_ID_EXIT = 23;
123
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500124 static final int YES = 1;
125 static final int NO = 0;
126
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800127 private static final String PACKAGE_NAME = "com.android.stk";
Wink Saville79085fc2009-06-09 10:27:23 -0700128 private static final String MENU_ACTIVITY_NAME =
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800129 PACKAGE_NAME + ".StkMenuActivity";
Wink Saville79085fc2009-06-09 10:27:23 -0700130 private static final String INPUT_ACTIVITY_NAME =
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800131 PACKAGE_NAME + ".StkInputActivity";
Wink Saville79085fc2009-06-09 10:27:23 -0700132
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800133 // Notification id used to display Idle Mode text in NotificationManager.
134 private static final int STK_NOTIFICATION_ID = 333;
Wink Saville79085fc2009-06-09 10:27:23 -0700135
136 // Inner class used for queuing telephony messages (proactive commands,
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800137 // session end) while the service is busy processing a previous message.
138 private class DelayedCmd {
139 // members
140 int id;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700141 CatCmdMessage msg;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800142
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700143 DelayedCmd(int id, CatCmdMessage msg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800144 this.id = id;
145 this.msg = msg;
146 }
147 }
148
149 @Override
150 public void onCreate() {
151 // Initialize members
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) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800163
Banavathu, Srinivas Naik87cda962012-07-17 15:32:44 +0530164 mStkService = com.android.internal.telephony.cat.CatService
165 .getInstance();
166
167 if (mStkService == null) {
168 stopSelf();
169 CatLog.d(this, " Unable to get Service handle");
duho.roae291582013-09-03 12:08:35 +0900170 StkAppInstaller.unInstall(mContext);
Banavathu, Srinivas Naik87cda962012-07-17 15:32:44 +0530171 return;
172 }
173
174 waitForLooper();
John Wang62acae42009-10-08 11:20:23 -0700175 // onStart() method can be passed a null intent
176 // TODO: replace onStart() with onStartCommand()
177 if (intent == null) {
178 return;
179 }
180
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800181 Bundle args = intent.getExtras();
John Wang62acae42009-10-08 11:20:23 -0700182
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800183 if (args == null) {
184 return;
185 }
186
187 Message msg = mServiceHandler.obtainMessage();
188 msg.arg1 = args.getInt(OPCODE);
189 switch(msg.arg1) {
190 case OP_CMD:
191 msg.obj = args.getParcelable(CMD_MSG);
192 break;
193 case OP_RESPONSE:
Wink Saville94e982b2014-07-11 07:38:14 -0700194 case OP_CARD_STATUS_CHANGED:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800195 msg.obj = args;
196 /* falls through */
197 case OP_LAUNCH_APP:
198 case OP_END_SESSION:
199 case OP_BOOT_COMPLETED:
200 break;
201 default:
202 return;
203 }
204 mServiceHandler.sendMessage(msg);
205 }
206
207 @Override
208 public void onDestroy() {
209 waitForLooper();
210 mServiceLooper.quit();
211 }
212
213 @Override
214 public IBinder onBind(Intent intent) {
215 return null;
216 }
217
218 public void run() {
219 Looper.prepare();
220
221 mServiceLooper = Looper.myLooper();
222 mServiceHandler = new ServiceHandler();
223
224 Looper.loop();
225 }
226
227 /*
228 * Package api used by StkMenuActivity to indicate if its on the foreground.
229 */
230 void indicateMenuVisibility(boolean visibility) {
231 mMenuIsVisibile = visibility;
232 }
233
234 /*
235 * Package api used by StkMenuActivity to get its Menu parameter.
236 */
237 Menu getMenu() {
238 return mCurrentMenu;
239 }
240
241 /*
242 * Package api used by UI Activities and Dialogs to communicate directly
243 * with the service to deliver state information and parameters.
244 */
245 static StkAppService getInstance() {
246 return sInstance;
247 }
248
249 private void waitForLooper() {
250 while (mServiceHandler == null) {
251 synchronized (this) {
252 try {
253 wait(100);
254 } catch (InterruptedException e) {
255 }
256 }
257 }
258 }
259
260 private final class ServiceHandler extends Handler {
261 @Override
262 public void handleMessage(Message msg) {
263 int opcode = msg.arg1;
264
265 switch (opcode) {
266 case OP_LAUNCH_APP:
267 if (mMainCmd == null) {
268 // nothing todo when no SET UP MENU command didn't arrive.
269 return;
270 }
271 launchMenuActivity(null);
272 break;
273 case OP_CMD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700274 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800275 // There are two types of commands:
276 // 1. Interactive - user's response is required.
277 // 2. Informative - display a message, no interaction with the user.
278 //
Wink Saville79085fc2009-06-09 10:27:23 -0700279 // Informative commands can be handled immediately without any delay.
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800280 // Interactive commands can't override each other. So if a command
281 // is already in progress, we need to queue the next command until
282 // the user has responded or a timeout expired.
283 if (!isCmdInteractive(cmdMsg)) {
284 handleCmd(cmdMsg);
285 } else {
286 if (!mCmdInProgress) {
287 mCmdInProgress = true;
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700288 handleCmd((CatCmdMessage) msg.obj);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800289 } else {
290 mCmdsQ.addLast(new DelayedCmd(OP_CMD,
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700291 (CatCmdMessage) msg.obj));
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800292 }
293 }
294 break;
295 case OP_RESPONSE:
296 if (responseNeeded) {
297 handleCmdResponse((Bundle) msg.obj);
298 }
299 // call delayed commands if needed.
300 if (mCmdsQ.size() != 0) {
301 callDelayedMsg();
302 } else {
303 mCmdInProgress = false;
304 }
305 // reset response needed state var to its original value.
306 responseNeeded = true;
307 break;
308 case OP_END_SESSION:
309 if (!mCmdInProgress) {
310 mCmdInProgress = true;
311 handleSessionEnd();
312 } else {
313 mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null));
314 }
315 break;
316 case OP_BOOT_COMPLETED:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700317 CatLog.d(this, "OP_BOOT_COMPLETED");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800318 if (mMainCmd == null) {
319 StkAppInstaller.unInstall(mContext);
320 }
321 break;
322 case OP_DELAYED_MSG:
323 handleDelayedCmd();
324 break;
Wink Saville94e982b2014-07-11 07:38:14 -0700325 case OP_CARD_STATUS_CHANGED:
326 CatLog.d(this, "Card/Icc Status change received");
327 handleCardStatusChangeAndIccRefresh((Bundle) msg.obj);
328 break;
329 }
330 }
331
332 private void handleCardStatusChangeAndIccRefresh(Bundle args) {
333 boolean cardStatus = args.getBoolean(AppInterface.CARD_STATUS);
334
335 CatLog.d(this, "CardStatus: " + cardStatus);
336 if (cardStatus == false) {
337 CatLog.d(this, "CARD is ABSENT");
338 // Uninstall STKAPP, Clear Idle text, Stop StkAppService
339 StkAppInstaller.unInstall(mContext);
340 mNotificationManager.cancel(STK_NOTIFICATION_ID);
341 stopSelf();
342 } else {
343 IccRefreshResponse state = new IccRefreshResponse();
344 state.refreshResult = args.getInt(AppInterface.REFRESH_RESULT);
345
346 CatLog.d(this, "Icc Refresh Result: "+ state.refreshResult);
347 if ((state.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) ||
348 (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET)) {
349 // Clear Idle Text
350 mNotificationManager.cancel(STK_NOTIFICATION_ID);
351 }
352
353 if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
354 // Uninstall STkmenu
355 StkAppInstaller.unInstall(mContext);
356 mCurrentMenu = null;
357 mMainCmd = null;
358 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800359 }
360 }
361 }
362
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700363 private boolean isCmdInteractive(CatCmdMessage cmd) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800364 switch (cmd.getCmdType()) {
365 case SEND_DTMF:
366 case SEND_SMS:
367 case SEND_SS:
368 case SEND_USSD:
369 case SET_UP_IDLE_MODE_TEXT:
370 case SET_UP_MENU:
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500371 case CLOSE_CHANNEL:
372 case RECEIVE_DATA:
373 case SEND_DATA:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800374 return false;
375 }
376
377 return true;
378 }
379
380 private void handleDelayedCmd() {
381 if (mCmdsQ.size() != 0) {
382 DelayedCmd cmd = mCmdsQ.poll();
383 switch (cmd.id) {
384 case OP_CMD:
385 handleCmd(cmd.msg);
386 break;
387 case OP_END_SESSION:
388 handleSessionEnd();
389 break;
390 }
391 }
392 }
393
394 private void callDelayedMsg() {
395 Message msg = mServiceHandler.obtainMessage();
396 msg.arg1 = OP_DELAYED_MSG;
397 mServiceHandler.sendMessage(msg);
398 }
399
400 private void handleSessionEnd() {
401 mCurrentCmd = mMainCmd;
402 lastSelectedItem = null;
Wink Saville79085fc2009-06-09 10:27:23 -0700403 // In case of SET UP MENU command which removed the app, don't
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800404 // update the current menu member.
405 if (mCurrentMenu != null && mMainCmd != null) {
406 mCurrentMenu = mMainCmd.getMenu();
407 }
408 if (mMenuIsVisibile) {
409 launchMenuActivity(null);
410 }
411 if (mCmdsQ.size() != 0) {
412 callDelayedMsg();
413 } else {
414 mCmdInProgress = false;
415 }
416 // In case a launch browser command was just confirmed, launch that url.
417 if (launchBrowser) {
418 launchBrowser = false;
419 launchBrowser(mBrowserSettings);
420 }
421 }
422
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700423 private void handleCmd(CatCmdMessage cmdMsg) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800424 if (cmdMsg == null) {
425 return;
426 }
427 // save local reference for state tracking.
428 mCurrentCmd = cmdMsg;
429 boolean waitForUsersResponse = true;
430
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700431 CatLog.d(this, cmdMsg.getCmdType().name());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800432 switch (cmdMsg.getCmdType()) {
433 case DISPLAY_TEXT:
434 TextMessage msg = cmdMsg.geTextMessage();
435 responseNeeded = msg.responseNeeded;
Jeevaka Badrappan854a25c2012-12-01 16:32:03 +0200436 waitForUsersResponse = msg.responseNeeded;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800437 if (lastSelectedItem != null) {
438 msg.title = lastSelectedItem;
439 } else if (mMainCmd != null){
440 msg.title = mMainCmd.getMenu().title;
441 } else {
442 // TODO: get the carrier name from the SIM
443 msg.title = "";
444 }
445 launchTextDialog();
446 break;
447 case SELECT_ITEM:
448 mCurrentMenu = cmdMsg.getMenu();
449 launchMenuActivity(cmdMsg.getMenu());
450 break;
451 case SET_UP_MENU:
452 mMainCmd = mCurrentCmd;
453 mCurrentMenu = cmdMsg.getMenu();
454 if (removeMenu()) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700455 CatLog.d(this, "Uninstall App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800456 mCurrentMenu = null;
457 StkAppInstaller.unInstall(mContext);
458 } else {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700459 CatLog.d(this, "Install App");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800460 StkAppInstaller.install(mContext);
461 }
462 if (mMenuIsVisibile) {
463 launchMenuActivity(null);
464 }
465 break;
466 case GET_INPUT:
467 case GET_INKEY:
468 launchInputActivity();
469 break;
470 case SET_UP_IDLE_MODE_TEXT:
471 waitForUsersResponse = false;
472 launchIdleText();
473 break;
474 case SEND_DTMF:
475 case SEND_SMS:
476 case SEND_SS:
477 case SEND_USSD:
478 waitForUsersResponse = false;
479 launchEventMessage();
480 break;
481 case LAUNCH_BROWSER:
482 launchConfirmationDialog(mCurrentCmd.geTextMessage());
483 break;
484 case SET_UP_CALL:
485 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg);
486 break;
487 case PLAY_TONE:
488 launchToneDialog();
489 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500490 case OPEN_CHANNEL:
491 launchOpenChannelDialog();
492 break;
493 case CLOSE_CHANNEL:
494 case RECEIVE_DATA:
495 case SEND_DATA:
496 TextMessage m = mCurrentCmd.geTextMessage();
497
498 if ((m != null) && (m.text == null)) {
499 switch(cmdMsg.getCmdType()) {
500 case CLOSE_CHANNEL:
501 m.text = getResources().getString(R.string.default_close_channel_msg);
502 break;
503 case RECEIVE_DATA:
504 m.text = getResources().getString(R.string.default_receive_data_msg);
505 break;
506 case SEND_DATA:
507 m.text = getResources().getString(R.string.default_send_data_msg);
508 break;
509 }
510 }
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700511 /*
512 * Display indication in the form of a toast to the user if required.
513 */
514 launchEventMessage();
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500515 break;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800516 }
517
518 if (!waitForUsersResponse) {
519 if (mCmdsQ.size() != 0) {
520 callDelayedMsg();
521 } else {
522 mCmdInProgress = false;
523 }
524 }
525 }
526
527 private void handleCmdResponse(Bundle args) {
528 if (mCurrentCmd == null) {
529 return;
530 }
Alex Yakavenkad8e2ecd2012-04-20 17:10:15 -0700531 if (mStkService == null) {
532 mStkService = com.android.internal.telephony.cat.CatService.getInstance();
533 if (mStkService == null) {
534 // This should never happen (we should be responding only to a message
535 // that arrived from StkService). It has to exist by this time
536 throw new RuntimeException("mStkService is null when we need to send response");
537 }
538 }
539
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700540 CatResponseMessage resMsg = new CatResponseMessage(mCurrentCmd);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800541
542 // set result code
543 boolean helpRequired = args.getBoolean(HELP, false);
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700544 boolean confirmed = false;
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800545
546 switch(args.getInt(RES_ID)) {
547 case RES_ID_MENU_SELECTION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700548 CatLog.d(this, "RES_ID_MENU_SELECTION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800549 int menuSelection = args.getInt(MENU_SELECTION);
550 switch(mCurrentCmd.getCmdType()) {
551 case SET_UP_MENU:
552 case SELECT_ITEM:
553 lastSelectedItem = getItemName(menuSelection);
554 if (helpRequired) {
555 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
556 } else {
557 resMsg.setResultCode(ResultCode.OK);
558 }
559 resMsg.setMenuSelection(menuSelection);
560 break;
561 }
562 break;
563 case RES_ID_INPUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700564 CatLog.d(this, "RES_ID_INPUT");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800565 String input = args.getString(INPUT);
Pierre Fröjd97503262010-11-08 13:59:36 +0100566 Input cmdInput = mCurrentCmd.geInput();
567 if (cmdInput != null && cmdInput.yesNo) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800568 boolean yesNoSelection = input
569 .equals(StkInputActivity.YES_STR_RESPONSE);
570 resMsg.setYesNo(yesNoSelection);
571 } else {
572 if (helpRequired) {
573 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
574 } else {
575 resMsg.setResultCode(ResultCode.OK);
576 resMsg.setInput(input);
577 }
578 }
579 break;
580 case RES_ID_CONFIRM:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700581 CatLog.d(this, "RES_ID_CONFIRM");
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700582 confirmed = args.getBoolean(CONFIRMATION);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800583 switch (mCurrentCmd.getCmdType()) {
584 case DISPLAY_TEXT:
585 resMsg.setResultCode(confirmed ? ResultCode.OK
586 : ResultCode.UICC_SESSION_TERM_BY_USER);
587 break;
588 case LAUNCH_BROWSER:
589 resMsg.setResultCode(confirmed ? ResultCode.OK
590 : ResultCode.UICC_SESSION_TERM_BY_USER);
591 if (confirmed) {
592 launchBrowser = true;
593 mBrowserSettings = mCurrentCmd.getBrowserSettings();
594 }
595 break;
596 case SET_UP_CALL:
597 resMsg.setResultCode(ResultCode.OK);
598 resMsg.setConfirmation(confirmed);
599 if (confirmed) {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100600 launchEventMessage(mCurrentCmd.getCallSettings().callMsg);
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800601 }
602 break;
603 }
604 break;
605 case RES_ID_DONE:
606 resMsg.setResultCode(ResultCode.OK);
607 break;
608 case RES_ID_BACKWARD:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700609 CatLog.d(this, "RES_ID_BACKWARD");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800610 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
611 break;
612 case RES_ID_END_SESSION:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700613 CatLog.d(this, "RES_ID_END_SESSION");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800614 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
615 break;
616 case RES_ID_TIMEOUT:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700617 CatLog.d(this, "RES_ID_TIMEOUT");
Naveen Kallad5176892009-11-30 12:45:29 -0800618 // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
619 // Clear message after delay, successful) expects result code OK.
620 // If the command qualifier specifies no user response is required
621 // then send OK instead of NO_RESPONSE_FROM_USER
622 if ((mCurrentCmd.getCmdType().value() == AppInterface.CommandType.DISPLAY_TEXT
623 .value())
624 && (mCurrentCmd.geTextMessage().userClear == false)) {
625 resMsg.setResultCode(ResultCode.OK);
626 } else {
627 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
628 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800629 break;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500630 case RES_ID_CHOICE:
631 int choice = args.getInt(CHOICE);
632 CatLog.d(this, "User Choice=" + choice);
633 switch (choice) {
634 case YES:
635 resMsg.setResultCode(ResultCode.OK);
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700636 confirmed = true;
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500637 break;
638 case NO:
639 resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
640 break;
641 }
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700642
643 if (mCurrentCmd.getCmdType().value() == AppInterface.CommandType.OPEN_CHANNEL
644 .value()) {
645 resMsg.setConfirmation(confirmed);
646 }
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500647 break;
Siddartha Pothapragadacacf7b82012-04-16 19:42:38 -0700648
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800649 default:
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700650 CatLog.d(this, "Unknown result id");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800651 return;
652 }
653 mStkService.onCmdResponse(resMsg);
654 }
655
656 /**
657 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
658 *
659 * @param userAction If the userAction is yes then we always return 0 otherwise
660 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
661 * then we are the foreground app and we'll return 0 as from our perspective a
662 * user action did cause. If it's false than we aren't the foreground app and
663 * FLAG_ACTIVITY_NO_USER_ACTION is returned.
Wink Saville79085fc2009-06-09 10:27:23 -0700664 *
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800665 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
666 */
667 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) {
668 return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ?
669 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
670 }
671
672 private void launchMenuActivity(Menu menu) {
673 Intent newIntent = new Intent(Intent.ACTION_VIEW);
674 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
675 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
676 | Intent.FLAG_ACTIVITY_CLEAR_TOP;
677 if (menu == null) {
678 // We assume this was initiated by the user pressing the tool kit icon
679 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
680
681 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
682 } else {
683 // We don't know and we'll let getFlagActivityNoUserAction decide.
684 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
685
686 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
687 }
688 newIntent.setFlags(intentFlags);
689 mContext.startActivity(newIntent);
690 }
691
692 private void launchInputActivity() {
693 Intent newIntent = new Intent(Intent.ACTION_VIEW);
694 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
695 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
696 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME);
697 newIntent.putExtra("INPUT", mCurrentCmd.geInput());
698 mContext.startActivity(newIntent);
699 }
700
701 private void launchTextDialog() {
702 Intent newIntent = new Intent(this, StkDialogActivity.class);
703 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
duho.ro76abd4b2013-09-22 08:36:35 +0900704 | Intent.FLAG_ACTIVITY_CLEAR_TOP
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800705 | Intent.FLAG_ACTIVITY_NO_HISTORY
706 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
707 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
708 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
709 startActivity(newIntent);
710 }
711
712 private void launchEventMessage() {
Johan Hellman3aec01c2011-02-10 10:15:28 +0100713 launchEventMessage(mCurrentCmd.geTextMessage());
714 }
715
716 private void launchEventMessage(TextMessage msg) {
John Josephb22e7d82009-12-15 14:53:17 -0800717 if (msg == null || msg.text == null) {
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800718 return;
719 }
720 Toast toast = new Toast(mContext.getApplicationContext());
721 LayoutInflater inflate = (LayoutInflater) mContext
722 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
723 View v = inflate.inflate(R.layout.stk_event_msg, null);
724 TextView tv = (TextView) v
725 .findViewById(com.android.internal.R.id.message);
726 ImageView iv = (ImageView) v
727 .findViewById(com.android.internal.R.id.icon);
728 if (msg.icon != null) {
729 iv.setImageBitmap(msg.icon);
730 } else {
731 iv.setVisibility(View.GONE);
732 }
733 if (!msg.iconSelfExplanatory) {
734 tv.setText(msg.text);
735 }
736
737 toast.setView(v);
738 toast.setDuration(Toast.LENGTH_LONG);
739 toast.setGravity(Gravity.BOTTOM, 0, 0);
740 toast.show();
741 }
742
743 private void launchConfirmationDialog(TextMessage msg) {
744 msg.title = lastSelectedItem;
745 Intent newIntent = new Intent(this, StkDialogActivity.class);
746 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
747 | Intent.FLAG_ACTIVITY_NO_HISTORY
748 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
749 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
750 newIntent.putExtra("TEXT", msg);
751 startActivity(newIntent);
752 }
753
754 private void launchBrowser(BrowserSettings settings) {
755 if (settings == null) {
756 return;
757 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800758
Abhishek Adappa840c82f2013-02-26 10:19:49 -0800759 Intent intent = null;
760 Uri data = null;
David Brown7c03cfe2011-10-20 15:36:12 -0700761
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800762 if (settings.url != null) {
dujin.cha486c1d02011-11-02 22:14:25 +0900763 CatLog.d(this, "settings.url = " + settings.url);
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900764 if ((settings.url.startsWith("http://") || (settings.url.startsWith("https://")))) {
dujin.cha486c1d02011-11-02 22:14:25 +0900765 data = Uri.parse(settings.url);
766 } else {
767 String modifiedUrl = "http://" + settings.url;
768 CatLog.d(this, "modifiedUrl = " + modifiedUrl);
769 data = Uri.parse(modifiedUrl);
770 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800771 }
Abhishek Adappa840c82f2013-02-26 10:19:49 -0800772 if (data != null) {
773 intent = new Intent(Intent.ACTION_VIEW);
774 intent.setData(data);
775 } else {
776 // if the command did not contain a URL,
777 // launch the browser to the default homepage.
778 CatLog.d(this, "launch browser with default URL ");
779 intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
780 Intent.CATEGORY_APP_BROWSER);
781 }
David Brown7c03cfe2011-10-20 15:36:12 -0700782
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800783 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
784 switch (settings.mode) {
785 case USE_EXISTING_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800786 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
787 break;
788 case LAUNCH_NEW_BROWSER:
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800789 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
790 break;
791 case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
792 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
793 break;
794 }
795 // start browser activity
796 startActivity(intent);
797 // a small delay, let the browser start, before processing the next command.
Wink Saville79085fc2009-06-09 10:27:23 -0700798 // this is good for scenarios where a related DISPLAY TEXT command is
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800799 // followed immediately.
800 try {
801 Thread.sleep(10000);
802 } catch (InterruptedException e) {}
803 }
804
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800805 private void launchIdleText() {
806 TextMessage msg = mCurrentCmd.geTextMessage();
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900807
Wink Saville046db4b2011-11-01 20:42:54 -0700808 if (msg == null) {
dujin.cha2a0eb2a2011-11-11 15:03:57 +0900809 CatLog.d(this, "mCurrent.getTextMessage is NULL");
810 mNotificationManager.cancel(STK_NOTIFICATION_ID);
Wink Saville046db4b2011-11-01 20:42:54 -0700811 return;
812 }
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800813 if (msg.text == null) {
814 mNotificationManager.cancel(STK_NOTIFICATION_ID);
815 } else {
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100816 PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800817 new Intent(mContext, StkAppService.class), 0);
818
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100819 final Notification.Builder notificationBuilder = new Notification.Builder(
820 StkAppService.this);
Christopher Posselwhite2ad2ab72013-05-27 07:51:06 +0200821 if (mMainCmd != null && mMainCmd.getMenu() != null) {
822 notificationBuilder.setContentTitle(mMainCmd.getMenu().title);
823 } else {
824 notificationBuilder.setContentTitle("");
825 }
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100826 notificationBuilder
827 .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
828 notificationBuilder.setContentIntent(pendingIntent);
829 notificationBuilder.setOngoing(true);
830 // Set text and icon for the status bar and notification body.
831 if (!msg.iconSelfExplanatory) {
832 notificationBuilder.setContentText(msg.text);
Christopher Posselwhite2ad2ab72013-05-27 07:51:06 +0200833 notificationBuilder.setTicker(msg.text);
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100834 }
835 if (msg.icon != null) {
836 notificationBuilder.setLargeIcon(msg.icon);
837 } else {
838 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this
839 .getResources().getSystem(),
840 com.android.internal.R.drawable.stat_notify_sim_toolkit);
841 notificationBuilder.setLargeIcon(bitmapIcon);
842 }
Selim Cinek62eb3fe2014-08-27 17:52:23 +0200843 notificationBuilder.setColor(mContext.getResources().getColor(
844 com.android.internal.R.color.system_notification_accent_color));
Alex Khlivnuik50e0a1d2013-01-25 12:50:48 +0100845 mNotificationManager.notify(STK_NOTIFICATION_ID, notificationBuilder.build());
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800846 }
847 }
848
849 private void launchToneDialog() {
850 Intent newIntent = new Intent(this, ToneDialog.class);
851 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
852 | Intent.FLAG_ACTIVITY_NO_HISTORY
853 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
854 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
855 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
856 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
857 startActivity(newIntent);
858 }
859
Kazuhiro Ondo764167c2011-10-21 16:05:05 -0500860 private void launchOpenChannelDialog() {
861 TextMessage msg = mCurrentCmd.geTextMessage();
862 if (msg == null) {
863 CatLog.d(this, "msg is null, return here");
864 return;
865 }
866
867 msg.title = getResources().getString(R.string.stk_dialog_title);
868 if (msg.text == null) {
869 msg.text = getResources().getString(R.string.default_open_channel_msg);
870 }
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(R.string.stk_dialog_accept),
878 new DialogInterface.OnClickListener() {
879 public void onClick(DialogInterface dialog, int which) {
880 Bundle args = new Bundle();
881 args.putInt(RES_ID, RES_ID_CHOICE);
882 args.putInt(CHOICE, YES);
883 Message message = mServiceHandler.obtainMessage();
884 message.arg1 = OP_RESPONSE;
885 message.obj = args;
886 mServiceHandler.sendMessage(message);
887 }
888 })
889 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
890 new DialogInterface.OnClickListener() {
891 public void onClick(DialogInterface dialog, int which) {
892 Bundle args = new Bundle();
893 args.putInt(RES_ID, RES_ID_CHOICE);
894 args.putInt(CHOICE, NO);
895 Message message = mServiceHandler.obtainMessage();
896 message.arg1 = OP_RESPONSE;
897 message.obj = args;
898 mServiceHandler.sendMessage(message);
899 }
900 })
901 .create();
902
903 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
904 if (!mContext.getResources().getBoolean(
905 com.android.internal.R.bool.config_sf_slowBlur)) {
906 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
907 }
908
909 dialog.show();
910 }
911
912 private void launchTransientEventMessage() {
913 TextMessage msg = mCurrentCmd.geTextMessage();
914 if (msg == null) {
915 CatLog.d(this, "msg is null, return here");
916 return;
917 }
918
919 msg.title = getResources().getString(R.string.stk_dialog_title);
920
921 final AlertDialog dialog = new AlertDialog.Builder(mContext)
922 .setIconAttribute(android.R.attr.alertDialogIcon)
923 .setTitle(msg.title)
924 .setMessage(msg.text)
925 .setCancelable(false)
926 .setPositiveButton(getResources().getString(android.R.string.ok),
927 new DialogInterface.OnClickListener() {
928 public void onClick(DialogInterface dialog, int which) {
929 }
930 })
931 .create();
932
933 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
934 if (!mContext.getResources().getBoolean(
935 com.android.internal.R.bool.config_sf_slowBlur)) {
936 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
937 }
938
939 dialog.show();
940 }
941
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800942 private String getItemName(int itemId) {
943 Menu menu = mCurrentCmd.getMenu();
944 if (menu == null) {
945 return null;
946 }
947 for (Item item : menu.items) {
948 if (item.id == itemId) {
949 return item.text;
950 }
951 }
952 return null;
953 }
954
955 private boolean removeMenu() {
956 try {
Wink Saville79085fc2009-06-09 10:27:23 -0700957 if (mCurrentMenu.items.size() == 1 &&
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800958 mCurrentMenu.items.get(0) == null) {
959 return true;
960 }
961 } catch (NullPointerException e) {
Alex Yakavenkad41f1d92010-07-12 14:13:13 -0700962 CatLog.d(this, "Unable to get Menu's items size");
The Android Open Source Project9d9730a2009-03-03 19:32:37 -0800963 return true;
964 }
965 return false;
966 }
967}