aboutsummaryrefslogtreecommitdiffstats
path: root/org_apg/src/org/apg/util/ApgCon.java
diff options
context:
space:
mode:
Diffstat (limited to 'org_apg/src/org/apg/util/ApgCon.java')
-rw-r--r--org_apg/src/org/apg/util/ApgCon.java836
1 files changed, 836 insertions, 0 deletions
diff --git a/org_apg/src/org/apg/util/ApgCon.java b/org_apg/src/org/apg/util/ApgCon.java
new file mode 100644
index 000000000..73ee66ad9
--- /dev/null
+++ b/org_apg/src/org/apg/util/ApgCon.java
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2011 Markus Doits <markus.doits@googlemail.com>
+ *
+ * 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.apg.util;
+
+import org.apg.util.ApgConInterface.OnCallFinishListener;
+import org.apg.IApgService;
+
+import android.content.ComponentName;
+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 java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * A APG-AIDL-Wrapper
+ *
+ * <p>
+ * This class can be used by other projects to simplify connecting to the
+ * APG-AIDL-Service. Kind of wrapper of for AIDL.
+ * </p>
+ *
+ * <p>
+ * It is not used in this project.
+ * </p>
+ *
+ * @author Markus Doits <markus.doits@googlemail.com>
+ * @version 1.1rc1
+ *
+ */
+public class ApgCon {
+ private static final boolean LOCAL_LOGV = true;
+ private static final boolean LOCAL_LOGD = true;
+
+ private final static String TAG = "ApgCon";
+ 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.
+ * Being unsuccessful for this number of seconds, a connection
+ * is assumed to be failed.
+ */
+ public int secondsToWaitForConnection = 15;
+
+ private class CallAsync extends AsyncTask<String, Void, Void> {
+
+ @Override
+ protected Void doInBackground(String... arg) {
+ if( LOCAL_LOGD ) Log.d(TAG, "Async execution starting");
+ call(arg[0]);
+ return null;
+ }
+
+ protected void onPostExecute(Void res) {
+ if( LOCAL_LOGD ) Log.d(TAG, "Async execution finished");
+ mAsyncRunning = false;
+
+ }
+
+ }
+
+ private final Context mContext;
+ private final error mConnectionStatus;
+ private boolean mAsyncRunning = false;
+ private OnCallFinishListener mOnCallFinishListener;
+
+ private final Bundle mResult = new Bundle();
+ private final Bundle mArgs = new Bundle();
+ private final ArrayList<String> mErrorList = new ArrayList<String>();
+ private final ArrayList<String> mWarningList = new ArrayList<String>();
+
+ /** Remote service for decrypting and encrypting data */
+ private IApgService mApgService = null;
+
+ /** Set apgService accordingly to connection status */
+ private ServiceConnection mApgConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if( LOCAL_LOGD ) Log.d(TAG, "IApgService bound to apgService");
+ mApgService = IApgService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if( LOCAL_LOGD ) Log.d(TAG, "IApgService disconnected");
+ mApgService = null;
+ }
+ };
+
+ /**
+ * Different types of local errors
+ */
+ public static enum error {
+ /**
+ * no error
+ */
+ NO_ERROR,
+ /**
+ * generic error
+ */
+ GENERIC,
+ /**
+ * connection to apg service not possible
+ */
+ CANNOT_BIND_TO_APG,
+ /**
+ * function to call not provided
+ */
+ CALL_MISSING,
+ /**
+ * apg service does not know what to do
+ */
+ CALL_NOT_KNOWN,
+ /**
+ * could not find APG being installed
+ */
+ APG_NOT_FOUND,
+ /**
+ * found APG but without AIDL interface
+ */
+ APG_AIDL_MISSING,
+ /**
+ * found APG but with wrong API
+ */
+ APG_API_MISSMATCH
+ }
+
+ private static enum ret {
+ ERROR, // returned from AIDL
+ RESULT, // returned from AIDL
+ WARNINGS, // mixed AIDL and LOCAL
+ ERRORS, // mixed AIDL and LOCAL
+ }
+
+ /**
+ * Constructor
+ *
+ * <p>
+ * Creates a new ApgCon object and searches for the right APG version on
+ * initialization. If not found, errors are printed to the error log.
+ * </p>
+ *
+ * @param ctx
+ * the running context
+ */
+ public ApgCon(Context ctx) {
+ if( LOCAL_LOGV ) Log.v(TAG, "EncryptionService created");
+ mContext = ctx;
+
+ error tmpError = null;
+ try {
+ if( LOCAL_LOGV ) Log.v(TAG, "Searching for the right APG version");
+ ServiceInfo apgServices[] = ctx.getPackageManager().getPackageInfo("org.thialfihar.android.apg",
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA).services;
+ if (apgServices == null) {
+ Log.e(TAG, "Could not fetch services");
+ tmpError = error.GENERIC;
+ } else {
+ boolean apgServiceFound = false;
+ for (ServiceInfo inf : apgServices) {
+ if( LOCAL_LOGV ) Log.v(TAG, "Found service of APG: " + inf.name);
+ if (inf.name.equals("org.thialfihar.android.apg.ApgService")) {
+ apgServiceFound = true;
+ if (inf.metaData == null) {
+ Log.w(TAG, "Could not determine ApgService API");
+ Log.w(TAG, "This probably won't work!");
+ 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, "This probably won't work!");
+ 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");
+ tmpError = error.NO_ERROR;
+ }
+ }
+ }
+
+ if (!apgServiceFound) {
+ Log.e(TAG, "Could not find APG with AIDL interface, this probably won't work");
+ mErrorList.add("(LOCAL) Could not find APG with AIDL interface, this probably won't work");
+ mResult.putInt(ret.ERROR.name(), error.APG_AIDL_MISSING.ordinal());
+ tmpError = error.APG_NOT_FOUND;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not find APG, is it installed?", e);
+ mErrorList.add("(LOCAL) Could not find APG, is it installed?");
+ mResult.putInt(ret.ERROR.name(), error.APG_NOT_FOUND.ordinal());
+ tmpError = error.APG_NOT_FOUND;
+ }
+
+ mConnectionStatus = tmpError;
+
+ }
+
+ /** try to connect to the apg service */
+ private boolean connect() {
+ if( LOCAL_LOGV ) Log.v(TAG, "trying to bind the apgService to context");
+
+ if (mApgService != null) {
+ if( LOCAL_LOGV ) Log.v(TAG, "allready connected");
+ return true;
+ }
+
+ try {
+ mContext.bindService(new Intent(IApgService.class.getName()), mApgConnection, Context.BIND_AUTO_CREATE);
+ } catch (Exception e) {
+ Log.e(TAG, "could not bind APG service", e);
+ return false;
+ }
+
+ int waitCount = 0;
+ while (mApgService == null && waitCount++ < secondsToWaitForConnection) {
+ if( LOCAL_LOGV ) Log.v(TAG, "sleeping 1 second to wait for apg");
+ android.os.SystemClock.sleep(1000);
+ }
+
+ if (waitCount >= secondsToWaitForConnection) {
+ if( LOCAL_LOGV ) Log.v(TAG, "slept waiting for nothing!");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnects ApgCon from Apg
+ *
+ * <p>
+ * This should be called whenever all work with APG is done (e.g. everything
+ * you wanted to encrypt is encrypted), since connections with AIDL should
+ * not be upheld indefinitely.
+ * <p>
+ *
+ * <p>
+ * Also, if you destroy you end using your ApgCon-instance, this must be
+ * called or else the connection to APG is leaked
+ * </p>
+ */
+ public void disconnect() {
+ if( LOCAL_LOGV ) Log.v(TAG, "disconnecting apgService");
+ if (mApgService != null) {
+ mContext.unbindService(mApgConnection);
+ mApgService = null;
+ }
+ }
+
+ private boolean initialize() {
+ if (mApgService == null) {
+ if (!connect()) {
+ if( LOCAL_LOGV ) Log.v(TAG, "connection to apg service failed");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Calls a function from APG's AIDL-interface
+ *
+ * <p>
+ * After you have set up everything with {@link #setArg(String, String)}
+ * (and variants), you can call a function of the AIDL-interface. This
+ * will:
+ * <ul>
+ * <li>start connection to the remote interface (if not already connected)</li>
+ * <li>call the function passed with all parameters synchronously</li>
+ * <li>set up everything to retrieve the result and/or warnings/errors</li>
+ * <li>call the callback if provided
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * Note your thread will be blocked during execution - if you want to call
+ * the function asynchronously, see {@link #callAsync(String)}.
+ * </p>
+ *
+ * @param function
+ * a remote function to call
+ * @return true, if call successful (= no errors), else false
+ *
+ * @see #callAsync(String)
+ * @see #setArg(String, String)
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public boolean call(String function) {
+ boolean success = this.call(function, mArgs, mResult);
+ if (mOnCallFinishListener != null) {
+ try {
+ if( LOCAL_LOGD ) Log.d(TAG, "About to execute callback");
+ mOnCallFinishListener.onCallFinish(mResult);
+ if( LOCAL_LOGD ) Log.d(TAG, "Callback executed");
+ } catch (Exception e) {
+ Log.w(TAG, "Exception on callback: (" + e.getClass() + ") " + e.getMessage(), e);
+ mWarningList.add("(LOCAL) Could not execute callback (" + e.getClass() + "): " + e.getMessage());
+ }
+ }
+ return success;
+ }
+
+ /**
+ * Calls a function of remote interface asynchronously
+ *
+ * <p>
+ * This does exactly the same as {@link #call(String)}, but asynchronously.
+ * While connection to APG and work are done in background, your thread can
+ * go on executing.
+ * <p>
+ *
+ * <p>
+ * To see whether the task is finished, you have two possibilities:
+ * <ul>
+ * <li>In your thread, poll {@link #isRunning()}</li>
+ * <li>Supply a callback with {@link #setOnCallFinishListener(OnCallFinishListener)}</li>
+ * </ul>
+ * </p>
+ *
+ * @param function
+ * a remote function to call
+ *
+ * @see #call(String)
+ * @see #isRunning()
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public void callAsync(String function) {
+ mAsyncRunning = true;
+ new CallAsync().execute(function);
+ }
+
+ private boolean call(String function, Bundle pArgs, Bundle pReturn) {
+
+ if (!initialize()) {
+ mErrorList.add("(LOCAL) Cannot bind to ApgService");
+ mResult.putInt(ret.ERROR.name(), error.CANNOT_BIND_TO_APG.ordinal());
+ return false;
+ }
+
+ if (function == null || function.length() == 0) {
+ mErrorList.add("(LOCAL) Function to call missing");
+ mResult.putInt(ret.ERROR.name(), error.CALL_MISSING.ordinal());
+ return false;
+ }
+
+ try {
+ Boolean success = (Boolean) IApgService.class.getMethod(function, Bundle.class, Bundle.class).invoke(mApgService, pArgs, pReturn);
+ mErrorList.addAll(pReturn.getStringArrayList(ret.ERRORS.name()));
+ mWarningList.addAll(pReturn.getStringArrayList(ret.WARNINGS.name()));
+ return success;
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Remote call not known (" + function + "): " + e.getMessage(), e);
+ mErrorList.add("(LOCAL) Remote call not known (" + function + "): " + e.getMessage());
+ mResult.putInt(ret.ERROR.name(), error.CALL_NOT_KNOWN.ordinal());
+ return false;
+ } catch (InvocationTargetException e) {
+ Throwable orig = e.getTargetException();
+ Log.w(TAG, "Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage(), orig);
+ mErrorList.add("(LOCAL) Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage());
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "Generic error (" + e.getClass() + "): " + e.getMessage(), e);
+ mErrorList.add("(LOCAL) Generic error (" + e.getClass() + "): " + e.getMessage());
+ mResult.putInt(ret.ERROR.name(), error.GENERIC.ordinal());
+ return false;
+ }
+
+ }
+
+ /**
+ * Set a string argument for APG
+ *
+ * <p>
+ * This defines a string argument for APG's AIDL-interface.
+ * </p>
+ *
+ * <p>
+ * To know what key-value-pairs are possible (or required), take a look into
+ * the IApgService.aidl
+ * </p>
+ *
+ * <p>
+ * Note that parameters are not reseted after a call, so you have to
+ * reset ({@link #clearArgs()}) them manually if you want to.
+ * </p>
+ *
+ *
+ * @param key
+ * the key
+ * @param val
+ * the value
+ *
+ * @see #clearArgs()
+ */
+ public void setArg(String key, String val) {
+ mArgs.putString(key, val);
+ }
+
+ /**
+ * Set a string-array argument for APG
+ *
+ * <p>
+ * If the AIDL-parameter is an {@literal ArrayList<String>}, you have to use
+ * this function.
+ * </p>
+ *
+ * <code>
+ * <pre>
+ * setArg("a key", new String[]{ "entry 1", "entry 2" });
+ * </pre>
+ * </code>
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, String vals[]) {
+ ArrayList<String> list = new ArrayList<String>();
+ for (String val : vals) {
+ list.add(val);
+ }
+ mArgs.putStringArrayList(key, list);
+ }
+
+ /**
+ * Set up a boolean argument for APG
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, boolean val) {
+ mArgs.putBoolean(key, val);
+ }
+
+ /**
+ * Set up a int argument for APG
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, int val) {
+ mArgs.putInt(key, val);
+ }
+
+ /**
+ * Set up a int-array argument for APG
+ * <p>
+ * If the AIDL-parameter is an {@literal ArrayList<Integer>}, you have to
+ * use this function.
+ * </p>
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, int vals[]) {
+ ArrayList<Integer> list = new ArrayList<Integer>();
+ for (int val : vals) {
+ list.add(val);
+ }
+ 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
+ *
+ * <p>
+ * Anything the has been set up with the various
+ * {@link #setArg(String, String)} functions is cleared.
+ * </p>
+ *
+ * <p>
+ * Note that any warning, error, callback, result, etc. is NOT cleared with
+ * this.
+ * </p>
+ *
+ * @see #reset()
+ */
+ public void clearArgs() {
+ mArgs.clear();
+ }
+
+ /**
+ * Return the object associated with the key
+ *
+ * @param key
+ * the object's key you want to return
+ * @return an object at position key, or null if not set
+ */
+ public Object getArg(String key) {
+ return mArgs.get(key);
+ }
+
+ /**
+ * Iterates through the errors
+ *
+ * <p>
+ * With this method you can iterate through all errors. The errors are only
+ * returned once and deleted immediately afterwards, so you can only return
+ * each error once.
+ * </p>
+ *
+ * @return a human readable description of a error that happened, or null if
+ * no more errors
+ *
+ * @see #hasNextError()
+ * @see #clearErrors()
+ */
+ public String getNextError() {
+ if (mErrorList.size() != 0)
+ return mErrorList.remove(0);
+ else
+ return null;
+ }
+
+ /**
+ * Check if there are any new errors
+ *
+ * @return true, if there are unreturned errors, false otherwise
+ *
+ * @see #getNextError()
+ */
+ public boolean hasNextError() {
+ return mErrorList.size() != 0;
+ }
+
+ /**
+ * Get the numeric representation of the last error
+ *
+ * <p>
+ * Values <100 mean the error happened locally, values >=100 mean the error
+ * happened at the remote side (APG). See the IApgService.aidl (or get the
+ * human readable description with {@link #getNextError()}) for what
+ * errors >=100 mean.
+ * </p>
+ *
+ * @return the id of the error that happened
+ */
+ public int getError() {
+ if (mResult.containsKey(ret.ERROR.name()))
+ return mResult.getInt(ret.ERROR.name());
+ else
+ return -1;
+ }
+
+ /**
+ * Iterates through the warnings
+ *
+ * <p>
+ * With this method you can iterate through all warnings. Warnings are
+ * only returned once and deleted immediately afterwards, so you can only
+ * return each warning once.
+ * </p>
+ *
+ * @return a human readable description of a warning that happened, or null
+ * if no more warnings
+ *
+ * @see #hasNextWarning()
+ * @see #clearWarnings()
+ */
+ public String getNextWarning() {
+ if (mWarningList.size() != 0)
+ return mWarningList.remove(0);
+ else
+ return null;
+ }
+
+ /**
+ * Check if there are any new warnings
+ *
+ * @return true, if there are unreturned warnings, false otherwise
+ *
+ * @see #getNextWarning()
+ */
+ public boolean hasNextWarning() {
+ return mWarningList.size() != 0;
+ }
+
+ /**
+ * Get the result
+ *
+ * <p>
+ * This gets your result. After doing an encryption or decryption with APG,
+ * you get the output with this function.
+ * </p>
+ *
+ * <p>
+ * Note when your last remote call is unsuccessful, the result will
+ * still have the same value like the last successful call (or null, if no
+ * call was successful). To ensure you do not work with old call's results,
+ * either be sure to {@link #reset()} (or at least {@link #clearResult()})
+ * your instance before each new call or always check that
+ * {@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>
+ * Unlike {@link #getResult()}, which only returns any en-/decrypted
+ * message, this function returns the complete information that was returned
+ * by Apg. This also includes the "RESULT", but additionally the warnings,
+ * errors and any other information.
+ * </p>
+ * <p>
+ * For warnings and errors it is suggested to use the functions that are
+ * provided here, namely {@link #getError()}, {@link #getNextError()},
+ * {@link #get_next_Warning()} etc.), but if any call returns something non
+ * standard, you have access to the complete result bundle to extract the
+ * information.
+ * </p>
+ *
+ * @return the complete result bundle of the last call to apg
+ */
+ public Bundle getResultBundle() {
+ return mResult;
+ }
+
+ public error getConnectionStatus() {
+ return mConnectionStatus;
+ }
+
+ /**
+ * Clears all unfetched errors
+ *
+ * @see #getNextError()
+ * @see #hasNextError()
+ */
+ public void clearErrors() {
+ mErrorList.clear();
+ mResult.remove(ret.ERROR.name());
+ }
+
+ /**
+ * Clears all unfetched warnings
+ *
+ * @see #getNextWarning()
+ * @see #hasNextWarning()
+ */
+ public void clearWarnings() {
+ mWarningList.clear();
+ }
+
+ /**
+ * Clears the last mResult
+ *
+ * @see #getResult()
+ */
+ public void clearResult() {
+ mResult.remove(ret.RESULT.name());
+ }
+
+ /**
+ * Set a callback listener when call to AIDL finishes
+ *
+ * @param obj
+ * a object to call back after async execution
+ * @see ApgConInterface
+ */
+ public void setOnCallFinishListener(OnCallFinishListener lis) {
+ mOnCallFinishListener = lis;
+ }
+
+ /**
+ * Clears any callback object
+ *
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public void clearOnCallFinishListener() {
+ mOnCallFinishListener = null;
+ }
+
+ /**
+ * Checks if an async execution is running
+ *
+ * <p>
+ * If you started something with {@link #callAsync(String)}, this will
+ * return true if the task is still running
+ * </p>
+ *
+ * @return true, if an async task is still running, false otherwise
+ *
+ * @see #callAsync(String)
+ *
+ */
+ public boolean isRunning() {
+ return mAsyncRunning;
+ }
+
+ /**
+ * Completely resets your instance
+ *
+ * <p>
+ * This currently resets everything in this instance. Errors, warnings,
+ * results, callbacks, ... are removed. Any connection to the remote
+ * interface is upheld, though.
+ * </p>
+ *
+ * <p>
+ * Note when an async execution ({@link #callAsync(String)}) is
+ * running, it's result, warnings etc. will still be evaluated (which might
+ * be not what you want). Also mind that any callback you set is also
+ * reseted, so when finishing the execution any before defined callback will
+ * NOT BE TRIGGERED.
+ * </p>
+ */
+ public void reset() {
+ clearErrors();
+ clearWarnings();
+ clearArgs();
+ clearOnCallFinishListener();
+ mResult.clear();
+ }
+
+}