Merge branch '210-support-panic-kit-purge' into 'master'

Support for Destructive Panic Actions

This MR is based on and requires !52.

More details are available in the commit message.

All known issues have now been fixed by new upstream releases.

See merge request !54
This commit is contained in:
akwizgran
2016-01-13 15:37:38 +00:00
5 changed files with 330 additions and 7 deletions

View File

@@ -194,6 +194,11 @@
<activity
android:name=".android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting" >
<intent-filter>
<action android:name="info.guardianproject.panic.action.CONNECT" />
<action android:name="info.guardianproject.panic.action.DISCONNECT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".android.panic.PanicResponderActivity"

View File

@@ -17,6 +17,7 @@ dependencies {
compile "com.android.support:preference-v14:23.1.1"
compile "com.android.support:design:23.1.1"
compile "info.guardianproject.panic:panic:0.5"
compile "info.guardianproject.trustedintents:trustedintents:0.2"
}
dependencyVerification {
@@ -29,6 +30,7 @@ dependencyVerification {
'com.android.support:support-annotations:f347a35b9748a4103b39a6714a77e2100f488d623fd6268e259c177b200e9d82',
'com.android.support:recyclerview-v7:7606373da0931a1e62588335465a0e390cd676c98117edab29220317495faefd',
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
]
}

View File

@@ -8,4 +8,22 @@
android:summary="@string/lock_setting_summary"
android:defaultValue="true"/>
<PreferenceCategory
android:title="Destructive Actions">
<ListPreference
android:key="pref_key_panic_app"
android:title="@string/panic_app_setting_title"
android:summary="@string/panic_app_setting_summary"
android:icon="@android:drawable/ic_menu_close_clear_cancel"/>
<CheckBoxPreference
android:key="pref_key_purge"
android:title="@string/purge_setting_title"
android:summary="@string/purge_setting_summary"
android:enabled="false"
android:defaultValue="false"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -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<CharSequence> entries = new ArrayList<CharSequence>();
ArrayList<CharSequence> entryValues = new ArrayList<CharSequence>();
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;
}
}

View File

@@ -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);
}
});
}
}