Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c80e7d04 authored by Michael Hoisie's avatar Michael Hoisie Committed by Brett Chabot
Browse files

Cherry pick customize JNI method binding name.

This commit enables the ability to bind to a native methods
that have been renamed according to a fixed pattern.
This capability is needed for Robolectric because its
bytecode instrumentation renames the original native methods
in order to support potentially shadowing them.

This capability is cherry picked from the layoutlib-native
branch.

Flag: EXEMPT: Only changes the behavior on host-side test.
Bug: 323057854
Test: m libandroid_runtime
Merged-In: I1a25389f7c6eb3270a32d858ee114ddadbc72a65
Change-Id: Idfb9e0900957e3a13bde10ba9626dd9b6ff4ac99
parent 43db9d0d
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -69,9 +69,47 @@ static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
    return static_cast<T>(res);
}

//  Inline variable that specifies the method binding format.
//  The expected format is 'XX${method}XX', where ${method} represents the original method name.
//  This variable is shared across all translation units. This is treated as a global variable as
//  per C++ 17.
inline std::string jniMethodFormat;

inline static void setJniMethodFormat(std::string value) {
    jniMethodFormat = value;
}

// Potentially translates the given JNINativeMethods if setJniMethodFormat has been set.
// Has no effect otherwise
inline const JNINativeMethod* maybeRenameJniMethods(const JNINativeMethod* gMethods,
                                                    int numMethods) {
    if (jniMethodFormat.empty()) {
        return gMethods;
    }
    // Make a copy of gMethods with reformatted method names.
    JNINativeMethod* modifiedMethods = new JNINativeMethod[numMethods];
    LOG_ALWAYS_FATAL_IF(!modifiedMethods, "Failed to allocate a copy of the JNI methods");

    size_t methodNamePos = jniMethodFormat.find("${method}");
    LOG_ALWAYS_FATAL_IF(methodNamePos == std::string::npos,
                        "Invalid jniMethodFormat: could not find '${method}' in pattern");

    for (int i = 0; i < numMethods; i++) {
        modifiedMethods[i] = gMethods[i];
        std::string modifiedName = jniMethodFormat;
        modifiedName.replace(methodNamePos, 9, gMethods[i].name);
        char* modifiedNameChars = new char[modifiedName.length() + 1];
        LOG_ALWAYS_FATAL_IF(!modifiedNameChars, "Failed to allocate the new method name");
        std::strcpy(modifiedNameChars, modifiedName.c_str());
        modifiedMethods[i].name = modifiedNameChars;
    }
    return modifiedMethods;
}

static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
                                       const JNINativeMethod* gMethods, int numMethods) {
    int res = jniRegisterNativeMethods(env, className, gMethods, numMethods);
    const JNINativeMethod* modifiedMethods = maybeRenameJniMethods(gMethods, numMethods);
    int res = jniRegisterNativeMethods(env, className, modifiedMethods, numMethods);
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
    return res;
}