npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-shield-guard

v0.1.2

Published

A robust React Native library for detecting root, jailbreak, and emulator environments.

Readme

react-native-shield-guard

A React Native TurboModule that detects Android emulators at the native C++ layer. On Android it performs 7 independent native checks; on iOS and web the functions are safe no-ops that always return false / "".

Platform scope — Active detection runs on Android only. The library ships a compiled-to-spec iOS stub so your project builds on every platform without #ifdef guards in JavaScript.


Table of Contents

  1. How it works
  2. What emulators are detected
  3. Requirements
  4. Installation
  5. Android Setup
  6. Expo (managed & bare workflow)
  7. Usage
  8. API Reference
  9. Understanding getResult()
  10. Platform Behaviour Summary
  11. Troubleshooting
  12. Contributing
  13. License

How it works

When you call isDetected() for the first time, the native C++ engine runs seven sequential checks. Each check appends a human-readable line to an internal list; if that list is non-empty after all checks, isDetected() returns true. You can retrieve every finding as a newline-separated string via getResult().

The seven checks (from EmulatorDetection.cpp):

| # | Check | What it examines | |---|-------|-----------------| | 1 | Hardware properties | ro.build.product, ro.product.manufacturer, ro.bootloader, ro.product.device, ro.hardware — compared against known emulator values | | 2 | Mount points | /proc/mounts — looks for vboxsf (VirtualBox shared-folder driver used by many emulators) | | 3 | CPU info | /proc/cpuinfo — searches for hypervisor, amd, and intel, which appear on x86 host machines running an Android VM | | 4 | Emulator files | Checks for the presence of 70+ known emulator-specific files and kernel modules across /system, /data, /dev, /sys, and /boot | | 5 | CPU architecture | ro.product.cpu.abilist — flags x86 architecture, which almost always means an emulator | | 6 | ARM-translation layer | Checks for libhoudini.so in loaded libraries and on disk, plus ro.dalvik.vm.native.bridge and ro.dalvik.vm.isa.arm* properties — all indicators of an x86 emulator running ARM apps through a translation shim | | 7 | Bluestacks memory scan | Reads /proc/self/maps and scans the memory regions of libc.so, libandroid_runtime.so, and libart.so for the string "bluestacks" |

All checks are implemented in native C++ (EmulatorDetection.cpp) and exposed to the React Native JS layer via a TurboModule (NativeShieldGuard.ts). The native code is compiled into libemulatordetector.so via CMake and loaded by the Java class com.reveny.emulatordetector.plugin.EmulatorDetection through System.loadLibrary("emulatordetector").


What emulators are detected

The detection database lives in EmulatorData.hpp. The library actively targets:

By hardware/product/device system properties: sdk, google_sdk, sdk_x86, andy, nox, droid4x, vbox86p, emu64x

By hardware name (ro.hardware): goldfish (Android Studio AVD), vbox86 (VirtualBox-based), nox, VM_x86, intel, amd, x86

By emulator-specific file presence:

| Emulator | Example fingerprint files | |----------|--------------------------| | Android Studio AVD | /dev/socket/qemud, /dev/qemu_pipe, hardware goldfish | | NoxPlayer | /system/bin/nox, /system/bin/noxd, /system/lib/libnoxd.so | | BlueStacks | /data/.bluestacks.prop, /dev/bst_gps, /sys/module/bstpgaipc | | LDPlayer | /system/lib/hw/gps.ld.so, /system/bin/ldinit, /system/app/LDAppStore/LDAppStore.apk | | Memu | /dev/memufp, /dev/memuguest, /system/lib/memuguest.ko | | PhoenixOS | /system/xbin/phoenix_compat, /system/phoenixos | | Genymotion | /system/bin/genybaseband, VirtualBox kernel modules | | KOPlayer | /system/bin/KOPLAYER.ini | | Droid4x | /system/bin/droid4x, /system/bin/droid4x-vbox-sf | | VirtualBox (generic) | /lib/vboxguest.ko, /lib/vboxsf.ko, /sys/module/vboxsf |


Requirements

| Requirement | Version | |-------------|---------| | React Native | ≥ 0.73 (New Architecture / TurboModules required) | | Android API | 24 (Android 7.0) minimum | | NDK | 27.1 or newer (auto-inherited from the host app) | | CMake | ≥ 3.13 (bundled with the Android SDK) | | iOS | Any (library compiles; always returns false / "") |

New Architecture is required. This is a TurboModule. It will not work with the legacy bridge (newArchEnabled=false).


Installation

# yarn (recommended)
yarn add react-native-shield-guard

# npm
npm install react-native-shield-guard

The library uses React Native autolinking — no react-native link step is needed on RN 0.73+.


Android Setup

