mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-23 11:21:29 +00:00
Refactor SyncthingService, moving killSyncthing() to background
This commit is contained in:
parent
84ae6953da
commit
4e32d60278
3 changed files with 140 additions and 155 deletions
|
@ -6,7 +6,6 @@ import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -98,15 +97,10 @@ public abstract class StateDialogActivity extends SyncthingActivity {
|
||||||
? R.string.web_gui_creating_key
|
? R.string.web_gui_creating_key
|
||||||
: R.string.api_loading);
|
: R.string.api_loading);
|
||||||
|
|
||||||
try {
|
mLoadingDialog = new AlertDialog.Builder(this)
|
||||||
mLoadingDialog = new AlertDialog.Builder(this)
|
.setCancelable(false)
|
||||||
.setCancelable(false)
|
.setView(dialogLayout)
|
||||||
.setView(dialogLayout)
|
.show();
|
||||||
.show();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// Catch and do nothing, workaround for https://stackoverflow.com/q/46030692/1837158
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissLoadingDialog() {
|
private void dismissLoadingDialog() {
|
||||||
|
|
|
@ -28,8 +28,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.LineNumberReader;
|
import java.io.LineNumberReader;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import eu.chainfire.libsuperuser.Shell;
|
import eu.chainfire.libsuperuser.Shell;
|
||||||
|
@ -149,8 +149,7 @@ public class SyncthingRunnable implements Runnable {
|
||||||
// Syncthing was shut down (via API or SIGKILL), do nothing.
|
// Syncthing was shut down (via API or SIGKILL), do nothing.
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// Syncthing is already running, kill it and try again.
|
Log.w(TAG, "Another Syncthing instance is already running, requesting restart via SyncthingService intent");
|
||||||
killSyncthing();
|
|
||||||
//fallthrough
|
//fallthrough
|
||||||
case 3:
|
case 3:
|
||||||
// Restart was requested via Rest API call.
|
// Restart was requested via Rest API call.
|
||||||
|
@ -246,42 +245,48 @@ public class SyncthingRunnable implements Runnable {
|
||||||
}.start();
|
}.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnSyncthingKilled {
|
||||||
|
void onKilled();
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Look for running libsyncthing.so processes and kill them.
|
* Look for running libsyncthing.so processes and kill them.
|
||||||
* Try a SIGINT first, then try again with SIGKILL.
|
* Try a SIGINT first, then try again with SIGKILL.
|
||||||
*/
|
*/
|
||||||
public void killSyncthing() {
|
public void killSyncthing(OnSyncthingKilled onKilledListener) {
|
||||||
for (int i = 0; i < 2; i++) {
|
new Thread(() -> {
|
||||||
Process ps = null;
|
for (int i = 0; i < 2; i++) {
|
||||||
DataOutputStream psOut = null;
|
Process ps = null;
|
||||||
try {
|
DataOutputStream psOut = null;
|
||||||
ps = Runtime.getRuntime().exec((mUseRoot) ? "su" : "sh");
|
|
||||||
psOut = new DataOutputStream(ps.getOutputStream());
|
|
||||||
psOut.writeBytes("ps | grep libsyncthing.so\n");
|
|
||||||
psOut.writeBytes("exit\n");
|
|
||||||
psOut.flush();
|
|
||||||
ps.waitFor();
|
|
||||||
InputStreamReader isr = new InputStreamReader(ps.getInputStream(), "UTF-8");
|
|
||||||
BufferedReader br = new BufferedReader(isr);
|
|
||||||
String id;
|
|
||||||
while ((id = br.readLine()) != null) {
|
|
||||||
id = id.trim().split("\\s+")[1];
|
|
||||||
killProcessId(id, i > 0);
|
|
||||||
}
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
Log.w(TAG_KILL, "Failed list Syncthing processes", e);
|
|
||||||
} finally {
|
|
||||||
try {
|
try {
|
||||||
if (psOut != null)
|
ps = Runtime.getRuntime().exec((mUseRoot) ? "su" : "sh");
|
||||||
psOut.close();
|
psOut = new DataOutputStream(ps.getOutputStream());
|
||||||
} catch (IOException e) {
|
psOut.writeBytes("ps | grep libsyncthing.so\n");
|
||||||
Log.w(TAG_KILL, "Failed close the psOut stream", e);
|
psOut.writeBytes("exit\n");
|
||||||
}
|
psOut.flush();
|
||||||
if (ps != null) {
|
ps.waitFor();
|
||||||
ps.destroy();
|
InputStreamReader isr = new InputStreamReader(ps.getInputStream(), "UTF-8");
|
||||||
|
BufferedReader br = new BufferedReader(isr);
|
||||||
|
String id;
|
||||||
|
while ((id = br.readLine()) != null) {
|
||||||
|
id = id.trim().split("\\s+")[1];
|
||||||
|
killProcessId(id, i > 0);
|
||||||
|
}
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
Log.w(TAG_KILL, "Failed list Syncthing processes", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (psOut != null)
|
||||||
|
psOut.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG_KILL, "Failed close the psOut stream", e);
|
||||||
|
}
|
||||||
|
if (ps != null) {
|
||||||
|
ps.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
onKilledListener.onKilled();
|
||||||
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,11 +408,9 @@ public class SyncthingRunnable implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Process setupAndLaunch(HashMap<String, String> env) throws IOException {
|
private Process setupAndLaunch(HashMap<String, String> env) throws IOException {
|
||||||
Process process = null;
|
|
||||||
|
|
||||||
if (mUseRoot) {
|
if (mUseRoot) {
|
||||||
ProcessBuilder pb = new ProcessBuilder("su");
|
ProcessBuilder pb = new ProcessBuilder("su");
|
||||||
process = pb.start();
|
Process process = pb.start();
|
||||||
// The su binary prohibits the inheritance of environment variables.
|
// The su binary prohibits the inheritance of environment variables.
|
||||||
// Even with --preserve-environment the environment gets messed up.
|
// Even with --preserve-environment the environment gets messed up.
|
||||||
// We therefore start a root shell, and set all the environment variables manually.
|
// We therefore start a root shell, and set all the environment variables manually.
|
||||||
|
@ -421,12 +424,11 @@ public class SyncthingRunnable implements Runnable {
|
||||||
// If we did not use exec, we would wait infinitely for the process to terminate (ret = process.waitFor(); in run()).
|
// If we did not use exec, we would wait infinitely for the process to terminate (ret = process.waitFor(); in run()).
|
||||||
// With exec the whole process terminates when Syncthing exits.
|
// With exec the whole process terminates when Syncthing exits.
|
||||||
suOut.writeBytes("exec " + TextUtils.join(" ", mCommand) + "\n");
|
suOut.writeBytes("exec " + TextUtils.join(" ", mCommand) + "\n");
|
||||||
suOut = null;
|
return process;
|
||||||
} else {
|
} else {
|
||||||
ProcessBuilder pb = new ProcessBuilder(mCommand);
|
ProcessBuilder pb = new ProcessBuilder(mCommand);
|
||||||
pb.environment().putAll(env);
|
pb.environment().putAll(env);
|
||||||
process = pb.start();
|
return pb.start();
|
||||||
}
|
}
|
||||||
return process;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.PRNGFixes;
|
import com.android.PRNGFixes;
|
||||||
|
@ -39,6 +38,7 @@ import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the native syncthing instance and provides an API to access it.
|
* Holds the native syncthing instance and provides an API to access it.
|
||||||
|
@ -126,7 +126,7 @@ public class SyncthingService extends Service implements
|
||||||
|
|
||||||
private final HashSet<OnApiChangeListener> mOnApiChangeListeners =
|
private final HashSet<OnApiChangeListener> mOnApiChangeListeners =
|
||||||
new HashSet<>();
|
new HashSet<>();
|
||||||
|
|
||||||
private final BroadcastReceiver mPowerSaveModeChangedReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mPowerSaveModeChangedReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@ -135,17 +135,19 @@ public class SyncthingService extends Service implements
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INIT: Service is starting up and initializing.
|
* Indicates the current state of SyncthingService and of Syncthing itself.
|
||||||
* STARTING: Syncthing binary is starting (but the API is not yet ready).
|
|
||||||
* ACTIVE: Syncthing binary is up and running.
|
|
||||||
* DISABLED: Syncthing binary is stopped according to user preferences.
|
|
||||||
*/
|
*/
|
||||||
public enum State {
|
public enum State {
|
||||||
|
/** Service is initializing, Syncthing was not started yet. */
|
||||||
INIT,
|
INIT,
|
||||||
|
/** Syncthing binary is starting. */
|
||||||
STARTING,
|
STARTING,
|
||||||
|
/** Syncthing binary is running, API is available. */
|
||||||
ACTIVE,
|
ACTIVE,
|
||||||
|
/** Syncthing is stopped according to user preferences. */
|
||||||
DISABLED,
|
DISABLED,
|
||||||
ERROR
|
/** There is some problem that prevents Syncthing from running. */
|
||||||
|
ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
private State mCurrentState = State.INIT;
|
private State mCurrentState = State.INIT;
|
||||||
|
@ -164,7 +166,7 @@ public class SyncthingService extends Service implements
|
||||||
|
|
||||||
private DeviceStateHolder mDeviceStateHolder;
|
private DeviceStateHolder mDeviceStateHolder;
|
||||||
|
|
||||||
private SyncthingRunnable mRunnable;
|
private SyncthingRunnable mSyncthingRunnable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles intents, either {@link #ACTION_RESTART}, or intents having
|
* Handles intents, either {@link #ACTION_RESTART}, or intents having
|
||||||
|
@ -177,14 +179,12 @@ public class SyncthingService extends Service implements
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
|
|
||||||
if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
|
||||||
shutdown();
|
shutdown(State.INIT, () -> new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR));
|
||||||
mCurrentState = State.INIT;
|
|
||||||
updateState();
|
|
||||||
} else if (ACTION_RESET.equals(intent.getAction())) {
|
} else if (ACTION_RESET.equals(intent.getAction())) {
|
||||||
shutdown();
|
shutdown(State.INIT, () -> {
|
||||||
new SyncthingRunnable(this, SyncthingRunnable.Command.reset).run();
|
new SyncthingRunnable(this, SyncthingRunnable.Command.reset).run();
|
||||||
mCurrentState = State.INIT;
|
new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
updateState();
|
});
|
||||||
} else {
|
} else {
|
||||||
mDeviceStateHolder.update(intent);
|
mDeviceStateHolder.update(intent);
|
||||||
updateState();
|
updateState();
|
||||||
|
@ -196,7 +196,7 @@ public class SyncthingService extends Service implements
|
||||||
* Checks according to preferences and charging/wifi state, whether syncthing should be enabled
|
* Checks according to preferences and charging/wifi state, whether syncthing should be enabled
|
||||||
* or not.
|
* or not.
|
||||||
*
|
*
|
||||||
* Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is
|
* Depending on the result, syncthing is started or stopped, and {@link #onApiChange} is
|
||||||
* called.
|
* called.
|
||||||
*/
|
*/
|
||||||
private void updateState() {
|
private void updateState() {
|
||||||
|
@ -209,30 +209,10 @@ public class SyncthingService extends Service implements
|
||||||
|
|
||||||
// HACK: Make sure there is no syncthing binary left running from an improper
|
// HACK: Make sure there is no syncthing binary left running from an improper
|
||||||
// shutdown (eg Play Store update).
|
// shutdown (eg Play Store update).
|
||||||
// NOTE: This will log an exception if syncthing is not actually running.
|
shutdown(State.INIT, () -> {
|
||||||
shutdown();
|
Log.i(TAG, "Starting syncthing according to current state and preferences");
|
||||||
|
new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
Log.i(TAG, "Starting syncthing according to current state and preferences");
|
});
|
||||||
mConfig = null;
|
|
||||||
try {
|
|
||||||
mConfig = new ConfigXml(SyncthingService.this);
|
|
||||||
} catch (ConfigXml.OpenConfigException e) {
|
|
||||||
mCurrentState = State.ERROR;
|
|
||||||
Toast.makeText(this, R.string.config_create_failed, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mConfig != null) {
|
|
||||||
mCurrentState = State.STARTING;
|
|
||||||
|
|
||||||
if (mApi != null)
|
|
||||||
registerOnWebGuiAvailableListener(mApi);
|
|
||||||
if (mEventProcessor != null)
|
|
||||||
registerOnWebGuiAvailableListener(mEventProcessor);
|
|
||||||
pollWebGui();
|
|
||||||
mRunnable = new SyncthingRunnable(this, SyncthingRunnable.Command.main);
|
|
||||||
new Thread(mRunnable).start();
|
|
||||||
updateNotification();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Stop syncthing.
|
// Stop syncthing.
|
||||||
else {
|
else {
|
||||||
|
@ -240,11 +220,8 @@ public class SyncthingService extends Service implements
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log.i(TAG, "Stopping syncthing according to current state and preferences");
|
Log.i(TAG, "Stopping syncthing according to current state and preferences");
|
||||||
mCurrentState = State.DISABLED;
|
shutdown(State.DISABLED, () -> {});
|
||||||
|
|
||||||
shutdown();
|
|
||||||
}
|
}
|
||||||
onApiChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -321,68 +298,74 @@ public class SyncthingService extends Service implements
|
||||||
registerReceiver(mNetworkReceiver,
|
registerReceiver(mNetworkReceiver,
|
||||||
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||||
}
|
}
|
||||||
new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
updateState();
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
.registerOnSharedPreferenceChangeListener(this);
|
.registerOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the initial configuration, updates the config when coming from an old
|
* Sets up the initial configuration, and updates the config when coming from an old
|
||||||
* version, and reads syncthing URL and API key (these are passed internally as
|
* version.
|
||||||
* {@code Pair<String, String>}.
|
|
||||||
*/
|
*/
|
||||||
private class StartupTask extends AsyncTask<Void, Void, Pair<URL, String>> {
|
private class StartupTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
|
public StartupTask() {
|
||||||
|
mCurrentState = State.STARTING;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pair<URL, String> doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
try {
|
try {
|
||||||
mConfig = new ConfigXml(SyncthingService.this);
|
mConfig = new ConfigXml(SyncthingService.this);
|
||||||
mConfig.updateIfNeeded();
|
mConfig.updateIfNeeded();
|
||||||
return new Pair<>(mConfig.getWebGuiUrl(), mConfig.getApiKey());
|
|
||||||
} catch (ConfigXml.OpenConfigException e) {
|
} catch (ConfigXml.OpenConfigException e) {
|
||||||
return null;
|
Toast.makeText(SyncthingService.this, R.string.config_create_failed,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
onApiChange(State.ERROR);
|
||||||
|
cancel(true);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Pair<URL, String> urlAndKey) {
|
protected void onPostExecute(Void aVoid) {
|
||||||
if (urlAndKey == null) {
|
mApi = new RestApi(SyncthingService.this, mConfig.getWebGuiUrl(), mConfig.getApiKey(),
|
||||||
Toast.makeText(SyncthingService.this, R.string.config_create_failed,
|
SyncthingService.this::onSyncthingStarted, () -> onApiChange(mCurrentState));
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
mCurrentState = State.ERROR;
|
|
||||||
onApiChange();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second, () -> {
|
|
||||||
mCurrentState = State.ACTIVE;
|
|
||||||
onApiChange();
|
|
||||||
new Thread(() -> {
|
|
||||||
for (Folder r : mApi.getFolders()) {
|
|
||||||
try {
|
|
||||||
mObservers.add(new FolderObserver(mApi, r));
|
|
||||||
} catch (FolderObserver.FolderNotExistingException e) {
|
|
||||||
Log.w(TAG, "Failed to add observer for folder", e);
|
|
||||||
} catch (StackOverflowError e) {
|
|
||||||
Log.w(TAG, "Failed to add folder observer", e);
|
|
||||||
Toast.makeText(SyncthingService.this,
|
|
||||||
R.string.toast_folder_observer_stack_overflow,
|
|
||||||
Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}, SyncthingService.this::onApiChange);
|
|
||||||
|
|
||||||
mEventProcessor = new EventProcessor(SyncthingService.this, mApi);
|
mEventProcessor = new EventProcessor(SyncthingService.this, mApi);
|
||||||
|
|
||||||
registerOnWebGuiAvailableListener(mApi);
|
if (mApi != null)
|
||||||
registerOnWebGuiAvailableListener(mEventProcessor);
|
registerOnWebGuiAvailableListener(mApi);
|
||||||
|
if (mEventProcessor != null)
|
||||||
|
registerOnWebGuiAvailableListener(mEventProcessor);
|
||||||
Log.i(TAG, "Web GUI will be available at " + mConfig.getWebGuiUrl());
|
Log.i(TAG, "Web GUI will be available at " + mConfig.getWebGuiUrl());
|
||||||
updateState();
|
|
||||||
|
pollWebGui();
|
||||||
|
mSyncthingRunnable = new SyncthingRunnable(SyncthingService.this, SyncthingRunnable.Command.main);
|
||||||
|
new Thread(mSyncthingRunnable).start();
|
||||||
|
updateNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onSyncthingStarted() {
|
||||||
|
onApiChange(State.ACTIVE);
|
||||||
|
new Thread(() -> {
|
||||||
|
for (Folder r : mApi.getFolders()) {
|
||||||
|
try {
|
||||||
|
mObservers.add(new FolderObserver(mApi, r));
|
||||||
|
} catch (FolderObserver.FolderNotExistingException e) {
|
||||||
|
Log.w(TAG, "Failed to add observer for folder", e);
|
||||||
|
} catch (StackOverflowError e) {
|
||||||
|
Log.w(TAG, "Failed to add folder observer", e);
|
||||||
|
Toast.makeText(SyncthingService.this,
|
||||||
|
R.string.toast_folder_observer_stack_overflow,
|
||||||
|
Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return mBinder;
|
return mBinder;
|
||||||
|
@ -405,7 +388,7 @@ public class SyncthingService extends Service implements
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Shutting down service immediately");
|
Log.i(TAG, "Shutting down service immediately");
|
||||||
shutdown();
|
shutdown(State.DISABLED, () -> {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,13 +400,17 @@ public class SyncthingService extends Service implements
|
||||||
unregisterReceiver(mNetworkReceiver);
|
unregisterReceiver(mNetworkReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutdown() {
|
/**
|
||||||
|
* Stop Syncthing and all helpers like event processor, api handler and folder observers.
|
||||||
|
*
|
||||||
|
* Sets {@link #mCurrentState} to newState, and calls onKilledListener once Syncthing is killed.
|
||||||
|
*/
|
||||||
|
private void shutdown(State newState, SyncthingRunnable.OnSyncthingKilled onKilledListener) {
|
||||||
|
onApiChange(newState);
|
||||||
|
|
||||||
if (mEventProcessor != null)
|
if (mEventProcessor != null)
|
||||||
mEventProcessor.shutdown();
|
mEventProcessor.shutdown();
|
||||||
|
|
||||||
if (mRunnable != null)
|
|
||||||
mRunnable.killSyncthing();
|
|
||||||
|
|
||||||
if (mApi != null)
|
if (mApi != null)
|
||||||
mApi.shutdown();
|
mApi.shutdown();
|
||||||
|
|
||||||
|
@ -433,6 +420,13 @@ public class SyncthingService extends Service implements
|
||||||
|
|
||||||
Stream.of(mObservers).forEach(FolderObserver::stopWatching);
|
Stream.of(mObservers).forEach(FolderObserver::stopWatching);
|
||||||
mObservers.clear();
|
mObservers.clear();
|
||||||
|
|
||||||
|
if (mSyncthingRunnable != null) {
|
||||||
|
mSyncthingRunnable.killSyncthing(onKilledListener);
|
||||||
|
mSyncthingRunnable = null;
|
||||||
|
} else {
|
||||||
|
onKilledListener.onKilled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -498,17 +492,14 @@ public class SyncthingService extends Service implements
|
||||||
mConfig.getApiKey(), result -> {
|
mConfig.getApiKey(), result -> {
|
||||||
synchronized (stateLock) {
|
synchronized (stateLock) {
|
||||||
if (mStopScheduled) {
|
if (mStopScheduled) {
|
||||||
mCurrentState = State.DISABLED;
|
shutdown(State.DISABLED, () -> {});
|
||||||
onApiChange();
|
|
||||||
shutdown();
|
|
||||||
mStopScheduled = false;
|
mStopScheduled = false;
|
||||||
stopSelf();
|
stopSelf();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(TAG, "Web GUI has come online at " + mConfig.getWebGuiUrl());
|
Log.i(TAG, "Web GUI has come online at " + mConfig.getWebGuiUrl());
|
||||||
mCurrentState = State.STARTING;
|
onApiChange(State.STARTING);
|
||||||
onApiChange();
|
|
||||||
Stream.of(mOnWebGuiAvailableListeners).forEach(OnWebGuiAvailableListener::onWebGuiAvailable);
|
Stream.of(mOnWebGuiAvailableListeners).forEach(OnWebGuiAvailableListener::onWebGuiAvailable);
|
||||||
mOnWebGuiAvailableListeners.clear();
|
mOnWebGuiAvailableListeners.clear();
|
||||||
});
|
});
|
||||||
|
@ -519,7 +510,8 @@ public class SyncthingService extends Service implements
|
||||||
*
|
*
|
||||||
* Must only be called from SyncthingService or {@link RestApi} on the main thread.
|
* Must only be called from SyncthingService or {@link RestApi} on the main thread.
|
||||||
*/
|
*/
|
||||||
private void onApiChange() {
|
private void onApiChange(State newState) {
|
||||||
|
mCurrentState = newState;
|
||||||
for (Iterator<OnApiChangeListener> i = mOnApiChangeListeners.iterator();
|
for (Iterator<OnApiChangeListener> i = mOnApiChangeListeners.iterator();
|
||||||
i.hasNext(); ) {
|
i.hasNext(); ) {
|
||||||
OnApiChangeListener listener = i.next();
|
OnApiChangeListener listener = i.next();
|
||||||
|
@ -566,24 +558,21 @@ public class SyncthingService extends Service implements
|
||||||
* @return True if the import was successful, false otherwise (eg if files aren't found).
|
* @return True if the import was successful, false otherwise (eg if files aren't found).
|
||||||
*/
|
*/
|
||||||
public boolean importConfig() {
|
public boolean importConfig() {
|
||||||
mCurrentState = State.DISABLED;
|
|
||||||
shutdown();
|
|
||||||
File config = new File(EXPORT_PATH, ConfigXml.CONFIG_FILE);
|
File config = new File(EXPORT_PATH, ConfigXml.CONFIG_FILE);
|
||||||
File privateKey = new File(EXPORT_PATH, PRIVATE_KEY_FILE);
|
File privateKey = new File(EXPORT_PATH, PRIVATE_KEY_FILE);
|
||||||
File publicKey = new File(EXPORT_PATH, PUBLIC_KEY_FILE);
|
File publicKey = new File(EXPORT_PATH, PUBLIC_KEY_FILE);
|
||||||
if (!config.exists() || !privateKey.exists() || !publicKey.exists())
|
if (!config.exists() || !privateKey.exists() || !publicKey.exists())
|
||||||
return false;
|
return false;
|
||||||
|
shutdown(State.INIT, () -> {
|
||||||
try {
|
try {
|
||||||
Files.copy(config, new File(getFilesDir(), ConfigXml.CONFIG_FILE));
|
Files.copy(config, new File(getFilesDir(), ConfigXml.CONFIG_FILE));
|
||||||
Files.copy(privateKey, new File(getFilesDir(), PRIVATE_KEY_FILE));
|
Files.copy(privateKey, new File(getFilesDir(), PRIVATE_KEY_FILE));
|
||||||
Files.copy(publicKey, new File(getFilesDir(), PUBLIC_KEY_FILE));
|
Files.copy(publicKey, new File(getFilesDir(), PUBLIC_KEY_FILE));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Failed to import config", e);
|
Log.w(TAG, "Failed to import config", e);
|
||||||
}
|
}
|
||||||
mCurrentState = State.INIT;
|
new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
onApiChange();
|
});
|
||||||
new StartupTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue