content: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the Common Development
   * and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at
   * src/com/vodafone360/people/VODAFONE.LICENSE.txt or
   * http://github.com/360/360-Engine-for-Android
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each file and
   * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
   * If applicable, add the following below this CDDL HEADER, with the fields
   * enclosed by brackets "[]" replaced with your own identifying information:
   * Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   *
   * Copyright 2010 Vodafone Sales & Services Ltd.  All rights reserved.
   * Use is subject to license terms.
   */
  
  package com.vodafone360.people.engine.login;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.bouncycastle.crypto.InvalidCipherTextException;
  
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  
  import com.vodafone360.people.ApplicationCache;
  import com.vodafone360.people.Intents;
  import com.vodafone360.people.Settings;
  import com.vodafone360.people.SettingsManager;
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.database.tables.StateTable;
  import com.vodafone360.people.datatypes.AuthSessionHolder;
  import com.vodafone360.people.datatypes.BaseDataType;
  import com.vodafone360.people.datatypes.LoginDetails;
  import com.vodafone360.people.datatypes.PublicKeyDetails;
  import com.vodafone360.people.datatypes.RegistrationDetails;
  import com.vodafone360.people.datatypes.SimpleText;
  import com.vodafone360.people.engine.BaseEngine;
  import com.vodafone360.people.engine.IEngineEventCallback;
  import com.vodafone360.people.engine.EngineManager.EngineId;
  import com.vodafone360.people.engine.contactsync.NativeContactsApi;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.service.ServiceUiRequest;
  import com.vodafone360.people.service.agent.NetworkAgent;
  import com.vodafone360.people.service.agent.UiAgent;
  import com.vodafone360.people.service.agent.NetworkAgent.AgentState;
  import com.vodafone360.people.service.io.ResponseQueue;
  import com.vodafone360.people.service.io.api.Auth;
  import com.vodafone360.people.service.receivers.SmsBroadcastReceiver;
  import com.vodafone360.people.utils.LogUtils;
  import com.vodafone360.people.utils.LoginPreferences;
  import com.vodafone360.people.utils.SimCard;
  
  /**
   * Engine handling sign in to existing accounts and sign up to new accounts.
   */
  public class LoginEngine extends BaseEngine {
      /**
       * List of states for LoginEngine.
       */
      private enum State {
          NOT_INITIALISED,
          NOT_REGISTERED,
          LOGGED_ON,
          LOGGED_OFF,
          LOGGED_OFF_WAITING_FOR_RETRY,
          LOGGED_OFF_WAITING_FOR_NETWORK,
          LOGIN_FAILED,
          LOGIN_FAILED_WRONG_CREDENTIALS,
          SIGNING_UP,
          FETCHING_TERMS_OF_SERVICE,
          FETCHING_PRIVACY_STATEMENT,
          FETCHING_USERNAME_STATE,
          REQUESTING_ACTIVATION_CODE,
          CREATING_SESSION_MANUAL,
          ACTIVATING_ACCOUNT,
          CREATING_SESSION_AUTO,
          RETRIEVING_PUBLIC_KEY
      }
  
      /**
       * Text coming from the server contains these carriage return characters,
       * which need to be exchanged with space characters to improve layout.
       */
      private static final char CARRIAGE_RETURN_CHARACTER = (char) 13;
  
      /**
       * Text coming from the server contains carriage return characters, which
       * need to be exchanged with these space characters to improve layout.
       */
      private static final char SPACE_CHARACTER = (char) 32;
      /**
       * used for sending unsolicited ui events
       */
      private final UiAgent mUiAgent = mEventCallback.getUiAgent();
  
      /**
       * mutex for thread synchronization
       */
      private final Object mMutex = new Object();
      /**
       * To convert between seconds and milliseconds
       */
      private static final int MS_IN_SECONDS = 1000;
  
      /**
       * Current state of the engine
       */
      private State mState = State.NOT_INITIALISED;
  
      /**
       * Context used for listening to SMS events
       */
      private Context mContext;
  
      /**
       * Database used for fetching/storing state information
       */
      private DatabaseHelper mDb;
  
      /**
       * Contains the authenticated session information while the user is logged
       * in
       */
      private static AuthSessionHolder sActivatedSession = null;
  
      /**
       * Contains user login details such as user name and password
       */
      private final LoginDetails mLoginDetails = new LoginDetails();
  
      /**
       * Contains a public key for encrypting login details to be sent to the
       * server
       */
      private PublicKeyDetails mPublicKey = new PublicKeyDetails();
  
      /**
       * Contains user registration details such as name, username, password, etc.
       */
      private RegistrationDetails mRegistrationDetails = new RegistrationDetails();
  
      /**
       * Determines if current login information from database can be used to
       * establish a session. Set to false if login fails repeatedly.
       */
      private boolean mAreLoginDetailsValid = true;
  
      /**
       * Timeout used when waiting for the server to sent an activation SMS. If
       * this time in milliseconds is exceeded, the login will fail with SMS not
       * received error.
       */
      private static final long ACTIVATE_LOGIN_TIMEOUT = 72000;
  
      /**
       * Holds the activation code once an activation SMS has been received.
       */
      private String mActivationCode = null;
  
      /**
       * Set to true when sign in or sign up has been completed by the user. When
       * this is false the landing page will be displayed when the application is
       * launched.
       */
      private boolean mIsRegistrationComplete = false;
  
      /**
       * Contains a list of login engine observers which will be notified when the
       * login engine changes state.
       */
      private final ArrayList<ILoginEventsListener> mEventsListener = new ArrayList<ILoginEventsListener>();
  
      /**
       * Determines if the user is currently logged in with a valid session
       */
      private boolean mCurrentLoginState = false;
  
      /**
       * Listener interface that can be used by clients to receive login state
       * events from the engine.
       */
      public static interface ILoginEventsListener {
          void onLoginStateChanged(boolean loggedIn);
      }
  
      /**
       * Public constructor.
       * 
       * @param context The service Context
       * @param eventCallback Provides access to useful engine manager
       *            functionality
       * @param db The Now+ database used for fetching/storing login state
       *            information
       */
      public LoginEngine(Context context, IEngineEventCallback eventCallback, DatabaseHelper db) {
          super(eventCallback);
          LogUtils.logD("LoginEngine.LoginEngine()");
          mContext = context;
          mDb = db;
  
          mEngineId = EngineId.LOGIN_ENGINE;
      }
  
      /**
       * This will be called immediately after creation to perform extra
       * initialisation.
       */
      @Override
      public void onCreate() {
          LogUtils.logD("LoginEngine.OnCreate()");
          mState = State.NOT_INITIALISED;
          IntentFilter filter = new IntentFilter(SmsBroadcastReceiver.ACTION_ACTIVATION_CODE);
          mContext.registerReceiver(mEventReceiver, filter);
          mIsRegistrationComplete = StateTable.isRegistrationComplete(mDb.getReadableDatabase());
          initializeEngine();
      }
  
      /**
       * This will be called just before the engine is shutdown. Cleans up any
       * resources used.
       */
      @Override
      public void onDestroy() {
          LogUtils.logD("LoginEngine.onDestroy()");
  
  //        Intent intent = new Intent();
  //        intent.setAction(Intents.HIDE_LOGIN_NOTIFICATION);
  //        mContext.sendBroadcast(intent);
  
          removeAllListeners();
          mContext.unregisterReceiver(mEventReceiver);
      }
  
      /**
       * Called by the INowPlusService implementation to start a manual login.
       * Adds a manual login UI request to the queue and kicks the worker thread.
       * 
       * @param loginDetails The login information entered by the user
       */
      public void addUiLoginRequest(LoginDetails loginDetails) {
          LogUtils.logD("LoginEngine.addUiLoginRequest()");
          addUiRequestToQueue(ServiceUiRequest.LOGIN, loginDetails);
      }
  
      /**
       * Called by the INowPlusService implementation to remove user data Issues a
       * UI request to effectively log out.
       */
      public void addUiRemoveUserDataRequest() {
          LogUtils.logD("LoginEngine.addUiRemoveUserDataRequest()");
          addUiRequestToQueue(ServiceUiRequest.REMOVE_USER_DATA, null);
      }
  
      /**
       * Called by the INowPlusService implementation to start the sign-up
       * process. Adds a registration UI request to the queue and kicks the worker
       * thread.
       * 
       * @param details The registration details entered by the user
       */
      public void addUiRegistrationRequest(RegistrationDetails details) {
          LogUtils.logD("LoginEngine.addUiRegistrationRequest()");
          addUiRequestToQueue(ServiceUiRequest.REGISTRATION, details);
      }
  
      /**
       * Called by the INowPlusService implementation to fetch terms of service
       * text for the UI. Adds a fetch terms of service UI request to the queue
       * and kicks the worker thread.
       */
      public void addUiFetchTermsOfServiceRequest() {
          LogUtils.logD("LoginEngine.addUiFetchTermsOfServiceRequest()");
          addUiRequestToQueue(ServiceUiRequest.FETCH_TERMS_OF_SERVICE, null);
      }
  
      /**
       * Called by the INowPlusService implementation to privacy statement for the
       * UI. Adds a fetch privacy statement UI request to the queue and kicks the
       * worker thread.
       */
      public void addUiFetchPrivacyStatementRequest() {
          LogUtils.logD("LoginEngine.addUiFetchPrivacyStatementRequest()");
          addUiRequestToQueue(ServiceUiRequest.FETCH_PRIVACY_STATEMENT, null);
      }
  
      /**
       * Called by the INowPlusService implementation to check if a username is
       * available for registration. Adds a get username state UI request to the
       * queue and kicks the worker thread.
       * 
       * @param username Username to fetch the state of
       * TODO: Not currently used by UI.
       */
      public void addUiGetUsernameStateRequest(String username) {
          LogUtils.logD("LoginEngine.addUiGetUsernameStateRequest()");
          addUiRequestToQueue(ServiceUiRequest.USERNAME_AVAILABILITY, username);
      }
  
      /**
       * Return the absolute time in milliseconds when the engine needs to run
       * (based on System.currentTimeMillis).
       * 
       * @return -1 never needs to run, 0 needs to run as soon as possible,
       *         CurrentTime + 60000 to run in 1 minute, etc.
       */
      @Override
      public long getNextRunTime() {
          if (isCommsResponseOutstanding()) {
              return 0;
          }
          if (uiRequestReady()) {
              return 0;
          }
          switch (mState) {
              case NOT_INITIALISED:
                  return 0;
              case NOT_REGISTERED:
              case LOGGED_ON:
              case LOGIN_FAILED:
              case LOGIN_FAILED_WRONG_CREDENTIALS:
                  return -1;
              case REQUESTING_ACTIVATION_CODE:
              case SIGNING_UP:
                  if (mActivationCode != null) {
                      return 0;
                  }
                  break;
              case LOGGED_OFF:
                  return 0;
              case LOGGED_OFF_WAITING_FOR_NETWORK:
                  if (NetworkAgent.getAgentState() == AgentState.CONNECTED) {
                      return 0;
                  }
                  break;
              case RETRIEVING_PUBLIC_KEY:
                  return 0;
              default:
                  // Do nothing.
                  break;
          }
          return getCurrentTimeout();
      }
  
      /**
       * Do some work but anything that takes longer than 1 second must be broken
       * up. The type of work done includes:
       * <ul>
       * <li>If a comms response from server is outstanding, process it</li>
       * <li>If a timeout is pending, process it</li>
       * <li>If an SMS activation code has been received, process it</li>
       * <li>Retry auto login if necessary</li>
       * </ul>
       */
      @Override
      public void run() {
          LogUtils.logD("LoginEngine.run()");
          if (isCommsResponseOutstanding() && processCommsInQueue()) {
              return;
          }
          if (processTimeout()) {
              return;
          }
          switch (mState) {
              case NOT_INITIALISED:
                  initializeEngine();
                  return;
              case REQUESTING_ACTIVATION_CODE:
              case SIGNING_UP:
                  if (mActivationCode != null) {
                      handleSmsResponse();
                      return;
                  }
                  break;
              case RETRIEVING_PUBLIC_KEY:
                  break;
              case LOGGED_OFF:
              case LOGGED_OFF_WAITING_FOR_NETWORK:
              	AuthSessionHolder session = StateTable.fetchSession(mDb.getReadableDatabase());
              	
                  // if session is null we try to login automatically again
              	if (null == session) {
              		if (retryAutoLogin()) {
                          return;
                      }
              	} else {	// otherwise we try to reuse the session
              		sActivatedSession = session;
              		newState(State.LOGGED_ON);
              	}
              default: // do nothing.
                  break;
          }
          if (uiRequestReady() && processUiQueue()) {
              return;
          }
      }
  
      /**
       * Helper function which returns true if a UI request is waiting on the
       * queue and we are ready to process it.
       * 
       * @return true if the request can be processed now.
       */
      private boolean uiRequestReady() {
          if (!isUiRequestOutstanding()) {
              return false;
          }
          switch (mState) {
              case NOT_REGISTERED:
              case LOGGED_OFF:
              case LOGGED_ON:
              case LOGIN_FAILED:
              case CREATING_SESSION_AUTO:
              case LOGIN_FAILED_WRONG_CREDENTIALS:
              case LOGGED_OFF_WAITING_FOR_RETRY:
                  return true;
              default:
                  return false;
          }
      }
  
      /**
       * Determines if the user is currently logged in with a valid session.
       * 
       * @return true if logged in, false otherwise
       */
      public boolean isLoggedIn() {
          return mState == State.LOGGED_ON;
      }
  
      /**
       * Add a listener which will receive events whenever the login state
       * changes.
       * 
       * @param listener The callback interface
       */
      public synchronized void addListener(ILoginEventsListener listener) {
          LogUtils.logD("LoginEngine.addListener()");
          if (!mEventsListener.contains(listener)) {
              mEventsListener.add(listener);
          }
      }
  
      /**
       * Remove a listener added by the addListener function.
       * 
       * @param listener The same callback interface passed in to the add function
       */
      public synchronized void removeListener(ILoginEventsListener listener) {
          LogUtils.logD("LoginEngine.removeListener()");
          mEventsListener.remove(listener);
      }
  
      /**
       * Remove all ILoginStateChangeListener (done as part of cleanup).
       */
      private synchronized void removeAllListeners() {
          LogUtils.logD("LoginEngine.removeAllListeners()");
          if (mEventsListener != null) {
              mEventsListener.clear();
          }
      }
  
      /**
       * Once the engine has finished processing a user request, this function is
       * called to restore the state back to an appropriate value (based on the
       * user login state).
       */
      private synchronized void restoreLoginState() {
          LogUtils.logD("LoginEngine.restoreLoginState");
          if (mIsRegistrationComplete) {
              if (sActivatedSession != null) {
                  newState(State.LOGGED_ON);
              } else {
                  if (mAreLoginDetailsValid) {
                      newState(State.LOGGED_OFF);
                  } else {
                      newState(State.LOGIN_FAILED);
                  }
              }
          } else {
              newState(State.NOT_REGISTERED);
          }
      }
  
      /**
       * Called when a server response is received, processes the response based
       * on the engine state.
       * 
       * @param resp Response data from server
       */
      @Override
      protected void processCommsResponse(ResponseQueue.DecodedResponse resp) {
          LogUtils.logD("LoginEngine.processCommsResponse() - resp = " + resp);
          switch (mState) {
              case SIGNING_UP:
                  handleSignUpResponse(resp.mDataTypes);
                  break;
              case RETRIEVING_PUBLIC_KEY:
                  handleNewPublicKeyResponse(resp.mDataTypes);
                  break;
              case CREATING_SESSION_MANUAL:
                  handleCreateSessionManualResponse(resp.mDataTypes);
                  break;
              case CREATING_SESSION_AUTO:
                  handleCreateSessionAutoResponse(resp.mDataTypes);
                  break;
              case REQUESTING_ACTIVATION_CODE:
                  handleRequestingActivationResponse(resp.mDataTypes);
                  break;
              case ACTIVATING_ACCOUNT:
                  handleActivateAccountResponse(resp.mDataTypes);
                  break;
              case FETCHING_TERMS_OF_SERVICE:
              case FETCHING_PRIVACY_STATEMENT:
              case FETCHING_USERNAME_STATE:
                  handleServerSimpleTextResponse(resp.mDataTypes, mState);
                  break;
              default: // do nothing.
                  break;
          }
      }
  
      /**
       * Called when a UI request is ready to be processed. Handlers the UI
       * request based on the type.
       * 
       * @param requestId UI request type
       * @param data Interpretation of this data depends on the request type
       */
      @Override
      protected void processUiRequest(ServiceUiRequest requestId, Object data) {
          LogUtils.logD("LoginEngine.processUiRequest() - reqID = " + requestId);
          switch (requestId) {
              case LOGIN:
                  startManualLoginProcess((LoginDetails)data);
                  break;
              case REGISTRATION:
                  startRegistrationProcessCrypted((RegistrationDetails)data);
                  break;
              case REMOVE_USER_DATA:
                  startLogout();
                  // Remove NAB Account at this point (does nothing on 1.X)
                  NativeContactsApi.getInstance().removePeopleAccount();
                  super.onReset(); // Sets the reset flag as done
                  break;
              case LOGOUT:
                  startLogout();
                  break;
              case FETCH_TERMS_OF_SERVICE:
                  startFetchTermsOfService();
                  break;
              case FETCH_PRIVACY_STATEMENT:
                  startFetchPrivacyStatement();
                  break;
              case USERNAME_AVAILABILITY:
                  startFetchUsernameState((String)data);
                  break;
              default:
                  completeUiRequest(ServiceStatus.ERROR_NOT_FOUND, null);
          }
      }
  
      /**
       * Called by the run() function the first time it is executed to perform
       * non-trivial initialisation such as auto login.
       */
      private void initializeEngine() {
          LogUtils.logD("LoginEngine.initialiseEngine()");
          if (ServiceStatus.SUCCESS == mDb.fetchLogonCredentialsAndPublicKey(mLoginDetails,
                  mPublicKey)) {
              sActivatedSession = StateTable.fetchSession(mDb.getReadableDatabase());
          }
          mAreLoginDetailsValid = true;
          restoreLoginState();
          clearTimeout();
      }
  
      /**
       * Starts the sign-up process where the password is RSA encrypted. A setting
       * determines if this function is used in preference to
       * {@link #startRegistrationProcess(RegistrationDetails)} function.
       * 
       * @param details Registration details received from the UI request
       */
      private void startRegistrationProcessCrypted(RegistrationDetails details) {
          LogUtils.logD("startRegistrationCrypted");
          if (details == null || details.mUsername == null || details.mPassword == null) {
              completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER, null);
              return;
          }
  
          setRegistrationComplete(false);
          setActivatedSession(null);
          mRegistrationDetails.copy(details);
          mLoginDetails.mUsername = mRegistrationDetails.mUsername;
          mLoginDetails.mPassword = mRegistrationDetails.mPassword;
  
          try {
              final long timestampInSeconds = System.currentTimeMillis() / MS_IN_SECONDS;
              final byte[] theBytes = prepareBytesForSignup(timestampInSeconds);
              mLoginDetails.mAutoConnect = true;
              mLoginDetails.mRememberMe = true;
              mLoginDetails.mMobileNo = mRegistrationDetails.mMsisdn;
              mLoginDetails.mSubscriberId = SimCard.getSubscriberId(mContext);
              mDb.modifyCredentialsAndPublicKey(mLoginDetails, mPublicKey);
  
              startSignUpCrypted(theBytes, timestampInSeconds);
          } catch (InvalidCipherTextException e) {
              e.printStackTrace();
          }
      }
  
      /**
       * Encrypts the sign-up data ready for sending to the server
       * 
       * @param timeStampInSeconds Current time in milliseconds
       * @return Raw data that can be sent to the server
       */
      private byte[] prepareBytesForSignup(long timeStampInSeconds) throws InvalidCipherTextException {
          byte[] theBytes = null;
          if (mPublicKey != null) {
              if (mPublicKey.mExponential != null && (mPublicKey.mModulus != null)) {
                  theBytes = RSAEncryptionUtils.encryptRSA(RSAEncryptionUtils.getRSAPubKey(
                          mPublicKey.mModulus, mPublicKey.mExponential), makeSecurePassword(
                          mLoginDetails.mUsername, mLoginDetails.mPassword, timeStampInSeconds));
              }
          }
          if (theBytes == null) {
              RSAEncryptionUtils.copyDefaultPublicKey(mPublicKey);// we'll store
              // the default
              // public key
              // into the db
              theBytes = RSAEncryptionUtils.encryptRSA(RSAEncryptionUtils.getDefaultPublicKey(),
                      makeSecurePassword(mLoginDetails.mUsername, mLoginDetails.mPassword,
                              timeStampInSeconds));
          }
          return theBytes;
      }
  
      /**
       * Concatenates the username, password, current time and some other data
       * into a string which can be encrypted and sent to the server.
       * 
       * @param userName User name for sign-in or sign-up
       * @param password Password as entered by user
       * @param ts Current time in milliseconds
       * @return Concatenated data
       */
      private static String makeSecurePassword(String userName, String password, long ts) {
          String appSecret = SettingsManager.getProperty(Settings.APP_SECRET_KEY);// RSAEncrypter.testAppSecretThrottled;
          final char amp = '&';
          if (ts <= 0 || //
                  userName == null || userName.trim().length() == 0 || //
                  password == null || password.trim().length() == 0 || //
                  // set application key somewhere
                  appSecret == null || appSecret.trim().length() == 0)
              return null;
          final String passwordT = password.trim();
          final String userNameT = userName.trim();
          final StringBuffer sb = new StringBuffer();
          sb.append(appSecret).append(amp).append(Long.toString(ts)).append(amp).append(userNameT)
                  .append(amp).append(passwordT);
          return sb.toString();
      }
  
      /**
       * Puts the engine into the signing up state and sends an encrypted sign-up
       * request to the server.
       * 
       * @param theBytes byte-array containing encrypted password data.
       * @param timestamp Current timestamp.
       */
      private void startSignUpCrypted(byte[] theBytes, long timestamp) {
          LogUtils.logD("startSignUpCrypted()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          mActivationCode = null;
          newState(State.SIGNING_UP);
  
          if (!validateRegistrationDetails()) {
              completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER, null);
              return;
          }
          int reqId = Auth.signupUserCrypted(
                  this,
                  mRegistrationDetails.mFullname,
                  mRegistrationDetails.mUsername,
                  theBytes, // what is encrypted
                  timestamp, mRegistrationDetails.mEmail, mRegistrationDetails.mBirthdayDate,
                  mRegistrationDetails.mMsisdn, mRegistrationDetails.mAcceptedTAndC,
                  mRegistrationDetails.mCountrycode, mRegistrationDetails.mTimezone,
                  mRegistrationDetails.mLanguage, mRegistrationDetails.mMobileOperatorId,
                  mRegistrationDetails.mMobileModelId, mRegistrationDetails.mSendConfirmationMail,
                  mRegistrationDetails.mSendConfirmationSms,
                  mRegistrationDetails.mSubscribeToNewsLetter);
          if (!setReqId(reqId)) {
              completeUiRequest(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Basic check to determine if the registration details given by the user
       * are valid
       * 
       * @return true if the details are valid, false otherwise.
       */
      private boolean validateRegistrationDetails() {
          if (mRegistrationDetails.mFullname == null || mRegistrationDetails.mEmail == null
                  || mRegistrationDetails.mBirthdayDate == null
                  || mRegistrationDetails.mMsisdn == null
                  || mRegistrationDetails.mAcceptedTAndC == null
                  || mRegistrationDetails.mCountrycode == null
                  || mRegistrationDetails.mTimezone == null || mRegistrationDetails.mLanguage == null
                  || mRegistrationDetails.mMobileOperatorId == null
                  || mRegistrationDetails.mMobileModelId == null
                  || mRegistrationDetails.mSendConfirmationMail == null
                  || mRegistrationDetails.mSendConfirmationSms == null
                  || mRegistrationDetails.mSubscribeToNewsLetter == null) {
              return false;
          }
          return true;
      }
  
      /**
       * Requests a new Public Key from the server.
       */
      public void getNewPublicKey() {
          LogUtils.logD("LoginEngine.getNewPublicKey");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          mActivationCode = null;
          newState(State.RETRIEVING_PUBLIC_KEY);
          if (!setReqId(Auth.getPublicKey(this))) {
              completeUiRequest(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sends a fetch terms of service request to the server.
       */
      private void startFetchTermsOfService() {
          LogUtils.logD("LoginEngine.startFetchTermsOfService()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
              updateTermsState(ServiceStatus.ERROR_COMMS, null);
              return;
          }
          newState(State.FETCHING_TERMS_OF_SERVICE);
          if (!setReqId(Auth.getTermsAndConditions(this))) {
              updateTermsState(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sends a fetch privacy statement request to the server.
       */
      private void startFetchPrivacyStatement() {
          LogUtils.logD("LoginEngine.startFetchPrivacyStatement()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
              updateTermsState(ServiceStatus.ERROR_COMMS, null);
              return;
          }
          newState(State.FETCHING_PRIVACY_STATEMENT);
          if (!setReqId(Auth.getPrivacyStatement(this))) {
              updateTermsState(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sends a fetch user-name state request to the server.
       * 
       * @param username, the user-name to retrieve information for.
       */
      private void startFetchUsernameState(String username) {
          LogUtils.logD("LoginEngine.startFetchUsernameState()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          mLoginDetails.mUsername = username;
          newState(State.FETCHING_USERNAME_STATE);
          if (!setReqId(Auth.getUsernameState(this, username))) {
              completeUiRequest(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sets registration complete flag to false then starts a new sign-in
       * request.
       * 
       * @param details Login details received from the UI request
       */
      private void startManualLoginProcess(LoginDetails details) {
          LogUtils.logD("LoginEngine.startManualLoginProcess()");
          setRegistrationComplete(false);
          setActivatedSession(null);
          if (details == null) {
              completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER, null);
              return;
          }
          mLoginDetails.copy(details);
          mLoginDetails.mSubscriberId = SimCard.getSubscriberId(mContext);
          mDb.modifyCredentialsAndPublicKey(mLoginDetails, mPublicKey);
  
          if (Settings.ENABLE_ACTIVATION) {
              startRequestActivationCode();
          } else {
              startGetSessionManual();
          }
      }
  
      /**
       * Sends a request activation code request to the server.
       */
      private void startRequestActivationCode() {
          LogUtils.logD("LoginEngine.startRequestActivationCode()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          mActivationCode = null;
          newState(State.REQUESTING_ACTIVATION_CODE);
          if (!setReqId(Auth.requestActivationCode(this, mLoginDetails.mUsername,
                  mLoginDetails.mMobileNo))) {
              completeUiRequest(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sends a get session by credentials request to the server.
       */
      private void startGetSessionManual() {
          LogUtils.logD("LoginEngine.startGetSessionManual()");
          
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          newState(State.CREATING_SESSION_MANUAL);
          if (!setReqId(Auth.getSessionByCredentials(this, mLoginDetails.mUsername,
                  mLoginDetails.mPassword, null))) {
              completeUiRequest(ServiceStatus.ERROR_COMMS, null);
              return;
          }
      }
  
      /**
       * Sends a activate account request to the server.
       */
      private void startActivateAccount() {
          LogUtils.logD("LoginEngine.startActivateAccount()");
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
          	completeUiRequest(NetworkAgent.getServiceStatusfromDisconnectReason(), null);
              return;
          }
          
          if (Settings.ENABLE_ACTIVATION) {
              newState(State.ACTIVATING_ACCOUNT);
              if (!setReqId(Auth.activate(this, mActivationCode))) {
                  completeUiRequest(ServiceStatus.ERROR_COMMS, null);
                  return;
              }
          } else {
              setRegistrationComplete(true);
              completeUiRequest(ServiceStatus.SUCCESS, null);
          }
      }
  
      /**
       * Clears the session and registration complete information so that the user
       * will need to manually login again to use the now+ services. Does not
       * currently send a request to the server to log out.
       */
      private void startLogout() {
          LogUtils.logD("LoginEngine.startLogout()");
          setRegistrationComplete(false);
          setActivatedSession(null);
          completeUiRequest(ServiceStatus.SUCCESS, null);
      }
  
      /**
       * Clears the session and registration complete information so that the user
       * will need to manually login again to use the now+ services. Does not
       * currently send a request to the server to log out.
       */
      public final void logoutAndRemoveUser() {
  
          LogUtils.logD("LoginEngine.startLogout()");
          addUiRemoveUserDataRequest();
          LoginPreferences.clearPreferencesFile(mContext);
          mUiAgent.sendUnsolicitedUiEvent(ServiceUiRequest.UNSOLICITED_GO_TO_LANDING_PAGE, null);
      }
  
      /**
       * Retries to log the user in based on credential information stored in the
       * database.
       * 
       * @return true if the login process was able to start
       */
      public boolean retryAutoLogin() {
          LogUtils.logD("LoginEngine.retryAutoLogin()");
          setActivatedSession(null);
          if (ServiceStatus.SUCCESS != mDb.fetchLogonCredentialsAndPublicKey(mLoginDetails,
                  mPublicKey)) {
              LogUtils
                      .logE("LoginEngine.retryAutoLogin() - Unable to fetch credentials from database");
              mAreLoginDetailsValid = false;
              newState(State.LOGIN_FAILED);
              return false;
          }
  
          // AA: commented the condition out if (Settings.ENABLE_ACTIVATION) {
          // AA: the old version if (mCurrentSubscriberId == null ||
          // !mCurrentSubscriberId.equals(mLoginDetails.mSubscriberId)) { //
          // logging off/fail will be done in another way according to bug 8288
          final String currentSubscriberId = SimCard.getSubscriberId(mContext);
          if (currentSubscriberId != null
                  && !currentSubscriberId.equals(mLoginDetails.mSubscriberId)) {
              LogUtils.logV("LoginEngine.retryAutoLogin() -"
                      + " SIM card has changed or is missing (old subId = "
                      + mLoginDetails.mSubscriberId + ", new subId = " + currentSubscriberId + ")");
              mAreLoginDetailsValid = false;
              newState(State.LOGIN_FAILED);
              return false;
          }
          // }
  
          if (mLoginDetails.mUsername == null || mLoginDetails.mPassword == null
                  || mLoginDetails.mMobileNo == null) {
              LogUtils.logV("LoginEngine.retryAutoLogin() - Username, password "
                      + "or mobile number are missing (old username = " + mLoginDetails.mUsername
                      + ", mobile no = " + mLoginDetails.mMobileNo + ")");
              mAreLoginDetailsValid = false;
              newState(State.LOGIN_FAILED);
              return false;
          }
          mAreLoginDetailsValid = true;
  
          if (NetworkAgent.getAgentState() != NetworkAgent.AgentState.CONNECTED) {
              newState(State.LOGGED_OFF_WAITING_FOR_NETWORK);
              LogUtils.logV("LoginEngine.retryAutoLogin() - Internet connection down.  "
                      + "Will try again when connection is available");
              return false;
          }
  
          newState(State.CREATING_SESSION_AUTO);
          if (!setReqId(Auth.getSessionByCredentials(this, mLoginDetails.mUsername,
                  mLoginDetails.mPassword, null))) {
              return false;
          }
          return true;
      }
  
      /**
       * Helper function to set the registration complete flag and update the
       * database with the new state.
       * 
       * @param value true if registration is completed
       */
      private void setRegistrationComplete(boolean value) {
          LogUtils.logD("LoginEngine.setRegistrationComplete(" + value + ")");
          if (value != mIsRegistrationComplete) {
              StateTable.setRegistrationComplete(value, mDb.getWritableDatabase());
              mIsRegistrationComplete = value;
              if (mIsRegistrationComplete) {
                  // Create NAB Account at this point (does nothing on 1.X
                  // devices)
                  final NativeContactsApi nabApi = NativeContactsApi.getInstance();
                  if (!nabApi.isPeopleAccountCreated()) {
                      // TODO: React upon failure to create account
                      nabApi.addPeopleAccount(LoginPreferences.getUsername());
                  }
              }
          }
      }
  
      /**
       * Changes the state of the engine. Also displays the login notification if
       * necessary.
       * 
       * @param newState The new state
       */
      private void newState(State newState) {
          State oldState = mState;
          synchronized (mMutex) {
              if (newState == mState) {
                  return;
              }
              mState = newState;
          }
          LogUtils.logV("LoginEngine.newState: " + oldState + " -> " + mState);
          Intent intent = null;
          // Update notification
          switch (mState) {
              case LOGIN_FAILED_WRONG_CREDENTIALS:
                  intent = new Intent();
                  intent.setAction(Intents.START_LOGIN_ACTIVITY);
                  mContext.sendBroadcast(intent);
  
                  setRegistrationComplete(false);            	
                  break;
              // here should be no break
              case NOT_REGISTERED:
              case LOGIN_FAILED:
  //                intent = new Intent();
  //                intent.setAction(Intents.LOGIN_FAILED);
  //                mContext.sendBroadcast(intent);
  
                  setRegistrationComplete(false);
                  // startLogout();
                  // mDb.removeUserData();
                  // sending user to login screen again
                  // should be done by UI itself because
                  // when it's done from here it cause problems when user tries to
                  // login
                  // giving wrong credentials, ui flow will be broken
  
                  break;
              case LOGGED_OFF:
              case LOGGED_OFF_WAITING_FOR_NETWORK:
              case LOGGED_OFF_WAITING_FOR_RETRY:
              case LOGGED_ON:
  //                intent = new Intent();
  //                intent.setAction(Intents.HIDE_LOGIN_NOTIFICATION);
  //                mContext.sendBroadcast(intent);
                  break;
              default:// do nothing
                  break;
          }
  
          // Update listeners with any state changes
          switch (mState) {
              case NOT_REGISTERED:
              case LOGIN_FAILED:
              case LOGIN_FAILED_WRONG_CREDENTIALS:
              case LOGGED_OFF:
                  onLoginStateChanged(false);
                  break;
              case LOGGED_ON:
                  onLoginStateChanged(true);
                  break;
              default: // do nothing.
                  break;
          }
      }
  
      /**
       * Called when the engine transitions between the logged in and logged out
       * states. Notifies listeners.
       * 
       * @param loggedIn true if the user is now logged in, false otherwise.
       */
      private synchronized void onLoginStateChanged(boolean loggedIn) {
          LogUtils.logD("LoginEngine.onLoginStateChanged() Login state changed to "
                  + (loggedIn ? "logged in." : "logged out."));
          if (loggedIn == mCurrentLoginState) {
              return;
          }
          mCurrentLoginState = loggedIn;
          for (ILoginEventsListener listener : mEventsListener) {
              listener.onLoginStateChanged(loggedIn);
          }
      }
  
      /**
       * A helper function which determines which activity should be displayed
       * when the UI is loaded.
       * 
       * @return true if landing page should be displayed, false otherwise
       */
      public synchronized boolean getLoginRequired() {
          LogUtils.logD("LoginEngine.getLoginRequired() - " + !mIsRegistrationComplete);
          return !mIsRegistrationComplete;
      }
  
      /**
       * Retrieves the active comms session.
       * 
       * @return The session or NULL if the user is logged out.
       */
      public static AuthSessionHolder getSession() {
          return sActivatedSession;
      }
  
      /**
       * Helper function to store the new session in the database and inform
       * clients that the session has changed.
       * 
       * @param session The new session or NULL if the user has logged off.
       */
      public synchronized void setActivatedSession(AuthSessionHolder session) {
          LogUtils.logD("LoginEngine.setActivatedSession() session[" + session + "]");
          sActivatedSession = session;
          if (session != null) {
              LogUtils.logD("LoginEngine.setActivatedSession() Login successful");
          } else {
              LogUtils.logW("LoginEngine.setActivatedSession() "
                      + "Login unsuccessful, the session is NULL");
          }
          StateTable.setSession(session, mDb.getWritableDatabase());
      }
  
      /**
       * Called when a response to the sign-up API is received. In case of success
       * sets a timeout value waiting for the activation SMS to arrive.
       * 
       * @param data The received data
       */
      private void handleSignUpResponse(List<BaseDataType> data) {
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.CONTACT_DATA_TYPE, data);
          LogUtils.logD("LoginEngine.handleSignUpResponse() errorStatus[" + errorStatus.name() + "]");
          if (errorStatus == ServiceStatus.SUCCESS) {
              LogUtils.logD("LoginEngine.handleSignUpResponse() - Registration successful");
              if (!Settings.ENABLE_ACTIVATION) {
                  startGetSessionManual();
              } else {
                  // Now waiting for SMS...
                  setTimeout(ACTIVATE_LOGIN_TIMEOUT);
              }
              // AA
          } else if (errorStatus == ServiceStatus.ERROR_INVALID_PUBLIC_KEY) {
              // start new key retrieval and make the new cycle
              getNewPublicKey();
  
          } else {
              completeUiRequest(errorStatus, null);
          }
      }
  
      /**
       * Called when a response to the server fetch public key request is
       * received. Validates the response and stores the new public key details.
       * 
       * @param mDataTypes Response data from server.
       */
      private void handleNewPublicKeyResponse(List<BaseDataType> mDataTypes) {
          LogUtils.logD("LoginEngine.handleNewPublicKeyResponse()");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.PUBLIC_KEY_DETAILS_DATA_TYPE, mDataTypes);
          if (errorStatus == ServiceStatus.SUCCESS) {
              LogUtils.logD("LoginEngine.handleNewPublicKeyResponse() - Succesfully retrieved");
              // AA
              // 1. save to DB; save the flag that we aren't using default and
              // have to use one from DB
              // 2. start registration again
              mPublicKey = (PublicKeyDetails)mDataTypes.get(0);
              // done in startRegistrationProcessCrypted already
              // mDb.modifyCredentialsAndPublicKey(mLoginDetails, mPublicKey);
              startRegistrationProcessCrypted(mRegistrationDetails);
          } else {
              completeUiRequest(errorStatus, null);
          }
      }
  
      /**
       * Called when a response to the GetSessionByCredentials API is received
       * (manual login). In case of success, tries to activate the account using
       * the activation code received by SMS.
       * 
       * @param data The received data
       */
      private void handleCreateSessionManualResponse(List<BaseDataType> data) {
          LogUtils.logD("LoginEngine.handleCreateSessionManualResponse()");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.AUTH_SESSION_HOLDER_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS && (data.size() > 0)) {
              setActivatedSession((AuthSessionHolder)data.get(0));
              startActivateAccount();
              return;
          }
          completeUiRequest(errorStatus, null);
      }
  
      /**
       * Called when a response to the GetSessionByCredentials API is received
       * (auto login). In case of success, moves to the logged in state
       * 
       * @param data The received data
       */
      private void handleCreateSessionAutoResponse(List<BaseDataType> data) {
          LogUtils.logD("LoginEngine.handleCreateSessionResponse()");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.AUTH_SESSION_HOLDER_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS && (data.size() > 0)) {
              clearTimeout();
              setActivatedSession((AuthSessionHolder)data.get(0));
              newState(State.LOGGED_ON);
          } else {
              LogUtils.logE("LoginEngine.handleCreateSessionResponse() - Auto Login Failed Error "
                      + errorStatus);
              // AA:the 1st retry failed, just go to the start page,
              // if (loginAttemptsRemaining()
              // && errorStatus!=ServiceStatus.ERROR_INVALID_PASSWORD) {
              // newState(State.LOGGED_OFF_WAITING_FOR_RETRY);
              // setTimeout(LOGIN_RETRY_TIME);
              // } else {
              // mAreLoginDetailsValid = false;
              if (errorStatus == ServiceStatus.ERROR_INVALID_PASSWORD) {
                  mAreLoginDetailsValid = false;
                  newState(State.LOGIN_FAILED_WRONG_CREDENTIALS);
              } else {
                  newState(State.LOGIN_FAILED);
              }
              // }
          }
      }
  
      /**
       * Called when a response to the RequestActivationCode API is received
       * (manual login). In case of success, tries to fetch a login session from
       * the server.
       * 
       * @param data The received data
       */
      private void handleRequestingActivationResponse(List<BaseDataType> data) {
          LogUtils.logD("LoginEngine.handleRequestingActivationResponse()");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.STATUS_MSG_DATA_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS) {
              // Now waiting for SMS...
              setTimeout(ACTIVATE_LOGIN_TIMEOUT);
              return;
          }
          completeUiRequest(errorStatus, null);
      }
  
      /**
       * Called when a response to the Activate API is received (manual login or
       * sign-up). In case of success, moves to the logged in state and completes
       * the manual login or sign-up request.
       * 
       * @param data The received data
       */
      private void handleActivateAccountResponse(List<BaseDataType> data) {
          LogUtils.logD("LoginEngine.handleActivateAccountResponse()");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.STATUS_MSG_DATA_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS) {
              LogUtils
                      .logD("LoginEngine.handleActivateAccountResponse: ** Mobile number activated **");
              setRegistrationComplete(true);
              completeUiRequest(ServiceStatus.SUCCESS, null);
              return;
          }
          setActivatedSession(null);
          completeUiRequest(ServiceStatus.ERROR_ACCOUNT_ACTIVATION_FAILED, null);
      }
  
      /**
       * Called when a response to the GetTermsAndConditions, GetPrivacyStatement
       * and GetUsernameState APIs are received. In case of success, completes the
       * request and passes the response data to the UI.
       * 
       * @param data The received data
       */
      private void handleServerSimpleTextResponse(List<BaseDataType> data, State type) {
          LogUtils.logV("LoginEngine.handleServerSimpleTextResponse()");
          ServiceStatus serviceStatus = getResponseStatus(BaseDataType.SIMPLE_TEXT_DATA_TYPE, data);
  
          String result = null;
          if (serviceStatus == ServiceStatus.SUCCESS) {
              result = ((SimpleText) data.get(0)).mValue.toString().replace(
                      CARRIAGE_RETURN_CHARACTER, SPACE_CHARACTER);
  
              switch (type) {
                  case FETCHING_TERMS_OF_SERVICE:
                      LogUtils.logD("LoginEngine.handleServerSimpleTextResponse() Terms of Service");
                      ApplicationCache.setTermsOfService(result, mContext);
                      break;
                  case FETCHING_PRIVACY_STATEMENT:
                      LogUtils.logD("LoginEngine.handleServerSimpleTextResponse() Privacy Statemet");
                      ApplicationCache.setPrivacyStatemet(result, mContext);
                      break;
                  case FETCHING_USERNAME_STATE:
                      // TODO: Unused by UI.
                      break;
              }
          }
  
          updateTermsState(serviceStatus, result);
      }
  
      /***
       * Informs the UI to update any terms which are being shown on screen.
       *
       * @param serviceStatus Current ServiceStatus.
       * @param messageText Legacy call for old UI (TODO: remove after UI-Refresh
       *          merge).  NULL when combined with a ServiceStatus of
       *          ERROR_COMMS, or contains the Privacy or Terms and Conditions
       *          text to be displayed in the UI.
       */
      private void updateTermsState(ServiceStatus serviceStatus, String messageText) {
          ApplicationCache.setTermsStatus(serviceStatus);
  
          /** Trigger UiAgent. **/
          mUiAgent.sendUnsolicitedUiEvent(ServiceUiRequest.TERMS_CHANGED_EVENT, null);
  
          /** Clear this request from the UI queue. **/
          completeUiRequest(serviceStatus, messageText);
      }
  
      /**
       * A broadcast receiver which is used to receive notifications when a data
       * SMS arrives.
       */
      private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
          /**
           * Called when an SMS arrives. The activation code is extracted from the
           * given intent and the worker thread kicked.
           * 
           * @param context Context from which the intent was broadcast
           * @param intent Will only process the SMS which the action is
           *            {@link SmsBroadcastReceiver#ACTION_ACTIVATION_CODE}.
           */
          @Override
          public void onReceive(Context context, Intent intent) {
              LogUtils.logD("LoginEngine.BroadcastReceiver.onReceive - Processing data sms");
              if (intent.getAction().equals(SmsBroadcastReceiver.ACTION_ACTIVATION_CODE)) {
                  String activationCode = intent.getStringExtra("code");
                  LogUtils
                          .logD("LoginEngine.BroadcastReceiver.onReceive - Activation code Received: "
                                  + activationCode);
                  synchronized (LoginEngine.this) {
                      mActivationCode = activationCode;
                  }
                  mEventCallback.kickWorkerThread();
              }
          }
  
      };
  
      /**
       * Called when an SMS is received during sign-up or manual login. Starts
       * requesting a session from the server.
       */
      private void handleSmsResponse() {
          LogUtils.logD("LoginEngine.handleSmsResponse(" + mActivationCode + ")");
          clearTimeout();
          startGetSessionManual();
      }
  
      /**
       * Called by the base engine implementation whenever a UI request is
       * completed to do any necessary cleanup. We use it to restore our state to
       * a suitable value.
       */
      @Override
      protected void onRequestComplete() {
          LogUtils.logD("LoginEngine.onRequestComplete()");
          restoreLoginState();
      }
  
      /**
       * Handles timeouts for SMS activation and auto login retries.
       */
      protected synchronized void onTimeoutEvent() {
          LogUtils.logD("LoginEngine.onTimeoutEvent()");
          switch (mState) {
              case REQUESTING_ACTIVATION_CODE:
              case SIGNING_UP:
                  completeUiRequest(ServiceStatus.ERROR_SMS_CODE_NOT_RECEIVED, null);
                  break;
              case LOGGED_OFF_WAITING_FOR_RETRY:
                  retryAutoLogin();
                  break;
              default: // do nothing.
                  break;
          }
      }
  
      /**
       * Called by the framework before a remove user data operation takes place.
       * Initiates a suitable UI request which will kick the worker thread.
       */
      @Override
      public void onReset() {
          
          // reset the engine as if it was just created
          super.onReset();
          setRegistrationComplete(false);
          mState = State.NOT_REGISTERED;
          mRegistrationDetails = new RegistrationDetails();
          mActivationCode = null;
          onLoginStateChanged(false);
      }
  
      /**
       * Set 'dummy' auth session for test purposes only.
       * 
       * @param session 'dummy' session supplied to LoginEngine
       */
      public static void setTestSession(AuthSessionHolder session) {
          sActivatedSession = session;
      }
  }
  