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