diff --git a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java
index 7383dc0a..8e24c15e 100644
--- a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java
+++ b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java
@@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 import com.owncloud.android.lib.common.OwnCloudClientFactory;
 import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
 import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
 import com.owncloud.android.lib.resources.files.RemoteFile;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
@@ -80,7 +81,12 @@ public class MainActivity extends Activity implements OnRemoteOperationListener,
         
     	Uri serverUri = Uri.parse(getString(R.string.server_base_url) + AccountUtils.WEBDAV_PATH_4_0);
     	mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true);
-    	mClient.setBasicCredentials(getString(R.string.username), getString(R.string.password));
+    	mClient.setCredentials(
+    			OwnCloudCredentialsFactory.newBasicCredentials(
+    					getString(R.string.username), 
+    					getString(R.string.password)
+				)
+		);
     	
     	mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list);
     	((ListView)findViewById(R.id.list_view)).setAdapter(mFilesAdapter);
diff --git a/src/com/owncloud/android/lib/common/OwnCloudAccount.java b/src/com/owncloud/android/lib/common/OwnCloudAccount.java
new file mode 100644
index 00000000..339eb128
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudAccount.java
@@ -0,0 +1,98 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2014 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.lib.common;
+
+
+import java.io.IOException;
+
+import com.owncloud.android.lib.common.accounts.AccountUtils;
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
+
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.net.Uri;
+
+/**
+ * OwnCloud Account
+ * 
+ * @author David A. Velasco
+ */
+public class OwnCloudAccount {
+
+    private Uri mBaseUri; 
+    
+    private OwnCloudCredentials mCredentials;
+    
+    private String mSavedAccountName;
+    
+    
+    public OwnCloudAccount(Account savedAccount, Context context) 
+    		throws AccountNotFoundException, AuthenticatorException, 
+    		IOException, OperationCanceledException {
+    	
+    	if (savedAccount == null) {
+    		throw new IllegalArgumentException("Parameter 'savedAccount' cannot be null");
+    	}
+    	if (context == null) {
+    		throw new IllegalArgumentException("Parameter 'context' cannot be null");
+    	}
+    	
+    	mSavedAccountName = savedAccount.name;
+        mBaseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, savedAccount));
+        mCredentials = AccountUtils.getCredentialsForAccount(context, savedAccount);
+        if (mCredentials == null) {
+        	mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
+        }
+    }
+    
+    
+    public OwnCloudAccount(Uri baseUri, OwnCloudCredentials credentials) {
+        if (baseUri == null) {
+            throw new IllegalArgumentException("Parameter 'baseUri' cannot be null");
+        }
+        mSavedAccountName = null;
+        mBaseUri = baseUri;
+        mCredentials = credentials != null ? 
+        		credentials : OwnCloudCredentialsFactory.getAnonymousCredentials();
+        String username = mCredentials.getUsername();
+        if (username != null) {
+        	mSavedAccountName = AccountUtils.buildAccountName(mBaseUri, username);
+        }
+    }
+    
+
+	public boolean isAnonymous() {
+        return (mCredentials == null);
+    }
+    
+    public Uri getBaseUri() {
+        return mBaseUri;
+    }
+            
+    public OwnCloudCredentials getCredentials() {
+        return mCredentials;
+    }
+    
+    public String getName() {
+    	return mSavedAccountName;
+    }
+
+    
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
new file mode 100644
index 00000000..6855cfc4
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
@@ -0,0 +1,48 @@
+package com.owncloud.android.lib.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.AuthScope;
+
+public class OwnCloudBasicCredentials implements OwnCloudCredentials {
+
+	private String mUsername;
+	private String mPassword;
+
+	public OwnCloudBasicCredentials(String username, String password) {
+		mUsername = username != null ? username : "";
+		mPassword = password != null ? password : "";
+	}
+
+	@Override
+	public void applyTo(OwnCloudClient client) {
+        List<String> authPrefs = new ArrayList<String>(1);
+        authPrefs.add(AuthPolicy.BASIC);
+        client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
+        
+        client.getParams().setAuthenticationPreemptive(true);
+        client.getState().setCredentials(
+        		AuthScope.ANY, 
+        		new UsernamePasswordCredentials(mUsername, mPassword)
+		);
+	}
+
+	@Override
+	public String getUsername() {
+		return mUsername;
+	}
+
+	@Override
+	public String getAuthToken() {
+		return mPassword;
+	}
+
+	@Override
+	public boolean authTokenExpires() {
+		return false;
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java
new file mode 100644
index 00000000..fb6f2018
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java
@@ -0,0 +1,51 @@
+package com.owncloud.android.lib.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.AuthScope;
+
+import com.owncloud.android.lib.common.network.BearerAuthScheme;
+import com.owncloud.android.lib.common.network.BearerCredentials;
+
+public class OwnCloudBearerCredentials implements OwnCloudCredentials {
+
+	private String mAccessToken;
+	
+	public OwnCloudBearerCredentials(String accessToken) {
+		mAccessToken = accessToken != null ? accessToken : "";
+	}
+
+	@Override
+	public void applyTo(OwnCloudClient client) {
+	    AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);
+	    
+	    List<String> authPrefs = new ArrayList<String>(1);
+	    authPrefs.add(BearerAuthScheme.AUTH_POLICY);
+	    client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
+	    
+	    client.getParams().setAuthenticationPreemptive(true);
+	    client.getState().setCredentials(
+	    		AuthScope.ANY, 
+	    		new BearerCredentials(mAccessToken)
+		);
+	}
+
+	@Override
+	public String getUsername() {
+		// its unknown
+		return null;
+	}
+	
+	@Override
+	public String getAuthToken() {
+		return mAccessToken;
+	}
+
+	@Override
+	public boolean authTokenExpires() {
+		return true;
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java
index 14405326..e6696ef0 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudClient.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java
@@ -27,10 +27,8 @@ package com.owncloud.android.lib.common;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
 
-import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.Cookie;
 import org.apache.commons.httpclient.Header;
 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpConnectionManager;
@@ -39,17 +37,14 @@ import org.apache.commons.httpclient.HttpMethod;
 import org.apache.commons.httpclient.HttpMethodBase;
 import org.apache.commons.httpclient.HttpVersion;
 import org.apache.commons.httpclient.URI;
-import org.apache.commons.httpclient.UsernamePasswordCredentials;
-import org.apache.commons.httpclient.auth.AuthPolicy;
-import org.apache.commons.httpclient.auth.AuthScope;
 import org.apache.commons.httpclient.cookie.CookiePolicy;
 import org.apache.commons.httpclient.methods.HeadMethod;
 import org.apache.commons.httpclient.params.HttpMethodParams;
 import org.apache.http.HttpStatus;
 import org.apache.http.params.CoreProtocolPNames;
 
-import com.owncloud.android.lib.common.network.BearerAuthScheme;
-import com.owncloud.android.lib.common.network.BearerCredentials;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.network.WebdavUtils;
 
 
@@ -57,74 +52,82 @@ import android.net.Uri;
 import android.util.Log;
 
 public class OwnCloudClient extends HttpClient {
-    private static final int MAX_REDIRECTIONS_COUNT = 3;
-    
-    private Uri mUri;
-    private Uri mWebdavUri;
-    private Credentials mCredentials;
-    private boolean mFollowRedirects;
-    private String mSsoSessionCookie;
-    final private static String TAG = OwnCloudClient.class.getSimpleName();
+	
+    private static final String TAG = OwnCloudClient.class.getSimpleName();
     public static final String USER_AGENT = "Android-ownCloud";
+    private static final int MAX_REDIRECTIONS_COUNT = 3;
+    private static final String PARAM_SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header";
+    private static final boolean PARAM_SINGLE_COOKIE_HEADER_VALUE = true;
     
-    static private byte[] sExhaustBuffer = new byte[1024];
+    private static byte[] sExhaustBuffer = new byte[1024];
+    
+    private static int sIntanceCounter = 0;
+    private boolean mFollowRedirects = true;
+    private OwnCloudCredentials mCredentials = null;
+    private int mInstanceNumber = 0;
+    
+    private Uri mBaseUri;
     
     /**
      * Constructor
      */
-    public OwnCloudClient(HttpConnectionManager connectionMgr) {
+    public OwnCloudClient(Uri baseUri, HttpConnectionManager connectionMgr) {
         super(connectionMgr);
-        Log.d(TAG, "Creating OwnCloudClient");
+        
+        if (baseUri == null) {
+        	throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
+        }
+        mBaseUri = baseUri;
+        
+        mInstanceNumber = sIntanceCounter++;
+        Log.d(TAG + " #" + mInstanceNumber, "Creating OwnCloudClient");
+        
         getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);
-        getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
-        mFollowRedirects = true;
-        mSsoSessionCookie = null;
+        getParams().setParameter(
+        		CoreProtocolPNames.PROTOCOL_VERSION, 
+        		HttpVersion.HTTP_1_1);
+        
+        getParams().setCookiePolicy(
+        		CookiePolicy.BROWSER_COMPATIBILITY);	// to keep sessions
+        getParams().setParameter(
+        		PARAM_SINGLE_COOKIE_HEADER, 			// to avoid problems with some web servers
+        		PARAM_SINGLE_COOKIE_HEADER_VALUE);
+        
+        clearCredentials();
     }
 
-    public void setBearerCredentials(String accessToken) {
-        AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);
-        
-        List<String> authPrefs = new ArrayList<String>(1);
-        authPrefs.add(BearerAuthScheme.AUTH_POLICY);
-        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
-        
-        mCredentials = new BearerCredentials(accessToken);
-        getState().setCredentials(AuthScope.ANY, mCredentials);
-        mSsoSessionCookie = null;
-    }
-
-    public void setBasicCredentials(String username, String password) {
-        List<String> authPrefs = new ArrayList<String>(1);
-        authPrefs.add(AuthPolicy.BASIC);
-        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
-        
-        getParams().setAuthenticationPreemptive(true);
-        mCredentials = new UsernamePasswordCredentials(username, password);
-        getState().setCredentials(AuthScope.ANY, mCredentials);
-        mSsoSessionCookie = null;
-    }
-    
-    public void setSsoSessionCookie(String accessToken) {
-        getParams().setAuthenticationPreemptive(false);
-        getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
-        mSsoSessionCookie = accessToken;
-        mCredentials = null;
+    
+    public void setCredentials(OwnCloudCredentials credentials) {
+    	if (credentials != null) {
+    		mCredentials = credentials;
+    		mCredentials.applyTo(this);
+    	} else {
+    		clearCredentials();
+    	}
     }
     
+    public void clearCredentials() {
+		if (!(mCredentials instanceof OwnCloudAnonymousCredentials)) {
+			mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
+		}
+		mCredentials.applyTo(this);
+	}
     
     /**
      * Check if a file exists in the OC server
      * 
-     * TODO replace with ExistenceOperation
+     * @deprecated	Use ExistenceCheckOperation instead
      * 
-     * @return              'true' if the file exists; 'false' it doesn't exist
-     * @throws  Exception   When the existence could not be determined
+     * @return              	'true' if the file exists; 'false' it doesn't exist
+     * @throws  	Exception   When the existence could not be determined
      */
+    @Deprecated
     public boolean existsFile(String path) throws IOException, HttpException {
-        HeadMethod head = new HeadMethod(mWebdavUri.toString() + WebdavUtils.encodePath(path));
+        HeadMethod head = new HeadMethod(getWebdavUri() + WebdavUtils.encodePath(path));
         try {
             int status = executeMethod(head);
-            Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":""));
+            Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status +
+            		((status != HttpStatus.SC_OK)?"(FAIL)":""));
             exhaustResponse(head.getResponseBodyAsStream());
             return (status == HttpStatus.SC_OK);
             
@@ -140,19 +143,21 @@ public class OwnCloudClient extends HttpClient {
      * 
      * Sets the socket and connection timeouts only for the method received.
      * 
-     * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default'
+     * The timeouts are both in milliseconds; 0 means 'infinite'; 
+     * < 0 means 'do not change the default'
      * 
      * @param method            HTTP method request.
      * @param readTimeout       Timeout to set for data reception
      * @param conntionTimout    Timeout to set for connection establishment
      */
-    public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException {
+    public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) 
+    		throws HttpException, IOException {
         int oldSoTimeout = getParams().getSoTimeout();
         int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout();
         try {
             if (readTimeout >= 0) { 
                 method.getParams().setSoTimeout(readTimeout);   // this should be enough...
-                getParams().setSoTimeout(readTimeout);          // ... but this looks like necessary for HTTPS
+                getParams().setSoTimeout(readTimeout);          // ... but HTTPS needs this
             }
             if (connectionTimeout >= 0) {
                 getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
@@ -167,43 +172,72 @@ public class OwnCloudClient extends HttpClient {
     
     @Override
     public int executeMethod(HttpMethod method) throws IOException, HttpException {
-        boolean customRedirectionNeeded = false;
-        try {
-            method.setFollowRedirects(mFollowRedirects);
-        } catch (Exception e) {
-            //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed");
-            customRedirectionNeeded = mFollowRedirects;
+        try {	// just to log 
+	        boolean customRedirectionNeeded = false;
+	        
+	        try {
+	            method.setFollowRedirects(mFollowRedirects);
+	        } catch (Exception e) {
+	        	/*
+	            if (mFollowRedirects) 
+	        		Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() 
+	        			+ " method, custom redirection will be used if needed");
+        		*/
+	            customRedirectionNeeded = mFollowRedirects;
+	        }
+        
+	        Log.d(TAG + " #" + mInstanceNumber, "REQUEST " + 
+	        		method.getName() + " " + method.getPath());
+        
+	        logCookiesAtRequest(method.getRequestHeaders(), "before");
+	        logCookiesAtState("before");
+	        
+	        int status = super.executeMethod(method);
+        
+	        if (customRedirectionNeeded) {
+	        	status = patchRedirection(status, method);
+	        }
+
+	        logCookiesAtRequest(method.getRequestHeaders(), "after");
+	        logCookiesAtState("after");
+	        logSetCookiesAtResponse(method.getResponseHeaders());
+	        
+	        return status;
+	        
+        } catch (IOException e) {
+        	Log.d(TAG + " #" + mInstanceNumber, "Exception occured", e);
+        	throw e;
         }
-        if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {
-            method.addRequestHeader("Cookie", mSsoSessionCookie);
-        }
-        int status = super.executeMethod(method);
+    }
+
+	private int patchRedirection(int status, HttpMethod method) throws HttpException, IOException {
         int redirectionsCount = 0;
-        while (customRedirectionNeeded &&
-                redirectionsCount < MAX_REDIRECTIONS_COUNT &&
+        while (redirectionsCount < MAX_REDIRECTIONS_COUNT &&
                 (   status == HttpStatus.SC_MOVED_PERMANENTLY || 
                     status == HttpStatus.SC_MOVED_TEMPORARILY ||
                     status == HttpStatus.SC_TEMPORARY_REDIRECT)
                 ) {
             
             Header location = method.getResponseHeader("Location");
+            if (location == null) {
+            	location = method.getResponseHeader("location");
+            }
             if (location != null) {
-                Log.d(TAG,  "Location to redirect: " + location.getValue());
+                Log.d(TAG + " #" + mInstanceNumber,  
+                		"Location to redirect: " + location.getValue());
                 method.setURI(new URI(location.getValue(), true));
                 status = super.executeMethod(method);
                 redirectionsCount++;
                 
             } else {
-                Log.d(TAG,  "No location to redirect!");
+                Log.d(TAG + " #" + mInstanceNumber,  "No location to redirect!");
                 status = HttpStatus.SC_NOT_FOUND;
             }
         }
-        
         return status;
-    }
+	}
 
-
-    /**
+	/**
      * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
      * 
      * @param responseBodyAsStream      InputStream with the HTTP response to exhaust.
@@ -215,53 +249,137 @@ public class OwnCloudClient extends HttpClient {
                 responseBodyAsStream.close();
             
             } catch (IOException io) {
-                Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io);
+                Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" +
+                		" will be IGNORED", io);
             }
         }
     }
 
     /**
-     * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client.
+     * Sets the connection and wait-for-data timeouts to be applied by default to the methods 
+     * performed by this client.
      */
     public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) {
-            getParams().setSoTimeout(defaultDataTimeout);
-            getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);
-    }
-
-    /**
-     * Sets the Webdav URI for the helper methods that receive paths as parameters, instead of full URLs
-     * @param uri
-     */
-    public void setWebdavUri(Uri uri) {
-        mWebdavUri = uri;
+    	if (defaultDataTimeout >= 0) { 
+    		getParams().setSoTimeout(defaultDataTimeout);
+    	}
+    	if (defaultConnectionTimeout >= 0) {
+    		getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);
+    	}
     }
 
     public Uri getWebdavUri() {
-        return mWebdavUri;
+    	if (mCredentials instanceof OwnCloudBearerCredentials) {
+    		return Uri.parse(mBaseUri + AccountUtils.ODAV_PATH);
+    	} else {
+    		return Uri.parse(mBaseUri + AccountUtils.WEBDAV_PATH_4_0);
+    	}
     }
     
     /**
-     * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs
+     * Sets the root URI to the ownCloud server.   
+     *
+     * Use with care. 
+     * 
      * @param uri
      */
     public void setBaseUri(Uri uri) {
-        mUri = uri;
+        if (uri == null) {
+        	throw new IllegalArgumentException("URI cannot be NULL");
+        }
+        mBaseUri = uri;
     }
 
     public Uri getBaseUri() {
-        return mUri;
+        return mBaseUri;
     }
 
-    public final Credentials getCredentials() {
+    public final OwnCloudCredentials getCredentials() {
         return mCredentials;
     }
     
-    public final String getSsoSessionCookie() {
-        return mSsoSessionCookie;
-    }
-
     public void setFollowRedirects(boolean followRedirects) {
         mFollowRedirects = followRedirects;
     }
 
+    
+	private void logCookiesAtRequest(Header[] headers, String when) {
+        int counter = 0;
+        for (int i=0; i<headers.length; i++) {
+        	if (headers[i].getName().toLowerCase().equals("cookie")) {
+        		Log.d(TAG + " #" + mInstanceNumber, 
+        				"Cookies at request (" + when + ") (" + counter++ + "): " + 
+        						headers[i].getValue());
+        	}
+        }
+        if (counter == 0) {
+    		Log.d(TAG + " #" + mInstanceNumber, "No cookie at request before");
+        }
+	}
+
+    private void logCookiesAtState(String string) {
+        Cookie[] cookies = getState().getCookies();
+        if (cookies.length == 0) {
+    		Log.d(TAG + " #" + mInstanceNumber, "No cookie at STATE before");
+        } else {
+    		Log.d(TAG + " #" + mInstanceNumber, "Cookies at STATE (before)");
+	        for (int i=0; i<cookies.length; i++) {
+	    		Log.d(TAG + " #" + mInstanceNumber, "    (" + i + "):" +
+	    				"\n        name: " + cookies[i].getName() +
+	    				"\n        value: " + cookies[i].getValue() +
+	    				"\n        domain: " + cookies[i].getDomain() +
+	    				"\n        path: " + cookies[i].getPath()
+	    				);
+	        }
+        }
+	}
+
+	private void logSetCookiesAtResponse(Header[] headers) {
+        int counter = 0;
+        for (int i=0; i<headers.length; i++) {
+        	if (headers[i].getName().toLowerCase().equals("set-cookie")) {
+        		Log.d(TAG + " #" + mInstanceNumber, 
+        				"Set-Cookie (" + counter++ + "): " + headers[i].getValue());
+        	}
+        }
+        if (counter == 0) {
+    		Log.d(TAG + " #" + mInstanceNumber, "No set-cookie");
+        }
+        
+	}
+	
+	public String getCookiesString(){
+		Cookie[] cookies = getState().getCookies(); 
+		String cookiesString ="";
+		for (Cookie cookie: cookies) {
+			cookiesString = cookiesString + cookie.toString() + ";";
+			
+			logCookie(cookie);
+		}
+		
+		return cookiesString;
+		
+	}
+
+	public int getConnectionTimeout() {
+        return getHttpConnectionManager().getParams().getConnectionTimeout();
+	}
+	
+	public int getDataTimeout() {
+		return getParams().getSoTimeout();
+	}
+	
+	private void logCookie(Cookie cookie) {
+    	Log.d(TAG, "Cookie name: "+ cookie.getName() );
+    	Log.d(TAG, "       value: "+ cookie.getValue() );
+    	Log.d(TAG, "       domain: "+ cookie.getDomain());
+    	Log.d(TAG, "       path: "+ cookie.getPath() );
+    	Log.d(TAG, "       version: "+ cookie.getVersion() );
+    	Log.d(TAG, "       expiryDate: " + 
+    			(cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--"));
+    	Log.d(TAG, "       comment: "+ cookie.getComment() );
+    	Log.d(TAG, "       secure: "+ cookie.getSecure() );
+    }
+
+
 }
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
index 47c8f455..6c1944a5 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
@@ -69,74 +69,122 @@ public class OwnCloudClientFactory {
      */
     public static OwnCloudClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
         //Log_OC.d(TAG, "Creating OwnCloudClient associated to " + account.name);
-       
-        Uri webdavUri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
-        Uri uri = Uri.parse(AccountUtils.constructBasicURLForAccount(appContext, account));
+        Uri baseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account));
         AccountManager am = AccountManager.get(appContext);
         boolean isOauth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
         boolean isSamlSso = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
-        OwnCloudClient client = createOwnCloudClient(webdavUri, appContext, !isSamlSso);
-        client.setBaseUri(uri);
+        OwnCloudClient client = createOwnCloudClient(baseUri, appContext, !isSamlSso);
         
         if (isOauth2) {    
-            String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), false);
-            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
+            String accessToken = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
+            		false);
+            
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
+    		);
         
         } else if (isSamlSso) {    // TODO avoid a call to getUserData here
-            String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), false);
-            client.setSsoSessionCookie(accessToken);
+            String accessToken = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
+            		false);
             
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken)
+    		);
+
         } else {
             String username = account.name.substring(0, account.name.lastIndexOf('@'));
             //String password = am.getPassword(account);
-            String password = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), false);
-            client.setBasicCredentials(username, password);
+            String password = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
+            		false);
+            
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newBasicCredentials(username, password)
+    		);
+            
         }
         
