From c2dcf7ae74e26f95f504d8df0b900a81a4e96a2f Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sun, 10 Feb 2013 17:30:51 -0800 Subject: [PATCH] Refactor MasterSecret initialization, access, and timeout paths. 1) Consolidate all of the KeyCachingService interaction into a single mixin. Activities extend delegates which call through to the mixin. 2) Switch Activity increment/decrement triggers from onStop to onPause in order to account for some screen locks that don't stop activities. --- .../ApplicationPreferencesActivity.java | 38 ++-- .../securesms/AutoInitiateActivity.java | 7 +- .../securesms/ContactSelectionActivity.java | 35 +--- .../securesms/ConversationActivity.java | 37 +--- .../securesms/ConversationListActivity.java | 185 +++++------------- .../securesms/KeyScanningActivity.java | 11 +- .../securesms/PassphraseRequiredActivity.java | 8 + .../securesms/PassphraseRequiredMixin.java | 128 ++++++++++++ .../PassphraseRequiredSherlockActivity.java | 45 +++++ ...hraseRequiredSherlockFragmentActivity.java | 44 +++++ ...assphraseRequiredSherlockListActivity.java | 45 +++++ ...aseRequiredSherlockPreferenceActivity.java | 48 +++++ .../securesms/ReceiveKeyActivity.java | 7 +- .../securesms/SaveIdentityActivity.java | 8 +- .../securesms/service/KeyCachingService.java | 16 +- 15 files changed, 412 insertions(+), 250 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredActivity.java create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredMixin.java create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredSherlockActivity.java create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredSherlockFragmentActivity.java create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredSherlockListActivity.java create mode 100644 src/org/thoughtcrime/securesms/PassphraseRequiredSherlockPreferenceActivity.java diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java index e3da323780..ec0e1de785 100644 --- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -30,9 +30,6 @@ import android.provider.ContactsContract; import android.util.Log; import android.widget.Toast; -import com.actionbarsherlock.app.SherlockPreferenceActivity; -import com.actionbarsherlock.view.MenuItem; - import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactIdentityManager; import org.thoughtcrime.securesms.crypto.IdentityKey; @@ -43,6 +40,8 @@ import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.MemoryCleaner; import org.thoughtcrime.securesms.util.Trimmer; +import com.actionbarsherlock.view.MenuItem; + import java.util.List; /** @@ -52,7 +51,7 @@ import java.util.List; * */ -public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { +public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPreferenceActivity { private static final int PICK_IDENTITY_CONTACT = 1; private static final int IMPORT_IDENTITY_ID = 2; @@ -119,19 +118,9 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_START_EVENT); - startService(intent); - } - - @Override - public void onStop() { - super.onStop(); - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT); - startService(intent); + public void onDestroy() { + MemoryCleaner.clean((MasterSecret)getIntent().getParcelableExtra("master_secret")); + super.onDestroy(); } @Override @@ -146,12 +135,6 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } } - @Override - public void onDestroy() { - MemoryCleaner.clean((MasterSecret)getIntent().getParcelableExtra("master_secret")); - super.onDestroy(); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -237,6 +220,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class IdentityPreferenceClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType(ContactsContract.Contacts.CONTENT_TYPE); @@ -246,6 +230,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class ViewMyIdentityClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { Intent viewIdentityIntent = new Intent(ApplicationPreferencesActivity.this, ViewIdentityActivity.class); viewIdentityIntent.putExtra("identity_key", IdentityKeyUtil.getIdentityKey(ApplicationPreferencesActivity.this)); @@ -256,6 +241,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class ExportMyIdentityClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { if (!IdentityKeyUtil.hasIdentityKey(ApplicationPreferencesActivity.this)) { Toast.makeText(ApplicationPreferencesActivity.this, @@ -287,6 +273,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class ImportContactIdentityClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { MasterSecret masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret"); @@ -305,6 +292,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class ManageIdentitiesClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { MasterSecret masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret"); @@ -323,6 +311,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener { + @Override public boolean onPreferenceClick(Preference preference) { SharedPreferences settings = getSharedPreferences(KeyCachingService.PREFERENCES_NAME, 0); @@ -382,7 +371,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { Log.w("ApplicationPreferencesActivity", nfe); return false; } - + if (Integer.parseInt((String)newValue) < 1) { return false; } @@ -393,5 +382,4 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity { } } - } diff --git a/src/org/thoughtcrime/securesms/AutoInitiateActivity.java b/src/org/thoughtcrime/securesms/AutoInitiateActivity.java index 95d3ad8ce5..9820af5901 100644 --- a/src/org/thoughtcrime/securesms/AutoInitiateActivity.java +++ b/src/org/thoughtcrime/securesms/AutoInitiateActivity.java @@ -24,8 +24,6 @@ import android.util.Log; import android.view.View; import android.widget.Button; -import com.actionbarsherlock.app.SherlockActivity; - import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.LocalKeyRecord; @@ -42,7 +40,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner; * @author Moxie Marlinspike * */ -public class AutoInitiateActivity extends SherlockActivity { +public class AutoInitiateActivity extends PassphraseRequiredSherlockActivity { private long threadId; private Recipient recipient; @@ -77,12 +75,14 @@ public class AutoInitiateActivity extends SherlockActivity { } private class OkListener implements View.OnClickListener { + @Override public void onClick(View v) { initiateKeyExchange(); } } private class CancelListener implements View.OnClickListener { + @Override public void onClick(View v) { Log.w("AutoInitiateActivity", "Exempting threadID: " + threadId); exemptThread(AutoInitiateActivity.this, threadId); @@ -114,5 +114,4 @@ public class AutoInitiateActivity extends SherlockActivity { (new RemoteKeyRecord(context,recipient).getCurrentRemoteKey() == null) && (new LocalKeyRecord(context, masterSecret, recipient).getCurrentKeyPair() == null); } - } diff --git a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java index b486a5fd8d..1388ae5d30 100644 --- a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java +++ b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java @@ -22,17 +22,15 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import org.thoughtcrime.securesms.recipients.Recipients; + import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.Tab; import com.actionbarsherlock.app.ActionBar.TabListener; -import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -import org.thoughtcrime.securesms.recipients.Recipients; -import org.thoughtcrime.securesms.service.KeyCachingService; - /** * Activity container for selecting a list of contacts. Provides a tab frame for * contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity @@ -41,7 +39,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService; * @author Moxie Marlinspike * */ -public class ContactSelectionActivity extends SherlockFragmentActivity { +public class ContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity { private ContactSelectionListFragment contactsFragment; private ContactSelectionGroupsFragment groupsFragment; @@ -64,18 +62,6 @@ public class ContactSelectionActivity extends SherlockFragmentActivity { setupRecentTab(); } - @Override - protected void onStart() { - super.onStart(); - registerPassphraseActivityStarted(); - } - - @Override - protected void onStop() { - super.onStop(); - registerPassphraseActivityStopped(); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = this.getSupportMenuInflater(); @@ -160,19 +146,4 @@ public class ContactSelectionActivity extends SherlockFragmentActivity { recentTab.setIcon(R.drawable.ic_tab_recent); this.getSupportActionBar().addTab(recentTab); } - - private void registerPassphraseActivityStarted() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_START_EVENT); - startService(intent); - } - - private void registerPassphraseActivityStopped() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT); - startService(intent); - } - - - } diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 9b8c99150f..eb5404e4a3 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -75,7 +75,6 @@ import org.thoughtcrime.securesms.util.Util; import ws.com.google.android.mms.MmsException; -import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; @@ -91,7 +90,7 @@ import java.util.List; * @author Moxie Marlinspike * */ -public class ConversationActivity extends SherlockFragmentActivity +public class ConversationActivity extends PassphraseRequiredSherlockFragmentActivity implements ConversationFragment.ConversationFragmentListener { @@ -116,7 +115,6 @@ public class ConversationActivity extends SherlockFragmentActivity private AttachmentTypeSelectorAdapter attachmentAdapter; private AttachmentManager attachmentManager; - private BroadcastReceiver killActivityReceiver; private BroadcastReceiver securityUpdateReceiver; private Recipients recipients; @@ -161,20 +159,10 @@ public class ConversationActivity extends SherlockFragmentActivity if (!isExistingConversation()) initializeRecipientsInput(); - - registerPassphraseActivityStarted(); - } - - @Override - protected void onStop() { - super.onStop(); - - registerPassphraseActivityStopped(); } @Override protected void onDestroy() { - unregisterReceiver(killActivityReceiver); unregisterReceiver(securityUpdateReceiver); saveDraft(); MemoryCleaner.clean(masterSecret); @@ -518,13 +506,6 @@ public class ConversationActivity extends SherlockFragmentActivity private void initializeReceivers() { - killActivityReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - finish(); - } - }; - securityUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -539,10 +520,6 @@ public class ConversationActivity extends SherlockFragmentActivity } }; - registerReceiver(killActivityReceiver, - new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT), - KeyCachingService.KEY_PERMISSION, null); - registerReceiver(securityUpdateReceiver, new IntentFilter(KeyExchangeProcessor.SECURITY_UPDATE_EVENT), KeyCachingService.KEY_PERMISSION, null); @@ -767,18 +744,6 @@ public class ConversationActivity extends SherlockFragmentActivity } } - private void registerPassphraseActivityStarted() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_START_EVENT); - startService(intent); - } - - private void registerPassphraseActivityStopped() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT); - startService(intent); - } - // Listeners private class AddRecipientButtonListener implements OnClickListener { diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index ed7fca6605..3f98b8d979 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -1,18 +1,12 @@ package org.thoughtcrime.securesms; -import android.app.NotificationManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.SharedPreferences; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.Parcelable; import android.provider.ContactsContract; import android.util.Log; @@ -31,20 +25,17 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.SendReceiveService; import org.thoughtcrime.securesms.util.MemoryCleaner; -import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -public class ConversationListActivity extends SherlockFragmentActivity +public class ConversationListActivity extends PassphraseRequiredSherlockFragmentActivity implements ConversationListFragment.ConversationSelectedListener { private ConversationListFragment fragment; private MasterSecret masterSecret; - private BroadcastReceiver killActivityReceiver; - private BroadcastReceiver newKeyReceiver; private ApplicationMigrationManager migrationManager; private boolean havePromptedForPassphrase = false; @@ -56,7 +47,6 @@ public class ConversationListActivity extends SherlockFragmentActivity setContentView(R.layout.conversation_list_activity); getSupportActionBar().setTitle("TextSecure"); - initializeKillReceiver(); initializeSenderReceiverService(); initializeResources(); initializeContactUpdatesReceiver(); @@ -65,51 +55,71 @@ public class ConversationListActivity extends SherlockFragmentActivity @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - createConversationIfNecessary(intent); - } - - @Override - public void onPause() { - super.onPause(); - - if (newKeyReceiver != null) { - unregisterReceiver(newKeyReceiver); - newKeyReceiver = null; - } - - isVisible = false; + this.setIntent(intent); } @Override public void onResume() { super.onResume(); - clearNotifications(); - initializeKeyCachingServiceRegistration(); isVisible = true; } @Override - public void onStart() { - super.onStart(); - registerPassphraseActivityStarted(); + public void onPause() { + super.onPause(); + + isVisible = false; } @Override public void onStop() { super.onStop(); havePromptedForPassphrase = false; - registerPassphraseActivityStopped(); } @Override public void onDestroy() { - Log.w("SecureSMS", "onDestroy..."); - unregisterReceiver(killActivityReceiver); + Log.w("ConversationListActivity", "onDestroy..."); MemoryCleaner.clean(masterSecret); super.onDestroy(); } + @Override + public void onMasterSecretCleared() { + this.masterSecret = null; + this.fragment.setMasterSecret(null); + this.invalidateOptionsMenu(); + + if (!havePromptedForPassphrase && isVisible) { + promptForPassphrase(); + } + } + + @Override + public void onNewMasterSecret(MasterSecret masterSecret) { + this.masterSecret = masterSecret; + + if (masterSecret != null) { + if (!IdentityKeyUtil.hasIdentityKey(this)) { + new Thread(new IdentityKeyInitializer()).start(); + } + + if (!MasterSecretUtil.hasAsymmericMasterSecret(this)) { + new Thread(new AsymmetricMasteSecretInitializer()).start(); + } + + if (!isDatabaseMigrated()) initializeDatabaseMigration(); + else DecryptingQueue.schedulePendingDecrypts(this, masterSecret); + } + + this.fragment.setMasterSecret(masterSecret); + this.invalidateOptionsMenu(); + this.havePromptedForPassphrase = false; + createConversationIfNecessary(this.getIntent()); + } + + @Override public boolean onPrepareOptionsMenu(Menu menu) { Log.w("ConversationListActivity", "onPrepareOptionsMenu..."); @@ -190,11 +200,8 @@ public class ConversationListActivity extends SherlockFragmentActivity ApplicationExportListener listener = new ApplicationExportManager.ApplicationExportListener() { @Override public void onPrepareForImport() { - initializeWithMasterSecret(null); - - Intent clearKeyIntent = new Intent(KeyCachingService.CLEAR_KEY_ACTION, null, - ConversationListActivity.this, KeyCachingService.class); - startService(clearKeyIntent); + onMasterSecretCleared(); + handleClearPassphrase(); } }; @@ -208,45 +215,6 @@ public class ConversationListActivity extends SherlockFragmentActivity startService(intent); } - private void initializeWithMasterSecret(MasterSecret masterSecret) { - this.masterSecret = masterSecret; - - if (masterSecret != null) { - if (!IdentityKeyUtil.hasIdentityKey(this)) { - new Thread(new IdentityKeyInitializer()).start(); - } - - if (!MasterSecretUtil.hasAsymmericMasterSecret(this)) { - new Thread(new AsymmetricMasteSecretInitializer()).start(); - } - - if (!isDatabaseMigrated()) initializeDatabaseMigration(); - else DecryptingQueue.schedulePendingDecrypts(this, masterSecret); - } - - this.fragment.setMasterSecret(masterSecret); - this.invalidateOptionsMenu(); - createConversationIfNecessary(this.getIntent()); - } - - private void initializeKillReceiver() { - this.killActivityReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - ConversationListActivity.this.masterSecret = null; - fragment.setMasterSecret(null); - - if (isVisible) { - promptForPassphrase(); - } - } - }; - - registerReceiver(this.killActivityReceiver, - new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT), - KeyCachingService.KEY_PERMISSION, null); - } - private void initializeContactUpdatesReceiver() { ContentObserver observer = new ContentObserver(null) { @Override @@ -288,26 +256,10 @@ public class ConversationListActivity extends SherlockFragmentActivity } } - private void initializeKeyCachingServiceRegistration() { - Log.w("ConversationListActivity", "Checking caching service..."); - this.newKeyReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.w("ConversationListActivity", "Got a key broadcast..."); - initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret")); - } - }; - - IntentFilter filter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT); - registerReceiver(newKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null); - - Intent bindIntent = new Intent(this, KeyCachingService.class); - bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE); - } - private void initializeResources() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE); } this.fragment = (ConversationListFragment)this.getSupportFragmentManager() @@ -319,13 +271,6 @@ public class ConversationListActivity extends SherlockFragmentActivity .getBoolean("migrated", false); } - private void clearNotifications() { - NotificationManager manager = - (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); - - manager.cancel(KeyCachingService.NOTIFICATION_ID); - } - private void createConversationIfNecessary(Intent intent) { long thread = intent.getLongExtra("thread_id", -1L); String type = intent.getType(); @@ -364,43 +309,6 @@ public class ConversationListActivity extends SherlockFragmentActivity } } - private void registerPassphraseActivityStarted() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_START_EVENT); - startService(intent); - } - - private void registerPassphraseActivityStopped() { - Intent intent = new Intent(this, KeyCachingService.class); - intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT); - startService(intent); - } - - private ServiceConnection serviceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService(); - MasterSecret masterSecret = keyCachingService.getMasterSecret(); - - initializeWithMasterSecret(masterSecret); - - if (masterSecret == null && !havePromptedForPassphrase) - promptForPassphrase(); - - Intent cachingIntent = new Intent(ConversationListActivity.this, KeyCachingService.class); - startService(cachingIntent); - - try { - ConversationListActivity.this.unbindService(this); - } catch (IllegalArgumentException iae) { - Log.w("SecureSMS", iae); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) {} - }; - private class IdentityKeyInitializer implements Runnable { @Override public void run() { @@ -414,5 +322,4 @@ public class ConversationListActivity extends SherlockFragmentActivity MasterSecretUtil.generateAsymmetricMasterSecret(ConversationListActivity.this, masterSecret); } } - } diff --git a/src/org/thoughtcrime/securesms/KeyScanningActivity.java b/src/org/thoughtcrime/securesms/KeyScanningActivity.java index 6e6bc8abc0..88d5977fd8 100644 --- a/src/org/thoughtcrime/securesms/KeyScanningActivity.java +++ b/src/org/thoughtcrime/securesms/KeyScanningActivity.java @@ -19,23 +19,22 @@ package org.thoughtcrime.securesms; import android.content.Intent; import android.widget.Toast; -import com.actionbarsherlock.app.SherlockActivity; +import org.thoughtcrime.securesms.crypto.SerializableKey; +import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.Dialogs; + import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; -import org.thoughtcrime.securesms.crypto.SerializableKey; -import org.thoughtcrime.securesms.util.Base64; -import org.thoughtcrime.securesms.util.Dialogs; - /** * Activity for initiating/receiving key QR code scans. * * @author Moxie Marlinspike */ -public abstract class KeyScanningActivity extends SherlockActivity { +public abstract class KeyScanningActivity extends PassphraseRequiredSherlockActivity { @Override public boolean onPrepareOptionsMenu(Menu menu) { diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredActivity.java new file mode 100644 index 0000000000..45fd862676 --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredActivity.java @@ -0,0 +1,8 @@ +package org.thoughtcrime.securesms; + +import org.thoughtcrime.securesms.crypto.MasterSecret; + +public interface PassphraseRequiredActivity { + public void onMasterSecretCleared(); + public void onNewMasterSecret(MasterSecret masterSecret); +} diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredMixin.java b/src/org/thoughtcrime/securesms/PassphraseRequiredMixin.java new file mode 100644 index 0000000000..860c43f4d4 --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredMixin.java @@ -0,0 +1,128 @@ +package org.thoughtcrime.securesms; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.IBinder; + +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.service.KeyCachingService; + + +public class PassphraseRequiredMixin { + + private KeyCachingServiceConnection serviceConnection; + private BroadcastReceiver clearKeyReceiver; + private BroadcastReceiver newKeyReceiver; + + public void onCreate(Context context, PassphraseRequiredActivity activity) { + initializeClearKeyReceiver(context, activity); + } + + public void onResume(Context context, PassphraseRequiredActivity activity) { + initializeNewKeyReceiver(context, activity); + initializeServiceConnection(context, activity); + KeyCachingService.registerPassphraseActivityStarted(context); + } + + public void onPause(Context context, PassphraseRequiredActivity activity) { + removeNewKeyReceiver(context); + removeServiceConnection(context); + KeyCachingService.registerPassphraseActivityStopped(context); + } + + public void onDestroy(Context context, PassphraseRequiredActivity activity) { + removeClearKeyReceiver(context); + } + + private void initializeClearKeyReceiver(Context context, final PassphraseRequiredActivity activity) { + this.clearKeyReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + activity.onMasterSecretCleared(); + } + }; + + IntentFilter filter = new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT); + context.registerReceiver(clearKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null); + } + + private void initializeNewKeyReceiver(Context context, final PassphraseRequiredActivity activity) { + this.newKeyReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + activity.onNewMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret")); + } + }; + + IntentFilter filter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT); + context.registerReceiver(newKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null); + } + + private void initializeServiceConnection(Context context, PassphraseRequiredActivity activity) { + Intent cachingIntent = new Intent(context, KeyCachingService.class); + context.startService(cachingIntent); + + this.serviceConnection = new KeyCachingServiceConnection(activity); + + Intent bindIntent = new Intent(context, KeyCachingService.class); + context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE); + } + + private void removeClearKeyReceiver(Context context) { + if (clearKeyReceiver != null) { + context.unregisterReceiver(clearKeyReceiver); + clearKeyReceiver = null; + } + } + + private void removeNewKeyReceiver(Context context) { + if (newKeyReceiver != null) { + context.unregisterReceiver(newKeyReceiver); + newKeyReceiver = null; + } + } + + private void removeServiceConnection(Context context) { + if (this.serviceConnection != null && this.serviceConnection.isBound()) { + context.unbindService(this.serviceConnection); + } + } + + private static class KeyCachingServiceConnection implements ServiceConnection { + private final PassphraseRequiredActivity activity; + + private boolean isBound; + + public KeyCachingServiceConnection(PassphraseRequiredActivity activity) { + this.activity = activity; + this.isBound = false; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService(); + MasterSecret masterSecret = keyCachingService.getMasterSecret(); + this.isBound = true; + + if (masterSecret == null) { + activity.onMasterSecretCleared(); + } else { + activity.onNewMasterSecret(masterSecret); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + this.isBound = false; + } + + public boolean isBound() { + return this.isBound; + } + } + +} diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockActivity.java new file mode 100644 index 0000000000..b9726e6a3c --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockActivity.java @@ -0,0 +1,45 @@ +package org.thoughtcrime.securesms; + +import android.os.Bundle; + +import org.thoughtcrime.securesms.crypto.MasterSecret; + +import com.actionbarsherlock.app.SherlockActivity; + +public class PassphraseRequiredSherlockActivity extends SherlockActivity implements PassphraseRequiredActivity { + + private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + delegate.onCreate(this, this); + } + + @Override + protected void onResume() { + super.onResume(); + delegate.onResume(this, this); + } + + @Override + protected void onPause() { + super.onPause(); + delegate.onPause(this, this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + delegate.onDestroy(this, this); + } + + @Override + public void onMasterSecretCleared() { + finish(); + } + + @Override + public void onNewMasterSecret(MasterSecret masterSecret) {} + +} diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockFragmentActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockFragmentActivity.java new file mode 100644 index 0000000000..462f1cefc2 --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockFragmentActivity.java @@ -0,0 +1,44 @@ +package org.thoughtcrime.securesms; + +import android.os.Bundle; + +import org.thoughtcrime.securesms.crypto.MasterSecret; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +public class PassphraseRequiredSherlockFragmentActivity extends SherlockFragmentActivity implements PassphraseRequiredActivity { + + private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + delegate.onCreate(this, this); + } + + @Override + protected void onResume() { + super.onResume(); + delegate.onResume(this, this); + } + + @Override + protected void onPause() { + super.onPause(); + delegate.onPause(this, this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + delegate.onDestroy(this, this); + } + + @Override + public void onMasterSecretCleared() { + finish(); + } + + @Override + public void onNewMasterSecret(MasterSecret masterSecret) {} +} diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockListActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockListActivity.java new file mode 100644 index 0000000000..3d061cbb18 --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockListActivity.java @@ -0,0 +1,45 @@ +package org.thoughtcrime.securesms; + +import android.os.Bundle; + +import org.thoughtcrime.securesms.crypto.MasterSecret; + +import com.actionbarsherlock.app.SherlockListActivity; + +public class PassphraseRequiredSherlockListActivity extends SherlockListActivity implements PassphraseRequiredActivity { + + private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + delegate.onCreate(this, this); + } + + @Override + protected void onResume() { + super.onResume(); + delegate.onResume(this, this); + } + + @Override + protected void onPause() { + super.onPause(); + delegate.onPause(this, this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + delegate.onDestroy(this, this); + } + + @Override + public void onMasterSecretCleared() { + finish(); + } + + @Override + public void onNewMasterSecret(MasterSecret masterSecret) {} + +} diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockPreferenceActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockPreferenceActivity.java new file mode 100644 index 0000000000..a643a69fc7 --- /dev/null +++ b/src/org/thoughtcrime/securesms/PassphraseRequiredSherlockPreferenceActivity.java @@ -0,0 +1,48 @@ +package org.thoughtcrime.securesms; + +import android.os.Bundle; + +import org.thoughtcrime.securesms.crypto.MasterSecret; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +public abstract class PassphraseRequiredSherlockPreferenceActivity + extends SherlockPreferenceActivity + implements PassphraseRequiredActivity +{ + + private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + delegate.onCreate(this, this); + } + + @Override + protected void onResume() { + super.onResume(); + delegate.onResume(this, this); + } + + @Override + protected void onPause() { + super.onPause(); + delegate.onPause(this, this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + delegate.onDestroy(this, this); + } + + @Override + public void onMasterSecretCleared() { + finish(); + } + + @Override + public void onNewMasterSecret(MasterSecret masterSecret) {} + +} diff --git a/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java b/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java index c34e8c334a..e121c2fe88 100644 --- a/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java +++ b/src/org/thoughtcrime/securesms/ReceiveKeyActivity.java @@ -16,7 +16,6 @@ */ package org.thoughtcrime.securesms; -import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -40,7 +39,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner; * @author Moxie Marlinspike */ -public class ReceiveKeyActivity extends Activity { +public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity { private TextView descriptionText; private TextView signatureText; @@ -171,6 +170,7 @@ public class ReceiveKeyActivity extends Activity { } private class VerifyIdentityListener implements View.OnClickListener { + @Override public void onClick(View v) { Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class); intent.putExtra("recipient", recipient); @@ -181,6 +181,7 @@ public class ReceiveKeyActivity extends Activity { } private class VerifySessionListener implements View.OnClickListener { + @Override public void onClick(View v) { Intent intent = new Intent(ReceiveKeyActivity.this, VerifyKeysActivity.class); intent.putExtra("recipient", recipient); @@ -191,6 +192,7 @@ public class ReceiveKeyActivity extends Activity { } private class OkListener implements View.OnClickListener { + @Override public void onClick(View v) { keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId); finish(); @@ -198,6 +200,7 @@ public class ReceiveKeyActivity extends Activity { } private class CancelListener implements View.OnClickListener { + @Override public void onClick(View v) { ReceiveKeyActivity.this.finish(); } diff --git a/src/org/thoughtcrime/securesms/SaveIdentityActivity.java b/src/org/thoughtcrime/securesms/SaveIdentityActivity.java index 8d76cc7c21..1dafa0ccdd 100644 --- a/src/org/thoughtcrime/securesms/SaveIdentityActivity.java +++ b/src/org/thoughtcrime/securesms/SaveIdentityActivity.java @@ -25,8 +25,6 @@ import android.widget.Button; import android.widget.EditText; import android.widget.Toast; -import com.actionbarsherlock.app.SherlockActivity; - import org.thoughtcrime.securesms.crypto.IdentityKey; import org.thoughtcrime.securesms.crypto.InvalidKeyException; import org.thoughtcrime.securesms.crypto.MasterSecret; @@ -39,7 +37,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner; * * @author Moxie Marlinspike */ -public class SaveIdentityActivity extends SherlockActivity { +public class SaveIdentityActivity extends PassphraseRequiredSherlockActivity { private MasterSecret masterSecret; private IdentityKey identityKey; @@ -83,6 +81,7 @@ public class SaveIdentityActivity extends SherlockActivity { } private class OkListener implements View.OnClickListener { + @Override public void onClick(View v) { if (identityName.getText() == null || identityName.getText().toString().trim().length() == 0) { Toast.makeText(SaveIdentityActivity.this, @@ -99,6 +98,7 @@ public class SaveIdentityActivity extends SherlockActivity { builder.setMessage(R.string.SaveIdentityActivity_an_identity_key_with_the_specified_name_already_exists); builder.setPositiveButton(R.string.SaveIdentityActivity_manage_identities, new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(SaveIdentityActivity.this, ReviewIdentitiesActivity.class); intent.putExtra("master_secret", masterSecret); @@ -115,9 +115,9 @@ public class SaveIdentityActivity extends SherlockActivity { } private class CancelListener implements View.OnClickListener { + @Override public void onClick(View v) { finish(); } } - } diff --git a/src/org/thoughtcrime/securesms/service/KeyCachingService.java b/src/org/thoughtcrime/securesms/service/KeyCachingService.java index d8b9283781..89790405a7 100644 --- a/src/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/src/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -20,6 +20,7 @@ import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Binder; @@ -44,8 +45,7 @@ import org.thoughtcrime.securesms.notifications.MessageNotifier; public class KeyCachingService extends Service { - public static final int NOTIFICATION_ID = 1337; - public static final int SERVICE_RUNNING_ID = 4141; + public static final int SERVICE_RUNNING_ID = 4141; public static final String KEY_PERMISSION = "org.thoughtcrime.securesms.ACCESS_SECRETS"; public static final String NEW_KEY_EVENT = "org.thoughtcrime.securesms.service.action.NEW_KEY_EVENT"; @@ -215,4 +215,16 @@ public class KeyCachingService extends Service { return KeyCachingService.this; } } + + public static void registerPassphraseActivityStarted(Context activity) { + Intent intent = new Intent(activity, KeyCachingService.class); + intent.setAction(KeyCachingService.ACTIVITY_START_EVENT); + activity.startService(intent); + } + + public static void registerPassphraseActivityStopped(Context activity) { + Intent intent = new Intent(activity, KeyCachingService.class); + intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT); + activity.startService(intent); + } }