Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 998d119b authored by Vincent Bourgmayer's avatar Vincent Bourgmayer
Browse files

Don't add a syncRequest if the same one is already running

parent 66e79c5c
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright © Vincent Bourgmayer (/e/ foundation).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */
package foundation.e.drive.models;

import android.content.Context;

import com.owncloud.android.lib.common.operations.RemoteOperation;

import foundation.e.drive.operations.DownloadFileOperation;
import foundation.e.drive.operations.UploadFileOperation;

/**
 * This class encapsulates data, for SynchronizationSerivce, about a thread which run a RemoteOperation
 *
 * @author Vincent Bourgmayer
 */
public class SyncWrapper {
    private final SyncRequest request;
    private final RemoteOperation remoteOperation;
    private boolean isRunning;

    /**
     * Build an instance of SyncThreadHolder for a file transfer
     * @param request SyncRequest at origin of the file transfer
     * @param context Application context, used to create RemoteOperation to run
     */
    public SyncWrapper(SyncRequest request, Context context) {
        this.request = request;
        remoteOperation = createRemoteOperation(request, context);
        isRunning = true;
    }

    public RemoteOperation getRemoteOperation() {
        return remoteOperation;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public synchronized void setRunning(boolean running) {
        isRunning = running;
    }

    /**
     * Create the RemoteOperation (to perform file transfer) based on SyncRequest
     * @param request syncRequest for the file
     * @param context App context to be passed to RemoteOperation's contructor
     * @return RemoteOperation for Upload/Download request or null
     */
    private static RemoteOperation createRemoteOperation(SyncRequest request, Context context) {
        final RemoteOperation operation;
        switch (request.getOperationType()) {
            case UPLOAD:
                final SyncedFileState sfs = request.getSyncedFileState();
                operation = new UploadFileOperation(sfs, context);
                break;
            case DOWNLOAD:
                final DownloadRequest downloadRequest = (DownloadRequest) request;
                operation = new DownloadFileOperation(downloadRequest.getRemoteFile(),
                        downloadRequest.getSyncedFileState(),
                        context);
                break;
            case REMOTE_DELETE:
            default:
                operation = null;
                break;
        }
        return operation;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof SyncRequest) {
            final SyncRequest objRequest = (SyncRequest) obj;
            return (objRequest.equals(this.request) && objRequest.getOperationType() == this.request.getOperationType());
        }
        return super.equals(obj);
    }
}
+57 −49
Original line number Diff line number Diff line
@@ -30,13 +30,13 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;

import foundation.e.drive.database.DbHelper;
import foundation.e.drive.models.DownloadRequest;
import foundation.e.drive.models.SyncRequest;
import foundation.e.drive.models.SyncedFileState;
import foundation.e.drive.models.SyncWrapper;
import foundation.e.drive.operations.DownloadFileOperation;
import foundation.e.drive.operations.RemoveFileOperation;
import foundation.e.drive.operations.UploadFileOperation;
@@ -52,10 +52,10 @@ public class SynchronizationService extends Service implements OnRemoteOperation
    private final SynchronizationBinder binder = new SynchronizationBinder();

    private ConcurrentLinkedDeque<SyncRequest> syncRequestQueue;
    private Hashtable<RemoteOperation, Integer> startedOperations; //Operations which are running
    private ConcurrentHashMap<Integer, SyncWrapper> startedSync; //Integer is thread index (1 to workerAmount)

    private Account account;
    private final int workerAmount = 4;
    private boolean[] threadWorkingState; //State of the threads; true mean the thread is working
    private Thread[] threadPool;
    private OwnCloudClient client;
    private OperationHandler handler;
@@ -76,9 +76,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation
        }

        syncRequestQueue = new ConcurrentLinkedDeque<>();
        startedOperations = new Hashtable<>();
        startedSync = new ConcurrentHashMap<>();
        threadPool = new Thread[workerAmount];
        threadWorkingState = new boolean[workerAmount];
        client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext());
        handler = new OperationHandler(this);

