Skip to content

AndroidAppsProcessor

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

AndroidAppsProcessor

com.jordansamhi.androspecter.AndroidAppsProcessor is an optional Redis/AndroZoo batch worker. It consumes APK hashes, downloads APKs, calls a subclass's analysis, and records success or failure. It is not needed for a single local APK.

Queue and lifecycle

For a redisRoot of my-analysis, the worker uses these Redis sets:

Key Role
my-analysis:pop Pending SHA-256 hashes, consumed with spop
my-analysis:success Successfully processed hashes
my-analysis:errors Failed or interrupted hashes

The worker skips hashes already in the success set and waits 30 seconds when the pending set is empty. Stop polling by interrupting the thread running run(). Errors are recorded and logged; there is no automatic requeue/retry policy.

The constructor's timeout is in minutes and applies to processApp, which runs on a dedicated analysis thread. processResults runs afterward on the polling thread, outside that timeout. A hash is marked successful only after both hooks return successfully. The caller retains ownership of RedisManager.

On timeout, the analysis thread is interrupted. If it does not terminate within five seconds, the worker stops polling to avoid overlapping analyses. This is cooperative cancellation, not a hard process limit. APK cleanup is deferred until an uncooperative analysis finishes.

Implement a worker

This complete subclass stores a detached graph before returning from the analysis thread. It does not pass live Soot objects to processResults on another thread:

import com.jordansamhi.androspecter.AndroidAppsProcessor;
import com.jordansamhi.androspecter.AndroZooUtils;
import com.jordansamhi.androspecter.analysis.AndroidAnalysis;
import com.jordansamhi.androspecter.graph.CallGraphSnapshot;
import com.jordansamhi.androspecter.network.RedisManager;
import java.nio.file.Path;

public final class GraphWorker extends AndroidAppsProcessor {
    private final Path platforms;
    private CallGraphSnapshot graph;

    /**
     * Creates a worker with a five-minute analysis limit per APK.
     * @param redis caller-owned Redis connection
     * @param downloader downloader using a dedicated worker directory
     * @param platforms Android SDK platforms directory
     */
    public GraphWorker(RedisManager redis, AndroZooUtils downloader, Path platforms) {
        super(redis, downloader, "my-analysis", 5);
        this.platforms = platforms;
    }

    /** Analyzes the downloaded APK on the worker's analysis thread. */
    @Override
    protected void processApp(String apkPath) {
        try (AndroidAnalysis analysis = AndroidAnalysis.open(Path.of(apkPath), platforms)) {
            graph = analysis.callGraph();
        }
    }

    /** Consumes detached results on the polling thread after analysis succeeds. */
    @Override
    protected void processResults() {
        System.out.println("Graph edges: " + graph.edges().size());
    }
}

Place the imports below at the top of your executable's source file, then run the remaining statements inside its main method after creating the download directory. The snippet assumes the subclass above is available:

import com.jordansamhi.androspecter.AndroZooUtils;
import com.jordansamhi.androspecter.network.RedisManager;
import java.nio.file.Path;
import java.util.Objects;

String key = Objects.requireNonNull(System.getenv("ANDROZOO_API_KEY"));
AndroZooUtils downloader = new AndroZooUtils(key, "/absolute/path/to/worker-downloads");
try (RedisManager redis = new RedisManager("localhost", "6379",
        System.getenv("REDIS_PASSWORD"))) {
    new GraphWorker(redis, downloader, Path.of("/absolute/path/to/sdk/platforms")).run();
}

Use a dedicated download directory. The processor deletes the APK path returned by the downloader, including a previously cached APK at that path. Do not use a folder containing files you intend to retain as the worker's cache.

A process should run only one Soot analysis at a time. Scale independent workers across processes. For custom orchestration, use Analysis sessions and Cancellation and logging directly.

Clone this wiki locally