HiddenApiRefinePlugin

repository·main·Indexed 43 days ago

https://github.com/rikkaapps/hiddenapirefineplugin

A Gradle plugin that simplifies the use of hidden Android APIs in system tools. It uses the Android Gradle Plugin Transform API to rename user-defined stub classes to match real system classes via the @RefineAs annotation, enabling a linking approach that avoids reflection and common Kotlin or R8 issues.

Tokens
1.3K
Snippets
4
Records
4
Agent score
6%

What's inside HiddenApiRefinePlugin

  1. Integrate HiddenApiRefinePlugin into your Android application

    main

    To use the hidden APIs in your main application module, follow these steps:

    1. Apply the plugin: Apply dev.rikka.tools.refine to your Android application module. If you have multiple modules using hidden APIs, apply it to all of them. You can apply it to all Android modules at once from your root build.gradle using the subprojects block.
    2. Import the stub module: Add your hidden-api module using compileOnly so that the stubs are available at compile time but do not exist in the final APK.
    3. (Optional) Add the runtime library: If you need the Refine.unsafeCast method, add the dev.rikka.tools.refine:runtime dependency via implementation.
    // 1. Apply plugin in application module (or root build.gradle)
    plugins {
        id('dev.rikka.tools.refine') version '<version>'
    }
    
    // OR apply to all subprojects in root build.gradle
    subprojects {
        plugins.withId("com.android.base") {
            plugins.apply('dev.rikka.tools.refine')
        }
    }
    
    // 2. Import stub module
    dependencies {
        compileOnly project(':hidden-api')
    }
    
    // 3. Optional: Import runtime for unsafeCast
    // implementation("dev.rikka.tools.refine:runtime:<version>")
  2. Create a 'hidden-api' module

    main

    To use hidden APIs via the linking way without the typical drawbacks (like bridge classes or Kotlin linking issues), you must create a stub module. This module contains classes that mimic the structure of the hidden Android APIs.

    1. Create a new Android library module.
    2. Add the necessary annotation dependencies to the module's build.gradle.
    3. Define your hidden classes using the @RefineAs annotation to map your stub classes to the actual Android system classes.
    dependencies {
        annotationProcessor 'dev.rikka.tools.refine:annotation-processor:<version>'
        compileOnly 'dev.rikka.tools.refine:annotation:<version>'
    }
  3. Use hidden APIs with Refine.unsafeCast

    main

    Once the plugin is configured, you can access hidden APIs by casting the system object to your stub class using Refine.<StubClass>unsafeCast(systemObject).

    Because the plugin renames the classes during the transform phase and R8 inlines/removes the cast, the resulting bytecode is identical to a normal method call on the real system object, avoiding the overhead and complexity of reflection.

    Refine.<PackageManagerHidden>unsafeCast(context.getPackageManager())
                 .addOnPermissionsChangeListener(new PackageManagerHidden.OnPermissionsChangedListener() {
                     @Override
                     public void onPermissionsChanged(int uid) {
                         // do stuff
                     }
                 });
  4. Use the @RefineAs annotation to map stub classes

    main

    The @RefineAs annotation tells the HiddenApiRefinePlugin to rename your stub classes and their members to match the target Android system classes during the build process. This allows you to use your stub classes in your code while the plugin ensures they are treated as the real system classes at runtime.

    Example: Mapping a stub PackageManagerHidden to the real android.content.pm.PackageManager.

    package android.content.pm;
    
    import dev.rikka.tools.refine.RefineAs;
    
    @RefineAs(PackageManager.class)
    public class PackageManagerHidden {
        public interface OnPermissionsChangedListener {
            void onPermissionsChanged(int uid);
        );
    
        public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
            throw new RuntimeException("Stub!");
        }
        public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
            throw new RuntimeException("Stub!");
        }
    }