Fix dialog handling.

* Changes
  + VpnSettings:
    + Add reconnect dialog to showDialog() framework for handling screen
      rotation.
    + Dismiss other alert dialogs in onDestroy()
    + Remove onPrepareDialog() from VpnSettings.
    + Remove updateConnectDialog() from VpnProfileActor.
    + Add authentication error dialog.
    + Add unknown server dialog.
  + SecuritySettings:
    + Make cstor dialogs cancelable.
    + Add cancelable listener.
  Patch Set 6:
  + VpnSettings:
    + Disable preferences when connecting/disconnecting.
    + Fix state broadcast when binding a VpnService goes wrong.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dd7ba17..f7d76a4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1850,6 +1850,8 @@
     <string name="vpn_confirm_add_profile_cancellation">Are you sure you don\'t want to create this profile?</string>
     <string name="vpn_confirm_edit_profile_cancellation">Are you sure you want to discard the changes made to this profile?</string>
     <string name="vpn_confirm_reconnect">Unable to connect to the network. Do you want to try again?</string>
+    <string name="vpn_unknown_server_dialog_msg">Server name cannot be resolved. Do you want to check your server name setting?</string>
+    <string name="vpn_auth_error_dialog_msg">The username or password you entered is incorrect. Do you want to try again?</string>
 
     <!-- VPN type selection activity title -->
     <string name="vpn_type_title">Add VPN</string>
@@ -1870,8 +1872,6 @@
     <!-- Preference summary text when VPN is not connected -->
     <string name="vpn_connect_hint">Connect to network</string>
 
-    <string name="vpn_default_profile_name">nowhere</string>
-
     <!-- Name of a VPN profile -->
     <string name="vpn_name">VPN name</string>
     <string name="vpn_a_name">a VPN name</string>
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 79a3948..b34e8b1 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -442,8 +442,9 @@
         }
     }
 
-    private class CstorHelper implements
-            DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+    private class CstorHelper implements DialogInterface.OnClickListener,
+            DialogInterface.OnDismissListener,
+            DialogInterface.OnCancelListener {
         private Keystore mKeystore = Keystore.getInstance();
         private View mView;
         private int mDialogId;
@@ -523,14 +524,18 @@
                     .show();
         }
 
