Merge two-way synch changes with synch-service refactoring for SSL warning
authorDavid A. Velasco <dvelasco@solidgear.es>
Thu, 25 Oct 2012 08:41:24 +0000 (10:41 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Thu, 25 Oct 2012 08:41:24 +0000 (10:41 +0200)
1  2 
src/com/owncloud/android/operations/SynchronizeFolderOperation.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

index ad7df51,0000000..91de06e
mode 100644,000000..100644
--- /dev/null
@@@ -1,218 -1,0 +1,234 @@@
-                 
-                 
 +/* ownCloud Android client application
 + *   Copyright (C) 2012 Bartek Przybylski
 + *
 + *   This program is free software: you can redistribute it and/or modify
 + *   it under the terms of the GNU General Public License as published by
 + *   the Free Software Foundation, either version 3 of the License, or
 + *   (at your option) any later version.
 + *
 + *   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.operations;
 +
 +import java.util.List;
 +import java.util.Vector;
 +
 +import org.apache.http.HttpStatus;
 +import org.apache.jackrabbit.webdav.MultiStatus;
 +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 +
 +import android.accounts.Account;
 +import android.content.Context;
 +import android.content.Intent;
 +import android.util.Log;
 +
 +import com.owncloud.android.datamodel.DataStorageManager;
 +import com.owncloud.android.datamodel.OCFile;
 +import com.owncloud.android.files.services.FileDownloader;
++import com.owncloud.android.files.services.FileObserverService;
 +
 +import eu.alefzero.webdav.WebdavClient;
 +import eu.alefzero.webdav.WebdavEntry;
 +import eu.alefzero.webdav.WebdavUtils;
 +
 +
 +/**
 + * Remote operation performing the synchronization a the contents of a remote folder with the local database
 + * 
 + * @author David A. Velasco
 + */
 +public class SynchronizeFolderOperation extends RemoteOperation {
 +
 +    private static final String TAG = SynchronizeFolderOperation.class.getCanonicalName();
 +
 +    /** Remote folder to synchronize */
 +    private String mRemotePath;
 +    
 +    /** Timestamp for the synchronization in progress */
 +    private long mCurrentSyncTime;
 +    
 +    /** Id of the folder to synchronize in the local database */
 +    private long mParentId;
 +    
 +    /** Access to the local database */
 +    private DataStorageManager mStorageManager;
 +    
 +    /** Account where the file to synchronize belongs */
 +    private Account mAccount;
 +    
 +    /** Android context; necessary to send requests to the download service; maybe something to refactor */
 +    private Context mContext;
 +    
 +    /** Files and folders contained in the synchronized folder */
 +    private List<OCFile> mChildren;
 +    
 +    
 +    public SynchronizeFolderOperation(  String remotePath, 
 +                                        long currentSyncTime, 
 +                                        long parentId, 
 +                                        DataStorageManager dataStorageManager, 
 +                                        Account account, 
 +                                        Context context ) {
 +        mRemotePath = remotePath;
 +        mCurrentSyncTime = currentSyncTime;
 +        mParentId = parentId;
 +        mStorageManager = dataStorageManager;
 +        mAccount = account;
 +        mContext = context;
 +    }
 +    
 +    
 +    /**
 +     * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
 +     * 
 +     * @return      List of files and folders contained in the synchronized folder.
 +     */
 +    public List<OCFile> getChildren() {
 +        return mChildren;
 +    }
 +    
 +    
 +    @Override
 +    protected RemoteOperationResult run(WebdavClient client) {
 +        RemoteOperationResult result = null;
 +        
 +        // code before in FileSyncAdapter.fetchData
 +        PropFindMethod query = null;
 +        try {
 +            Log.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
 +            
 +            // remote request 
 +            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
 +            int status = client.executeMethod(query);
 +            
 +            // check and process response   - /// TODO take into account all the possible status per child-resource
 +            if (isMultiStatus(status)) { 
 +                MultiStatus resp = query.getResponseBodyAsMultiStatus();
 +            
 +                // synchronize properties of the parent folder, if necessary
 +                if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
 +                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
 +                    OCFile parent = fillOCFile(we);
 +                    parent.setParentId(mParentId);
 +                    mStorageManager.saveFile(parent);
 +                    mParentId = parent.getFileId();
 +                }
 +                
 +                // read contents in folder
 +                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
 +                for (int i = 1; i < resp.getResponses().length; ++i) {
 +                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
 +                    OCFile file = fillOCFile(we);
 +                    file.setParentId(mParentId);
 +                    OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
 +                    if (oldFile != null) {
 +                        if (oldFile.keepInSync() && file.getModificationTimestamp() > oldFile.getModificationTimestamp()) {
++                            disableObservance(file);        // first disable observer so we won't get file upload right after download
 +                            requestContentDownload(file);
 +                        }
 +                        file.setKeepInSync(oldFile.keepInSync());
 +                    }
 +                
 +                    updatedFiles.add(file);
 +                }
-     
++                                
 +                // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
 +                mStorageManager.saveFiles(updatedFiles);
 +
 +                
 +                // removal of obsolete files
 +                mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
 +                OCFile file;
 +                String currentSavePath = FileDownloader.getSavePath(mAccount.name);
 +                for (int i=0; i < mChildren.size(); ) {
 +                    file = mChildren.get(i);
 +                    if (file.getLastSyncDate() != mCurrentSyncTime) {
 +                        Log.d(TAG, "removing file: " + file);
 +                        mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
 +                        mChildren.remove(i);
 +                    } else {
 +                        i++;
 +                    }
 +                }
 +                
 +            } else {
 +                client.exhaustResponse(query.getResponseBodyAsStream());
 +            }
 +            
 +            // prepare result object
 +            result = new RemoteOperationResult(isMultiStatus(status), status);
 +            Log.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
 +            
 +            
 +        } catch (Exception e) {
 +            result = new RemoteOperationResult(e);
 +            Log.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
 +
 +        } finally {
 +            if (query != null)
 +                query.releaseConnection();  // let the connection available for other methods
 +        }
 +        
 +        return result;
 +    }
 +    
++
 +    public boolean isMultiStatus(int status) {
 +        return (status == HttpStatus.SC_MULTI_STATUS); 
 +    }
 +
 +
 +    /**
 +     * Creates and populates a new {@link OCFile} object with the data read from the server.
 +     * 
 +     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
 +     * @return          New OCFile instance representing the remote resource described by we.
 +     */
 +    private OCFile fillOCFile(WebdavEntry we) {
 +        OCFile file = new OCFile(we.decodedPath());
 +        file.setCreationTimestamp(we.createTimestamp());
 +        file.setFileLength(we.contentLength());
 +        file.setMimetype(we.contentType());
 +        file.setModificationTimestamp(we.modifiedTimesamp());
 +        file.setLastSyncDate(mCurrentSyncTime);
 +        return file;
 +    }
 +    
 +    
++    /**
++     * Request to stop the observance of local updates for a file.  
++     * 
++     * @param file      OCFile representing the remote file to stop to monitor for local updates
++     */
++    private void disableObservance(OCFile file) {
++        Log.d(TAG, "Disabling observation of remote file" + file.getRemotePath());
++        Intent intent = new Intent(mContext, FileObserverService.class);
++        intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_ADD_DOWNLOADING_FILE);
++        intent.putExtra(FileObserverService.KEY_CMD_ARG, file.getRemotePath());
++        mContext.startService(intent);
++        
++    }
++
++
 +    /** 
 +     * Requests a download to the file download service
 +     * 
 +     * @param   file    OCFile representing the remote file to download
 +     */
 +    private void requestContentDownload(OCFile file) {
 +        Intent intent = new Intent(mContext, FileDownloader.class);
 +        intent.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
 +        intent.putExtra(FileDownloader.EXTRA_FILE, file);
 +        mContext.startService(intent);
 +    }
 +
 +
 +}
  package com.owncloud.android.syncadapter;\r
  \r
  import java.io.IOException;\r
 +import java.net.UnknownHostException;\r
  import java.util.List;\r
 -import java.util.Vector;\r
  \r
 -import org.apache.http.HttpStatus;\r
  import org.apache.jackrabbit.webdav.DavException;\r
 -import org.apache.jackrabbit.webdav.MultiStatus;\r
 -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;\r
 -import org.json.JSONObject;\r
  \r
 -import com.owncloud.android.AccountUtils;\r
  import com.owncloud.android.R;\r
 -import com.owncloud.android.authenticator.AccountAuthenticator;\r
 +import com.owncloud.android.datamodel.DataStorageManager;\r
  import com.owncloud.android.datamodel.FileDataStorageManager;\r
  import com.owncloud.android.datamodel.OCFile;\r
