# How to Implement the FadRec Screen Recording Accessibility Service in Android

> Learn to implement the FadRec screen recording accessibility service in Android with code examples. Capture screenshots using HardwareBuffer or system global actions.

- Repository: [Faded/FadCam](https://github.com/anonfaded/FadCam)
- Tags: how-to-guide
- Published: 2026-05-13

---

**The FadRec screen recording feature uses an Android `AccessibilityService` that listens for a custom broadcast intent to trigger screenshot capture via modern `HardwareBuffer` APIs on Android R+ or system global actions on older versions.**

The `FadRecScreenshotAccessibilityService` in the [anonfaded/FadCam](https://github.com/anonfaded/FadCam) repository enables screen recording through an accessibility-based architecture. This service coordinates screenshot capture by registering a `BroadcastReceiver` that responds to application-specific intents, allowing seamless integration with the FadCam recording workflow.

## Service Architecture and Lifecycle

The service implements a broadcast-driven architecture defined in [`FadRecScreenshotAccessibilityService.java`](https://github.com/anonfaded/FadCam/blob/main/FadRecScreenshotAccessibilityService.java). It registers a private `BroadcastReceiver` during `onServiceConnected` to listen exclusively for the `ACTION_TRIGGER_FADREC_SCREENSHOT` intent.

```java
// Inside FadRecScreenshotAccessibilityService.java
private final BroadcastReceiver triggerReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null ||
            !Constants.ACTION_TRIGGER_FADREC_SCREENSHOT.equals(intent.getAction())) {
            return;
        }
        FLog.d(TAG, "Trigger broadcast received in accessibility service.");
        captureNow();
    }
};

private void ensureReceiverRegistered() {
    if (receiverRegistered) return;
    IntentFilter filter = new IntentFilter(Constants.ACTION_TRIGGER_FADREC_SCREENSHOT);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        registerReceiver(triggerReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
    } else {
        registerReceiver(triggerReceiver, filter);
    }
    receiverRegistered = true;
}

```

The service maintains a "pending capture" flag in shared preferences (`PREFS_NAME = "fadrec_screenshot_shortcut"`) to handle race conditions where the broadcast arrives before the service fully initializes.

## Triggering Screenshots from Your Activity

To initiate capture, [`ScreenShotCaptureActivity.java`](https://github.com/anonfaded/FadCam/blob/main/ScreenShotCaptureActivity.java) first validates that the accessibility service is enabled using `FadRecScreenshotAccessibilityService.isServiceEnabled()`. If disabled, it redirects users to system accessibility settings. Otherwise, it marks a pending capture and broadcasts the trigger intent.

```java
// Inside ScreenShotCaptureActivity.java
if (!FadRecScreenshotAccessibilityService.isServiceEnabled(this)) {
    // Prompt the user to enable the service
    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
    startActivity(intent);
    return;
}

// Mark that a capture is pending, then send the broadcast
FadRecScreenshotAccessibilityService.markPendingCapture(this);
Intent triggerIntent = new Intent(Constants.ACTION_TRIGGER_FADREC_SCREENSHOT);
sendBroadcast(triggerIntent);

```

This pattern ensures the service receives the trigger even if it restarts between the activity check and broadcast delivery.

## Screenshot Capture Implementation

The `captureNow()` method implements version-specific screenshot logic. On **Android R (API 30)** and above, it uses the accessibility service's native `takeScreenshot()` method with `Display.DEFAULT_DISPLAY`, receiving a `ScreenshotResult` containing a `HardwareBuffer`.

```java
private void captureNow() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        // Modern API – returns a ScreenshotResult with a HardwareBuffer
        takeScreenshot(Display.DEFAULT_DISPLAY, Executors.newSingleThreadExecutor(),
            new TakeScreenshotCallback() {
                @Override public void onSuccess(@NonNull ScreenshotResult result) {
                    saveScreenshotResult(result);
                }
                @Override public void onFailure(int errorCode) {
                    showToast(R.string.screenshot_capture_failed);
                }
            });
    } else {
        // Fallback for Android 9/10
        boolean ok = performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT);
        showToast(ok ? R.string.screenshot_capture_system_saved
                    : R.string.screenshot_capture_failed);
    }
}

```

For **Android 9-10 (API 28-29)**, the service falls back to `performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT)`, delegating capture to the system screenshot mechanism.

## Saving and Broadcasting Results

Upon successful capture on modern Android versions, the service converts the `HardwareBuffer` to a `Bitmap`, creates an ARGB copy, and persists it via `PhotoStorageHelper.saveJpegBitmap()`.

```java
private void saveScreenshotResult(@NonNull ScreenshotResult result) {
    HardwareBuffer hb = result.getHardwareBuffer();
    Bitmap hbBitmap = Bitmap.wrapHardwareBuffer(hb, result.getColorSpace());
    Bitmap copy = hbBitmap.copy(Bitmap.Config.ARGB_8888, false);
    Uri uri = PhotoStorageHelper.saveJpegBitmap(
            getApplicationContext(),
            copy,
            false,
            PhotoStorageHelper.ShotSource.FADREC);
    // …broadcast success/failure as shown earlier…
}

```

After saving, the service clears the pending capture flag and broadcasts `Constants.ACTION_RECORDING_COMPLETE` with the saved URI, allowing other application components to process the new screenshot immediately.

## Summary

- The **FadRecScreenshotAccessibilityService** registers a dedicated `BroadcastReceiver` for `ACTION_TRIGGER_FADREC_SCREENSHOT` intents during service connection.
- **Trigger validation** occurs in [`ScreenShotCaptureActivity.java`](https://github.com/anonfaded/FadCam/blob/main/ScreenShotCaptureActivity.java), which checks service status via `isServiceEnabled()` before marking captures pending and broadcasting.
- **Modern Android (API 30+)** uses `takeScreenshot(Display.DEFAULT_DISPLAY, ...)` with `HardwareBuffer` processing, while older versions rely on `performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT)`.
- The **PhotoStorageHelper** class handles JPEG persistence, after which the service broadcasts completion with the content URI.

## Frequently Asked Questions

### How does the FadRec service receive capture commands?

The service implements a private `BroadcastReceiver` registered in `onServiceConnected` that listens exclusively for `Constants.ACTION_TRIGGER_FADREC_SCREENSHOT`. Applications send this broadcast via `ScreenShotCaptureActivity` to trigger the `captureNow()` method.

### What Android versions support the screenshot API?

**Android R (API 30)** and above support the native `takeScreenshot()` method with `HardwareBuffer` return. Android 9 and 10 (API 28-29) use the fallback `performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT)`, while earlier versions require alternative capture methods not implemented in this service.

### How is the screenshot saved after capture?

On API 30+, the service extracts the `HardwareBuffer` from the `ScreenshotResult`, wraps it into a `Bitmap`, creates a mutable ARGB copy, and passes it to `PhotoStorageHelper.saveJpegBitmap()` with the `FADREC` shot source identifier. The method returns a content URI that gets broadcast to the application.

### Why use an AccessibilityService instead of MediaProjection?

The FadRec implementation leverages `AccessibilityService` privileges to call `takeScreenshot()` without requiring the `MediaProjection` consent dialog on every capture. This provides a seamless user experience for automated screenshot workflows, though it requires the user to enable the service in system accessibility settings.