+        public void onCancel(DialogInterface dialog) {
+            if (mCstorAddCredentialHelper != null) {
+                // release the object here so that it doesn't get triggerred in
+                // onDismiss()
+                mCstorAddCredentialHelper = null;
+                finish();
+            }
+        }
+
         public void onClick(DialogInterface dialog, int which) {
             if (which == DialogInterface.BUTTON_NEGATIVE) {
-                if (mCstorAddCredentialHelper != null) {
-                    // release the object here so that it doesn't get triggerred in
-                    // onDismiss()
-                    mCstorAddCredentialHelper = null;
-                    finish();
-                }
+                onCancel(dialog);
                 return;
             }
 
@@ -797,7 +802,7 @@
                     .setTitle(R.string.cstor_access_dialog_title)
                     .setPositiveButton(android.R.string.ok, this)
                     .setNegativeButton(android.R.string.cancel, this)
-                    .setCancelable(false)
+                    .setOnCancelListener(this)
                     .create();
             d.setOnDismissListener(this);
             return d;
@@ -837,7 +842,7 @@
                     .setTitle(R.string.cstor_set_passwd_dialog_title)
                     .setPositiveButton(android.R.string.ok, this)
                     .setNegativeButton(android.R.string.cancel, this)
-                    .setCancelable(false)
+                    .setOnCancelListener(this)
                     .create();
             d.setOnDismissListener(this);
             return d;
@@ -872,7 +877,7 @@
                     .setTitle(R.string.cstor_name_credential_dialog_title)
                     .setPositiveButton(android.R.string.ok, this)
                     .setNegativeButton(android.R.string.cancel, this)
-                    .setCancelable(false)
+                    .setOnCancelListener(this)
                     .create();
             d.setOnDismissListener(this);
             return d;
diff --git a/src/com/android/settings/vpn/AuthenticationActor.java b/src/com/android/settings/vpn/AuthenticationActor.java
index 537438f..2584fbd 100644
--- a/src/com/android/settings/vpn/AuthenticationActor.java
+++ b/src/com/android/settings/vpn/AuthenticationActor.java
@@ -99,14 +99,18 @@
 
     //@Override
     public View createConnectView() {
-        return View.inflate(mContext, R.layout.vpn_connect_dialog_view, null);
-    }
+        View v = View.inflate(mContext, R.layout.vpn_connect_dialog_view, null);
+        TextView usernameView = (TextView) v.findViewById(R.id.username_value);
+        TextView passwordView = (TextView) v.findViewById(R.id.password_value);
+        CheckBox saveUsername = (CheckBox) v.findViewById(R.id.save_username);
 
-    //@Override
-    public void updateConnectView(Dialog d) {
         String username = mProfile.getSavedUsername();
-        if (username == null) username = "";
-        updateConnectView(d, username, "", !TextUtils.isEmpty(username));
+        if (!TextUtils.isEmpty(username)) {
+            usernameView.setText(username);
+            saveUsername.setChecked(true);
+            passwordView.requestFocus();
+        }
+        return v;
     }
 
     protected Context getContext() {
@@ -118,22 +122,20 @@
         ServiceConnection c = new ServiceConnection() {
             public void onServiceConnected(ComponentName className,
                     IBinder service) {
-                boolean success = false;
                 try {
-                    success = IVpnService.Stub.asInterface(service)
+                    boolean success = IVpnService.Stub.asInterface(service)
                             .connect(mProfile, username, password);
-                } catch (Throwable e) {
-                    Log.e(TAG, "connect()", e);
-                    checkStatus();
-                } finally {
-                    mContext.unbindService(this);
-
                     if (!success) {
                         Log.d(TAG, "~~~~~~ connect() failed!");
-                        broadcastConnectivity(VpnState.IDLE);
                     } else {
                         Log.d(TAG, "~~~~~~ connect() succeeded!");
                     }
+                } catch (Throwable e) {
+                    Log.e(TAG, "connect()", e);
+                    broadcastConnectivity(VpnState.IDLE,
+                            VpnManager.VPN_ERROR_CONNECTION_FAILED);
+                } finally {
+                    mContext.unbindService(this);
                 }
             }
 
@@ -141,7 +143,10 @@
                 checkStatus();
             }
         };
-        if (!bindService(c)) broadcastConnectivity(VpnState.IDLE);
+        if (!bindService(c)) {
+            broadcastConnectivity(VpnState.IDLE,
+                    VpnManager.VPN_ERROR_CONNECTION_FAILED);
+        }
     }
 
     //@Override
@@ -156,7 +161,6 @@
                     checkStatus();
                 } finally {
                     mContext.unbindService(this);
-                    broadcastConnectivity(VpnState.IDLE);
                 }
             }
 
@@ -164,7 +168,9 @@
                 checkStatus();
             }
         };
-        bindService(c);
+        if (!bindService(c)) {
+            checkStatus();
+        }
     }
 
     //@Override
@@ -174,8 +180,9 @@
                     IBinder service) {
                 try {
                     IVpnService.Stub.asInterface(service).checkStatus(mProfile);
-                } catch (Throwable e) {
+                } catch (RemoteException e) {
                     Log.e(TAG, "checkStatus()", e);
+                    broadcastConnectivity(VpnState.IDLE);
                 } finally {
                     notify();
                 }
@@ -196,21 +203,14 @@
         return mVpnManager.bindVpnService(c);
     }
 
-    private void updateConnectView(Dialog d, String username,
-            String password, boolean toSaveUsername) {
-        TextView usernameView = (TextView) d.findViewById(R.id.username_value);
-        TextView passwordView = (TextView) d.findViewById(R.id.password_value);
-        CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username);
-        usernameView.setText(username);
-        passwordView.setText(password);
-        saveUsername.setChecked(toSaveUsername);
-        if (toSaveUsername) passwordView.requestFocus();
-    }
-
     private void broadcastConnectivity(VpnState s) {
         mVpnManager.broadcastConnectivity(mProfile.getName(), s);
     }
 
