Merge branch 'use-android-executor' into 'master'

Use Android executor for background API calls

Some Android API calls need to be made from a thread with a message queue, but to keep the UI responsive they shouldn't be made from the UI thread.

This patch gives AndroidExecutor a captive thread with a message queue to execute tasks, and converts various background tasks from creating their own threads to using AndroidExecutor and IoExecutor.

This allows us to upgrade the support library to 23.2.1. Fixes #332.

See merge request !161
This commit is contained in:
akwizgran
2016-05-03 14:34:49 +00:00
13 changed files with 133 additions and 87 deletions

View File

@@ -37,7 +37,7 @@ dependencies {
compile project(':briar-core')
testCompile 'junit:junit:4.12'
testCompile 'net.jodah:concurrentunit:0.4.2'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:appcompat-v7:23.2.1'
testApt 'com.google.dagger:dagger-compiler:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
testCompile project(':briar-tests')
@@ -45,8 +45,10 @@ dependencies {
dependencyVerification {
verify = [
'com.android.support:appcompat-v7:0a8762214382b7e8d4b989b4ac10b5c846b957d767ccb7bccbc6be5afa885a82',
'com.android.support:support-v4:5c7dceb6c824089fe80f502e5206264048ef8bffa4e8ddeab180b81723e79b7f',
'com.android.support:support-annotations:f347a35b9748a4103b39a6714a77e2100f488d623fd6268e259c177b200e9d82',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
]
}

View File

@@ -11,7 +11,7 @@ repositories {
}
dependencies {
def supportVersion = '23.1.1'
def supportVersion = '23.2.1'
compile project(':briar-api')
compile project(':briar-core')
compile fileTree(dir: 'libs', include: '*.jar')
@@ -53,13 +53,15 @@ dependencyVerification {
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:c76d936395b50705a3f98c9220c22d2599aeb9e609f559f6048975cfc1f686b8',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:5c7dceb6c824089fe80f502e5206264048ef8bffa4e8ddeab180b81723e79b7f',
'com.android.support:appcompat-v7:0a8762214382b7e8d4b989b4ac10b5c846b957d767ccb7bccbc6be5afa885a82',
'com.android.support:preference-v7:4b6dabaa4400cbed885c7edc885aa6372468f48d628cc0d4a04b9ccd128ed324',
'com.android.support:preference-v14:a69906c2b29b315ac3c1fdf01537a7557660a65b8ea1cf891baa8665e1197459',
'com.android.support:design:41a9cd75ca78f25df5f573db7cedf8bb66beae00c330943923ba9f3e2051736d',
'com.android.support:support-annotations:f347a35b9748a4103b39a6714a77e2100f488d623fd6268e259c177b200e9d82',
'com.android.support:recyclerview-v7:7606373da0931a1e62588335465a0e390cd676c98117edab29220317495faefd',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
]
}

View File

@@ -41,7 +41,8 @@ public abstract class BriarActivity extends BaseActivity {
new BriarServiceConnection();
@Inject
DatabaseConfig databaseConfig;
protected DatabaseConfig databaseConfig;
private boolean bound = false;
// 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) {
// Use a new thread to avoid deadlock with executor tasks
new Thread() {
@Override
public void run() {

View File

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

View File

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

View File

@@ -10,11 +10,13 @@ import java.util.concurrent.Future;
public interface AndroidExecutor {
/**
* Runs the given task on the main UI thread and returns a Future for
* getting the result.
* Runs the given task on a background thread with a message queue and
* returns a Future for getting the result.
*/
<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);
}

View File

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

View File

@@ -24,8 +24,8 @@ import com.google.zxing.Result;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.android.util.CameraView;
import org.briarproject.android.util.QrCodeDecoder;
import org.briarproject.android.util.QrCodeUtils;
@@ -40,8 +40,10 @@ import org.briarproject.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.api.keyagreement.Payload;
import org.briarproject.api.keyagreement.PayloadEncoder;
import org.briarproject.api.keyagreement.PayloadParser;
import org.briarproject.api.lifecycle.IoExecutor;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -70,6 +72,11 @@ public class ShowQrCodeFragment extends BaseEventFragment
protected PayloadEncoder payloadEncoder;
@Inject
protected PayloadParser payloadParser;
@Inject
protected AndroidExecutor androidExecutor;
@Inject
@IoExecutor
protected Executor ioExecutor;
private LinearLayout qrLayout;
private CameraView cameraView;
@@ -81,7 +88,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
private boolean gotRemotePayload;
private volatile KeyAgreementTask task;
private volatile BluetoothAdapter adapter;
private volatile boolean waitingForBluetooth;
public static ShowQrCodeFragment newInstance() {
@@ -130,8 +136,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
Display display = getActivity().getWindowManager().getDefaultDisplay();
boolean portrait = display.getWidth() < display.getHeight();
qrLayout.setOrientation(portrait ? VERTICAL : HORIZONTAL);
adapter = BluetoothAdapter.getDefaultAdapter();
}
@Override
@@ -145,11 +149,17 @@ public class ShowQrCodeFragment extends BaseEventFragment
getActivity().registerReceiver(receiver, filter);
// Enable BT adapter if it is not already on.
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && !adapter.isEnabled()) {
waitingForBluetooth = true;
AndroidUtils.enableBluetooth(adapter, true);
} else
androidExecutor.execute(new Runnable() {
public void run() {
adapter.enable();
}
});
} else {
startListening();
}
}
@Override
@@ -174,21 +184,21 @@ public class ShowQrCodeFragment extends BaseEventFragment
private void startListening() {
task = keyAgreementTaskFactory.getTask();
gotRemotePayload = false;
new Thread(new Runnable() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
task.listen();
}
}).start();
});
}
private void stopListening() {
new Thread(new Runnable() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
task.stopListening();
}
}).start();
});
}
private void openCamera() {

View File

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

View File

@@ -48,17 +48,6 @@ public class AndroidUtils {
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,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake

View File

@@ -1,46 +1,59 @@
package org.briarproject.system;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import org.briarproject.android.api.AndroidExecutor;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
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) {
Context ctx = app.getApplicationContext();
handler = new FutureTaskHandler(ctx.getMainLooper());
private volatile Handler handler = null;
@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) {
Future<V> f = new FutureTask<V>(c);
handler.sendMessage(Message.obtain(handler, 0, f));
FutureTask<V> f = new FutureTask<>(c);
execute(f);
return f;
}
public void execute(Runnable r) {
startIfNecessary();
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.SeedProvider;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@@ -23,7 +25,8 @@ public class AndroidSystemModule {
}
@Provides
public AndroidExecutor providePlatformExecutor(Application app) {
return new AndroidExecutorImpl(app);
@Singleton
public AndroidExecutor provideAndroidExecutor() {
return new AndroidExecutorImpl();
}
}

View File

@@ -27,6 +27,7 @@ dependencyVerification {
'org.jmock:jmock:75d4bdaf636879f0215830c5e6ab99407069a625eaffde5d57b32d887b75dc14',
'org.jmock:jmock-junit4:81e3fff46ed56738a6f3f5147525d1d85cda591ce5df007cc193e735cee31113',
'org.jmock:jmock-legacy:19c76059eb254775ba884fc8039bc5c7d1700dc68cc55ad3be5b405a2a8a1819',
'org.jmock:jmock-testjar:c3642147a5980771dde19d5f1d782d4790a7f9b1521bf9c8cd2b4c23f6384730',
'org.hamcrest:hamcrest-library:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hamcrest:hamcrest-core:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'cglib:cglib-nodep:3a9ab1f5de15b49dedc7e96d35363c4c43a2b2cd42275f5d4731846042f3fcf2',