+        // Restore cookies
+        AccountUtils.restoreCookies(account, client, appContext);
+        
         return client;
     }
     
     
     public static OwnCloudClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
-        Uri webdavUri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
-        Uri uri = Uri.parse(AccountUtils.constructBasicURLForAccount(appContext, account));
+        Uri baseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account));
         AccountManager am = AccountManager.get(appContext);
         boolean isOauth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
         boolean isSamlSso = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
-        OwnCloudClient client = createOwnCloudClient(webdavUri, appContext, !isSamlSso);
-        client.setBaseUri(uri);
+        OwnCloudClient client = createOwnCloudClient(baseUri, appContext, !isSamlSso);
         
         if (isOauth2) {    // TODO avoid a call to getUserData here
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account,  AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), null, currentActivity, null, null);
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(
+            		account,  
+            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
+            		null, 
+            		currentActivity, 
+            		null, 
+            		null);
+            
             Bundle result = future.getResult();
             String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
             if (accessToken == null) throw new AuthenticatorException("WTF!");
-            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newBearerCredentials(accessToken)
+    		);
 
         } else if (isSamlSso) {    // TODO avoid a call to getUserData here
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), null, currentActivity, null, null);
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
+            		null, 
+            		currentActivity, 
+            		null, 
+            		null);
+            
             Bundle result = future.getResult();
             String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
             if (accessToken == null) throw new AuthenticatorException("WTF!");