++//<<<<<<< HEAD
 +import com.owncloud.android.operations.RemoteOperationResult;\r
 +import com.owncloud.android.operations.SynchronizeFolderOperation;\r
 +import com.owncloud.android.operations.UpdateOCVersionOperation;\r
++/*=======
+ import com.owncloud.android.files.services.FileDownloader;\r
+ import com.owncloud.android.files.services.FileObserverService;\r
+ import com.owncloud.android.utils.OwnCloudVersion;\r
++>>>>>>> origin/master*/
  \r
  import android.accounts.Account;\r
  import android.app.Notification;\r
@@@ -87,137 -90,209 +93,164 @@@ public class FileSyncAdapter extends Ab
          this.setAccount(account);\r
          this.setContentProvider(provider);\r
          this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
 +        try {\r
 +            this.initClientForCurrentAccount();\r
 +        } catch (UnknownHostException e) {\r
 +            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
 +            mSyncResult.tooManyRetries = true;\r
 +            notifyFailedSynchronization();\r
 +            return;\r
 +        }\r
          \r
 -        /*  Commented code for ugly performance tests\r
 -        mDelaysIndex = 0;\r
 -        mDelaysCount = 0;\r
 -        */\r
 -\r
 -        Log.d(TAG, "syncing owncloud account " + account.name);\r
 -\r
 -        sendStickyBroadcast(true, null);  // message to signal the start to the UI\r
 +        Log.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");\r
 +        sendStickyBroadcast(true, null, null);  // message to signal the start of the synchronization to the UI\r
          \r
 -        updateOCVersion();\r
 -\r
 -        String uri = getUri().toString();\r
 -        PropFindMethod query = null;\r
          try {\r
 +            updateOCVersion();\r
              mCurrentSyncTime = System.currentTimeMillis();\r
 -            query = new PropFindMethod(uri + "/");\r
 -            int status = getClient().executeMethod(query);\r
 -            if (status != HttpStatus.SC_UNAUTHORIZED) {\r
 -                MultiStatus resp = query.getResponseBodyAsMultiStatus();\r
 -\r
 -                if (resp.getResponses().length > 0) {\r
 -                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], getUri().getPath());\r
 -                    OCFile file = fillOCFile(we);\r
 -                    file.setParentId(0);\r
 -                    getStorageManager().saveFile(file);\r
 -                    if (!mCancellation) {\r
 -                        fetchData(uri, syncResult, file.getFileId());\r
 -                    }\r
 -                }\r
 -\r
 +            if (!mCancellation) {\r
 +                fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);\r
 +                \r
              } else {\r
 -                syncResult.stats.numAuthExceptions++;\r
 +                Log.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");\r
              }\r
 -        } catch (IOException e) {\r
 -            syncResult.stats.numIoExceptions++;\r
 -            logException(e, uri + "/");\r
 -            \r
 -        } catch (DavException e) {\r
 -            syncResult.stats.numParseExceptions++;\r
 -            logException(e, uri + "/");\r
              \r
 -        } catch (Exception e) {\r
 -            // TODO something smart with syncresult\r
 -            logException(e, uri + "/");\r
 -            mRightSync = false;\r
              \r
          } finally {\r
 -            if (query != null)\r
 -                query.releaseConnection();  // let the connection available for other methods\r
 -            mRightSync &= (syncResult.stats.numIoExceptions == 0 && syncResult.stats.numAuthExceptions == 0 && syncResult.stats.numParseExceptions == 0);\r
 -            if (!mRightSync && mIsManualSync) {\r
 +            // it's important making this although very unexpected errors occur; that's the reason for the finally\r
 +            \r
 +            if (mFailedResultsCounter > 0 && mIsManualSync) {\r
                  /// don't let the system synchronization manager retries MANUAL synchronizations\r
                  //      (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)\r
 -                syncResult.tooManyRetries = true;\r
 +                mSyncResult.tooManyRetries = true;\r
                  \r
                  /// notify the user about the failure of MANUAL synchronization\r
 -                Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());\r
 -                notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
 -                // TODO put something smart in the contentIntent below\r
 -                notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
 -                notification.setLatestEventInfo(getContext().getApplicationContext(), \r
 -                                                getContext().getString(R.string.sync_fail_ticker), \r
 -                                                String.format(getContext().getString(R.string.sync_fail_content), account.name), \r
 -                                                notification.contentIntent);\r
 -                ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);\r
 +                notifyFailedSynchronization();\r
              }\r
 -            sendStickyBroadcast(false, null);        // message to signal the end to the UI\r
 +            sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI\r
          }\r
          \r
 -        /*  Commented code for ugly performance tests\r
 -        long sum = 0, mean = 0, max = 0, min = Long.MAX_VALUE;\r
 -        for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {\r
 -            sum += mResponseDelays[i];\r
 -            max = Math.max(max, mResponseDelays[i]);\r
 -            min = Math.min(min, mResponseDelays[i]);\r
 -        }\r
 -        mean = sum / mDelaysCount;\r
 -        Log.e(TAG, "SYNC STATS - response: mean time = " + mean + " ; max time = " + max + " ; min time = " + min);\r
 -        \r
 -        sum = 0; max = 0; min = Long.MAX_VALUE;\r
 -        for (int i=0; i<MAX_DELAYS && i<mDelaysCount; i++) {\r
 -            sum += mSaveDelays[i];\r
 -            max = Math.max(max, mSaveDelays[i]);\r
 -            min = Math.min(min, mSaveDelays[i]);\r
 +    }\r
 +    \r
 +    \r
 +    \r
 +    /**\r
 +     * Called by system SyncManager when a synchronization is required to be cancelled.\r
 +     * \r
 +     * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder\r
 +     * fetched will be still saved in the database. See onPerformSync implementation.\r
 +     */\r
 +    @Override\r
 +    public void onSyncCanceled() {\r
 +        Log.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");\r
 +        mCancellation = true;\r
 +        super.onSyncCanceled();\r
 +    }\r
 +    \r
 +    \r
 +    /**\r
 +     * Updates the locally stored version value of the ownCloud server\r
 +     */\r
 +    private void updateOCVersion() {\r
 +        UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext());\r
 +        RemoteOperationResult result = update.execute(getClient());\r
 +        if (!result.isSuccess()) {\r
 +            mLastFailedResult = result; \r
          }\r
 -        mean = sum / mDelaysCount;\r
 -        Log.e(TAG, "SYNC STATS - save:     mean time = " + mean + " ; max time = " + max + " ; min time = " + min);\r
 -        Log.e(TAG, "SYNC STATS - folders measured: " + mDelaysCount);\r
 -        */\r
 -        \r
      }\r
  \r
 -    private void fetchData(String uri, SyncResult syncResult, long parentId) {\r
 -        PropFindMethod query = null;\r
 -        Vector<OCFile> children = null;\r
 -        try {\r
 -            Log.d(TAG, "fetching " + uri);\r
 -            \r
 -            // remote request \r
 -            query = new PropFindMethod(uri);\r
 -            /*  Commented code for ugly performance tests\r
 -            long responseDelay = System.currentTimeMillis();\r
 -            */\r
 -            int status = getClient().executeMethod(query);\r
 -            /*  Commented code for ugly performance tests\r
 -            responseDelay = System.currentTimeMillis() - responseDelay;\r
 -            Log.e(TAG, "syncing: RESPONSE TIME for " + uri + " contents, " + responseDelay + "ms");\r
 -            */\r
 -            if (status != HttpStatus.SC_UNAUTHORIZED) {\r
 -                MultiStatus resp = query.getResponseBodyAsMultiStatus();\r
 +    \r
 +    \r
 +    /**\r
 +     * Synchronize the properties of files and folders contained in a remote folder given by remotePath.\r
 +     * \r
 +     * @param remotePath        Remote path to the folder to synchronize.\r
 +     * @param parentId          Database Id of the folder to synchronize.\r
 +     */\r
 +    private void fetchData(String remotePath, long parentId) {\r
 +        \r
 +        if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))\r
 +            return;\r
 +        \r
 +        // perform folder synchronization\r
 +        SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation(  remotePath, \r
 +                                                                                    mCurrentSyncTime, \r
 +                                                                                    parentId, \r
 +                                                                                    getStorageManager(), \r
 +                                                                                    getAccount(), \r
 +                                                                                    getContext()\r
 +                                                                                  );\r
 +        RemoteOperationResult result = synchFolderOp.execute(getClient());\r
 +        \r
 +        \r
 +        // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess\r
 +        sendStickyBroadcast(true, remotePath, null);\r
 +        \r
 +        if (result.isSuccess()) {\r
 +            // synchronize children folders \r
 +            List<OCFile> children = synchFolderOp.getChildren();\r
 +            fetchChildren(children);    // beware of the 'hidden' recursion here!\r
              \r
++//<<<<<<< HEAD
 +        } else {\r
 +            if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED) {\r
 +                mSyncResult.stats.numAuthExceptions++;\r
 +                \r
 +            } else if (result.getException() instanceof DavException) {\r
 +                mSyncResult.stats.numParseExceptions++;\r
 +                \r
 +            } else if (result.getException() instanceof IOException) { \r
 +                mSyncResult.stats.numIoExceptions++;\r
++/*=======
+                 // insertion or update of files\r
+                 List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);\r
+                 for (int i = 1; i < resp.getResponses().length; ++i) {\r
+                     WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());\r
+                     OCFile file = fillOCFile(we);\r
+                     file.setParentId(parentId);\r
+                     if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&\r
+                             getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&\r
+                             file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())\r
+                                                                          .getModificationTimestamp()) {\r
+                         // first disable observer so we won't get file upload right after download\r
+                         Log.d(TAG, "Disabling observation of remote file" + file.getRemotePath());\r
+                         Intent intent = new Intent(getContext(), FileObserverService.class);\r
+                         intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_ADD_DOWNLOADING_FILE);\r
+                         intent.putExtra(FileObserverService.KEY_CMD_ARG, file.getRemotePath());\r
+                         getContext().startService(intent);\r
+                         intent = new Intent(this.getContext(), FileDownloader.class);\r
+                         intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());\r
+                         intent.putExtra(FileDownloader.EXTRA_FILE, file);\r
+                         file.setKeepInSync(true);\r
+                         getContext().startService(intent);\r
+                     }\r
+                     if (getStorageManager().getFileByPath(file.getRemotePath()) != null)\r
+                         file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());\r
++>>>>>>> origin/master*/
                  \r
 -                    // Log.v(TAG, "adding file: " + file);\r
 -                    updatedFiles.add(file);\r
 -                }\r
 -                /*  Commented code for ugly performance tests\r
 -                long saveDelay = System.currentTimeMillis();\r
 -                 */            \r
 -                getStorageManager().saveFiles(updatedFiles);    // all "at once" ; trying to get a best performance in database update\r
 -                /*  Commented code for ugly performance tests\r
 -                saveDelay = System.currentTimeMillis() - saveDelay;\r
 -                Log.e(TAG, "syncing: SAVE TIME for " + uri + " contents, " + mSaveDelays[mDelaysIndex] + "ms");\r
 -                 */\r
 -            \r
 -                // removal of obsolete files\r
 -                 children = getStorageManager().getDirectoryContent(\r
 -                        getStorageManager().getFileById(parentId));\r
 -                OCFile file;\r
 -                String currentSavePath = FileDownloader.getSavePath(getAccount().name);\r
 -                for (int i=0; i < children.size(); ) {\r
 -                    file = children.get(i);\r
 -                    if (file.getLastSyncDate() != mCurrentSyncTime) {\r
 -                        Log.v(TAG, "removing file: " + file);\r
 -                        getStorageManager().removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));\r
 -                        children.remove(i);\r
 -                    } else {\r
 -                        i++;\r
 -                    }\r
 -                }\r
 -            \r
 -            } else {\r
 -                syncResult.stats.numAuthExceptions++;\r
              }\r
 -        } catch (IOException e) {\r
 -            syncResult.stats.numIoExceptions++;\r
 -            logException(e, uri);\r
 -            \r
 -        } catch (DavException e) {\r
 -            syncResult.stats.numParseExceptions++;\r
 -            logException(e, uri);\r
 +            mFailedResultsCounter++;\r
 +            mLastFailedResult = result;\r
 +        }\r
              \r
 -        } catch (Exception e) {\r
 -            // TODO something smart with syncresult\r
 -            mRightSync = false;\r
 -            logException(e, uri);\r
 -\r
 -        } finally {\r
 -            if (query != null)\r
 -                query.releaseConnection();  // let the connection available for other methods\r
 +    }\r
  \r
 -            // synchronized folder -> notice to UI\r
 -            sendStickyBroadcast(true, getStorageManager().getFileById(parentId).getRemotePath());\r
 +    /**\r
 +     * Checks if a failed result should terminate the synchronization process immediately, according to\r
 +     * OUR OWN POLICY\r
 +     * \r
 +     * @param   failedResult        Remote operation result to check.\r
 +     * @return                      'True' if the result should immediately finish the synchronization\r
 +     */\r
 +    private boolean isFinisher(RemoteOperationResult failedResult) {\r
 +        if  (failedResult != null) {\r
 +            RemoteOperationResult.ResultCode code = failedResult.getCode();\r
 +            return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||\r
 +                    code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||\r
 +                    code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||\r
 +                    code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));\r
          }\r
 -        \r
 -        \r
 -        fetchChildren(children, syncResult);\r
 -        if (mCancellation) Log.d(TAG, "Leaving " + uri + " because cancelation request");\r
 -        \r
 -        \r
 -        /*  Commented code for ugly performance tests\r
 -        mResponseDelays[mDelaysIndex] = responseDelay;\r
 -        mSaveDelays[mDelaysIndex] = saveDelay;\r
 -        mDelaysCount++;\r
 -        mDelaysIndex++;\r
 -        if (mDelaysIndex >= MAX_DELAYS)\r
 -            mDelaysIndex = 0;\r
 -         */\r
 -        \r
 +        return false;\r
      }\r
  \r
      /**\r