Skip to content

SootUtils

Jordan Samhi edited this page Oct 23, 2026 · 2 revisions

SootUtils

SootUtils provides instance methods over the current global Soot scene. Create it with new SootUtils(); construction does not initialize or own Soot. For managed initialization and graph snapshots, use Analysis sessions and Call graphs and inspection.

Returned SootClass, SootMethod, and SootMethodRef values are live analysis objects. Fresh collection containers do not detach their contents: use them only while the relevant scene is valid and do not reuse them after G.reset() or session closure. Queries read the current scene/graph rather than retaining results across resets.

Use within an initialized session

This complete SootClassSummary.java application takes <apk> <platforms> <class-name> <method-sub-signature>. The class and method must exist in the loaded model. Quote a sub-signature such as 'void onCreate(android.os.Bundle)' as one shell argument.

import com.jordansamhi.androspecter.SootUtils;
import com.jordansamhi.androspecter.analysis.AndroidAnalysis;
import java.nio.file.Path;
import soot.Scene;

/**
 * Command-line example using live Soot utilities within a managed analysis lifetime.
 * The default constructor performs no analysis; invoke {@link #main(String[])} to run it.
 */
public final class SootClassSummary {
    /**
     * Prints hierarchy, component, statement-count, and graph information for a loaded method.
     * The requested class and declared method must exist in the processed model.
     *
     * @param args APK path, SDK platforms directory, class name, and full method sub-signature
     * @throws IllegalArgumentException if the argument count or input paths are invalid
     * @throws RuntimeException if initialization, class/method lookup, or body loading fails
     */
    public static void main(String[] args) {
        if (args.length != 4) {
            throw new IllegalArgumentException(
                    "Usage: SootClassSummary <apk> <platforms> <class-name> <method-sub-signature>");
        }
        try (var analysis = AndroidAnalysis.open(Path.of(args[0]), Path.of(args[1]))) {
            analysis.context().checkValid();
            var utils = new SootUtils();
            var clazz = Scene.v().getSootClass(args[2]);
            var method = clazz.getMethod(args[3]);
            System.out.println("Superclasses: "
                    + utils.getClassNames(utils.getAllSuperClasses(clazz)));
            System.out.println("Component kind: " + utils.getComponentType(clazz));
            System.out.println("Processed Jimple units: " + utils.getNumberOfStmt(method));
            System.out.println("In graph: " + utils.isInCallGraph(method, Scene.v().getCallGraph()));
        }
    }
}

Class and method utilities

Method Scope and behavior
getMethodRef(className, methodSubSignature) Looks up a declared method using its full sub-signature, including return and parameter types; logs resolution failure and returns null
getAllSuperClasses(clazz) Returns the immediate superclass and its ancestors
getAllInterfaces(clazz) Returns directly implemented interfaces and their superinterfaces; does not traverse the class's superclass chain
getClassNames(classes) Copies class names into a set of strings
getClasses() / getAllClasses() Current scene classes except the exact dummy-main class; includes framework and library classes
getAllMethods() All methods of those scene classes
getMethodsFromPackage(prefix) Concrete methods of concrete classes whose class name starts with the supplied string; this is not a package-boundary match
getNonLibraryClasses() / getAllMethodsExceptLibraries() Excludes dummy main and classes classified by the current LibrariesManager policy
getComponentType(clazz) Infers Activity, Service, BroadcastReceiver, or ContentProvider from the superclass chain; otherwise NON_COMPONENT
isDummyMainClass(clazz) Tests the exact dummy-main class name used by the library

Component classification is a hierarchy check, not a check of manifest registration. Library classification is separate from Soot's application-class flag. For an explicit package-boundary or library predicate, use Inspection and LibraryFilter.

What statement counts measure

getNumberOfStmt(method) retrieves the active body of a concrete method and counts its Jimple units; nonconcrete methods contribute zero. getNumberOfStmt(methods) sums that measure for the supplied collection. Body loading can fail and can be expensive.

Despite their historical names:

  • getNumberOfStmtInApp() sums over the current scene, excluding dummy main. It can include loaded framework/library code.
  • getNumberOfStmtInAppWithoutLibraries() excludes the legacy known-library policy as well as dummy main. It does not mean “only classes marked application by Soot.”

These are processed-model counts, potentially including generated helpers, identity statements, and returns. They are not original DEX instruction counts. Select an explicit class/method scope before counting when comparing APKs.

Live call-graph utilities

Method Behavior
countEdgesInCallGraph(graph) Total graph edge count
countEdgesWithNonLibraryTargets(graph) Edges whose target belongs to a current scene class outside dummy main and the known-library policy
getMethodsInCallGraph(graph) Source/target methods incident to edges, excluding dummy-main methods
getCountOfNodes(graph) Size of the preceding incident-method set
getMethodsInCallGraphExceptLibraries(graph) Further restricts incident methods to current non-library classes
isInCallGraph(method, graph) Tests incident membership, excluding dummy main
isCalledInCallGraph(method, graph) Tests whether a current edge targets the method, excluding dummy main
getCallGraphAdjacencyList(graph) New mutable map of source methods to target lists; one target entry per edge, including repeated targets; contents remain live Soot methods

For repeated queries, prefer the indexed immutable CallGraphSnapshot. Its node set intentionally includes generated dummy-main nodes, so its count can differ from getCountOfNodes. Neither representation includes declared methods with no graph edge.

Low-level setup

setupSoot(platformsPath, apkPath, wholeAnalysis) resets Soot, selects APK input, configures whole-program mode, allows phantom references, and loads necessary classes with output disabled. setupSootWithOutput(platformsPath, apkPath, outputPath, wholeAnalysis) additionally configures DEX output and overwrite behavior.

These methods configure and load Soot; they do not construct FlowDroid's Android lifecycle entry points, run the complete analysis packs, write/sign an APK, or produce a completed Android call graph by themselves. Use FlowdroidUtils or the managed session for Android-aware construction. Never call these reset-performing initializers inside an active managed session.

Source: SootUtils.java.

Clone this wiki locally