Merge "Fix terminal response not sent during sim hotswap."
diff --git a/OWNERS b/OWNERS
index cb08ba3..46b828f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,6 @@
 amitmahajan@google.com
 rgreenwalt@google.com
-sanketpadawe@google.com
 jminjie@google.com
+hallliu@google.com
+paulye@google.com
+breadley@google.com
diff --git a/src/com/android/stk/StkAppService.java b/src/com/android/stk/StkAppService.java
index 97fcd6c..055cbec 100644
--- a/src/com/android/stk/StkAppService.java
+++ b/src/com/android/stk/StkAppService.java
@@ -29,6 +29,7 @@
 import android.app.ActivityManagerNative;
 import android.app.IProcessObserver;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -90,8 +91,8 @@
 
 import java.util.Iterator;
 import java.util.LinkedList;
-import java.lang.System;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import static com.android.internal.telephony.cat.CatCmdMessage.
                    SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
@@ -138,6 +139,8 @@
         private CatCmdMessage mCurrentSetupEventCmd = null;
         private CatCmdMessage mIdleModeTextCmd = null;
         private boolean mIdleModeTextVisible = false;
+        // Determins whether the current session was initiated by user operation.
+        protected boolean mIsSessionFromUser = false;
         final synchronized void setPendingActivityInstance(Activity act) {
             CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act);
             callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act);
@@ -634,20 +637,44 @@
             case OP_SET_ACT_INST:
                 Activity act = (Activity) msg.obj;
                 if (mStkContext[slotId].mActivityInstance != act) {
-                    CatLog.d(LOG_TAG, "Set activity instance - " + act);
+                    CatLog.d(LOG_TAG, "Set pending activity instance - " + act);
                     Activity previous = mStkContext[slotId].mActivityInstance;
                     mStkContext[slotId].mActivityInstance = act;
-                    // Finish the previous one if it has not been finished yet somehow.
-                    if (previous != null && !previous.isDestroyed() && !previous.isFinishing()) {
+                    // Finish the previous one if it was replaced with new one
+                    // but it has not been finished yet somehow.
+                    if (act != null && previous != null && !previous.isDestroyed()
+                            && !previous.isFinishing()) {
                         CatLog.d(LOG_TAG, "Finish the previous pending activity - " + previous);
                         previous.finish();
                     }
+                    // Pending activity is registered in the following 2 scnarios;
+                    // A. TERMINAL RESPONSE was sent to the card.
+                    // B. Activity was moved to the background before TR is sent to the card.
+                    // No need to observe idle screen for the pending activity in the scenario A.
+                    if (act != null && mStkContext[slotId].mCmdInProgress) {
+                        startToObserveIdleScreen(slotId);
+                    } else {
+                        if (mStkContext[slotId].mCurrentCmd != null) {
+                            unregisterProcessObserver(
+                                    mStkContext[slotId].mCurrentCmd.getCmdType(), slotId);
+                        }
+                    }
                 }
                 break;
             case OP_SET_DAL_INST:
                 Activity dal = (Activity) msg.obj;
-                CatLog.d(LOG_TAG, "Set dialog instance. " + dal);
-                mStkContext[slotId].mDialogInstance = dal;
+                if (mStkContext[slotId].mDialogInstance != dal) {
+                    CatLog.d(LOG_TAG, "Set pending dialog instance - " + dal);
+                    mStkContext[slotId].mDialogInstance = dal;
+                    if (dal != null) {
+                        startToObserveIdleScreen(slotId);
+                    } else {
+                        if (mStkContext[slotId].mCurrentCmd != null) {
+                            unregisterProcessObserver(
+                                    mStkContext[slotId].mCurrentCmd.getCmdType(), slotId);
+                        }
+                    }
+                }
                 break;
             case OP_SET_IMMED_DAL_INST:
                 Activity immedDal = (Activity) msg.obj;
@@ -711,15 +738,6 @@
                     // Clear Idle Text
                     cancelIdleText(slotId);
                 }
