Use AndroidExecutor for background tasks that make API calls.

This commit is contained in:
akwizgran
2016-04-29 12:18:40 +01:00
parent cb8bfeb2ce
commit c21854fbe4
10 changed files with 107 additions and 71 deletions

View File

@@ -41,7 +41,8 @@ public abstract class BriarActivity extends BaseActivity {
new BriarServiceConnection(); new BriarServiceConnection();
@Inject @Inject
DatabaseConfig databaseConfig; protected DatabaseConfig databaseConfig;
private boolean bound = false; private boolean bound = false;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@@ -94,6 +95,7 @@ public abstract class BriarActivity extends BaseActivity {
} }
protected void signOut(final boolean removeFromRecentApps) { protected void signOut(final boolean removeFromRecentApps) {
// Use a new thread to avoid deadlock with executor tasks
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {

View File

@@ -5,6 +5,7 @@ import android.support.v7.app.ActionBar;
import android.view.MenuItem; import android.view.MenuItem;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.settings.SettingsManager; import org.briarproject.api.settings.SettingsManager;
@@ -12,8 +13,12 @@ import javax.inject.Inject;
public class SettingsActivity extends BriarActivity { public class SettingsActivity extends BriarActivity {
@Inject protected SettingsManager settingsManager; @Inject
@Inject protected EventBus eventBus; protected AndroidExecutor androidExecutor;
@Inject
protected SettingsManager settingsManager;
@Inject
protected EventBus eventBus;
@Override @Override
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
@@ -33,6 +38,10 @@ public class SettingsActivity extends BriarActivity {
component.inject(this); component.inject(this);
} }
public AndroidExecutor getAndroidExecutor() {
return androidExecutor;
}
public SettingsManager getSettingsManager() { public SettingsManager getSettingsManager() {
return settingsManager; return settingsManager;
} }

View File

@@ -9,6 +9,7 @@ import android.os.StrictMode.VmPolicy;
import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceManager;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.db.DatabaseConfig; import org.briarproject.api.db.DatabaseConfig;
@@ -29,6 +30,8 @@ public class SplashScreenActivity extends BaseActivity {
@Inject @Inject
protected DatabaseConfig dbConfig; protected DatabaseConfig dbConfig;
@Inject
protected AndroidExecutor androidExecutor;
public SplashScreenActivity() { public SplashScreenActivity() {
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL); Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
@@ -87,12 +90,11 @@ public class SplashScreenActivity extends BaseActivity {
} }
private void setPreferencesDefaults() { private void setPreferencesDefaults() {
new Thread() { androidExecutor.execute(new Runnable() {
@Override
public void run() { public void run() {
PreferenceManager.setDefaultValues(SplashScreenActivity.this, PreferenceManager.setDefaultValues(SplashScreenActivity.this,
R.xml.panic_preferences, false); R.xml.panic_preferences, false);
} }
}.start(); });
} }
} }

View File

@@ -10,11 +10,13 @@ import java.util.concurrent.Future;
public interface AndroidExecutor { public interface AndroidExecutor {
/** /**
* Runs the given task on the main UI thread and returns a Future for * Runs the given task on a background thread with a message queue and
* getting the result. * returns a Future for getting the result.
*/ */
<V> Future<V> submit(Callable<V> c); <V> Future<V> submit(Callable<V> c);
/** Runs the given task on the main UI thread. */ /**
* Runs the given task on a background thread with a message queue.
*/
void execute(Runnable r); void execute(Runnable r);
} }

View File

@@ -18,7 +18,7 @@ import android.view.ViewGroup;
import org.acra.ACRA; import org.acra.ACRA;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.SettingsActivity; import org.briarproject.android.SettingsActivity;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.util.UserFeedback; import org.briarproject.android.util.UserFeedback;
import org.briarproject.android.widget.PreferenceDividerDecoration; import org.briarproject.android.widget.PreferenceDividerDecoration;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -55,6 +55,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
Logger.getLogger(SettingsFragment.class.getName()); Logger.getLogger(SettingsFragment.class.getName());
private SettingsActivity listener; private SettingsActivity listener;
private AndroidExecutor androidExecutor;
private ListPreference enableBluetooth; private ListPreference enableBluetooth;
private ListPreference torOverMobile; private ListPreference torOverMobile;
private CheckBoxPreference notifyPrivateMessages; private CheckBoxPreference notifyPrivateMessages;
@@ -74,6 +75,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
try { try {
listener = (SettingsActivity) context; listener = (SettingsActivity) context;
androidExecutor = listener.getAndroidExecutor();
settingsManager = listener.getSettingsManager(); settingsManager = listener.getSettingsManager();
eventBus = listener.getEventBus(); eventBus = listener.getEventBus();
} catch (ClassCastException e) { } catch (ClassCastException e) {
@@ -218,23 +220,19 @@ public class SettingsFragment extends PreferenceFragmentCompat
} }
private void triggerFeedback() { private void triggerFeedback() {
new Thread(new Runnable() { androidExecutor.execute(new Runnable() {
@Override
public void run() { public void run() {
ACRA.getErrorReporter() ACRA.getErrorReporter().handleException(new UserFeedback(),
.handleException(new UserFeedback(), false); false);
} }
}).start(); });
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object o) { public boolean onPreferenceChange(Preference preference, Object o) {
if (preference == enableBluetooth) { if (preference == enableBluetooth) {
bluetoothSetting = Boolean.valueOf((String) o); bluetoothSetting = Boolean.valueOf((String) o);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); enableOrDisableBluetooth(bluetoothSetting);
if (adapter != null) {
AndroidUtils.enableBluetooth(adapter, bluetoothSetting);
}
storeBluetoothSettings(); storeBluetoothSettings();
} else if (preference == torOverMobile) { } else if (preference == torOverMobile) {
torSetting = Boolean.valueOf((String) o); torSetting = Boolean.valueOf((String) o);
@@ -255,6 +253,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
return true; return true;
} }
private void enableOrDisableBluetooth(final boolean enable) {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
androidExecutor.execute(new Runnable() {
public void run() {
if (enable) adapter.enable();
else adapter.disable();
}
});
}
}
private void storeTorSettings() { private void storeTorSettings() {
listener.runOnDbThread(new Runnable() { listener.runOnDbThread(new Runnable() {
public void run() { public void run() {

View File

@@ -24,8 +24,8 @@ import com.google.zxing.Result;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.AndroidComponent; import org.briarproject.android.AndroidComponent;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.fragment.BaseEventFragment; import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.android.util.CameraView; import org.briarproject.android.util.CameraView;
import org.briarproject.android.util.QrCodeDecoder; import org.briarproject.android.util.QrCodeDecoder;
import org.briarproject.android.util.QrCodeUtils; import org.briarproject.android.util.QrCodeUtils;
@@ -70,6 +70,8 @@ public class ShowQrCodeFragment extends BaseEventFragment
protected PayloadEncoder payloadEncoder; protected PayloadEncoder payloadEncoder;
@Inject @Inject
protected PayloadParser payloadParser; protected PayloadParser payloadParser;
@Inject
protected AndroidExecutor androidExecutor;
private LinearLayout qrLayout; private LinearLayout qrLayout;
private CameraView cameraView; private CameraView cameraView;
@@ -81,7 +83,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
private boolean gotRemotePayload; private boolean gotRemotePayload;
private volatile KeyAgreementTask task; private volatile KeyAgreementTask task;
private volatile BluetoothAdapter adapter;
private volatile boolean waitingForBluetooth; private volatile boolean waitingForBluetooth;
public static ShowQrCodeFragment newInstance() { public static ShowQrCodeFragment newInstance() {
@@ -130,8 +131,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
Display display = getActivity().getWindowManager().getDefaultDisplay(); Display display = getActivity().getWindowManager().getDefaultDisplay();
boolean portrait = display.getWidth() < display.getHeight(); boolean portrait = display.getWidth() < display.getHeight();
qrLayout.setOrientation(portrait ? VERTICAL : HORIZONTAL); qrLayout.setOrientation(portrait ? VERTICAL : HORIZONTAL);
adapter = BluetoothAdapter.getDefaultAdapter();
} }
@Override @Override
@@ -145,11 +144,17 @@ public class ShowQrCodeFragment extends BaseEventFragment
getActivity().registerReceiver(receiver, filter); getActivity().registerReceiver(receiver, filter);
// Enable BT adapter if it is not already on. // Enable BT adapter if it is not already on.
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && !adapter.isEnabled()) { if (adapter != null && !adapter.isEnabled()) {
waitingForBluetooth = true; waitingForBluetooth = true;
AndroidUtils.enableBluetooth(adapter, true); androidExecutor.execute(new Runnable() {
} else public void run() {
adapter.enable();
}
});
} else {
startListening(); startListening();
}
} }
@Override @Override

View File

@@ -9,9 +9,9 @@ import android.support.v7.preference.PreferenceManager;
import org.briarproject.android.AndroidComponent; import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity; import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.db.DatabaseConfig; import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.util.FileUtils;
import org.iilab.IilabEngineeringRSA2048Pin; import org.iilab.IilabEngineeringRSA2048Pin;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -23,6 +23,7 @@ import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder; import info.guardianproject.panic.PanicResponder;
import info.guardianproject.trustedintents.TrustedIntents; import info.guardianproject.trustedintents.TrustedIntents;
import static android.content.Intent.ACTION_DELETE;
import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_LOCK; import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_LOCK;
import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_PURGE; import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_PURGE;
import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_UNINSTALL; import static org.briarproject.android.panic.PanicPreferencesFragment.KEY_UNINSTALL;
@@ -31,7 +32,11 @@ public class PanicResponderActivity extends BriarActivity {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(PanicResponderActivity.class.getName()); Logger.getLogger(PanicResponderActivity.class.getName());
@Inject protected DatabaseConfig databaseConfig;
@Inject
protected DatabaseConfig databaseConfig;
@Inject
protected AndroidExecutor androidExecutor;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@@ -62,16 +67,14 @@ public class PanicResponderActivity extends BriarActivity {
deleteAllData(); deleteAllData();
LOG.info("Uninstalling..."); LOG.info("Uninstalling...");
Intent uninstall = new Intent(Intent.ACTION_DELETE); Intent uninstall = new Intent(ACTION_DELETE);
uninstall.setData( uninstall.setData(
Uri.parse("package:" + getPackageName())); Uri.parse("package:" + getPackageName()));
startActivity(uninstall); startActivity(uninstall);
} } else if (sharedPref.getBoolean(KEY_PURGE, false)) {
else if (sharedPref.getBoolean(KEY_PURGE, false)) {
LOG.info("Purging all data..."); LOG.info("Purging all data...");
deleteAllData(); deleteAllData();
} } else if (sharedPref.getBoolean(KEY_LOCK, true)) {
else if (sharedPref.getBoolean(KEY_LOCK, true)) {
LOG.info("Signing out..."); LOG.info("Signing out...");
signOut(true); signOut(true);
} }
@@ -107,8 +110,7 @@ public class PanicResponderActivity extends BriarActivity {
} }
private void deleteAllData() { private void deleteAllData() {
new Thread() { androidExecutor.execute(new Runnable() {
@Override
public void run() { public void run() {
clearSharedPrefs(); clearSharedPrefs();
// TODO somehow delete/shred the database more thoroughly // TODO somehow delete/shred the database more thoroughly
@@ -120,7 +122,6 @@ public class PanicResponderActivity extends BriarActivity {
LOG.info("Signing out..."); LOG.info("Signing out...");
signOut(true); signOut(true);
} }
}.start(); });
} }
} }

View File

@@ -48,17 +48,6 @@ public class AndroidUtils {
til.setError(null); til.setError(null);
} }
public static void enableBluetooth(final BluetoothAdapter adapter,
final boolean enable) {
new Thread() {
@Override
public void run() {
if (enable) adapter.enable();
else adapter.disable();
}
}.start();
}
public static String getBluetoothAddress(Context ctx, public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake

View File

@@ -1,46 +1,59 @@
package org.briarproject.system; package org.briarproject.system;
import android.app.Application;
import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message;
import org.briarproject.android.api.AndroidExecutor; import org.briarproject.android.api.AndroidExecutor;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
class AndroidExecutorImpl implements AndroidExecutor { class AndroidExecutorImpl implements AndroidExecutor {
private final Handler handler; private final Runnable loop;
private final AtomicBoolean started = new AtomicBoolean(false);
private final CountDownLatch startLatch = new CountDownLatch(1);
AndroidExecutorImpl(Application app) { private volatile Handler handler = null;
Context ctx = app.getApplicationContext();
handler = new FutureTaskHandler(ctx.getMainLooper()); @Inject
AndroidExecutorImpl() {
loop = new Runnable() {
public void run() {
Looper.prepare();
handler = new Handler();
startLatch.countDown();
Looper.loop();
}
};
}
private void startIfNecessary() {
if (started.getAndSet(true)) return;
Thread t = new Thread(loop, "AndroidExecutor");
t.setDaemon(true);
t.start();
try {
startLatch.await();
} catch (InterruptedException e) {
throw new RejectedExecutionException(e);
}
} }
public <V> Future<V> submit(Callable<V> c) { public <V> Future<V> submit(Callable<V> c) {
Future<V> f = new FutureTask<V>(c); FutureTask<V> f = new FutureTask<>(c);
handler.sendMessage(Message.obtain(handler, 0, f)); execute(f);
return f; return f;
} }
public void execute(Runnable r) { public void execute(Runnable r) {
startIfNecessary();
handler.post(r); handler.post(r);
} }
private static class FutureTaskHandler extends Handler {
private FutureTaskHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message m) {
((FutureTask<?>) m.obj).run();
}
}
} }

View File

@@ -6,6 +6,8 @@ import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.api.system.LocationUtils; import org.briarproject.api.system.LocationUtils;
import org.briarproject.api.system.SeedProvider; import org.briarproject.api.system.SeedProvider;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -23,7 +25,8 @@ public class AndroidSystemModule {
} }
@Provides @Provides
public AndroidExecutor providePlatformExecutor(Application app) { @Singleton
return new AndroidExecutorImpl(app); public AndroidExecutor provideAndroidExecutor() {
return new AndroidExecutorImpl();
} }
} }