diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/org/thialfihar/android/apg/ApgService.java | 118 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/Constants.java | 3 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/DecryptActivity.java | 13 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/IApgService.aidl | 18 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/Id.java | 3 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java | 54 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java | 138 | ||||
-rw-r--r-- | src/org/thialfihar/android/apg/utils/ApgCon.java | 97 |
8 files changed, 409 insertions, 35 deletions
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> |