-
-                if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
-                    // Uninstall STkmenu
-                    if (isAllOtherCardsAbsent(slotId)) {
-                        StkAppInstaller.unInstall(mContext);
-                    }
-                    mStkContext[slotId].mCurrentMenu = null;
-                    mStkContext[slotId].mMainCmd = null;
-                }
             }
         }
     }
@@ -770,8 +788,36 @@
         return false;
     }
 
-    private void handleIdleScreen(int slotId) {
+    private void startToObserveIdleScreen(int slotId) {
+        if (!mStkContext[slotId].mIsSessionFromUser) {
+            if (!isScreenIdle()) {
+                synchronized (this) {
+                    if (mProcessObserver == null && !mServiceHandler.hasMessages(OP_IDLE_SCREEN)) {
+                        registerProcessObserver();
+                    }
+                }
+            } else {
+                handleIdleScreen(slotId);
+            }
+        }
+    }
 
+    private void handleIdleScreen(int slotId) {
+        // It might be hard for user to recognize that the dialog or screens belong to SIM Toolkit
+        // application if the current session was not initiated by user but by the SIM card,
+        // so it is recommended to send TERMINAL RESPONSE if user goes to the idle screen.
+        if (!mStkContext[slotId].mIsSessionFromUser) {
+            Activity dialog = mStkContext[slotId].getPendingDialogInstance();
+            if (dialog != null) {
+                dialog.finish();
+                mStkContext[slotId].mDialogInstance = null;
+            }
+            Activity activity = mStkContext[slotId].getPendingActivityInstance();
+            if (activity != null) {
+                activity.finish();
+                mStkContext[slotId].mActivityInstance = null;
+            }
+        }
         // If the idle screen event is present in the list need to send the
         // response to SIM.
         CatLog.d(this, "Need to send IDLE SCREEN Available event to SIM");
@@ -798,6 +844,20 @@
         }
     }
 
+    /**
+     * Sends TERMINAL RESPONSE or ENVELOPE
+     *
+     * @param args detailed parameters of the response
+     * @param slotId slot identifier
+     */
+    public void sendResponse(Bundle args, int slotId) {
+        Message msg = mServiceHandler.obtainMessage();
+        msg.arg1 = OP_RESPONSE;
+        msg.arg2 = slotId;
+        msg.obj = args;
+        mServiceHandler.sendMessage(msg);
+    }
+
     private void sendResponse(int resId, int slotId, boolean confirm) {
         Message msg = mServiceHandler.obtainMessage();
         msg.arg1 = OP_RESPONSE;
@@ -883,6 +943,7 @@
             CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]");
         }
         mStkContext[slotId].lastSelectedItem = null;
+        mStkContext[slotId].mIsSessionFromUser = false;
         // In case of SET UP MENU command which removed the app, don't
         // update the current menu member.
         if (mStkContext[slotId].mCurrentMenu != null && mStkContext[slotId].mMainCmd != null) {
@@ -1153,6 +1214,7 @@
         }
     }
 
