auto import from //depot/cupcake/@135843
diff --git a/src/com/android/stk/BootCompletedReceiver.java b/src/com/android/stk/BootCompletedReceiver.java
new file mode 100644
index 0000000..12cb32e
--- /dev/null
+++ b/src/com/android/stk/BootCompletedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Boot completed receiver. used to reset the app install state every time the
+ * device boots.
+ *
+ */
+public class BootCompletedReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        // make sure the app icon is removed every time the device boots.
+        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+            Bundle args = new Bundle();
+            args.putInt(StkAppService.OPCODE, StkAppService.OP_BOOT_COMPLETED);
+            context.startService(new Intent(context, StkAppService.class)
+                    .putExtras(args));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/stk/StkApp.java b/src/com/android/stk/StkApp.java
new file mode 100644
index 0000000..ebd52b1
--- /dev/null
+++ b/src/com/android/stk/StkApp.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.Application;
+
+import com.android.internal.telephony.gsm.stk.Duration;
+
+/**
+ * Top-level Application class for STK app.
+ */
+abstract class StkApp extends Application {
+    // Application constants
+    public static final boolean DBG = true;
+
+    // Identifiers for option menu items
+    static final int MENU_ID_END_SESSION = android.view.Menu.FIRST;
+    static final int MENU_ID_BACK = android.view.Menu.FIRST + 1;
+    static final int MENU_ID_HELP = android.view.Menu.FIRST + 2;
+
+    // UI timeout, 30 seconds - used for display dialog and activities.
+    static final int UI_TIMEOUT = (40 * 1000);
+
+    // Tone default timeout - 2 seconds
+    static final int TONE_DFEAULT_TIMEOUT = (2 * 1000);
+
+    public static final String TAG = "STK App";
+
+    /**
+     * This function calculate the time in MS from a duration instance.
+     * returns zero when duration is null.
+     */
+    public static int calculateDurationInMilis(Duration duration) {
+        int timeout = 0;
+        if (duration != null) {
+            switch (duration.timeUnit) {
+            case MINUTE:
+                timeout = 1000 * 60;
+                break;
+            case TENTH_SECOND:
+                timeout = 1000 * 10;
+                break;
+            case SECOND:
+            default:
+                timeout = 1000;
+                break;
+            }
+            timeout *= duration.timeInterval;
+        }
+        return timeout;
+    }
+}
diff --git a/src/com/android/stk/StkAppInstaller.java b/src/com/android/stk/StkAppInstaller.java
new file mode 100644
index 0000000..7c04a43
--- /dev/null
+++ b/src/com/android/stk/StkAppInstaller.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import com.android.internal.telephony.gsm.stk.StkLog;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * Application installer for SIM Toolkit.
+ * 
+ */
+abstract class StkAppInstaller {
+    private StkAppInstaller() {}
+
+    static void install(Context context) {
+        setAppState(context, true);
+    }
+
+    static void unInstall(Context context) {
+        setAppState(context, false);
+    }
+
+    private static void setAppState(Context context, boolean install) {
+        if (context == null) {
+            return;
+        }
+        PackageManager pm = context.getPackageManager();
+        if (pm == null) {
+            return;
+        }
+        // check that STK app package is known to the PackageManager
+        ComponentName cName = new ComponentName("com.android.stk",
+                "com.android.stk.StkLauncherActivity");
+        int state = install ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+
+        try {
+            pm.setComponentEnabledSetting(cName, state,
+                    PackageManager.DONT_KILL_APP);
+        } catch (Exception e) {
+            StkLog.d("StkAppInstaller", "Could not change STK app state");
+        }
+    }
+}
diff --git a/src/com/android/stk/StkAppService.java b/src/com/android/stk/StkAppService.java
new file mode 100644
index 0000000..136bc71
--- /dev/null
+++ b/src/com/android/stk/StkAppService.java
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.telephony.gsm.stk.AppInterface;
+import com.android.internal.telephony.gsm.stk.Menu;
+import com.android.internal.telephony.gsm.stk.Item;
+import com.android.internal.telephony.gsm.stk.ResultCode;
+import com.android.internal.telephony.gsm.stk.StkCmdMessage;
+import com.android.internal.telephony.gsm.stk.StkCmdMessage.BrowserSettings;
+import com.android.internal.telephony.gsm.stk.StkLog;
+import com.android.internal.telephony.gsm.stk.StkResponseMessage;
+import com.android.internal.telephony.gsm.stk.TextMessage;
+
+import java.util.LinkedList;
+
+/**
+ * SIM toolkit application level service. Interacts with Telephopny messages,
+ * application's launch and user input from STK UI elements.
+ * 
+ */
+public class StkAppService extends Service implements Runnable {
+
+    // members
+    private volatile Looper mServiceLooper;
+    private volatile ServiceHandler mServiceHandler;
+    private AppInterface mStkService;
+    private Context mContext = null;
+    private StkCmdMessage mMainCmd = null;
+    private StkCmdMessage mCurrentCmd = null;
+    private Menu mCurrentMenu = null;
+    private String lastSelectedItem = null;
+    private boolean mMenuIsVisibile = false;
+    private boolean responseNeeded = true;
+    private boolean mCmdInProgress = false;
+    private NotificationManager mNotificationManager = null;
+    private LinkedList<DelayedCmd> mCmdsQ = null;
+    private boolean launchBrowser = false;
+    private BrowserSettings mBrowserSettings = null;
+    static StkAppService sInstance = null;
+
+    // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when
+    // creating an intent.
+    private enum InitiatedByUserAction {
+        yes,            // The action was started via a user initiated action
+        unknown,        // Not known for sure if user initated the action
+    }
+
+    // constants
+    static final String OPCODE = "op";
+    static final String CMD_MSG = "cmd message";
+    static final String RES_ID = "response id";
+    static final String MENU_SELECTION = "menu selection";
+    static final String INPUT = "input";
+    static final String HELP = "help";
+    static final String CONFIRMATION = "confirm";
+
+    // operations ids for different service functionality.
+    static final int OP_CMD = 1;
+    static final int OP_RESPONSE = 2;
+    static final int OP_LAUNCH_APP = 3;
+    static final int OP_END_SESSION = 4;
+    static final int OP_BOOT_COMPLETED = 5;
+    private static final int OP_DELAYED_MSG = 6;
+
+    // Response ids
+    static final int RES_ID_MENU_SELECTION = 11;
+    static final int RES_ID_INPUT = 12;
+    static final int RES_ID_CONFIRM = 13;
+    static final int RES_ID_DONE = 14;
+
+    static final int RES_ID_TIMEOUT = 20;
+    static final int RES_ID_BACKWARD = 21;
+    static final int RES_ID_END_SESSION = 22;
+    static final int RES_ID_EXIT = 23;
+
+    private static final String PACKAGE_NAME = "com.android.stk";
+    private static final String MENU_ACTIVITY_NAME = 
+                                        PACKAGE_NAME + ".StkMenuActivity";
+    private static final String INPUT_ACTIVITY_NAME = 
+                                        PACKAGE_NAME + ".StkInputActivity";
+    
+    // Notification id used to display Idle Mode text in NotificationManager.
+    private static final int STK_NOTIFICATION_ID = 333;
+    
+    // Inner class used for queuing telephony messages (proactive commands, 
+    // session end) while the service is busy processing a previous message.
+    private class DelayedCmd {
+        // members
+        int id;
+        StkCmdMessage msg;
+
+        DelayedCmd(int id, StkCmdMessage msg) {
+            this.id = id;
+            this.msg = msg;
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        // Initialize members
+        mStkService = com.android.internal.telephony.gsm.stk.StkService
+                .getInstance();
+        if (mStkService == null) {
+            StkLog.d(this, " Unable to get Service handle");
+            return;
+        }
+
+        mCmdsQ = new LinkedList<DelayedCmd>();
+        Thread serviceThread = new Thread(null, this, "Stk App Service");
+        serviceThread.start();
+        mContext = getBaseContext();
+        mNotificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        sInstance = this;
+    }
+
+    @Override
+    public void onStart(Intent intent, int startId) {
+        waitForLooper();
+
+        Bundle args = intent.getExtras();
+        if (args == null) {
+            return;
+        }
+
+        Message msg = mServiceHandler.obtainMessage();
+        msg.arg1 = args.getInt(OPCODE);
+        switch(msg.arg1) {
+        case OP_CMD:
+            msg.obj = args.getParcelable(CMD_MSG);
+            break;
+        case OP_RESPONSE:
+            msg.obj = args;
+            /* falls through */
+        case OP_LAUNCH_APP:
+        case OP_END_SESSION:
+        case OP_BOOT_COMPLETED:
+            break;
+        default:
+            return;
+        }
+        mServiceHandler.sendMessage(msg);
+    }
+
+    @Override
+    public void onDestroy() {
+        waitForLooper();
+        mServiceLooper.quit();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    public void run() {
+        Looper.prepare();
+
+        mServiceLooper = Looper.myLooper();
+        mServiceHandler = new ServiceHandler();
+
+        Looper.loop();
+    }
+
+    /*
+     * Package api used by StkMenuActivity to indicate if its on the foreground.
+     */
+    void indicateMenuVisibility(boolean visibility) {
+        mMenuIsVisibile = visibility;
+    }
+
+    /*
+     * Package api used by StkMenuActivity to get its Menu parameter.
+     */
+    Menu getMenu() {
+        return mCurrentMenu;
+    }
+
+    /*
+     * Package api used by UI Activities and Dialogs to communicate directly
+     * with the service to deliver state information and parameters.
+     */
+    static StkAppService getInstance() {
+        return sInstance;
+    }
+
+    private void waitForLooper() {
+        while (mServiceHandler == null) {
+            synchronized (this) {
+                try {
+                    wait(100);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    private final class ServiceHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            int opcode = msg.arg1;
+
+            switch (opcode) {
+            case OP_LAUNCH_APP:
+                if (mMainCmd == null) {
+                    // nothing todo when no SET UP MENU command didn't arrive.
+                    return;
+                }
+                launchMenuActivity(null);
+                break;
+            case OP_CMD:
+                StkCmdMessage cmdMsg = (StkCmdMessage) msg.obj;
+                // There are two types of commands:
+                // 1. Interactive - user's response is required.
+                // 2. Informative - display a message, no interaction with the user.
+                //
+                // Informative commands can be handled immediately without any delay. 
+                // Interactive commands can't override each other. So if a command
+                // is already in progress, we need to queue the next command until
+                // the user has responded or a timeout expired.
+                if (!isCmdInteractive(cmdMsg)) {
+                    handleCmd(cmdMsg);
+                } else {
+                    if (!mCmdInProgress) {
+                        mCmdInProgress = true;
+                        handleCmd((StkCmdMessage) msg.obj);
+                    } else {
+                        mCmdsQ.addLast(new DelayedCmd(OP_CMD,
+                                (StkCmdMessage) msg.obj));
+                    }
+                }
+                break;
+            case OP_RESPONSE:
+                if (responseNeeded) {
+                    handleCmdResponse((Bundle) msg.obj);
+                }
+                // call delayed commands if needed.
+                if (mCmdsQ.size() != 0) {
+                    callDelayedMsg();
+                } else {
+                    mCmdInProgress = false;
+                }
+                // reset response needed state var to its original value.
+                responseNeeded = true;
+                break;
+            case OP_END_SESSION:
+                if (!mCmdInProgress) {
+                    mCmdInProgress = true;
+                    handleSessionEnd();
+                } else {
+                    mCmdsQ.addLast(new DelayedCmd(OP_END_SESSION, null));
+                }
+                break;
+            case OP_BOOT_COMPLETED:
+                StkLog.d(this, "OP_BOOT_COMPLETED");
+                if (mMainCmd == null) {
+                    StkAppInstaller.unInstall(mContext);
+                }
+                break;
+            case OP_DELAYED_MSG:
+                handleDelayedCmd();
+                break;
+            }
+        }
+    }
+
+    private boolean isCmdInteractive(StkCmdMessage cmd) {
+        switch (cmd.getCmdType()) {
+        case SEND_DTMF:
+        case SEND_SMS:
+        case SEND_SS:
+        case SEND_USSD:
+        case SET_UP_IDLE_MODE_TEXT:
+        case SET_UP_MENU:
+            return false;
+        }
+
+        return true;
+    }
+
+    private void handleDelayedCmd() {
+        if (mCmdsQ.size() != 0) {
+            DelayedCmd cmd = mCmdsQ.poll();
+            switch (cmd.id) {
+            case OP_CMD:
+                handleCmd(cmd.msg);
+                break;
+            case OP_END_SESSION:
+                handleSessionEnd();
+                break;
+            }
+        }
+    }
+
+    private void callDelayedMsg() {
+        Message msg = mServiceHandler.obtainMessage();
+        msg.arg1 = OP_DELAYED_MSG;
+        mServiceHandler.sendMessage(msg);
+    }
+
+    private void handleSessionEnd() {
+        mCurrentCmd = mMainCmd;
+        lastSelectedItem = null;
+        // In case of SET UP MENU command which removed the app, don't 
+        // update the current menu member.
+        if (mCurrentMenu != null && mMainCmd != null) {
+            mCurrentMenu = mMainCmd.getMenu();
+        }
+        if (mMenuIsVisibile) {
+            launchMenuActivity(null);
+        }
+        if (mCmdsQ.size() != 0) {
+            callDelayedMsg();
+        } else {
+            mCmdInProgress = false;
+        }
+        // In case a launch browser command was just confirmed, launch that url.
+        if (launchBrowser) {
+            launchBrowser = false;
+            launchBrowser(mBrowserSettings);
+        }
+    }
+
+    private void handleCmd(StkCmdMessage cmdMsg) {
+        if (cmdMsg == null) {
+            return;
+        }
+        // save local reference for state tracking.
+        mCurrentCmd = cmdMsg;
+        boolean waitForUsersResponse = true;
+
+        StkLog.d(this, cmdMsg.getCmdType().name());
+        switch (cmdMsg.getCmdType()) {
+        case DISPLAY_TEXT:
+            TextMessage msg = cmdMsg.geTextMessage();
+            responseNeeded = msg.responseNeeded;
+            if (lastSelectedItem != null) {
+                msg.title = lastSelectedItem;
+            } else if (mMainCmd != null){
+                msg.title = mMainCmd.getMenu().title;
+            } else {
+                // TODO: get the carrier name from the SIM
+                msg.title = "";
+            }
+            launchTextDialog();
+            break;
+        case SELECT_ITEM:
+            mCurrentMenu = cmdMsg.getMenu();
+            launchMenuActivity(cmdMsg.getMenu());
+            break;
+        case SET_UP_MENU:
+            mMainCmd = mCurrentCmd;
+            mCurrentMenu = cmdMsg.getMenu();
+            if (removeMenu()) {
+                StkLog.d(this, "Uninstall App");
+                mCurrentMenu = null;
+                StkAppInstaller.unInstall(mContext);
+            } else {
+                StkLog.d(this, "Install App");
+                StkAppInstaller.install(mContext);
+            }
+            if (mMenuIsVisibile) {
+                launchMenuActivity(null);
+            }
+            break;
+        case GET_INPUT:
+        case GET_INKEY:
+            launchInputActivity();
+            break;
+        case SET_UP_IDLE_MODE_TEXT:
+            waitForUsersResponse = false;
+            launchIdleText();
+            break;
+        case SEND_DTMF:
+        case SEND_SMS:
+        case SEND_SS:
+        case SEND_USSD:
+            waitForUsersResponse = false;
+            launchEventMessage();
+            break;
+        case LAUNCH_BROWSER:
+            launchConfirmationDialog(mCurrentCmd.geTextMessage());
+            break;
+        case SET_UP_CALL:
+            launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg);
+            break;
+        case PLAY_TONE:
+            launchToneDialog();
+            break;
+        }
+
+        if (!waitForUsersResponse) {
+            if (mCmdsQ.size() != 0) {
+                callDelayedMsg();
+            } else {
+                mCmdInProgress = false;
+            }
+        }
+    }
+
+    private void handleCmdResponse(Bundle args) {
+        if (mCurrentCmd == null) {
+            return;
+        }
+        StkResponseMessage resMsg = new StkResponseMessage(mCurrentCmd);
+
+        // set result code
+        boolean helpRequired = args.getBoolean(HELP, false);
+
+        switch(args.getInt(RES_ID)) {
+        case RES_ID_MENU_SELECTION:
+            StkLog.d(this, "RES_ID_MENU_SELECTION");
+            int menuSelection = args.getInt(MENU_SELECTION);
+            switch(mCurrentCmd.getCmdType()) {
+            case SET_UP_MENU:
+            case SELECT_ITEM:
+                lastSelectedItem = getItemName(menuSelection);
+                if (helpRequired) {
+                    resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
+                } else {
+                    resMsg.setResultCode(ResultCode.OK);
+                }
+                resMsg.setMenuSelection(menuSelection);
+                break;
+            }
+            break;
+        case RES_ID_INPUT:
+            StkLog.d(this, "RES_ID_INPUT");
+            String input = args.getString(INPUT);
+            if (mCurrentCmd.geInput().yesNo) {
+                boolean yesNoSelection = input
+                        .equals(StkInputActivity.YES_STR_RESPONSE);
+                resMsg.setYesNo(yesNoSelection);
+            } else {
+                if (helpRequired) {
+                    resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
+                } else {
+                    resMsg.setResultCode(ResultCode.OK);
+                    resMsg.setInput(input);
+                }
+            }
+            break;
+        case RES_ID_CONFIRM:
+            StkLog.d(this, "RES_ID_CONFIRM");
+            boolean confirmed = args.getBoolean(CONFIRMATION);
+            switch (mCurrentCmd.getCmdType()) {
+            case DISPLAY_TEXT:
+                resMsg.setResultCode(confirmed ? ResultCode.OK
+                        : ResultCode.UICC_SESSION_TERM_BY_USER);
+                break;
+            case LAUNCH_BROWSER:
+                resMsg.setResultCode(confirmed ? ResultCode.OK
+                        : ResultCode.UICC_SESSION_TERM_BY_USER);
+                if (confirmed) {
+                    launchBrowser = true;
+                    mBrowserSettings = mCurrentCmd.getBrowserSettings();
+                }
+                break;
+            case SET_UP_CALL:
+                resMsg.setResultCode(ResultCode.OK);
+                resMsg.setConfirmation(confirmed);
+                if (confirmed) {
+                    launchCallMsg();
+                }
+                break;
+            }
+            break;
+        case RES_ID_DONE:
+            resMsg.setResultCode(ResultCode.OK);
+            break;
+        case RES_ID_BACKWARD:
+            StkLog.d(this, "RES_ID_BACKWARD");
+            resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
+            break;
+        case RES_ID_END_SESSION:
+            StkLog.d(this, "RES_ID_END_SESSION");
+            resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
+            break;
+        case RES_ID_TIMEOUT:
+            StkLog.d(this, "RES_ID_TIMEOUT");
+            resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
+            break;
+        default:
+            StkLog.d(this, "Unknown result id");
+            return;
+        }
+        mStkService.onCmdResponse(resMsg);
+    }
+
+    /**
+     * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
+     *
+     * @param userAction If the userAction is yes then we always return 0 otherwise
+     * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
+     * then we are the foreground app and we'll return 0 as from our perspective a
+     * user action did cause. If it's false than we aren't the foreground app and
+     * FLAG_ACTIVITY_NO_USER_ACTION is returned.
+     * 
+     * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
+     */
+    private int getFlagActivityNoUserAction(InitiatedByUserAction userAction) {
+        return ((userAction == InitiatedByUserAction.yes) | mMenuIsVisibile) ?
+                                                    0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+    }
+
+    private void launchMenuActivity(Menu menu) {
+        Intent newIntent = new Intent(Intent.ACTION_VIEW);
+        newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
+        int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP;
+        if (menu == null) {
+            // We assume this was initiated by the user pressing the tool kit icon
+            intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
+
+            newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
+        } else {
+            // We don't know and we'll let getFlagActivityNoUserAction decide.
+            intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
+
+            newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
+        }
+        newIntent.setFlags(intentFlags);
+        mContext.startActivity(newIntent);
+    }
+
+    private void launchInputActivity() {
+        Intent newIntent = new Intent(Intent.ACTION_VIEW);
+        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
+        newIntent.setClassName(PACKAGE_NAME, INPUT_ACTIVITY_NAME);
+        newIntent.putExtra("INPUT", mCurrentCmd.geInput());
+        mContext.startActivity(newIntent);
+    }
+
+    private void launchTextDialog() {
+        Intent newIntent = new Intent(this, StkDialogActivity.class);
+        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                | Intent.FLAG_ACTIVITY_NO_HISTORY
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
+        newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
+        startActivity(newIntent);
+    }
+
+    private void launchEventMessage() {
+        TextMessage msg = mCurrentCmd.geTextMessage();
+        if (msg == null) {
+            return;
+        }
+        Toast toast = new Toast(mContext.getApplicationContext());
+        LayoutInflater inflate = (LayoutInflater) mContext
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View v = inflate.inflate(R.layout.stk_event_msg, null);
+        TextView tv = (TextView) v
+                .findViewById(com.android.internal.R.id.message);
+        ImageView iv = (ImageView) v
+                .findViewById(com.android.internal.R.id.icon);
+        if (msg.icon != null) {
+            iv.setImageBitmap(msg.icon);
+        } else {
+            iv.setVisibility(View.GONE);
+        }
+        if (!msg.iconSelfExplanatory) {
+            tv.setText(msg.text);
+        }
+
+        toast.setView(v);
+        toast.setDuration(Toast.LENGTH_LONG);
+        toast.setGravity(Gravity.BOTTOM, 0, 0);
+        toast.show();
+    }
+
+    private void launchConfirmationDialog(TextMessage msg) {
+        msg.title = lastSelectedItem;
+        Intent newIntent = new Intent(this, StkDialogActivity.class);
+        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_NO_HISTORY
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
+        newIntent.putExtra("TEXT", msg);
+        startActivity(newIntent);
+    }
+
+    private void launchBrowser(BrowserSettings settings) {
+        if (settings == null) {
+            return;
+        }
+        // Set browser launch mode
+        Intent intent = new Intent();
+        intent.setClassName("com.android.browser",
+                "com.android.browser.BrowserActivity");
+
+        // to launch home page, make sure that data Uri is null. 
+        Uri data = null;
+        if (settings.url != null) {
+            data = Uri.parse(settings.url);
+        }
+        intent.setData(data);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        switch (settings.mode) {
+        case USE_EXISTING_BROWSER:
+            intent.setAction(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            break;
+        case LAUNCH_NEW_BROWSER:
+            intent.setAction(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            break;
+        case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            break;
+        }
+        // start browser activity
+        startActivity(intent);
+        // a small delay, let the browser start, before processing the next command.
+        // this is good for scenarios where a related DISPLAY TEXT command is 
+        // followed immediately.
+        try {
+            Thread.sleep(10000);
+        } catch (InterruptedException e) {}
+    }
+
+    private void launchCallMsg() {
+        TextMessage msg = mCurrentCmd.getCallSettings().callMsg;
+        if (msg.text == null || msg.text.length() == 0) {
+            return;
+        }
+        msg.title = lastSelectedItem;
+
+        Toast toast = Toast.makeText(mContext.getApplicationContext(), msg.text,
+                Toast.LENGTH_LONG);
+        toast.setGravity(Gravity.BOTTOM, 0, 0);
+        toast.show();
+    }
+
+    private void launchIdleText() {
+        TextMessage msg = mCurrentCmd.geTextMessage();
+        if (msg.text == null) {
+            mNotificationManager.cancel(STK_NOTIFICATION_ID);
+        } else {
+            Notification notification = new Notification();
+            RemoteViews contentView = new RemoteViews(
+                    PACKAGE_NAME,
+                    com.android.internal.R.layout.status_bar_latest_event_content);
+
+            notification.flags |= Notification.FLAG_NO_CLEAR;
+            notification.icon = com.android.internal.R.drawable.stat_notify_sim_toolkit;
+            // Set text and icon for the status bar and notification body.
+            if (!msg.iconSelfExplanatory) {
+                notification.tickerText = msg.text;
+                contentView.setTextViewText(com.android.internal.R.id.text,
+                        msg.text);
+            }
+            if (msg.icon != null) {
+                contentView.setImageViewBitmap(com.android.internal.R.id.icon,
+                        msg.icon);
+            } else {
+                contentView
+                        .setImageViewResource(
+                                com.android.internal.R.id.icon,
+                                com.android.internal.R.drawable.stat_notify_sim_toolkit);
+            }
+            notification.contentView = contentView;
+            notification.contentIntent = PendingIntent.getService(mContext, 0,
+                    new Intent(mContext, StkAppService.class), 0);
+
+            mNotificationManager.notify(STK_NOTIFICATION_ID, notification);
+        }
+    }
+
+    private void launchToneDialog() {
+        Intent newIntent = new Intent(this, ToneDialog.class);
+        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_NO_HISTORY
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | getFlagActivityNoUserAction(InitiatedByUserAction.unknown));
+        newIntent.putExtra("TEXT", mCurrentCmd.geTextMessage());
+        newIntent.putExtra("TONE", mCurrentCmd.getToneSettings());
+        startActivity(newIntent);
+    }
+
+    private String getItemName(int itemId) {
+        Menu menu = mCurrentCmd.getMenu();
+        if (menu == null) {
+            return null;
+        }
+        for (Item item : menu.items) {
+            if (item.id == itemId) {
+                return item.text;
+            }
+        }
+        return null;
+    }
+
+    private boolean removeMenu() {
+        try {
+            if (mCurrentMenu.items.size() == 1 && 
+                mCurrentMenu.items.get(0) == null) {
+                return true;
+            }
+        } catch (NullPointerException e) {
+            StkLog.d(this, "Unable to get Menu's items size");
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/stk/StkCmdReceiver.java b/src/com/android/stk/StkCmdReceiver.java
new file mode 100644
index 0000000..6f05de6
--- /dev/null
+++ b/src/com/android/stk/StkCmdReceiver.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import com.android.internal.telephony.gsm.stk.AppInterface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Receiver class to get STK intents, broadcasted by telephony layer.
+ * 
+ */
+public class StkCmdReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        if (action.equals(AppInterface.STK_CMD_ACTION)) {
+            handleCommandMessage(context, intent);
+        } else if (action.equals(AppInterface.STK_SESSION_END_ACTION)) {
+            handleSessionEnd(context, intent);
+        }
+    }
+
+    private void handleCommandMessage(Context context, Intent intent) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_CMD);
+        args.putParcelable(StkAppService.CMD_MSG, intent
+                .getParcelableExtra("STK CMD"));
+        context.startService(new Intent(context, StkAppService.class)
+                .putExtras(args));
+    }
+
+    private void handleSessionEnd(Context context, Intent intent) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_END_SESSION);
+        context.startService(new Intent(context, StkAppService.class)
+                .putExtras(args));
+    }
+}
diff --git a/src/com/android/stk/StkDialogActivity.java b/src/com/android/stk/StkDialogActivity.java
new file mode 100644
index 0000000..15eacec
--- /dev/null
+++ b/src/com/android/stk/StkDialogActivity.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import com.android.internal.telephony.gsm.stk.TextMessage;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * AlretDialog used for DISPLAY TEXT commands.
+ * 
+ */
+public class StkDialogActivity extends Activity implements View.OnClickListener {
+    // members
+    TextMessage mTextMsg;
+
+    Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+            case MSG_ID_TIMEOUT:
+                sendResponse(StkAppService.RES_ID_TIMEOUT);
+                finish();
+                break;
+            }
+        }
+    };
+
+    //keys) for saving the state of the dialog in the icicle
+    private static final String TEXT = "text";
+
+    // message id for time out 
+    private static final int MSG_ID_TIMEOUT = 1;
+
+    // buttons id
+    public static final int OK_BUTTON = R.id.button_ok;
+    public static final int CANCEL_BUTTON = R.id.button_cancel;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        initFromIntent(getIntent());
+        if (mTextMsg == null) {
+            finish();
+            return;
+        }
+
+        requestWindowFeature(Window.FEATURE_LEFT_ICON);
+        Window window = getWindow();
+
+        setContentView(R.layout.stk_msg_dialog);
+        TextView mMessageView = (TextView) window
+                .findViewById(R.id.dialog_message);
+
+        Button okButton = (Button) findViewById(R.id.button_ok);
+        Button cancelButton = (Button) findViewById(R.id.button_cancel);
+
+        okButton.setOnClickListener(this);
+        cancelButton.setOnClickListener(this);
+
+        setTitle(mTextMsg.title);
+        if (!(mTextMsg.iconSelfExplanatory && mTextMsg.icon != null)) {
+            mMessageView.setText(mTextMsg.text); 
+        }
+
+        if (mTextMsg.icon == null) {
+            window.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+                    com.android.internal.R.drawable.stat_notify_sim_toolkit);
+        } else {
+            window.setFeatureDrawable(Window.FEATURE_LEFT_ICON,
+                    new BitmapDrawable(mTextMsg.icon));
+        }
+    }
+
+    public void onClick(View v) {
+        String input = null;
+
+        switch (v.getId()) {
+        case OK_BUTTON:
+            sendResponse(StkAppService.RES_ID_CONFIRM, true);
+            finish();
+            break;
+        case CANCEL_BUTTON:
+            sendResponse(StkAppService.RES_ID_CONFIRM, false);
+            finish();
+            break;
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+        case KeyEvent.KEYCODE_BACK:
+            sendResponse(StkAppService.RES_ID_BACKWARD);
+            finish();
+            break;
+        }
+        return false;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        startTimeOut();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        cancelTimeOut();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putParcelable(TEXT, mTextMsg);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        
+        mTextMsg = savedInstanceState.getParcelable(TEXT);
+    }
+
+    private void sendResponse(int resId, boolean confirmed) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
+        args.putInt(StkAppService.RES_ID, resId);
+        args.putBoolean(StkAppService.CONFIRMATION, confirmed);
+        startService(new Intent(this, StkAppService.class).putExtras(args));
+    }
+
+    private void sendResponse(int resId) {
+        sendResponse(resId, true);
+    }
+
+    private void initFromIntent(Intent intent) {
+
+        if (intent != null) {
+            mTextMsg = intent.getParcelableExtra("TEXT");
+        } else {
+            finish();
+        }
+    }
+
+    private void cancelTimeOut() {
+        mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
+    }
+
+    private void startTimeOut() {
+        // Reset timeout.
+        cancelTimeOut();
+        int dialogDuration = StkApp.calculateDurationInMilis(mTextMsg.duration);
+        if (dialogDuration == 0) {
+            dialogDuration = StkApp.UI_TIMEOUT;
+        }
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
+                .obtainMessage(MSG_ID_TIMEOUT), dialogDuration);
+    }
+}
diff --git a/src/com/android/stk/StkDigitsKeyListener.java b/src/com/android/stk/StkDigitsKeyListener.java
new file mode 100644
index 0000000..eb3d53a
--- /dev/null
+++ b/src/com/android/stk/StkDigitsKeyListener.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.text.method.NumberKeyListener;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+
+/**
+ * For entering dates in a text field.
+ */
+public class StkDigitsKeyListener extends NumberKeyListener {
+    @Override
+    protected char[] getAcceptedChars() {
+        return CHARACTERS;
+    }
+
+    public int getInputType() {
+        return EditorInfo.TYPE_CLASS_PHONE;
+    }
+    
+    public static StkDigitsKeyListener getInstance() {
+        if (sInstance != null) {
+            return sInstance;
+        }
+        sInstance = new StkDigitsKeyListener();
+        return sInstance;
+    }
+
+    /**
+     * The characters that are used.
+     * 
+     * @see KeyEvent#getMatch
+     * @see #getAcceptedChars
+     */
+    public static final char[] CHARACTERS = new char[] {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', '+'};
+
+    private static StkDigitsKeyListener sInstance;
+}
diff --git a/src/com/android/stk/StkInputActivity.java b/src/com/android/stk/StkInputActivity.java
new file mode 100644
index 0000000..f1627e8
--- /dev/null
+++ b/src/com/android/stk/StkInputActivity.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.TextView.BufferType;
+
+import com.android.internal.telephony.gsm.stk.FontSize;
+import com.android.internal.telephony.gsm.stk.Input;
+import com.android.internal.telephony.gsm.stk.StkLog;
+
+/**
+ * Display a request for a text input a long with a text edit form.
+ */
+public class StkInputActivity extends Activity implements View.OnClickListener,
+        TextWatcher {
+
+    // Members
+    private int mState;
+    private Context mContext;
+    private EditText mTextIn = null;
+    private TextView mPromptView = null;
+    private View mYesNoLayout = null;
+    private View mNormalLayout = null;
+    private Input mStkInput = null;
+
+    // Constants
+    private static final int STATE_TEXT = 1;
+    private static final int STATE_YES_NO = 2;
+
+    static final String YES_STR_RESPONSE = "YES";
+    static final String NO_STR_RESPONSE = "NO";
+
+    // Font size factor values.
+    static final float NORMAL_FONT_FACTOR = 1;
+    static final float LARGE_FONT_FACTOR = 2;
+    static final float SMALL_FONT_FACTOR = (1 / 2);
+
+    // message id for time out 
+    private static final int MSG_ID_TIMEOUT = 1;
+
+    Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+            case MSG_ID_TIMEOUT:
+                //mAcceptUsersInput = false;
+                sendResponse(StkAppService.RES_ID_TIMEOUT);
+                finish();
+                break;
+            }
+        }
+    };
+
+    // Click listener to handle buttons press..
+    public void onClick(View v) {
+        String input = null;
+
+        switch (v.getId()) {
+        case R.id.button_ok:
+            // Check that text entered is valid .
+            if (!verfiyTypedText()) {
+                return;
+            }
+            input = mTextIn.getText().toString();
+            break;
+        // Yes/No layout buttons.
+        case R.id.button_yes:
+            input = YES_STR_RESPONSE;
+            break;
+        case R.id.button_no:
+            input = NO_STR_RESPONSE;
+            break;
+        }
+
+        sendResponse(StkAppService.RES_ID_INPUT, input, false);
+        finish();
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Set the layout for this activity.
+        requestWindowFeature(Window.FEATURE_LEFT_ICON);
+        setContentView(R.layout.stk_input);
+
+        // Initialize members
+        mTextIn = (EditText) this.findViewById(R.id.in_text);
+        mPromptView = (TextView) this.findViewById(R.id.prompt);
+
+        // Set buttons listeners.
+        Button okButton = (Button) findViewById(R.id.button_ok);     
+        Button yesButton = (Button) findViewById(R.id.button_yes);
+        Button noButton = (Button) findViewById(R.id.button_no);
+
+        okButton.setOnClickListener(this);
+        yesButton.setOnClickListener(this);
+        noButton.setOnClickListener(this);
+
+        mYesNoLayout = findViewById(R.id.yes_no_layout);
+        mNormalLayout = findViewById(R.id.normal_layout);
+
+        // Get the calling intent type: text/key, and setup the 
+        // display parameters.
+        Intent intent = getIntent();
+        if (intent != null) {
+            mStkInput = intent.getParcelableExtra("INPUT");
+            if (mStkInput == null) {
+                finish();
+            } else {
+                mState = mStkInput.yesNo ? STATE_YES_NO : STATE_TEXT;
+                configInputDisplay();
+            }
+        } else {
+            finish();
+        }
+        mContext = getBaseContext();
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        mTextIn.addTextChangedListener(this);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        startTimeOut();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        cancelTimeOut();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+        case KeyEvent.KEYCODE_BACK:
+            sendResponse(StkAppService.RES_ID_BACKWARD, null, false);
+            finish();
+            break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    private void sendResponse(int resId) {
+        sendResponse(resId, null, false);
+    }
+
+    private void sendResponse(int resId, String input, boolean help) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
+        args.putInt(StkAppService.RES_ID, resId);
+        if (input != null) {
+            args.putString(StkAppService.INPUT, input);
+        }
+        args.putBoolean(StkAppService.HELP, help);
+        mContext.startService(new Intent(mContext, StkAppService.class)
+                .putExtras(args));
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(android.view.Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        menu.add(android.view.Menu.NONE, StkApp.MENU_ID_END_SESSION, 1,
+                R.string.menu_end_session);
+        menu.add(0, StkApp.MENU_ID_HELP, 2, R.string.help);
+
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(android.view.Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(true);
+        menu.findItem(StkApp.MENU_ID_HELP).setVisible(mStkInput.helpAvailable);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+        case StkApp.MENU_ID_END_SESSION:
+            sendResponse(StkAppService.RES_ID_END_SESSION);
+            finish();
+            return true;
+        case StkApp.MENU_ID_HELP:
+            sendResponse(StkAppService.RES_ID_INPUT, "", true);
+            finish();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int count,
+            int after) {
+    }
+
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        // Reset timeout.
+        startTimeOut();
+    }
+
+    public void afterTextChanged(Editable s) {
+    }
+
+    private boolean verfiyTypedText() {
+        // If not enough input was typed in stay on the edit screen.
+        if (mTextIn.getText().length() < mStkInput.minLen) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private void cancelTimeOut() {
+        mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
+    }
+
+    private void startTimeOut() {
+        cancelTimeOut();
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
+                .obtainMessage(MSG_ID_TIMEOUT), StkApp.UI_TIMEOUT);
+    }
+
+    private void configInputDisplay() {
+        TextView numOfCharsView = (TextView) findViewById(R.id.num_of_chars);
+        TextView inTypeView = (TextView) findViewById(R.id.input_type);
+
+        int inTypeId = R.string.alphabet;
+
+        // set the prompt.
+        mPromptView.setText(mStkInput.text);
+
+        // Set input type (alphabet/digit) info close to the InText form. 
+        if (mStkInput.digitOnly) {
+            mTextIn.setKeyListener(StkDigitsKeyListener.getInstance());
+            inTypeId = R.string.digits;
+        }
+        inTypeView.setText(inTypeId);
+
+        if (mStkInput.icon != null) {
+            setFeatureDrawable(Window.FEATURE_LEFT_ICON, new BitmapDrawable(
+                    mStkInput.icon));
+        }
+
+        // Handle specific global and text attributes.
+        switch (mState) {
+        case STATE_TEXT:        
+            int maxLen = mStkInput.maxLen;
+            int minLen = mStkInput.minLen;
+            mTextIn.setFilters(new InputFilter[] {new InputFilter.LengthFilter(
+                    maxLen)});
+ 
+            // Set number of chars info.
+            String lengthLimit = String.valueOf(minLen);
+            if (maxLen != minLen) {
+                lengthLimit = minLen + " - " + maxLen;
+            }
+            numOfCharsView.setText(lengthLimit);
+
+            if (!mStkInput.echo) {
+                mTextIn.setTransformationMethod(PasswordTransformationMethod
+                        .getInstance());
+            }
+            // Set default text if present.
+            if (mStkInput.defaultText != null) {
+                mTextIn.setText(mStkInput.defaultText);
+            } else {
+                // make sure the text is cleared
+                mTextIn.setText("", BufferType.EDITABLE);
+            }
+
+            break;
+        case STATE_YES_NO:
+            // Set display mode - normal / yes-no layout
+            mYesNoLayout.setVisibility(View.VISIBLE);
+            mNormalLayout.setVisibility(View.GONE);
+            break;
+        }
+    }
+
+    private float getFontSizeFactor(FontSize size) {
+        final float[] fontSizes = 
+            {NORMAL_FONT_FACTOR, LARGE_FONT_FACTOR, SMALL_FONT_FACTOR};
+
+        return fontSizes[size.ordinal()];
+    }
+}
diff --git a/src/com/android/stk/StkLauncherActivity.java b/src/com/android/stk/StkLauncherActivity.java
new file mode 100644
index 0000000..df9fa24
--- /dev/null
+++ b/src/com/android/stk/StkLauncherActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Launcher class. Serve as the app's MAIN activity, send an intent to the 
+ * StkAppService and finish.
+ * 
+ */
+public class StkLauncherActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_LAUNCH_APP);
+        startService(new Intent(this, StkAppService.class).putExtras(args));
+
+        finish();
+    }
+}
diff --git a/src/com/android/stk/StkMenuActivity.java b/src/com/android/stk/StkMenuActivity.java
new file mode 100644
index 0000000..9b6d027
--- /dev/null
+++ b/src/com/android/stk/StkMenuActivity.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.telephony.gsm.stk.Item;
+import com.android.internal.telephony.gsm.stk.Menu;
+import com.android.internal.telephony.gsm.stk.StkLog;
+
+/**
+ * ListActivity used for displaying STK menus. These can be SET UP MENU and 
+ * SELECT ITEM menus. This activity is started multiple times with different 
+ * menu content.
+ *
+ */
+public class StkMenuActivity extends ListActivity {
+    private Context mContext;
+    private Menu mStkMenu = null;
+    private int mState = STATE_MAIN;
+    private boolean mAcceptUsersInput = true;
+
+    private TextView mTitleTextView = null;
+    private ImageView mTitleIconView = null;
+    private ProgressBar mProgressView = null;
+
+    StkAppService appService = StkAppService.getInstance();
+
+    // Internal state values
+    static final int STATE_MAIN = 1;
+    static final int STATE_SECONDARY = 2;
+
+    // message id for time out 
+    private static final int MSG_ID_TIMEOUT = 1;
+
+    Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+            case MSG_ID_TIMEOUT:
+                mAcceptUsersInput = false;
+                sendResponse(StkAppService.RES_ID_TIMEOUT);
+                break;
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        StkLog.d(this, "onCreate");
+        // Remove the default title, customized one is used.
+        requestWindowFeature(Window.FEATURE_NO_TITLE);  
+        // Set the layout for this activity.
+        setContentView(R.layout.stk_menu_list);
+
+        mTitleTextView = (TextView) findViewById(R.id.title_text);
+        mTitleIconView = (ImageView) findViewById(R.id.title_icon);
+        mProgressView = (ProgressBar) findViewById(R.id.progress_bar);
+        mContext = getBaseContext();
+
+        initFromIntent(getIntent());
+        mAcceptUsersInput = true;
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        StkLog.d(this, "onNewIntent");
+        initFromIntent(intent);
+        mAcceptUsersInput = true;
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+ 
+        if (!mAcceptUsersInput) {
+            return;
+        }
+
+        Item item = getSelectedItem(position);
+        if (item == null) {
+            return;
+        }
+        sendResponse(StkAppService.RES_ID_MENU_SELECTION, item.id, false);
+        mAcceptUsersInput = false;
+        mProgressView.setVisibility(View.VISIBLE);
+        mProgressView.setIndeterminate(true);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+
+        if (!mAcceptUsersInput) {
+            return true;
+        }
+
+        switch (keyCode) {
+        case KeyEvent.KEYCODE_BACK:
+            switch (mState) {
+            case STATE_SECONDARY:
+                cancelTimeOut();
+                mAcceptUsersInput = false;
+                sendResponse(StkAppService.RES_ID_BACKWARD);
+                return true;
+            case STATE_MAIN:
+                break;
+            }
+            break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        appService.indicateMenuVisibility(true);
+        mStkMenu = appService.getMenu();
+        if (mStkMenu == null) {
+            finish();
+            return;
+        }
+        displayMenu();
+        startTimeOut();
+        // whenever this activity is resumed after a sub activity was invoked
+        // (Browser, In call screen) switch back to main state and enable  
+        // user's input;
+        if (!mAcceptUsersInput) {
+            mState = STATE_MAIN;
+            mAcceptUsersInput = true;
+        }
+        // make sure the progress bar is not shown.
+        mProgressView.setIndeterminate(false);
+        mProgressView.setVisibility(View.GONE);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        appService.indicateMenuVisibility(false);
+        cancelTimeOut();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        StkLog.d(this, "onDestroy");
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(android.view.Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        menu.add(0, StkApp.MENU_ID_END_SESSION, 1, R.string.menu_end_session);
+        menu.add(0, StkApp.MENU_ID_HELP, 2, R.string.help);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(android.view.Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        boolean helpVisible = false;
+        boolean mainVisible = false;
+
+        if (mState == STATE_SECONDARY) {
+            mainVisible = true;
+        }
+        if (mStkMenu != null) {
+            helpVisible = mStkMenu.helpAvailable;
+        }
+
+        menu.findItem(StkApp.MENU_ID_END_SESSION).setVisible(mainVisible);
+        menu.findItem(StkApp.MENU_ID_HELP).setVisible(helpVisible);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (!mAcceptUsersInput) {
+            return true;
+        }
+        switch (item.getItemId()) {
+        case StkApp.MENU_ID_END_SESSION:
+            cancelTimeOut();
+            mAcceptUsersInput = false;
+            // send session end response.
+            sendResponse(StkAppService.RES_ID_END_SESSION);
+            return true;
+        case StkApp.MENU_ID_HELP:
+            cancelTimeOut();
+            mAcceptUsersInput = false;
+            int position = getSelectedItemPosition();
+            Item stkItem = getSelectedItem(position);
+            if (stkItem == null) {
+                break;
+            }
+            // send help needed response.
+            sendResponse(StkAppService.RES_ID_MENU_SELECTION, stkItem.id, true);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putInt("STATE", mState);
+        outState.putParcelable("MENU", mStkMenu);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        mState = savedInstanceState.getInt("STATE");
+        mStkMenu = savedInstanceState.getParcelable("MENU");
+    }
+    
+    private void cancelTimeOut() {
+        mTimeoutHandler.removeMessages(MSG_ID_TIMEOUT);
+    }
+
+    private void startTimeOut() {
+        if (mState == STATE_SECONDARY) {
+            // Reset timeout.
+            cancelTimeOut();
+            mTimeoutHandler.sendMessageDelayed(mTimeoutHandler
+                    .obtainMessage(MSG_ID_TIMEOUT), StkApp.UI_TIMEOUT);
+        }
+    }
+     
+    // Bind list adapter to the items list.
+    private void displayMenu() {
+
+        if (mStkMenu != null) {
+            // Display title & title icon
+            if (mStkMenu.titleIcon != null) {
+                mTitleIconView.setImageBitmap(mStkMenu.titleIcon);
+                mTitleIconView.setVisibility(View.VISIBLE);
+            } else {
+                mTitleIconView.setVisibility(View.GONE);
+            }
+            if (!mStkMenu.titleIconSelfExplanatory) {
+                mTitleTextView.setVisibility(View.VISIBLE);
+                if (mStkMenu.title == null) {
+                    mTitleTextView.setText(R.string.app_name);
+                } else {
+                    mTitleTextView.setText(mStkMenu.title);
+                }
+            } else {
+                mTitleTextView.setVisibility(View.INVISIBLE);
+            }
+            // create an array adapter for the menu list
+            StkMenuAdapter adapter = new StkMenuAdapter(this,
+                    mStkMenu.items, mStkMenu.itemsIconSelfExplanatory);
+            // Bind menu list to the new adapter.
+            setListAdapter(adapter);
+            // Set default item
+            setSelection(mStkMenu.defaultItem);
+        }
+    }
+
+    private void initFromIntent(Intent intent) {
+
+        if (intent != null) {
+            mState = intent.getIntExtra("STATE", STATE_MAIN);
+        } else {
+            finish();
+        }
+    }
+
+    private Item getSelectedItem(int position) {
+        Item item = null;
+        if (mStkMenu != null) {
+            try {
+                item = mStkMenu.items.get(position);
+            } catch (IndexOutOfBoundsException e) {
+                if (StkApp.DBG) {
+                    StkLog.d(this, "Invalid menu");
+                }
+            } catch (NullPointerException e) {
+                if (StkApp.DBG) {
+                    StkLog.d(this, "Invalid menu");
+                }
+            }
+        }
+        return item;
+    }
+
+    private void sendResponse(int resId) {
+        sendResponse(resId, 0, false);
+    }
+
+    private void sendResponse(int resId, int itemId, boolean help) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
+        args.putInt(StkAppService.RES_ID, resId);
+        args.putInt(StkAppService.MENU_SELECTION, itemId);
+        args.putBoolean(StkAppService.HELP, help);
+        mContext.startService(new Intent(mContext, StkAppService.class)
+                .putExtras(args));
+    }
+}
diff --git a/src/com/android/stk/StkMenuAdapter.java b/src/com/android/stk/StkMenuAdapter.java
new file mode 100644
index 0000000..253b39a
--- /dev/null
+++ b/src/com/android/stk/StkMenuAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import com.android.internal.telephony.gsm.stk.Item;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Icon list view adapter to show the list of STK items.
+ */
+public class StkMenuAdapter extends ArrayAdapter<Item> {
+    private final LayoutInflater mInflater;
+    private boolean mIcosSelfExplanatory = false;
+
+    public StkMenuAdapter(Context context, List<Item> items,
+            boolean icosSelfExplanatory) {
+        super(context, 0, items);
+        mInflater = LayoutInflater.from(context);
+        mIcosSelfExplanatory = icosSelfExplanatory;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final Item item = getItem(position);
+
+        if (convertView == null) {
+            convertView = mInflater.inflate(R.layout.stk_menu_item, parent,
+                    false);
+        }
+
+        if (!mIcosSelfExplanatory || (mIcosSelfExplanatory && item.icon == null)) {
+            ((TextView) convertView.findViewById(R.id.text)).setText(item.text);
+        }
+        ImageView imageView = ((ImageView) convertView.findViewById(R.id.icon));
+        if (item.icon == null) {
+            imageView.setVisibility(View.GONE);
+        } else {
+            imageView.setImageBitmap(item.icon);
+            imageView.setVisibility(View.VISIBLE);
+        }
+
+        return convertView;
+    }
+}
diff --git a/src/com/android/stk/ToneDialog.java b/src/com/android/stk/ToneDialog.java
new file mode 100644
index 0000000..9cf4295
--- /dev/null
+++ b/src/com/android/stk/ToneDialog.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Vibrator;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.telephony.gsm.stk.TextMessage;
+import com.android.internal.telephony.gsm.stk.ToneSettings;
+
+/**
+ * Activity used for PLAY TONE command.
+ * 
+ */
+public class ToneDialog extends Activity {
+    TextMessage toneMsg = null;
+    ToneSettings settings = null;
+    TonePlayer player = null;
+
+    /**
+     * Handler used to stop tones from playing when the duration ends.
+     */
+    Handler mToneStopper = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MSG_ID_STOP_TONE:
+                sendResponse(StkAppService.RES_ID_DONE);
+                finish();
+                break;
+            }
+        }
+    };
+
+    Vibrator mVibrator = new Vibrator();
+
+    // Message id to signal tone duration timeout.
+    private static final int MSG_ID_STOP_TONE = 0xda;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+       initFromIntent(getIntent());
+
+        // remove window title
+        View title = findViewById(com.android.internal.R.id.title);
+        title.setVisibility(View.GONE);
+        // set customized content view
+        setContentView(R.layout.stk_tone_dialog);
+
+        TextView tv = (TextView) findViewById(R.id.message);
+        ImageView iv = (ImageView) findViewById(R.id.icon);
+
+        // set text and icon
+        tv.setText(toneMsg.text);
+        if (toneMsg.icon == null) {
+            iv.setImageResource(com.android.internal.R.drawable.ic_volume);
+        } else {
+            iv.setImageBitmap(toneMsg.icon);
+        }
+
+        // Start playing tone and vibration
+        player = new TonePlayer();
+        player.play(settings.tone);
+        int timeout = StkApp.calculateDurationInMilis(settings.duration);
+        if (timeout == 0) {
+            timeout = StkApp.TONE_DFEAULT_TIMEOUT;
+        }
+        mToneStopper.sendEmptyMessageDelayed(MSG_ID_STOP_TONE, timeout);
+        if (settings.vibrate) {
+            mVibrator.vibrate(timeout);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        mToneStopper.removeMessages(MSG_ID_STOP_TONE);
+        player.stop();
+        player.release();
+        mVibrator.cancel();
+    }
+    
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+        case KeyEvent.KEYCODE_BACK:
+            sendResponse(StkAppService.RES_ID_END_SESSION);
+            finish();
+            break;
+        }
+        return false;
+    }
+
+    private void initFromIntent(Intent intent) {
+        if (intent == null) {
+            finish();
+        }
+        toneMsg = intent.getParcelableExtra("TEXT");
+        settings = intent.getParcelableExtra("TONE");
+    }
+
+    private void sendResponse(int resId) {
+        Bundle args = new Bundle();
+        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
+        args.putInt(StkAppService.RES_ID, resId);
+        startService(new Intent(this, StkAppService.class).putExtras(args));
+    }
+}
diff --git a/src/com/android/stk/TonePlayer.java b/src/com/android/stk/TonePlayer.java
new file mode 100644
index 0000000..00e859d
--- /dev/null
+++ b/src/com/android/stk/TonePlayer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stk;
+
+import java.util.HashMap;
+
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import com.android.internal.telephony.gsm.stk.Tone;
+
+/**
+ * Class that implements a tones player for the SIM toolkit application.
+ * 
+ */
+public class TonePlayer {
+    private static final HashMap<Tone, Integer> mToneMap = new HashMap<Tone, Integer>();
+
+    static {
+        // Map STK tone ids to the system tone ids.
+        mToneMap.put(Tone.DIAL, ToneGenerator.TONE_SUP_DIAL);
+        mToneMap.put(Tone.BUSY, ToneGenerator.TONE_SUP_BUSY);
+        mToneMap.put(Tone.CONGESTION, ToneGenerator.TONE_SUP_CONGESTION);
+        mToneMap.put(Tone.RADIO_PATH_ACK, ToneGenerator.TONE_SUP_RADIO_ACK);
+        mToneMap.put(Tone.RADIO_PATH_NOT_AVAILABLE, ToneGenerator.TONE_SUP_RADIO_NOTAVAIL);
+        mToneMap.put(Tone.ERROR_SPECIAL_INFO, ToneGenerator.TONE_SUP_ERROR);
+        mToneMap.put(Tone.CALL_WAITING, ToneGenerator.TONE_SUP_CALL_WAITING);
+        mToneMap.put(Tone.RINGING, ToneGenerator.TONE_SUP_RINGTONE);
+        mToneMap.put(Tone.GENERAL_BEEP, ToneGenerator.TONE_PROP_BEEP);
+        mToneMap.put(Tone.POSITIVE_ACK, ToneGenerator.TONE_PROP_ACK);
+        mToneMap.put(Tone.NEGATIVE_ACK, ToneGenerator.TONE_PROP_NACK);
+    }
+
+    private ToneGenerator mToneGenerator = null;
+
+    TonePlayer() {
+        mToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, 100);
+    }
+
+    public void play(Tone tone) {
+        int toneId = getToneId(tone);
+        if (toneId > 0 && mToneGenerator != null) {
+            mToneGenerator.startTone(toneId);
+        }
+    }
+
+    public void stop() {
+        if (mToneGenerator != null) {
+            mToneGenerator.stopTone();
+        }
+    }
+
+    public void release() {
+        mToneGenerator.release();
+    }
+
+    private int getToneId(Tone tone) {
+        int toneId = ToneGenerator.TONE_PROP_BEEP;
+
+        if (tone != null && mToneMap.containsKey(tone)) {
+            toneId = mToneMap.get(tone);
+        }
+        return toneId;
+    }
+}