ProfileService: Implement the rest of its interface

As per the FIXMEs, the list of profiles should eventually become a map.
Since it can only be modified on the main thread anyway, the current
code is still safe, and it works, it's just not optimal.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Samuel Holland 2017-07-31 21:22:02 -05:00
parent 0ea5ae605c
commit b6653fd7f0

View File

@ -12,8 +12,10 @@ import android.util.Log;
import com.wireguard.config.Profile; import com.wireguard.config.Profile;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -47,6 +49,84 @@ public class ProfileService extends Service {
return START_STICKY; return START_STICKY;
} }
private class ProfileAdder extends AsyncTask<Void, Void, Boolean> {
private final Profile profile;
private ProfileAdder(Profile profile) {
super();
this.profile = profile;
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.i(TAG, "Adding profile " + profile.getName());
try {
final String configFile = profile.getName() + ".conf";
final FileOutputStream stream = openFileOutput(configFile, MODE_PRIVATE);
stream.write(profile.toString().getBytes(StandardCharsets.UTF_8));
stream.close();
return true;
} catch (IOException e) {
Log.e(TAG, "Could not create profile " + profile.getName(), e);
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (!result)
return;
profile.setIsConnected(false);
profiles.add(profile);
}
}
private class ProfileConnecter extends AsyncTask<Void, Void, Boolean> {
private final Profile profile;
private ProfileConnecter(Profile profile) {
super();
this.profile = profile;
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.i(TAG, "Running wg-quick up for profile " + profile.getName());
final File configFile = new File(getFilesDir(), profile.getName() + ".conf");
return RootShell.run(null, "wg-quick up '" + configFile.getPath() + "'") == 0;
}
@Override
protected void onPostExecute(Boolean result) {
if (!result)
return;
profile.setIsConnected(true);
}
}
private class ProfileDisconnecter extends AsyncTask<Void, Void, Boolean> {
private final Profile profile;
private ProfileDisconnecter(Profile profile) {
super();
this.profile = profile;
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.i(TAG, "Running wg-quick down for profile " + profile.getName());
final File configFile = new File(getFilesDir(), profile.getName() + ".conf");
return RootShell.run(null, "wg-quick down '" + configFile.getPath() + "'") == 0;
}
@Override
protected void onPostExecute(Boolean result) {
if (!result)
return;
profile.setIsConnected(false);
}
}
private class ProfileLoader extends AsyncTask<File, Void, List<Profile>> { private class ProfileLoader extends AsyncTask<File, Void, List<Profile>> {
@Override @Override
protected List<Profile> doInBackground(File... files) { protected List<Profile> doInBackground(File... files) {
@ -83,9 +163,82 @@ public class ProfileService extends Service {
} }
} }
private class ProfileRemover extends AsyncTask<Void, Void, Boolean> {
private final Profile profile;
private ProfileRemover(Profile profile) {
super();
this.profile = profile;
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.i(TAG, "Removing profile " + profile.getName());
final File configFile = new File(getFilesDir(), profile.getName() + ".conf");
if (configFile.delete()) {
return true;
} else {
Log.e(TAG, "Could not delete configuration for profile " + profile.getName());
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (!result)
return;
profiles.remove(profile);
}
}
private class ProfileUpdater extends AsyncTask<Void, Void, Boolean> {
private final Profile profile, newProfile;
private final boolean wasConnected;
private ProfileUpdater(Profile profile, Profile newProfile, boolean wasConnected) {
super();
this.profile = profile;
this.newProfile = newProfile;
this.wasConnected = wasConnected;
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.i(TAG, "Updating profile " + profile.getName());
if (!newProfile.getName().equals(profile.getName()))
throw new IllegalStateException("Profile name mismatch: " + profile.getName());
try {
final String configFile = profile.getName() + ".conf";
final FileOutputStream stream = openFileOutput(configFile, MODE_PRIVATE);
stream.write(newProfile.toString().getBytes(StandardCharsets.UTF_8));
stream.close();
return true;
} catch (IOException e) {
Log.e(TAG, "Could not update profile " + profile.getName(), e);
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (!result)
return;
// FIXME: This is also why the list of profiles should be a map.
final int index = profiles.indexOf(profile);
profiles.set(index, newProfile);
if (wasConnected)
new ProfileConnecter(newProfile).execute();
}
}
private class ProfileServiceBinder extends Binder implements ProfileServiceInterface { private class ProfileServiceBinder extends Binder implements ProfileServiceInterface {
@Override @Override
public void connectProfile(Profile profile) { public void connectProfile(Profile profile) {
if (!profiles.contains(profile))
return;
if (profile.getIsConnected())
return;
new ProfileConnecter(profile).execute();
} }
@Override @Override
@ -97,6 +250,11 @@ public class ProfileService extends Service {
@Override @Override
public void disconnectProfile(Profile profile) { public void disconnectProfile(Profile profile) {
if (!profiles.contains(profile))
return;
if (!profile.getIsConnected())
return;
new ProfileDisconnecter(profile).execute();
} }
@Override @Override
@ -106,10 +264,28 @@ public class ProfileService extends Service {
@Override @Override
public void removeProfile(Profile profile) { public void removeProfile(Profile profile) {
if (!profiles.contains(profile))
return;
if (profile.getIsConnected())
new ProfileDisconnecter(profile).execute();
new ProfileRemover(profile).execute();
} }
@Override @Override
public void saveProfile(Profile newProfile) { public void saveProfile(Profile newProfile) {
// FIXME: This is why the list of profiles should be a map.
Profile profile = null;
for (Profile p : profiles)
if (p.getName().equals(newProfile.getName()))
profile = p;
if (profile != null) {
final boolean wasConnected = profile.getIsConnected();
if (wasConnected)
new ProfileDisconnecter(profile).execute();
new ProfileUpdater(profile, newProfile, wasConnected).execute();
} else {
new ProfileAdder(newProfile).execute();
}
} }
} }
} }