+    @SuppressWarnings("FallThrough")
     private void handleCmdResponse(Bundle args, int slotId) {
         CatLog.d(LOG_TAG, "handleCmdResponse, sim id: " + slotId);
         if (mStkContext[slotId].mCurrentCmd == null) {
@@ -1182,6 +1244,8 @@
             int menuSelection = args.getInt(MENU_SELECTION);
             switch(mStkContext[slotId].mCurrentMenuCmd.getCmdType()) {
             case SET_UP_MENU:
+                mStkContext[slotId].mIsSessionFromUser = true;
+                // Fall through
             case SELECT_ITEM:
                 mStkContext[slotId].lastSelectedItem = getItemName(menuSelection, slotId);
                 if (helpRequired) {
@@ -1624,19 +1688,23 @@
         return getNotificationId(slotId) + (notificationType * mSimCount);
     }
 
-    public boolean isStkDialogActivated(Context context) {
-        String stkDialogActivity = "com.android.stk.StkDialogActivity";
-        boolean activated = false;
-        final ActivityManager am = (ActivityManager) context.getSystemService(
-                Context.ACTIVITY_SERVICE);
-        String topActivity = am.getRunningTasks(1).get(0).topActivity.getClassName();
-
-        CatLog.d(LOG_TAG, "isStkDialogActivated: " + topActivity);
-        if (topActivity.equals(stkDialogActivity)) {
-            activated = true;
+    /**
+     * Checks whether the dialog exists as the top activity of this task.
+     *
+     * @return true if the top activity of this task is the dialog.
+     */
+    public boolean isStkDialogActivated() {
+        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        ComponentName componentName = am.getAppTasks().get(0).getTaskInfo().topActivity;
+        if (componentName != null) {
+            String[] split = componentName.getClassName().split(Pattern.quote("."));
+            String topActivity = split[split.length - 1];
+            CatLog.d(LOG_TAG, "Top activity: " + topActivity);
+            if (TextUtils.equals(topActivity, StkDialogActivity.class.getSimpleName())) {
+                return true;
+            }
         }
-        CatLog.d(LOG_TAG, "activated : " + activated);
-        return activated;
+        return false;
     }
 
     private void replaceEventList(int slotId) {
@@ -1766,6 +1834,7 @@
                     }
                 };
                 ActivityManagerNative.getDefault().registerProcessObserver(observer);
+                CatLog.d(this, "Started to observe the foreground activity");
                 mProcessObserver = observer;
             } catch (RemoteException e) {
                 CatLog.d(this, "Failed to register the process observer");
@@ -1803,6 +1872,7 @@
         if (mProcessObserver != null) {
             try {
                 ActivityManagerNative.getDefault().unregisterProcessObserver(mProcessObserver);
+                CatLog.d(this, "Stopped to observe the foreground activity");
                 mProcessObserver = null;
             } catch (RemoteException e) {
                 CatLog.d(this, "Failed to unregister the process observer");
diff --git a/src/com/android/stk/StkDialogActivity.java b/src/com/android/stk/StkDialogActivity.java
index 3503134..1462480 100644
--- a/src/com/android/stk/StkDialogActivity.java
+++ b/src/com/android/stk/StkDialogActivity.java
@@ -48,6 +48,8 @@
     private StkAppService appService = StkAppService.getInstance();
     // Determines whether Terminal Response (TR) has been sent
     private boolean mIsResponseSent = false;
+    // Determines whether this is in the pending state.
+    private boolean mIsPending = false;
     // Utilize AlarmManager for real-time countdown
     private static final String DIALOG_ALARM_TAG = LOG_TAG;
     private static final long NO_DIALOG_ALARM = -1;
@@ -58,6 +60,8 @@
     private static final String ALARM_TIME_KEY = "alarm_time";
     private static final String RESPONSE_SENT_KEY = "response_sent";
     private static final String SLOT_ID_KEY = "slotid";
+    private static final String PENDING = "pending";
+
 
     private AlertDialog mAlertDialog;
 
@@ -85,9 +89,7 @@
                     @Override
                     public void onClick(DialogInterface dialog, int id) {
                         CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId);
-                        cancelTimeOut();
                         sendResponse(StkAppService.RES_ID_CONFIRM, true);
-                        finish();
                     }
                 });
 
@@ -96,9 +98,7 @@
                     @Override
                     public void onClick(DialogInterface dialog,int id) {
                         CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId);
-                        cancelTimeOut();
                         sendResponse(StkAppService.RES_ID_CONFIRM, false);
-                        finish();
                     }
                 });
 
@@ -106,9 +106,7 @@
                     @Override
                     public void onCancel(DialogInterface dialog) {
                         CatLog.d(LOG_TAG, "Moving backward!, mSlotId: " + mSlotId);
-                        cancelTimeOut();
                         sendResponse(StkAppService.RES_ID_BACKWARD);
-                        finish();
                     }
                 });
 
@@ -160,6 +158,8 @@
         super.onResume();
         CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
                 "], sim id: " + mSlotId);
