@Override
  public boolean handleMessage(Message message) {
    mUpdater.removeMessages(0);

    if (isResumed()) {
      try {
        LegacyVpnInfo info = mService.getLegacyVpnInfo();
        if (mInfo != null) {
          VpnPreference preference = mPreferences.get(mInfo.key);
          if (preference != null) {
            preference.update(-1);
          }
          mInfo = null;
        }
        if (info != null) {
          VpnPreference preference = mPreferences.get(info.key);
          if (preference != null) {
            preference.update(info.state);
            mInfo = info;
          }
        }
      } catch (Exception e) {
        // ignore
      }
      mUpdater.sendEmptyMessageDelayed(0, 1000);
    }
    return true;
  }
  @Override
  public void onClick(DialogInterface dialog, int button) {
    if (button == DialogInterface.BUTTON_POSITIVE) {
      // Always save the profile.
      VpnProfile profile = mDialog.getProfile();
      mKeyStore.put(Credentials.VPN + profile.key, profile.encode());

      // Update the preference.
      VpnPreference preference = mPreferences.get(profile.key);
      if (preference != null) {
        disconnect(profile.key);
        preference.update(profile);
      } else {
        preference = new VpnPreference(getActivity(), profile);
        preference.setOnPreferenceClickListener(this);
        mPreferences.put(profile.key, preference);
        getPreferenceScreen().addPreference(preference);
      }

      // If we are not editing, connect!
      if (!mDialog.isEditing()) {
        try {
          connect(profile);
        } catch (Exception e) {
          Log.e(TAG, "connect", e);
        }
      }
    }
  }
  @Override
  public boolean onContextItemSelected(MenuItem item) {
    if (mDialog != null) {
      Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
      return false;
    }

    VpnPreference preference = mPreferences.get(mSelectedKey);
    if (preference == null) {
      Log.v(TAG, "onContextItemSelected() is called but no preference is found");
      return false;
    }

    switch (item.getItemId()) {
      case R.string.vpn_menu_edit:
        mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
        mDialog.setOnDismissListener(this);
        mDialog.show();
        return true;
      case R.string.vpn_menu_delete:
        disconnect(mSelectedKey);
        getPreferenceScreen().removePreference(preference);
        mPreferences.remove(mSelectedKey);
        mKeyStore.delete(Credentials.VPN + mSelectedKey);
        return true;
    }
    return false;
  }
  @Override
  public void onResume() {
    super.onResume();

    // Check KeyStore here, so others do not need to deal with it.
    if (!mKeyStore.isUnlocked()) {
      if (!mUnlocking) {
        // Let us unlock KeyStore. See you later!
        Credentials.getInstance().unlock(getActivity());
      } else {
        // We already tried, but it is still not working!
        finishFragment();
      }
      mUnlocking = !mUnlocking;
      return;
    }

    // Now KeyStore is always unlocked. Reset the flag.
    mUnlocking = false;

    // Currently we are the only user of profiles in KeyStore.
    // Assuming KeyStore and KeyGuard do the right thing, we can
    // safely cache profiles in the memory.
    if (mPreferences.size() == 0) {
      PreferenceGroup group = getPreferenceScreen();

      final Context context = getActivity();
      final List<VpnProfile> profiles = loadVpnProfiles(mKeyStore);
      for (VpnProfile profile : profiles) {
        final VpnPreference pref = new VpnPreference(context, profile);
        pref.setOnPreferenceClickListener(this);
        mPreferences.put(profile.key, pref);
        group.addPreference(pref);
      }
    }

    // Show the dialog if there is one.
    if (mDialog != null) {
      mDialog.setOnDismissListener(this);
      mDialog.show();
    }

    // Start monitoring.
    if (mUpdater == null) {
      mUpdater = new Handler(this);
    }
    mUpdater.sendEmptyMessage(0);

    // Register for context menu. Hmmm, getListView() is hidden?
    registerForContextMenu(getListView());
  }