aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-07-01 23:23:53 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2013-07-01 23:23:53 +0200
commit14350679d1ef17fe1add86b1ec1def99f9a8f0cc (patch)
tree861e9887b687b7374f458b9302a02870a2e41aa9 /OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api
parent202ccc2c36d191fbecdcc3098875dd075454c71a (diff)
downloadopen-keychain-14350679d1ef17fe1add86b1ec1def99f9a8f0cc.tar.gz
open-keychain-14350679d1ef17fe1add86b1ec1def99f9a8f0cc.tar.bz2
open-keychain-14350679d1ef17fe1add86b1ec1def99f9a8f0cc.zip
Restructure
Diffstat (limited to 'OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api')
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersActivity.java44
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersFragment.java85
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java367
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl28
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/ServiceActivity.java199
5 files changed, 723 insertions, 0 deletions
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersActivity.java
new file mode 100644
index 000000000..e3aa0ed49
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersActivity.java
@@ -0,0 +1,44 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.MainActivity;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.MenuItem;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class CryptoConsumersActivity extends SherlockFragmentActivity {
+ private ActionBar mActionBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActionBar = getSupportActionBar();
+
+ setContentView(R.layout.crypto_consumers_activity);
+
+ mActionBar.setDisplayShowTitleEnabled(true);
+ mActionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ /**
+ * Menu Options
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // app icon in Action Bar clicked; go home
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersFragment.java
new file mode 100644
index 000000000..935d64560
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoConsumersFragment.java
@@ -0,0 +1,85 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.provider.KeychainContract.CryptoConsumers;
+import org.sufficientlysecure.keychain.util.Log;
+
+import com.actionbarsherlock.app.SherlockListFragment;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.widget.SimpleCursorAdapter;
+
+import android.view.View;
+import android.widget.ListView;
+
+public class CryptoConsumersFragment extends SherlockListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ // This is the Adapter being used to display the list's data.
+ SimpleCursorAdapter mAdapter;
+
+ // If non-null, this is the current filter the user has provided.
+ String mCurFilter;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText("TODO no crypto consumers");
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2,
+ null, new String[] { CryptoConsumers.PACKAGE_NAME, CryptoConsumers.PACKAGE_NAME },
+ new int[] { android.R.id.text1, android.R.id.text2 }, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ // Insert desired behavior here.
+ Log.i("FragmentComplexList", "Item clicked: " + id);
+ }
+
+ // These are the Contacts rows that we will retrieve.
+ static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { CryptoConsumers._ID,
+ CryptoConsumers.PACKAGE_NAME };
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ // First, pick the base URI to use depending on whether we are
+ // currently filtering.
+ Uri baseUri = CryptoConsumers.CONTENT_URI;
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null,
+ CryptoConsumers.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mAdapter.swapCursor(data);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ // This is called when the last Cursor provided to onLoadFinished()
+ // above is about to be closed. We need to make sure we are no
+ // longer using it.
+ mAdapter.swapCursor(null);
+ }
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java
new file mode 100644
index 000000000..c7d40d376
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.remote_api;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.openintents.crypto.CryptoError;
+import org.openintents.crypto.CryptoSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.helper.PgpMain;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote_api.IServiceActivityCallback;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
+
+import org.openintents.crypto.ICryptoCallback;
+import org.openintents.crypto.ICryptoService;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class CryptoService extends Service {
+ Context mContext;
+
+ // just one pool of 4 threads, pause on every user action needed
+ final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(20);
+ PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
+ TimeUnit.SECONDS, mPoolQueue);
+
+ public static final String ACTION_SERVICE_ACTIVITY = "org.sufficientlysecure.keychain.crypto_provider.IServiceActivityCallback";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mContext = this;
+ Log.d(Constants.TAG, "CryptoService, onCreate()");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(Constants.TAG, "CryptoService, onDestroy()");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // return different binder for connections from internal service activity
+ if (ACTION_SERVICE_ACTIVITY.equals(intent.getAction())) {
+ String callingPackageName = intent.getPackage();
+
+ // this binder can only be used from OpenPGP Keychain
+ if (callingPackageName.equals(Constants.PACKAGE_NAME)) {
+ return mBinderServiceActivity;
+ } else {
+ Log.e(Constants.TAG, "This binder can only be used from " + Constants.PACKAGE_NAME);
+ return null;
+ }
+ } else {
+ return mBinder;
+ }
+ }
+
+ private synchronized void encryptSafe(byte[] inputBytes, String[] encryptionUserIds,
+ ICryptoCallback callback) throws RemoteException {
+ try {
+ // build InputData and write into OutputStream
+ InputStream inputStream = new ByteArrayInputStream(inputBytes);
+ long inputLength = inputBytes.length;
+ InputData inputData = new InputData(inputStream, inputLength);
+
+ OutputStream outStream = new ByteArrayOutputStream();
+
+ // TODO: hardcoded...
+ boolean useAsciiArmor = true;
+ int compressionId = 2; // zlib
+
+ // PgpMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor,
+ // compressionId, encryptionKeyIds, encryptionPassphrase, Preferences
+ // .getPreferences(this).getDefaultEncryptionAlgorithm(),
+ // secretKeyId,
+ // Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences
+ // .getPreferences(this).getForceV3Signatures(),
+ // PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "KeychainService, Exception!", e);
+
+ try {
+ callback.onError(new CryptoError(0, e.getMessage()));
+ } catch (Exception t) {
+ Log.e(Constants.TAG, "Error returning exception to client", t);
+ }
+ }
+ }
+
+ private synchronized void decryptAndVerifySafe(byte[] inputBytes, ICryptoCallback callback)
+ throws RemoteException {
+ try {
+ // build InputData and write into OutputStream
+ InputStream inputStream = new ByteArrayInputStream(inputBytes);
+ long inputLength = inputBytes.length;
+ InputData inputData = new InputData(inputStream, inputLength);
+
+ OutputStream outputStream = new ByteArrayOutputStream();
+
+ long secretKeyId = PgpMain.getDecryptionKeyId(mContext, inputStream);
+ if (secretKeyId == Id.key.none) {
+ throw new PgpMain.PgpGeneralException(getString(R.string.error_noSecretKeyFound));
+ }
+
+ Log.d(Constants.TAG, "Got input:\n" + new String(inputBytes));
+
+ Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
+
+ String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, secretKeyId);
+
+ if (passphrase == null) {
+ Log.d(Constants.TAG, "No passphrase! Activity required!");
+
+ // start passphrase dialog
+ Bundle extras = new Bundle();
+ extras.putLong(ServiceActivity.EXTRA_SECRET_KEY_ID, secretKeyId);
+ pauseQueueAndStartServiceActivity(ServiceActivity.ACTION_CACHE_PASSPHRASE, extras);
+ }
+
+ // if (signedOnly) {
+ // resultData = PgpMain.verifyText(this, this, inputData, outStream,
+ // lookupUnknownKey);
+ // } else {
+ // resultData = PgpMain.decryptAndVerify(this, this, inputData, outStream,
+ // PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
+ // assumeSymmetricEncryption);
+ // }
+
+ Bundle outputBundle = PgpMain.decryptAndVerify(mContext, null, inputData, outputStream,
+ passphrase, false);
+
+ outputStream.close();
+
+ byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
+
+ // get signature informations from bundle
+ boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE);
+ long signatureKeyId = outputBundle
+ .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
+ String signatureUserId = outputBundle
+ .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
+ boolean signatureSuccess = outputBundle
+ .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS);
+ boolean signatureUnknown = outputBundle
+ .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN);
+
+ CryptoSignatureResult sigResult = new CryptoSignatureResult(signatureUserId, signature,
+ signatureSuccess, signatureUnknown);
+
+ // return over handler on client side
+ callback.onSuccess(outputBytes, sigResult);
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "KeychainService, Exception!", e);
+
+ try {
+ callback.onError(new CryptoError(0, e.getMessage()));
+ } catch (Exception t) {
+ Log.e(Constants.TAG, "Error returning exception to client", t);
+ }
+ }
+ }
+
+ private final ICryptoService.Stub mBinder = new ICryptoService.Stub() {
+
+ @Override
+ public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds,
+ final ICryptoCallback callback) throws RemoteException {
+
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ encryptSafe(inputBytes, encryptionUserIds, callback);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoService", e);
+ }
+ }
+ };
+
+ checkAndEnqueue(r);
+ }
+
+ @Override
+ public void encryptAndSign(byte[] inputBytes, String[] encryptionUserIds,
+ String signatureUserId, ICryptoCallback callback) throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void sign(byte[] inputBytes, String signatureUserId, ICryptoCallback callback)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void decryptAndVerify(final byte[] inputBytes, final ICryptoCallback callback)
+ throws RemoteException {
+
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ decryptAndVerifySafe(inputBytes, callback);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoService", e);
+ }
+ }
+ };
+
+ checkAndEnqueue(r);
+ }
+
+ @Override
+ public void setup(boolean asciiArmor, boolean newKeyring, String newKeyringUserId)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
+ };
+
+ private final IServiceActivityCallback.Stub mBinderServiceActivity = new IServiceActivityCallback.Stub() {
+
+ @Override
+ public void register(boolean success, String packageName) throws RemoteException {
+
+ if (success) {
+ // resume threads
+ if (isPackageAllowed(packageName)) {
+ mThreadPool.resume();
+ } else {
+ // TODO: should not happen?
+ }
+ } else {
+ // TODO
+ mPoolQueue.clear();
+ }
+
+ }
+
+ @Override
+ public void cachePassphrase(boolean success, String passphrase) throws RemoteException {
+
+ }
+
+ };
+
+ private void checkAndEnqueue(Runnable r) {
+ if (isCallerAllowed()) {
+ mThreadPool.execute(r);
+
+ Log.d(Constants.TAG, "Enqueued runnable…");
+ } else {
+ String[] callingPackages = getPackageManager()
+ .getPackagesForUid(Binder.getCallingUid());
+
+ Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
+ Bundle extras = new Bundle();
+ // TODO: currently simply uses first entry
+ extras.putString(ServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
+ pauseQueueAndStartServiceActivity(ServiceActivity.ACTION_REGISTER, extras);
+
+ mThreadPool.execute(r);
+
+ Log.d(Constants.TAG, "Enqueued runnable…");
+ }
+ }
+
+ /**
+ * Checks if process that binds to this service (i.e. the package name corresponding to the
+ * process) is in the list of allowed package names.
+ *
+ * @return true if process is allowed to use this service
+ */
+ private boolean isCallerAllowed() {
+ String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+
+ // is calling package allowed to use this service?
+ for (int i = 0; i < callingPackages.length; i++) {
+ String currentPkg = callingPackages[i];
+
+ if (isPackageAllowed(currentPkg)) {
+ return true;
+ }
+ }
+
+ Log.d(Constants.TAG, "Caller is NOT allowed!");
+ return false;
+ }
+
+ private boolean isPackageAllowed(String packageName) {
+ Log.d(Constants.TAG, "packageName: " + packageName);
+
+ ArrayList<String> allowedPkgs = ProviderHelper.getCryptoConsumers(mContext);
+ Log.d(Constants.TAG, "allowed: " + allowedPkgs);
+
+ // check if package is allowed to use our service
+ if (allowedPkgs.contains(packageName)) {
+ Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
+
+ return true;
+ } else if (Constants.PACKAGE_NAME.equals(packageName)) {
+ Log.d(Constants.TAG, "Package is OpenPGP Keychain! -> allowed!");
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void pauseQueueAndStartServiceActivity(String action, Bundle extras) {
+ mThreadPool.pause();
+
+ Log.d(Constants.TAG, "starting activity...");
+ Intent intent = new Intent(getBaseContext(), ServiceActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ // intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+ intent.setAction(action);
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ getApplication().startActivity(intent);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl
new file mode 100644
index 000000000..871560cc8
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.remote_api;
+
+
+interface IServiceActivityCallback {
+
+
+ oneway void register(in boolean success, in String packageName);
+
+ oneway void cachePassphrase(in boolean success, in String passphrase);
+
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/ServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/ServiceActivity.java
new file mode 100644
index 000000000..055de9be1
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/ServiceActivity.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote_api.IServiceActivityCallback;
+import org.sufficientlysecure.keychain.helper.PgpMain;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class ServiceActivity extends SherlockFragmentActivity {
+
+ public static final String ACTION_REGISTER = "org.sufficientlysecure.keychain.REGISTER";
+ public static final String ACTION_CACHE_PASSPHRASE = "org.sufficientlysecure.keychain.CRYPTO_CACHE_PASSPHRASE";
+
+ public static final String EXTRA_SECRET_KEY_ID = "secretKeyId";
+ public static final String EXTRA_PACKAGE_NAME = "packageName";
+
+ private IServiceActivityCallback mService;
+ private boolean mServiceBound;
+
+ private ServiceConnection mServiceActivityConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IServiceActivityCallback.Stub.asInterface(service);
+ Log.d(Constants.TAG, "connected to ICryptoServiceActivity");
+ mServiceBound = true;
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ Log.d(Constants.TAG, "disconnected from ICryptoServiceActivity");
+ mServiceBound = false;
+ }
+ };
+
+ /**
+ * If not already bound, bind!
+ *
+ * @return
+ */
+ public boolean bindToService() {
+ if (mService == null && !mServiceBound) { // if not already connected
+ try {
+ Log.d(Constants.TAG, "not bound yet");
+
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction("org.openintents.crypto.ICryptoService");
+ bindService(serviceIntent, mServiceActivityConnection, Context.BIND_AUTO_CREATE);
+
+ return true;
+ } catch (Exception e) {
+ Log.d(Constants.TAG, "Exception", e);
+ return false;
+ }
+ } else { // already connected
+ Log.d(Constants.TAG, "already bound... ");
+ return true;
+ }
+ }
+
+ public void unbindFromService() {
+ unbindService(mServiceActivityConnection);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.d(Constants.TAG, "onCreate…");
+
+ // bind to our own crypto service
+ bindToService();
+
+ handleActions(getIntent());
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // unbind from our crypto service
+ if (mServiceActivityConnection != null) {
+ unbindFromService();
+ }
+ }
+
+ protected void handleActions(Intent intent) {
+ String action = intent.getAction();
+ Bundle extras = intent.getExtras();
+
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ /**
+ * com.android.crypto actions
+ */
+ if (ACTION_REGISTER.equals(action)) {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+
+ setContentView(R.layout.register_crypto_consumer_activity);
+
+ Button allowButton = (Button) findViewById(R.id.register_crypto_consumer_allow);
+ Button disallowButton = (Button) findViewById(R.id.register_crypto_consumer_disallow);
+
+ allowButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ ProviderHelper.addCryptoConsumer(ServiceActivity.this, packageName);
+ // Intent data = new Intent();
+
+ setResult(RESULT_OK);
+ finish();
+ }
+ });
+
+ disallowButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+ } else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
+ long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
+
+ showPassphraseDialog(secretKeyId);
+ } else {
+ Log.e(Constants.TAG, "Wrong action!");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ /**
+ * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
+ * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
+ * for a symmetric passphrase
+ */
+ private void showPassphraseDialog(long secretKeyId) {
+ // Message is received after passphrase is cached
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ try {
+ PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
+ messenger, secretKeyId);
+
+ passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
+ } catch (PgpMain.PgpGeneralException e) {
+ Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
+ // send message to handler to start encryption directly
+ returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
+ }
+ }
+}