-            client.setSsoSessionCookie(accessToken);
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken)
+    		);
+
 
         } else {
             String username = account.name.substring(0, account.name.lastIndexOf('@'));
             //String password = am.getPassword(account);
             //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account,  AccountTypeUtils.getAuthTokenTypePass(account.type), null, currentActivity, null, null);
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(
+            		account,  
+            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
+            		null, 
+            		currentActivity, 
+            		null, 
+            		null);
+            
             Bundle result = future.getResult();
             String password = result.getString(AccountManager.KEY_AUTHTOKEN);
-            client.setBasicCredentials(username, password);
+            client.setCredentials(
+            		OwnCloudCredentialsFactory.newBasicCredentials(username, password)
+    		);
         }
         
+        // Restore cookies
+        AccountUtils.restoreCookies(account, client, appContext);
+        
         return client;
     }
     
     /**
      * Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud client connections.
      * 
-     * @param uri       URL to the ownCloud server
+     * @param uri       URL to the ownCloud server; BASE ENTRY POINT, not WebDavPATH
      * @param context   Android context where the OwnCloudClient is being created.
      * @return          A OwnCloudClient object ready to be used
      */
@@ -150,14 +198,12 @@ public class OwnCloudClientFactory {
             Log.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
         }
         
-        OwnCloudClient client = new OwnCloudClient(NetworkUtils.getMultiThreadedConnManager());
-        
+        OwnCloudClient client = new OwnCloudClient(uri, NetworkUtils.getMultiThreadedConnManager());
         client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
-        client.setWebdavUri(uri);
         client.setFollowRedirects(followRedirects);
         
         return client;
     }
     
