StkAppService.java revision c46d8a06f810a3b5b570e43e7e0ed8b36efd23e5
1/* 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 19import android.app.Notification; 20import android.app.NotificationManager; 21import android.app.PendingIntent; 22import android.app.Service; 23import android.content.Context; 24import android.content.Intent; 25import android.net.Uri; 26import android.os.Bundle; 27import android.os.Handler; 28import android.os.IBinder; 29import android.os.Looper; 30import android.os.Message; 31import android.view.Gravity; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.widget.ImageView; 35import android.widget.RemoteViews; 36import android.widget.TextView; 37import android.widget.Toast; 38 39import com.android.internal.telephony.gsm.stk.AppInterface; 40import com.android.internal.telephony.gsm.stk.Menu; 41import com.android.internal.telephony.gsm.stk.Item; 42import com.android.internal.telephony.gsm.stk.ResultCode; 43import com.android.internal.telephony.gsm.stk.StkCmdMessage; 44import com.android.internal.telephony.gsm.stk.StkCmdMessage.BrowserSettings; 45import com.android.internal.telephony.gsm.stk.StkLog; 46import com.android.internal.telephony.gsm.stk.StkResponseMessage; 47import com.android.internal.telephony.gsm.stk.TextMessage; 48 49import java.util.LinkedList; 50 51/** 52 * SIM toolkit application level service. Interacts with Telephopny messages, 53 * application's launch and user input from STK UI elements. 54 * 55 */ 56public class StkAppService extends Service implements Runnable { 57 58 // members 59 private volatile Looper mServiceLooper; 60 private volatile ServiceHandler mServiceHandler; 61 private AppInterface mStkService; 62 private Context mContext = null; 63 private StkCmdMessage mMainCmd = null; 64 private StkCmdMessage mCurrentCmd = null; 65 private Menu mCurrentMenu = null; 66 private String lastSelectedItem = null; 67 private boolean mMenuIsVisibile = false; 68 private boolean responseNeeded = true; 69 private boolean mCmdInProgress = false; 70 private NotificationManager mNotificationManager = null; 71 private LinkedList<DelayedCmd> mCmdsQ = null; 72 private boolean launchBrowser = false; 73 private BrowserSettings mBrowserSettings = null; 74 static StkAppService sInstance = null; 75 76 // constants 77 static final String OPCODE = "op"; 78 static final String CMD_MSG = "cmd message"; 79 static final String RES_ID = "response id"; 80 static final String MENU_SELECTION = "menu selection"; 81 static final String INPUT = "input"; 82 static final String HELP = "help"; 83 static final String CONFIRMATION = "confirm"; 84 85 // operations ids for different service functionality. 86 static final int OP_CMD = 1; 87 static final int OP_RESPONSE = 2; 88 static final int OP_LAUNCH_APP = 3; 89 static final int OP_END_SESSION = 4; 90 static final int OP_BOOT_COMPLETED = 5; 91 private static final int OP_DELAYED_MSG = 6; 92 93 // Response ids 94 static final int RES_ID_MENU_SELECTION = 11; 95 static final int RES_ID_INPUT = 12; 96 static final int RES_ID_CONFIRM = 13; 97 static final int RES_ID_DONE = 14; 98 99 static final int RES_ID_TIMEOUT = 20; 100 static final int RES_ID_BACKWARD = 21; 101 static final int RES_ID_END_SESSION = 22; 102 static final int RES_ID_EXIT = 23; 103 104 private static final String TAG = "StkAppService"; 105 106 private static final String PACKAGE_NAME = "com.android.stk"; 107 private static final String MENU_ACTIVITY_NAME = 108 PACKAGE_NAME + ".StkMenuActivity"; 109 private static final String INPUT_ACTIVITY_NAME = 110 PACKAGE_NAME + ".StkInputActivity"; 111 112 // Notification id used to display Idle Mode text in NotificationManager. 113 private static final int STK_NOTIFICATION_ID = 333; 114 115 // Inner class used for queuing telephony messages (proactive commands, 116 // session end) while the service is busy processing a previous message. 117 private class DelayedCmd { 118 // members 119 int id; 120 StkCmdMessage msg; 121 122 DelayedCmd(int id, StkCmdMessage msg) { 123 this.id = id; 124 this.msg = msg; 125 } 126 } 127 128 @Override 129 public void onCreate() { 130 // Initialize members 131 mStkService = com.android.internal.telephony.gsm.stk.Service 132 .getInstance(); 133 if (mStkService == null) { 134 StkLog.d(this, " Unable to get Service handle"); 135 return; 136 } 137 138 mCmdsQ = new LinkedList<DelayedCmd>(); 139 Thread serviceThread = new Thread(null, this, "Stk App Service"); 140 serviceThread.start(); 141 mContext = getBaseContext(); 142 mNotificationManager = (NotificationManager) mContext 143 .getSystemService(Context.NOTIFICATION_SERVICE); 144 sInstance = this; 145 } 146 147 @Override 148 public void onStart(Intent intent, int startId) { 149 waitForLooper(); 150 151 Bundle args = intent.getExtras(); 152 if (args == null) { 153 return; 154 } 155 156 Message msg = mServiceHandler.obtainMessage(); 157 msg.arg1 = args.getInt(OPCODE); 158 switch(msg.arg1) { 159 case OP_CMD: 160 msg.obj = args.getParcelable(CMD_MSG); 161 break; 162 case OP_RESPONSE: 163 msg.obj = args; 164 /* falls through */ 165 case OP_LAUNCH_APP: 166 case OP_END_SESSION: 167 case OP_BOOT_COMPLETED: 168 break; 169 default: 170 return; 171 } 172 mServiceHandler.sendMessage(msg); 173 } 174 175 @Override 176 public void onDestroy() { 177 waitForLooper(); 178 mServiceLooper.quit(); 179 } 180 181 @Override 182 public IBinder onBind(Intent intent) { 183 return null; 184 } 185 186 public void run() { 187 Looper.prepare(); 188 189 mServiceLooper = Looper.myLooper(); 190 mServiceHandler = new ServiceHandler(); 191 192 Looper.loop(); 193 } 194 195 /* 196 * Package api used by StkMenuActivity to indicate if its on the foreground. 197 */ 198 void indicateMenuVisibility(boolean visibilty) { 199 mMenuIsVisibile = visibilty; 200 } 201 202 /* 203 * Package api used by StkMenuActivity to get its Menu parameter. 204 */ 205 Menu getMenu() { 206 return mCurrentMenu; 207 } 208 209 /* 210 * Package api used by UI Activities and Dialogs to communicate directly 211 * with the service to deliver state information and parameters. 212 */ 213 static StkAppService getInstance() { 214 return sInstance; 215 } 216 217 private void waitForLooper() { 218 while (mServiceHandler == null) { 219 synchronized (this) { 220 try { 221 wait(100); 222 } catch (InterruptedException e) { 223 } 224 } 225 } 226 } 227 228 private final class ServiceHandler extends Handler { 229 @Override 230 public void handleMessage(Message msg) { 231 int opcode = msg.arg1; 232 233 switch (opcode) { 234 case OP_LAUNCH_APP: 235 if (mMainCmd == null) { 236 // nothing todo when no SET UP MENU command didn't arrive. 237 return; 238 } 239 launchMenuActivity(null); 240 break; 241 case OP_CMD: 242 StkCmdMessage cmdMsg = (StkCmdMessage) msg.obj; 243 // There are two types of commands: 244 // 1. Interactive - user's response is required. 245 // 2. Informative - display a message, no interaction with the user. 246 // 247 // Informative commands can be handled immediately without any delay. 248 // Interactive commands can't override each other. So if a command 249 // is already in progress, we need to queue the next command until 250 // the user has responded or a timeout expired. 251 if (!isCmdInteractive(cmdMsg)) { 252 handleCmd(cmdMsg); 253 } else { 254 if (!mCmdInProgress) { 255 mCmdInProgress = true; 256 handleCmd((StkCmdMessage) msg.obj); 257 } else { 258 mCmdsQ.addLast(new DelayedCmd(OP_CMD, 259 (StkCmdMessage) msg.obj)); 260 } 261 } 262 break; 263 case OP_RESPONSE: 264 if (responseNeeded) { 265 handleCmdResponse((Bundle) msg.obj); 266 } 267 // call delayed commands if needed. 268 if (mCmdsQ.size() != 0) { 269 callDelayedMsg(); 270 } else { 271 mCmdInProgress = false; 272 } 273 // reset response needed state var to its original value. 274 responseNeeded = true; 275 break; 276 case OP_END_SESSION: 277 if (!mCmdInProgress) { 278 mCmdInProgress = true; 279 handleSessionEnd(); 280 } else { 281 mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null)); 282 } 283 break; 284 case OP_BOOT_COMPLETED: 285 StkLog.d(this, "OP_BOOT_COMPLETED"); 286 if (mMainCmd == null) { 287 StkAppInstaller.unInstall(mContext); 288 } 289 break; 290 case OP_DELAYED_MSG: 291 handleDelayedCmd(); 292 break; 293 } 294 } 295 } 296 297 private boolean isCmdInteractive(StkCmdMessage cmd) { 298 switch (cmd.getCmdType()) { 299 case SEND_DTMF: 300 case SEND_SMS: 301 case SEND_SS: 302 case SEND_USSD: 303 case SET_UP_IDLE_MODE_TEXT: 304 case SET_UP_MENU: 305 return false; 306 } 307 308 return true; 309 } 310 311 private void handleDelayedCmd() { 312 if (mCmdsQ.size() != 0) { 313 DelayedCmd cmd = mCmdsQ.poll(); 314 switch (cmd.id) { 315 case OP_CMD: 316 handleCmd(cmd.msg); 317 break; 318 case OP_END_SESSION: 319 handleSessionEnd(); 320 break; 321 } 322 } 323 } 324 325 private void callDelayedMsg() { 326 Message msg = mServiceHandler.obtainMessage(); 327 msg.arg1 = OP_DELAYED_MSG; 328 mServiceHandler.sendMessage(msg); 329 } 330 331 private void handleSessionEnd() { 332 mCurrentCmd = mMainCmd; 333 lastSelectedItem = null; 334 // In case of SET UP MENU command which removed the app, don't 335 // update the current menu member. 336 if (mCurrentMenu != null && mMainCmd != null) { 337 mCurrentMenu = mMainCmd.getMenu(); 338 } 339 if (mMenuIsVisibile) { 340 launchMenuActivity(null); 341 } 342 if (mCmdsQ.size() != 0) { 343 callDelayedMsg(); 344 } else { 345 mCmdInProgress = false; 346 } 347 // In case a launch browser command was just confirmed, launch that url. 348 if (launchBrowser) { 349 launchBrowser = false; 350 launchBrowser(mBrowserSettings); 351 } 352 } 353 354 private void handleCmd(StkCmdMessage cmdMsg) { 355 if (cmdMsg == null) { 356 return; 357 } 358 // save local reference for state tracking. 359 mCurrentCmd = cmdMsg; 360 boolean waitForUsersResponse = true; 361 362 StkLog.d(this, cmdMsg.getCmdType().name()); 363 switch (cmdMsg.getCmdType()) { 364 case DISPLAY_TEXT: 365 TextMessage msg = cmdMsg.geTextMessage(); 366 responseNeeded = msg.responseNeeded; 367 if (lastSelectedItem != null) { 368 msg.title = lastSelectedItem; 369 } else if (mMainCmd != null){ 370 msg.title = mMainCmd.getMenu().title; 371 } else { 372 // TODO: get the carrier name from the SIM 373 msg.title = ""; 374 } 375 launchTextDialog(); 376 break; 377 case SELECT_ITEM: 378 mCurrentMenu = cmdMsg.getMenu(); 379 launchMenuActivity(cmdMsg.getMenu()); 380 break; 381 case SET_UP_MENU: 382 mMainCmd = mCurrentCmd; 383 mCurrentMenu = cmdMsg.getMenu(); 384 if (removeMenu()) { 385 StkLog.d(this, "Uninstall App"); 386 mCurrentMenu = null; 387 StkAppInstaller.unInstall(mContext); 388 } else { 389 StkLog.d(this, "Install App"); 390 StkAppInstaller.install(mContext); 391 } 392 if (mMenuIsVisibile) { 393 launchMenuActivity(null); 394 } 395 break; 396 case GET_INPUT: 397 case GET_INKEY: 398 launchInputActivity(); 399 break; 400 case SET_UP_IDLE_MODE_TEXT: 401 waitForUsersResponse = false; 402 launchIdleText(); 403 break; 404 case SEND_DTMF: 405 case SEND_SMS: 406 case SEND_SS: 407 case SEND_USSD: 408 waitForUsersResponse = false; 409 launchEventMessage(); 410 break; 411 case LAUNCH_BROWSER: 412 launchConfirmationDialog(mCurrentCmd.geTextMessage()); 413 break; 414 case SET_UP_CALL: 415 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg); 416 break; 417 case PLAY_TONE: 418 launchToneDialog(); 419 break; 420 } 421 422 if (!waitForUsersResponse) { 423 if (mCmdsQ.size() != 0) { 424 callDelayedMsg(); 425 } else { 426 mCmdInProgress = false; 427 } 428 } 429 } 430 431 private void handleCmdResponse(Bundle args) { 432 if (mCurrentCmd == null) { 433 return; 434 } 435 StkResponseMessage resMsg = new StkResponseMessage(mCurrentCmd); 436 437 // set result code 438 boolean helpRequired = args.getBoolean(HELP, false); 439 440 switch(args.getInt(RES_ID)) { 441 case RES_ID_MENU_SELECTION: 442 StkLog.d(this, "RES_ID_MENU_SELECTION"); 443 int menuSelection = args.getInt(MENU_SELECTION); 444 switch(mCurrentCmd.getCmdType()) { 445 case SET_UP_MENU: 446 case SELECT_ITEM: 447 lastSelectedItem = getItemName(menuSelection); 448 if (helpRequired) { 449 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 450 } else { 451 resMsg.setResultCode(ResultCode.OK); 452 } 453 resMsg.setMenuSelection(menuSelection); 454 break; 455 } 456 break; 457 case RES_ID_INPUT: 458 StkLog.d(this, "RES_ID_INPUT"); 459 String input = args.getString(INPUT); 460 if (mCurrentCmd.geInput().yesNo) { 461 boolean yesNoSelection = input 462 .equals(StkInputActivity.YES_STR_RESPONSE); 463 resMsg.setYesNo(yesNoSelection); 464 } else { 465 if (helpRequired) { 466 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 467 } else { 468 resMsg.setResultCode(ResultCode.OK); 469 resMsg.setInput(input); 470 } 471 } 472 break; 473 case RES_ID_CONFIRM: 474 StkLog.d(this, "RES_ID_CONFIRM"); 475 boolean confirmed = args.getBoolean(CONFIRMATION); 476 switch (mCurrentCmd.getCmdType()) { 477 case DISPLAY_TEXT: 478 resMsg.setResultCode(confirmed ? ResultCode.OK 479 : ResultCode.UICC_SESSION_TERM_BY_USER); 480 break; 481 case LAUNCH_BROWSER: 482 resMsg.setResultCode(confirmed ? ResultCode.OK 483 : ResultCode.UICC_SESSION_TERM_BY_USER); 484 if (confirmed) { 485 launchBrowser = true; 486 mBrowserSettings = mCurrentCmd.getBrowserSettings(); 487 } 488 break; 489 case SET_UP_CALL: 490 resMsg.setResultCode(ResultCode.OK); 491 resMsg.setConfirmation(confirmed); 492 if (confirmed) { 493 launchCallMsg(); 494 } 495 break; 496 } 497 break; 498 case RES_ID_DONE: 499 resMsg.setResultCode(ResultCode.OK); 500 break; 501 case RES_ID_BACKWARD: 502 StkLog.d(this, "RES_ID_BACKWARD"); 503 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER); 504 break; 505 case RES_ID_END_SESSION: 506 StkLog.d(this, "RES_ID_END_SESSION"); 507 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER); 508 break; 509 case RES_ID_TIMEOUT: 510 StkLog.d(this, "RES_ID_TIMEOUT"); 511 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER); 512 break; 513 default: 514 StkLog.d(this, "Unknown result id"); 515 return; 516 } 517 mStkService.onCmdResponse(resMsg); 518 } 519 520 private void launchMenuActivity(Menu menu) { 521 Intent newIntent = new Intent(Intent.ACTION_VIEW); 522 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 523 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 524 newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME); 525 if (menu == null) { 526 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN); 527 } else { 528 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY); 529 } 530 mContext.startActivity(newIntent); 531 } 532 533 private void launchInputActivity() { 534 Intent newIntent = new Intent(Intent.ACTION_VIEW); 535 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 536 newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME); 537 newIntent.putExtra("INPUT", mCurrentCmd.geInput()); 538 mContext.startActivity(newIntent); 539 } 540 541 private void launchTextDialog() { 542 Intent newIntent = new Intent(this, StkDialogActivity.class); 543 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 544 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK 545 | Intent.FLAG_ACTIVITY_NO_HISTORY 546 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 547 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 548 startActivity(newIntent); 549 } 550 551 private void launchEventMessage() { 552 TextMessage msg = mCurrentCmd.geTextMessage(); 553 if (msg == null) { 554 return; 555 } 556 Toast toast = new Toast(mContext.getApplicationContext()); 557 LayoutInflater inflate = (LayoutInflater) mContext 558 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 559 View v = inflate.inflate(R.layout.stk_event_msg, null); 560 TextView tv = (TextView) v 561 .findViewById(com.android.internal.R.id.message); 562 ImageView iv = (ImageView) v 563 .findViewById(com.android.internal.R.id.icon); 564 if (msg.icon != null) { 565 iv.setImageBitmap(msg.icon); 566 } else { 567 iv.setVisibility(View.GONE); 568 } 569 if (!msg.iconSelfExplanatory) { 570 tv.setText(msg.text); 571 } 572 573 toast.setView(v); 574 toast.setDuration(Toast.LENGTH_LONG); 575 toast.setGravity(Gravity.BOTTOM, 0, 0); 576 toast.show(); 577 } 578 579 private void launchConfirmationDialog(TextMessage msg) { 580 msg.title = lastSelectedItem; 581 Intent newIntent = new Intent(this, StkDialogActivity.class); 582 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 583 | Intent.FLAG_ACTIVITY_NO_HISTORY 584 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 585 newIntent.putExtra("TEXT", msg); 586 startActivity(newIntent); 587 } 588 589 private void launchBrowser(BrowserSettings settings) { 590 if (settings == null) { 591 return; 592 } 593 // Set browser launch mode 594 Intent intent = new Intent(); 595 intent.setClassName("com.android.browser", 596 "com.android.browser.BrowserActivity"); 597 598 // to launch home page, make sure that data Uri is null. 599 Uri data = null; 600 if (settings.url != null) { 601 data = Uri.parse(settings.url); 602 } 603 intent.setData(data); 604 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 605 switch (settings.mode) { 606 case USE_EXISTING_BROWSER: 607 intent.setAction(Intent.ACTION_VIEW); 608 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 609 break; 610 case LAUNCH_NEW_BROWSER: 611 intent.setAction(Intent.ACTION_VIEW); 612 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 613 break; 614 case LAUNCH_IF_NOT_ALREADY_LAUNCHED: 615 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 616 break; 617 } 618 // start browser activity 619 startActivity(intent); 620 // a small delay, let the browser start, before processing the next command. 621 // this is good for scenarios where a related DISPLAY TEXT command is 622 // followed immediately. 623 try { 624 Thread.sleep(10000); 625 } catch (InterruptedException e) {} 626 } 627 628 private void launchCallMsg() { 629 TextMessage msg = mCurrentCmd.getCallSettings().callMsg; 630 if (msg.text == null || msg.text.length() == 0) { 631 return; 632 } 633 msg.title = lastSelectedItem; 634 635 Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text, 636 Toast.LENGTH_LONG); 637 toast.setGravity(Gravity.BOTTOM, 0, 0); 638 toast.show(); 639 } 640 641 private void launchIdleText() { 642 TextMessage msg = mCurrentCmd.geTextMessage(); 643 if (msg.text == null) { 644 mNotificationManager.cancel(STK_NOTIFICATION_ID); 645 } else { 646 Notification notification = new Notification(); 647 RemoteViews contentView = new RemoteViews( 648 PACKAGE_NAME, 649 com.android.internal.R.layout.status_bar_latest_event_content); 650 651 notification.flags |= Notification.FLAG_NO_CLEAR; 652 notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit; 653 // Set text and icon for the status bar and notification body. 654 if (!msg.iconSelfExplanatory) { 655 notification.tickerText = msg.text; 656 contentView.setTextViewText(com.android.internal.R.id.text, 657 msg.text); 658 } 659 if (msg.icon != null) { 660 contentView.setImageViewBitmap(com.android.internal.R.id.icon, 661 msg.icon); 662 } else { 663 contentView 664 .setImageViewResource( 665 com.android.internal.R.id.icon, 666 com.android.internal.R.drawable.stat_notify_sim_toolkit); 667 } 668 notification.contentView = contentView; 669 notification.contentIntent = PendingIntent.getService(mContext, 0, 670 new Intent(mContext, StkAppService.class), 0); 671 672 mNotificationManager.notify(STK_NOTIFICATION_ID, notification); 673 } 674 } 675 676 private void launchToneDialog() { 677 Intent newIntent = new Intent(this, ToneDialog.class); 678 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 679 | Intent.FLAG_ACTIVITY_NO_HISTORY 680 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 681 newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage()); 682 newIntent.putExtra("TONE", mCurrentCmd.getToneSettings()); 683 startActivity(newIntent); 684 } 685 686 private String getItemName(int itemId) { 687 Menu menu = mCurrentCmd.getMenu(); 688 if (menu == null) { 689 return null; 690 } 691 for (Item item : menu.items) { 692 if (item.id == itemId) { 693 return item.text; 694 } 695 } 696 return null; 697 } 698 699 private boolean removeMenu() { 700 try { 701 if (mCurrentMenu.items.size() == 1 && 702 mCurrentMenu.items.get(0) == null) { 703 return true; 704 } 705 } catch (NullPointerException e) { 706 StkLog.d(this, "Unable to get Menu's items size"); 707 return true; 708 } 709 return false; 710 } 711} 712