From c65ac9fafe5a0978c5976ef9ae666e67b4efe796 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Jul 2017 01:48:57 -0500 Subject: ProfileService: Create it and move profile loading The long-running service is needed for keeping track of which profiles are enabled, for showing notifications, and for the tile service to use. Since it has to know which profiles exist anyway, moving the main ObservableList there avoids some code duplication. It ensures the list is only loaded once, so it cannot get out of sync. It also makes the ProfileList activity load faster, because it doesn't have to wait for file I/O; and it provides a canonical place for storing the Profile objects so they are accessible everywhere, instead of having to look them up by name. This does present some challenges with leaking activities, because all listeners must be removed from the profiles list (and its contents) when an activity is stopped. Signed-off-by: Jason A. Donenfeld --- app/src/main/AndroidManifest.xml | 4 ++ .../com/wireguard/android/ProfileListActivity.java | 75 ++++++++++------------ .../java/com/wireguard/android/ProfileService.java | 75 ++++++++++++++++++++++ .../wireguard/android/ProfileServiceInterface.java | 13 ++++ 4 files changed, 126 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/wireguard/android/ProfileService.java create mode 100644 app/src/main/java/com/wireguard/android/ProfileServiceInterface.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 82186447..02a8b7fa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,10 @@ + + diff --git a/app/src/main/java/com/wireguard/android/ProfileListActivity.java b/app/src/main/java/com/wireguard/android/ProfileListActivity.java index afa8a123..9094efaf 100644 --- a/app/src/main/java/com/wireguard/android/ProfileListActivity.java +++ b/app/src/main/java/com/wireguard/android/ProfileListActivity.java @@ -1,64 +1,57 @@ package com.wireguard.android; import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.databinding.DataBindingUtil; -import android.databinding.ObservableArrayList; -import android.databinding.ObservableList; -import android.os.AsyncTask; import android.os.Bundle; -import android.util.Log; +import android.os.IBinder; import com.wireguard.android.databinding.ProfileListActivityBinding; -import com.wireguard.config.Profile; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; public class ProfileListActivity extends Activity { - private final ObservableList profiles = new ObservableArrayList<>(); + private final ServiceConnection connection = new ProfileServiceConnection(); + private ProfileListActivityBinding binding; + private ProfileServiceInterface service; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final ProfileListActivityBinding binding = - DataBindingUtil.setContentView(this, R.layout.profile_list_activity); - binding.setProfiles(profiles); - new ProfileLoader().execute(getFilesDir().listFiles()); + binding = DataBindingUtil.setContentView(this, R.layout.profile_list_activity); + // Ensure the long-running service is started. This only needs to happen once. + Intent intent = new Intent(this, ProfileService.class); + startService(intent); + } + + @Override + public void onStart() { + super.onStart(); + Intent intent = new Intent(this, ProfileService.class); + bindService(intent, connection, Context.BIND_AUTO_CREATE); } - private class ProfileLoader extends AsyncTask> { - private static final String TAG = "WGProfileLoader"; + @Override + public void onStop() { + super.onStop(); + if (service != null) { + unbindService(connection); + service = null; + } + } + private class ProfileServiceConnection implements ServiceConnection { @Override - protected ArrayList doInBackground(File... files) { - final ArrayList loadedProfiles = new ArrayList<>(); - for (File file : files) { - final String fileName = file.getName(); - final int suffixStart = fileName.lastIndexOf(".conf"); - if (suffixStart <= 0) { - Log.w(TAG, "Ignoring stray file " + fileName); - continue; - } - final Profile profile = new Profile(fileName.substring(0, suffixStart)); - try { - final FileInputStream inputStream = openFileInput(fileName); - profile.fromStream(inputStream); - loadedProfiles.add(profile); - } catch (IOException e) { - Log.w(TAG, "Failed to load profile from " + fileName, e); - } - if (isCancelled()) - break; - } - return loadedProfiles; + public void onServiceConnected(ComponentName component, IBinder binder) { + service = (ProfileServiceInterface) binder; + binding.setProfiles(service.getProfiles()); } @Override - protected void onPostExecute(ArrayList loadedProfiles) { - // FIXME: This should replace an existing profile if the name matches. - profiles.addAll(loadedProfiles); + public void onServiceDisconnected(ComponentName component) { + // This function is only called when the service crashes or goes away unexpectedly. + service = null; } } } diff --git a/app/src/main/java/com/wireguard/android/ProfileService.java b/app/src/main/java/com/wireguard/android/ProfileService.java new file mode 100644 index 00000000..172f230c --- /dev/null +++ b/app/src/main/java/com/wireguard/android/ProfileService.java @@ -0,0 +1,75 @@ +package com.wireguard.android; + +import android.app.Service; +import android.content.Intent; +import android.databinding.ObservableArrayList; +import android.databinding.ObservableList; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import com.wireguard.config.Profile; + +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +/** + * Service that handles profile state coordination and all background processing for the app. + */ + +public class ProfileService extends Service { + private static final String TAG = "ProfileService"; + + private final IBinder binder = new ProfileServiceBinder(); + private final ObservableList profiles = new ObservableArrayList<>(); + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + @Override + public void onCreate() { + new ProfileLoader().execute(getFilesDir().listFiles()); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_STICKY; + } + + private class ProfileLoader extends AsyncTask> { + @Override + protected List doInBackground(File... files) { + final List loadedProfiles = new LinkedList<>(); + for (File file : files) { + final String fileName = file.getName(); + final String profileName = fileName.substring(0, fileName.length() - 5); + final Profile profile = new Profile(profileName); + try { + profile.parseFrom(openFileInput(fileName)); + loadedProfiles.add(profile); + } catch (IOException e) { + Log.w(TAG, "Failed to load profile from " + fileName, e); + } + if (isCancelled()) + break; + } + return loadedProfiles; + } + + @Override + protected void onPostExecute(List loadedProfiles) { + profiles.addAll(loadedProfiles); + } + } + + private class ProfileServiceBinder extends Binder implements ProfileServiceInterface { + public ObservableList getProfiles() { + return profiles; + } + } +} diff --git a/app/src/main/java/com/wireguard/android/ProfileServiceInterface.java b/app/src/main/java/com/wireguard/android/ProfileServiceInterface.java new file mode 100644 index 00000000..6eb310ad --- /dev/null +++ b/app/src/main/java/com/wireguard/android/ProfileServiceInterface.java @@ -0,0 +1,13 @@ +package com.wireguard.android; + +import android.databinding.ObservableList; + +import com.wireguard.config.Profile; + +/** + * Interface for the background connection service. + */ + +public interface ProfileServiceInterface { + ObservableList getProfiles(); +} -- cgit v1.2.3