@@ -97,11 +96,20 @@ public class SynchronizationService extends Service implements OnRemoteOperation
    }

    public void queueOperation(SyncRequest request){
        for (SyncWrapper syncWrapper : startedSync.values()) {
            if (syncWrapper.equals(request)) {
                return;
            }
        }
        syncRequestQueue.remove(request);
        syncRequestQueue.add(request);
    }

    public void queueOperations(Collection<SyncRequest> requests){
        for (SyncWrapper syncWrapper : startedSync.values()) {
            requests.removeIf(syncRequest -> syncWrapper.equals(syncRequest));
        }

        syncRequestQueue.removeAll(requests);
        syncRequestQueue.addAll(requests);
    }
@@ -113,37 +121,55 @@ public class SynchronizationService extends Service implements OnRemoteOperation
        }
    }

    private void startWorker(int threadIndex){
        final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account);
        if (threadWorkingState[threadIndex]
                || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) {
            return;
        }

    private void startWorker(Integer threadIndex){
        if (!canStart(threadIndex)) return;

        final SyncRequest request = this.syncRequestQueue.poll(); //return null if empty
        if (request == null)  return;

        final RemoteOperation operation = this.createRemoteOperation(request);
        final SyncWrapper syncWrapper = new SyncWrapper(request, getApplicationContext());
        final RemoteOperation operation = syncWrapper.getRemoteOperation();

        if (operation != null) {
            Log.v(TAG, " an operation has been poll from queue");


            if (CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) {
                CommonUtils.createNotificationChannel(this);
                startedOperations.put(operation, threadIndex);
                Log.v(TAG, " starts " + request.getSyncedFileState().getName()
                        + " "  + request.getOperationType().name() + " on thread " + threadIndex);

                threadPool[threadIndex] = operation.execute(client, this, handler);
                threadWorkingState[threadIndex] = true;
                startedSync.put(threadIndex, syncWrapper);
            }
        }
    }

    /**
     * Check if conditions are met to run a new file transfer
     * @param threadIndex index of thread on which we want to perform the transfer
     * @return false if nogo
     */
    private boolean canStart(int threadIndex) {
        final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account);

        final SyncWrapper syncWrapper = startedSync.get(threadIndex);

        if ( (syncWrapper != null && syncWrapper.isRunning())
                || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) {
            return false;
        }
        return true;
    }

    @Override
    public void onRemoteOperationFinish(RemoteOperation callerOperation, RemoteOperationResult result) {
        Log.d(TAG, "onRemoteOperationFinish()");
        Integer threadIndex = this.startedOperations.remove(callerOperation);
        if (threadIndex != null) {
            this.threadWorkingState[threadIndex] = false;
            this.startWorker(threadIndex);
        for (Map.Entry<Integer, SyncWrapper> keyValue : startedSync.entrySet()) {
            if (keyValue.getValue().getRemoteOperation().equals(callerOperation)) {
                keyValue.getValue().setRunning(false);
                startWorker(keyValue.getKey());
            }
        }

        if (callerOperation instanceof RemoveFileOperation){
@@ -209,26 +235,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation
        }
    }

    private RemoteOperation createRemoteOperation(SyncRequest request){
        RemoteOperation operation;
        switch (request.getOperationType()){
            case UPLOAD:
                final SyncedFileState sfs = request.getSyncedFileState();
                operation = new UploadFileOperation(sfs, getApplicationContext());
                break;
            case DOWNLOAD:
                final DownloadRequest downloadRequest = (DownloadRequest) request;
                operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState(), getApplicationContext());
                break;
            case REMOTE_DELETE:
            default:
                operation = null;
                break;
        }
        return operation;
    }


    /**
     * Handler for the class
     */
@@ -244,10 +250,12 @@ public class SynchronizationService extends Service implements OnRemoteOperation
        @Override
        public void handleMessage(Message msg) {
            Log.i(TAG, "handler.handleMessage()");
            Bundle data = msg.getData();

            serviceWeakRef.get()
                    .threadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState");
            final Bundle data = msg.getData();
            if (data == null || !data.containsKey("thread index") || !data.containsKey("mThreadWorkingState")) {
                return ;
            }
            final SyncWrapper syncWrapper = serviceWeakRef.get().startedSync.get(data.getInt("thread index"));
            if (syncWrapper != null) syncWrapper.setRunning(data.getBoolean("mThreadWorkingState"));
        }
    }