For most projects no manual changes are needed after installation. Autolinking wires everything up automatically.

Verify autolinking resolved correctly

npx react-native config

Under "dependencies" you should see "react-native-shield-guard" with an "android" block containing a "cmakeListsPath" key.

Pinning the NDK version (optional)

The library inherits the ndkVersion from your host app's Gradle configuration. To pin an explicit version, add to android/gradle.properties in your app:

ndkVersion=27.1.12297006

First-time clean build

After adding the library for the first time, a clean build clears any cached CMake state:

cd android && ./gradlew clean && cd ..
npx react-native run-android

Confirming the native library is bundled

After a successful build you can verify libemulatordetector.so was produced:

android/app/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a/libemulatordetector.so

The library is built for all four ABIs: arm64-v8a, armeabi-v7a, x86, x86_64.


Expo (managed & bare workflow)

The library ships an Expo Config Plugin (app.plugin.js). The plugin is a passthrough — it registers the library with Expo's plugin system so it is correctly resolved during expo prebuild. No additional Gradle configuration is injected because React Native autolinking handles everything automatically.

Step 1 — Register the plugin

Add the plugin to your app.json or app.config.js:

{
  "expo": {
    "plugins": [
      "react-native-shield-guard"
    ]
  }
}

Step 2 — Run prebuild

npx expo prebuild

This generates (or regenerates) the native android/ and ios/ directories with the library already linked.

Step 3 — Build

npx expo run:android

New Architecture — Make sure newArchEnabled=true is present in the generated android/gradle.properties (default from Expo SDK 51+).


Usage

Basic — block the app on emulators

import React, { useEffect } from 'react';
import { Alert } from 'react-native';
import { isDetected } from 'react-native-shield-guard';

export default function App() {
  useEffect(() => {
    if (isDetected()) {
      Alert.alert(
        'Unsupported Environment',
        'This application cannot run on an Android emulator.',
      );
    }
  }, []);

  // ... rest of app
}

Display detection details

import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { isDetected, getResult } from 'react-native-shield-guard';

export default function App() {
  const [detected, setDetected] = useState<boolean | null>(null);
  const [log, setLog] = useState('');

  useEffect(() => {
    // Both calls are synchronous
    const found = isDetected();
    setDetected(found);
    if (found) {
      setLog(getResult());   // only meaningful after isDetected()
    }
  }, []);

  return (
    <View style={styles.container}>
      <Text>Running on emulator: {detected === null ? '...' : detected ? 'YES' : 'NO'}</Text>
      {log ? <Text style={styles.log}>{log}</Text> : null}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', padding: 24 },
  log: { fontFamily: 'monospace', fontSize: 12, backgroundColor: '#eee', padding: 10, marginTop: 12 },
});

Blocking pattern — prevent rendering entirely

The cleanest way to block an emulator is at the app entry point, before any React tree mounts:

// index.js
import { AppRegistry } from 'react-native';
import { isDetected } from 'react-native-shield-guard';
import App from './App';
import BlockedScreen from './BlockedScreen';
import { name as appName } from './app.json';

// isDetected() is synchronous — safe to call here
const Root = isDetected() ? BlockedScreen : App;

AppRegistry.registerComponent(appName, () => Root);

API Reference

Import everything from 'react-native-shield-guard'.


isDetected(): boolean

Runs all 7 native emulator checks on Android. Returns true if any check finds emulator-specific evidence.

  • Synchronous — no Promise, no callback.
  • Stateful — results are cached in the native layer. Calling it multiple times is safe and cheap after the first call.
  • On iOS / web always returns false (no native code runs).
import { isDetected } from 'react-native-shield-guard';

const onEmulator: boolean = isDetected();

| Return | Meaning | |--------|---------| | true | One or more emulator indicators were found on this Android device. | | false | No emulator indicators found, or the platform is iOS / web. |


getResult(): string

Returns a newline-separated string listing every emulator indicator that was found during the last isDetected() call.

  • Returns "" if nothing was detected, or if isDetected() has not been called yet.
  • Must be called after isDetected() — the internal detection list is only populated during isDetected().
  • On iOS / web always returns "".
import { isDetected, getResult } from 'react-native-shield-guard';

if (isDetected()) {
  const details = getResult();
  console.log(details);
  // Example output on an Android Studio AVD:
  //   - Detected Product: sdk
  //   - Detected Hardware: goldfish
  //   - Detected x86 Architecture
  //   - Detected Hypervisor in cpuinfo
}

Understanding getResult()

Each line corresponds to one positive finding from the 7 native checks:

