aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml13
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/org/thialfihar/android/apg/ApgService.java118
-rw-r--r--src/org/thialfihar/android/apg/Constants.java3
-rw-r--r--src/org/thialfihar/android/apg/DecryptActivity.java13
-rw-r--r--src/org/thialfihar/android/apg/IApgService.aidl18
-rw-r--r--src/org/thialfihar/android/apg/Id.java3
-rw-r--r--src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java54
-rw-r--r--src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java138
-rw-r--r--src/org/thialfihar/android/apg/utils/ApgCon.java97
10 files changed, 423 insertions, 36 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 937eb488a..e72197138 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -204,12 +204,17 @@
<intent-filter>
<action android:name="org.thialfihar.android.apg.IApgService"/>
</intent-filter>
- <meta-data android:name="api_version" android:value="1" />
+ <meta-data android:name="api_version" android:value="2" />
</service>
<provider
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
android:name="org.thialfihar.android.apg.provider.DataProvider"
android:authorities="org.thialfihar.android.apg.provider"/>
+
+ <provider
+ android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS"
+ android:name="org.thialfihar.android.apg.provider.ApgServiceBlobProvider"
+ android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider"/>
</application>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" />
@@ -219,10 +224,16 @@
android:label="@string/permission_read_key_details_label"
android:description="@string/permission_read_key_details_description"/>
+ <permission android:name="org.thialfihar.android.apg.permission.STORE_BLOBS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permission_store_blobs_label"
+ android:description="@string/permission_store_blobs_description"/>
+
<uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" />
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="org.thialfihar.android.apg.permission.STORE_BLOBS"/>
</manifest>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5b29e4f12..62e0935a9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -288,6 +288,8 @@
<!-- permission strings -->
<string name="permission_read_key_details_label">Read key details from APG.</string>
<string name="permission_read_key_details_description">Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string>
+ <string name="permission_store_blobs_label">Store blobs to en/decrypt with APG.</string>
+ <string name="permission_store_blobs_description">Store and read files on the android file system through APG. It cannot read files of other applications.</string>
<!-- action strings -->
<string name="action_encrypt">Encrypt</string>
diff --git a/src/org/thialfihar/android/apg/ApgService.java b/src/org/thialfihar/android/apg/ApgService.java
index 0a25c6055..46d8b4765 100644
--- a/src/org/thialfihar/android/apg/ApgService.java
+++ b/src/org/thialfihar/android/apg/ApgService.java
@@ -2,6 +2,7 @@ package org.thialfihar.android.apg;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
@@ -14,17 +15,19 @@ import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
+import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
public class ApgService extends Service {
private final static String TAG = "ApgService";
- private static final boolean LOCAL_LOGV = true;
- private static final boolean LOCAL_LOGD = true;
+ public static final boolean LOCAL_LOGV = true;
+ public static final boolean LOCAL_LOGD = true;
@Override
public IBinder onBind(Intent intent) {
@@ -33,7 +36,7 @@ public class ApgService extends Service {
}
/** error status */
- private enum error {
+ private static enum error {
ARGUMENTS_MISSING,
APG_FAILURE,
NO_MATCHING_SECRET_KEY,
@@ -44,9 +47,16 @@ public class ApgService extends Service {
return ordinal() + 100;
}
}
+
+ private static enum call {
+ encrypt_with_passphrase,
+ encrypt_with_public_key,
+ decrypt,
+ get_keys
+ }
/** all arguments that can be passed by calling application */
- private enum arg {
+ public static enum arg {
MESSAGE, // message to encrypt or to decrypt
SYMMETRIC_PASSPHRASE, // key for symmetric en/decryption
PUBLIC_KEYS, // public keys for encryption
@@ -58,10 +68,11 @@ public class ApgService extends Service {
SIGNATURE_KEY, // key for signing
PRIVATE_KEY_PASSPHRASE, // passphrase for encrypted private key
KEY_TYPE, // type of key (private or public)
+ BLOB, // blob passed
}
/** all things that might be returned */
- private enum ret {
+ private static enum ret {
ERRORS, // string array list with errors
WARNINGS, // string array list with warnings
ERROR, // numeric error
@@ -75,21 +86,18 @@ public class ApgService extends Service {
static {
HashSet<arg> args = new HashSet<arg>();
args.add(arg.SYMMETRIC_PASSPHRASE);
- args.add(arg.MESSAGE);
- FUNCTIONS_REQUIRED_ARGS.put("encrypt_with_passphrase", args);
+ FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_passphrase.name(), args);
args = new HashSet<arg>();
args.add(arg.PUBLIC_KEYS);
- args.add(arg.MESSAGE);
- FUNCTIONS_REQUIRED_ARGS.put("encrypt_with_public_key", args);
+ FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_public_key.name(), args);
args = new HashSet<arg>();
- args.add(arg.MESSAGE);
- FUNCTIONS_REQUIRED_ARGS.put("decrypt", args);
+ FUNCTIONS_REQUIRED_ARGS.put(call.decrypt.name(), args);
args = new HashSet<arg>();
args.add(arg.KEY_TYPE);
- FUNCTIONS_REQUIRED_ARGS.put("get_keys", args);
+ FUNCTIONS_REQUIRED_ARGS.put(call.get_keys.name(), args);
}
/** optional arguments for each AIDL function */
@@ -103,14 +111,18 @@ public class ApgService extends Service {
args.add(arg.COMPRESSION);
args.add(arg.PRIVATE_KEY_PASSPHRASE);
args.add(arg.SIGNATURE_KEY);
- FUNCTIONS_OPTIONAL_ARGS.put("encrypt_with_passphrase", args);
- FUNCTIONS_OPTIONAL_ARGS.put("encrypt_with_public_key", args);
+ args.add(arg.BLOB);
+ args.add(arg.MESSAGE);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_passphrase.name(), args);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_public_key.name(), args);
args = new HashSet<arg>();
args.add(arg.SYMMETRIC_PASSPHRASE);
args.add(arg.PUBLIC_KEYS);
args.add(arg.PRIVATE_KEY_PASSPHRASE);
- FUNCTIONS_OPTIONAL_ARGS.put("decrypt", args);
+ args.add(arg.MESSAGE);
+ args.add(arg.BLOB);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.decrypt.name(), args);
}
/** a map from ApgService parameters to function calls to get the default */
@@ -136,6 +148,14 @@ public class ApgService extends Service {
Log.e(TAG, "Function method exception: " + e.getMessage());
}
}
+
+ private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[8];
+ int len = 0;
+ while( (len = is.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ }
private static Cursor getKeyEntries(HashMap<String, Object> pParams) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -304,6 +324,16 @@ public class ApgService extends Service {
}
}
}
+
+ if(pFunction.equals(call.encrypt_with_passphrase.name()) ||
+ pFunction.equals(call.encrypt_with_public_key.name()) ||
+ pFunction.equals(call.decrypt.name())) {
+ // check that either MESSAGE or BLOB are there
+ if( !pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add("Arguments missing: Neither MESSAGE nor BLOG found");
+ }
+
+ }
}
/**
@@ -378,6 +408,7 @@ public class ApgService extends Service {
}
private boolean encrypt(Bundle pArgs, Bundle pReturn) {
+ boolean isBlob = pArgs.containsKey(arg.BLOB.name());
long pubMasterKeys[] = {};
if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) {
@@ -391,7 +422,17 @@ public class ApgService extends Service {
pubMasterKeys = getMasterKey(pubKeys, pReturn);
}
- InputStream inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ InputStream inStream = null;
+ if(isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on opening blob", e);
+ }
+ } else {
+ inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ }
InputData in = new InputData(inStream, 0); // XXX Size second param?
OutputStream out = new ByteArrayOutputStream();
@@ -427,7 +468,18 @@ public class ApgService extends Service {
return false;
}
if( LOCAL_LOGV ) Log.v(TAG, "Encrypted");
- pReturn.putString(ret.RESULT.name(), out.toString());
+ if(isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on writing blob", e);
+ }
+ } else {
+ pReturn.putString(ret.RESULT.name(), out.toString());
+ }
return true;
}
@@ -481,11 +533,24 @@ public class ApgService extends Service {
if (!prepareArgs("decrypt", pArgs, pReturn)) {
return false;
}
+
+ boolean isBlob = pArgs.containsKey(arg.BLOB.name());
String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs
.getString(arg.PRIVATE_KEY_PASSPHRASE.name());
- InputStream inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ InputStream inStream = null;
+ if(isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on opening blob", e);
+ }
+ } else {
+ inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ }
+
InputData in = new InputData(inStream, 0); // XXX what size in second parameter?
OutputStream out = new ByteArrayOutputStream();
if( LOCAL_LOGV ) Log.v(TAG, "About to decrypt");
@@ -508,9 +573,20 @@ public class ApgService extends Service {
}
return false;
}
- if( LOCAL_LOGV ) Log.v(TAG, "Decrypted");
-
- pReturn.putString(ret.RESULT.name(), out.toString());
+ if( LOCAL_LOGV ) Log.v(TAG, "... decrypted");
+
+ if(isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on writing blob", e);
+ }
+ } else {
+ pReturn.putString(ret.RESULT.name(), out.toString());
+ }
return true;
}
diff --git a/src/org/thialfihar/android/apg/Constants.java b/src/org/thialfihar/android/apg/Constants.java
index 89751e268..bd0746085 100644
--- a/src/org/thialfihar/android/apg/Constants.java
+++ b/src/org/thialfihar/android/apg/Constants.java
@@ -19,6 +19,9 @@ package org.thialfihar.android.apg;
import android.os.Environment;
public final class Constants {
+
+ public static final String tag = "APG";
+
public static final class path {
public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
}
diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java
index dfa4cb69b..a10a168de 100644
--- a/src/org/thialfihar/android/apg/DecryptActivity.java
+++ b/src/org/thialfihar/android/apg/DecryptActivity.java
@@ -30,6 +30,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.text.ClipboardManager;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AnimationUtils;
@@ -189,19 +190,28 @@ public class DecryptActivity extends BaseActivity {
// ignore, then
}
} else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
+ Log.d(Constants.tag, "Apg Intent DECRYPT startet");
Bundle extras = mIntent.getExtras();
if (extras == null) {
+ Log.d(Constants.tag, "extra bundle was null");
extras = new Bundle();
+ } else {
+ Log.d(Constants.tag, "got extras");
}
mData = extras.getByteArray(Apg.EXTRA_DATA);
String textData = null;
if (mData == null) {
+ Log.d(Constants.tag, "EXTRA_DATA was null");
textData = extras.getString(Apg.EXTRA_TEXT);
+ } else {
+ Log.d(Constants.tag, "Got data from EXTRA_DATA");
}
if (textData != null) {
+ Log.d(Constants.tag, "textData null, matching text ...");
Matcher matcher = Apg.PGP_MESSAGE.matcher(textData);
if (matcher.matches()) {
+ Log.d(Constants.tag, "PGP_MESSAGE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
@@ -209,11 +219,14 @@ public class DecryptActivity extends BaseActivity {
} else {
matcher = Apg.PGP_SIGNED_MESSAGE.matcher(textData);
if (matcher.matches()) {
+ Log.d(Constants.tag, "PGP_SIGNED_MESSAGE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
mMessage.setText(textData);
mDecryptButton.setText(R.string.btn_verify);
+ } else {
+ Log.d(Constants.tag, "Nothing matched!");
}
}
}
diff --git a/src/org/thialfihar/android/apg/IApgService.aidl b/src/org/thialfihar/android/apg/IApgService.aidl
index df46805ac..25780f366 100644
--- a/src/org/thialfihar/android/apg/IApgService.aidl
+++ b/src/org/thialfihar/android/apg/IApgService.aidl
@@ -27,6 +27,11 @@ interface IApgService {
*
* (required)
* String "MESSAGE" = Message to encrypt
+ * OR
+ * String "BLOB" = ContentUri to a file handle
+ * with binary data to encrypt
+ * (Attention: file will be overwritten
+ * with encrypted content!)
*
* (optional)
* int "ENCRYPTION_ALGORYTHM" = Encryption Algorithm
@@ -55,7 +60,8 @@ interface IApgService {
* String "PRIVATE_KEY_PASSPHRASE" = Passphrase for signing key
*
* Bundle returnVals (in addition to the ERRORS/WARNINGS above):
- * String "RESULT" = Encrypted message
+ * If "MESSAGE" was set:
+ * String "RESULT" = Encrypted message
*/
/* Additional argument for function below:
@@ -77,7 +83,12 @@ interface IApgService {
/* Bundle params:
* (required)
- * String "MESSAGE" = Message to decrypt
+ * String "MESSAGE" = Message to dencrypt
+ * OR
+ * String "BLOB" = ContentUri to a file handle
+ * with binary data to dencrypt
+ * (Attention: file will be overwritten
+ * with dencrypted content!)
*
* (optional)
* String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase for decryption
@@ -86,7 +97,8 @@ interface IApgService {
* String "PRIVATE_KEY_PASSPHRASE" = Private keys's passphrase on asymmetric encryption
*
* Bundle return_vals:
- * String "RESULT" = Decrypted message
+ * If "MESSAGE" was set:
+ * String "RESULT" = Decrypted message
*/
boolean decrypt(in Bundle params, out Bundle returnVals);
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java
index 1ce53eabd..2b486e84a 100644
--- a/src/org/thialfihar/android/apg/Id.java
+++ b/src/org/thialfihar/android/apg/Id.java
@@ -19,6 +19,9 @@ package org.thialfihar.android.apg;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
public final class Id {
+
+ public static final String TAG = "APG";
+
public static final class menu {
public static final int export = 0x21070001;
public static final int delete = 0x21070002;
diff --git a/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java b/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java
new file mode 100644
index 000000000..9a891ddfa
--- /dev/null
+++ b/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java
@@ -0,0 +1,54 @@
+package org.thialfihar.android.apg.provider;
+
+import org.thialfihar.android.apg.ApgService;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.util.Log;
+
+public class ApgServiceBlobDatabase extends SQLiteOpenHelper {
+
+ private static final String TAG = "ApgServiceBlobDatabase";
+
+ private static final int VERSION = 1;
+ private static final String NAME = "apg_service_blob_data";
+ private static final String TABLE = "data";
+
+ public ApgServiceBlobDatabase(Context context) {
+ super(context, NAME, null, VERSION);
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "constructor called");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
+ db.execSQL("create table " + TABLE + " ( _id integer primary key autoincrement," +
+ "key text not null)");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onUpgrade() called");
+ // no upgrade necessary yet
+ }
+
+ public Uri insert(ContentValues vals) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
+ SQLiteDatabase db = this.getWritableDatabase();
+ long newId = db.insert(TABLE, null, vals);
+ return ContentUris.withAppendedId(ApgServiceBlobProvider.CONTENT_URI, newId);
+ }
+
+ public Cursor query(String id, String key) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
+ SQLiteDatabase db = this.getReadableDatabase();
+ return db.query(TABLE, new String[] {"_id"},
+ "_id = ? and key = ?", new String[] {id, key},
+ null, null, null);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java b/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java
new file mode 100644
index 000000000..fd4145f4c
--- /dev/null
+++ b/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java
@@ -0,0 +1,138 @@
+package org.thialfihar.android.apg.provider;
+
+import org.thialfihar.android.apg.ApgService;
+import org.thialfihar.android.apg.Constants;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+public class ApgServiceBlobProvider extends ContentProvider {
+
+ private static final String TAG = "ApgServiceBlobProvider";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://org.thialfihar.android.apg.provider.apgserviceblobprovider");
+
+ private static final String COLUMN_KEY = "key";
+
+ private static final String STORE_PATH = Constants.path.app_dir+"/ApgServiceBlobs";
+
+ private ApgServiceBlobDatabase mDb = null;
+
+ public ApgServiceBlobProvider() {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor called");
+ File dir = new File(STORE_PATH);
+ dir.mkdirs();
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor finished");
+ }
+
+ @Override
+ public int delete(Uri arg0, String arg1, String[] arg2) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "delete() called");
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri arg0) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "getType() called");
+ // not needed for now
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues ignored) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
+ // ContentValues are actually ignored, because we want to store a blob with no more information
+ // but have to create an record with the password generated here first
+
+ ContentValues vals = new ContentValues();
+
+ // Insert a random key in the database. This has to provided by the caller when updating or
+ // getting the blob
+ String password = UUID.randomUUID().toString();
+ vals.put(COLUMN_KEY, password);
+
+ Uri insertedUri = mDb.insert(vals);
+ return Uri.withAppendedPath(insertedUri, password);
+ }
+
+ @Override
+ public boolean onCreate() {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
+ mDb = new ApgServiceBlobDatabase(getContext());
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "update() called");
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, FileNotFoundException {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "openFile() called");
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with uri: "+uri.toString());
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with mode: "+mode);
+
+ List<String> segments = uri.getPathSegments();
+ if(segments.size() < 2) {
+ throw new SecurityException("Password not found in URI");
+ }
+ String id = segments.get(0);
+ String key = segments.get(1);
+
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... got id: "+id);
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... and key: "+key);
+
+ // get the data
+ Cursor result = mDb.query(id, key);
+
+ if(result.getCount() == 0) {
+ // either the key is wrong or no id exists
+ throw new FileNotFoundException("No file found with that ID and/or password");
+ }
+
+ File targetFile = new File(STORE_PATH, id);
+ if(mode.equals("w")) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file w");
+ if( !targetFile.exists() ) {
+ try {
+ targetFile.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "... got IEOException on creating new file", e);
+ throw new FileNotFoundException("Could not create file to write to");
+ }
+ }
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE );
+ } else if(mode.equals("r")) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file r");
+ if( !targetFile.exists() ) {
+ throw new FileNotFoundException("Error: Could not find the file requested");
+ }
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/thialfihar/android/apg/utils/ApgCon.java b/src/org/thialfihar/android/apg/utils/ApgCon.java
index 475f7e9a9..c46ccf6b1 100644
--- a/src/org/thialfihar/android/apg/utils/ApgCon.java
+++ b/src/org/thialfihar/android/apg/utils/ApgCon.java
@@ -16,22 +16,27 @@
package org.thialfihar.android.apg.utils;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
+import org.thialfihar.android.apg.IApgService;
+import org.thialfihar.android.apg.utils.ApgConInterface.OnCallFinishListener;
-import android.content.Context;
import android.content.ComponentName;
-import android.content.ServiceConnection;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
-import org.thialfihar.android.apg.IApgService;
-import org.thialfihar.android.apg.utils.ApgConInterface.OnCallFinishListener;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
/**
* A APG-AIDL-Wrapper
@@ -46,7 +51,7 @@ import org.thialfihar.android.apg.utils.ApgConInterface.OnCallFinishListener;
* </p>
*
* @author Markus Doits <markus.doits@googlemail.com>
- * @version 1.0rc1
+ * @version 1.1rc1
*
*/
public class ApgCon {
@@ -54,7 +59,8 @@ public class ApgCon {
private static final boolean LOCAL_LOGD = true;
private final static String TAG = "ApgCon";
- private final static int API_VERSION = 1; // aidl api-version it expects
+ private final static int API_VERSION = 2; // aidl api-version it expects
+ private final static String BLOB_URI = "content://org.thialfihar.android.apg.provider.apgserviceblobprovider";
/**
* How many seconds to wait for a connection to AGP when connecting.
@@ -80,7 +86,6 @@ public class ApgCon {
}
-
private final Context mContext;
private final error mConnectionStatus;
private boolean mAsyncRunning = false;
@@ -187,9 +192,9 @@ public class ApgCon {
mWarningList.add("(LOCAL) Could not determine ApgService API");
tmpError = error.APG_API_MISSMATCH;
} else if (inf.metaData.getInt("api_version") != API_VERSION) {
- Log.w(TAG, "Found ApgService API version" + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
+ Log.w(TAG, "Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
Log.w(TAG, "This probably won't work!");
- mWarningList.add("(LOCAL) Found ApgService API version" + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
+ mWarningList.add("(LOCAL) Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
tmpError = error.APG_API_MISSMATCH;
} else {
if( LOCAL_LOGV ) Log.v(TAG, "Found api_version " + API_VERSION + ", everything should work");
@@ -495,6 +500,41 @@ public class ApgCon {
}
mArgs.putIntegerArrayList(key, list);
}
+
+ /**
+ * Set up binary data to en/decrypt
+ *
+ * @param is
+ * InputStream to get the data from
+ */
+ public void setBlob(InputStream is) {
+ if( LOCAL_LOGD ) Log.d(TAG, "setBlob() called");
+ // 1. get the new contentUri
+ ContentResolver cr = mContext.getContentResolver();
+ Uri contentUri = cr.insert(Uri.parse(BLOB_URI), new ContentValues());
+
+ // 2. insert binary data
+ OutputStream os = null;
+ try {
+ os = cr.openOutputStream(contentUri, "w");
+ } catch( Exception e ) {
+ Log.e(TAG, "... exception on setBlob", e);
+ }
+
+ byte[] buffer = new byte[8];
+ int len = 0;
+ try {
+ while( (len = is.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ if(LOCAL_LOGD) Log.d(TAG, "... write finished, now closing");
+ os.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... error on writing buffer", e);
+ }
+
+ mArgs.putString("BLOB", contentUri.toString() );
+ }
/**
* Clears all arguments
@@ -628,18 +668,53 @@ public class ApgCon {
* {@link #hasNextError()} is false.
* </p>
*
+ * <p>
+ * Note: When handling binary data with {@link #setBlob(InputStream)}, you
+ * get your result with {@link #getBlobResult()}.
+ * </p>
+ *
* @return the mResult of the last {@link #call(String)} or
* {@link #callAsync(String)}.
*
* @see #reset()
* @see #clearResult()
* @see #getResultBundle()
+ * @see #getBlobResult()
*/
public String getResult() {
return mResult.getString(ret.RESULT.name());
}
/**
+ * Get the binary result
+ *
+ * <p>
+ * This gets your binary result. It only works if you called {@link #setBlob(InputStream)} before.
+ *
+ * If you did not call encrypt nor decrypt, this will be the same data as you inputed.
+ * </p>
+ *
+ * @return InputStream of the binary data which was en/decrypted
+ *
+ * @see #setBlob(InputStream)
+ * @see #getResult()
+ */
+ public InputStream getBlobResult() {
+ if(mArgs.containsKey("BLOB")) {
+ ContentResolver cr = mContext.getContentResolver();
+ InputStream in = null;
+ try {
+ in = cr.openInputStream(Uri.parse(mArgs.getString("BLOB")));
+ } catch( Exception e ) {
+ Log.e(TAG, "Could not return blob in result", e);
+ }
+ return in;
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Get the result bundle
*
* <p>