+    private void broadcastConnectivity(VpnState s, int errorCode) {
+        mVpnManager.broadcastConnectivity(mProfile.getName(), s, errorCode);
+    }
+
     private void wait(Object o, int ms) {
         synchronized (o) {
             try {
diff --git a/src/com/android/settings/vpn/VpnProfileActor.java b/src/com/android/settings/vpn/VpnProfileActor.java
index 1e71e86..da601f8 100644
--- a/src/com/android/settings/vpn/VpnProfileActor.java
+++ b/src/com/android/settings/vpn/VpnProfileActor.java
@@ -38,12 +38,6 @@
     View createConnectView();
 
     /**
-     * Updates the view in the connect dialog.
-     * @param dialog the recycled connect dialog.
-     */
-    void updateConnectView(Dialog dialog);
-
-    /**
      * Validates the inputs in the dialog.
      * @param dialog the connect dialog
      * @return an error message if the inputs are not valid
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java
index d38b664..08dd8dd 100644
--- a/src/com/android/settings/vpn/VpnSettings.java
+++ b/src/com/android/settings/vpn/VpnSettings.java
@@ -99,7 +99,12 @@
     private static final int CONNECT_BUTTON = DialogInterface.BUTTON1;
     private static final int OK_BUTTON = DialogInterface.BUTTON1;
 
-    private static final int DIALOG_CONNECT = 0;
+    private static final int DIALOG_CONNECT = 1;
+    private static final int DIALOG_RECONNECT = 2;
+    private static final int DIALOG_AUTH_ERROR = 3;
+    private static final int DIALOG_UNKNOWN_SERVER = 4;
+
+    private static final int NO_ERROR = 0;
 
     private PreferenceScreen mAddVpn;
     private PreferenceCategory mVpnListContainer;
@@ -115,7 +120,6 @@
 
     // actor engaged in connecting
     private VpnProfileActor mConnectingActor;
-    private boolean mStateSaved = false;
 
     // states saved for unlocking keystore
     private Runnable mUnlockAction;
@@ -125,7 +129,9 @@
     private ConnectivityReceiver mConnectivityReceiver =
             new ConnectivityReceiver();
 
-    private boolean mConnectingError;
+    private int mConnectingErrorCode = NO_ERROR;
+
+    private Dialog mShowingDialog;
 
     private StatusChecker mStatusChecker = new StatusChecker();
 
@@ -156,11 +162,10 @@
         String profileName = (savedInstanceState == null)
                 ? null
                 : savedInstanceState.getString(STATE_ACTIVE_ACTOR);
-        mStateSaved = !TextUtils.isEmpty(profileName);
         retrieveVpnListFromStorage();
-        if (mStateSaved) {
-            mConnectingActor =
-                    getActor(mVpnPreferenceMap.get(profileName).mProfile);
+        if (!TextUtils.isEmpty(profileName)) {
+            mActiveProfile = mVpnPreferenceMap.get(profileName).mProfile;
+            mConnectingActor = getActor(mActiveProfile);
         } else {
             checkVpnConnectionStatusInBackground();
         }
@@ -188,6 +193,7 @@
     protected synchronized void onSaveInstanceState(Bundle outState) {
         if (mConnectingActor == null) return;
 
+        Log.d(TAG, "   ~~~~~    save connecting actor");
         outState.putString(STATE_ACTIVE_ACTOR,
                 mConnectingActor.getProfile().getName());
     }
@@ -197,6 +203,9 @@
         super.onDestroy();
         unregisterForContextMenu(getListView());
         mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver);
+        if ((mShowingDialog != null) && mShowingDialog.isShowing()) {
+            mShowingDialog.dismiss();
+        }
     }
 
     @Override
@@ -205,39 +214,76 @@
             case DIALOG_CONNECT:
                 return createConnectDialog();
 
+            case DIALOG_RECONNECT:
+                return createReconnectDialogBuilder().create();
+
+            case DIALOG_AUTH_ERROR:
+                return createAuthErrorDialog();
+
+            case DIALOG_UNKNOWN_SERVER:
+                return createUnknownServerDialog();
+
             default:
-                return null;
+                return super.onCreateDialog(id);
         }
     }
 
     private Dialog createConnectDialog() {
-        if (mConnectingActor == null) {
-            Log.e(TAG, "no connecting actor to create the dialog");
-            return null;
-        }
-        String name = (mConnectingActor == null)
-                ? getString(R.string.vpn_default_profile_name)
-                : mConnectingActor.getProfile().getName();
-        Dialog d = new AlertDialog.Builder(this)
+        return new AlertDialog.Builder(this)
                 .setView(mConnectingActor.createConnectView())
                 .setTitle(String.format(getString(R.string.vpn_connect_to),
-                        name))
+                        mConnectingActor.getProfile().getName()))
                 .setPositiveButton(getString(R.string.vpn_connect_button),
                         this)
                 .setNegativeButton(getString(android.R.string.cancel),
                         this)
                 .create();
-        return d;
     }
 
-    @Override
-    protected void onPrepareDialog (int id, Dialog dialog) {
-        if (mStateSaved) {
-            mStateSaved = false;
-            super.onPrepareDialog(id, dialog);
-        } else if (mConnectingActor != null) {
-            mConnectingActor.updateConnectView(dialog);
-        }
+    private AlertDialog.Builder createReconnectDialogBuilder() {
+        return new AlertDialog.Builder(this)
+                .setTitle(android.R.string.dialog_alert_title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(R.string.vpn_confirm_reconnect)
+                .setPositiveButton(R.string.vpn_yes_button,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int w) {
+                                connectOrDisconnect(mConnectingActor.getProfile());
+                            }
+                        })
+                .setNegativeButton(R.string.vpn_no_button,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int w) {
+                                onIdle();
+                            }
+                        })
+                .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                            public void onCancel(DialogInterface dialog) {
+                                onIdle();
+                            }
+                        });
+    }
+
+    private Dialog createAuthErrorDialog() {
+        return createReconnectDialogBuilder()
+                .setMessage(R.string.vpn_auth_error_dialog_msg)
+                .create();
+    }
+
+    private Dialog createUnknownServerDialog() {
+        return createReconnectDialogBuilder()
+                .setMessage(R.string.vpn_unknown_server_dialog_msg)
+                .setPositiveButton(R.string.vpn_yes_button,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int w) {
+                                VpnProfile p = mConnectingActor.getProfile();
+                                onIdle();
+                                mIndexOfEditedProfile =
+                                        mVpnProfileList.indexOf(p);
+                                startVpnEditor(p);
+                            }
+                        })
+                .create();
     }
 
     @Override
@@ -252,8 +298,8 @@
             menu.setHeaderTitle(p.getName());
 
             boolean isIdle = (state == VpnState.IDLE);
-            boolean isNotConnect =
-                    (isIdle || (state == VpnState.DISCONNECTING));
+            boolean isNotConnect = (isIdle || (state == VpnState.DISCONNECTING)
+                    || (state == VpnState.CANCELLED));
             menu.add(0, CONTEXT_MENU_CONNECT_ID, 0, R.string.vpn_menu_connect)
                     .setEnabled(isIdle && (mActiveProfile == null));
             menu.add(0, CONTEXT_MENU_DISCONNECT_ID, 0,
@@ -363,17 +409,18 @@
     // Called when the buttons on the connect dialog are clicked.
     //@Override
     public synchronized void onClick(DialogInterface dialog, int which) {
-        dismissDialog(DIALOG_CONNECT);
         if (which == CONNECT_BUTTON) {
             Dialog d = (Dialog) dialog;
             String error = mConnectingActor.validateInputs(d);
             if (error == null) {
                 changeState(mConnectingActor.getProfile(), VpnState.CONNECTING);
                 mConnectingActor.connect(d);
+                removeDialog(DIALOG_CONNECT);
                 return;
             } else {
+                dismissDialog(DIALOG_CONNECT);
                 // show error dialog
-                new AlertDialog.Builder(this)
+                mShowingDialog = new AlertDialog.Builder(this)
                         .setTitle(android.R.string.dialog_alert_title)
                         .setIcon(android.R.drawable.ic_dialog_alert)
                         .setMessage(String.format(getString(
@@ -385,8 +432,11 @@
                                         showDialog(DIALOG_CONNECT);
                                     }
                                 })
-                        .show();
+                        .create();
+                mShowingDialog.show();
             }
+        } else {
+            removeDialog(DIALOG_CONNECT);
         }
     }
 
@@ -428,13 +478,14 @@
                         }
                     }
                 };
-        new AlertDialog.Builder(this)
+        mShowingDialog = new AlertDialog.Builder(this)
                 .setTitle(android.R.string.dialog_alert_title)
                 .setIcon(android.R.drawable.ic_dialog_alert)
                 .setMessage(R.string.vpn_confirm_profile_deletion)
                 .setPositiveButton(android.R.string.ok, onClickListener)
                 .setNegativeButton(R.string.vpn_no_button, onClickListener)
-                .show();
+                .create();
+        mShowingDialog.show();
     }
 
     // Randomly generates an ID for the profile.
@@ -583,8 +634,8 @@
         }
 
         mConnectingActor = getActor(p);
+        mActiveProfile = p;
         if (mConnectingActor.isConnectDialogNeeded()) {
-            removeDialog(DIALOG_CONNECT);
             showDialog(DIALOG_CONNECT);
         } else {
             changeState(p, VpnState.CONNECTING);
@@ -605,8 +656,6 @@
                 break;
 
             case CONNECTED:
-                mConnectingError = false;
-                // pass through
             case DISCONNECTING:
                 changeState(p, VpnState.DISCONNECTING);
                 getActor(p).disconnect();
@@ -625,16 +674,13 @@
         switch (state) {
         case CONNECTED:
             mConnectingActor = null;
-            // pass through
-        case CONNECTING:
             mActiveProfile = p;
             disableProfilePreferencesIfOneActive();
             break;
 
+        case CONNECTING:
         case DISCONNECTING:
-            if (oldState == VpnState.CONNECTING) {
-                mConnectingError = true;
-            }
+            disableProfilePreferencesIfOneActive();
             break;
 
         case CANCELLED:
@@ -642,31 +688,34 @@
             break;
 
         case IDLE:
-            assert(mActiveProfile != p);
-            mActiveProfile = null;
-            mConnectingActor = null;
-            enableProfilePreferences();
+            assert(mActiveProfile == p);
 
-            if (oldState == VpnState.CONNECTING) mConnectingError = true;
-            if (mConnectingError) showReconnectDialog(p);
+            switch (mConnectingErrorCode) {
+                case NO_ERROR:
+                    onIdle();
+                    break;
+
+                case VpnManager.VPN_ERROR_AUTH:
+                    showDialog(DIALOG_AUTH_ERROR);
+                    break;
+
+                case VpnManager.VPN_ERROR_UNKNOWN_SERVER:
+                    showDialog(DIALOG_UNKNOWN_SERVER);
+                    break;
+
+                default:
+                    showDialog(DIALOG_RECONNECT);
+                    break;
+            }
+            mConnectingErrorCode = NO_ERROR;
             break;
         }
     }
 
-    private void showReconnectDialog(final VpnProfile p) {
-        new AlertDialog.Builder(this)
-                .setTitle(android.R.string.dialog_alert_title)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(R.string.vpn_confirm_reconnect)
-                .setPositiveButton(R.string.vpn_yes_button,
-                        new DialogInterface.OnClickListener() {
-                            public void onClick(DialogInterface dialog, int w) {
-                                dialog.dismiss();
-                                connectOrDisconnect(p);
-                            }
-                        })
-                .setNegativeButton(R.string.vpn_no_button, null)
-                .show();
+    private void onIdle() {
+        mActiveProfile = null;
+        mConnectingActor = null;
+        enableProfilePreferences();
     }
 
     private void disableProfilePreferencesIfOneActive() {
@@ -674,6 +723,7 @@
 
         for (VpnProfile p : mVpnProfileList) {
             switch (p.getState()) {
+                case CONNECTING:
                 case DISCONNECTING:
                 case IDLE:
                     mVpnPreferenceMap.get(p.getName()).setEnabled(false);
@@ -856,14 +906,20 @@
 
             VpnState s = (VpnState) intent.getSerializableExtra(
                     VpnManager.BROADCAST_CONNECTION_STATE);
+
             if (s == null) {
                 Log.e(TAG, "received null connectivity state");
                 return;
             }
+
+            mConnectingErrorCode = intent.getIntExtra(
+                    VpnManager.BROADCAST_ERROR_CODE, NO_ERROR);
+
             VpnPreference pref = mVpnPreferenceMap.get(profileName);
             if (pref != null) {
                 Log.d(TAG, "received connectivity: " + profileName
-                        + ": connected? " + s);
+                        + ": connected? " + s
+                        + "   err=" + mConnectingErrorCode);
                 changeState(pref.mProfile, s);
             } else {
                 Log.e(TAG, "received connectivity: " + profileName