| Output line | Source check | |---|---| | - Detected Product: <value> | Hardware props — matched ro.build.product | | - Detected Manufacturer: <value> | Hardware props — matched ro.product.manufacturer | | - Detected Bootloader: <value> | Hardware props — ro.bootloader == "nox" | | - Detected Device: <value> | Hardware props — matched ro.product.device | | - Detected Hardware: <value> | Hardware props — matched ro.hardware | | - Detected VM Module in Mounts | Mount check — vboxsf in /proc/mounts | | - Detected Hypervisor in cpuinfo | CPU info — "hypervisor" in /proc/cpuinfo | | - Detected Host CPU in cpuinfo | CPU info — "amd" or "intel" in /proc/cpuinfo | | - Detected Emulator File: <path> | File check (primary list) | | - Detected Emulator File (2): <path> | File check (extended list for newer emulator versions) | | - Detected Nox Property File | File check — "nox" string in /data/property/ listing | | - Detected x86 Architecture | ABI check — x86 in ro.product.cpu.abilist | | - Detected ARM Translation | ARM translation — libhoudini.so in loaded process libraries | | - Detected ARM Translation Property | ARM translation — ro.dalvik.vm.native.bridge is non-empty | | - Detected ARM Translation Library | ARM translation — libhoudini.so exists on disk | | - Detected ARM Translation Property (2) | ARM translation — ro.dalvik.vm.isa.arm mapped to x86 | | - Detected Bluestacks Memory | Memory scan — "bluestacks" string found in process memory maps |


Platform Behaviour Summary

| Platform | isDetected() | getResult() | C++ code runs | |----------|---------------|--------------|---------------| | Android | Runs all 7 checks | Returns detection lines | ✅ Yes | | iOS | Always false | Always "" | ❌ Stub only | | Web | Always false | Always "" | ❌ JS fallback |

The iOS stub is intentional. A TurboModule must have a native implementation on every registered platform for the project to compile, so the library ships a valid but inert Obj-C++ implementation that satisfies the codegen-generated protocol (NativeShieldGuardSpec).


Troubleshooting

TurboModuleRegistry.getEnforcing … 'ShieldGuard' could not be found

The New Architecture is required. Check android/gradle.properties:

newArchEnabled=true

Build error: react_codegen_ShieldGuardSpec already exists

This is a monorepo-specific issue. It occurs when the app's react.root is set to the repository root, causing React Native to run codegen twice (once for the library, once for the app) inside the same Gradle build.

In android/app/build.gradle of the host/example app, ensure root points to the app directory, not the monorepo root:

react {
    // Correct: points to the directory that contains the app's package.json
    root = file("../..")

    // Also correct the paths of related directories accordingly:
    reactNativeDir = file("../../../node_modules/react-native")
    codegenDir     = file("../../../node_modules/@react-native/codegen")
}

After fixing, run ./gradlew clean before rebuilding.


Build error: APP_BUILD_SCRIPT points to an unknown file

This error comes from the legacy ndk-build system failing when the project path contains spaces. The library has already been migrated to CMake (android/CMakeLists.txt). If you see this error, ensure you are on a version of the library that uses CMake (the externalNativeBuild block in android/build.gradle should reference cmake, not ndkBuild).


isDetected() returns false on a known emulator

The detection database is maintained in EmulatorData.hpp. Some newer emulator versions (e.g. BlueStacks 5+, modern cloud-based Android runners) have improved at disguising themselves. If a particular emulator is not being caught:

  1. Run the app on it and print getResult() to see which checks did fire.
  2. Inspect the emulator's system properties (adb shell getprop) and look for values not yet in EmulatorData.hpp.
  3. Add the missing signatures following the Contributing guide.

Warning: CXX5304 — This version only understands SDK XML version 4

This is a non-fatal warning produced when the bundled CMake version is slightly behind the installed Android SDK tools. The build succeeds normally. To eliminate it, update your Android SDK Build Tools via Android Studio → SDK Manager → SDK Tools → Android SDK Build Tools.


Contributing

Adding a new emulator to the detection database

All detection data lives in:

android/src/main/jni/Include/EmulatorData.hpp

Add the emulator's identifying values to the appropriate vectors:

// products vector — value of ro.build.product
inline std::vector<std::string> products {
    // ... existing entries ...
    "your_emulator_product",
};

// manufacturers vector — value of ro.product.manufacturer
// hardwares vector — value of ro.hardware
// deviceInfo vector — value of ro.product.device
// emulatorFiles vector — emulator-specific file paths

Adding a new detection technique

  1. Write a void checkYourTechnique() function in EmulatorDetection.cpp that calls detections.push_back(...) for each finding.
  2. Declare the function in EmulatorDetection.hpp.
  3. Call it from isDetected() alongside the existing checks.

Running the example app locally

# From the library root
yarn install
yarn example android

License

MIT — see LICENSE for details.