diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index ac262a3c2..e5e660ddd 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -194,6 +194,11 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/panic/PanicPreferencesFragment.java b/briar-android/src/org/briarproject/android/panic/PanicPreferencesFragment.java
index 7fd096213..3ee8915c2 100644
--- a/briar-android/src/org/briarproject/android/panic/PanicPreferencesFragment.java
+++ b/briar-android/src/org/briarproject/android/panic/PanicPreferencesFragment.java
@@ -1,14 +1,240 @@
package org.briarproject.android.panic;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
+import android.text.TextUtils;
import org.briarproject.R;
-public class PanicPreferencesFragment extends PreferenceFragmentCompat {
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import info.guardianproject.panic.Panic;
+import info.guardianproject.panic.PanicResponder;
+
+public class PanicPreferencesFragment extends PreferenceFragmentCompat
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private static final Logger LOG =
+ Logger.getLogger(PanicPreferencesFragment.class.getName());
+
+ private PackageManager pm;
+ private CheckBoxPreference lockPref;
+ private ListPreference panicAppPref;
+ private CheckBoxPreference purgePref;
@Override
public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.panic_preferences);
+
+ pm = getActivity().getPackageManager();
+
+ lockPref = (CheckBoxPreference) findPreference("pref_key_lock");
+ panicAppPref = (ListPreference) findPreference("pref_key_panic_app");
+ purgePref = (CheckBoxPreference) findPreference("pref_key_purge");
+
+ // check for connect/disconnect intents from panic trigger apps
+ if (PanicResponder.checkForDisconnectIntent(getActivity())) {
+ LOG.info("Received DISCONNECT intent from Panic Trigger App.");
+ // the necessary action should have been performed by the check
+ getActivity().finish();
+ } else {
+ // check if we got a connect intent from a not yet connected app
+ String packageName =
+ PanicResponder.getConnectIntentSender(getActivity());
+ if (!TextUtils.isEmpty((packageName)) &&
+ !TextUtils.equals(packageName,
+ PanicResponder
+ .getTriggerPackageName(getActivity()))) {
+
+ // A new panic trigger app asks us to connect
+ LOG.info("Received CONNECT intent from new Panic Trigger App.");
+
+ // Show dialog allowing the user to opt-in
+ showOptInDialog();
+ }
+ }
+
+ ArrayList entries = new ArrayList();
+ ArrayList entryValues = new ArrayList();
+ entries.add(0, getString(R.string.panic_app_setting_none));
+ entryValues.add(0, Panic.PACKAGE_NAME_NONE);
+
+ for (ResolveInfo resolveInfo : PanicResponder.resolveTriggerApps(pm)) {
+ if (resolveInfo.activityInfo == null)
+ continue;
+ entries.add(resolveInfo.activityInfo.loadLabel(pm));
+ entryValues.add(resolveInfo.activityInfo.packageName);
+ }
+
+ panicAppPref.setEntries(
+ entries.toArray(new CharSequence[entries.size()]));
+ panicAppPref.setEntryValues(
+ entryValues.toArray(new CharSequence[entryValues.size()]));
+ panicAppPref.setDefaultValue(Panic.PACKAGE_NAME_NONE);
+
+ panicAppPref.setOnPreferenceChangeListener(
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference,
+ Object newValue) {
+ String packageName = (String) newValue;
+ PanicResponder.setTriggerPackageName(getActivity(),
+ packageName);
+ showPanicApp(packageName);
+
+ if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
+ purgePref.setChecked(false);
+ purgePref.setEnabled(false);
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ } else {
+ purgePref.setEnabled(true);
+ }
+
+ return true;
+ }
+ });
+
+ if (entries.size() <= 1) {
+ panicAppPref.setOnPreferenceClickListener(
+ new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(
+ Preference preference) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(
+ "market://details?id=info.guardianproject.ripple"));
+ getActivity().startActivity(intent);
+ return true;
+ }
+ });
+ }
}
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getPreferenceScreen().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(this);
+ showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getPreferenceScreen().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
+ // enable locking if purging gets enabled
+ if (key.equals("pref_key_purge")
+ && sharedPreferences.getBoolean("pref_key_purge", false)) {
+ lockPref.setChecked(true);
+ }
+ // disable purging if locking gets disabled
+ if (key.equals("pref_key_lock")
+ && !sharedPreferences.getBoolean("pref_key_lock", true)
+ && sharedPreferences.getBoolean("pref_key_purge", false)) {
+ purgePref.setChecked(false);
+ }
+ }
+
+ private void showPanicApp(String triggerPackageName) {
+ if (TextUtils.isEmpty(triggerPackageName)
+ || triggerPackageName.equals(Panic.PACKAGE_NAME_NONE)) {
+ // no panic app set
+ panicAppPref.setValue(Panic.PACKAGE_NAME_NONE);
+ panicAppPref
+ .setSummary(getString(R.string.panic_app_setting_summary));
+ panicAppPref.setIcon(
+ android.R.drawable.ic_menu_close_clear_cancel);
+ purgePref.setEnabled(false);
+ } else {
+ // display connected panic app
+ try {
+ panicAppPref.setValue(triggerPackageName);
+ panicAppPref.setSummary(pm.getApplicationLabel(
+ pm.getApplicationInfo(triggerPackageName, 0)));
+ panicAppPref.setIcon(
+ pm.getApplicationIcon(triggerPackageName));
+ purgePref.setEnabled(true);
+ } catch (PackageManager.NameNotFoundException e) {
+ // revert back to no app, just to be safe
+ PanicResponder.setTriggerPackageName(getActivity(),
+ Panic.PACKAGE_NAME_NONE);
+ showPanicApp(Panic.PACKAGE_NAME_NONE);
+ }
+ }
+ }
+
+ private void showOptInDialog() {
+ DialogInterface.OnClickListener okListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ PanicResponder.setTriggerPackageName(getActivity());
+ showPanicApp(PanicResponder
+ .getTriggerPackageName(getActivity()));
+ getActivity().setResult(Activity.RESULT_OK);
+ }
+ };
+ DialogInterface.OnClickListener cancelListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ }
+ };
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(getContext());
+ builder.setTitle(
+ getString(R.string.dialog_title_connect_panic_app));
+
+ CharSequence app = getString(R.string.unknown_app);
+ String packageName = getCallingPackageName();
+ if (packageName != null) {
+ try {
+ app = pm.getApplicationLabel(
+ pm.getApplicationInfo(packageName, 0));
+ } catch (PackageManager.NameNotFoundException e) {
+ LOG.warning(e.toString());
+ }
+ }
+
+ String text = String.format(
+ getString(R.string.dialog_message_connect_panic_app), app);
+ builder.setMessage(text);
+ builder.setPositiveButton(android.R.string.ok, okListener);
+ builder.setNegativeButton(android.R.string.cancel, cancelListener);
+ builder.show();
+ }
+
+ private String getCallingPackageName() {
+ ComponentName componentName = getActivity().getCallingActivity();
+ String packageName = null;
+ if (componentName != null) {
+ packageName = componentName.getPackageName();
+ }
+ return packageName;
+ }
+
}
diff --git a/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java b/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java
index df758e295..1643767af 100644
--- a/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java
+++ b/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java
@@ -7,25 +7,78 @@ import android.os.Bundle;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.android.BriarActivity;
+import org.briarproject.api.db.DatabaseConfig;
+import org.briarproject.util.FileUtils;
+import org.iilab.IilabEngineeringRSA2048Pin;
import java.util.logging.Logger;
+import javax.inject.Inject;
+
+import info.guardianproject.GuardianProjectRSA4096;
+import info.guardianproject.panic.Panic;
+import info.guardianproject.panic.PanicResponder;
+import info.guardianproject.trustedintents.TrustedIntents;
+
public class PanicResponderActivity extends BriarActivity {
private static final Logger LOG =
Logger.getLogger(PanicResponderActivity.class.getName());
+ @Inject private DatabaseConfig databaseConfig;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- SharedPreferences sharedPref = PreferenceManager
- .getDefaultSharedPreferences(this);
+ TrustedIntents trustedIntents = TrustedIntents.get(this);
+ // Guardian Project Ripple
+ trustedIntents.addTrustedSigner(GuardianProjectRSA4096.class);
+ // Amnesty International's Panic Button, made by iilab.org
+ trustedIntents.addTrustedSigner(IilabEngineeringRSA2048Pin.class);
- Intent intent = getIntent();
- if (intent != null && sharedPref.getBoolean("pref_key_lock", true)) {
- LOG.info("Signing out...");
- signOut(true);
+ Intent intent = trustedIntents.getIntentFromTrustedSender(this);
+ if (intent != null) {
+ // received intent from trusted app
+ if (Panic.isTriggerIntent(intent)) {
+ SharedPreferences sharedPref = PreferenceManager
+ .getDefaultSharedPreferences(this);
+
+ LOG.info("Received Panic Trigger...");
+
+ if (PanicResponder.receivedTriggerFromConnectedApp(this)) {
+ LOG.info("Panic Trigger came from connected app.");
+ LOG.info("Performing destructive responses...");
+
+ // Performing destructive panic responses
+ if (sharedPref.getBoolean("pref_key_purge", false)) {
+ LOG.info("Purging all data...");
+ deleteAllData();
+ }
+ // still sign out if enabled
+ else if (sharedPref.getBoolean("pref_key_lock", true)) {
+ LOG.info("Signing out...");
+ signOut(true);
+ }
+
+ // TODO add other panic behavior such as:
+ // * send a pre-defined message to certain contacts (#212)
+ // * uninstall the app (#211)
+
+ }
+ // Performing non-destructive default panic response
+ else if (sharedPref.getBoolean("pref_key_lock", true)) {
+ LOG.info("Signing out...");
+ signOut(true);
+ }
+ }
+ }
+ // received intent from non-trusted app
+ else {
+ intent = getIntent();
+ if (intent != null && Panic.isTriggerIntent(intent)) {
+ LOG.info("Signing out...");
+ signOut(true);
+ }
}
if (Build.VERSION.SDK_INT >= 21) {
@@ -34,4 +87,23 @@ public class PanicResponderActivity extends BriarActivity {
finish();
}
}
+
+ private void deleteAllData() {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ // TODO somehow delete/shred the database more thoroughly
+ FileUtils
+ .deleteFileOrDir(
+ databaseConfig.getDatabaseDirectory());
+ clearSharedPrefs();
+ PanicResponder.deleteAllAppData(PanicResponderActivity.this);
+
+ // nothing left to do after everything is deleted,
+ // so still sign out
+ LOG.info("Signing out...");
+ signOut(true);
+ }
+ });
+ }
}
\ No newline at end of file