Repository navigation
FlowdroidUtils
FlowdroidUtils is the low-level FlowDroid wrapper. For new integrations, Analysis sessions provide lifecycle checks, instance settings, and detached results through AndroidAnalysis. Use this wrapper directly when the surrounding application already owns and coordinates global Soot state.
new FlowdroidUtils(apkPath) stores a non-null APK path without opening it. Call initializeFlowdroid(platformsPath, sootConfig, algorithm) before running taint analysis:
-
platformsPathis the Android SDK platforms directory. - A null
sootConfigselects AndroSpecter'sSootConfig. Otherwise, pass anIInfoflowConfigcallback. - Algorithms are
CHA,RTA,VTA, orSPARK, ignoring case and surrounding whitespace; null selectsCHA. Unsupported values throwIllegalArgumentException. - Initialization calls
G.reset(), disables code elimination, enables precise path reconstruction, and constructs Android entry points and a call graph. It then configures subsequent taint runs to reuse the current Soot instance. - The returned
SetupApplicationis live and mutable. It must not be reused after the relevant Soot state is reset. The wrapper does not detect external resets or acquire the managed-session guard.
A fourth argument, Consumer<InfoflowAndroidConfiguration>, can customize options after defaults and before backend construction. For example, configuration -> configuration.setDataFlowTimeout(60) sets the backend data-flow timeout in seconds. This low-level callback receives the live configuration and may override defaults; it does not provide the managed builder's copying or protected-input validation. Reinitialization discards the previous initialization and clears cancellation; a failed attempt does not restore the old scene.
Do not initialize this wrapper while an AndroidAnalysis or another Soot user is active. Except for abortAnalysis(), operations on one wrapper must not run concurrently. The caller owns final cleanup.
The five-argument overload adds InitializationMode after the customizer:
initializeFlowdroid(platformsPath, sootConfig, algorithm, customizer, initializationMode)
InitializationMode.EAGER preserves the existing behavior. CLASSES_ONLY initializes APK class loading with the Android platform and additional classpath, prepares Jimple, and patches framework models, but does not discover Android callbacks or build a graph. It uses FlowDroid-compatible Soot input settings and respects the supplied Soot-options callback, including exclusions. Class loading itself can still load supporting classes and prepare bodies; this is not manifest-only parsing.
The returned application is configured with SootIntegrationMode.UseExistingInstance. A later application.constructCallgraph() or wrapper taint run builds its Android prerequisites without resetting the loaded scene. Graph construction configures the selected algorithm at promotion. This preserves intervening scene/body edits under the default no-code-elimination configuration. The caller must continue to own the same Soot generation throughout.
Class-only initialization rejects a customizer that selects an existing-scene integration mode before loading: this initializer creates and owns its new scene. A class-loading failure or observed cancellation leaves the wrapper uninitialized; subsequent taint calls throw until initialization succeeds. Cancellation during loading remains cooperative.
For application code, prefer the managed builder's initializationMode(CLASSES_ONLY), pathReconstruction(...), callbacks(...), additionalClasspath(...), and guarded configureFlowDroid(...). Analysis sessions documents their exact defaults, validation, and a scoped example. Direct wrapper customization deliberately remains an advanced caller-owned API.
Save as LowLevelTaint.java, using the JDK 17/source-build dependency setup in Analysis sessions. Arguments are <apk> <platforms> <sources-sinks-file>. This example must have exclusive ownership of Soot for its whole run.
import com.jordansamhi.androspecter.FlowdroidUtils;
import com.jordansamhi.androspecter.files.SourceSinkConfiguration;
import com.jordansamhi.androspecter.result.TaintAnalysisResult;
import com.jordansamhi.androspecter.result.TaintResultFormatter;
import java.nio.file.Path;
import soot.G;
/**
* Command-line example for callers that own and coordinate Soot's global lifecycle themselves.
* The default constructor performs no analysis; invoke {@link #main(String[])} to run it.
*/
public final class LowLevelTaint {
/**
* Initializes FlowDroid, runs the file-defined taint query, resets Soot, and prints detached findings.
* No managed session or other Soot user may be active during this call. Printed findings may be partial.
*
* @param args APK path, SDK platforms directory, and source/sink definition file
* @throws IllegalArgumentException if the argument count or source/sink definitions are invalid
* @throws RuntimeException if definitions cannot be read or backend initialization fails
*/
public static void main(String[] args) {
if (args.length != 3) {
throw new IllegalArgumentException(
"Usage: LowLevelTaint <apk> <platforms> <sources-sinks-file>");
}
var definitions = SourceSinkConfiguration.fromFile(Path.of(args[2]));
TaintAnalysisResult result;
try {
var flowdroid = new FlowdroidUtils(args[0]);
flowdroid.initializeFlowdroid(args[1], null, "SPARK",
configuration -> configuration.setDataFlowTimeout(60));
result = flowdroid.runTaintAnalysisResult(
definitions.toSourceMethods(), definitions.toSinkMethods());
} finally {
G.reset();
}
System.out.println("Status: " + result.status());
result.diagnostics().forEach(System.err::println);
for (var finding : result.findings()) {
System.out.println(TaintResultFormatter.formatFinding(finding));
}
}
}The printed findings may be partial: interpret them together with the status. The result remains valid after the finally block because it contains detached values.
| Method | Contract |
|---|---|
runTaintAnalysisResult(sources, sinks) |
Returns an immutable result with a fresh analysis UUID |
runTaintAnalysisResult(sources, sinks, analysisId) |
Uses a caller-supplied UUID to associate results with other records |
runTaintAnalysis(sources, sinks) |
Returns a mutable independent set of legacy text reports for successful analysis; identical reports are deduplicated |
The arguments are non-null sets of AndroidMethod descriptors. Do not mutate them during execution. Initialization must have succeeded. Structured results distinguish SUCCESS, FAILED, TIMED_OUT, and CANCELLED, retaining available findings and nullable path/endpoint metadata. Backend exceptions before a result is returned yield no findings. Missing initialization and null arguments throw; JVM errors are not converted to statuses. See Taint analysis for the full data contract.
The legacy text method throws IllegalStateException for failed, timed-out, cancelled, or unavailable results. It returns an empty set only for a successful result with no findings. Flows without reconstructed paths are retained and marked accordingly. A successful result does not certify analysis soundness; parser and typing warnings remain in logs.
abortAnalysis() records cancellation and forwards a best-effort abort request to the current backend. It may be called from another thread, but it does not wait for termination. Callback and call-graph phases may not stop immediately. Retain exclusive Soot ownership until the operation returns. Subsequent taint calls report cancellation until reinitialization clears the request.
getPackageName() lazily reads the APK manifest without requiring initialization. It returns null when the package cannot be obtained and logs read failures. The first outcome, including failure, is cached for that wrapper; later calls do not retry.
Source: FlowdroidUtils.java.
- Analysis sessions
- APK metadata
- Call graphs and inspection
- Graph export and comparison
- Taint analysis
- Instrumentation
- Cancellation and logging
- Migration and troubleshooting
- Publishing releases