aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-09-06 13:52:57 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2013-09-06 13:52:57 +0200
commitde8e1a39d59e1a0f40e173ba3c28e4250d1a48ef (patch)
tree5cbc34684c63888d3e8e775ad35f79d498908cb7
parentb9eaf235621ff59a661b45b20ac1106f42ee4be5 (diff)
parent3a66c1c25aff1dadf4779c2df0fac04f3a9f3c0a (diff)
downloadopen-keychain-de8e1a39d59e1a0f40e173ba3c28e4250d1a48ef.tar.gz
open-keychain-de8e1a39d59e1a0f40e173ba3c28e4250d1a48ef.tar.bz2
open-keychain-de8e1a39d59e1a0f40e173ba3c28e4250d1a48ef.zip
merge k9mail back into master
-rw-r--r--API.md34
-rw-r--r--OLD_API.md68
-rw-r--r--OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml61
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.aidl (renamed from OpenPGP-Keychain/src/com/android/crypto/CryptoError.aidl)2
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.java (renamed from OpenPGP-Keychain/src/com/android/crypto/CryptoError.java)2
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoServiceConnection.java (renamed from OpenPGP-Keychain/src/com/android/crypto/CryptoServiceConnection.java)24
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.aidl (renamed from OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.aidl)2
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.java (renamed from OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.java)2
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoCallback.aidl (renamed from OpenPGP-Keychain/src/com/android/crypto/ICryptoCallback.aidl)16
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl (renamed from OpenPGP-Keychain/src/com/android/crypto/ICryptoService.aidl)54
-rw-r--r--OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java317
-rw-r--r--OpenPGP-Keychain/AndroidManifest.xml160
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/ic_action_cancel.pngbin0 -> 1358 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/ic_action_done.pngbin0 -> 1320 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi/ic_action_cancel.pngbin0 -> 1202 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi/ic_action_done.pngbin0 -> 1197 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-xhdpi/ic_action_cancel.pngbin0 -> 1488 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-xhdpi/ic_action_done.pngbin0 -> 1546 bytes
-rw-r--r--OpenPGP-Keychain/res/layout/actionbar_custom_view_done.xml27
-rw-r--r--OpenPGP-Keychain/res/layout/actionbar_custom_view_done_cancel.xml29
-rw-r--r--OpenPGP-Keychain/res/layout/actionbar_include_cancel_button.xml36
-rw-r--r--OpenPGP-Keychain/res/layout/actionbar_include_done_button.xml36
-rw-r--r--OpenPGP-Keychain/res/layout/api_app_register_activity.xml24
-rw-r--r--OpenPGP-Keychain/res/layout/api_app_settings_activity.xml16
-rw-r--r--OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml96
-rw-r--r--OpenPGP-Keychain/res/layout/api_apps_adapter_list_item.xml28
-rw-r--r--OpenPGP-Keychain/res/layout/api_apps_list_activity.xml12
-rw-r--r--OpenPGP-Keychain/res/layout/decrypt.xml1
-rw-r--r--OpenPGP-Keychain/res/layout/register_crypto_consumer_activity.xml25
-rw-r--r--OpenPGP-Keychain/res/menu/api_app_settings.xml13
-rw-r--r--OpenPGP-Keychain/res/values/strings.xml16
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.aidl20
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.java76
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/CryptoServiceConnection.java91
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.aidl20
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.java76
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/ICryptoCallback.aidl32
-rw-r--r--OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl82
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java1
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoActivity.java92
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java196
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/RegisterActivity.java74
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java49
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java29
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java20
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java120
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java61
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java70
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java107
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java208
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java409
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoServiceActivity.java267
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl26
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java75
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java44
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java90
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java54
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/.SelectSecretKeyFragment.java.kate-swpbin0 -> 64 bytes
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java8
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java13
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java10
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java12
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java89
-rw-r--r--README.md66
65 files changed, 2838 insertions, 852 deletions
diff --git a/API.md b/API.md
new file mode 100644
index 000000000..80c270840
--- /dev/null
+++ b/API.md
@@ -0,0 +1,34 @@
+# Security Model
+
+## Basic goals
+
+* Intents without permissions should only work based on user interaction (e.g. click a button in a dialog)
+
+Android primitives to exchange data: Intent, Intent with return values, Send (also an Intent), Content Provider, AIDL
+
+## Without Permissions
+
+### Intents
+All Intents start with ``org.sufficientlysecure.keychain.action.``
+
+* ``android.intent.action.VIEW`` connected to .gpg and .asc files: Import Key and Decrypt
+* ``android.intent.action.SEND connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
+* ``IMPORT``
+* ``IMPORT_FROM_FILE``
+* ``IMPORT_FROM_QR_CODE``
+* ``IMPORT_FROM_NFC``
+* ``SHARE_KEYRING``
+* ``SHARE_KEYRING_WITH_QR_CODE``
+* ``SHARE_KEYRING_WITH_NFC``
+* ``EDIT_KEYRING``
+* ``SELECT_PUBLIC_KEYRINGS``
+* ``SELECT_SECRET_KEYRING``
+* ``ENCRYPT``
+* ``ENCRYPT_FILE``
+* ``DECRYPT``
+* ``DECRYPT_FILE``
+
+TODO:
+- remove IMPORT, SHARE intents, simplify ENCRYPT and DECRYPT intents (include _FILE derivates like done in SEND based on file type)
+- EDIT_KEYRING and CREATE_KEYRING, should be available via for registered apps
+- new intent REGISTER_APP? \ No newline at end of file
diff --git a/OLD_API.md b/OLD_API.md
new file mode 100644
index 000000000..1a9bab0a7
--- /dev/null
+++ b/OLD_API.md
@@ -0,0 +1,68 @@
+This is the old API. Currently disabled!
+
+# Security Model
+
+## Basic goals
+
+* Intents without permissions should only work based on user interaction (e.g. click a button in a dialog)
+
+Android primitives to exchange data: Intent, Intent with return values, Send (also an Intent), Content Provider, AIDL
+
+## Possible Permissions
+
+* ACCESS_API: Encrypt/Sign/Decrypt/Create keys without user interaction (intents, remote service), Read key information (not the actual keys)(content provider)
+* ACCESS_KEYS: get and import actual public and secret keys (remote service)
+
+
+## Without Permissions
+
+### Intents
+All Intents start with org.sufficientlysecure.keychain.action.
+
+* android.intent.action.VIEW connected to .gpg and .asc files: Import Key and Decrypt
+* android.intent.action.SEND connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
+* IMPORT
+* IMPORT_FROM_FILE
+* IMPORT_FROM_QR_CODE
+* IMPORT_FROM_NFC
+* SHARE_KEYRING
+* SHARE_KEYRING_WITH_QR_CODE
+* SHARE_KEYRING_WITH_NFC
+* EDIT_KEYRING
+* SELECT_PUBLIC_KEYRINGS
+* SELECT_SECRET_KEYRING
+* ENCRYPT
+* ENCRYPT_FILE
+* DECRYPT
+* DECRYPT_FILE
+
+## With permission ACCESS_API
+
+### Intents
+
+* CREATE_KEYRING
+* ENCRYPT_AND_RETURN
+* ENCRYPT_STREAM_AND_RETURN
+* GENERATE_SIGNATURE_AND_RETURN
+* DECRYPT_AND_RETURN
+* DECRYPT_STREAM_AND_RETURN
+
+### Broadcast Receiver
+On change of database the following broadcast is send.
+* DATABASE_CHANGE
+
+### Content Provider
+
+* The whole content provider requires a permission (only read)
+* Don't give out blobs (keys can be accessed by ACCESS_KEYS via remote service)
+* Make an internal and external content provider (or pathes with <path-permission>)
+* Look at android:grantUriPermissions especially for ApgServiceBlobProvider
+* Only give out android:readPermission
+
+### ApgApiService (Remote Service)
+AIDL service
+
+## With permission ACCESS_KEYS
+
+### ApgKeyService (Remote Service)
+AIDL service to access actual private keyring objects \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml b/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml
index 447734a52..af1e345d6 100644
--- a/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml
+++ b/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml
@@ -8,65 +8,72 @@
android:layout_height="match_parent"
android:orientation="vertical" >
- <Button
- android:id="@+id/crypto_provider_demo_register"
- android:layout_width="match_parent"
+ <TextView
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:onClick="registerCryptoProvider"
- android:text="Register crypto provider" />
+ android:text="Encrypt User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <Button
- android:id="@+id/aidl_demo_select_secret_key"
+ <EditText
+ android:id="@+id/crypto_provider_demo_encrypt_user_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:onClick="selectSecretKeyOnClick"
- android:text="Select secret key" />
+ android:text="dominik@dominikschuermann.de"
+ android:textAppearance="@android:style/TextAppearance.Small" />
- <Button
- android:id="@+id/aidl_demo_select_encryption_key"
- android:layout_width="match_parent"
+ <TextView
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:onClick="selectEncryptionKeysOnClick"
- android:text="Select encryption key(s)" />
+ android:text="Message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
- android:id="@+id/aidl_demo_message"
+ android:id="@+id/crypto_provider_demo_message"
android:layout_width="match_parent"
android:layout_height="150dip"
android:text="message"
android:textAppearance="@android:style/TextAppearance.Small" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Ciphertext"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
<EditText
- android:id="@+id/aidl_demo_ciphertext"
+ android:id="@+id/crypto_provider_demo_ciphertext"
android:layout_width="match_parent"
android:layout_height="150dip"
android:text="ciphertext"
android:textAppearance="@android:style/TextAppearance.Small" />
<Button
- android:id="@+id/aidl_demo_encrypt"
+ android:id="@+id/crypto_provider_demo_encrypt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="encryptOnClick"
android:text="Encrypt" />
<Button
- android:id="@+id/aidl_demo_decrypt"
+ android:id="@+id/crypto_provider_demo_sign"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:onClick="decryptOnClick"
- android:text="Decrypt" />
+ android:onClick="signOnClick"
+ android:text="Sign" />
- <TextView
+ <Button
+ android:id="@+id/crypto_provider_demo_encrypt_and_sign"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="APG Data:" />
+ android:layout_height="wrap_content"
+ android:onClick="encryptAndSignOnClick"
+ android:text="Encrypt and Sign" />
- <TextView
- android:id="@+id/aidl_demo_data"
+ <Button
+ android:id="@+id/crypto_provider_demo_decrypt"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minLines="10" />
+ android:layout_height="wrap_content"
+ android:onClick="decryptOnClick"
+ android:text="Decrypt" />
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/android/crypto/CryptoError.aidl b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.aidl
index d1b941212..7b67c8995 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/CryptoError.aidl
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
// Declare CryptoError so AIDL can find it and knows that it implements the parcelable protocol.
parcelable CryptoError; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/android/crypto/CryptoError.java b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.java
index 9540f4f68..265fe2633 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/CryptoError.java
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoError.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/OpenPGP-Keychain/src/com/android/crypto/CryptoServiceConnection.java b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoServiceConnection.java
index 4d659d344..d9e91f772 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/CryptoServiceConnection.java
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoServiceConnection.java
@@ -1,4 +1,22 @@
-package com.android.crypto;
+/*
+ * 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.openintents.crypto;
+
+import org.openintents.crypto.ICryptoService;
import android.content.ComponentName;
import android.content.Context;
@@ -50,8 +68,8 @@ public class CryptoServiceConnection {
Log.d(TAG, "not bound yet");
Intent serviceIntent = new Intent();
- serviceIntent.setAction("com.android.crypto.ICryptoService");
- serviceIntent.setPackage(cryptoProviderPackageName); // TODO: test
+ serviceIntent.setAction("org.openintents.crypto.ICryptoService");
+ serviceIntent.setPackage(cryptoProviderPackageName);
mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
Context.BIND_AUTO_CREATE);
diff --git a/OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.aidl b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.aidl
index 21862c497..1d39bac70 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.aidl
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
// Declare CryptoSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
parcelable CryptoSignatureResult; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.java b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.java
index 87f5f43b5..e193b73b3 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/CryptoSignatureResult.java
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/CryptoSignatureResult.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/OpenPGP-Keychain/src/com/android/crypto/ICryptoCallback.aidl b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoCallback.aidl
index af6587c04..1f910d4c6 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/ICryptoCallback.aidl
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoCallback.aidl
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
-import com.android.crypto.CryptoSignatureResult;
-import com.android.crypto.CryptoError;
+import org.openintents.crypto.CryptoSignatureResult;
+import org.openintents.crypto.CryptoError;
interface ICryptoCallback {
-
- oneway void onEncryptSignSuccess(in byte[] outputBytes);
- oneway void onDecryptVerifySuccess(in byte[] outputBytes, in CryptoSignatureResult signatureResult);
+ /**
+ * CryptoSignatureResult is only returned if the Callback was used from decryptAndVerify
+ *
+ */
+ oneway void onSuccess(in byte[] outputBytes, in CryptoSignatureResult signatureResult);
oneway void onError(in CryptoError error);
-
- oneway void onActivityRequired(in Intent intent);
} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/com/android/crypto/ICryptoService.aidl b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl
index 05baa16e0..c84ca28fb 100644
--- a/OpenPGP-Keychain/src/com/android/crypto/ICryptoService.aidl
+++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.crypto;
+package org.openintents.crypto;
-import com.android.crypto.ICryptoCallback;
+import org.openintents.crypto.ICryptoCallback;
/**
* All methods are oneway, which means they are asynchronous and non-blocking.
@@ -29,40 +29,38 @@ interface ICryptoService {
*
* @param inputBytes
* Byte array you want to encrypt
- * @param encryptionKeyIds
- * Ids of public keys used for encryption
- * @param handler
- * Results are returned to this Handler after successful encryption
+ * @param encryptionUserIds
+ * User Ids (emails) of recipients
+ * @param callback
+ * Callback where to return results
*/
oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in ICryptoCallback callback);
/**
- * Encrypt and sign
+ * Sign
*
- *
- *
* @param inputBytes
* Byte array you want to encrypt
- * @param signatureKeyId
- * Key id of key to sign with
- * @param handler
- * Results are returned to this Handler after successful encryption and signing
+ * @param signatureUserId
+ * User Ids (email) of sender
+ * @param callback
+ * Callback where to return results
*/
- oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback);
+ oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback);
/**
- * Sign
+ * Encrypt and sign
*
- *
- *
* @param inputBytes
* Byte array you want to encrypt
- * @param signatureId
- *
- * @param handler
- * Results are returned to this Handler after successful encryption and signing
+ * @param encryptionUserIds
+ * User Ids (emails) of recipients
+ * @param signatureUserId
+ * User Ids (email) of sender
+ * @param callback
+ * Callback where to return results
*/
- oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback);
+ oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback);
/**
* Decrypts and verifies given input bytes. If no signature is present this method
@@ -70,9 +68,15 @@ interface ICryptoService {
*
* @param inputBytes
* Byte array you want to decrypt and verify
- * @param handler
- * Handler where to return results to after successful encryption
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void decryptAndVerify(in byte[] inputBytes, in ICryptoCallback callback);
+
+ /**
+ * Opens setup using default parameters
+ *
*/
- oneway void decryptAndVerify(in byte[] inputBytes, in ICryptoCallback callback);
+ oneway void setup(boolean asciiArmor, boolean newKeyring, String newKeyringUserId);
} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java b/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java
index b915e2a76..319820d7c 100644
--- a/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java
+++ b/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 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.
@@ -16,51 +16,42 @@
package org.sufficientlysecure.keychain.demo;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openintents.crypto.CryptoError;
+import org.openintents.crypto.CryptoServiceConnection;
+import org.openintents.crypto.CryptoSignatureResult;
+import org.openintents.crypto.ICryptoCallback;
+import org.openintents.crypto.ICryptoService;
import org.sufficientlysecure.keychain.demo.R;
import org.sufficientlysecure.keychain.integration.Constants;
-import org.sufficientlysecure.keychain.integration.KeychainData;
-import org.sufficientlysecure.keychain.integration.KeychainIntentHelper;
-import org.sufficientlysecure.keychain.service.IKeychainApiService;
-import org.sufficientlysecure.keychain.service.IKeychainKeyService;
-import org.sufficientlysecure.keychain.service.handler.IKeychainDecryptHandler;
-import org.sufficientlysecure.keychain.service.handler.IKeychainEncryptHandler;
-import org.sufficientlysecure.keychain.service.handler.IKeychainGetDecryptionKeyIdHandler;
import android.app.Activity;
import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
-import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListAdapter;
import android.widget.TextView;
-import android.widget.Toast;
public class CryptoProviderDemoActivity extends Activity {
Activity mActivity;
- TextView mMessageTextView;
- TextView mCiphertextTextView;
- TextView mDataTextView;
-
- KeychainIntentHelper mKeychainIntentHelper;
- KeychainData mKeychainData;
+ EditText mMessage;
+ EditText mCiphertext;
+ EditText mEncryptUserId;
+ EditText mSignUserId;
- private IKeychainApiService service = null;
- private ServiceConnection svcConn = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder binder) {
- service = IKeychainApiService.Stub.asInterface(binder);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- service = null;
- }
- };
+ private CryptoServiceConnection mCryptoServiceConnection;
@Override
public void onCreate(Bundle icicle) {
@@ -69,170 +60,208 @@ public class CryptoProviderDemoActivity extends Activity {
mActivity = this;
- mMessageTextView = (TextView) findViewById(R.id.aidl_demo_message);
- mCiphertextTextView = (TextView) findViewById(R.id.aidl_demo_ciphertext);
- mDataTextView = (TextView) findViewById(R.id.aidl_demo_data);
-
- mKeychainIntentHelper = new KeychainIntentHelper(mActivity);
- mKeychainData = new KeychainData();
+ mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
+ mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
+ mEncryptUserId = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
- bindService(new Intent(IKeychainApiService.class.getName()), svcConn,
- Context.BIND_AUTO_CREATE);
+ selectCryptoProvider();
}
- public void registerCryptoProvider(View view) {
- try {
- startActivityForResult(Intent.createChooser(new Intent("com.android.crypto.REGISTER"),
- "select crypto provider"), 123);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(mActivity, "No app that handles com.android.crypto.REGISTER!",
- Toast.LENGTH_LONG).show();
- Log.e(Constants.TAG, "No app that handles com.android.crypto.REGISTER!");
+ /**
+ * Callback from remote crypto service
+ */
+ final ICryptoCallback.Stub encryptCallback = new ICryptoCallback.Stub() {
+
+ @Override
+ public void onSuccess(final byte[] outputBytes, CryptoSignatureResult signatureResult)
+ throws RemoteException {
+ Log.d(Constants.TAG, "onEncryptSignSuccess");
+
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ mCiphertext.setText(new String(outputBytes));
+
+ }
+ });
}
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == 123) {
- if (resultCode == RESULT_OK) {
- String packageName = data.getStringExtra("packageName");
- Log.d(Constants.TAG, "packageName: " + packageName);
- }
+ @Override
+ public void onError(CryptoError error) throws RemoteException {
+ Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
+ Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
}
- // boolean result = mKeychainIntentHelper.onActivityResult(requestCode, resultCode, data,
- // mKeychainData);
- // if (result) {
- // updateView();
- // }
+ };
- // continue with other activity results
- super.onActivityResult(requestCode, resultCode, data);
- }
+ final ICryptoCallback.Stub decryptCallback = new ICryptoCallback.Stub() {
+
+ @Override
+ public void onSuccess(final byte[] outputBytes, final CryptoSignatureResult signatureResult)
+ throws RemoteException {
+ Log.d(Constants.TAG, "onDecryptVerifySuccess");
+
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ mMessage.setText(new String(outputBytes) + "\n\n" + signatureResult.toString());
+
+ }
+ });
+
+ }
+
+ @Override
+ public void onError(CryptoError error) throws RemoteException {
+ Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
+ Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
+ }
+
+ };
public void encryptOnClick(View view) {
- byte[] inputBytes = mMessageTextView.getText().toString().getBytes();
+ byte[] inputBytes = mMessage.getText().toString().getBytes();
try {
- service.encryptAsymmetric(inputBytes, null, true, 0, mKeychainData.getPublicKeys(), 7,
- encryptHandler);
+ mCryptoServiceConnection.getService().encrypt(inputBytes,
+ new String[] { mEncryptUserId.getText().toString() }, encryptCallback);
} catch (RemoteException e) {
- exceptionImplementation(-1, e.toString());
+ Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
}
- public void decryptOnClick(View view) {
- byte[] inputBytes = mCiphertextTextView.getText().toString().getBytes();
+ public void signOnClick(View view) {
+ byte[] inputBytes = mMessage.getText().toString().getBytes();
try {
- service.decryptAndVerifyAsymmetric(inputBytes, null, null, decryptHandler);
+ mCryptoServiceConnection.getService().sign(inputBytes,
+ mSignUserId.getText().toString(), encryptCallback);
} catch (RemoteException e) {
- exceptionImplementation(-1, e.toString());
+ Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
}
- private void updateView() {
- if (mKeychainData.getDecryptedData() != null) {
- mMessageTextView.setText(mKeychainData.getDecryptedData());
+ public void encryptAndSignOnClick(View view) {
+ byte[] inputBytes = mMessage.getText().toString().getBytes();
+
+ try {
+ mCryptoServiceConnection.getService().encryptAndSign(inputBytes,
+ new String[] { mEncryptUserId.getText().toString() },
+ mSignUserId.getText().toString(), encryptCallback);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
- if (mKeychainData.getEncryptedData() != null) {
- mCiphertextTextView.setText(mKeychainData.getEncryptedData());
+ }
+
+ public void decryptOnClick(View view) {
+ byte[] inputBytes = mCiphertext.getText().toString().getBytes();
+
+ try {
+ mCryptoServiceConnection.getService().decryptAndVerify(inputBytes, decryptCallback);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
- mDataTextView.setText(mKeychainData.toString());
}
@Override
public void onDestroy() {
super.onDestroy();
- unbindService(svcConn);
- }
-
- private void exceptionImplementation(int exceptionId, String error) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Exception!").setMessage(error).setPositiveButton("OK", null).show();
+ if (mCryptoServiceConnection != null) {
+ mCryptoServiceConnection.unbindFromService();
+ }
}
- private final IKeychainEncryptHandler.Stub encryptHandler = new IKeychainEncryptHandler.Stub() {
+ private static class CryptoProviderElement {
+ private String packageName;
+ private String simpleName;
+ private Drawable icon;
- @Override
- public void onException(final int exceptionId, final String message) throws RemoteException {
- runOnUiThread(new Runnable() {
- public void run() {
- exceptionImplementation(exceptionId, message);
- }
- });
+ public CryptoProviderElement(String packageName, String simpleName, Drawable icon) {
+ this.packageName = packageName;
+ this.simpleName = simpleName;
+ this.icon = icon;
}
@Override
- public void onSuccess(final byte[] outputBytes, String outputUri) throws RemoteException {
- runOnUiThread(new Runnable() {
- public void run() {
- mKeychainData.setEncryptedData(new String(outputBytes));
- updateView();
- }
- });
+ public String toString() {
+ return simpleName;
}
+ }
- };
+ private void selectCryptoProvider() {
+ Intent intent = new Intent(ICryptoService.class.getName());
- private final IKeychainDecryptHandler.Stub decryptHandler = new IKeychainDecryptHandler.Stub() {
+ final ArrayList<CryptoProviderElement> providerList = new ArrayList<CryptoProviderElement>();
- @Override
- public void onException(final int exceptionId, final String message) throws RemoteException {
- runOnUiThread(new Runnable() {
- public void run() {
- exceptionImplementation(exceptionId, message);
- }
- });
- }
+ List<ResolveInfo> resInfo = getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ for (ResolveInfo resolveInfo : resInfo) {
+ if (resolveInfo.serviceInfo == null)
+ continue;
- @Override
- public void onSuccess(final byte[] outputBytes, String outputUri, boolean signature,
- long signatureKeyId, String signatureUserId, boolean signatureSuccess,
- boolean signatureUnknown) throws RemoteException {
- runOnUiThread(new Runnable() {
- public void run() {
- mKeychainData.setDecryptedData(new String(outputBytes));
- updateView();
- }
- });
+ String packageName = resolveInfo.serviceInfo.packageName;
+ String simpleName = String.valueOf(resolveInfo.serviceInfo
+ .loadLabel(getPackageManager()));
+ Drawable icon = resolveInfo.serviceInfo.loadIcon(getPackageManager());
+ providerList.add(new CryptoProviderElement(packageName, simpleName, icon));
+ }
- }
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ alert.setTitle("Select Crypto Provider!");
+ alert.setCancelable(false);
- };
+ if (!providerList.isEmpty()) {
- private final IKeychainGetDecryptionKeyIdHandler.Stub helperHandler = new IKeychainGetDecryptionKeyIdHandler.Stub() {
+ // Init ArrayAdapter with Crypto Providers
+ ListAdapter adapter = new ArrayAdapter<CryptoProviderElement>(this,
+ android.R.layout.select_dialog_item, android.R.id.text1, providerList) {
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // User super class to create the View
+ View v = super.getView(position, convertView, parent);
+ TextView tv = (TextView) v.findViewById(android.R.id.text1);
- @Override
- public void onException(final int exceptionId, final String message) throws RemoteException {
- runOnUiThread(new Runnable() {
- public void run() {
- exceptionImplementation(exceptionId, message);
- }
- });
- }
+ // Put the image on the TextView
+ tv.setCompoundDrawablesWithIntrinsicBounds(providerList.get(position).icon,
+ null, null, null);
- @Override
- public void onSuccess(long arg0, boolean arg1) throws RemoteException {
- // TODO Auto-generated method stub
+ // Add margin between image and text (support various screen densities)
+ int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
+ tv.setCompoundDrawablePadding(dp5);
- }
+ return v;
+ }
+ };
- };
+ alert.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
- /**
- * Selection is done with Intents, not AIDL!
- *
- * @param view
- */
- public void selectSecretKeyOnClick(View view) {
- mKeychainIntentHelper.selectSecretKey();
- }
+ public void onClick(DialogInterface dialog, int position) {
+ String packageName = providerList.get(position).packageName;
- public void selectEncryptionKeysOnClick(View view) {
- mKeychainIntentHelper.selectPublicKeys("user@example.com");
+ // bind to service
+ mCryptoServiceConnection = new CryptoServiceConnection(
+ CryptoProviderDemoActivity.this, packageName);
+ mCryptoServiceConnection.bindToService();
- }
+ dialog.dismiss();
+ }
+ });
+ } else {
+ alert.setMessage("No Crypto Provider installed!");
+ }
+ alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ finish();
+ }
+ });
+
+ AlertDialog ad = alert.create();
+ ad.show();
+ }
+ }
}
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index d75350048..6de9221ca 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -67,24 +67,27 @@
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
- <permission-group
- android:name="org.sufficientlysecure.keychain.permission-group.keychain"
- android:description="@string/permission_group_description"
- android:icon="@drawable/icon"
- android:label="@string/permission_group_label" />
-
- <permission
- android:name="org.sufficientlysecure.keychain.permission.ACCESS_KEYS"
- android:description="@string/permission_access_keys_description"
- android:label="@string/permission_access_keys_label"
- android:permissionGroup="org.sufficientlysecure.keychain.permission-group.keychain"
- android:protectionLevel="dangerous" />
- <permission
- android:name="org.sufficientlysecure.keychain.permission.ACCESS_API"
- android:description="@string/permission_access_api_description"
- android:label="@string/permission_access_api_label"
- android:permissionGroup="org.sufficientlysecure.keychain.permission-group.keychain"
- android:protectionLevel="dangerous" />
+ <!-- TODO: disabled, old API -->
+ <!-- <permission-group -->
+ <!-- android:name="org.sufficientlysecure.keychain.permission-group.keychain" -->
+ <!-- android:description="@string/permission_group_description" -->
+ <!-- android:icon="@drawable/icon" -->
+ <!-- android:label="@string/permission_group_label" /> -->
+
+
+ <!-- <permission -->
+ <!-- android:name="org.sufficientlysecure.keychain.permission.ACCESS_KEYS" -->
+ <!-- android:description="@string/permission_access_keys_description" -->
+ <!-- android:label="@string/permission_access_keys_label" -->
+ <!-- android:permissionGroup="org.sufficientlysecure.keychain.permission-group.keychain" -->
+ <!-- android:protectionLevel="dangerous" /> -->
+ <!-- <permission -->
+ <!-- android:name="org.sufficientlysecure.keychain.permission.ACCESS_API" -->
+ <!-- android:description="@string/permission_access_api_description" -->
+ <!-- android:label="@string/permission_access_api_label" -->
+ <!-- android:permissionGroup="org.sufficientlysecure.keychain.permission-group.keychain" -->
+ <!-- android:protectionLevel="dangerous" /> -->
+
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
<application
@@ -253,6 +256,7 @@
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
+ <!-- Workaround to match files in pathes with dots in them, like /cdcard/my.folder/test.gpg -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\.gpg" />
@@ -411,82 +415,92 @@
android:exported="false"
android:process=":passphrase_cache" />
<service android:name="org.sufficientlysecure.keychain.service.KeychainIntentService" />
- <service
- android:name="org.sufficientlysecure.keychain.service.KeychainApiService"
- android:enabled="true"
- android:exported="true"
- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API"
- android:process=":remoteapi" >
- <intent-filter>
- <action android:name="org.sufficientlysecure.keychain.service.IKeychainApiService" />
- </intent-filter>
-
- <meta-data
- android:name="api_version"
- android:value="3" />
- </service>
- <service
- android:name="org.sufficientlysecure.keychain.service.KeychainKeyService"
- android:enabled="true"
- android:exported="true"
- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_KEYS"
- android:process=":remotekeys" >
- <intent-filter>
- <action android:name="org.sufficientlysecure.keychain.service.IKeychainKeyService" />
- </intent-filter>
- <meta-data
- android:name="api_version"
- android:value="3" />
- </service>
+ <!-- TODO: disabled, old API! -->
+ <!-- <service -->
+ <!-- android:name="org.sufficientlysecure.keychain.service.KeychainApiService" -->
+ <!-- android:enabled="true" -->
+ <!-- android:exported="true" -->
+ <!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" -->
+ <!-- android:process=":remoteapi" > -->
+ <!-- <intent-filter> -->
+ <!-- <action android:name="org.sufficientlysecure.keychain.service.IKeychainApiService" /> -->
+ <!-- </intent-filter> -->
+
+
+ <!-- <meta-data -->
+ <!-- android:name="api_version" -->
+ <!-- android:value="3" /> -->
+ <!-- </service> -->
+ <!-- <service -->
+ <!-- android:name="org.sufficientlysecure.keychain.service.KeychainKeyService" -->
+ <!-- android:enabled="true" -->
+ <!-- android:exported="true" -->
+ <!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_KEYS" -->
+ <!-- android:process=":remotekeys" > -->
+ <!-- <intent-filter> -->
+ <!-- <action android:name="org.sufficientlysecure.keychain.service.IKeychainKeyService" /> -->
+ <!-- </intent-filter> -->
+
+
+ <!-- <meta-data -->
+ <!-- android:name="api_version" -->
+ <!-- android:value="3" /> -->
+ <!-- </service> -->
<provider
android:name="org.sufficientlysecure.keychain.provider.KeychainProviderInternal"
android:authorities="org.sufficientlysecure.keychain.internal"
android:exported="false" />
- <provider
- android:name="org.sufficientlysecure.keychain.provider.KeychainProviderExternal"
- android:authorities="org.sufficientlysecure.keychain"
- android:exported="true"
- android:readPermission="org.sufficientlysecure.keychain.permission.ACCESS_API" />
+ <!-- TODO: disabled, old API -->
+ <!-- <provider -->
+ <!-- android:name="org.sufficientlysecure.keychain.provider.KeychainProviderExternal" -->
+ <!-- android:authorities="org.sufficientlysecure.keychain" -->
+ <!-- android:exported="true" -->
+ <!-- android:readPermission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
+
<!-- TODO: authority! -->
- <provider
- android:name="org.sufficientlysecure.keychain.provider.KeychainServiceBlobProvider"
- android:authorities="org.sufficientlysecure.keychain.provider.apgserviceblobprovider"
- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" />
+ <!-- <provider -->
+ <!-- android:name="org.sufficientlysecure.keychain.provider.KeychainServiceBlobProvider" -->
+ <!-- android:authorities="org.sufficientlysecure.keychain.provider.apgserviceblobprovider" -->
+ <!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
- <!-- Crypto Provider other intents -->
- <activity
- android:name=".crypto_provider.CryptoActivity"
- android:label="TODO crypto activity"
- android:process=":crypto" >
- <intent-filter>
- <action android:name="org.sufficientlysecure.keychain.CRYPTO_CACHE_PASSPHRASE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
+ <!-- Remote API internal intents -->
- <!-- Crypto Provider API -->
<activity
- android:name=".crypto_provider.RegisterActivity"
- android:label="TODO reg"
+ android:name="org.sufficientlysecure.keychain.remote_api.CryptoServiceActivity"
+ android:exported="false"
+ android:label="@string/app_name"
android:process=":crypto" >
- <intent-filter>
- <action android:name="com.android.crypto.REGISTER" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
+ <!-- Don't publish intents, they are only used internally! -->
</activity>
+ <activity
+ android:name="org.sufficientlysecure.keychain.remote_api.RegisteredAppsListActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:exported="false"
+ android:label="@string/title_crypto_consumers" />
+ <activity
+ android:name="org.sufficientlysecure.keychain.remote_api.AppSettingsActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:exported="false" />
+
+ <!-- Remote API -->
<service
- android:name="org.sufficientlysecure.keychain.crypto_provider.CryptoService"
+ android:name="org.sufficientlysecure.keychain.remote_api.CryptoService"
android:enabled="true"
android:exported="true"
android:process=":crypto" >
<intent-filter>
- <action android:name="com.android.crypto.ICryptoService" />
+ <action android:name="org.openintents.crypto.ICryptoService" />
+ </intent-filter>
+ <intent-filter>
+
+ <!-- Can only be used from OpenPGP Keychain (internal): -->
+ <action android:name="org.sufficientlysecure.keychain.crypto_provider.IServiceActivityCallback" />
</intent-filter>
<meta-data
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_action_cancel.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_action_cancel.png
new file mode 100644
index 000000000..cde36e1fa
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-hdpi/ic_action_cancel.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_action_done.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_action_done.png
new file mode 100644
index 000000000..58bf97217
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-hdpi/ic_action_done.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_action_cancel.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_action_cancel.png
new file mode 100644
index 000000000..9f4c3d6a2
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-mdpi/ic_action_cancel.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_action_done.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_action_done.png
new file mode 100644
index 000000000..cf5fab3ad
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-mdpi/ic_action_done.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_cancel.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_cancel.png
new file mode 100644
index 000000000..ca7d159fd
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_cancel.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_done.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_done.png
new file mode 100644
index 000000000..b8915716e
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-xhdpi/ic_action_done.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/layout/actionbar_custom_view_done.xml b/OpenPGP-Keychain/res/layout/actionbar_custom_view_done.xml
new file mode 100644
index 000000000..bcbb5f5c1
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/actionbar_custom_view_done.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:divider="@drawable/abs__list_divider_holo_light"
+ android:dividerPadding="12dp"
+ android:orientation="horizontal"
+ android:showDividers="end" >
+
+ <include layout="@layout/actionbar_include_done_button" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/actionbar_custom_view_done_cancel.xml b/OpenPGP-Keychain/res/layout/actionbar_custom_view_done_cancel.xml
new file mode 100644
index 000000000..8c8428e75
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/actionbar_custom_view_done_cancel.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:divider="@drawable/abs__list_divider_holo_light"
+ android:dividerPadding="12dp"
+ android:orientation="horizontal"
+ android:showDividers="middle" >
+
+ <include layout="@layout/actionbar_include_cancel_button" />
+
+ <include layout="@layout/actionbar_include_done_button" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/actionbar_include_cancel_button.xml b/OpenPGP-Keychain/res/layout/actionbar_include_cancel_button.xml
new file mode 100644
index 000000000..0f0521d3c
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/actionbar_include_cancel_button.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/actionbar_cancel"
+ style="@style/Widget.Sherlock.ActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/actionbar_cancel_text"
+ style="@style/Widget.Sherlock.ActionBar.TabText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawableLeft="@drawable/ic_action_cancel"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:paddingRight="20dp"
+ android:text="Cancel (set in-code!)" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/actionbar_include_done_button.xml b/OpenPGP-Keychain/res/layout/actionbar_include_done_button.xml
new file mode 100644
index 000000000..54e5933e3
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/actionbar_include_done_button.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/actionbar_done"
+ style="@style/Widget.Sherlock.ActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <TextView
+ android:id="@+id/actionbar_done_text"
+ style="@style/Widget.Sherlock.ActionBar.TabText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawableLeft="@drawable/ic_action_done"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:paddingRight="20dp"
+ android:text="Done (set in-code!)" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/res/layout/api_app_register_activity.xml
new file mode 100644
index 000000000..40b8f54d1
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/api_app_register_activity.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="8dp" >
+
+ <TextView
+ android:id="@+id/api_register_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="3dip"
+ android:text="@string/api_register_text"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote_api.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/api_app_settings_activity.xml b/OpenPGP-Keychain/res/layout/api_app_settings_activity.xml
new file mode 100644
index 000000000..c80d767ac
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/api_app_settings_activity.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:padding="8dp" >
+
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote_api.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
new file mode 100644
index 000000000..307da3efb
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:paddingBottom="3dip" >
+
+ <ImageView
+ android:id="@+id/api_app_settings_app_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="6dp"
+ android:src="@drawable/icon" />
+
+ <TextView
+ android:id="@+id/api_app_settings_app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/api_app_settings_app_icon"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:text="Name (set in-code)"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/api_app_settings_select_key_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/api_settings_select_key" />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp" >
+
+ <TextView
+ android:id="@+id/api_app_settings_user_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/api_settings_no_key"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/api_app_settings_user_id_rest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/api_app_settings_advanced_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_show_advanced" />
+
+ <LinearLayout
+ android:id="@+id/api_app_settings_advanced"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="invisible" >
+
+ <CheckBox
+ android:id="@+id/api_app_ascii_armor"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_asciiArmour" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/api_apps_adapter_list_item.xml b/OpenPGP-Keychain/res/layout/api_apps_adapter_list_item.xml
new file mode 100644
index 000000000..cb20a20af
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/api_apps_adapter_list_item.xml
@@ -0,0 +1,28 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:gravity="left"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/api_apps_adapter_item_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:src="@drawable/icon" />
+
+ <TextView
+ android:id="@+id/api_apps_adapter_item_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/api_apps_adapter_item_icon"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:text="Set in-code!"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
new file mode 100644
index 000000000..e4657c107
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/crypto_consumers_list_fragment"
+ android:name="org.sufficientlysecure.keychain.remote_api.RegisteredAppsListFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/decrypt.xml b/OpenPGP-Keychain/res/layout/decrypt.xml
index 4901536ae..84f1fc298 100644
--- a/OpenPGP-Keychain/res/layout/decrypt.xml
+++ b/OpenPGP-Keychain/res/layout/decrypt.xml
@@ -22,7 +22,6 @@
android:orientation="vertical" >
<ScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true" >
diff --git a/OpenPGP-Keychain/res/layout/register_crypto_consumer_activity.xml b/OpenPGP-Keychain/res/layout/register_crypto_consumer_activity.xml
deleted file mode 100644
index 24a64f1ac..000000000
--- a/OpenPGP-Keychain/res/layout/register_crypto_consumer_activity.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Register?" />
-
- <Button
- android:id="@+id/register_crypto_consumer_allow"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Allow access" />
-
- <Button
- android:id="@+id/register_crypto_consumer_disallow"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Disallow" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/api_app_settings.xml b/OpenPGP-Keychain/res/menu/api_app_settings.xml
new file mode 100644
index 000000000..1ee05f5de
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/api_app_settings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_api_settings_revoke"
+ android:showAsAction="never"
+ android:title="@string/api_settings_revoke"/>
+ <item
+ android:id="@+id/menu_api_settings_cancel"
+ android:showAsAction="never"
+ android:title="@string/api_settings_cancel"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index c698eab79..70f4993d7 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -30,6 +30,7 @@
<string name="title_createKey">Create Key</string>
<string name="title_editKey">Edit Key</string>
<string name="title_preferences">Preferences</string>
+ <string name="title_crypto_consumers">Registered Applications</string>
<string name="title_keyServerPreference">Key Server Preference</string>
<string name="title_changePassPhrase">Change Passphrase</string>
<string name="title_setPassPhrase">Set Passphrase</string>
@@ -87,6 +88,7 @@
<string name="menu_managePublicKeys">Manage Public Keys</string>
<string name="menu_manageSecretKeys">Manage Secret Keys</string>
<string name="menu_preferences">Settings</string>
+ <string name="menu_apiAppSettings">Registered Apps</string>
<string name="menu_importFromFile">Import from file</string>
<string name="menu_importFromQrCode">Import from QR Code</string>
<string name="menu_importFromNfc">Import from NFC</string>
@@ -362,4 +364,18 @@
<string name="intent_send_encrypt">OpenPGP: Encrypt</string>
<string name="intent_send_decrypt">OpenPGP: Decrypt</string>
+ <!-- Remote API -->
+ <string name="api_no_apps">No registered applications!</string>
+ <string name="api_settings_show_advanced">Show advanced settings…</string>
+ <string name="api_settings_hide_advanced">Hide advanced settings…</string>
+ <string name="api_settings_no_key">No key selected</string>
+ <string name="api_settings_select_key">Select key</string>
+ <string name="api_settings_save">Save</string>
+ <string name="api_settings_cancel">Cancel</string>
+ <string name="api_settings_revoke">Revoke access</string>
+ <string name="api_register_text">The following application requests access to OpenPGP Keychain\'s API.\n\nAllow permanent access?</string>
+ <string name="api_register_allow">Allow access</string>
+ <string name="api_register_disallow">Disallow access</string>
+ <string name="api_register_error_select_key">Please select a key!</string>
+
</resources>
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.aidl b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.aidl
new file mode 100644
index 000000000..7b67c8995
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.openintents.crypto;
+
+// Declare CryptoError so AIDL can find it and knows that it implements the parcelable protocol.
+parcelable CryptoError; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.java b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.java
new file mode 100644
index 000000000..265fe2633
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoError.java
@@ -0,0 +1,76 @@
+/*
+ * 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.openintents.crypto;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CryptoError implements Parcelable {
+ int errorId;
+ String message;
+
+ public CryptoError() {
+ }
+
+ public CryptoError(int errorId, String message) {
+ this.errorId = errorId;
+ this.message = message;
+ }
+
+ public CryptoError(CryptoError b) {
+ this.errorId = b.errorId;
+ this.message = b.message;
+ }
+
+ public int getErrorId() {
+ return errorId;
+ }
+
+ public void setErrorId(int errorId) {
+ this.errorId = errorId;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(errorId);
+ dest.writeString(message);
+ }
+
+ public static final Creator<CryptoError> CREATOR = new Creator<CryptoError>() {
+ public CryptoError createFromParcel(final Parcel source) {
+ CryptoError error = new CryptoError();
+ error.errorId = source.readInt();
+ error.message = source.readString();
+ return error;
+ }
+
+ public CryptoError[] newArray(final int size) {
+ return new CryptoError[size];
+ }
+ };
+}
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/CryptoServiceConnection.java b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoServiceConnection.java
new file mode 100644
index 000000000..d9e91f772
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoServiceConnection.java
@@ -0,0 +1,91 @@
+/*
+ * 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.openintents.crypto;
+
+import org.openintents.crypto.ICryptoService;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.util.Log;
+
+public class CryptoServiceConnection {
+ private Context mApplicationContext;
+
+ private ICryptoService mService;
+ private boolean bound;
+ private String cryptoProviderPackageName;
+
+ private static final String TAG = "CryptoConnection";
+
+ public CryptoServiceConnection(Context context, String cryptoProviderPackageName) {
+ mApplicationContext = context.getApplicationContext();
+ this.cryptoProviderPackageName = cryptoProviderPackageName;
+ }
+
+ public ICryptoService getService() {
+ return mService;
+ }
+
+ private ServiceConnection mCryptoServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = ICryptoService.Stub.asInterface(service);
+ Log.d(TAG, "connected to service");
+ bound = true;
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ Log.d(TAG, "disconnected from service");
+ bound = false;
+ }
+ };
+
+ /**
+ * If not already bound, bind!
+ *
+ * @return
+ */
+ public boolean bindToService() {
+ if (mService == null && !bound) { // if not already connected
+ try {
+ Log.d(TAG, "not bound yet");
+
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction("org.openintents.crypto.ICryptoService");
+ serviceIntent.setPackage(cryptoProviderPackageName);
+ mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
+ Context.BIND_AUTO_CREATE);
+
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Exception", e);
+ return false;
+ }
+ } else { // already connected
+ Log.d(TAG, "already bound... ");
+ return true;
+ }
+ }
+
+ public void unbindFromService() {
+ mApplicationContext.unbindService(mCryptoServiceConnection);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.aidl b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.aidl
new file mode 100644
index 000000000..1d39bac70
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.openintents.crypto;
+
+// Declare CryptoSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
+parcelable CryptoSignatureResult; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.java b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.java
new file mode 100644
index 000000000..e193b73b3
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/CryptoSignatureResult.java
@@ -0,0 +1,76 @@
+/*
+ * 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.openintents.crypto;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CryptoSignatureResult implements Parcelable {
+ String signatureUserId;
+
+ boolean signature;
+ boolean signatureSuccess;
+ boolean signatureUnknown;
+
+ public CryptoSignatureResult() {
+
+ }
+
+ public CryptoSignatureResult(String signatureUserId, boolean signature,
+ boolean signatureSuccess, boolean signatureUnknown) {
+ this.signatureUserId = signatureUserId;
+
+ this.signature = signature;
+ this.signatureSuccess = signatureSuccess;
+ this.signatureUnknown = signatureUnknown;
+ }
+
+ public CryptoSignatureResult(CryptoSignatureResult b) {
+ this.signatureUserId = b.signatureUserId;
+
+ this.signature = b.signature;
+ this.signatureSuccess = b.signatureSuccess;
+ this.signatureUnknown = b.signatureUnknown;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(signatureUserId);
+
+ dest.writeByte((byte) (signature ? 1 : 0));
+ dest.writeByte((byte) (signatureSuccess ? 1 : 0));
+ dest.writeByte((byte) (signatureUnknown ? 1 : 0));
+ }
+
+ public static final Creator<CryptoSignatureResult> CREATOR = new Creator<CryptoSignatureResult>() {
+ public CryptoSignatureResult createFromParcel(final Parcel source) {
+ CryptoSignatureResult vr = new CryptoSignatureResult();
+ vr.signatureUserId = source.readString();
+ vr.signature = source.readByte() == 1;
+ vr.signatureSuccess = source.readByte() == 1;
+ vr.signatureUnknown = source.readByte() == 1;
+ return vr;
+ }
+
+ public CryptoSignatureResult[] newArray(final int size) {
+ return new CryptoSignatureResult[size];
+ }
+ };
+}
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoCallback.aidl b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoCallback.aidl
new file mode 100644
index 000000000..1f910d4c6
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.openintents.crypto;
+
+import org.openintents.crypto.CryptoSignatureResult;
+import org.openintents.crypto.CryptoError;
+
+interface ICryptoCallback {
+
+ /**
+ * CryptoSignatureResult is only returned if the Callback was used from decryptAndVerify
+ *
+ */
+ oneway void onSuccess(in byte[] outputBytes, in CryptoSignatureResult signatureResult);
+
+
+ oneway void onError(in CryptoError error);
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl
new file mode 100644
index 000000000..c84ca28fb
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl
@@ -0,0 +1,82 @@
+/*
+ * 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.openintents.crypto;
+
+import org.openintents.crypto.ICryptoCallback;
+
+/**
+ * All methods are oneway, which means they are asynchronous and non-blocking.
+ * Results are returned to the callback, which has to be implemented on client side.
+ */
+interface ICryptoService {
+
+ /**
+ * Encrypt
+ *
+ * @param inputBytes
+ * Byte array you want to encrypt
+ * @param encryptionUserIds
+ * User Ids (emails) of recipients
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in ICryptoCallback callback);
+
+ /**
+ * Sign
+ *
+ * @param inputBytes
+ * Byte array you want to encrypt
+ * @param signatureUserId
+ * User Ids (email) of sender
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback);
+
+ /**
+ * Encrypt and sign
+ *
+ * @param inputBytes
+ * Byte array you want to encrypt
+ * @param encryptionUserIds
+ * User Ids (emails) of recipients
+ * @param signatureUserId
+ * User Ids (email) of sender
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback);
+
+ /**
+ * Decrypts and verifies given input bytes. If no signature is present this method
+ * will only decrypt.
+ *
+ * @param inputBytes
+ * Byte array you want to decrypt and verify
+ * @param callback
+ * Callback where to return results
+ */
+ oneway void decryptAndVerify(in byte[] inputBytes, in ICryptoCallback callback);
+
+ /**
+ * Opens setup using default parameters
+ *
+ */
+ oneway void setup(boolean asciiArmor, boolean newKeyring, String newKeyringUserId);
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
index 382f144d7..b0d60cf94 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
@@ -62,6 +62,7 @@ public final class Id {
public static final int import_from_file = 0x21070020;
public static final int import_from_qr_code = 0x21070021;
public static final int import_from_nfc = 0x21070022;
+ public static final int crypto_consumers = 0x21070023;
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoActivity.java
deleted file mode 100644
index b1d248e42..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoActivity.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.sufficientlysecure.keychain.crypto_provider;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-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.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-
-public class CryptoActivity extends SherlockFragmentActivity {
-
- public static final String ACTION_CACHE_PASSPHRASE = "org.sufficientlysecure.keychain.CRYPTO_CACHE_PASSPHRASE";
-
- public static final String EXTRA_SECRET_KEY_ID = "secret_key_id";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- handleActions(getIntent());
- }
-
- protected void handleActions(Intent intent) {
-
- // TODO: Important: Check if calling package is in list!
-
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- /**
- * com.android.crypto actions
- */
- 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);
- }
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java
deleted file mode 100644
index 1a57a457d..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.crypto_provider;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-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.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-
-import com.android.crypto.CryptoError;
-import com.android.crypto.ICryptoCallback;
-import com.android.crypto.ICryptoService;
-import com.android.crypto.CryptoSignatureResult;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-public class CryptoService extends Service {
- Context mContext;
-
- @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 mBinder;
- }
-
- 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!");
- // No passphrase cached for this ciphertext! Intent required to cache
- // passphrase!
- Intent intent = new Intent(CryptoActivity.ACTION_CACHE_PASSPHRASE);
- intent.putExtra(CryptoActivity.EXTRA_SECRET_KEY_ID, secretKeyId);
- callback.onActivityRequired(intent);
- return;
- }
-
- // 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.onDecryptVerifySuccess(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(byte[] inputBytes, String[] encryptionUserIds, ICryptoCallback callback)
- throws RemoteException {
- // TODO Auto-generated method stub
-
- }
-
- @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(byte[] inputBytes, ICryptoCallback callback)
- throws RemoteException {
- decryptAndVerifySafe(inputBytes, callback);
- }
-
- };
-
- // /**
- // * As we can not throw an exception through Android RPC, we assign identifiers to the
- // exception
- // * types.
- // *
- // * @param e
- // * @return
- // */
- // private int getExceptionId(Exception e) {
- // if (e instanceof NoSuchProviderException) {
- // return 0;
- // } else if (e instanceof NoSuchAlgorithmException) {
- // return 1;
- // } else if (e instanceof SignatureException) {
- // return 2;
- // } else if (e instanceof IOException) {
- // return 3;
- // } else if (e instanceof PgpGeneralException) {
- // return 4;
- // } else if (e instanceof PGPException) {
- // return 5;
- // } else {
- // return -1;
- // }
- // }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/RegisterActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/RegisterActivity.java
deleted file mode 100644
index 39b29f9a0..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/RegisterActivity.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.sufficientlysecure.keychain.crypto_provider;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-
-public class RegisterActivity extends Activity {
-
- public static final String ACTION_REGISTER = "com.android.crypto.REGISTER";
-
- public static final String EXTRA_PACKAGE_NAME = "packageName";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- handleActions(getIntent());
- }
-
- protected void handleActions(Intent intent) {
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- final String callingPackageName = this.getCallingPackage();
-
- /**
- * com.android.crypto actions
- */
- if (ACTION_REGISTER.equals(action)) {
- 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(RegisterActivity.this, callingPackageName);
- Intent data = new Intent();
- data.putExtra(EXTRA_PACKAGE_NAME, "org.sufficientlysecure.keychain");
-
- setResult(RESULT_OK, data);
- finish();
- }
- });
-
- disallowButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- setResult(RESULT_CANCELED);
- finish();
- }
- });
-
- } else {
- Log.e(Constants.TAG, "Please use com.android.crypto.REGISTER as intent action!");
- finish();
- }
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 6a71ca0ba..5dc561923 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -120,28 +120,33 @@ public class OtherHelper {
public static void checkPackagePermissionForActions(Activity activity, String pkgName,
String permName, String action, String[] restrictedActions) {
if (action != null) {
- PackageManager pkgManager = activity.getPackageManager();
-
- for (int i = 0; i < restrictedActions.length; i++) {
- if (restrictedActions[i].equals(action)) {
- if (pkgName != null
- && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName
- .equals(Constants.PACKAGE_NAME))) {
- Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action "
- + action + " was granted!");
- } else {
- String error = pkgName + " does NOT have permission " + permName
- + ". Action " + action + " was NOT granted!";
- Log.e(Constants.TAG, error);
- Toast.makeText(activity, activity.getString(R.string.errorMessage, error),
- Toast.LENGTH_LONG).show();
-
- // end activity
- activity.setResult(Activity.RESULT_CANCELED, null);
- activity.finish();
- }
- }
- }
+// PackageManager pkgManager = activity.getPackageManager();
+
+// for (int i = 0; i < restrictedActions.length; i++) {
+// if (restrictedActions[i].equals(action)) {
+// if (pkgName != null
+// && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName
+// .equals(Constants.PACKAGE_NAME))) {
+// Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action "
+// + action + " was granted!");
+// } else {
+// String error = pkgName + " does NOT have permission " + permName
+// + ". Action " + action + " was NOT granted!";
+// Log.e(Constants.TAG, error);
+// Toast.makeText(activity, activity.getString(R.string.errorMessage, error),
+// Toast.LENGTH_LONG).show();
+//
+// // end activity
+// activity.setResult(Activity.RESULT_CANCELED, null);
+// activity.finish();
+// }
+// }
+// }
+
+ // TODO: currently always cancels! THis is the old API
+ // end activity
+ activity.setResult(Activity.RESULT_CANCELED, null);
+ activity.finish();
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 46928c6fa..93ee50a5e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -43,7 +43,7 @@ public class KeychainContract {
String CREATION = "creation";
String EXPIRY = "expiry";
String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
- String KEY_DATA = "key_data"; // PGPPublicKey / PGPSecretKey blob
+ String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob
String RANK = "rank";
}
@@ -53,8 +53,13 @@ public class KeychainContract {
String RANK = "rank";
}
- interface CryptoConsumersColumns {
+ interface ApiAppsColumns {
String PACKAGE_NAME = "package_name";
+ String KEY_ID = "key_id"; // not a database id
+ String ASCII_ARMOR = "ascii_armor";
+ String ENCRYPTION_ALGORITHM = "encryption_algorithm";
+ String HASH_ALORITHM = "hash_algorithm";
+ String COMPRESSION = "compression";
}
public static final class KeyTypes {
@@ -82,7 +87,8 @@ public class KeychainContract {
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_KEYS = "keys";
- public static final String BASE_CRYPTO_CONSUMERS = "crypto_consumers";
+ public static final String BASE_API_APPS = "api_apps";
+ public static final String PATH_BY_PACKAGE_NAME = "package_name";
public static class KeyRings implements KeyRingsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
@@ -213,15 +219,24 @@ public class KeychainContract {
}
}
- public static class CryptoConsumers implements CryptoConsumersColumns, BaseColumns {
+ public static class ApiApps implements ApiAppsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_CRYPTO_CONSUMERS).build();
+ .appendPath(BASE_API_APPS).build();
/** Use if multiple items get returned */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.crypto_consumers";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
/** Use if a single item is returned */
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.crypto_consumers";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps";
+
+ public static Uri buildIdUri(String rowId) {
+ return CONTENT_URI.buildUpon().appendPath(rowId).build();
+ }
+
+ public static Uri buildByPackageNameUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName)
+ .build();
+ }
}
public static class DataStream {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index f30292b52..6da96f45f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -18,7 +18,7 @@
package org.sufficientlysecure.keychain.provider;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.KeychainContract.CryptoConsumersColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
@@ -37,7 +37,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String KEY_RINGS = "key_rings";
String KEYS = "keys";
String USER_IDS = "user_ids";
- String CRYPTO_CONSUMERS = "crypto_consumers";
+ String API_APPS = "api_apps";
}
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
@@ -64,10 +64,14 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
+ BaseColumns._ID + ") ON DELETE CASCADE)";
- private static final String CREATE_CRYPTO_CONSUMERS = "CREATE TABLE IF NOT EXISTS "
- + Tables.CRYPTO_CONSUMERS + " (" + BaseColumns._ID
- + " INTEGER PRIMARY KEY AUTOINCREMENT, " + CryptoConsumersColumns.PACKAGE_NAME
- + " TEXT UNIQUE)";
+ private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS "
+ + Tables.API_APPS + " (" + BaseColumns._ID
+ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ApiAppsColumns.PACKAGE_NAME
+ + " TEXT UNIQUE, " + ApiAppsColumns.KEY_ID + " INT64, "
+ + ApiAppsColumns.ASCII_ARMOR + " INTEGER, "
+ + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsColumns.COMPRESSION + " INTEGER)";
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -80,7 +84,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEY_RINGS);
db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_IDS);
- db.execSQL(CREATE_CRYPTO_CONSUMERS);
+ db.execSQL(CREATE_API_APPS);
}
@Override
@@ -108,7 +112,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
break;
case 4:
- db.execSQL(CREATE_CRYPTO_CONSUMERS);
+ db.execSQL(CREATE_API_APPS);
default:
break;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 49286b9ce..edb82e632 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,11 @@
package org.sufficientlysecure.keychain.provider;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.HashMap;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.KeychainContract.CryptoConsumers;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
@@ -44,7 +42,6 @@ import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -81,7 +78,9 @@ public class KeychainProvider extends ContentProvider {
private static final int SECRET_KEY_RING_USER_ID = 221;
private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
- private static final int CRYPTO_CONSUMERS = 301;
+ private static final int API_APPS = 301;
+ private static final int API_APPS_BY_ROW_ID = 302;
+ private static final int API_APPS_BY_PACKAGE_NAME = 303;
// private static final int DATA_STREAM = 401;
@@ -227,9 +226,12 @@ public class KeychainProvider extends ContentProvider {
SECRET_KEY_RING_USER_ID_BY_ROW_ID);
/**
- * Crypto Consumers
+ * API apps
*/
- matcher.addURI(authority, KeychainContract.BASE_CRYPTO_CONSUMERS, CRYPTO_CONSUMERS);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
+ + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
/**
* data stream
@@ -290,8 +292,12 @@ public class KeychainProvider extends ContentProvider {
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
return UserIds.CONTENT_ITEM_TYPE;
- case CRYPTO_CONSUMERS:
- return CryptoConsumers.CONTENT_TYPE;
+ case API_APPS:
+ return ApiApps.CONTENT_TYPE;
+
+ case API_APPS_BY_ROW_ID:
+ case API_APPS_BY_PACKAGE_NAME:
+ return ApiApps.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
@@ -600,10 +606,23 @@ public class KeychainProvider extends ContentProvider {
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
-
- case CRYPTO_CONSUMERS:
- qb.setTables(Tables.CRYPTO_CONSUMERS);
-
+
+ case API_APPS:
+ qb.setTables(Tables.API_APPS);
+
+ break;
+ case API_APPS_BY_ROW_ID:
+ qb.setTables(Tables.API_APPS);
+
+ qb.appendWhere(BaseColumns._ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
+
+ break;
+ case API_APPS_BY_PACKAGE_NAME:
+ qb.setTables(Tables.API_APPS);
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+
break;
default:
@@ -653,6 +672,7 @@ public class KeychainProvider extends ContentProvider {
rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_KEY:
@@ -660,11 +680,13 @@ public class KeychainProvider extends ContentProvider {
rowId = db.insertOrThrow(Tables.KEYS, null, values);
rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_USER_ID:
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case SECRET_KEY_RING:
@@ -672,6 +694,7 @@ public class KeychainProvider extends ContentProvider {
rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case SECRET_KEY_RING_KEY:
@@ -679,6 +702,7 @@ public class KeychainProvider extends ContentProvider {
rowId = db.insertOrThrow(Tables.KEYS, null, values);
rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case SECRET_KEY_RING_USER_ID:
@@ -686,13 +710,17 @@ public class KeychainProvider extends ContentProvider {
rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
break;
+ case API_APPS:
+ rowId = db.insertOrThrow(Tables.API_APPS, null, values);
+ rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+
+ break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
} catch (SQLiteConstraintException e) {
Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?");
@@ -720,6 +748,7 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.KEY_RINGS,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
@@ -728,24 +757,33 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.KEY_RINGS,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID:
count = db.delete(Tables.KEYS,
buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
selectionArgs);
break;
+ case API_APPS_BY_ROW_ID:
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
+ selectionArgs);
+ break;
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
+ selectionArgs);
+ break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
return count;
}
@@ -771,6 +809,8 @@ public class KeychainProvider extends ContentProvider {
values,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
selection), selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
@@ -781,6 +821,8 @@ public class KeychainProvider extends ContentProvider {
values,
buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
selection), selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID:
@@ -788,19 +830,28 @@ public class KeychainProvider extends ContentProvider {
.update(Tables.KEYS, values,
buildDefaultKeysSelection(uri, getKeyType(match), selection),
selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
break;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
count = db.update(Tables.USER_IDS, values,
buildDefaultUserIdsSelection(uri, selection), selectionArgs);
break;
+ case API_APPS_BY_ROW_ID:
+ count = db.update(Tables.API_APPS, values,
+ buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
+ break;
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.update(Tables.API_APPS, values,
+ buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
} catch (SQLiteConstraintException e) {
Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?");
@@ -883,6 +934,29 @@ public class KeychainProvider extends ContentProvider {
return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection;
}
+ /**
+ * Build default selection statement for API apps. If no extra selection is specified only build
+ * where clause with rowId
+ *
+ * @param uri
+ * @param selection
+ * @return
+ */
+ private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) {
+ String lastPathSegment = uri.getLastPathSegment();
+
+ String andSelection = "";
+ if (!TextUtils.isEmpty(selection)) {
+ andSelection = " AND (" + selection + ")";
+ }
+
+ if (packageSelection) {
+ return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection;
+ } else {
+ return BaseColumns._ID + "=" + lastPathSegment + andSelection;
+ }
+ }
+
// @Override
// public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// int match = mUriMatcher.match(uri);
@@ -899,10 +973,12 @@ public class KeychainProvider extends ContentProvider {
* updated, or deleted
*/
private void sendBroadcastDatabaseChange(int keyType, String contentItemType) {
- Intent intent = new Intent();
- intent.setAction(ACTION_BROADCAST_DATABASE_CHANGE);
- intent.putExtra(EXTRA_BROADCAST_KEY_TYPE, keyType);
- intent.putExtra(EXTRA_BROADCAST_CONTENT_ITEM_TYPE, contentItemType);
- getContext().sendBroadcast(intent, Constants.PERMISSION_ACCESS_API);
+ // TODO: Disabled, old API
+ // Intent intent = new Intent();
+ // intent.setAction(ACTION_BROADCAST_DATABASE_CHANGE);
+ // intent.putExtra(EXTRA_BROADCAST_KEY_TYPE, keyType);
+ // intent.putExtra(EXTRA_BROADCAST_CONTENT_ITEM_TYPE, contentItemType);
+ //
+ // getContext().sendBroadcast(intent, Constants.PERMISSION_ACCESS_API);
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 57d3b54d6..d0fcfe999 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -31,11 +31,12 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.PgpConversionHelper;
import org.sufficientlysecure.keychain.helper.PgpHelper;
import org.sufficientlysecure.keychain.helper.PgpMain;
-import org.sufficientlysecure.keychain.provider.KeychainContract.CryptoConsumers;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.remote_api.AppSettings;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -718,13 +719,13 @@ public class ProviderHelper {
return cursor;
}
- public static ArrayList<String> getCryptoConsumers(Context context) {
- Cursor cursor = context.getContentResolver().query(CryptoConsumers.CONTENT_URI, null, null,
- null, null);
+ public static ArrayList<String> getRegisteredApiApps(Context context) {
+ Cursor cursor = context.getContentResolver().query(ApiApps.CONTENT_URI, null, null, null,
+ null);
ArrayList<String> packageNames = new ArrayList<String>();
if (cursor != null) {
- int packageNameCol = cursor.getColumnIndex(CryptoConsumers.PACKAGE_NAME);
+ int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
if (cursor.moveToFirst()) {
do {
packageNames.add(cursor.getString(packageNameCol));
@@ -739,9 +740,53 @@ public class ProviderHelper {
return packageNames;
}
- public static void addCryptoConsumer(Context context, String packageName) {
+ private static void contentValueForApiApps() {
+
+ }
+
+ public static void insertApiApp(Context context, AppSettings appSettings) {
ContentValues values = new ContentValues();
- values.put(CryptoConsumers.PACKAGE_NAME, packageName);
- context.getContentResolver().insert(CryptoConsumers.CONTENT_URI, values);
+ values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
+ values.put(ApiApps.KEY_ID, appSettings.getKeyId());
+ values.put(ApiApps.ASCII_ARMOR, appSettings.isAsciiArmor());
+ // TODO: other parameters
+ context.getContentResolver().insert(ApiApps.CONTENT_URI, values);
+ }
+
+ public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
+ final ContentValues cv = new ContentValues();
+ cv.put(KeychainContract.ApiApps.KEY_ID, appSettings.getKeyId());
+
+ cv.put(KeychainContract.ApiApps.ASCII_ARMOR, appSettings.isAsciiArmor());
+ // TODO: other parameters
+
+ if (context.getContentResolver().update(uri, cv, null, null) <= 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ public static AppSettings getApiAppSettings(Context context, Uri uri) {
+ AppSettings settings = new AppSettings();
+ Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
+ if (cur == null) {
+ return null;
+ }
+ if (cur.moveToFirst()) {
+ settings.setPackageName(cur.getString(cur
+ .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+
+ settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
+
+ settings.setAsciiArmor(cur.getInt(cur
+ .getColumnIndexOrThrow(KeychainContract.ApiApps.ASCII_ARMOR)) == 1);
+
+ settings.setPackageName(cur.getString(cur
+ .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+
+ settings.setPackageName(cur.getString(cur
+ .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ }
+
+ return settings;
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java
new file mode 100644
index 000000000..fe251a5e7
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettings.java
@@ -0,0 +1,70 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.Id;
+
+public class AppSettings {
+ private String packageName;
+ private long keyId = Id.key.none;
+ private boolean asciiArmor;
+ private int encryptionAlgorithm = 7; // AES-128
+ private int hashAlgorithm = 10; // SHA-512
+ private int compression = 2; // zlib
+
+ public AppSettings() {
+
+ }
+
+ public AppSettings(String packageName) {
+ super();
+ this.packageName = packageName;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+ }
+
+ public long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(long scretKeyId) {
+ this.keyId = scretKeyId;
+ }
+
+ public boolean isAsciiArmor() {
+ return asciiArmor;
+ }
+
+ public void setAsciiArmor(boolean asciiArmor) {
+ this.asciiArmor = asciiArmor;
+ }
+
+ public int getEncryptionAlgorithm() {
+ return encryptionAlgorithm;
+ }
+
+ public void setEncryptionAlgorithm(int encryptionAlgorithm) {
+ this.encryptionAlgorithm = encryptionAlgorithm;
+ }
+
+ public int getHashAlgorithm() {
+ return hashAlgorithm;
+ }
+
+ public void setHashAlgorithm(int hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
+
+ public int getCompression() {
+ return compression;
+ }
+
+ public void setCompression(int compression) {
+ this.compression = compression;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java
new file mode 100644
index 000000000..155369601
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsActivity.java
@@ -0,0 +1,107 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+
+public class AppSettingsActivity extends SherlockFragmentActivity {
+ private Uri mAppUri;
+
+ private AppSettingsFragment settingsFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Inflate a "Done" custom action bar view to serve as the "Up" affordance.
+ final LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext()
+ .getSystemService(LAYOUT_INFLATER_SERVICE);
+ final View customActionBarView = inflater
+ .inflate(R.layout.actionbar_custom_view_done, null);
+
+ ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text))
+ .setText(R.string.api_settings_save);
+ customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // "Done"
+ save();
+ }
+ });
+
+ // Show the custom action bar view and hide the normal Home icon and title.
+ final ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM
+ | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
+ actionBar.setCustomView(customActionBarView);
+
+ setContentView(R.layout.api_app_settings_activity);
+
+ settingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ Intent intent = getIntent();
+ mAppUri = intent.getData();
+ if (mAppUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mAppUri);
+ loadData(mAppUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.api_app_settings, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_api_settings_revoke:
+ revokeAccess();
+ return true;
+ case R.id.menu_api_settings_cancel:
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Uri appUri) {
+ AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
+ settingsFragment.setAppSettings(settings);
+ }
+
+ private void revokeAccess() {
+ if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ throw new RuntimeException();
+ }
+ finish();
+ }
+
+ private void save() {
+ ProviderHelper.updateApiApp(this, settingsFragment.getAppSettings(), mAppUri);
+
+ finish();
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java
new file mode 100644
index 000000000..1bb447a7d
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/AppSettingsFragment.java
@@ -0,0 +1,208 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.PgpHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.SelectSecretKeyActivity;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class AppSettingsFragment extends Fragment {
+
+ // model
+ private AppSettings appSettings;
+
+ // view
+ private LinearLayout mAdvancedSettingsContainer;
+ private Button mAdvancedSettingsButton;
+ private TextView mAppNameView;
+ private ImageView mAppIconView;
+ private TextView mKeyUserId;
+ private TextView mKeyUserIdRest;
+ private Button mSelectKeyButton;
+ private CheckBox mAsciiArmorCheckBox;
+
+ public AppSettings getAppSettings() {
+ return appSettings;
+ }
+
+ public void setAppSettings(AppSettings appSettings) {
+ this.appSettings = appSettings;
+ setPackage(appSettings.getPackageName());
+ updateSelectedKeyView(appSettings.getKeyId());
+ mAsciiArmorCheckBox.setChecked(appSettings.isAsciiArmor());
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
+ initView(view);
+ return view;
+ }
+
+ private void initView(View view) {
+ mAdvancedSettingsButton = (Button) view.findViewById(R.id.api_app_settings_advanced_button);
+ mAdvancedSettingsContainer = (LinearLayout) view
+ .findViewById(R.id.api_app_settings_advanced);
+
+ mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
+ mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
+ mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id);
+ mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest);
+ mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button);
+ mAsciiArmorCheckBox = (CheckBox) view.findViewById(R.id.api_app_ascii_armor);
+
+ mSelectKeyButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ selectSecretKey();
+ }
+ });
+
+ mAsciiArmorCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ appSettings.setAsciiArmor(isChecked);
+ }
+ });
+
+ final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
+ visibleAnimation.setDuration(250);
+ final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
+ invisibleAnimation.setDuration(250);
+
+ // TODO: Better: collapse/expand animation
+ // final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
+ // Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
+ // Animation.RELATIVE_TO_SELF, 0.0f);
+ // animation2.setDuration(150);
+
+ mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
+ mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
+ mAdvancedSettingsContainer.setVisibility(View.INVISIBLE);
+ mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced);
+ } else {
+ mAdvancedSettingsContainer.startAnimation(visibleAnimation);
+ mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
+ mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ private void selectSecretKey() {
+ Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
+ startActivityForResult(intent, Id.request.secret_keys);
+ }
+
+ private void setPackage(String packageName) {
+ PackageManager pm = getActivity().getApplicationContext().getPackageManager();
+
+ // get application name and icon from package manager
+ String appName = null;
+ Drawable appIcon = null;
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
+
+ appName = (String) pm.getApplicationLabel(ai);
+ appIcon = pm.getApplicationIcon(ai);
+ } catch (final NameNotFoundException e) {
+ // fallback
+ appName = packageName;
+ }
+ mAppNameView.setText(appName);
+ mAppIconView.setImageDrawable(appIcon);
+ }
+
+ private void updateSelectedKeyView(long secretKeyId) {
+ if (secretKeyId == Id.key.none) {
+ mKeyUserId.setText(R.string.api_settings_no_key);
+ mKeyUserIdRest.setText("");
+ } else {
+ String uid = getResources().getString(R.string.unknownUserId);
+ String uidExtra = "";
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
+ getActivity(), secretKeyId);
+ if (keyRing != null) {
+ PGPSecretKey key = PgpHelper.getMasterKey(keyRing);
+ if (key != null) {
+ String userId = PgpHelper.getMainUserIdSafe(getActivity(), key);
+ String chunks[] = userId.split(" <", 2);
+ uid = chunks[0];
+ if (chunks.length > 1) {
+ uidExtra = "<" + chunks[1];
+ }
+ }
+ }
+ mKeyUserId.setText(uid);
+ mKeyUserIdRest.setText(uidExtra);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(Constants.TAG, "onactivityresult " + requestCode + " " + resultCode);
+ switch (requestCode) {
+
+ case Id.request.secret_keys: {
+ long secretKeyId;
+ if (resultCode == Activity.RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
+
+ } else {
+ secretKeyId = Id.key.none;
+ }
+ appSettings.setKeyId(secretKeyId);
+ updateSelectedKeyView(secretKeyId);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+}
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..125967c66
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoService.java
@@ -0,0 +1,409 @@
+/*
+ * 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 java.util.prefs.Preferences;
+
+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.KeychainContract;
+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.net.Uri;
+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())) {
+
+ // this binder can only be used from OpenPGP Keychain
+ if (isCallerAllowed(true)) {
+ return mBinderServiceActivity;
+ } else {
+ Log.e(Constants.TAG, "This binder can only be used from " + Constants.PACKAGE_NAME);
+ return null;
+ }
+ } else {
+ return mBinder;
+ }
+ }
+
+ private String getCachedPassphrase(long keyId) {
+ String passphrase = PassphraseCacheService.getCachedPassphrase(mContext, keyId);
+
+ if (passphrase == null) {
+ Log.d(Constants.TAG, "No passphrase! Activity required!");
+
+ // start passphrase dialog
+ Bundle extras = new Bundle();
+ extras.putLong(CryptoServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
+ pauseQueueAndStartServiceActivity(CryptoServiceActivity.ACTION_CACHE_PASSPHRASE, extras);
+ }
+
+ return passphrase;
+ }
+
+ private synchronized void encryptSafe(byte[] inputBytes, String[] encryptionUserIds,
+ AppSettings appSettings, 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();
+
+ String passphrase = getCachedPassphrase(appSettings.getKeyId());
+
+ PgpMain.encryptAndSign(mContext, null, inputData, outputStream,
+ appSettings.isAsciiArmor(), appSettings.getCompression(), new long[] {},
+ "test", appSettings.getEncryptionAlgorithm(), Id.key.none,
+ appSettings.getHashAlgorithm(), true, passphrase);
+
+ // PgpMain.encryptAndSign(this, this, inputData, outputStream,
+ // appSettings.isAsciiArmor(),
+ // appSettings.getCompression(), encryptionKeyIds, encryptionPassphrase,
+ // appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
+ // appSettings.getHashAlgorithm(), true, passphrase);
+
+ outputStream.close();
+
+ byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
+
+ // return over handler on client side
+ callback.onSuccess(outputBytes, null);
+ } 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();
+
+ // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
+ // app, Fix this?
+ 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 = getCachedPassphrase(secretKeyId);
+
+ // 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 {
+
+ final AppSettings settings = getAppSettings();
+
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ encryptSafe(inputBytes, encryptionUserIds, settings, 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 onRegistered(boolean success, String packageName) throws RemoteException {
+
+ if (success) {
+ // resume threads
+ if (isPackageAllowed(packageName, false)) {
+ mThreadPool.resume();
+ } else {
+ // TODO: should not happen?
+ }
+ } else {
+ // TODO
+ mPoolQueue.clear();
+ }
+
+ }
+
+ @Override
+ public void onCachedPassphrase(boolean success) throws RemoteException {
+
+ }
+
+ };
+
+ private void checkAndEnqueue(Runnable r) {
+ if (isCallerAllowed(false)) {
+ 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(CryptoServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
+ pauseQueueAndStartServiceActivity(CryptoServiceActivity.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.
+ *
+ * @param allowOnlySelf
+ * allow only Keychain app itself
+ * @return true if process is allowed to use this service
+ */
+ private boolean isCallerAllowed(boolean allowOnlySelf) {
+ 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, allowOnlySelf)) {
+ return true;
+ }
+ }
+
+ Log.d(Constants.TAG, "Caller is NOT allowed!");
+ return false;
+ }
+
+ private AppSettings getAppSettings() {
+ 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];
+
+ Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
+
+ AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
+
+ return settings;
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if packageName is a registered app for the API.
+ *
+ * @param packageName
+ * @param allowOnlySelf
+ * allow only Keychain app itself
+ * @return
+ */
+ private boolean isPackageAllowed(String packageName, boolean allowOnlySelf) {
+ Log.d(Constants.TAG, "packageName: " + packageName);
+
+ ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(mContext);
+ Log.d(Constants.TAG, "allowed: " + allowedPkgs);
+
+ // check if package is allowed to use our service
+ if (allowedPkgs.contains(packageName) && (!allowOnlySelf)) {
+ 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(), CryptoServiceActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setAction(action);
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ getApplication().startActivity(intent);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoServiceActivity.java
new file mode 100644
index 000000000..06c64559a
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/CryptoServiceActivity.java
@@ -0,0 +1,267 @@
+/*
+ * 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.Id;
+import org.sufficientlysecure.keychain.R;
+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 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.os.RemoteException;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+public class CryptoServiceActivity extends SherlockFragmentActivity {
+
+ public static final String ACTION_REGISTER = "org.sufficientlysecure.keychain.remote_api.REGISTER";
+ public static final String ACTION_CACHE_PASSPHRASE = "org.sufficientlysecure.keychain.remote_api.CRYPTO_CACHE_PASSPHRASE";
+
+ public static final String EXTRA_SECRET_KEY_ID = "secretKeyId";
+ public static final String EXTRA_PACKAGE_NAME = "packageName";
+
+ private IServiceActivityCallback mServiceCallback;
+ private boolean mServiceBound;
+
+ // view
+ AppSettingsFragment settingsFragment;
+
+ private ServiceConnection mServiceActivityConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mServiceCallback = IServiceActivityCallback.Stub.asInterface(service);
+ Log.d(Constants.TAG, "connected to ICryptoServiceActivity");
+ mServiceBound = true;
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mServiceCallback = null;
+ Log.d(Constants.TAG, "disconnected from ICryptoServiceActivity");
+ mServiceBound = false;
+ }
+ };
+
+ /**
+ * If not already bound, bind!
+ *
+ * @return
+ */
+ public boolean bindToService() {
+ if (mServiceCallback == null && !mServiceBound) { // if not already connected
+ try {
+ Log.d(Constants.TAG, "not bound yet");
+
+ Intent serviceIntent = new Intent();
+ serviceIntent
+ .setAction("org.sufficientlysecure.keychain.crypto_provider.IServiceActivityCallback");
+ 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);
+
+ // Inflate a "Done"/"Cancel" custom action bar view
+ final LayoutInflater inflater = (LayoutInflater) getSupportActionBar()
+ .getThemedContext().getSystemService(LAYOUT_INFLATER_SERVICE);
+ final View customActionBarView = inflater.inflate(
+ R.layout.actionbar_custom_view_done_cancel, null);
+
+ ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text))
+ .setText(R.string.api_register_allow);
+ customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Allow
+
+ // user needs to select a key!
+ if (settingsFragment.getAppSettings().getKeyId() == Id.key.none) {
+ Toast.makeText(CryptoServiceActivity.this,
+ R.string.api_register_error_select_key, Toast.LENGTH_LONG)
+ .show();
+ } else {
+ ProviderHelper.insertApiApp(CryptoServiceActivity.this,
+ settingsFragment.getAppSettings());
+
+ try {
+ mServiceCallback.onRegistered(true, packageName);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "ServiceActivity");
+ }
+ finish();
+ }
+ }
+ });
+ ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
+ .setText(R.string.api_register_disallow);
+ customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Disallow
+ try {
+ mServiceCallback.onRegistered(false, packageName);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "ServiceActivity");
+ }
+ finish();
+ }
+ });
+
+ // Show the custom action bar view and hide the normal Home icon and title.
+ final ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME
+ | ActionBar.DISPLAY_SHOW_TITLE);
+ actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+
+ setContentView(R.layout.api_app_register_activity);
+
+ settingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ AppSettings settings = new AppSettings(packageName);
+ settingsFragment.setAppSettings(settings);
+
+
+ // TODO: handle if app is already registered
+ // LinearLayout layoutRegister = (LinearLayout)
+ // findViewById(R.id.register_crypto_consumer_register_layout);
+ // LinearLayout layoutEdit = (LinearLayout)
+ // findViewById(R.id.register_crypto_consumer_edit_layout);
+ //
+ // // if already registered show edit buttons
+ // ArrayList<String> allowedPkgs = ProviderHelper.getCryptoConsumers(this);
+ // if (allowedPkgs.contains(packageName)) {
+ // Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
+ // layoutRegister.setVisibility(View.GONE);
+ // layoutEdit.setVisibility(View.VISIBLE);
+ // } else {
+ // layoutRegister.setVisibility(View.VISIBLE);
+ // layoutEdit.setVisibility(View.GONE);
+ // }
+
+ } else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
+ long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
+
+ showPassphraseDialog(secretKeyId);
+ } else {
+ Log.e(Constants.TAG, "Wrong action!");
+ 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) {
+ try {
+ mServiceCallback.onCachedPassphrase(true);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "ServiceActivity");
+ }
+ 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);
+ }
+ }
+}
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..e9949e1ae
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/IServiceActivityCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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 onRegistered(in boolean success, in String packageName);
+
+ oneway void onCachedPassphrase(in boolean success);
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java
new file mode 100644
index 000000000..9bf66a90a
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class RegisteredAppsAdapter extends CursorAdapter {
+
+ private LayoutInflater mInflater;
+ private PackageManager pm;
+
+ public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ pm = context.getApplicationContext().getPackageManager();
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
+ ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
+
+ String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
+ if (packageName != null) {
+ // get application name
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
+
+ text.setText(pm.getApplicationLabel(ai));
+ icon.setImageDrawable(pm.getApplicationIcon(ai));
+ } catch (final NameNotFoundException e) {
+ // fallback
+ text.setText(packageName);
+ }
+ } else {
+ // fallback
+ text.setText(packageName);
+ }
+
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.java
new file mode 100644
index 000000000..f5487e2a3
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListActivity.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 RegisteredAppsListActivity extends SherlockFragmentActivity {
+ private ActionBar mActionBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActionBar = getSupportActionBar();
+
+ setContentView(R.layout.api_apps_list_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/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java
new file mode 100644
index 000000000..d1e52a2d6
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/remote_api/RegisteredAppsListFragment.java
@@ -0,0 +1,90 @@
+package org.sufficientlysecure.keychain.remote_api;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+
+import android.content.ContentUris;
+import android.content.Intent;
+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.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import com.actionbarsherlock.app.SherlockListFragment;
+
+public class RegisteredAppsListFragment extends SherlockListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ // This is the Adapter being used to display the list's data.
+ RegisteredAppsAdapter mAdapter;
+
+ // If non-null, this is the current filter the user has provided.
+ String mCurFilter;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ // edit app settings
+ Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
+ intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
+ startActivity(intent);
+ }
+ });
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.api_no_apps));
+
+ // 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 RegisteredAppsAdapter(getActivity(), null, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ // These are the Contacts rows that we will retrieve.
+ static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID,
+ ApiApps.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 = ApiApps.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,
+ ApiApps.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/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index d11b8e92a..343e5fbc3 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -48,6 +48,12 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
+/**
+ * This service runs in its own process, but is available to all other processes as the main
+ * passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
+ * convenience.
+ *
+ */
public class PassphraseCacheService extends Service {
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
@@ -74,9 +80,9 @@ public class PassphraseCacheService extends Service {
Context mContext;
/**
- * This caches a new passphrase by sending a new command to the service. An android service is
- * only run once. Thus, when the service is already started, new commands just add new events to
- * the alarm manager for new passphrases to let them timeout in the future.
+ * This caches a new passphrase in memory by sending a new command to the service. An android
+ * service is only run once. Thus, when the service is already started, new commands just add
+ * new events to the alarm manager for new passphrases to let them timeout in the future.
*
* @param context
* @param keyId
@@ -95,21 +101,23 @@ public class PassphraseCacheService extends Service {
}
/**
- * Gets a cached passphrase from memory, blocking method
+ * Gets a cached passphrase from memory by sending an intent to the service. This method is
+ * designed to wait until the service returns the passphrase.
*
* @param context
* @param keyId
- * @return
+ * @return passphrase or null (if no passphrase is cached for this keyId)
*/
public static String getCachedPassphrase(Context context, long keyId) {
Log.d(TAG, "getCachedPassphrase() get masterKeyId for " + keyId);
+
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_GET);
final Object mutex = new Object();
final Bundle returnBundle = new Bundle();
- HandlerThread handlerThread = new HandlerThread("getPassphrase");
+ HandlerThread handlerThread = new HandlerThread("getPassphraseThread");
handlerThread.start();
Handler returnHandler = new Handler(handlerThread.getLooper()) {
@Override
@@ -121,6 +129,7 @@ public class PassphraseCacheService extends Service {
synchronized (mutex) {
mutex.notify();
}
+ // quit handlerThread
getLooper().quit();
}
};
@@ -147,6 +156,12 @@ public class PassphraseCacheService extends Service {
}
}
+ /**
+ * Internal implementation to get cached passphrase.
+ *
+ * @param keyId
+ * @return
+ */
private String getCachedPassphraseImpl(long keyId) {
Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId);
@@ -163,20 +178,20 @@ public class PassphraseCacheService extends Service {
}
masterKeyId = masterKey.getKeyID();
}
- Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId" + masterKeyId);
+ Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
// get cached passphrase
String cachedPassphrase = mPassphraseCache.get(masterKeyId);
if (cachedPassphrase == null) {
- // TODO: fix!
- // check if secret key has a passphrase
- // if (!hasPassphrase(context, masterKeyId)) {
- // // cache empty passphrase
- // addCachedPassphrase(context, masterKeyId, "");
- // return "";
- // } else {
- return null;
- // }
+ // if key has no passphrase -> cache and return empty passphrase
+ if (!hasPassphrase(this, masterKeyId)) {
+ Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
+
+ addCachedPassphrase(this, masterKeyId, "");
+ return "";
+ } else {
+ return null;
+ }
}
// set it again to reset the cache life cycle
Log.d(TAG, "Cache passphrase again when getting it!");
@@ -196,17 +211,10 @@ public class PassphraseCacheService extends Service {
try {
PGPSecretKey secretKey = PgpHelper.getMasterKey(ProviderHelper
.getPGPSecretKeyRingByKeyId(context, secretKeyId));
-
- Log.d(Constants.TAG, "Check if key has no passphrase...");
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
"SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
if (testKey != null) {
- Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!");
-
- // cache empty passphrase
- PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), "");
-
return false;
}
} catch (PGPException e) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/.SelectSecretKeyFragment.java.kate-swp b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/.SelectSecretKeyFragment.java.kate-swp
new file mode 100644
index 000000000..c72aa8947
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/.SelectSecretKeyFragment.java.kate-swp
Binary files differ
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
index 447801e55..3f5ca536f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote_api.RegisteredAppsListActivity;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockActivity;
@@ -80,6 +81,9 @@ public class MainActivity extends SherlockActivity {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
.setIcon(R.drawable.ic_menu_settings)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(0, Id.menu.option.crypto_consumers, 0, R.string.menu_apiAppSettings)
+ .setIcon(R.drawable.ic_menu_settings)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
return true;
}
@@ -91,6 +95,10 @@ public class MainActivity extends SherlockActivity {
startActivity(new Intent(this, PreferencesActivity.class));
return true;
+ case Id.menu.option.crypto_consumers:
+ startActivity(new Intent(this, RegisteredAppsListActivity.class));
+ return true;
+
default:
break;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index 8b3e75d05..b9c42a17c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -67,7 +67,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
// application this would come from a resource.
setEmptyText(getString(R.string.listEmpty));
- mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.public_key);
+ mAdapter = new SelectKeyCursorAdapter(mActivity, null, 0, mListView, Id.type.public_key);
setListAdapter(mAdapter);
@@ -160,11 +160,12 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
+ SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
"(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS
+ " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys."
- + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_ENCRYPT
- + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND "
- + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY
- + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
+ + " AND valid_keys." + Keys.IS_REVOKED + " = '0' AND valid_keys."
+ + Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
+ + now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
+ + Keys.EXPIRY + " >= '" + now + "')) AS "
+ + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
String inMasterKeyList = null;
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 4871b74bc..9b87f085c 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -73,7 +73,7 @@ public class SelectSecretKeyFragment extends SherlockListFragment implements
// application this would come from a resource.
setEmptyText(getString(R.string.listEmpty));
- mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.secret_key);
+ mAdapter = new SelectKeyCursorAdapter(mActivity, null, 0, mListView, Id.type.secret_key);
setListAdapter(mAdapter);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java
index 198140e0c..94d578384 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java
@@ -23,19 +23,14 @@ import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPPublicKeyRingCollection;
import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSecretKeyRingCollection;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.helper.PgpConversionHelper;
import org.sufficientlysecure.keychain.helper.PgpHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -45,11 +40,6 @@ import org.sufficientlysecure.keychain.R;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
-/**
- * A custom Loader to search for bad adware apps, based on
- * https://github.com/brosmike/AirPush-Detector. Daniel Bjorge licensed it under Apachev2 after
- * asking him by mail.
- */
public class ImportKeysListLoader extends AsyncTaskLoader<List<Map<String, String>>> {
public static final String MAP_ATTR_USER_ID = "user_id";
public static final String MAP_ATTR_FINGERPINT = "fingerprint";
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java
index 17423136f..5d8b7d1b1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java
@@ -44,9 +44,9 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
public final static String PROJECTION_ROW_AVAILABLE = "available";
public final static String PROJECTION_ROW_VALID = "valid";
- @SuppressWarnings("deprecation")
- public SelectKeyCursorAdapter(Context context, ListView listView, Cursor c, int keyType) {
- super(context, c);
+ public SelectKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView,
+ int keyType) {
+ super(context, c, flags);
mInflater = LayoutInflater.from(context);
mListView = listView;
@@ -65,8 +65,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- boolean valid = cursor.getInt(cursor
- .getColumnIndex(PROJECTION_ROW_VALID)) > 0;
+ boolean valid = cursor.getInt(cursor.getColumnIndex(PROJECTION_ROW_VALID)) > 0;
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
@@ -101,8 +100,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
status.setText(R.string.canSign);
}
} else {
- if (cursor.getInt(cursor
- .getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) {
+ if (cursor.getInt(cursor.getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) {
// has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or
// expired
status.setText(R.string.expired);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
new file mode 100644
index 000000000..d6170a4e2
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Example from
+ * http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html
+ */
+public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
+
+ public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
+ TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+ }
+
+ public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
+ TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
+ RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+ }
+
+ public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
+ TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+ }
+
+ public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
+ TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+ }
+
+ private boolean isPaused;
+ private ReentrantLock pauseLock = new ReentrantLock();
+ private Condition unpaused = pauseLock.newCondition();
+
+ protected void beforeExecute(Thread t, Runnable r) {
+ super.beforeExecute(t, r);
+ pauseLock.lock();
+ try {
+ while (isPaused)
+ unpaused.await();
+ } catch (InterruptedException ie) {
+ t.interrupt();
+ } finally {
+ pauseLock.unlock();
+ }
+ }
+
+ public void pause() {
+ pauseLock.lock();
+ try {
+ isPaused = true;
+ } finally {
+ pauseLock.unlock();
+ }
+ }
+
+ public void resume() {
+ pauseLock.lock();
+ try {
+ isPaused = false;
+ unpaused.signalAll();
+ } finally {
+ pauseLock.unlock();
+ }
+ }
+} \ No newline at end of file
diff --git a/README.md b/README.md
index 1d6956b7d..62f986b99 100644
--- a/README.md
+++ b/README.md
@@ -68,72 +68,6 @@ See http://docs.oseems.com/general/application/eclipse/fix-gc-overhead-limit-exc
1. Open svg file in Inkscape
2. Extensions -> Color -> darker (2 times!)
-# Security Model
-
-## Basic goals
-
-* Intents without permissions should only work based on user interaction (e.g. click a button in a dialog)
-
-Android primitives to exchange data: Intent, Intent with return values, Send (also an Intent), Content Provider, AIDL
-
-## Possible Permissions
-
-* ACCESS_API: Encrypt/Sign/Decrypt/Create keys without user interaction (intents, remote service), Read key information (not the actual keys)(content provider)
-* ACCESS_KEYS: get and import actual public and secret keys (remote service)
-
-## Without Permissions
-
-### Intents
-All Intents start with org.sufficientlysecure.keychain.action.
-
-* android.intent.action.VIEW connected to .gpg and .asc files: Import Key and Decrypt
-* android.intent.action.SEND connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
-* IMPORT
-* IMPORT_FROM_FILE
-* IMPORT_FROM_QR_CODE
-* IMPORT_FROM_NFC
-* SHARE_KEYRING
-* SHARE_KEYRING_WITH_QR_CODE
-* SHARE_KEYRING_WITH_NFC
-* EDIT_KEYRING
-* SELECT_PUBLIC_KEYRINGS
-* SELECT_SECRET_KEYRING
-* ENCRYPT
-* ENCRYPT_FILE
-* DECRYPT
-* DECRYPT_FILE
-
-## With permission ACCESS_API
-
-### Intents
-
-* CREATE_KEYRING
-* ENCRYPT_AND_RETURN
-* ENCRYPT_STREAM_AND_RETURN
-* GENERATE_SIGNATURE_AND_RETURN
-* DECRYPT_AND_RETURN
-* DECRYPT_STREAM_AND_RETURN
-
-### Broadcast Receiver
-On change of database the following broadcast is send.
-* DATABASE_CHANGE
-
-### Content Provider
-
-* The whole content provider requires a permission (only read)
-* Don't give out blobs (keys can be accessed by ACCESS_KEYS via remote service)
-* Make an internal and external content provider (or pathes with <path-permission>)
-* Look at android:grantUriPermissions especially for ApgServiceBlobProvider
-* Only give out android:readPermission
-
-### ApgApiService (Remote Service)
-AIDL service
-
-## With permission ACCESS_KEYS
-
-### ApgKeyService (Remote Service)
-AIDL service to access actual private keyring objects
-
# Coding Style
## Code