+        // The pending dialog is unregistered if this instance was registered as it before.
+        setPendingState(false);
 
         /*
          * If the userClear flag is set and dialogduration is set to 0, the display Text
@@ -216,23 +216,13 @@
         CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" +
                 mIsResponseSent + "], sim id: " + mSlotId);
 
-        // Avoid calling finish() or setPendingDialogInstance()
-        // if the activity is being restarted now.
-        if (isChangingConfigurations()) {
+        // Nothing should be done here if this activity is being finished or restarted now.
+        if (isFinishing() || isChangingConfigurations()) {
             return;
         }
 
-        if (!mTextMsg.responseNeeded) {
-            return;
-        }
-        if (!mIsResponseSent) {
-            appService.getStkContext(mSlotId).setPendingDialogInstance(this);
-        } else {
-            CatLog.d(LOG_TAG, "finish.");
-            appService.getStkContext(mSlotId).setPendingDialogInstance(null);
-            cancelTimeOut();
-            finish();
-        }
+        // This is registered as the pending dialog as this was sent to the background.
+        setPendingState(true);
     }
 
     @Override
@@ -270,6 +260,7 @@
         outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent);
         outState.putLong(ALARM_TIME_KEY, mAlarmTime);
         outState.putInt(SLOT_ID_KEY, mSlotId);
+        outState.putBoolean(PENDING, mIsPending);
     }
 
     @Override
@@ -282,7 +273,11 @@
         mIsResponseSent = savedInstanceState.getBoolean(RESPONSE_SENT_KEY);
         mAlarmTime = savedInstanceState.getLong(ALARM_TIME_KEY, NO_DIALOG_ALARM);
         mSlotId = savedInstanceState.getInt(SLOT_ID_KEY);
-        appService.getStkContext(mSlotId).setPendingDialogInstance(this);
+
+        // The pending dialog must be replaced if the previous instance was in the pending state.
+        if (savedInstanceState.getBoolean(PENDING)) {
+            setPendingState(true);
+        }
 
         if (mAlarmTime != NO_DIALOG_ALARM) {
             startTimeOut();
@@ -308,7 +303,18 @@
         }
     }
 
+    private void setPendingState(boolean on) {
+        if (mTextMsg.responseNeeded) {
+            if (mIsPending != on) {
+                appService.getStkContext(mSlotId).setPendingDialogInstance(on ? this : null);
+                mIsPending = on;
+            }
+        }
+    }
+
     private void sendResponse(int resId, boolean confirmed) {
+        cancelTimeOut();
+
         if (mSlotId == -1) {
             CatLog.d(LOG_TAG, "sim id is invalid");
             return;
@@ -330,6 +336,10 @@
             startService(new Intent(this, StkAppService.class).putExtras(args));
             mIsResponseSent = true;
         }
+        if (!isFinishing()) {
+            finish();
+        }
+
     }
 
     private void sendResponse(int resId) {
@@ -391,7 +401,6 @@
                     CatLog.d(LOG_TAG, "The alarm time is reached");
                     mAlarmTime = NO_DIALOG_ALARM;
                     sendResponse(StkAppService.RES_ID_TIMEOUT);
-                    finish();
                 }
             };
 }
diff --git a/src/com/android/stk/StkInputActivity.java b/src/com/android/stk/StkInputActivity.java
index fa688c7..26ae4ce 100644
--- a/src/com/android/stk/StkInputActivity.java
+++ b/src/com/android/stk/StkInputActivity.java
@@ -33,6 +33,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.WindowManager;
 import android.view.inputmethod.EditorInfo;
 import android.widget.Button;
 import android.widget.EditText;
@@ -80,6 +81,7 @@
     private static final String RESPONSE_SENT_KEY = "response_sent";
     private static final String INPUT_STRING_KEY = "input_string";
     private static final String ALARM_TIME_KEY = "alarm_time";
+    private static final String PENDING = "pending";
 
     private static final String INPUT_ALARM_TAG = LOG_TAG;
     private static final long NO_INPUT_ALARM = -1;
@@ -88,6 +90,8 @@
     private StkAppService appService = StkAppService.getInstance();
 
     private boolean mIsResponseSent = false;
+    // Determines whether this is in the pending state.
+    private boolean mIsPending = false;
     private int mSlotId = -1;
 
     // Click listener to handle buttons press..
@@ -108,8 +112,8 @@
             input = mTextIn.getText().toString();
             break;
         case R.id.button_cancel:
-            appService.getStkContext(mSlotId).setPendingActivityInstance(this);
             sendResponse(StkAppService.RES_ID_END_SESSION);
+            finish();
             return;
         // Yes/No layout buttons.
         case R.id.button_yes:
@@ -142,7 +146,6 @@
             break;
         }
         CatLog.d(LOG_TAG, "handleClick, ready to response");
-        appService.getStkContext(mSlotId).setPendingActivityInstance(this);
         sendResponse(StkAppService.RES_ID_INPUT, input, false);
     }
 
@@ -208,6 +211,11 @@
         super.onResume();
         CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
                 "], slot id: " + mSlotId);
+        // If the terminal has already sent response to the card when this activity is resumed,
+        // keep this as a pending activity as this should be finished when the session ends.
+        if (!mIsResponseSent) {
+            setPendingState(false);
+        }
 
         if (mAlarmTime == NO_INPUT_ALARM) {
             startTimeOut();
@@ -228,17 +236,21 @@
         super.onStop();
         CatLog.d(LOG_TAG, "onStop - mIsResponseSent[" + mIsResponseSent + "]");
 
-        // Nothing should be done here if this activity is being restarted now.
-        if (isChangingConfigurations()) {
+        // Nothing should be done here if this activity is being finished or restarted now.
+        if (isFinishing() || isChangingConfigurations()) {
             return;
         }
 
-        // It is unnecessary to keep this activity if the response was already sent and
-        // this got invisible because of the other full-screen activity in this application.
-        if (mIsResponseSent && appService.isTopOfStack()) {
-            finish();
+        if (mIsResponseSent) {
+            // It is unnecessary to keep this activity if the response was already sent and
+            // the dialog activity is NOT on the top of this activity.
+            if (!appService.isStkDialogActivated()) {
+                finish();
+            }
         } else {
-            appService.getStkContext(mSlotId).setPendingActivityInstance(this);
+            // This should be registered as the pending activity here
+            // only when no response has been sent back to the card.
+            setPendingState(true);
         }
     }
 
@@ -282,7 +294,6 @@
         switch (keyCode) {
         case KeyEvent.KEYCODE_BACK:
             CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK");
-            appService.getStkContext(mSlotId).setPendingActivityInstance(this);
             sendResponse(StkAppService.RES_ID_BACKWARD, null, false);
             return true;
         }
@@ -314,14 +325,17 @@
                 + help + "]");
         mIsResponseSent = true;
         Bundle args = new Bundle();
-        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
-        args.putInt(StkAppService.SLOT_ID, mSlotId);
         args.putInt(StkAppService.RES_ID, resId);
         if (input != null) {
             args.putString(StkAppService.INPUT, input);
         }
         args.putBoolean(StkAppService.HELP, help);
-        startService(new Intent(this, StkAppService.class).putExtras(args));
+        appService.sendResponse(args, mSlotId);
+
+        // This instance should be set as a pending activity and finished by the service
+        if (resId != StkAppService.RES_ID_END_SESSION) {
+            setPendingState(true);
+        }
     }
 
     @Override
@@ -368,7 +382,6 @@
             return true;
         case StkApp.MENU_ID_HELP:
             sendResponse(StkAppService.RES_ID_INPUT, "", true);
-            finish();
             return true;
         }
         return false;
@@ -380,6 +393,7 @@
         outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent);
         outState.putString(INPUT_STRING_KEY, mTextIn.getText().toString());
         outState.putLong(ALARM_TIME_KEY, mAlarmTime);
+        outState.putBoolean(PENDING, mIsPending);
     }
 
     @Override
@@ -400,6 +414,22 @@
         if (mAlarmTime != NO_INPUT_ALARM) {
             startTimeOut();
         }
+
+        if (!mIsResponseSent && !savedInstanceState.getBoolean(PENDING)) {
+            // If this is in the foreground and no response has been sent to the card,
+            // this must not be registered as pending activity by the previous instance.
+            // No need to renew nor clear pending activity in this case.
+        } else {
+            // Renew the instance of the pending activity.
+            setPendingState(true);
+        }
+    }
+
+    private void setPendingState(boolean on) {
+        if (mIsPending != on) {
+            appService.getStkContext(mSlotId).setPendingActivityInstance(on ? this : null);
+            mIsPending = on;
+        }
     }
 
     public void beforeTextChanged(CharSequence s, int start, int count,
@@ -501,6 +531,10 @@
                         .getInstance());
             }
             mTextIn.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN);
+            // Request the initial focus on the edit box and show the software keyboard.
+            mTextIn.requestFocus();
+            getWindow().setSoftInputMode(
+                    WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
             // Set default text if present.
             if (mStkInput.defaultText != null) {
                 mTextIn.setText(mStkInput.defaultText);
@@ -544,8 +578,6 @@
                 public void onAlarm() {
                     CatLog.d(LOG_TAG, "The alarm time is reached");
                     mAlarmTime = NO_INPUT_ALARM;
-                    appService.getStkContext(mSlotId).setPendingActivityInstance(
-                            StkInputActivity.this);
                     sendResponse(StkAppService.RES_ID_TIMEOUT);
                 }
             };
diff --git a/src/com/android/stk/StkMenuActivity.java b/src/com/android/stk/StkMenuActivity.java
index 87884db..bc62e5e 100644
--- a/src/com/android/stk/StkMenuActivity.java
+++ b/src/com/android/stk/StkMenuActivity.java
@@ -54,6 +54,8 @@
     private boolean mAcceptUsersInput = true;
     private int mSlotId = -1;
     private boolean mIsResponseSent = false;
+    // Determines whether this is in the pending state.
+    private boolean mIsPending = false;
 
     private TextView mTitleTextView = null;
     private ImageView mTitleIconView = null;
@@ -69,6 +71,7 @@
     private static final String ACCEPT_USERS_INPUT_KEY = "accept_users_input";
     private static final String RESPONSE_SENT_KEY = "response_sent";
     private static final String ALARM_TIME_KEY = "alarm_time";
+    private static final String PENDING = "pending";
 
     private static final String SELECT_ALARM_TAG = LOG_TAG;
     private static final long NO_SELECT_ALARM = -1;
@@ -131,10 +134,6 @@
         }
 
         CatLog.d(LOG_TAG, "onListItemClick Id: " + item.id + ", mState: " + mState);
-        // ONLY set SECONDARY menu. It will be finished when the following command is comming.
-        if (mState == STATE_SECONDARY) {
-            appService.getStkContext(mSlotId).setPendingActivityInstance(this);
-        }
         sendResponse(StkAppService.RES_ID_MENU_SELECTION, item.id, false);
         invalidateOptionsMenu();
     }
@@ -152,7 +151,6 @@
             switch (mState) {
             case STATE_SECONDARY:
                 CatLog.d(LOG_TAG, "STATE_SECONDARY");
-                appService.getStkContext(mSlotId).setPendingActivityInstance(this);
                 sendResponse(StkAppService.RES_ID_BACKWARD);
                 return true;
             case STATE_MAIN:
@@ -166,12 +164,6 @@
     }
 
     @Override
-    public void onRestart() {
-        super.onRestart();
-        CatLog.d(LOG_TAG, "onRestart, slot id: " + mSlotId);
-    }
-
-    @Override
     public void onResume() {
         super.onResume();
 
@@ -190,6 +182,11 @@
         }
         displayMenu();
 
+        // If the terminal has already sent response to the card when this activity is resumed,
+        // keep this as a pending activity as this should be finished when the session ends.
+        if (!mIsResponseSent) {
+            setPendingState(false);
+        }
         if (mAlarmTime == NO_SELECT_ALARM) {
             startTimeOut();
         }
@@ -225,35 +222,21 @@
         super.onStop();
         CatLog.d(LOG_TAG, "onStop, slot id: " + mSlotId + "," + mIsResponseSent + "," + mState);
 
-        // Nothing should be done here if this activity is being restarted now.
-        if (isChangingConfigurations()) {
+        // Nothing should be done here if this activity is being finished or restarted now.
+        if (isFinishing() || isChangingConfigurations()) {
             return;
         }
 
-        //The menu should stay in background, if
-        //1. the dialog is pop up in the screen, but the user does not response to the dialog.
-        //2. the menu activity enters Stop state (e.g pressing HOME key) but mIsResponseSent is false.
         if (mIsResponseSent) {
-            // ONLY finish SECONDARY menu. MAIN menu should always stay in the root of stack.
-            if (mState == STATE_SECONDARY) {
-                if (!appService.isStkDialogActivated(this)) {
-                    CatLog.d(LOG_TAG, "STATE_SECONDARY finish.");
-                    cancelTimeOut();//To avoid the timer time out and send TR again.
-                    finish();
-                } else {
-                     if (appService != null) {
-                         appService.getStkContext(mSlotId).setPendingActivityInstance(this);
-                     }
-                }
+            // It is unnecessary to keep this activity if the response was already sent and
+            // the dialog activity is NOT on the top of this activity.
+            if (mState == STATE_SECONDARY && !appService.isStkDialogActivated()) {
+                finish();
             }
         } else {
-            if (appService != null) {
-                if (mState == STATE_SECONDARY) {
-                    appService.getStkContext(mSlotId).setPendingActivityInstance(this);
-                }
-            } else {
-                CatLog.d(LOG_TAG, "onStop: null appService.");
-            }
+            // This instance should be registered as the pending activity here
+            // only when no response has been sent back to the card.
+            setPendingState(true);
         }
     }
 
@@ -363,6 +346,7 @@
         outState.putBoolean(ACCEPT_USERS_INPUT_KEY, mAcceptUsersInput);
         outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent);
         outState.putLong(ALARM_TIME_KEY, mAlarmTime);
+        outState.putBoolean(PENDING, mIsPending);
     }
 
     @Override
@@ -385,6 +369,24 @@
         if (mAlarmTime != NO_SELECT_ALARM) {
             startTimeOut();
         }
+
+        if (!mIsResponseSent && !savedInstanceState.getBoolean(PENDING)) {
+            // If this is in the foreground and no response has been sent to the card,
+            // this must not be registered as pending activity by the previous instance.
+            // No need to renew nor clear pending activity in this case.
+        } else {
+            // Renew the instance of the pending activity.
+            setPendingState(true);
+        }
+    }
+
+    private void setPendingState(boolean on) {
+        if (mState == STATE_SECONDARY) {
+            if (mIsPending != on) {
+                appService.getStkContext(mSlotId).setPendingActivityInstance(on ? this : null);
+                mIsPending = on;
+            }
+        }
     }
 
     private void cancelTimeOut() {
@@ -498,12 +500,15 @@
 
         mIsResponseSent = true;
         Bundle args = new Bundle();
-        args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
-        args.putInt(StkAppService.SLOT_ID, mSlotId);
         args.putInt(StkAppService.RES_ID, resId);
         args.putInt(StkAppService.MENU_SELECTION, itemId);
         args.putBoolean(StkAppService.HELP, help);
-        startService(new Intent(this, StkAppService.class).putExtras(args));
+        appService.sendResponse(args, mSlotId);
+
+        // This instance should be set as a pending activity and finished by the service.
+        if (resId != StkAppService.RES_ID_END_SESSION) {
+            setPendingState(true);
+        }
     }
 
     private final BroadcastReceiver mLocalBroadcastReceiver = new BroadcastReceiver() {
@@ -525,8 +530,6 @@
                 public void onAlarm() {
                     CatLog.d(LOG_TAG, "The alarm time is reached");
                     mAlarmTime = NO_SELECT_ALARM;
-                    appService.getStkContext(mSlotId).setPendingActivityInstance(
-                            StkMenuActivity.this);
                     sendResponse(StkAppService.RES_ID_TIMEOUT);
                 }
             };