-    
+
 }
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManager.java b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java
new file mode 100644
index 00000000..e54ae191
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java
@@ -0,0 +1,53 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+
+package com.owncloud.android.lib.common;
+
+import java.io.IOException;
+
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
+
+
+/**
+ * Manager to create and reuse OwnCloudClient instances to access remote OC servers. 
+ * 
+ * @author David A. Velasco
+ * @author masensio
+ */
+
+public interface OwnCloudClientManager {
+
+	public OwnCloudClient getClientFor(OwnCloudAccount account, Context context);
+    
+    public OwnCloudClient removeClientFor(OwnCloudAccount account);
+
+    public void saveAllClients(Context context, String accountType)
+			throws AccountNotFoundException, AuthenticatorException, 
+			IOException, OperationCanceledException;
+    
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java
new file mode 100644
index 00000000..216e2e30
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java
@@ -0,0 +1,67 @@
+package com.owncloud.android.lib.common;
+
+public class OwnCloudClientManagerFactory {
+	
+	public static enum Policy {
+		ALWAYS_NEW_CLIENT,
+		SINGLE_SESSION_PER_ACCOUNT
+	}
+	
+	private static Policy sDefaultPolicy = Policy.ALWAYS_NEW_CLIENT;
+	
+	private static OwnCloudClientManager sDefaultSingleton;
+
+	public static OwnCloudClientManager newDefaultOwnCloudClientManager() {
+		return newOwnCloudClientManager(sDefaultPolicy);
+	}
+	
+	public static OwnCloudClientManager newOwnCloudClientManager(Policy policy) {
+		switch (policy) {
+			case ALWAYS_NEW_CLIENT:
+				return new SimpleFactoryManager();
+				
+			case SINGLE_SESSION_PER_ACCOUNT:
+				return new SingleSessionManager();
+				
+			default:
+				throw new IllegalArgumentException("Unknown policy");
+		}
+	}
+	
+    public static OwnCloudClientManager getDefaultSingleton() {
+    	if (sDefaultSingleton == null) {
+    		sDefaultSingleton = newDefaultOwnCloudClientManager();
+    	}
+    	return sDefaultSingleton;
+    }
+    
+    public static Policy getDefaultPolicy() {
+    	return sDefaultPolicy;
+    }
+
+    public static void setDefaultPolicy(Policy policy) {
+    	if (policy == null) {
+    		throw new IllegalArgumentException("Default policy cannot be NULL");
+    	}
+    	if (defaultSingletonMustBeUpdated(policy)) {
+    		sDefaultSingleton = null;
+    	}
+    	sDefaultPolicy = policy;
+    }
+    
+	private static boolean defaultSingletonMustBeUpdated(Policy policy) {
+		if (sDefaultSingleton == null) {
+			return false;
+		}
+		if (policy == Policy.ALWAYS_NEW_CLIENT && 
+				!(sDefaultSingleton instanceof SimpleFactoryManager)) {
+			return true;
+		}
+		if (policy == Policy.SINGLE_SESSION_PER_ACCOUNT && 
+				!(sDefaultSingleton instanceof SingleSessionManager)) {
+			return true;
+		}
+		return false;
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java
new file mode 100644
index 00000000..a3fff2eb
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java
@@ -0,0 +1,30 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2014 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.lib.common;
+
+public interface OwnCloudCredentials {
+
+	public void applyTo(OwnCloudClient ownCloudClient);
+
+	public String getUsername();
+	
+	public String getAuthToken();
+	
+	public boolean authTokenExpires();
+	
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
new file mode 100644
index 00000000..ee84859d
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
@@ -0,0 +1,54 @@
+package com.owncloud.android.lib.common;
+
+public class OwnCloudCredentialsFactory {
+
+	private static OwnCloudAnonymousCredentials sAnonymousCredentials;
+
+	public static OwnCloudCredentials newBasicCredentials(String username, String password) {
+		return new OwnCloudBasicCredentials(username, password);
+	}
+	
+	public static OwnCloudCredentials newBearerCredentials(String authToken) {
+        return new OwnCloudBearerCredentials(authToken);
+	}
+    
+	public static OwnCloudCredentials newSamlSsoCredentials(String sessionCookie) {
+		return new OwnCloudSamlSsoCredentials(sessionCookie);
+	}
+
+	public static final OwnCloudCredentials getAnonymousCredentials() {
+		if (sAnonymousCredentials == null) {
+			sAnonymousCredentials = new OwnCloudAnonymousCredentials();
+		}
+		return sAnonymousCredentials;
+	}
+
+	public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials {
+		
+		protected OwnCloudAnonymousCredentials() {
+		}
+		
+		@Override
+		public void applyTo(OwnCloudClient client) {
+			client.getState().clearCredentials();
+			client.getState().clearCookies();
+		}
+
+		@Override
+		public String getAuthToken() {
+			return "";
+		}
+
+		@Override
+		public boolean authTokenExpires() {
+			return false;
+		}
+
+		@Override
+		public String getUsername() {
+			// no user name
+			return null;
+		}
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java
new file mode 100644
index 00000000..d59f5b0c
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java
@@ -0,0 +1,57 @@
+package com.owncloud.android.lib.common;
+
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+
+import android.net.Uri;
+
+public class OwnCloudSamlSsoCredentials implements OwnCloudCredentials {
+
+	private String mSessionCookie;
+
+	public OwnCloudSamlSsoCredentials(String sessionCookie) {
+		mSessionCookie = sessionCookie != null ? sessionCookie : "";
+	}
+
+	@Override
+	public void applyTo(OwnCloudClient client) {
+        client.getParams().setAuthenticationPreemptive(false);
+        client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+        client.setFollowRedirects(false);
+        
+    	Uri serverUri = client.getBaseUri();
+        
+        String[] cookies = mSessionCookie.split(";");
+        if (cookies.length > 0) {
+        	Cookie cookie = null;
+            for (int i=0; i<cookies.length; i++) {
+            	int equalPos = cookies[i].indexOf('=');
+            	if (equalPos >= 0) {
+                	cookie = new Cookie();
+	            	cookie.setName(cookies[i].substring(0, equalPos));
+	    	        cookie.setValue(cookies[i].substring(equalPos + 1));
+	    	        cookie.setDomain(serverUri.getHost());	// VERY IMPORTANT 
+	    	        cookie.setPath(serverUri.getPath());	// VERY IMPORTANT
+	    	        client.getState().addCookie(cookie);
+            	}
+            }
+        }
+	}
+
+	@Override
+	public String getUsername() {
+		// its unknown
+		return null;
+	}
+	
+	@Override
+	public String getAuthToken() {
+		return mSessionCookie;
+	}
+
+	@Override
+	public boolean authTokenExpires() {
+		return true;
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/SimpleFactoryManager.java b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java
new file mode 100644
index 00000000..94918ec6
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java
@@ -0,0 +1,68 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+
+package com.owncloud.android.lib.common;
+
+
+import com.owncloud.android.lib.common.accounts.AccountUtils;
+
+import android.content.Context;
+import android.util.Log;
+
+public class SimpleFactoryManager implements OwnCloudClientManager {
+    
+	private static final String TAG = SimpleFactoryManager.class.getSimpleName();
+
+	@Override
+	public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) {
+		Log.d(TAG, "getClientFor(OwnCloudAccount ... : ");
+		OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(
+				account.getBaseUri(), 
+				context.getApplicationContext(),
+				true);
+
+		Log.d(TAG, "    new client {" + 
+				(account.getName() != null ? 
+						account.getName() :
+							AccountUtils.buildAccountName(
+					    			account.getBaseUri(), 
+					    			account.getCredentials().getAuthToken())) +
+					", " + client.hashCode() + "}");
+		
+		client.setCredentials(account.getCredentials());
+		return client;
+	}
+
+	@Override
+	public OwnCloudClient removeClientFor(OwnCloudAccount account) {
+		// nothing to do - not taking care of tracking instances!
+		return null;
+	}
+
+	@Override
+	public void saveAllClients(Context context, String accountType) {
+		// nothing to do - not taking care of tracking instances!
+	}
+
+}
diff --git a/src/com/owncloud/android/lib/common/SingleSessionManager.java b/src/com/owncloud/android/lib/common/SingleSessionManager.java
new file mode 100644
index 00000000..690056ff
--- /dev/null
+++ b/src/com/owncloud/android/lib/common/SingleSessionManager.java
@@ -0,0 +1,204 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+
+package com.owncloud.android.lib.common;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientFactory;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
+
+/**
+ * Implementation of {@link OwnCloudClientManager}
+ * 
+ * TODO check multithreading safety
+ * 
+ * @author David A. Velasco
+ * @author masensio
+ */
+
+public class SingleSessionManager implements OwnCloudClientManager {
+    
+	private static final String TAG = SingleSessionManager.class.getSimpleName();
+
+    private Map<String, OwnCloudClient> mClientsWithKnownUsername = 
+    		new HashMap<String, OwnCloudClient>();
+    
+    private Map<String, OwnCloudClient> mClientsWithUnknownUsername = 
+    		new HashMap<String, OwnCloudClient>();
+    
+    
+    @Override
+    public synchronized OwnCloudClient getClientFor(OwnCloudAccount account, Context context) {
+		Log.d(TAG, "getClientFor(OwnCloudAccount ... : ");
+    	if (account == null) {
+    		throw new IllegalArgumentException("Cannot get an OwnCloudClient for a null account");
+    	}
+
+    	OwnCloudClient client = null;
+    	String accountName = account.getName();
+    	String sessionName = AccountUtils.buildAccountName(
+    			account.getBaseUri(), 
+    			account.getCredentials().getAuthToken());
+    	
+    	if (accountName != null) {
+    		client = mClientsWithKnownUsername.get(accountName);
+    	}
+    	boolean reusingKnown = false;	// just for logs
+    	if (client == null) {
+    		if (accountName != null) {
+    			client = mClientsWithUnknownUsername.remove(sessionName);
+    			if (client != null) {
+    	    		Log.d(TAG, "    reusing client {" + sessionName + ", " + 
+    	    				client.hashCode() + "}");
+    				mClientsWithKnownUsername.put(accountName, client);
+    	    		Log.d(TAG, "    moved client to {" + accountName + ", " + 
+    	    				client.hashCode() + "}");
+    			}
+    		} else {
+        		client = mClientsWithUnknownUsername.get(sessionName);
+    		}
+    	} else {
+    		Log.d(TAG, "    reusing client {" + accountName + ", " + client.hashCode() + "}");
+    		reusingKnown = true;
+    	}
+    	
+    	if (client == null) {
+    		// no client to reuse - create a new one
+    		client = OwnCloudClientFactory.createOwnCloudClient(
+    				account.getBaseUri(), 
+    				context.getApplicationContext(), 
+    				true);	// TODO remove dependency on OwnCloudClientFactory
+    		
+    		// Restore Cookies ??
+    		AccountUtils.restoreCookies(accountName, client, context);		
+    		
+    		client.setCredentials(account.getCredentials());
+    		if (accountName != null) {
+    			mClientsWithKnownUsername.put(accountName, client);
+    			Log.d(TAG, "    new client {" + accountName + ", " + client.hashCode() + "}");
+
+    		} else {
+    			mClientsWithUnknownUsername.put(sessionName, client);
+    			Log.d(TAG, "    new client {" + sessionName + ", " + client.hashCode() + "}");
+    		}
+    	} else {
+    		if (!reusingKnown) {
+    			Log.d(TAG, "    reusing client {" + sessionName + ", " + client.hashCode() + "}");
+    		}
+    		keepCredentialsUpdated(account, client);
+    		keepUriUpdated(account, client);
+    	}
+    	
+    	return client;
+    }
+    
+    
+	@Override
+	public OwnCloudClient removeClientFor(OwnCloudAccount account) {
+		
+    	if (account == null) {
+    		return null;
+    	}
+
+    	OwnCloudClient client = null;
+    	String accountName = account.getName();
+    	if (accountName != null) {
+    		client = mClientsWithKnownUsername.remove(accountName);
+        	if (client != null) {
+        		Log.d(TAG, "Removed client {" + accountName + ", " + client.hashCode() + "}");
+        		return client;
+        	} else {
+        		Log.d(TAG, "No client tracked for  {" + accountName + "}");
+        	}
+    	}
+    	
+    	String sessionName = AccountUtils.buildAccountName(
+    			account.getBaseUri(), 
+    			account.getCredentials().getAuthToken());
+    	client = mClientsWithUnknownUsername.remove(sessionName);
+    	if (client != null) {
+			Log.d(TAG, "Removed client {" + sessionName + ", " + client.hashCode() + "}");
+			return  client;
+    	}
+		Log.d(TAG, "No client tracked for  {" + sessionName + "}");
+    		
+		Log.d(TAG, "No client removed");
+		return null;
+		
+	}
+
+    
+    @Override
+    public synchronized void saveAllClients(Context context, String accountType) 
+    		throws AccountNotFoundException, AuthenticatorException, IOException,
+    		OperationCanceledException {
+
+    	Iterator<String> accountNames = mClientsWithKnownUsername.keySet().iterator();
+    	String accountName = null;
+    	Account account = null;
+    	while (accountNames.hasNext()) {
+    		accountName = accountNames.next();
+    		account = new Account(accountName, accountType);
+    		AccountUtils.saveClient(
+    				mClientsWithKnownUsername.get(accountName),
+    				account, 
+    				context);
+    	}
+    }
+
+    
+	private void keepCredentialsUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) {
+		OwnCloudCredentials recentCredentials = account.getCredentials();
+		if (!recentCredentials.getAuthToken().equals(
+				reusedClient.getCredentials().getAuthToken())) {
+			reusedClient.setCredentials(recentCredentials);
+		}
+		
+	}
+
+	// this method is just a patch; we need to distinguish accounts in the same host but
+	// different paths; but that requires updating the accountNames for apps upgrading 
+	private void keepUriUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) {
+		Uri recentUri = account.getBaseUri();
+		if (!recentUri.equals(reusedClient.getBaseUri())) {
+			reusedClient.setBaseUri(recentUri);
+		}
+		
+	}
+
+
+}
diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
index 7f0cd351..b061c2e5 100644
--- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
+++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
@@ -25,18 +25,32 @@
 
 package com.owncloud.android.lib.common.accounts;
 
+import java.io.IOException;
+
+import org.apache.commons.httpclient.Cookie;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountsException;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
 import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
 
 public class AccountUtils {
+	
+	private static final String TAG = AccountUtils.class.getSimpleName();
+	
     public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";
     public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";
     public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";
-    private static final String ODAV_PATH = "/remote.php/odav";
+    public static final String ODAV_PATH = "/remote.php/odav";
     private static final String SAML_SSO_PATH = "/remote.php/webdav";
     public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";
     public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";
@@ -72,11 +86,15 @@ public class AccountUtils {
     
     /**
      * Constructs full url to host and webdav resource basing on host version
+     * 
+     * @deprecated 		To be removed in release 1.0. 
+     * 
      * @param context
      * @param account
      * @return url or null on failure
      * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
      */
+    @Deprecated
     public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {
         AccountManager ama = AccountManager.get(context);
         String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
@@ -94,13 +112,32 @@ public class AccountUtils {
     
     /**
      * Extracts url server from the account
+     * 
+     * @deprecated 	This method will be removed in version 1.0.
+     *  			Use {@link #getBaseUrlForAccount(Context, Account)}
+     *  		 	instead.   
+     * 
      * @param context
      * @param account
      * @return url server or null on failure
      * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
      */
-    public static String constructBasicURLForAccount(Context context, Account account) throws AccountNotFoundException {
-        AccountManager ama = AccountManager.get(context);
+    @Deprecated
+    public static String constructBasicURLForAccount(Context context, Account account) 
+    		throws AccountNotFoundException {
+    	return getBaseUrlForAccount(context, account);
+    }
+
+    /**
+     * Extracts url server from the account
+     * @param context
+     * @param account
+     * @return url server or null on failure
+     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager
+     */
+    public static String getBaseUrlForAccount(Context context, Account account) 
+    		throws AccountNotFoundException {
+        AccountManager ama = AccountManager.get(context.getApplicationContext());
         String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
         
         if (baseurl == null ) 
@@ -109,7 +146,148 @@ public class AccountUtils {
         return baseurl;
     }
     
+
+    /**
+     * 
+     * @return
+     * @throws IOException 
+     * @throws AuthenticatorException 
+     * @throws OperationCanceledException 
+     */
+	public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account) 
+			throws OperationCanceledException, AuthenticatorException, IOException {
+		
+		OwnCloudCredentials credentials = null;
+        AccountManager am = AccountManager.get(context);
+        
+        boolean isOauth2 = am.getUserData(
+        		account, 
+        		AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null;
+        
+        boolean isSamlSso = am.getUserData(
+        		account, 
+        		AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+        
+        if (isOauth2) {    
+            String accessToken = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), 
+            		false);
+            
+            credentials = OwnCloudCredentialsFactory.newBearerCredentials(accessToken);
+        
+        } else if (isSamlSso) {
+            String accessToken = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), 
+            		false);
+            
+            credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken);
+
+        } else {
+            String username = account.name.substring(0, account.name.lastIndexOf('@'));
+            String password = am.blockingGetAuthToken(
+            		account, 
+            		AccountTypeUtils.getAuthTokenTypePass(account.type), 
+            		false);
+            
+            credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);
+        }
+        
+        return credentials;
+        
+	}
+
+	
+    public static String buildAccountName(Uri serverBaseUrl, String username) {
+    	if (serverBaseUrl.getScheme() == null) {
+    		serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString()); 
+    	}
+        String accountName = username + "@" + serverBaseUrl.getHost();
+        if (serverBaseUrl.getPort() >= 0) {
+            accountName += ":" + serverBaseUrl.getPort();
+        }
+        return accountName;
+    }
     
+
+	public static void saveClient(OwnCloudClient client, Account savedAccount, Context context) {
+
+		// Account Manager
+		AccountManager ac = AccountManager.get(context.getApplicationContext());
+
+		if (client != null) {
+			String cookiesString = client.getCookiesString();
+			if (cookiesString != "") {
+				ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString); 
+				Log.d(TAG, "Saving Cookies: "+ cookiesString );
+			}
+		}
+
+	}
+	
+	
+  /**
+  * Restore the client cookies
+  * @param account
+  * @param client 
+  * @param context
+  */
+	public static void restoreCookies(Account account, OwnCloudClient client, Context context) {
+
+		Log.d(TAG, "Restoring cookies for " + account.name);
+
+		// Account Manager
+		AccountManager am = AccountManager.get(context.getApplicationContext());
+
+		Uri serverUri = (client.getBaseUri() != null)? client.getBaseUri() : client.getWebdavUri();
+
+		String cookiesString = am.getUserData(account, Constants.KEY_COOKIES);
+		if (cookiesString !=null) {
+			String[] cookies = cookiesString.split(";");
+			if (cookies.length > 0) {
+				for (int i=0; i< cookies.length; i++) {
+					Cookie cookie = new Cookie();
+					int equalPos = cookies[i].indexOf('=');
+					cookie.setName(cookies[i].substring(0, equalPos));
+					cookie.setValue(cookies[i].substring(equalPos + 1));
+					cookie.setDomain(serverUri.getHost());	// VERY IMPORTANT 
+					cookie.setPath(serverUri.getPath());	// VERY IMPORTANT
+
+					client.getState().addCookie(cookie);
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Restore the client cookies from accountName
+	 * @param accountName
+	 * @param client
+	 * @param context
+	 */
+	public static void restoreCookies(String accountName, OwnCloudClient client, Context context) {
+		Log.d(TAG, "Restoring cookies for " + accountName);
+
+		// Account Manager
+		AccountManager am = AccountManager.get(context.getApplicationContext());
+		
+		// Get account
+		Account account = null;
+		Account accounts[] = am.getAccounts();
+		for (Account a : accounts) {
+			if (a.name.equals(accountName)) {
+				account = a;
+				break;
+			}
+		}
+		
+		// Restoring cookies
+		if (account != null) {
+			restoreCookies(account, client, context);
+		}
+	}
+	
     public static class AccountNotFoundException extends AccountsException {
         
 		/** Generated - should be refreshed every time the class changes!! */
@@ -160,5 +338,10 @@ public class AccountUtils {
 	    * Flag signaling if the ownCloud server supports Share API"
 	    */
 	    public static final String KEY_SUPPORTS_SHARE_API = "oc_supports_share_api";
+	    /**
+	     * OC accout cookies
+	     */
+	    public static final String KEY_COOKIES = "oc_account_cookies";
 	}
+
 }
diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
index 9d453090..bef4b86c 100644
--- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
+++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
@@ -26,11 +26,12 @@ package com.owncloud.android.lib.common.operations;
 
 import java.io.IOException;
 
-import org.apache.commons.httpclient.Credentials;
-
+import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientFactory;
-import com.owncloud.android.lib.common.network.BearerCredentials;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 
 
@@ -105,7 +106,9 @@ public abstract class RemoteOperation implements Runnable {
         mAccount = account;
         mContext = context.getApplicationContext();
         try {
-            mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext);
+        	OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
+            mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+            		getClientFor(ocAccount, mContext);
         } catch (Exception e) {
             Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
             return new RemoteOperationResult(e);
@@ -135,12 +138,17 @@ public abstract class RemoteOperation implements Runnable {
      * 
      * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}. 
      * 
+     * @deprecated 	This method will be removed in version 1.0.
+     *  			Use {@link #execute(Account, Context, OnRemoteOperationListener, Handler)}
+     *  		 	instead.   
+     * 
      * @param account           ownCloud account in remote ownCloud server to reach during the execution of the operation.
      * @param context           Android context for the component calling the method.
      * @param listener          Listener to be notified about the execution of the operation.
      * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
      * @return                  Thread were the remote operation is executed.
      */
+	@Deprecated
     public Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
         if (account == null)
             throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
@@ -161,6 +169,42 @@ public abstract class RemoteOperation implements Runnable {
     }
 
     
+    /**
+     * Asynchronously executes the remote operation
+     * 
+     * This method should be used whenever an ownCloud account is available, 
+     * instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}.
+     * 
+     * @param account           ownCloud account in remote ownCloud server to reach during the 
+     * 							execution of the operation.
+     * @param context           Android context for the component calling the method.
+     * @param listener          Listener to be notified about the execution of the operation.
+     * @param listenerHandler   Handler associated to the thread where the methods of the listener 
+     * 							objects must be called.
+     * @return                  Thread were the remote operation is executed.
+     */
+    public Thread execute(Account account, Context context, OnRemoteOperationListener listener, 
+    		Handler listenerHandler) {
+    	
+        if (account == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+        if (context == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+        mAccount = account;
+        mContext = context.getApplicationContext();
+        mCallerActivity = null;
+        mClient = null;     // the client instance will be created from mAccount and mContext in the runnerThread to create below
+        
+        mListener = listener;
+        
+        mListenerHandler = listenerHandler;
+        
+        Thread runnerThread = new Thread(this);
+        runnerThread.start();
+        return runnerThread;
+    }
+
+    
 	/**
 	 * Asynchronously executes the remote operation
 	 * 
@@ -205,11 +249,17 @@ public abstract class RemoteOperation implements Runnable {
             try{
                 if (mClient == null) {
                     if (mAccount != null && mContext != null) {
+                    	/** DEPRECATED BLOCK - will be removed at version 1.0 */
                         if (mCallerActivity != null) {
-                            mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext, mCallerActivity);
+                            mClient = OwnCloudClientFactory.createOwnCloudClient(
+                            		mAccount, mContext, mCallerActivity);
                         } else {
-                            mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext);
+                        /** EOF DEPRECATED */
+                        	OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
+                            mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            		getClientFor(ocAccount, mContext);
                         }
+                        
                     } else {
                         throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
                     }
@@ -228,21 +278,20 @@ public abstract class RemoteOperation implements Runnable {
                 result = run(mClient);
         
             repeat = false;
+        	/** DEPRECATED BLOCK - will be removed at version 1.0 ; don't trust in this code 
+        	 * 						to trigger authentication update */
             if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
-//                    (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
                     (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
                 /// possible fail due to lack of authorization in an operation performed in foreground
-                Credentials cred = mClient.getCredentials();
-                String ssoSessionCookie = mClient.getSsoSessionCookie();
-                if (cred != null || ssoSessionCookie != null) {
+                OwnCloudCredentials cred = mClient.getCredentials();
+                if (cred != null) {
                     /// confirmed : unauthorized operation
                     AccountManager am = AccountManager.get(mContext);
-                    boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
-                    boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
-                    if (bearerAuthorization) {
-                        am.invalidateAuthToken(mAccount.type, ((BearerCredentials)cred).getAccessToken());
-                    } else if (samlBasedSsoAuthorization ) {
-                        am.invalidateAuthToken(mAccount.type, ssoSessionCookie);
+                    if (cred.authTokenExpires()) {
+                        am.invalidateAuthToken(
+                                mAccount.type, 
+                                cred.getAuthToken()
+                        );
                     } else {
                         am.clearPassword(mAccount);
                     }
@@ -251,8 +300,14 @@ public abstract class RemoteOperation implements Runnable {
                     result = null;
                 }
             }
+            /** EOF DEPRECATED BLOCK **/
         } while (repeat);
         
+        if (mAccount != null && mContext != null) {
+        	// Save Client Cookies
+            AccountUtils.saveClient(mClient, mAccount, mContext);
+        }
+        
         final RemoteOperationResult resultToSend = result;
         if (mListenerHandler != null && mListener != null) {
         	mListenerHandler.post(new Runnable() {
diff --git a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java
index 9a675587..458a91d3 100644
--- a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java
+++ b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java
@@ -59,37 +59,39 @@ public class GetRemoteStatusOperation extends RemoteOperation {
     private static final String NODE_INSTALLED = "installed";
     private static final String NODE_VERSION = "version";
     
-    private String mUrl;
     private RemoteOperationResult mLatestResult;
     private Context mContext;
 
-    public GetRemoteStatusOperation(String url, Context context) {
-        mUrl = url;
+    public GetRemoteStatusOperation(Context context) {
         mContext = context;
     }
     
-    private boolean tryConnection(OwnCloudClient wc, String urlSt) {
+    private boolean tryConnection(OwnCloudClient client) {
         boolean retval = false;
         GetMethod get = null;
+        String baseUrlSt = client.getBaseUri().toString();
         try {
-            get = new GetMethod(urlSt);
-            int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
+            get = new GetMethod(baseUrlSt + AccountUtils.STATUS_PATH);
+            int status = client.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
             String response = get.getResponseBodyAsString();
             if (status == HttpStatus.SC_OK) {
                 JSONObject json = new JSONObject(response);
                 if (!json.getBoolean(NODE_INSTALLED)) {
-                    mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+                    mLatestResult = new RemoteOperationResult(
+                    		RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
                 } else {
                     String version = json.getString(NODE_VERSION);
 					OwnCloudVersion ocVersion = new OwnCloudVersion(version);
                     if (!ocVersion.isVersionValid()) {
-                        mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+                        mLatestResult = new RemoteOperationResult(
+                        		RemoteOperationResult.ResultCode.BAD_OC_VERSION);
                         
                     } else {
-                        mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ? 
-                                                                    RemoteOperationResult.ResultCode.OK_SSL : 
-                                                                    RemoteOperationResult.ResultCode.OK_NO_SSL
-                            );
+                        mLatestResult = new RemoteOperationResult(
+                        		baseUrlSt.startsWith("https://") ?
+                        				RemoteOperationResult.ResultCode.OK_SSL :
+                    					RemoteOperationResult.ResultCode.OK_NO_SSL
+                        );
 
                         ArrayList<Object> data = new ArrayList<Object>();
                         data.add(ocVersion);
@@ -103,7 +105,8 @@ public class GetRemoteStatusOperation extends RemoteOperation {
             }
 
         } catch (JSONException e) {
-            mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+            mLatestResult = new RemoteOperationResult(
+            		RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
             
         } catch (Exception e) {
             mLatestResult = new RemoteOperationResult(e);
@@ -114,13 +117,13 @@ public class GetRemoteStatusOperation extends RemoteOperation {
         }
         
         if (mLatestResult.isSuccess()) {
-            Log.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+            Log.i(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage());
             
         } else if (mLatestResult.getException() != null) {
-            Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
+            Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
             
         } else {
-            Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+            Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage());
         }
 
         return retval;
@@ -138,16 +141,17 @@ public class GetRemoteStatusOperation extends RemoteOperation {
         if (!isOnline()) {
         	return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
         }
-        if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
-            tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
+        String baseUriStr = client.getBaseUri().toString();
+        if (baseUriStr.startsWith("http://") || baseUriStr.startsWith("https://")) {
+            tryConnection(client);
             
         } else {
-            client.setWebdavUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
-            boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); 
+            client.setBaseUri(Uri.parse("https://" + baseUriStr));
+            boolean httpsSuccess = tryConnection(client); 
             if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
                 Log.d(TAG, "establishing secure connection failed, trying non secure connection");
-                client.setWebdavUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
-                tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
+                client.setBaseUri(Uri.parse("http://" + baseUriStr));
+                tryConnection(client);
             }
         }
         return mLatestResult;
diff --git a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java
index 050b36d2..f153be44 100644
--- a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java
+++ b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java
@@ -78,7 +78,7 @@ public class GetRemoteUserNameOperation extends RemoteOperation {
 
 		//Get the user
 		try {
-			get = new GetMethod(client.getWebdavUri() + OCS_ROUTE);
+			get = new GetMethod(client.getBaseUri() + OCS_ROUTE);
 			get.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
 			status = client.executeMethod(get);
 			if(isSuccess(status)) {
diff --git a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java
index 65771dab..66ff274e 100644
--- a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java
+++ b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java
@@ -35,6 +35,7 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
 
 import com.owncloud.android.lib.common.OwnCloudClientFactory;
 import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
 import com.owncloud.android.lib.resources.files.RemoteFile;
 import com.owncloud.android.lib.common.network.NetworkUtils;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@@ -73,7 +74,6 @@ public class TestActivity extends Activity {
 	private String mPass;
 	private boolean mChunked;
 	
-	private static final String WEBDAV_PATH = "/remote.php/webdav";
 	private static final int BUFFER_SIZE = 1024;
 	
 	public static final String ASSETS__TEXT_FILE_NAME = "textFile.txt";
@@ -106,14 +106,17 @@ public class TestActivity extends Activity {
 			}
 		}
 		
-		Uri uri = Uri.parse(mServerUri + WEBDAV_PATH);
-		mClient = new OwnCloudClient(NetworkUtils.getMultiThreadedConnManager());
+		mClient = new OwnCloudClient(Uri.parse(mServerUri), NetworkUtils.getMultiThreadedConnManager());
 		mClient.setDefaultTimeouts(
 				OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT, 
 				OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT);
-		mClient.setWebdavUri(uri);
 		mClient.setFollowRedirects(true);
-		mClient.setBasicCredentials(mUser, mPass);
+		mClient.setCredentials(
+				OwnCloudCredentialsFactory.newBasicCredentials(
+						mUser, 
+						mPass
+				)
+		);
 		mClient.setBaseUri(Uri.parse(mServerUri));
 		
 		Log.v(TAG, "onCreate finished, ownCloud client ready");
diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java
new file mode 100644
index 00000000..b612744c
--- /dev/null
+++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientManagerFactoryTest.java
@@ -0,0 +1,155 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+package com.owncloud.android.lib.test_project.test;
+
+import com.owncloud.android.lib.common.OwnCloudClientManager;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.SingleSessionManager;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
+import com.owncloud.android.lib.common.SimpleFactoryManager;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * Unit test for OwnCloudClientManagerFactory
+ * 
+ * @author David A. Velasco
+ */
+public class OwnCloudClientManagerFactoryTest extends TestCase {
+	
+	@Override
+	protected void setUp() {
+		OwnCloudClientManagerFactory.setDefaultPolicy(Policy.ALWAYS_NEW_CLIENT);
+	}
+
+	public void testGetDefaultPolicy() {
+		Policy defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy();
+		assertNotNull("Returned null value", defaultPolicy);
+		assertTrue("Returned unknown value", 
+						(Policy.ALWAYS_NEW_CLIENT.equals(defaultPolicy) ||
+						(Policy.SINGLE_SESSION_PER_ACCOUNT.equals(defaultPolicy))));
+	}
+	
+	public void testSetDefaultPolicy() {
+		OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT);
+		Policy defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy();
+		assertEquals("SINGLE_SESSION_PER_ACCOUNT not set", 
+				Policy.SINGLE_SESSION_PER_ACCOUNT, defaultPolicy);
+		
+		OwnCloudClientManagerFactory.setDefaultPolicy(Policy.ALWAYS_NEW_CLIENT);
+		defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy();
+		assertEquals("ALWAYS_NEW_CLIENT not set", Policy.ALWAYS_NEW_CLIENT, defaultPolicy);
+		
+		try {
+			OwnCloudClientManagerFactory.setDefaultPolicy(null);
+			throw new AssertionFailedError("Accepted NULL parameter");
+			
+		} catch(Exception e) {
+			assertTrue("Unexpected exception when setting default policy null", 
+					(e instanceof IllegalArgumentException));
+		}
+		defaultPolicy = OwnCloudClientManagerFactory.getDefaultPolicy();
+		assertEquals("ALWAYS_NEW_CLIENT changed after setting null", 
+				Policy.ALWAYS_NEW_CLIENT, defaultPolicy);
+		
+	}
+
+	
+	public void testGetDefaultSingleton() {
+		OwnCloudClientManager mgr = OwnCloudClientManagerFactory.getDefaultSingleton();
+		assertNotNull("Returned NULL default singleton", mgr);
+		assertTrue("Default singleton does not implement default policy", 
+				mgr instanceof SimpleFactoryManager);
+		
+		OwnCloudClientManager mgr2 = OwnCloudClientManagerFactory.getDefaultSingleton();
+		assertSame("Not singleton", mgr, mgr2);
+		
+		OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT);
+		mgr = OwnCloudClientManagerFactory.getDefaultSingleton();
+		assertNotNull("Returned NULL default singleton", mgr);
+		assertTrue("Default singleton does not implement default policy", 
+				mgr instanceof SingleSessionManager);
+		
+		mgr2 = OwnCloudClientManagerFactory.getDefaultSingleton();
+		assertSame("Not singleton", mgr, mgr2);
+	}
+    
+	
+	public void testNewDefaultOwnCloudClientManager() {
+		OwnCloudClientManager mgr = OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager();
+		assertNotNull("Returned NULL default manager", mgr);
+		assertTrue("New manager does not implement default policy", 
+				mgr instanceof SimpleFactoryManager);
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.getDefaultSingleton());
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager());
+		
+		OwnCloudClientManagerFactory.setDefaultPolicy(Policy.SINGLE_SESSION_PER_ACCOUNT);
+		mgr = OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager();
+		assertNotNull("Returned NULL default manager", mgr);
+		assertTrue("New manager does not implement default policy", 
+				mgr instanceof SingleSessionManager);
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.getDefaultSingleton());
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager());
+		
+	}
+	
+	
+	public void testNewOwnCloudClientManager() {
+		OwnCloudClientManager mgr = OwnCloudClientManagerFactory.
+				newOwnCloudClientManager(Policy.ALWAYS_NEW_CLIENT);
+		
+		assertNotNull("Returned NULL manager", mgr);
+		assertTrue("New manager does not implement policy ALWAYS_NEW_CLIENT", 
+				mgr instanceof SimpleFactoryManager);
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.getDefaultSingleton());
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager());
+		assertNotSame("Not new instance", 
+				mgr, OwnCloudClientManagerFactory.newOwnCloudClientManager(
+						Policy.ALWAYS_NEW_CLIENT));
+		
+		
+		OwnCloudClientManager mgr2 = OwnCloudClientManagerFactory.
+				newOwnCloudClientManager(Policy.SINGLE_SESSION_PER_ACCOUNT);
+		
+		assertNotNull("Returned NULL manager", mgr2);
+		assertTrue("New manager does not implement policy SINGLE_SESSION_PER_ACCOUNT", 
+				mgr2 instanceof SingleSessionManager);
+		assertNotSame("Not new instance", 
+				mgr2, OwnCloudClientManagerFactory.getDefaultSingleton());
+		assertNotSame("Not new instance", 
+				mgr2, OwnCloudClientManagerFactory.newDefaultOwnCloudClientManager());
+		assertNotSame("Not new instance", 
+				mgr2, OwnCloudClientManagerFactory.newOwnCloudClientManager(
+						Policy.SINGLE_SESSION_PER_ACCOUNT));
+	}
+
+	
+}
diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java
new file mode 100644
index 00000000..43de601a
--- /dev/null
+++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java
@@ -0,0 +1,381 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+package com.owncloud.android.lib.test_project.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.SocketTimeoutException;
+import java.security.GeneralSecurityException;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+import junit.framework.AssertionFailedError;
+
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
+import com.owncloud.android.lib.common.network.NetworkUtils;
+import com.owncloud.android.lib.test_project.R;
+import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory;
+
+
+/**
+ * Unit test for OwnCloudClient
+ * 
+ * @author David A. Velasco
+ */
+public class OwnCloudClientTest extends AndroidTestCase {
+	
+	private static final String TAG = OwnCloudClientTest.class.getSimpleName();
+	
+	private Uri mServerUri;
+	private String mUsername;
+	private String mPassword;
+
+	public OwnCloudClientTest() {
+		super();
+		
+		Protocol pr = Protocol.getProtocol("https");
+		if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) {
+			try {
+				ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory();
+				Protocol.registerProtocol(
+						"https",
+						new Protocol("https", psf, 443));
+				
+			} catch (GeneralSecurityException e) {
+				throw new AssertionFailedError(
+						"Self-signed confident SSL context could not be loaded");
+			}
+		}
+	}
+	
+
+	@Override
+	protected void setUp() throws Exception {
+	    super.setUp();
+		mServerUri = Uri.parse(getContext().getString(R.string.server_base_url));
+		mUsername = getContext().getString(R.string.username);
+		mPassword = getContext().getString(R.string.password);
+	}
+	
+	
+	public void testConstructor() {
+		try {
+			new OwnCloudClient(null, NetworkUtils.getMultiThreadedConnManager());
+			throw new AssertionFailedError("Accepted NULL parameter");
+			
+		} catch(Exception e) {
+			assertTrue("Unexpected exception passing NULL baseUri", 
+					(e instanceof IllegalArgumentException));
+		}
+		
+		try {
+			new OwnCloudClient(mServerUri, null);
+			throw new AssertionFailedError("Accepted NULL parameter");
+			
+		} catch(Exception e) {
+			assertTrue("Unexpected exception passing NULL connectionMgr", 
+					(e instanceof IllegalArgumentException));
+		}
+		
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		assertNotNull("OwnCloudClient instance not built", client);
+		assertEquals("Wrong user agent", 
+				client.getParams().getParameter(HttpMethodParams.USER_AGENT), 
+				OwnCloudClient.USER_AGENT);
+	}
+
+	
+	public void testGetSetCredentials() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		
+		assertNotNull("Returned NULL credentials", client.getCredentials());
+		assertEquals("Not instanced without credentials", 
+				client.getCredentials(), OwnCloudCredentialsFactory.getAnonymousCredentials());
+		
+		OwnCloudCredentials credentials = 
+				OwnCloudCredentialsFactory.newBasicCredentials("user", "pass");
+		client.setCredentials(credentials);
+		assertEquals("Basic credentials not set", credentials, client.getCredentials());
+		
+		credentials = OwnCloudCredentialsFactory.newBearerCredentials("bearerToken");
+		client.setCredentials(credentials);
+		assertEquals("Bearer credentials not set", credentials, client.getCredentials());
+
+		credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials("samlSessionCookie=124");
+		client.setCredentials(credentials);
+		assertEquals("SAML2 session credentials not set", credentials, client.getCredentials());
+		
+	}
+    
+	public void testExecuteMethodWithTimeouts() throws HttpException, IOException {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		int connectionTimeout = client.getConnectionTimeout();
+		int readTimeout = client.getDataTimeout();
+		
+        HeadMethod head = new HeadMethod(client.getWebdavUri() + "/");
+        try {
+            client.executeMethod(head, 1, 1000);
+			throw new AssertionFailedError("Completed HEAD with impossible read timeout");
+            
+        } catch (Exception e) {
+            Log.e("OwnCloudClientTest", "EXCEPTION", e);
+            assertTrue("Unexcepted exception " + e.getLocalizedMessage(), 
+            		(e instanceof ConnectTimeoutException) || 
+            		(e instanceof SocketTimeoutException));
+            
+        } finally {
+            head.releaseConnection();
+        }
+        
+        assertEquals("Connection timeout was changed for future requests", 
+        		connectionTimeout, client.getConnectionTimeout());
+        assertEquals("Read timeout was changed for future requests", 
+        		readTimeout, client.getDataTimeout());
+        
+        try {
+            client.executeMethod(head, 1000, 1);
+			throw new AssertionFailedError("Completed HEAD with impossible connection timeout");
+            
+        } catch (Exception e) {
+            Log.e("OwnCloudClientTest", "EXCEPTION", e);
+            assertTrue("Unexcepted exception " + e.getLocalizedMessage(), 
+            		(e instanceof ConnectTimeoutException) || 
+            		(e instanceof SocketTimeoutException));
+            
+        } finally {
+            head.releaseConnection();
+        }
+        
+        assertEquals("Connection timeout was changed for future requests", 
+        		connectionTimeout, client.getConnectionTimeout());
+        assertEquals("Read timeout was changed for future requests", 
+        		readTimeout, client.getDataTimeout());
+        
+	}
+    
+    
+	public void testExecuteMethod() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+        HeadMethod head = new HeadMethod(client.getWebdavUri() + "/");
+        int status = -1;
+        try {
+            status = client.executeMethod(head);
+            assertTrue("Wrong status code returned: " + status, 
+            		status > 99 && status < 600);
+            
+        } catch (IOException e) {
+        	Log.e(TAG, "Exception in HEAD method execution", e);
+        	// TODO - make it fail? ; try several times, and make it fail if none
+        	//			is right?
+            
+        } finally {
+            head.releaseConnection();
+        }
+	}
+
+	
+	public void testExhaustResponse() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+
+		PropFindMethod propfind = null;
+		try {
+			propfind = new PropFindMethod(client.getWebdavUri() + "/",
+					DavConstants.PROPFIND_ALL_PROP,
+					DavConstants.DEPTH_0);
+			client.executeMethod(propfind);
+			InputStream responseBody = propfind.getResponseBodyAsStream();
+			if (responseBody != null) {
+				client.exhaustResponse(responseBody);
+
+				try {
+					int character = responseBody.read();
+					assertEquals("Response body was not fully exhausted", 
+							character, -1);		// -1 is acceptable
+					
+				} catch (IOException e) {
+					// this is the preferred result
+				}
+				
+			} else {
+	        	Log.e(TAG, "Could not test exhaustResponse due to wrong response");
+	        	// TODO - make it fail? ; try several times, and make it fail if none
+	        	//			is right?
+			}
+	            
+        } catch (IOException e) {
+        	Log.e(TAG, "Exception in PROPFIND method execution", e);
+        	// TODO - make it fail? ; try several times, and make it fail if none
+        	//			is right?
+            
+        } finally {
+            propfind.releaseConnection();
+        }
+		
+		client.exhaustResponse(null);	// must run with no exception
+	}
+
+	
+	public void testGetSetDefaultTimeouts() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		
+		int oldDataTimeout = client.getDataTimeout();
+		int oldConnectionTimeout = client.getConnectionTimeout();
+		
+		client.setDefaultTimeouts(oldDataTimeout + 1000, oldConnectionTimeout + 1000);
+		assertEquals("Data timeout not set", 
+				oldDataTimeout + 1000, client.getDataTimeout());
+		assertEquals("Connection timeout not set", 
+				oldConnectionTimeout + 1000, client.getConnectionTimeout());
+		
+		client.setDefaultTimeouts(0, 0);
+		assertEquals("Zero data timeout not set", 
+				0, client.getDataTimeout());
+		assertEquals("Zero connection timeout not set", 
+				0, client.getConnectionTimeout());
+			
+		client.setDefaultTimeouts(-1, -1);
+		assertEquals("Negative data timeout not ignored", 
+				0, client.getDataTimeout());
+		assertEquals("Negative connection timeout not ignored", 
+				0, client.getConnectionTimeout());
+		
+		client.setDefaultTimeouts(-1, 1000);
+		assertEquals("Negative data timeout not ignored", 
+				0, client.getDataTimeout());
+		assertEquals("Connection timeout not set", 
+				1000, client.getConnectionTimeout());
+		
+		client.setDefaultTimeouts(1000, -1);
+		assertEquals("Data timeout not set", 
+				1000, client.getDataTimeout());
+		assertEquals("Negative connection timeout not ignored", 
+				1000, client.getConnectionTimeout());
+			
+	}
+	
+	
+	public void testGetWebdavUri() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		client.setCredentials(OwnCloudCredentialsFactory.newBearerCredentials("fakeToken"));
+		Uri webdavUri = client.getWebdavUri();
+		assertTrue("WebDAV URI does not point to the right entry point for OAuth2 " +
+				"authenticated servers",
+				webdavUri.getPath().endsWith(AccountUtils.ODAV_PATH));
+		assertTrue("WebDAV URI is not a subpath of base URI", 
+				webdavUri.getAuthority().equals(mServerUri.getAuthority()) &&
+				webdavUri.getPath().startsWith(mServerUri.getPath()));
+		
+		client.setCredentials(OwnCloudCredentialsFactory.newBasicCredentials(
+				mUsername, mPassword));
+		webdavUri = client.getWebdavUri();
+		assertTrue("WebDAV URI does not point to the right entry point",
+				webdavUri.getPath().endsWith(AccountUtils.WEBDAV_PATH_4_0));
+		PropFindMethod propfind = null;
+		try {
+			propfind = new PropFindMethod(webdavUri + "/",
+					DavConstants.PROPFIND_ALL_PROP,
+					DavConstants.DEPTH_0);
+			int status = client.executeMethod(propfind);
+			assertEquals("WebDAV request did not work on WebDAV URI", 
+					HttpStatus.SC_MULTI_STATUS, status);
+			
+        } catch (IOException e) {
+        	Log.e(TAG, "Exception in PROPFIND method execution", e);
+        	// TODO - make it fail? ; try several times, and make it fail if none
+        	//			is right?
+            
+        } finally {
+            propfind.releaseConnection();
+        }
+		
+	}
+	
+    
+	public void testGetSetBaseUri() {
+		OwnCloudClient client = 
+				new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager());
+		assertEquals("Returned base URI different that URI passed to constructor", 
+				mServerUri, client.getBaseUri());
+		
+		Uri otherUri = Uri.parse("https://whatever.com/basePath/here");
+		client.setBaseUri(otherUri);
+		assertEquals("Returned base URI different that URI passed to constructor", 
+				otherUri, client.getBaseUri());
+		
+		try {
+			client.setBaseUri(null);
+			throw new AssertionFailedError("Accepted NULL parameter");
+			
+		} catch(Exception e) {
+			assertTrue("Unexpected exception passing NULL base URI", 
+					(e instanceof IllegalArgumentException));
+		}
+	}
+
+	
+	public void testGetCookiesString() {
+		// TODO implement test body
+		/*public String getCookiesString(){
+			Cookie[] cookies = getState().getCookies(); 
+			String cookiesString ="";
+			for (Cookie cookie: cookies) {
+				cookiesString = cookiesString + cookie.toString() + ";";
+				
+				logCookie(cookie);
+			}
+			
+			return cookiesString;
+			
+		}
+		*/
+	}
+
+
+	public void testSetFollowRedirects() {
+		// TODO - to implement this test we need a redirected server
+	}
+
+    
+}
diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java
new file mode 100644
index 00000000..3254c589
--- /dev/null
+++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java
@@ -0,0 +1,124 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+package com.owncloud.android.lib.test_project.test;
+
+import java.security.GeneralSecurityException;
+
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
+import com.owncloud.android.lib.common.SimpleFactoryManager;
+import com.owncloud.android.lib.test_project.R;
+import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Unit test for SimpleFactoryManager 
+ * 
+ * @author David A. Velasco
+ */
+
+public class SimpleFactoryManagerTest extends AndroidTestCase {
+
+	private SimpleFactoryManager mSFMgr;
+	
+	private Uri mServerUri;
+	private String mUsername;
+	private OwnCloudAccount mValidAccount;
+	private OwnCloudAccount mAnonymousAccount;
+	
+	public SimpleFactoryManagerTest() {
+		super();
+		
+		Protocol pr = Protocol.getProtocol("https");
+		if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) {
+			try {
+				ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory();
+				Protocol.registerProtocol(
+						"https",
+						new Protocol("https", psf, 443));
+				
+			} catch (GeneralSecurityException e) {
+				throw new AssertionFailedError(
+						"Self-signed confident SSL context could not be loaded");
+			}
+		}
+	}
+	
+	
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		mSFMgr = new SimpleFactoryManager();
+		mServerUri = Uri.parse(getContext().getString(R.string.server_base_url));
+		mUsername = getContext().getString(R.string.username);
+		
+		mValidAccount = new OwnCloudAccount(
+				mServerUri, OwnCloudCredentialsFactory.newBasicCredentials(
+						mUsername, 
+						getContext().getString(R.string.password)
+				)
+		);
+		
+		mAnonymousAccount = new OwnCloudAccount(
+				mServerUri, OwnCloudCredentialsFactory.getAnonymousCredentials());
+				
+	}
+	
+	public void testGetClientFor() {
+		OwnCloudClient client = mSFMgr.getClientFor(mValidAccount, getContext());
+		
+		assertNotSame("Got same client instances for same account",
+				client, mSFMgr.getClientFor(mValidAccount,  getContext()));
+		
+		assertNotSame("Got same client instances for different accounts",
+				client, mSFMgr.getClientFor(mAnonymousAccount, getContext()));
+		
+		// TODO harder tests
+	}
+    
+	public void testRemoveClientFor() {
+		OwnCloudClient client = mSFMgr.getClientFor(mValidAccount, getContext());
+		mSFMgr.removeClientFor(mValidAccount);
+		assertNotSame("Got same client instance after removing it from manager",
+				client, mSFMgr.getClientFor(mValidAccount,  getContext()));
+		
+		// TODO harder tests
+	}
+
+    
+	public void testSaveAllClients() {
+		// TODO implement test;
+		// 		or refactor saveAllClients() method out of OwnCloudClientManager to make 
+		//		it independent of AccountManager
+	}
+
+}
diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java
new file mode 100644
index 00000000..8e0799ae
--- /dev/null
+++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java
@@ -0,0 +1,123 @@
+/* ownCloud Android Library is available under MIT license
+ *   Copyright (C) 2014 ownCloud Inc.
+ *   
+ *   Permission is hereby granted, free of charge, to any person obtaining a copy
+ *   of this software and associated documentation files (the "Software"), to deal
+ *   in the Software without restriction, including without limitation the rights
+ *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *   copies of the Software, and to permit persons to whom the Software is
+ *   furnished to do so, subject to the following conditions:
+ *   
+ *   The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ *   
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
+ *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
+ *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+ *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ *   THE SOFTWARE.
+ *
+ */
+package com.owncloud.android.lib.test_project.test;
+
+import java.security.GeneralSecurityException;
+
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
+import com.owncloud.android.lib.common.SingleSessionManager;
+import com.owncloud.android.lib.test_project.R;
+import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Unit test for SingleSessionManager 
+ * 
+ * @author David A. Velasco
+ */
+public class SingleSessionManagerTest extends AndroidTestCase {
+
+	private SingleSessionManager mSSMgr;
+	
+	private Uri mServerUri;
+	private String mUsername;
+	private OwnCloudAccount mValidAccount;
+	private OwnCloudAccount mAnonymousAccount;
+	
+	public SingleSessionManagerTest() {
+		super();
+		
+		Protocol pr = Protocol.getProtocol("https");
+		if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) {
+			try {
+				ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory();
+				Protocol.registerProtocol(
+						"https",
+						new Protocol("https", psf, 443));
+				
+			} catch (GeneralSecurityException e) {
+				throw new AssertionFailedError(
+						"Self-signed confident SSL context could not be loaded");
+			}
+		}
+	}
+	
+	
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		mSSMgr = new SingleSessionManager();
+		mServerUri = Uri.parse(getContext().getString(R.string.server_base_url));
+		mUsername = getContext().getString(R.string.username);
+		
+		mValidAccount = new OwnCloudAccount(
+				mServerUri, OwnCloudCredentialsFactory.newBasicCredentials(
+						mUsername, 
+						getContext().getString(R.string.password)
+				)
+		);
+		
+		mAnonymousAccount = new OwnCloudAccount(
+				mServerUri, OwnCloudCredentialsFactory.getAnonymousCredentials());
+				
+	}
+	
+	public void testGetClientFor() {
+		OwnCloudClient client1 = mSSMgr.getClientFor(mValidAccount, getContext());
+		OwnCloudClient client2 = mSSMgr.getClientFor(mAnonymousAccount, getContext());
+		
+		assertNotSame("Got same client instances for different accounts",
+				client1, client2);
+		assertSame("Got different client instances for same account",
+				client1, mSSMgr.getClientFor(mValidAccount,  getContext()));
+		
+		// TODO harder tests
+	}
+    
+	public void testRemoveClientFor() {
+		OwnCloudClient client1 = mSSMgr.getClientFor(mValidAccount, getContext());
+		mSSMgr.removeClientFor(mValidAccount);
+		assertNotSame("Got same client instance after removing it from manager",
+				client1, mSSMgr.getClientFor(mValidAccount,  getContext()));
+		
+		// TODO harder tests
+	}
+
+    
+	public void testSaveAllClients() {
+		// TODO implement test;
+		// 		or refactor saveAllClients() method out of OwnCloudClientManager to make 
+		//		it independent of AccountManager
+	}
+
+}