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.identities;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import android.os.Bundle;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.database.tables.MyIdentitiesCacheTable;
  import com.vodafone360.people.datatypes.BaseDataType;
  import com.vodafone360.people.datatypes.Identity;
  import com.vodafone360.people.datatypes.IdentityCapability;
  import com.vodafone360.people.datatypes.PushEvent;
  import com.vodafone360.people.datatypes.StatusMsg;
  import com.vodafone360.people.engine.BaseEngine;
  import com.vodafone360.people.engine.IEngineEventCallback;
  import com.vodafone360.people.engine.EngineManager.EngineId;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.service.ServiceUiRequest;
  import com.vodafone360.people.service.agent.UiAgent;
  import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse;
  import com.vodafone360.people.service.io.api.Identities;
  import com.vodafone360.people.service.io.rpg.PushMessageTypes;
  import com.vodafone360.people.service.transport.ConnectionManager;
  import com.vodafone360.people.service.transport.tcp.ITcpConnectionListener;
  import com.vodafone360.people.utils.LogUtils;
  import com.vodafone360.people.utils.ThirdPartyAccount;
  
  /**
   * Engine responsible for handling retrieval and validation of Identities (e.g.
   * 3rd party/web accounts). The Identities engine can fetch a list of available
   * accounts, and set required capabilities and validate credentials for
   * specified accounts.
   */
  public class IdentityEngine extends BaseEngine implements ITcpConnectionListener {
  
      /**
       * States for IdentitiesEngine. States are based on the requests that the
       * engine needs to handle.
       */
      private enum State {
          IDLE,
          FETCHING_IDENTITIES,
          VALIDATING_IDENTITY_CREDENTIALS,
          SETTING_IDENTITY_STATUS,
          GETTING_MY_IDENTITIES,
          GETTING_MY_CHATABLE_IDENTITIES,
          DELETE_IDENTITY
      }
  
      /**
       * Mutex for thread synchronisation
       */
      private final Object mMutex = new Object();
  
      /**
       * Container class for Identity Capability Status request. Consists of a
       * network, identity id and a filter containing the required capabilities.
       */
      private static class IdentityStatusRequest {
          private String mNetwork;
  
          private String mIdentityId;
  
          private String mIdentityStatus = null;
  
          /**
           * Supply filter containing required capabilities.
           * 
           * @param filter Bundle containing capabilities filter.
           */
          public void setIdentityStatus(String status) {
              mIdentityStatus = status;
          }
      }
  
      /**
       * Container class encapsulating an Identity validation request containing:
       * dry-run flag, network, user-name, password, set of required capabilities.
       */
      private static class IdentityValidateCredentialsRequest {
      	/** Performs a dry run if true. */
          private boolean mDryRun;
          /** Network to sign into. */
          private String mNetwork;
          /** Username to sign into identity with. */
          private String mUserName;
          /** Password to sign into identity with. */
          private String mPassword;
  
          private Map<String, Boolean> mStatus = null;
  
          /**
           * Supply filter containing required capabilities.
           * 
           * @param filter Bundle containing capabilities filter.
           */
          public void setCapabilityStatus(Bundle filter) {
              mStatus = prepareBoolFilter(filter);
          }
      }
      
      /**
       * 
       * Container class for Delete Identity request. Consist network and identity id.
       *
       */
      private static class DeleteIdentityRequest {
      	/** Network to delete.*/
      	private String mNetwork;
      	/** IdentityID which needs to be Deleted.*/
      	private String mIdentityId;
      }
  
      /** The minimum interval between identity requests. */
      private static final long MIN_REQUEST_INTERVAL = 60 * 60 * 1000;
      /** The timestamp of which my identities were last requested. */
      private long mLastMyIdentitiesRequestTimestamp;
      /** The timestamp of which available identities were last requested. */
      private long mLastAvailableIdentitiesRequestTimestamp;
      
      /** The state of the state machine handling ui requests. */
      private State mState = State.IDLE;
  
      /** List array of Identities retrieved from Server. */
      private final ArrayList<Identity> mAvailableIdentityList;
      
      /** List array of Identities retrieved from Server. */
      private final ArrayList<Identity> mMyIdentityList;
  
      /** Holds the status messages of the setIdentityCapability-request. */
      private final ArrayList<StatusMsg> mStatusList = new ArrayList<StatusMsg>();
  
      /** The key for setIdentityCapability data type for the push ui message. */
      public static final String KEY_DATA = "data";
      /** The key for available identities for the push ui message. */
  	public static final String KEY_AVAILABLE_IDS = "availableids";
  	/** The key for my identities for the push ui message. */
  	public static final String KEY_MY_IDS = "myids";
  	/**
  	 * Maintaining DeleteIdentityRequest so that it can be later removed from
  	 * maintained cache.
  	 **/
  	private DeleteIdentityRequest identityToBeDeleted;
  	
      /**
       * The hard coded list of capabilities we use to getAvailableIdentities(): chat and status.
       */
      private final Map<String, List<String>> mGetAvailableIdentitiesFilter;
      
      /**
       * The hard coded list of capabilities we use to getMyIdentities(): chat and status.
       */
      private final Map<String, List<String>> mGetMyIdentitiesFilter;
      
      /**
       * The DatabaseHelper used to access the client database.
       */
      private final DatabaseHelper mDatabaseHelper;
  
      /**
       * A list containing all the currently pending SNS.
       */
      private List<String> mCurrentlyPendingSns;
      
      /**
       * Constructor
       * 
       * @param eventCallback IEngineEventCallback allowing engine to report back.
       */
      public IdentityEngine(IEngineEventCallback eventCallback, DatabaseHelper databaseHelper) {
          super(eventCallback);
          mEngineId = EngineId.IDENTITIES_ENGINE;
          mDatabaseHelper = databaseHelper;
          
          mMyIdentityList = new ArrayList<Identity>();
          // restore cached identities
          MyIdentitiesCacheTable.getCachedIdentities(databaseHelper.getReadableDatabase(),
                                                     mMyIdentityList);
          mAvailableIdentityList = new ArrayList<Identity>();
                  
          mLastMyIdentitiesRequestTimestamp = 0;
          mLastAvailableIdentitiesRequestTimestamp = 0;
          
          // initialize identity capabilities filter
          mGetAvailableIdentitiesFilter = new Hashtable<String, List<String>>();
          final List<String> capabilities = new ArrayList<String>();
          capabilities.add(IdentityCapability.CapabilityID.chat.name());
          capabilities.add(IdentityCapability.CapabilityID.get_own_status.name());
          
          mGetAvailableIdentitiesFilter.put(Identity.CAPABILITY, capabilities);
          
          final List<String> authType = new ArrayList<String>();
          authType.add(Identity.AUTH_TYPE_URL);
          authType.add(Identity.AUTH_TYPE_CREDENTIALS);
                  
          mGetAvailableIdentitiesFilter.put(Identity.AUTH_TYPE, authType);
          
          mGetMyIdentitiesFilter = new Hashtable<String, List<String>>();
          mGetMyIdentitiesFilter.put(Identity.CAPABILITY, capabilities);
      }
      
      /**
       * 
       * Gets all third party identities and adds the mobile identity
       * from 360 to them.
       * 
       * @return A list of all 3rd party identities the user is signed in to plus 
       * the 360 identity mobile. If the retrieval failed the list will
       * be empty.
       * 
       */
      public ArrayList<Identity> getAvailableThirdPartyIdentities() {
          
          final ArrayList<Identity> availableIdentityList;
          
          synchronized(mAvailableIdentityList) {
              // make a shallow copy
              availableIdentityList = new ArrayList<Identity>(mAvailableIdentityList);
          }
          
      	if ((availableIdentityList.size() == 0) && (
      			(System.currentTimeMillis() - mLastAvailableIdentitiesRequestTimestamp)
      				> MIN_REQUEST_INTERVAL)) {
      		sendGetAvailableIdentitiesRequest();
      	}
      	    	
      	return availableIdentityList;
      }
      
      /**
       * 
       * Gets all third party identities the user is currently signed up for. 
       * 
       * @return A list of 3rd party identities the user is signed in to or an 
       * empty list if something  went wrong retrieving the identities. 
       * 
       */
      public ArrayList<Identity> getMyThirdPartyIdentities() {
          
          final ArrayList<Identity> myIdentityList;
          
          synchronized(mMyIdentityList) {
              // make a shallow copy
              myIdentityList = new ArrayList<Identity>(mMyIdentityList);
          }
          
          if ((myIdentityList.size() == 0) && (
              (System.currentTimeMillis() - mLastMyIdentitiesRequestTimestamp)
      				> MIN_REQUEST_INTERVAL)) {
      		sendGetMyIdentitiesRequest();
      	}
          
      	return myIdentityList;
  	}
      
      /**
       * 
       * Gets the identity with the specified network name.
       * 
       * @param networkName The network name to get the identity for. E.g. "facebook.com".
       * 
       * @return The identity that matches the passed network name or null if the network name was
       * null or could not be found.
       * 
       */
      public Identity getIdentityForNetworkName(final String networkName) {
      	if (networkName == null) {
      		return null;
      	}
      	
      	for (int i = 0; i < mAvailableIdentityList.size(); i++) {
      		Identity identity = mAvailableIdentityList.get(i);
      		
      		if (identity != null && networkName.equals(identity.mNetwork)) {
      			return identity;
      		}
      	}
      	
      	return null;
      }
      
      
      /**
       * 
       * Takes all third party identities that have a chat capability set to true.
       * 
       * @return A list of chattable 3rd party identities the user is signed in to. If the retrieval identities failed the returned list will be empty.
       * 
       */
      public ArrayList<Identity> getMyChattableIdentities() {
      	final ArrayList<Identity> chattableIdentities = new ArrayList<Identity>();
      	final ArrayList<Identity> myIdentityList;
      	final int identityListSize;
          
          synchronized(mMyIdentityList) {
              // make a shallow copy
              myIdentityList = new ArrayList<Identity>(mMyIdentityList);
          }
      	
      	identityListSize = myIdentityList.size(); 
      	
      	// checking each identity for its chat capability and adding it to the
      	// list if it does
      	for (int i = 0; i < identityListSize; i++) {
      		Identity identity = myIdentityList.get(i);
      		List<IdentityCapability> capabilities = identity.mCapabilities;
      		
      		if (null == capabilities) {
      			continue;	// if the capabilties are null skip to next identity
      		}
      		
      		// run through capabilties and check for chat
      		for (int j = 0; j < capabilities.size(); j++) {
      			IdentityCapability capability = capabilities.get(j);
      			
      			if (null == capability) {
      				continue;	// skip null capabilities
      			}
      			
      			if ((capability.mCapability == IdentityCapability.CapabilityID.chat) &&
      					(capability.mValue)) {
      				chattableIdentities.add(identity);
      				break;
      			}
      		}
      	}
      	
      	return chattableIdentities;
      }
      
      
      /**
       * Sends a get my identities request to the server which will be handled
       * by onProcessCommsResponse once a response comes in.
       */
      private void sendGetMyIdentitiesRequest() {
          Identities.getMyIdentities(this, mGetMyIdentitiesFilter);
      }
      
      /**
       * Send a get available identities request to the backend which will be 
       * handled by onProcessCommsResponse once a response comes in.
       */
      private void sendGetAvailableIdentitiesRequest() {
      	Identities.getAvailableIdentities(this, mGetAvailableIdentitiesFilter);
      }
  
      /**
       * Enables or disables the given social network.
       * 
       * @param network Name of the identity,
       * @param identityId Id of identity.
       * @param identityStatus True if identity should be enabled, false otherwise.
       */
      public void addUiSetIdentityStatus(String network, String identityId, boolean identityStatus) {
          LogUtils.logD("IdentityEngine.addUiSetIdentityCapabilityStatus()");
          IdentityStatusRequest data = new IdentityStatusRequest();
          data.mIdentityId = identityId;
          data.mNetwork = network;
          data.setIdentityStatus(identityStatus ? Identities.ENABLE_IDENTITY
                  : Identities.DISABLE_IDENTITY);
          // do not empty reqQueue here, ui can put many at one time
          addUiRequestToQueue(ServiceUiRequest.SET_IDENTITY_CAPABILITY_STATUS, data);
      }
  
      /**
       * TODO: re-factor the method in the way that the UI doesn't pass the Bundle with capabilities
       * list to the Engine, but the Engine makes the list itself (UI/Engine separation).
       *  
       * Add request to validate user credentials for a specified identity.
       * 
       * @param dryRun True if this is a dry-run.
       * @param network Name of the network/identity.
       * @param username User-name for login for this identity.
       * @param password Password for login for this identity.
       * @param identityCapabilityStatus Bundle containing capability details for
       *            this identity.
       */
      public void addUiValidateIdentityCredentials(boolean dryRun, String network, String username,
              String password, Bundle identityCapabilityStatus) {
          LogUtils.logD("IdentityEngine.addUiValidateIdentityCredentials()");
          IdentityValidateCredentialsRequest data = new IdentityValidateCredentialsRequest();
          data.mDryRun = dryRun;
          data.mNetwork = network;
          data.mPassword = password;
          data.mUserName = username;
          data.setCapabilityStatus(identityCapabilityStatus);
          emptyUiRequestQueue();
          addUiRequestToQueue(ServiceUiRequest.VALIDATE_IDENTITY_CREDENTIALS, data);
      }
      
  
      /**
       * Delete the given social network.
       *
       * @param network Name of the identity,
       * @param identityId Id of identity.
       */
      public final void addUiDeleteIdentityRequest(final String network, final String identityId) {
      	LogUtils.logD("IdentityEngine.addUiRemoveIdentity()");
      	DeleteIdentityRequest data = new DeleteIdentityRequest();
  
      	data.mNetwork = network;
      	data.mIdentityId = identityId;
  
      	/**maintaining the sent object*/
      	setIdentityToBeDeleted(data);
  
      	addUiRequestToQueue(ServiceUiRequest.DELETE_IDENTITY, data);
      }
  
  	/**
  	 * Setting the DeleteIdentityRequest object.
  	 *
  	 * @param data
  	 */
  	private void setIdentityToBeDeleted(final DeleteIdentityRequest data) {
  		identityToBeDeleted = data;
  	}
  
  	/**
  	 * Return the DeleteIdentityRequest object.
  	 *
  	 */
  	private DeleteIdentityRequest getIdentityToBeDeleted() {
  		return identityToBeDeleted;
  	}
  
      /**
       * Issue any outstanding UI request.
       *
       * @param requestType Request to be issued.
       * @param dara Data associated with the request.
       */
      @Override
      protected void processUiRequest(ServiceUiRequest requestType, Object data) {
          LogUtils.logD("IdentityEngine.processUiRequest() - reqID = " + requestType);
          switch (requestType) {
              case GET_MY_IDENTITIES:
                  sendGetMyIdentitiesRequest();
                  completeUiRequest(ServiceStatus.SUCCESS);
                  break;
              case VALIDATE_IDENTITY_CREDENTIALS:
                  executeValidateIdentityCredentialsRequest(data);
                  break;
              case SET_IDENTITY_CAPABILITY_STATUS:
                  executeSetIdentityStatusRequest(data);
                  break;
              case DELETE_IDENTITY:
              	executeDeleteIdentityRequest(data);
              	break;
              default:
                  completeUiRequest(ServiceStatus.ERROR_NOT_FOUND, null);
                  break;
          }
      }
  
      /**
       * Issue request to set capabilities for a given Identity. (Request is not
       * issued if there is currently no connectivity).
       * 
       * @param data Bundled request data.
       */
      private void executeSetIdentityStatusRequest(Object data) {
          if (!isConnected()) {
              return;
          }
          newState(State.SETTING_IDENTITY_STATUS);
          IdentityStatusRequest reqData = (IdentityStatusRequest)data;
          if (!setReqId(Identities.setIdentityStatus(this, reqData.mNetwork, reqData.mIdentityId,
                  reqData.mIdentityStatus))) {
              completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER);
          }
      }
  
      /**
       * Issue request to validate the user credentials for an Identity. (Request
       * is not issued if there is currently no connectivity).
       * 
       * @param data Bundled request data.
       */
      private void executeValidateIdentityCredentialsRequest(Object data) {
          if (!isConnected()) {
              return;
          }
          newState(State.VALIDATING_IDENTITY_CREDENTIALS);
          IdentityValidateCredentialsRequest reqData = (IdentityValidateCredentialsRequest)data;
          if (!setReqId(Identities
                  .validateIdentityCredentials(this, reqData.mDryRun, reqData.mNetwork,
                          reqData.mUserName, reqData.mPassword, null, null, reqData.mStatus))) {
              completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER);
          }
          
          addPendingIdentity(reqData.mNetwork);
      }
      
      /**
       * Issue request to delete the identity as specified . (Request is not issued if there
       * is currently no connectivity).
       *
       * @param data bundled request data containing network and identityId.
       */
  
  	private void executeDeleteIdentityRequest(final Object data) {
  		if (!isConnected()) {
  			completeUiRequest(ServiceStatus.ERROR_NO_INTERNET);
  		}
  		newState(State.DELETE_IDENTITY);
  		DeleteIdentityRequest reqData = (DeleteIdentityRequest) data;
  
  		if (!setReqId(Identities.deleteIdentity(this, reqData.mNetwork,
  				reqData.mIdentityId))) {
  			completeUiRequest(ServiceStatus.ERROR_BAD_SERVER_PARAMETER);
  		}
  	}
      
      /**
       * Process a response received from Server. The response is handled
       * according to the current IdentityEngine state.
       * 
       * @param resp The decoded response.
       */
      @Override
      protected void processCommsResponse(DecodedResponse resp) {  	    	
      	LogUtils.logD("IdentityEngine.processCommsResponse() - resp = " + resp);
      	
      	if ((null == resp) || (null == resp.mDataTypes)) {
      		LogUtils.logE("Response objects or its contents were null. Aborting...");
      		return;
      	}
          
          // TODO replace this whole block with the response type in the DecodedResponse class in the future!
          if (resp.mReqId == 0 && resp.mDataTypes.size() > 0) {	// push msg
              PushEvent evt = (PushEvent)resp.mDataTypes.get(0);
              handlePushResponse(evt.mMessageType);
          } else if (resp.mDataTypes.size() > 0) {				// regular response
  	        switch (resp.mDataTypes.get(0).getType()) {
  	        	case BaseDataType.MY_IDENTITY_DATA_TYPE:
  	        		handleGetMyIdentitiesResponse(resp.mDataTypes);
  	        		break;
  	        	case BaseDataType.AVAILABLE_IDENTITY_DATA_TYPE:
  	        		handleGetAvailableIdentitiesResponse(resp.mDataTypes);
  	        		break;
  	        	case BaseDataType.IDENTITY_CAPABILITY_DATA_TYPE:
  	        		handleSetIdentityStatus(resp.mDataTypes);
  	        		break;
  	        	case BaseDataType.STATUS_MSG_DATA_TYPE:
  	        		handleValidateIdentityCredentials(resp.mDataTypes);
  	        		break;
  	        	case BaseDataType.IDENTITY_DELETION_DATA_TYPE:
  	        		handleDeleteIdentity(resp.mDataTypes);
  	        		break;
  	            default:
  	                LogUtils.logW("IdentityEngine.processCommsResponse DEFAULT should never happened.");
  	                break;
  	        }
          } else {		// responses data list is 0, that means e.g. no identities in an identities response
          	LogUtils.logW("IdentityEngine.processCommsResponse List was empty!");
          	
          	if (resp.getResponseType() == DecodedResponse.ResponseType.GET_MY_IDENTITIES_RESPONSE.ordinal()) {
          		pushIdentitiesToUi(ServiceUiRequest.GET_MY_IDENTITIES);
          	} else if (resp.getResponseType() == DecodedResponse.ResponseType.GET_AVAILABLE_IDENTITIES_RESPONSE.ordinal()) {
          		pushIdentitiesToUi(ServiceUiRequest.GET_AVAILABLE_IDENTITIES);
          	}        	
          } // end: replace this whole block with the response type in the DecodedResponse class in the future!
      }
  
      /**
       * Handle Status or Timeline Activity change Push message
       * 
       * @param evt Push message type (Status change or Timeline change).
       */
      private void handlePushResponse(PushMessageTypes evt) {
          LogUtils.logD("IdentityEngine handlePushRequest");
          switch (evt) {
              case IDENTITY_NETWORK_CHANGE:
                  sendGetAvailableIdentitiesRequest();
                  break;
              case IDENTITY_CHANGE:
              	mMyIdentityList.clear();
                  sendGetMyIdentitiesRequest();
                  mEventCallback.kickWorkerThread();
              	break;
              default:
                  // do nothing
          }
      }
  
      /**
       * Run function called via EngineManager. Should have a UI, Comms response
       * or timeout event to handle.
       */
      @Override
      public void run() {
          LogUtils.logD("IdentityEngine.run()");
          if (isCommsResponseOutstanding() && processCommsInQueue()) {
              LogUtils.logD("IdentityEngine.ResponseOutstanding and processCommsInQueue. mState = "
                      + mState.name());
              return;
          }
          if (processTimeout()) {
              return;
          }
          if (isUiRequestOutstanding()) {
              processUiQueue();
          }
      }
  
      /**
       * Change current IdentityEngine state.
       * 
       * @param newState new state.
       */
      private void newState(State newState) {
          State oldState = mState;
          synchronized (mMutex) {
              if (newState == mState) {
                  return;
              }
              mState = newState;
          }
          LogUtils.logV("IdentityEngine.newState: " + oldState + " -> " + mState);
      }
  
  
      /**
       * Handle Server response to request for available Identities. The response
       * should be a list of Identity items. The request is completed with
       * ServiceStatus.SUCCESS or ERROR_UNEXPECTED_RESPONSE if the data-type
       * retrieved are not Identity items.
       * 
       * @param data List of BaseDataTypes generated from Server response.
       */
      private void handleGetAvailableIdentitiesResponse(List<BaseDataType> data) {    	
          LogUtils.logD("IdentityEngine: handleServerGetAvailableIdentitiesResponse");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.AVAILABLE_IDENTITY_DATA_TYPE, data);
  
          if (errorStatus == ServiceStatus.SUCCESS) {
          	synchronized (mAvailableIdentityList) {
  	            mAvailableIdentityList.clear();
  
  	            for (BaseDataType item : data) {
  	                Identity identity = (Identity) item;
  	                if (!identity.isIdentityFieldBlankorNull()) {
                          mAvailableIdentityList.add(identity);
  	                }
  	            }
          	}
          }
  
          pushIdentitiesToUi(ServiceUiRequest.GET_AVAILABLE_IDENTITIES);
  
          LogUtils.logD("IdentityEngine: handleGetAvailableIdentitiesResponse complete request.");
      }
  
      /**
       * Handle Server response to request for available Identities. The response
       * should be a list of Identity items. The request is completed with
       * ServiceStatus.SUCCESS or ERROR_UNEXPECTED_RESPONSE if the data-type
       * retrieved are not Identity items.
       * 
       * @param data List of BaseDataTypes generated from Server response.
       */
      private void handleGetMyIdentitiesResponse(List<BaseDataType> data) {    	
          LogUtils.logD("IdentityEngine: handleGetMyIdentitiesResponse");
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.MY_IDENTITY_DATA_TYPE, data);
  
  
          if (errorStatus == ServiceStatus.SUCCESS) {  
          	synchronized (mMyIdentityList) {
  
  	            mMyIdentityList.clear();
  
  	            for (BaseDataType item : data) {
  	                Identity identity = (Identity) item;
  	                if (!identity.isIdentityFieldBlankorNull()) {
                          mMyIdentityList.add(identity);
  	                }
  	            }
  	            // cache the identities
  	            MyIdentitiesCacheTable.setCachedIdentities(mDatabaseHelper.getWritableDatabase(),
  	                                                       mMyIdentityList);
  	            clearPendingIdentities();
          	}
          } else if (errorStatus == ServiceStatus.ERROR_COMMS_TIMEOUT) {
          	// reset the pending list in case of a timeout
          	clearPendingIdentities();
          }
          pushIdentitiesToUi(ServiceUiRequest.GET_MY_IDENTITIES);
           
          // remove any identites returned from the pending list as they are validated by now! 
          removePendingIdentities(mMyIdentityList);
          
          LogUtils.logD("IdentityEngine: handleGetMyIdentitiesResponse complete request.");
      }
  
      /**
       * Handle Server response to set validate credentials request. The response
       * should be a Status-msg indicating whether the request has succeeded or
       * failed. The request is completed with the status result (or
       * ERROR_UNEXPECTED_RESPONSE if the data-type retrieved is not a
       * Status-msg).
       * 
       * @param data List of BaseDataTypes generated from Server response.
       */
      private void handleValidateIdentityCredentials(List<BaseDataType> data) {
          Bundle bu = null;
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.STATUS_MSG_DATA_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS) {
              mStatusList.clear();
              for (BaseDataType item : data) {
                  if (item.getType() == BaseDataType.STATUS_MSG_DATA_TYPE) {
                      mStatusList.add((StatusMsg)item);
                  } else {
                      completeUiRequest(ServiceStatus.ERROR_UNEXPECTED_RESPONSE);
                      return;
                  }
              }
              bu = new Bundle();
              if (mStatusList.size() == 1) {
                  bu.putBoolean("status", mStatusList.get(0).mStatus);
              } else {
                  LogUtils.logW("Status list sould have one item. It has " + mStatusList.size());
                  bu.putParcelableArrayList(KEY_DATA, mStatusList);
              }
          }
          completeUiRequest(errorStatus, bu);
  
          newState(State.IDLE);
          
          if (errorStatus == ServiceStatus.SUCCESS) {
              addUiRequestToQueue(ServiceUiRequest.GET_MY_IDENTITIES, null);
          } else if (errorStatus == ServiceStatus.ERROR_COMMS_TIMEOUT){
          	clearPendingIdentities();	// if we timeout we need to clear our pending identities
          }
  
      }
  
      /**
       * Handle Server response to set capability status request. The response
       * should be a Status-msg indicating whether the request has succeeded or
       * failed. The request is completed with the status result (or
       * ERROR_UNEXPECTED_RESPONSE if the data-type retrieved is not a
       * Status-msg).
       * 
       * @param data List of BaseDataTypes generated from Server response.
       */
      private void handleSetIdentityStatus(List<BaseDataType> data) {
          Bundle bu = null;
          ServiceStatus errorStatus = getResponseStatus(BaseDataType.STATUS_MSG_DATA_TYPE, data);
          if (errorStatus == ServiceStatus.SUCCESS) {
              mStatusList.clear();
              for (BaseDataType item : data) {
                  if (item.getType() == BaseDataType.STATUS_MSG_DATA_TYPE) {
                      mStatusList.add((StatusMsg)item);
                  } else {
                      completeUiRequest(ServiceStatus.ERROR_UNEXPECTED_RESPONSE);
                      return;
                  }
              }
              bu = new Bundle();
              bu.putParcelableArrayList(KEY_DATA, mStatusList);
          }
          completeUiRequest(errorStatus, bu);
          newState(State.IDLE);
      }
      
  	/**
  	 * Handle Server response of request to delete the identity. The response
  	 * should be a status that whether the operation is succeeded or not. The
  	 * response will be a status result otherwise ERROR_UNEXPECTED_RESPONSE if
  	 * the response is not as expected.
  	 *
  	 * @param data
  	 *            List of BaseDataTypes generated from Server response.
  	 */
  	private void handleDeleteIdentity(final List<BaseDataType> data) {
  		Bundle bu = null;
  		ServiceStatus errorStatus = getResponseStatus(
  				BaseDataType.IDENTITY_DELETION_DATA_TYPE, data);
  		if (errorStatus == ServiceStatus.SUCCESS) {
  			for (BaseDataType item : data) {
  				if (item.getType() == BaseDataType.IDENTITY_DELETION_DATA_TYPE) {
  					
  				    synchronized(mMyIdentityList) {
  				        // iterating through the subscribed identities
      					for (Identity identity : mMyIdentityList) {
      						if (identity.mIdentityId
      								.equals(getIdentityToBeDeleted().mIdentityId)) {
      							mMyIdentityList.remove(identity);
      							break;
      						}
      					}
      					// cache the new set of identities
      					MyIdentitiesCacheTable.setCachedIdentities(mDatabaseHelper.getWritableDatabase(),
      					                                           mMyIdentityList);
  				    }
  
  					completeUiRequest(ServiceStatus.SUCCESS);
  					return;
  				} else {
  					completeUiRequest(ServiceStatus.ERROR_UNEXPECTED_RESPONSE);
  					return;
  				}
  			}
  		}
  		completeUiRequest(errorStatus, bu);
  	}
  
      /**
       * 
       * Pushes the identities retrieved by get my identities or by get available identities
       * to the ui.
       * 
       * @param request The request type: either get my identities, or get available identities.
       */
      private void pushIdentitiesToUi(ServiceUiRequest request) {
      	String requestKey = null;
      	ArrayList<Identity> idBundle = null;
      	if (request == ServiceUiRequest.GET_AVAILABLE_IDENTITIES) {
      		requestKey = KEY_AVAILABLE_IDS;
      		synchronized (mAvailableIdentityList) {
      		    // provide a shallow copy
      		    idBundle = new ArrayList<Identity>(mAvailableIdentityList);
      		}
      	} else {
      		requestKey = KEY_MY_IDS;
      		synchronized (mMyIdentityList) {
      		    // provide a shallow copy
      		    idBundle = new ArrayList<Identity>(mMyIdentityList);
      		}
      	}
      	
          // send update to 3rd party identities ui if it is up
          Bundle b = new Bundle();
          b.putParcelableArrayList(requestKey, idBundle);
          
          UiAgent uiAgent = mEventCallback.getUiAgent();
          if (uiAgent != null && uiAgent.isSubscribed()) {
              uiAgent.sendUnsolicitedUiEvent(request, b);
          } // end: send update to 3rd party identities ui if it is up
      }
      
      /**
       * Get Connectivity status from the connection manager.
       * 
       * @return true True if the connection is active, false otherwise. 
       * 
       */
      private boolean isConnected() {
      	int connState = ConnectionManager.getInstance().getConnectionState();
          return (connState == ITcpConnectionListener.STATE_CONNECTED);
      }
     
  	@Override
  	public void onConnectionStateChanged(int state) {
  		if (state == ITcpConnectionListener.STATE_CONNECTED) {
  	        emptyUiRequestQueue();
  	        sendGetAvailableIdentitiesRequest();
  	        sendGetMyIdentitiesRequest();
  		} 
  	}
  	
      /**
       * Return the next run-time for the IdentitiesEngine. Will run as soon as
       * possible if we need to issue a request, or we have a resonse waiting.
       * 
       * @return next run-time.
       */
      @Override
      public long getNextRunTime() {
          if (isUiRequestOutstanding()) {
              return 0;
          }
          if (isCommsResponseOutstanding()) {
              return 0;
          }
          return getCurrentTimeout();
      }
  
      /** {@inheritDoc} */
      @Override
      public void onCreate() {
      }
  
      /** {@inheritDoc} */
      @Override
      public void onDestroy() {
      }
  
      /** {@inheritDoc} */
      @Override
      protected void onRequestComplete() {
      }
  
      /** {@inheritDoc} */
      @Override
      protected void onTimeoutEvent() {
      }
  
      /**
       * Generate Map containing boolean capability filters for supplied Bundle.
       * 
       * @param filter Bundle containing filter.
       * @return Map containing set of capabilities.
       */
      private static Map<String, Boolean> prepareBoolFilter(Bundle filter) {
          Map<String, Boolean> objectFilter = null;
          if (filter != null && (filter.keySet().size() > 0)) {
              objectFilter = new Hashtable<String, Boolean>();
              for (String key : filter.keySet()) {
                  objectFilter.put(key, filter.getBoolean(key));
              }
          } else {
              objectFilter = null;
          }
          return objectFilter;
      }
      
      /**
       * This method needs to be called as part of removeAllData()/changeUser()
       * routine.
       */
      /** {@inheritDoc} */
      @Override
      public final void onReset() {
          
          super.onReset();
          mMyIdentityList.clear();
          mAvailableIdentityList.clear();
          mStatusList.clear();
          mState = State.IDLE;
      }
      
      /***
       * Return TRUE if the given ThirdPartyAccount contains a Facebook account.
       * 
       * @param list List of Identity objects, can be NULL.
       * @return TRUE if the given Identity contains a Facebook account.
       */
      public boolean isFacebookInThirdPartyAccountList() {
          if (mMyIdentityList != null) {
              synchronized(mMyIdentityList) {
                  for (Identity identity : mMyIdentityList) {
                      if (identity.mName.toLowerCase().contains(ThirdPartyAccount.SNS_TYPE_FACEBOOK)) {
                          return true;
                      }
                  }
              }
          }
          LogUtils.logV("ApplicationCache."
                  + "isFacebookInThirdPartyAccountList() Facebook not found in list");
          return false;
      }
  
      /***
       * Return TRUE if the given Identity contains a Hyves account.
       * 
       * @param list List of ThirdPartyAccount objects, can be NULL.
       * @return TRUE if the given Identity contains a Hyves account.
       */
      public boolean isHyvesInThirdPartyAccountList() {
          if (mMyIdentityList != null) {
              synchronized(mMyIdentityList) {
                  for (Identity identity : mMyIdentityList) {
                      if (identity.mName.toLowerCase().contains(ThirdPartyAccount.SNS_TYPE_HYVES)) {
                          return true;
                      }
                  }
              }
          }
          LogUtils.logV("ApplicationCache."
                  + "isFacebookInThirdPartyAccountList() Hyves not found in list");
          return false;
      }
      
      /**
       * 
       * Adds a social network to the "to be validated" hash map. The map is used for checking
       * which social network is currently being validated with the backend.
       * 
       * @param socialNetwork The social network (Identity.mNetwork) to add to the pending list.
       * 
       */
      private synchronized void addPendingIdentity(final String socialNetwork) {
      	if (socialNetwork == null) {
      		return;
      	}
      	
      	if (mCurrentlyPendingSns == null) {
      		mCurrentlyPendingSns = new ArrayList<String>();
      	}
      	
      	synchronized (mCurrentlyPendingSns) {
      		mCurrentlyPendingSns.add(socialNetwork);
      	}
      }
      
      /**
       * 
       * Returns the currently pending identities as part of a list.
       * 
       * @return The currently pending identities in a list.
       * 
       */
      public List<Identity> getPendingIdentities() {
      	List<Identity> pendingIdentities = new ArrayList<Identity>();
      	if (mCurrentlyPendingSns == null) {
      		return pendingIdentities;
      	}
      	
      	for (int i = 0; i < mCurrentlyPendingSns.size(); i++) {
      		String pendingSns = mCurrentlyPendingSns.get(i);
      		if (pendingSns == null) {
      			continue;
      		}
      		
      		for (int j = 0; j < mAvailableIdentityList.size(); j++) {
      			Identity availableIdentity = mAvailableIdentityList.get(j);
      			if (pendingSns.equals(availableIdentity.mNetwork)) {
      				pendingIdentities.add(availableIdentity);
      			}
      		}
      	}
      	
      	return pendingIdentities;
      }
      
      /**
       * 
       * Deletes a social network that has been validated on the backend from the map.
       * 
       * @param socialNetwork A list of social networks to remove from the pending list.
       * 
       */
      private void removePendingIdentities(final List<Identity> socialNetworks) {
      	if (socialNetworks == null || mCurrentlyPendingSns == null) {
      		return;
      	}
      	
      	synchronized (mCurrentlyPendingSns) {
      		Iterator<String> iter = mCurrentlyPendingSns.iterator();
      		while (iter.hasNext()) { // remove social networks that match the passed network
      			Iterator<Identity> iterMyIdentities = socialNetworks.iterator();
      			while (iterMyIdentities.hasNext()) {
      				if (iter.next().equals(iterMyIdentities.next().mNetwork)) {
      					iter.remove();
      				}
      			}
      		}
      	}
      }
      
      /**
       * Clears the currently pending identities e.g. can be used in case of a validate identity 
       * request to the backend times out.
       */
      private void clearPendingIdentities() {
      	if (mCurrentlyPendingSns == null) {
      		return;
      	}
      	
      	synchronized (mCurrentlyPendingSns) {
      		mCurrentlyPendingSns.clear();
      	}
      }
      
      /**
       * 
       * Checks whether the third party account/identity is being added already by the user. If so it 
       * should not be possible to add the account again. If the account is currently being validated 
       * this method returns true.
       * 
       * @param networkName The network to check for. E.g. facebook.com.
       * 
       * @return True if the Identity is being validated on the backend currently.
       */
      public boolean isIdentityPending(final String networkName) {
      	if (networkName == null || mCurrentlyPendingSns == null) {
      		return false;
      	}
      	
      	for (int i = 0; i < mCurrentlyPendingSns.size(); i++) {
      		if (networkName.equals(mCurrentlyPendingSns.get(i))) {
      			return true;
      		}
      	}
      	
      	return false;
      }
  }
  