Full Code of rovo89/Xposed for AI

master aa7ae1dbd63d cached
24 files
150.3 KB
38.2k tokens
157 symbols
1 requests
Download .txt
Repository: rovo89/Xposed
Branch: master
Commit: aa7ae1dbd63d
Files: 24
Total size: 150.3 KB

Directory structure:
gitextract_pu6aq7zs/

├── .gitignore
├── ART.mk
├── Android.mk
├── Dalvik.mk
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── app_main.cpp
├── app_main2.cpp
├── fd_utils-inl.h
├── libxposed_art.cpp
├── libxposed_common.cpp
├── libxposed_common.h
├── libxposed_dalvik.cpp
├── libxposed_dalvik.h
├── xposed.cpp
├── xposed.h
├── xposed_logcat.cpp
├── xposed_logcat.h
├── xposed_offsets.h
├── xposed_safemode.cpp
├── xposed_safemode.h
├── xposed_service.cpp
├── xposed_service.h
└── xposed_shared.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*~


================================================
FILE: ART.mk
================================================
##########################################################
# Library for ART-specific functions
##########################################################

include $(CLEAR_VARS)

include art/build/Android.common_build.mk
$(eval $(call set-target-local-clang-vars))
$(eval $(call set-target-local-cflags-vars,ndebug))

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 23)))
  LOCAL_C_INCLUDES += \
    external/valgrind \
    external/valgrind/include
else
  include external/libcxx/libcxx.mk
  LOCAL_C_INCLUDES += \
    external/valgrind/main \
    external/valgrind/main/include
endif

LOCAL_SRC_FILES += \
  libxposed_common.cpp \
  libxposed_art.cpp

LOCAL_C_INCLUDES += \
  art/runtime \
  external/gtest/include

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 24)))
  LOCAL_C_INCLUDES += bionic/libc/private
endif

LOCAL_SHARED_LIBRARIES += \
  libart \
  liblog \
  libcutils \
  libandroidfw \
  libnativehelper

LOCAL_CFLAGS += \
  -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) \
  -DXPOSED_WITH_SELINUX=1

LOCAL_MODULE := libxposed_art
LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
LOCAL_MULTILIB := both

# Always build both architectures (if applicable)
ifeq ($(TARGET_IS_64_BIT),true)
  $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX)
endif

include $(BUILD_SHARED_LIBRARY)


================================================
FILE: Android.mk
================================================
##########################################################
# Customized app_process executable
##########################################################

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))
  LOCAL_SRC_FILES := app_main2.cpp
  LOCAL_MULTILIB := both
  LOCAL_MODULE_STEM_32 := app_process32_xposed
  LOCAL_MODULE_STEM_64 := app_process64_xposed
else
  LOCAL_SRC_FILES := app_main.cpp
  LOCAL_MODULE_STEM := app_process_xposed
endif

LOCAL_SRC_FILES += \
  xposed.cpp \
  xposed_logcat.cpp \
  xposed_service.cpp \
  xposed_safemode.cpp

LOCAL_SHARED_LIBRARIES := \
  libcutils \
  libutils \
  liblog \
  libbinder \
  libandroid_runtime \
  libdl

LOCAL_CFLAGS += -Wall -Werror -Wextra -Wunused
LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 17)))
  LOCAL_SHARED_LIBRARIES += libselinux
  LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1
endif

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 22)))
  LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
  LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
endif

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 23)))
  LOCAL_SHARED_LIBRARIES += libwilhelm
endif

LOCAL_MODULE := xposed
LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols

# Always build both architectures (if applicable)
ifeq ($(TARGET_IS_64_BIT),true)
  $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX)
endif

include $(BUILD_EXECUTABLE)

##########################################################
# Library for Dalvik-/ART-specific functions
##########################################################
ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))
  include frameworks/base/cmds/xposed/ART.mk
else
  include frameworks/base/cmds/xposed/Dalvik.mk
endif


================================================
FILE: Dalvik.mk
================================================
##########################################################
# Library for Dalvik-specific functions
##########################################################

include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
  libxposed_common.cpp \
  libxposed_dalvik.cpp

LOCAL_C_INCLUDES += \
  dalvik \
  dalvik/vm \
  external/stlport/stlport \
  bionic \
  bionic/libstdc++/include \
  libcore/include

LOCAL_SHARED_LIBRARIES := \
  libdvm \
  liblog \
  libdl \
  libnativehelper

ifeq ($(PLATFORM_SDK_VERSION),15)
  LOCAL_SHARED_LIBRARIES += libutils
else
  LOCAL_SHARED_LIBRARIES += libandroidfw
endif

LOCAL_CFLAGS := -Wall -Werror -Wextra -Wunused -Wno-unused-parameter
LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 17)))
  LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1
endif

LOCAL_MODULE := libxposed_dalvik
LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols

include $(BUILD_SHARED_LIBRARY)


================================================
FILE: MODULE_LICENSE_APACHE2
================================================


================================================
FILE: NOTICE
================================================

   Original work Copyright (c) 2005-2008, The Android Open Source Project
   Modified work Copyright (c) 2013, rovo89 and Tungstwenty

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS



================================================
FILE: app_main.cpp
================================================
/*
 * Main entry of app process.
 *
 * Starts the interpreted runtime, then starts up the application.
 *
 */

#define LOG_TAG "appproc"

#include <cutils/properties.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <cutils/process_name.h>
#include <cutils/memory.h>
#include <android_runtime/AndroidRuntime.h>

#if PLATFORM_SDK_VERSION >= 16
#include <sys/personality.h>
#endif

#include <stdio.h>
#include <unistd.h>

#include "xposed.h"
#include <dlfcn.h>

static bool isXposedLoaded = false;


namespace android {

void app_usage()
{
    fprintf(stderr,
        "Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
    fprintf(stderr, "   with Xposed support\n");
}

void _atrace_set_tracing_enabled(bool enabled)
{
    if (xposed::getSdkVersion() < 18)
        return;

    dlerror(); // Clear existing errors

    void (*PTR_atrace_set_tracing_enabled)(bool);
    *(void **) (&PTR_atrace_set_tracing_enabled) = dlsym(RTLD_DEFAULT, "atrace_set_tracing_enabled");

    const char *error;
    if ((error = dlerror()) != NULL) {
        ALOGE("Could not find address for function atrace_set_tracing_enabled: %s", error);
    } else {
        PTR_atrace_set_tracing_enabled(enabled);
    }
}

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime()
        : mParentDir(NULL)
        , mClassName(NULL)
        , mClass(NULL)
        , mArgC(0)
        , mArgV(NULL)
    {
    }

#if 0
    // this appears to be unused
    const char* getParentDir() const
    {
        return mParentDir;
    }
#endif

    const char* getClassName() const
    {
        return mClassName;
    }

    virtual void onVmCreated(JNIEnv* env)
    {
        if (isXposedLoaded)
            xposed::onVmCreated(env);

        if (mClassName == NULL) {
            return; // Zygote. Nothing to do here.
        }

        /*
         * This is a little awkward because the JNI FindClass call uses the
         * class loader associated with the native method we're executing in.
         * If called in onStarted (from RuntimeInit.finishInit because we're
         * launching "am", for example), FindClass would see that we're calling
         * from a boot class' native method, and so wouldn't look for the class
         * we're trying to look up in CLASSPATH. Unfortunately it needs to,
         * because the "am" classes are not boot classes.
         *
         * The easiest fix is to call FindClass here, early on before we start
         * executing boot class Java code and thereby deny ourselves access to
         * non-boot classes.
         */
        char* slashClassName = toSlashClassName(mClassName);
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName);
        }
        free(slashClassName);

        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
    }

    virtual void onStarted()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();

        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgC, mArgV);

        IPCThreadState::self()->stopProcess();
    }

    virtual void onZygoteInit()
    {
        // Re-enable tracing now that we're no longer in Zygote.
        _atrace_set_tracing_enabled(true);

        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

    virtual void onExit(int code)
    {
        if (mClassName == NULL) {
            // if zygote
            IPCThreadState::self()->stopProcess();
        }

        AndroidRuntime::onExit(code);
    }


    const char* mParentDir;
    const char* mClassName;
    jclass mClass;
    int mArgC;
    const char* const* mArgV;
};

}

using namespace android;

/*
 * sets argv0 to as much of newArgv0 as will fit
 */
static void setArgv0(const char *argv0, const char *newArgv0)
{
    strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0));
}

int main(int argc, char* const argv[])
{
    if (xposed::handleOptions(argc, argv))
        return 0;

#if PLATFORM_SDK_VERSION >= 16
#ifdef __arm__
    /*
     * b/7188322 - Temporarily revert to the compat memory layout
     * to avoid breaking third party apps.
     *
     * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
     *
     * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
     * changes the kernel mapping from bottom up to top-down.
     * This breaks some programs which improperly embed
     * an out of date copy of Android's linker.
     */
    char value[PROPERTY_VALUE_MAX];
    property_get("ro.kernel.qemu", value, "");
    bool is_qemu = (strcmp(value, "1") == 0);
    if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) {
        int current = personality(0xFFFFFFFF);
        if ((current & ADDR_COMPAT_LAYOUT) == 0) {
            personality(current | ADDR_COMPAT_LAYOUT);
            setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);
            execv("/system/bin/app_process", argv);
            return -1;
        }
    }
    unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP");
#endif
#endif

    // These are global variables in ProcessState.cpp
    mArgC = argc;
    mArgV = argv;

    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;

    AppRuntime runtime;
    const char* argv0 = argv[0];

    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm

    int i = runtime.addVmArguments(argc, argv);

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    const char* parentDir = NULL;
    const char* niceName = NULL;
    const char* className = NULL;
    while (i < argc) {
        const char* arg = argv[i++];
        if (!parentDir) {
            parentDir = arg;
        } else if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = "zygote";
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName = arg + 12;
        } else {
            className = arg;
            break;
        }
    }

    if (niceName && *niceName) {
        setArgv0(argv0, niceName);
        set_process_name(niceName);
    }

    runtime.mParentDir = parentDir;

    isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);
    if (zygote) {
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        runtime.mClassName = className;
        runtime.mArgC = argc - i;
        runtime.mArgV = argv + i;
        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}


================================================
FILE: app_main2.cpp
================================================
/*
 * Main entry of app process.
 *
 * Starts the interpreted runtime, then starts up the application.
 *
 */

#define LOG_TAG "appproc"

#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>

#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <cutils/memory.h>
#include <cutils/process_name.h>
#include <cutils/properties.h>
#include <cutils/trace.h>
#include <android_runtime/AndroidRuntime.h>
#include <private/android_filesystem_config.h>  // for AID_SYSTEM

#include "xposed.h"
#include <dlfcn.h>

static bool isXposedLoaded = false;


namespace android {

static void app_usage()
{
    fprintf(stderr,
        "Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
    fprintf(stderr, "   with Xposed support\n");
}

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, const size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
        , mClass(NULL)
    {
    }

    void setClassNameAndArgs(const String8& className, int argc, char * const *argv) {
        mClassName = className;
        for (int i = 0; i < argc; ++i) {
             mArgs.add(String8(argv[i]));
        }
    }

    virtual void onVmCreated(JNIEnv* env)
    {
        if (isXposedLoaded)
            xposed::onVmCreated(env);

        if (mClassName.isEmpty()) {
            return; // Zygote. Nothing to do here.
        }

        /*
         * This is a little awkward because the JNI FindClass call uses the
         * class loader associated with the native method we're executing in.
         * If called in onStarted (from RuntimeInit.finishInit because we're
         * launching "am", for example), FindClass would see that we're calling
         * from a boot class' native method, and so wouldn't look for the class
         * we're trying to look up in CLASSPATH. Unfortunately it needs to,
         * because the "am" classes are not boot classes.
         *
         * The easiest fix is to call FindClass here, early on before we start
         * executing boot class Java code and thereby deny ourselves access to
         * non-boot classes.
         */
        char* slashClassName = toSlashClassName(mClassName.string());
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
            env->ExceptionDescribe();
        }
        free(slashClassName);

        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
    }

    virtual void onStarted()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();

        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgs);

        IPCThreadState::self()->stopProcess();
    }

    virtual void onZygoteInit()
    {
#if PLATFORM_SDK_VERSION <= 22
        // Re-enable tracing now that we're no longer in Zygote.
        atrace_set_tracing_enabled(true);
#endif

        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

    virtual void onExit(int code)
    {
        if (mClassName.isEmpty()) {
            // if zygote
            IPCThreadState::self()->stopProcess();
        }

        AndroidRuntime::onExit(code);
    }


    String8 mClassName;
    Vector<String8> mArgs;
    jclass mClass;
};

}

using namespace android;

static size_t computeArgBlockSize(int argc, char* const argv[]) {
    // TODO: This assumes that all arguments are allocated in
    // contiguous memory. There isn't any documented guarantee
    // that this is the case, but this is how the kernel does it
    // (see fs/exec.c).
    //
    // Also note that this is a constant for "normal" android apps.
    // Since they're forked from zygote, the size of their command line
    // is the size of the zygote command line.
    //
    // We change the process name of the process by over-writing
    // the start of the argument block (argv[0]) with the new name of
    // the process, so we'd mysteriously start getting truncated process
    // names if the zygote command line decreases in size.
    uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
    uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
    end += strlen(argv[argc - 1]) + 1;
    return (end - start);
}

static void maybeCreateDalvikCache() {
#if defined(__aarch64__)
    static const char kInstructionSet[] = "arm64";
#elif defined(__x86_64__)
    static const char kInstructionSet[] = "x86_64";
#elif defined(__arm__)
    static const char kInstructionSet[] = "arm";
#elif defined(__i386__)
    static const char kInstructionSet[] = "x86";
#elif defined (__mips__) && !defined(__LP64__)
    static const char kInstructionSet[] = "mips";
#elif defined (__mips__) && defined(__LP64__)
    static const char kInstructionSet[] = "mips64";
#else
#error "Unknown instruction set"
#endif
    const char* androidRoot = getenv("ANDROID_DATA");
    LOG_ALWAYS_FATAL_IF(androidRoot == NULL, "ANDROID_DATA environment variable unset");

    char dalvikCacheDir[PATH_MAX];
    const int numChars = snprintf(dalvikCacheDir, PATH_MAX,
            "%s/dalvik-cache/%s", androidRoot, kInstructionSet);
    LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
            "Error constructing dalvik cache : %s", strerror(errno));

    int result = mkdir(dalvikCacheDir, 0711);
    LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
            "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));

    // We always perform these steps because the directory might
    // already exist, with wider permissions and a different owner
    // than we'd like.
    result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);
    LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));

    result = chmod(dalvikCacheDir, 0711);
    LOG_ALWAYS_FATAL_IF((result < 0),
            "Error changing dalvik-cache permissions : %s", strerror(errno));
}

#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif

static void runtimeStart(AppRuntime& runtime, const char *classname, const Vector<String8>& options, bool zygote)
{
#if PLATFORM_SDK_VERSION >= 23
  runtime.start(classname, options, zygote);
#else
  // try newer variant (5.1.1_r19 and later) first
  void (*ptr1)(AppRuntime&, const char*, const Vector<String8>&, bool);
  *(void **) (&ptr1) = dlsym(RTLD_DEFAULT, "_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEEb");

  if (ptr1 != NULL) {
    ptr1(runtime, classname, options, zygote);
    return;
  }

  // fall back to older variant
  void (*ptr2)(AppRuntime&, const char*, const Vector<String8>&);
  *(void **) (&ptr2) = dlsym(RTLD_DEFAULT, "_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEE");

  if (ptr2 != NULL) {
    ptr2(runtime, classname, options);
    return;
  }

  // should not happen
  LOG_ALWAYS_FATAL("app_process: could not locate AndroidRuntime::start() method.");
#endif
}

int main(int argc, char* const argv[])
{
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
        // EINVAL. Don't die on such kernels.
        if (errno != EINVAL) {
            LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
            return 12;
        }
    }

    if (xposed::handleOptions(argc, argv)) {
        return 0;
    }

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm.
    //
    // The first argument after the VM args is the "parent dir", which
    // is currently unused.
    //
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    //
    // For non zygote starts, these arguments will be followed by
    // the main class name. All remaining arguments are passed to
    // the main method of this class.
    //
    // For zygote starts, all remaining arguments are passed to the zygote.
    // main function.
    //
    // Note that we must copy argument string values since we will rewrite the
    // entire argument block when we apply the nice name to argv0.

    int i;
    for (i = 0; i < argc; i++) {
        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }
        runtime.addOption(strdup(argv[i]));
    }

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string());
        set_process_name(niceName.string());
    }

    if (zygote) {
        isXposedLoaded = xposed::initialize(true, startSystemServer, NULL, argc, argv);
        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        isXposedLoaded = xposed::initialize(false, false, className, argc, argv);
        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}


================================================
FILE: fd_utils-inl.h
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>
#include <unordered_map>
#include <set>
#include <vector>
#include <algorithm>

#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include <cutils/log.h>
#include "JNIHelp.h"
#include "ScopedPrimitiveArray.h"

static const char* kPathPrefixWhitelist[] = {
  "/data/app/",
  "/data/app-private/",
  "/system/app/",
  "/system/priv-app/",
  "/vendor/app/",
  "/vendor/priv-app/",
};

static const char* kFdPath = "/proc/self/fd";

// Keeps track of all relevant information (flags, offset etc.) of an
// open zygote file descriptor.
class FileDescriptorInfo {
 public:
  // Create a FileDescriptorInfo for a given file descriptor. Returns
  // |NULL| if an error occurred.
  static FileDescriptorInfo* createFromFd(int fd) {
    struct stat f_stat;
    // This should never happen; the zygote should always have the right set
    // of permissions required to stat all its open files.
    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
      ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
      return NULL;
    }

    if (S_ISSOCK(f_stat.st_mode)) {
      std::string socket_name;
      if (!GetSocketName(fd, &socket_name)) {
        return NULL;
      }

      if (!IsWhitelisted(socket_name)) {
        //ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
        return NULL;
      }

      return new FileDescriptorInfo(fd);
    }

    std::string file_path;
    if (!Readlink(fd, &file_path)) {
      return NULL;
    }

    if (!IsWhitelisted(file_path)) {
      //ALOGE("Not whitelisted : %s", file_path.c_str());
      return NULL;
    }

    // We only handle whitelisted regular files and character devices. Whitelisted
    // character devices must provide a guarantee of sensible behaviour when
    // reopened.
    //
    // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
    // S_ISLINK : Not supported.
    // S_ISBLK : Not supported.
    // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
    // with the child process across forks but those should have been closed
    // before we got to this point.
    if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
      ALOGE("Unsupported st_mode %d", f_stat.st_mode);
      return NULL;
    }

    // File descriptor flags : currently on FD_CLOEXEC. We can set these
    // using F_SETFD - we're single threaded at this point of execution so
    // there won't be any races.
    const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
    if (fd_flags == -1) {
      ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
      return NULL;
    }

    // File status flags :
    // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
    //   to the open() call.
    //
    // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
    //   do about these, since the file has already been created. We shall ignore
    //   them here.
    //
    // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
    //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
    //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
    //   their presence and pass them in to open().
    int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
    if (fs_flags == -1) {
      ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
      return NULL;
    }

    // File offset : Ignore the offset for non seekable files.
    const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));

    // We pass the flags that open accepts to open, and use F_SETFL for
    // the rest of them.
    static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
    int open_flags = fs_flags & (kOpenFlags);
    fs_flags = fs_flags & (~(kOpenFlags));

    return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
  }

  bool Detach() const {
    const int dev_null_fd = open("/dev/null", O_RDWR);
    if (dev_null_fd < 0) {
      ALOGE("Failed to open /dev/null : %s", strerror(errno));
      return false;
    }

    if (dup2(dev_null_fd, fd) == -1) {
      ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
      return false;
    }

    if (close(dev_null_fd) == -1) {
      ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
      return false;
    }

    return true;
  }

  bool Reopen() const {
    if (is_sock) {
      return true;
    }

    // NOTE: This might happen if the file was unlinked after being opened.
    // It's a common pattern in the case of temporary files and the like but
    // we should not allow such usage from the zygote.
    const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));

    if (new_fd == -1) {
      ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
      return false;
    }

    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
      close(new_fd);
      ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
      return false;
    }

    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
      close(new_fd);
      ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
      return false;
    }

    if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
      close(new_fd);
      ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
      return false;
    }

    if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
      close(new_fd);
      ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
      return false;
    }

    close(new_fd);

    return true;
  }

  const int fd;
  const struct stat stat;
  const std::string file_path;
  const int open_flags;
  const int fd_flags;
  const int fs_flags;
  const off_t offset;
  const bool is_sock;

 private:
  FileDescriptorInfo(int pfd) :
    fd(pfd),
    stat(),
    open_flags(0),
    fd_flags(0),
    fs_flags(0),
    offset(0),
    is_sock(true) {
  }

  FileDescriptorInfo(struct stat pstat, const std::string& pfile_path, int pfd, int popen_flags,
                     int pfd_flags, int pfs_flags, off_t poffset) :
    fd(pfd),
    stat(pstat),
    file_path(pfile_path),
    open_flags(popen_flags),
    fd_flags(pfd_flags),
    fs_flags(pfs_flags),
    offset(poffset),
    is_sock(false) {
  }

  // Returns true iff. a given path is whitelisted.
  static bool IsWhitelisted(const std::string& path) {
    for (size_t i = 0; i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) {
      if (path.compare(0, strlen(kPathPrefixWhitelist[i]), kPathPrefixWhitelist[i]) == 0) {
        return true;
      }
    }
    return false;
  }

  // TODO: Call android::base::Readlink instead of copying the code here.
  static bool Readlink(const int fd, std::string* result) {
    char path[64];
    snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);

    // Code copied from android::base::Readlink starts here :

    // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
    // and truncates to whatever size you do supply, so it can't be used to query.
    // We could call lstat first, but that would introduce a race condition that
    // we couldn't detect.
    // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
    char* buf = new char[4096];
    ssize_t len = readlink(path, buf, 4096);
    if (len == -1) {
      delete[] buf;
      return false;
    }

    result->assign(buf, len);
    delete[] buf;
    return true;
  }

  // Returns the locally-bound name of the socket |fd|. Returns true
  // iff. all of the following hold :
  //
  // - the socket's sa_family is AF_UNIX.
  // - the length of the path is greater than zero (i.e, not an unnamed socket).
  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
  //   address).
  static bool GetSocketName(const int fd, std::string* result) {
    sockaddr_storage ss;
    sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
    socklen_t addr_len = sizeof(ss);

    if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
      ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
      return false;
    }

#if PLATFORM_SDK_VERSION <= 23
    if (addr->sa_family == AF_NETLINK) {
      (*result) = "@netlink@";
      return true;
    }
#endif

    if (addr->sa_family != AF_UNIX) {
      //ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
      return false;
    }

    const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);

    size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
    // This is an unnamed local socket, we do not accept it.
    if (path_len == 0) {
      //ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
      return false;
    }

    // This is a local socket with an abstract address, we do not accept it.
    if (unix_addr->sun_path[0] == '\0') {
      //ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
      return false;
    }

    // If we're here, sun_path must refer to a null terminated filesystem
    // pathname (man 7 unix). Remove the terminator before assigning it to an
    // std::string.
    if (unix_addr->sun_path[path_len - 1] ==  '\0') {
      --path_len;
    }

    result->assign(unix_addr->sun_path, path_len);
    return true;
  }


  // DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
  FileDescriptorInfo(const FileDescriptorInfo&);
  void operator=(const FileDescriptorInfo&);
};

// A FileDescriptorTable is a collection of FileDescriptorInfo objects
// keyed by their FDs.
class FileDescriptorTable {
 public:
  // Creates a new FileDescriptorTable. This function scans
  // /proc/self/fd for the list of open file descriptors and collects
  // information about them. Returns NULL if an error occurs.
  static FileDescriptorTable* Create() {
    DIR* d = opendir(kFdPath);
    if (d == NULL) {
      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
      return NULL;
    }
    int dir_fd = dirfd(d);
    dirent* e;

    std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
    while ((e = readdir(d)) != NULL) {
      const int fd = ParseFd(e, dir_fd);
      if (fd == -1) {
        continue;
      }

      FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
      if (info == NULL) {
        continue;
      }
      info->Detach();
      open_fd_map[fd] = info;
    }

    if (closedir(d) == -1) {
      ALOGE("Unable to close directory : %s", strerror(errno));
      return NULL;
    }
    return new FileDescriptorTable(open_fd_map);
  }

  // Reopens all file descriptors that are contained in the table.
  void Reopen() {
    std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
    for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
      const FileDescriptorInfo* info = it->second;
      if (info != NULL) {
        info->Reopen();
        delete info;
      }
    }
  }

 private:
  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
      : open_fd_map_(map) {
  }

  static int ParseFd(dirent* e, int dir_fd) {
    char* end;
    const int fd = strtol(e->d_name, &end, 10);
    if ((*end) != '\0') {
      return -1;
    }

    // Don't bother with the standard input/output/error, they're handled
    // specially post-fork anyway.
    if (fd <= STDERR_FILENO || fd == dir_fd) {
      return -1;
    }

    return fd;
  }

  // Invariant: All values in this unordered_map are non-NULL.
  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;

  // DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
  FileDescriptorTable(const FileDescriptorTable&);
  void operator=(const FileDescriptorTable&);
};


================================================
FILE: libxposed_art.cpp
================================================
/**
 * This file includes functions specific to the ART runtime.
 */

#define LOG_TAG "Xposed"

#include "xposed_shared.h"
#include "libxposed_common.h"
#if PLATFORM_SDK_VERSION >= 21
#include "fd_utils-inl.h"
#endif

#include "thread.h"
#include "common_throws.h"
#if PLATFORM_SDK_VERSION >= 23
#include "art_method-inl.h"
#else
#include "mirror/art_method-inl.h"
#endif
#include "mirror/object-inl.h"
#include "mirror/throwable.h"
#include "native/scoped_fast_native_object_access.h"
#include "reflection.h"
#include "scoped_thread_state_change.h"
#include "well_known_classes.h"

#if PLATFORM_SDK_VERSION >= 24
#include "mirror/abstract_method.h"
#include "thread_list.h"
#endif

using namespace art;

#if PLATFORM_SDK_VERSION < 23
using art::mirror::ArtMethod;
#endif

namespace xposed {


////////////////////////////////////////////////////////////
// Library initialization
////////////////////////////////////////////////////////////

/** Called by Xposed's app_process replacement. */
bool xposedInitLib(XposedShared* shared) {
    xposed = shared;
    xposed->onVmCreated = &onVmCreatedCommon;
    return true;
}

/** Called very early during VM startup. */
bool onVmCreated(JNIEnv*) {
    // TODO: Handle CLASS_MIUI_RESOURCES?
    ArtMethod::xposed_callback_class = classXposedBridge;
    ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod;
    return true;
}


////////////////////////////////////////////////////////////
// Utility methods
////////////////////////////////////////////////////////////
void logExceptionStackTrace() {
    Thread* self = Thread::Current();
    ScopedObjectAccess soa(self);
#if PLATFORM_SDK_VERSION >= 23
    XLOG(ERROR) << self->GetException()->Dump();
#else
    XLOG(ERROR) << self->GetException(nullptr)->Dump();
#endif
}

////////////////////////////////////////////////////////////
// JNI methods
////////////////////////////////////////////////////////////

void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
            jobject, jint, jobject javaAdditionalInfo) {
    // Detect usage errors.
    ScopedObjectAccess soa(env);
    if (javaReflectedMethod == nullptr) {
#if PLATFORM_SDK_VERSION >= 23
        ThrowIllegalArgumentException("method must not be null");
#else
        ThrowIllegalArgumentException(nullptr, "method must not be null");
#endif
        return;
    }

    // Get the ArtMethod of the method to be hooked.
    ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);

    // Hook the method
    artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}

jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod,
            jint isResolved, jobjectArray, jclass, jobject javaReceiver, jobjectArray javaArgs) {
    ScopedFastNativeObjectAccess soa(env);
    if (UNLIKELY(!isResolved)) {
        ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaMethod);
        if (LIKELY(artMethod->IsXposedHookedMethod())) {
            javaMethod = artMethod->GetXposedHookInfo()->reflected_method;
        }
    }
#if PLATFORM_SDK_VERSION >= 23
    return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
#else
    return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, true);
#endif
}

void XposedBridge_setObjectClassNative(JNIEnv* env, jclass, jobject javaObj, jclass javaClazz) {
    ScopedObjectAccess soa(env);
    StackHandleScope<3> hs(soa.Self());
    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));
#if PLATFORM_SDK_VERSION >= 23
    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), clazz, true, true)) {
#else
    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(clazz, true, true)) {
#endif
        XLOG(ERROR) << "Could not initialize class " << PrettyClass(clazz.Get());
        return;
    }
    Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(javaObj)));
    Handle<mirror::Class> currentClass(hs.NewHandle(obj->GetClass()));
    if (clazz->GetObjectSize() != currentClass->GetObjectSize()) {
        std::string msg = StringPrintf("Different object sizes: %s (%d) vs. %s (%d)",
                PrettyClass(clazz.Get()).c_str(), clazz->GetObjectSize(),
                PrettyClass(currentClass.Get()).c_str(), currentClass->GetObjectSize());
#if PLATFORM_SDK_VERSION >= 23
        ThrowIllegalArgumentException(msg.c_str());
#else
        ThrowIllegalArgumentException(nullptr, msg.c_str());
#endif
        return;
    }
    obj->SetClass(clazz.Get());
}

void XposedBridge_dumpObjectNative(JNIEnv*, jclass, jobject) {
    // TODO Can be useful for debugging
    UNIMPLEMENTED(ERROR|LOG_XPOSED);
}

jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass, jobject javaObject, jclass javaClazz) {
    ScopedObjectAccess soa(env);
    StackHandleScope<3> hs(soa.Self());
    Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(javaObject)));
    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));
    Handle<mirror::Object> dest(hs.NewHandle(obj->Clone(soa.Self(), clazz.Get())));
    return soa.AddLocalReference<jobject>(dest.Get());
}

void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) {
    ScopedObjectAccess soa(env);
    StackHandleScope<1> hs(soa.Self());
    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));
    uint32_t flags = clazz->GetAccessFlags();
    if ((flags & kAccFinal) != 0) {
        clazz->SetAccessFlags(flags & ~kAccFinal);
    }
}

jint XposedBridge_getRuntime(JNIEnv*, jclass) {
    return 2; // RUNTIME_ART
}

#if PLATFORM_SDK_VERSION >= 21
static FileDescriptorTable* gClosedFdTable = NULL;

void XposedBridge_closeFilesBeforeForkNative(JNIEnv*, jclass) {
    gClosedFdTable = FileDescriptorTable::Create();
}

void XposedBridge_reopenFilesAfterForkNative(JNIEnv*, jclass) {
    gClosedFdTable->Reopen();
    delete gClosedFdTable;
    gClosedFdTable = NULL;
}
#endif

#if PLATFORM_SDK_VERSION >= 24
void XposedBridge_invalidateCallersNative(JNIEnv* env, jclass, jobjectArray javaMethods) {
    ScopedObjectAccess soa(env);
    auto* runtime = Runtime::Current();
    auto* cl = runtime->GetClassLinker();

    // Invalidate callers of the given methods.
    auto* abstract_methods = soa.Decode<mirror::ObjectArray<mirror::AbstractMethod>*>(javaMethods);
    size_t count = abstract_methods->GetLength();
    for (size_t i = 0; i < count; i++) {
        auto* abstract_method = abstract_methods->Get(i);
        if (abstract_method == nullptr) {
            continue;
        }
        ArtMethod* method = abstract_method->GetArtMethod();
        cl->InvalidateCallersForMethod(soa.Self(), method);
    }

    // Now instrument the stack to deoptimize methods which are being called right now.
    ScopedThreadSuspension sts(soa.Self(), kSuspended);
    ScopedSuspendAll ssa(__FUNCTION__);
    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
    runtime->GetThreadList()->ForEach([](Thread* thread, void*) SHARED_REQUIRES(Locks::mutator_lock_) {
        Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(thread);
    }, nullptr);
}
#endif

}  // namespace xposed


================================================
FILE: libxposed_common.cpp
================================================
/**
 * This file includes functions shared by different runtimes.
 */

#define LOG_TAG "Xposed"

#include "libxposed_common.h"
#include "JNIHelp.h"
#include <ScopedUtfChars.h>

#define private public
#if PLATFORM_SDK_VERSION == 15
#include <utils/ResourceTypes.h>
#else
#include <androidfw/ResourceTypes.h>
#endif
#undef private

namespace xposed {

////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////

bool xposedLoadedSuccessfully = false;
xposed::XposedShared* xposed = NULL;
jclass classXposedBridge = NULL;
static jclass classXResources = NULL;
static jclass classFileResult = NULL;

jmethodID methodXposedBridgeHandleHookedMethod = NULL;
static jmethodID methodXResourcesTranslateResId = NULL;
static jmethodID methodXResourcesTranslateAttrId = NULL;
static jmethodID constructorFileResult = NULL;


////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////

static int register_natives_XposedBridge(JNIEnv* env, jclass clazz);
static int register_natives_XResources(JNIEnv* env, jclass clazz);
static int register_natives_ZygoteService(JNIEnv* env, jclass clazz);


////////////////////////////////////////////////////////////
// Utility methods
////////////////////////////////////////////////////////////

/** Read an integer value from a configuration file. */
int readIntConfig(const char* fileName, int defaultValue) {
    FILE *fp = fopen(fileName, "r");
    if (fp == NULL)
        return defaultValue;

    int result;
    int success = fscanf(fp, "%i", &result);
    fclose(fp);

    return (success >= 1) ? result : defaultValue;
}


////////////////////////////////////////////////////////////
// Library initialization
////////////////////////////////////////////////////////////

bool initXposedBridge(JNIEnv* env) {
    classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
    if (classXposedBridge == NULL) {
        ALOGE("Error while loading Xposed class '%s':", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    classXposedBridge = reinterpret_cast<jclass>(env->NewGlobalRef(classXposedBridge));

    ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);
    if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {
        ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
        "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (methodXposedBridgeHandleHookedMethod == NULL) {
        ALOGE("ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}

bool initZygoteService(JNIEnv* env) {
    jclass zygoteServiceClass = env->FindClass(CLASS_ZYGOTE_SERVICE);
    if (zygoteServiceClass == NULL) {
        ALOGE("Error while loading ZygoteService class '%s':", CLASS_ZYGOTE_SERVICE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    if (register_natives_ZygoteService(env, zygoteServiceClass) != JNI_OK) {
        ALOGE("Could not register natives for '%s'", CLASS_ZYGOTE_SERVICE);
        env->ExceptionClear();
        return false;
    }

    classFileResult = env->FindClass(CLASS_FILE_RESULT);
    if (classFileResult == NULL) {
        ALOGE("Error while loading FileResult class '%s':", CLASS_FILE_RESULT);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    classFileResult = reinterpret_cast<jclass>(env->NewGlobalRef(classFileResult));

    constructorFileResult = env->GetMethodID(classFileResult, "<init>", "(JJ)V");
    if (constructorFileResult == NULL) {
        ALOGE("ERROR: could not find constructor %s(long, long)", CLASS_FILE_RESULT);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}

void onVmCreatedCommon(JNIEnv* env) {
    if (!initXposedBridge(env) || !initZygoteService(env)) {
        return;
    }

    if (!onVmCreated(env)) {
        return;
    }

    xposedLoadedSuccessfully = true;
    return;
}


////////////////////////////////////////////////////////////
// JNI methods
////////////////////////////////////////////////////////////

jboolean XposedBridge_hadInitErrors(JNIEnv*, jclass) {
    return !xposedLoadedSuccessfully;
}

jobject XposedBridge_getStartClassName(JNIEnv* env, jclass) {
    return env->NewStringUTF(xposed->startClassName);
}

jboolean XposedBridge_startsSystemServer(JNIEnv*, jclass) {
    return xposed->startSystemServer;
}

jint XposedBridge_getXposedVersion(JNIEnv*, jclass) {
    return xposed->xposedVersionInt;
}

jboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass) {
    classXResources = env->FindClass(CLASS_XRESOURCES);
    if (classXResources == NULL) {
        ALOGE("Error while loading XResources class '%s':", CLASS_XRESOURCES);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    classXResources = reinterpret_cast<jclass>(env->NewGlobalRef(classXResources));

    if (register_natives_XResources(env, classXResources) != JNI_OK) {
        ALOGE("Could not register natives for '%s'", CLASS_XRESOURCES);
        env->ExceptionClear();
        return false;
    }

    methodXResourcesTranslateResId = env->GetStaticMethodID(classXResources, "translateResId",
        "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
    if (methodXResourcesTranslateResId == NULL) {
        ALOGE("ERROR: could not find method %s.translateResId(int, XResources, Resources)", CLASS_XRESOURCES);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    methodXResourcesTranslateAttrId = env->GetStaticMethodID(classXResources, "translateAttrId",
        "(Ljava/lang/String;Landroid/content/res/XResources;)I");
    if (methodXResourcesTranslateAttrId == NULL) {
        ALOGE("ERROR: could not find method %s.findAttrId(String, XResources)", CLASS_XRESOURCES);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}

void XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass,
            jlong parserPtr, jobject origRes, jobject repRes) {

    using namespace android;

    ResXMLParser* parser = (ResXMLParser*)parserPtr;
    const ResXMLTree& mTree = parser->mTree;
    uint32_t* mResIds = (uint32_t*)mTree.mResIds;
    ResXMLTree_attrExt* tag;
    int attrCount;

    if (parser == NULL)
        return;

    do {
        switch (parser->next()) {
            case ResXMLParser::START_TAG:
                tag = (ResXMLTree_attrExt*)parser->mCurExt;
                attrCount = dtohs(tag->attributeCount);
                for (int idx = 0; idx < attrCount; idx++) {
                    ResXMLTree_attribute* attr = (ResXMLTree_attribute*)
                        (((const uint8_t*)tag)
                         + dtohs(tag->attributeStart)
                         + (dtohs(tag->attributeSize)*idx));

                    // find resource IDs for attribute names
                    int32_t attrNameID = parser->getAttributeNameID(idx);
                    // only replace attribute name IDs for app packages
                    if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) {
                        size_t attNameLen;
                        const char16_t* attrName = mTree.mStrings.stringAt(attrNameID, &attNameLen);
                        jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId,
                            env->NewString((const jchar*)attrName, attNameLen), origRes);
                        if (env->ExceptionCheck())
                            goto leave;

                        mResIds[attrNameID] = htodl(attrResID);
                    }

                    // find original resource IDs for reference values (app packages only)
                    if (attr->typedValue.dataType != Res_value::TYPE_REFERENCE)
                        continue;

                    jint oldValue = dtohl(attr->typedValue.data);
                    if (oldValue < 0x7f000000)
                        continue;

                    jint newValue = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateResId,
                        oldValue, origRes, repRes);
                    if (env->ExceptionCheck())
                        goto leave;

                    if (newValue != oldValue)
                        attr->typedValue.data = htodl(newValue);
                }
                continue;
            case ResXMLParser::END_DOCUMENT:
            case ResXMLParser::BAD_DOCUMENT:
                goto leave;
            default:
                continue;
        }
    } while (true);

    leave:
    parser->restart();
}


jboolean ZygoteService_checkFileAccess(JNIEnv* env, jclass, jstring filenameJ, jint mode) {
#if XPOSED_WITH_SELINUX
    ScopedUtfChars filename(env, filenameJ);
    return xposed->zygoteservice_accessFile(filename.c_str(), mode) == 0;
#else  // XPOSED_WITH_SELINUX
    return false;
#endif  // XPOSED_WITH_SELINUX
}

jobject ZygoteService_statFile(JNIEnv* env, jclass, jstring filenameJ) {
#if XPOSED_WITH_SELINUX
    ScopedUtfChars filename(env, filenameJ);

    struct stat st;
    int result = xposed->zygoteservice_statFile(filename.c_str(), &st);
    if (result != 0) {
        if (errno == ENOENT) {
            jniThrowExceptionFmt(env, "java/io/FileNotFoundException", "No such file or directory: %s", filename.c_str());
        } else {
            jniThrowExceptionFmt(env, "java/io/IOException", "%s while reading %s", strerror(errno), filename.c_str());
        }
        return NULL;
    }

    return env->NewObject(classFileResult, constructorFileResult, (jlong) st.st_size, (jlong) st.st_mtime);
#else  // XPOSED_WITH_SELINUX
    return NULL;
#endif  // XPOSED_WITH_SELINUX
}

jbyteArray ZygoteService_readFile(JNIEnv* env, jclass, jstring filenameJ) {
#if XPOSED_WITH_SELINUX
    ScopedUtfChars filename(env, filenameJ);

    int bytesRead = 0;
    char* content = xposed->zygoteservice_readFile(filename.c_str(), &bytesRead);
    if (content == NULL) {
        if (errno == ENOENT) {
            jniThrowExceptionFmt(env, "java/io/FileNotFoundException", "No such file or directory: %s", filename.c_str());
        } else {
            jniThrowExceptionFmt(env, "java/io/IOException", "%s while reading %s", strerror(errno), filename.c_str());
        }
        return NULL;
    }

    jbyteArray ret = env->NewByteArray(bytesRead);
    if (ret != NULL) {
        jbyte* arrptr = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
        if (arrptr) {
            memcpy(arrptr, content, bytesRead);
            env->ReleasePrimitiveArrayCritical(ret, arrptr, 0);
        }
    }

    free(content);
    return ret;
#else  // XPOSED_WITH_SELINUX
    return NULL;
#endif  // XPOSED_WITH_SELINUX
}

////////////////////////////////////////////////////////////
// JNI methods registrations
////////////////////////////////////////////////////////////

int register_natives_XposedBridge(JNIEnv* env, jclass clazz) {
    const JNINativeMethod methods[] = {
        NATIVE_METHOD(XposedBridge, hadInitErrors, "()Z"),
        NATIVE_METHOD(XposedBridge, getStartClassName, "()Ljava/lang/String;"),
        NATIVE_METHOD(XposedBridge, getRuntime, "()I"),
        NATIVE_METHOD(XposedBridge, startsSystemServer, "()Z"),
        NATIVE_METHOD(XposedBridge, getXposedVersion, "()I"),
        NATIVE_METHOD(XposedBridge, initXResourcesNative, "()Z"),
        NATIVE_METHOD(XposedBridge, hookMethodNative, "(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V"),
        NATIVE_METHOD(XposedBridge, setObjectClassNative, "(Ljava/lang/Object;Ljava/lang/Class;)V"),
        NATIVE_METHOD(XposedBridge, dumpObjectNative, "(Ljava/lang/Object;)V"),
        NATIVE_METHOD(XposedBridge, cloneToSubclassNative, "(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;"),
        NATIVE_METHOD(XposedBridge, removeFinalFlagNative, "(Ljava/lang/Class;)V"),
#if PLATFORM_SDK_VERSION >= 21
        NATIVE_METHOD(XposedBridge, invokeOriginalMethodNative,
            "!(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
        NATIVE_METHOD(XposedBridge, closeFilesBeforeForkNative, "()V"),
        NATIVE_METHOD(XposedBridge, reopenFilesAfterForkNative, "()V"),
#endif
#if PLATFORM_SDK_VERSION >= 24
        NATIVE_METHOD(XposedBridge, invalidateCallersNative, "([Ljava/lang/reflect/Member;)V"),
#endif
    };
    return env->RegisterNatives(clazz, methods, NELEM(methods));
}

int register_natives_XResources(JNIEnv* env, jclass clazz) {
    const JNINativeMethod methods[] = {
        NATIVE_METHOD(XResources, rewriteXmlReferencesNative, "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V"),
    };
    return env->RegisterNatives(clazz, methods, NELEM(methods));
}

int register_natives_ZygoteService(JNIEnv* env, jclass clazz) {
    const JNINativeMethod methods[] = {
        NATIVE_METHOD(ZygoteService, checkFileAccess, "(Ljava/lang/String;I)Z"),
        NATIVE_METHOD(ZygoteService, statFile, "(Ljava/lang/String;)L" CLASS_FILE_RESULT ";"),
        NATIVE_METHOD(ZygoteService, readFile, "(Ljava/lang/String;)[B"),
    };
    return env->RegisterNatives(clazz, methods, NELEM(methods));
}

}  // namespace xposed


================================================
FILE: libxposed_common.h
================================================
#ifndef LIBXPOSED_COMMON_H_
#define LIBXPOSED_COMMON_H_

#include "xposed_shared.h"

#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
  { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
#endif
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

namespace xposed {

#define CLASS_XPOSED_BRIDGE  "de/robv/android/xposed/XposedBridge"
#define CLASS_XRESOURCES     "android/content/res/XResources"
#define CLASS_MIUI_RESOURCES "android/content/res/MiuiResources"
#define CLASS_ZYGOTE_SERVICE "de/robv/android/xposed/services/ZygoteService"
#define CLASS_FILE_RESULT    "de/robv/android/xposed/services/FileResult"


/////////////////////////////////////////////////////////////////
// Provided by common part, used by runtime-specific implementation
/////////////////////////////////////////////////////////////////
extern jclass classXposedBridge;
extern jmethodID methodXposedBridgeHandleHookedMethod;

extern int readIntConfig(const char* fileName, int defaultValue);
extern void onVmCreatedCommon(JNIEnv* env);


/////////////////////////////////////////////////////////////////
// To be provided by runtime-specific implementation
/////////////////////////////////////////////////////////////////
extern "C" bool xposedInitLib(xposed::XposedShared* shared);
extern bool onVmCreated(JNIEnv* env);
extern void prepareSubclassReplacement(JNIEnv* env, jclass clazz);
extern void logExceptionStackTrace();

extern jint    XposedBridge_getRuntime(JNIEnv* env, jclass clazz);
extern void    XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
                                             jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect);
extern void    XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect);
extern jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect);
extern void    XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect);
extern void    XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass clazz, jclass javaClazz);

#if PLATFORM_SDK_VERSION >= 21
extern jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod, jint, jobjectArray,
                                                       jclass, jobject javaReceiver, jobjectArray javaArgs);
extern void    XposedBridge_closeFilesBeforeForkNative(JNIEnv* env, jclass clazz);
extern void    XposedBridge_reopenFilesAfterForkNative(JNIEnv* env, jclass clazz);
#endif
#if PLATFORM_SDK_VERSION >= 24
extern void    XposedBridge_invalidateCallersNative(JNIEnv*, jclass, jobjectArray javaMethods);
#endif

}  // namespace xposed

#endif  // LIBXPOSED_COMMON_H_


================================================
FILE: libxposed_dalvik.cpp
================================================
/**
 * This file includes functions specific to the Dalvik runtime.
 */

#define LOG_TAG "Xposed"

#include "libxposed_dalvik.h"
#include "xposed_offsets.h"

#include <dlfcn.h>

namespace xposed {

////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////

bool initMemberOffsets(JNIEnv* env);
void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self);
void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult, const Method* method, ::Thread* self);


////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////

static ClassObject* objectArrayClass = NULL;
static size_t arrayContentsOffset = 0;
static void* PTR_gDvmJit = NULL;


////////////////////////////////////////////////////////////
// Library initialization
////////////////////////////////////////////////////////////

/** Called by Xposed's app_process replacement. */
bool xposedInitLib(xposed::XposedShared* shared) {
    xposed = shared;
    xposed->onVmCreated = &onVmCreatedCommon;
    return true;
}

/** Called very early during VM startup. */
bool onVmCreated(JNIEnv* env) {
    if (!initMemberOffsets(env))
        return false;

    jclass classMiuiResources = env->FindClass(CLASS_MIUI_RESOURCES);
    if (classMiuiResources != NULL) {
        ClassObject* clazz = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), classMiuiResources);
        if (dvmIsFinalClass(clazz)) {
            ALOGD("Removing final flag for class '%s'", CLASS_MIUI_RESOURCES);
            clazz->accessFlags &= ~ACC_FINAL;
        }
    }
    env->ExceptionClear();

    Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(classXposedBridge, "invokeOriginalMethodNative",
        "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (xposedInvokeOriginalMethodNative == NULL) {
        ALOGE("ERROR: could not find method %s.invokeOriginalMethodNative(Member, int, Class[], Class, Object, Object[])", CLASS_XPOSED_BRIDGE);
        dvmLogExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    dvmSetNativeFunc(xposedInvokeOriginalMethodNative, XposedBridge_invokeOriginalMethodNative, NULL);

    objectArrayClass = dvmFindArrayClass("[Ljava/lang/Object;", NULL);
    if (objectArrayClass == NULL) {
        ALOGE("Error while loading Object[] class");
        dvmLogExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}

bool initMemberOffsets(JNIEnv* env) {
    PTR_gDvmJit = dlsym(RTLD_DEFAULT, "gDvmJit");

    if (PTR_gDvmJit == NULL) {
        offsetMode = MEMBER_OFFSET_MODE_NO_JIT;
    } else {
        offsetMode = MEMBER_OFFSET_MODE_WITH_JIT;
    }
    ALOGD("Using structure member offsets for mode %s", xposedOffsetModesDesc[offsetMode]);

    MEMBER_OFFSET_COPY(DvmJitGlobals, codeCacheFull);

    int overrideCodeCacheFull = readIntConfig(XPOSED_OVERRIDE_JIT_RESET_OFFSET, -1);
    if (overrideCodeCacheFull > 0 && overrideCodeCacheFull < 0x400) {
        ALOGI("Offset for DvmJitGlobals.codeCacheFull is overridden, new value is 0x%x", overrideCodeCacheFull);
        MEMBER_OFFSET_VAR(DvmJitGlobals, codeCacheFull) = overrideCodeCacheFull;
    }

    // detect offset of ArrayObject->contents
    jintArray dummyArray = env->NewIntArray(1);
    if (dummyArray == NULL) {
        ALOGE("Could allocate int array for testing");
        dvmLogExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    jint* dummyArrayElements = env->GetIntArrayElements(dummyArray, NULL);
    arrayContentsOffset = (size_t)dummyArrayElements - (size_t)dvmDecodeIndirectRef(dvmThreadSelf(), dummyArray);
    env->ReleaseIntArrayElements(dummyArray,dummyArrayElements, 0);
    env->DeleteLocalRef(dummyArray);

    if (arrayContentsOffset < 12 || arrayContentsOffset > 128) {
        ALOGE("Detected strange offset %d of ArrayObject->contents", arrayContentsOffset);
        return false;
    }

    return true;
}


////////////////////////////////////////////////////////////
// Utility methods
////////////////////////////////////////////////////////////

/** Portable clone of dvmSetObjectArrayElement() */
inline void setObjectArrayElement(const ArrayObject* obj, int index, Object* val) {
    uintptr_t arrayContents = (uintptr_t)obj + arrayContentsOffset;
    ((Object **)arrayContents)[index] = val;
    dvmWriteBarrierArray(obj, index, index + 1);
}

/** Wrapper used by the common part of the library. */
void logExceptionStackTrace() {
    dvmLogExceptionStackTrace();
}

/** Check whether a method is already hooked. */
inline bool isMethodHooked(const Method* method) {
    return (method->nativeFunc == &hookedMethodCallback);
}

////////////////////////////////////////////////////////////
// JNI methods
////////////////////////////////////////////////////////////

/** This is called when a hooked method is executed. */
void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
    if (!isMethodHooked(method)) {
        dvmThrowNoSuchMethodError("Could not find Xposed original method - how did you even get here?");
        return;
    }

    XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns;
    Method* original = (Method*) hookInfo;
    Object* originalReflected = hookInfo->reflectedMethod;
    Object* additionalInfo = hookInfo->additionalInfo;

    // convert/box arguments
    const char* desc = &method->shorty[1]; // [0] is the return type.
    Object* thisObject = NULL;
    size_t srcIndex = 0;
    size_t dstIndex = 0;

    // for non-static methods determine the "this" pointer
    if (!dvmIsStaticMethod(original)) {
        thisObject = (Object*) args[0];
        srcIndex++;
    }

    ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT);
    if (argsArray == NULL) {
        return;
    }

    while (*desc != '\0') {
        char descChar = *(desc++);
        JValue value;
        Object* obj;

        switch (descChar) {
        case 'Z':
        case 'C':
        case 'F':
        case 'B':
        case 'S':
        case 'I':
            value.i = args[srcIndex++];
            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
            dvmReleaseTrackedAlloc(obj, self);
            break;
        case 'D':
        case 'J':
            value.j = dvmGetArgLong(args, srcIndex);
            srcIndex += 2;
            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
            dvmReleaseTrackedAlloc(obj, self);
            break;
        case '[':
        case 'L':
            obj  = (Object*) args[srcIndex++];
            break;
        default:
            ALOGE("Unknown method signature description character: %c", descChar);
            obj = NULL;
            srcIndex++;
        }
        setObjectArrayElement(argsArray, dstIndex++, obj);
    }

    // call the Java handler function
    JValue result;
    dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,
        originalReflected, (int) original, additionalInfo, thisObject, argsArray);

    dvmReleaseTrackedAlloc(argsArray, self);

    // exceptions are thrown to the caller
    if (dvmCheckException(self)) {
        return;
    }

    // return result with proper type
    ClassObject* returnType = dvmGetBoxedReturnType(method);
    if (returnType->primitiveType == PRIM_VOID) {
        // ignored
    } else if (result.l == NULL) {
        if (dvmIsPrimitiveClass(returnType)) {
            dvmThrowNullPointerException("null result when primitive expected");
        }
        pResult->l = NULL;
    } else {
        if (!dvmUnboxPrimitive(result.l, returnType, pResult)) {
            dvmThrowClassCastException(result.l->clazz, returnType);
        }
    }
}


void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
            jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {
    // Usage errors?
    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {
        dvmThrowIllegalArgumentException("method and declaredClass must not be null");
        return;
    }

    // Find the internal representation of the method
    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
    Method* method = dvmSlotToMethod(declaredClass, slot);
    if (method == NULL) {
        dvmThrowNoSuchMethodError("Could not get internal representation for method");
        return;
    }

    if (isMethodHooked(method)) {
        // already hooked
        return;
    }

    // Save a copy of the original method and other hook info
    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));
    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));
    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // Replace method with our own code
    SET_METHOD_FLAG(method, ACC_NATIVE);
    method->nativeFunc = &hookedMethodCallback;
    method->insns = (const u2*) hookInfo;
    method->registersSize = method->insSize;
    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {
        // reset JIT cache
        char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));
        if (currentValue == 0 || currentValue == 1) {
            MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;
        } else {
            ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);
        }
    }
}


/**
 * Simplified copy of Method.invokeNative(), but calls the original (non-hooked) method
 * and has no access checks. Used to call the real implementation of hooked methods.
 */
void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult,
            const Method* method, ::Thread* self) {
    Method* meth = (Method*) args[1];
    if (meth == NULL) {
        meth = dvmGetMethodFromReflectObj((Object*) args[0]);
        if (isMethodHooked(meth)) {
            meth = (Method*) meth->insns;
        }
    }
    ArrayObject* params = (ArrayObject*) args[2];
    ClassObject* returnType = (ClassObject*) args[3];
    Object* thisObject = (Object*) args[4]; // null for static methods
    ArrayObject* argList = (ArrayObject*) args[5];

    // invoke the method
    pResult->l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true);
    return;
}

void XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) {
    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);
    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect);
    if (clz->status < CLASS_INITIALIZED && !dvmInitClass(clz)) {
        ALOGE("Could not initialize class %s", clz->descriptor);
        return;
    }
    obj->clazz = clz;
}

void XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect) {
    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);
    dvmDumpObject(obj);
}

jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) {
    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);
    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect);

    jobject copyIndirect = env->AllocObject(clzIndirect);
    if (copyIndirect == NULL)
        return NULL;

    Object* copy = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), copyIndirect);
    size_t size = obj->clazz->objectSize;
    size_t offset = sizeof(Object);
    memcpy((char*)copy + offset, (char*)obj + offset, size - offset);

    if (IS_CLASS_FLAG_SET(clz, CLASS_ISFINALIZABLE))
        dvmSetFinalizable(copy);

    return copyIndirect;
}

void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) {
    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), javaClazz);
    if (dvmIsFinalClass(clazz)) {
        clazz->accessFlags &= ~ACC_FINAL;
    }
}

jint XposedBridge_getRuntime(JNIEnv* env, jclass clazz) {
    return 1; // RUNTIME_DALVIK
}

} // namespace android


================================================
FILE: libxposed_dalvik.h
================================================
#ifndef LIBXPOSED_DALVIK_H_
#define LIBXPOSED_DALVIK_H_

#define ANDROID_SMP 1
#include "Dalvik.h"

#include "libxposed_common.h"

namespace xposed {

#define XPOSED_OVERRIDE_JIT_RESET_OFFSET XPOSED_DIR "conf/jit_reset_offset"

struct XposedHookInfo {
    struct {
        Method originalMethod;
        // copy a few bytes more than defined for Method in AOSP
        // to accomodate for (rare) extensions by the target ROM
        int dummyForRomExtensions[4];
    } originalMethodStruct;

    Object* reflectedMethod;
    Object* additionalInfo;
};

}  // namespace xposed

#endif  // LIBXPOSED_DALVIK_H_


================================================
FILE: xposed.cpp
================================================
/**
 * This file includes functions called directly from app_main.cpp during startup.
 */

#define LOG_TAG "Xposed"

#include "xposed.h"
#include "xposed_logcat.h"
#include "xposed_safemode.h"
#include "xposed_service.h"

#include <cstring>
#include <ctype.h>
#include <cutils/process_name.h>
#include <cutils/properties.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>

#if PLATFORM_SDK_VERSION >= 18
#include <sys/capability.h>
#else
#include <linux/capability.h>
#endif

namespace xposed {

////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////

XposedShared* xposed = new XposedShared;
static int sdkVersion = -1;
static char* argBlockStart;
static size_t argBlockLength;

const char* xposedVersion = "unknown (invalid " XPOSED_PROP_FILE ")";
uint32_t xposedVersionInt = 0;

////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////

/** Handle special command line options. */
bool handleOptions(int argc, char* const argv[]) {
    parseXposedProp();

    if (argc == 2 && strcmp(argv[1], "--xposedversion") == 0) {
        printf("Xposed version: %s\n", xposedVersion);
        return true;
    }

    if (argc == 2 && strcmp(argv[1], "--xposedtestsafemode") == 0) {
        printf("Testing Xposed safemode trigger\n");

        if (detectSafemodeTrigger(shouldSkipSafemodeDelay())) {
            printf("Safemode triggered\n");
        } else {
            printf("Safemode not triggered\n");
        }
        return true;
    }

    // From Lollipop coding, used to override the process name
    argBlockStart = argv[0];
    uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
    uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
    end += strlen(argv[argc - 1]) + 1;
    argBlockLength = end - start;

    return false;
}

/** Initialize Xposed (unless it is disabled). */
bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {
#if !defined(XPOSED_ENABLE_FOR_TOOLS)
    if (!zygote)
        return false;
#endif

    if (isMinimalFramework()) {
        ALOGI("Not loading Xposed for minimal framework (encrypted device)");
        return false;
    }

    xposed->zygote = zygote;
    xposed->startSystemServer = startSystemServer;
    xposed->startClassName = className;
    xposed->xposedVersionInt = xposedVersionInt;

#if XPOSED_WITH_SELINUX
    xposed->isSELinuxEnabled   = is_selinux_enabled() == 1;
    xposed->isSELinuxEnforcing = xposed->isSELinuxEnabled && security_getenforce() == 1;
#else
    xposed->isSELinuxEnabled   = false;
    xposed->isSELinuxEnforcing = false;
#endif  // XPOSED_WITH_SELINUX

    if (startSystemServer) {
        xposed::logcat::printStartupMarker();
    } else if (zygote) {
        // TODO Find a better solution for this
        // Give the primary Zygote process a little time to start first.
        // This also makes the log easier to read, as logs for the two Zygotes are not mixed up.
        sleep(10);
    }

    printRomInfo();

    if (startSystemServer) {
        if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) {
            return false;
        }
        xposed::logcat::start();
#if XPOSED_WITH_SELINUX
    } else if (xposed->isSELinuxEnabled) {
        if (!xposed::service::startMembased()) {
            return false;
        }
#endif  // XPOSED_WITH_SELINUX
    }

#if XPOSED_WITH_SELINUX
    // Don't let any further forks access the Zygote service
    if (xposed->isSELinuxEnabled) {
        xposed::service::membased::restrictMemoryInheritance();
    }
#endif  // XPOSED_WITH_SELINUX

    // FIXME Zygote has no access to input devices, this would need to be check in system_server context
    if (zygote && !isSafemodeDisabled() && detectSafemodeTrigger(shouldSkipSafemodeDelay()))
        disableXposed();

    if (isDisabled() || (!zygote && shouldIgnoreCommand(argc, argv)))
        return false;

    return addJarToClasspath();
}

/** Print information about the used ROM into the log */
void printRomInfo() {
    char release[PROPERTY_VALUE_MAX];
    char sdk[PROPERTY_VALUE_MAX];
    char manufacturer[PROPERTY_VALUE_MAX];
    char model[PROPERTY_VALUE_MAX];
    char rom[PROPERTY_VALUE_MAX];
    char fingerprint[PROPERTY_VALUE_MAX];
    char platform[PROPERTY_VALUE_MAX];
#if defined(__LP64__)
    const int bit = 64;
#else
    const int bit = 32;
#endif

    property_get("ro.build.version.release", release, "n/a");
    property_get("ro.build.version.sdk", sdk, "n/a");
    property_get("ro.product.manufacturer", manufacturer, "n/a");
    property_get("ro.product.model", model, "n/a");
    property_get("ro.build.display.id", rom, "n/a");
    property_get("ro.build.fingerprint", fingerprint, "n/a");
    property_get("ro.product.cpu.abi", platform, "n/a");

    ALOGI("-----------------");
    ALOGI("Starting Xposed version %s, compiled for SDK %d", xposedVersion, PLATFORM_SDK_VERSION);
    ALOGI("Device: %s (%s), Android version %s (SDK %s)", model, manufacturer, release, sdk);
    ALOGI("ROM: %s", rom);
    ALOGI("Build fingerprint: %s", fingerprint);
    ALOGI("Platform: %s, %d-bit binary, system server: %s", platform, bit, xposed->startSystemServer ? "yes" : "no");
    if (!xposed->zygote) {
        ALOGI("Class name: %s", xposed->startClassName);
    }
    ALOGI("SELinux enabled: %s, enforcing: %s",
            xposed->isSELinuxEnabled ? "yes" : "no",
            xposed->isSELinuxEnforcing ? "yes" : "no");
}

/** Parses /system/xposed.prop and stores selected values in variables */
void parseXposedProp() {
    FILE *fp = fopen(XPOSED_PROP_FILE, "r");
    if (fp == NULL) {
        ALOGE("Could not read %s: %s", XPOSED_PROP_FILE, strerror(errno));
        return;
    }

    char buf[512];
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        char* key = buf;
        // Ignore leading spaces for the key
        while (isspace(*key)) key++;

        // Skip comments
        if (*key == '#')
            continue;

        // Find the key/value separator
        char* value = strchr(buf, '=');
        if (value == NULL)
            continue;

        // Ignore trailing spaces for the key
        char* tmp = value;
        do { *tmp = 0; tmp--; } while (isspace(*tmp));

        // Ignore leading spaces for the value
        do { value++; } while (isspace(*value));

        // Remove trailing newline
        tmp = strpbrk(value, "\n\r");
        if (tmp != NULL)
            *tmp = 0;

        // Handle this entry
        if (!strcmp("version", key)) {
            int len = strlen(value);
            if (len == 0)
                continue;
            tmp = (char*) malloc(len + 1);
            strlcpy(tmp, value, len + 1);
            xposedVersion = tmp;
            xposedVersionInt = atoi(xposedVersion);
        }
    }
    fclose(fp);

    return;
}

/** Returns the SDK version of the system */
int getSdkVersion() {
    if (sdkVersion < 0) {
        char sdkString[PROPERTY_VALUE_MAX];
        property_get("ro.build.version.sdk", sdkString, "0");
        sdkVersion = atoi(sdkString);
    }
    return sdkVersion;
}

/** Check whether Xposed is disabled by a flag file */
bool isDisabled() {
    if (zygote_access(XPOSED_LOAD_BLOCKER, F_OK) == 0) {
        ALOGE("Found %s, not loading Xposed", XPOSED_LOAD_BLOCKER);
        return true;
    }
    return false;
}

/** Create a flag file to disable Xposed. */
void disableXposed() {
    int fd;
    // FIXME add a "touch" operation to xposed::service::membased
    fd = open(XPOSED_LOAD_BLOCKER, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd >= 0)
        close(fd);
}

/** Check whether safemode is disabled. */
bool isSafemodeDisabled() {
    if (zygote_access(XPOSED_SAFEMODE_DISABLE, F_OK) == 0)
        return true;
    else
        return false;
}

/** Check whether the delay for safemode should be skipped. */
bool shouldSkipSafemodeDelay() {
    if (zygote_access(XPOSED_SAFEMODE_NODELAY, F_OK) == 0)
        return true;
    else
        return false;
}

/** Ignore the broadcasts by various Superuser implementations to avoid spamming the Xposed log. */
bool shouldIgnoreCommand(int argc, const char* const argv[]) {
    if (argc < 4 || strcmp(xposed->startClassName, "com.android.commands.am.Am") != 0)
        return false;

    if (strcmp(argv[2], "broadcast") != 0 && strcmp(argv[2], "start") != 0)
        return false;

    bool mightBeSuperuser = false;
    for (int i = 3; i < argc; i++) {
        if (strcmp(argv[i], "com.noshufou.android.su.RESULT") == 0
         || strcmp(argv[i], "eu.chainfire.supersu.NativeAccess") == 0)
            return true;

        if (mightBeSuperuser && strcmp(argv[i], "--user") == 0)
            return true;

        const char* lastComponent = strrchr(argv[i], '.');
        if (!lastComponent)
            continue;

        if (strcmp(lastComponent, ".RequestActivity") == 0
         || strcmp(lastComponent, ".NotifyActivity") == 0
         || strcmp(lastComponent, ".SuReceiver") == 0)
            mightBeSuperuser = true;
    }

    return false;
}

/** Adds a path to the beginning of an environment variable. */
static bool addPathToEnv(const char* name, const char* path) {
    char* oldPath = getenv(name);
    if (oldPath == NULL) {
        setenv(name, path, 1);
    } else {
        char newPath[4096];
        int neededLength = snprintf(newPath, sizeof(newPath), "%s:%s", path, oldPath);
        if (neededLength >= (int)sizeof(newPath)) {
            ALOGE("ERROR: %s would exceed %" PRIuPTR " characters", name, sizeof(newPath));
            return false;
        }
        setenv(name, newPath, 1);
    }
    return true;
}

/** Add XposedBridge.jar to the Java classpath. */
bool addJarToClasspath() {
    ALOGI("-----------------");

    // Do we have a new version and are (re)starting zygote? Then load it!
    /*
    FIXME if you can
    if (xposed->startSystemServer && access(XPOSED_JAR_NEWVERSION, R_OK) == 0) {
        ALOGI("Found new Xposed jar version, activating it");
        if (rename(XPOSED_JAR_NEWVERSION, XPOSED_JAR) != 0) {
            ALOGE("Could not move %s to %s", XPOSED_JAR_NEWVERSION, XPOSED_JAR);
            return false;
        }
    }
    */

    if (access(XPOSED_JAR, R_OK) == 0) {
        if (!addPathToEnv("CLASSPATH", XPOSED_JAR))
            return false;

        ALOGI("Added Xposed (%s) to CLASSPATH", XPOSED_JAR);
        return true;
    } else {
        ALOGE("ERROR: Could not access Xposed jar '%s'", XPOSED_JAR);
        return false;
    }
}

/** Callback which checks the loaded shared libraries for libdvm/libart. */
static bool determineRuntime(const char** xposedLibPath) {
    FILE *fp = fopen("/proc/self/maps", "r");
    if (fp == NULL) {
        ALOGE("Could not open /proc/self/maps: %s", strerror(errno));
        return false;
    }

    bool success = false;
    char line[256];
    while (fgets(line, sizeof(line), fp) != NULL) {
        char* libname = strrchr(line, '/');
        if (!libname)
            continue;
        libname++;

        if (strcmp("libdvm.so\n", libname) == 0) {
            ALOGI("Detected Dalvik runtime");
            *xposedLibPath = XPOSED_LIB_DALVIK;
            success = true;
            break;

        } else if (strcmp("libart.so\n", libname) == 0) {
            ALOGI("Detected ART runtime");
            *xposedLibPath = XPOSED_LIB_ART;
            success = true;
            break;
        }
    }

    fclose(fp);
    return success;
}

/** Load the libxposed_*.so library for the currently active runtime. */
void onVmCreated(JNIEnv* env) {
    // Determine the currently active runtime
    const char* xposedLibPath = NULL;
    if (!determineRuntime(&xposedLibPath)) {
        ALOGE("Could not determine runtime, not loading Xposed");
        return;
    }

    // Load the suitable libxposed_*.so for it
    void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW);
    if (!xposedLibHandle) {
        ALOGE("Could not load libxposed: %s", dlerror());
        return;
    }

    // Clear previous errors
    dlerror();

    // Initialize the library
    bool (*xposedInitLib)(XposedShared* shared) = NULL;
    *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib");
    if (!xposedInitLib)  {
        ALOGE("Could not find function xposedInitLib");
        return;
    }

#if XPOSED_WITH_SELINUX
    xposed->zygoteservice_accessFile = &service::membased::accessFile;
    xposed->zygoteservice_statFile   = &service::membased::statFile;
    xposed->zygoteservice_readFile   = &service::membased::readFile;
#endif  // XPOSED_WITH_SELINUX

    if (xposedInitLib(xposed)) {
        xposed->onVmCreated(env);
    }
}

/** Set the process name */
void setProcessName(const char* name) {
    memset(argBlockStart, 0, argBlockLength);
    strlcpy(argBlockStart, name, argBlockLength);
    set_process_name(name);
}

/** Determine the UID/GID of Xposed Installer. */
bool determineXposedInstallerUidGid() {
    if (xposed->isSELinuxEnabled) {
        struct stat* st = (struct stat*) mmap(NULL, sizeof(struct stat), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        if (st == MAP_FAILED) {
            ALOGE("Could not allocate memory in determineXposedInstallerUidGid(): %s", strerror(errno));
            return false;
        }

        pid_t pid;
        if ((pid = fork()) < 0) {
            ALOGE("Fork in determineXposedInstallerUidGid() failed: %s", strerror(errno));
            munmap(st, sizeof(struct stat));
            return false;
        } else if (pid == 0) {
            // Child.
#if XPOSED_WITH_SELINUX
            if (setcon(ctx_app) != 0) {
                ALOGE("Could not switch to %s context", ctx_app);
                exit(EXIT_FAILURE);
            }
#endif  // XPOSED_WITH_SELINUX

            if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, st)) != 0) {
                ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno));
                exit(EXIT_FAILURE);
            }

            exit(EXIT_SUCCESS);
        }

        // Parent.
        int status;
        if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            munmap(st, sizeof(struct stat));
            return false;
        }

        xposed->installer_uid = st->st_uid;
        xposed->installer_gid = st->st_gid;
        munmap(st, sizeof(struct stat));
        return true;
    } else {
        struct stat st;
        if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, &st)) != 0) {
            ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno));
            return false;
        }

        xposed->installer_uid = st.st_uid;
        xposed->installer_gid = st.st_gid;
        return true;
    }
}

/** Switch UID/GID to the ones of Xposed Installer. */
bool switchToXposedInstallerUidGid() {
    if (setresgid(xposed->installer_gid, xposed->installer_gid, xposed->installer_gid) != 0) {
        ALOGE("Could not setgid(%d): %s", xposed->installer_gid, strerror(errno));
        return false;
    }
    if (setresuid(xposed->installer_uid, xposed->installer_uid, xposed->installer_uid) != 0) {
        ALOGE("Could not setuid(%d): %s", xposed->installer_uid, strerror(errno));
        return false;
    }
    return true;
}

/** Drop all capabilities except for the mentioned ones */
void dropCapabilities(int8_t keep[]) {
    struct __user_cap_header_struct header;
    struct __user_cap_data_struct cap[2];
    memset(&header, 0, sizeof(header));
    memset(&cap, 0, sizeof(cap));
    header.version = _LINUX_CAPABILITY_VERSION_3;
    header.pid = 0;

    if (keep != NULL) {
      for (int i = 0; keep[i] >= 0; i++) {
        cap[CAP_TO_INDEX(keep[i])].permitted |= CAP_TO_MASK(keep[i]);
      }
      cap[0].effective = cap[0].inheritable = cap[0].permitted;
      cap[1].effective = cap[1].inheritable = cap[1].permitted;
    }

    capset(&header, &cap[0]);
}

/**
 * Checks whether the system is booting into a minimal Android framework.
 * This is the case when the device is encrypted with a password that
 * has to be entered on boot. /data is a tmpfs in that case, so we
 * can't load any modules anyway.
 * The system will reboot later with the full framework.
 */
bool isMinimalFramework() {
    char voldDecrypt[PROPERTY_VALUE_MAX];
    property_get("vold.decrypt", voldDecrypt, "");
    return ((strcmp(voldDecrypt, "trigger_restart_min_framework") == 0) ||
            (strcmp(voldDecrypt, "1") == 0));
}

}  // namespace xposed


================================================
FILE: xposed.h
================================================
#ifndef XPOSED_H_
#define XPOSED_H_

#include "xposed_shared.h"

#define XPOSED_PROP_FILE "/system/xposed.prop"

#if defined(__LP64__)
  #define XPOSED_LIB_DIR "/system/lib64/"
#else
  #define XPOSED_LIB_DIR "/system/lib/"
#endif
#define XPOSED_LIB_DALVIK        XPOSED_LIB_DIR "libxposed_dalvik.so"
#define XPOSED_LIB_ART           XPOSED_LIB_DIR "libxposed_art.so"
#define XPOSED_JAR               "/system/framework/XposedBridge.jar"
#define XPOSED_JAR_NEWVERSION    XPOSED_DIR "bin/XposedBridge.jar.newversion"
#define XPOSED_LOAD_BLOCKER      XPOSED_DIR "conf/disabled"
#define XPOSED_SAFEMODE_NODELAY  XPOSED_DIR "conf/safemode_nodelay"
#define XPOSED_SAFEMODE_DISABLE  XPOSED_DIR "conf/safemode_disable"

#define XPOSED_CLASS_DOTS_ZYGOTE "de.robv.android.xposed.XposedBridge"
#define XPOSED_CLASS_DOTS_TOOLS  "de.robv.android.xposed.XposedBridge$ToolEntryPoint"

#if XPOSED_WITH_SELINUX
#include <selinux/selinux.h>
#define ctx_system ((security_context_t) "u:r:system_server:s0")
#if PLATFORM_SDK_VERSION >= 23
#define ctx_app    ((security_context_t) "u:r:untrusted_app:s0:c512,c768")
#else
#define ctx_app    ((security_context_t) "u:r:untrusted_app:s0")
#endif  // PLATFORM_SDK_VERSION >= 23
#endif  // XPOSED_WITH_SELINUX

namespace xposed {

    bool handleOptions(int argc, char* const argv[]);
    bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]);
    void printRomInfo();
    void parseXposedProp();
    int getSdkVersion();
    bool isDisabled();
    void disableXposed();
    bool isSafemodeDisabled();
    bool shouldSkipSafemodeDelay();
    bool shouldIgnoreCommand(int argc, const char* const argv[]);
    bool addJarToClasspath();
    void onVmCreated(JNIEnv* env);
    void setProcessName(const char* name);
    bool determineXposedInstallerUidGid();
    bool switchToXposedInstallerUidGid();
    void dropCapabilities(int8_t keep[] = NULL);
    bool isMinimalFramework();

}  // namespace xposed

#endif  // XPOSED_H_


================================================
FILE: xposed_logcat.cpp
================================================
/**
 * This file includes the Xposed service, which is especially used to work around SELinux restrictions.
 */

#define LOG_TAG "Xposed"

#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>

#include "xposed.h"
#include "xposed_service.h"
#include "xposed_logcat.h"


namespace xposed {
namespace logcat {

////////////////////////////////////////////////////////////
// Declarations
////////////////////////////////////////////////////////////

#define AID_LOG      1007
#define CAP_SYSLOG   34
char marker[50];


////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////

static void execLogcat() {
    int8_t keep[] = { CAP_SYSLOG, -1 };
    xposed::dropCapabilities(keep);

    // Execute a logcat command that will keep running in the background
    if (zygote_access(XPOSEDLOG_CONF_ALL, F_OK) == 0) {
        execl("/system/bin/logcat", "logcat",
            "-v", "time",            // include timestamps in the log
            (char*) 0);
    } else {
        execl("/system/bin/logcat", "logcat",
            "-v", "time",            // include timestamps in the log
            "-s",                    // be silent by default, except for the following tags
            "XposedStartupMarker:D", // marks the beginning of the current log
            "Xposed:I",              // Xposed framework and default logging
            "appproc:I",             // app_process
            "XposedInstaller:I",     // Xposed Installer
            "art:F",                 // ART crashes
            (char*) 0);
    }

    // We only get here in case of errors
    ALOGE("Could not execute logcat: %s", strerror(errno));
    exit(EXIT_FAILURE);
}

#ifndef dprintf
static inline int dprintf(int fd, const char *format, ...) {
    char* message;
    va_list args;
    va_start(args, format);
    int size = vasprintf(&message, format, args);
    if (size > 0) {
        write(fd, message, size);
        free(message);
    }
    va_end(args);
    return size;
}
#endif

static void runDaemon(int pipefd) {
    xposed::setProcessName("xposed_logcat");
    xposed::dropCapabilities();

    umask(0);
    int logfile = open(XPOSEDLOG, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
    if (logfile < 0) {
        ALOGE("Could not open %s: %s", XPOSEDLOG, strerror(errno));
        exit(EXIT_FAILURE);
    }

    FILE* pipe = fdopen(pipefd, "r");
    if (pipe == NULL) {
        ALOGE("fdopen failed for pipe file descriptor %d: %s", pipefd, strerror(errno));
        exit(EXIT_FAILURE);
    }

    char buf[512];
    bool foundMarker = false;
    long totalSize = 0;
    while (fgets(buf, sizeof(buf), pipe) != NULL) {
        if (buf[0] == '-')
            continue; // beginning of <logbuffer type>

        if (!foundMarker) {
            if (strstr(buf, "XposedStartupMarker") != NULL && strstr(buf, marker) != NULL) {
                foundMarker = true;
            }
            continue;
        }

        int len = strlen(buf);
        write(logfile, buf, len);

        totalSize += len;
        if (totalSize > XPOSEDLOG_MAX_SIZE) {
            dprintf(logfile, "\nReached maximum log size (%'d kB), further lines won't be logged.\n", XPOSEDLOG_MAX_SIZE / 1024);
            exit(EXIT_FAILURE);
        }
    }

    ALOGE("Broken pipe to logcat: %s", strerror(ferror(pipe)));
    close(logfile);
    exit(EXIT_FAILURE);
}

void printStartupMarker() {
    sprintf(marker, "Current time: %d, PID: %d", (int) time(NULL), getpid());
    ALOG(LOG_DEBUG, "XposedStartupMarker", marker, NULL);
}

void start() {
    // Fork to create a daemon
    pid_t pid;
    if ((pid = fork()) < 0) {
        ALOGE("Fork for Xposed logcat daemon failed: %s", strerror(errno));
        return;
    } else if (pid != 0) {
        return;
    }

    // Ensure that we're allowed to read all log entries
    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
        ALOGE("Failed to keep capabilities: %s", strerror(errno));
    }
    const gid_t groups[] = { AID_LOG };
    setgroups(1, groups);
    if (!xposed::switchToXposedInstallerUidGid()) {
        exit(EXIT_FAILURE);
    }

#if XPOSED_WITH_SELINUX
    if (xposed->isSELinuxEnabled) {
        if (setcon(ctx_app) != 0) {
            ALOGE("Could not switch to %s context", ctx_app);
            exit(EXIT_FAILURE);
        }
    }
#endif  // XPOSED_WITH_SELINUX

    int err = rename(XPOSEDLOG, XPOSEDLOG_OLD);
    if (err < 0 && errno != ENOENT) {
        ALOGE("%s while renaming log file %s -> %s", strerror(errno), XPOSEDLOG, XPOSEDLOG_OLD);
        exit(EXIT_FAILURE);
    }

    int pipeFds[2];
    if (pipe(pipeFds) < 0) {
        ALOGE("Could not allocate pipe for logcat output: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    fcntl(pipeFds[0], F_SETPIPE_SZ, 1048576);

    if ((pid = fork()) < 0) {
        ALOGE("Fork for logcat execution failed: %s", strerror(errno));
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        close(pipeFds[0]);
        if (dup2(pipeFds[1], STDOUT_FILENO) == -1) {
            ALOGE("Could not redirect stdout: %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
        if (dup2(pipeFds[1], STDERR_FILENO) == -1) {
            ALOGE("Could not redirect stdout: %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
        execLogcat();
    } else {
        close(pipeFds[1]);
        runDaemon(pipeFds[0]);
    }

    // Should never reach this point
    exit(EXIT_FAILURE);
}

}  // namespace logcat
}  // namespace xposed


================================================
FILE: xposed_logcat.h
================================================
#ifndef XPOSED_LOGCAT_H_
#define XPOSED_LOGCAT_H_

#define XPOSEDLOG            XPOSED_DIR "log/error.log"
#define XPOSEDLOG_OLD        XPOSEDLOG ".old"
#define XPOSEDLOG_CONF_ALL   XPOSED_DIR "conf/log_all"
#define XPOSEDLOG_MAX_SIZE   5*1024*1024

namespace xposed {
namespace logcat {

    void printStartupMarker();
    void start();

}  // namespace logcat
}  // namespace xposed

#endif /* XPOSED_LOGCAT_H_ */


================================================
FILE: xposed_offsets.h
================================================
/*
    Certain compile time parameters result in different offsets
    for members in structures. This file defines the offsets for
    members which cannot be accessed otherwise and some macros
    to simplify accessing them.
*/

#define MEMBER_OFFSET_ARRAY(type,member) offsets_array_ ## type ## _ ## member
#define MEMBER_OFFSET_VAR(type,member) offset_ ## type ## _ ## member
#define MEMBER_TYPE(type,member) offset_type_ ## type ## _ ## member

#define MEMBER_PTR(obj,type,member) \
    ( (MEMBER_TYPE(type,member)*)  ( (char*)(obj) + MEMBER_OFFSET_VAR(type,member) ) )
#define MEMBER_VAL(obj,type,member) *MEMBER_PTR(obj,type,member)

#define MEMBER_OFFSET_DEFINE(type,member,offsets...) \
    static int MEMBER_OFFSET_ARRAY(type,member)[] = { offsets }; \
    static int MEMBER_OFFSET_VAR(type,member);
#define MEMBER_OFFSET_COPY(type,member) MEMBER_OFFSET_VAR(type,member) = MEMBER_OFFSET_ARRAY(type,member)[offsetMode]


// here are the definitions of the modes and offsets
enum xposedOffsetModes {
    MEMBER_OFFSET_MODE_WITH_JIT,
    MEMBER_OFFSET_MODE_NO_JIT,
};
static xposedOffsetModes offsetMode;
const char* xposedOffsetModesDesc[] = {
    "WITH_JIT",
    "NO_JIT",
};

MEMBER_OFFSET_DEFINE(DvmJitGlobals, codeCacheFull, 120, 0)
#define offset_type_DvmJitGlobals_codeCacheFull bool



// helper to determine the required values (compile with XPOSED_SHOW_OFFSET=true)
#ifdef XPOSED_SHOW_OFFSETS
    template<int s> struct RESULT;
    #ifdef WITH_JIT
        #pragma message "WITH_JIT is defined"
    #else
       #pragma message "WITH_JIT is not defined"
    #endif
    RESULT<sizeof(Method)> SIZEOF_Method;
    RESULT<sizeof(Thread)> SIZEOF_Thread;
    RESULT<offsetof(DvmJitGlobals, codeCacheFull)> OFFSETOF_DvmJitGlobals_codeCacheFull;
#endif




================================================
FILE: xposed_safemode.cpp
================================================
/*
 * Detects input combinations for recovering from bootloops.
 *
 * The safemode trigger is detected if exactly one of the physical keys is pressed in
 * the first 2 seconds after detection startup (or already held down), and a total of
 * 5 consecutive presses of that same key are performed in the subsequent 5 seconds.
 *
 * 2 short vibrations are performed when the first key is pressed; an additional
 * vibration is performed for each subsequent press of the same key, and a final
 * long vibration is performed if the trigger was successful.
 *
 * The initial 2-second delay can be disabled through configuration; in that case,
 * one of the keys must already be pressed when the detection starts, otherwise
 * the detection fails and no delays are introduced.
 *
 * References:
 *   /frameworks/base/services/input/EventHub.cpp (AOSP)
 *   /include/uapi/linux/input.h (Linux)
 *   Using the Input Subsystem, Linux Journal
 */
#include "xposed_safemode.h"

#include <cstring>
#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <sys/epoll.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>

#define INITIAL_DELAY 2
#define DETECTION_TIMEOUT 5

#define DETECTION_PRESSES 5

#define VIBRATOR_CONTROL "/sys/class/timed_output/vibrator/enable"
#define VIBRATION_SHORT 150
#define VIBRATION_LONG 500
#define VIBRATION_INTERVAL 200

static const char *DEVICE_PATH = "/dev/input";
#define MAX_DEVICES 4

#define test_bit(bit, array)    (array[bit/8] & (1<<(bit%8)))

static const int physical_keycodes[] = { KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_POWER,
                                         KEY_HOME, KEY_BACK, KEY_MENU, KEY_CAMERA };



static void vibrate(int count, int duration_ms, int interval_ms) {
    int fd;
    int len;
    char value[30];

    if ((fd = open(VIBRATOR_CONTROL, O_RDWR)) < 0)
        // Failed to open the control file, ignore it
        return;

    len = sprintf(value, "%d\n", duration_ms);
    for (int i = 0; i < count; i++) {
        if (i != 0)
            // Pause between the several vibrations
            usleep((duration_ms + interval_ms) * 1000);
        // Vibrate (asynchronously)
        write(fd, value, len);
    }
    close(fd);
}


/*
 * Enumerates the existing input devices and opens handles for the ones that
 * report the relevant keys.
 *
 * Arguments:
 * - *fds: is filled on output with the file handles for the opened devices
 * - max_fds: maximum available entries in the fds array
 * - *pressedKey: is filled on output with
 *        0 if no key was found being held down at this instant
 *       -1 if more than one key was found being held down
 *       id of the pressed key, if only a single one was being held down
 * Returns:
 * - the number of opened device handles, filled in the *fds output parameter
 * - 0 if no devices were opened
 */
static int openKeyDevices(int *fds, int max_fds, int *pressedKey) {
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;

    int count = 0;
    // No key was detected as pressed, for the moment
    *pressedKey = 0;

    dir = opendir(DEVICE_PATH);
    if(dir == NULL)
        return 0;

    strcpy(devname, DEVICE_PATH);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while (count < max_fds && (de = readdir(dir))) {
        // Skip '.' and '..'
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;

        strcpy(filename, de->d_name);
        int fd = open(devname, O_RDWR | O_CLOEXEC);
        if(fd < 0)
            // Skip files that could not be opened
            continue;

        // Check if this device reports one of the relevant keys
        uint8_t keyBitmask[(KEY_MAX + 1) / 8];
        memset(keyBitmask, 0, sizeof(keyBitmask));
        ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBitmask)), keyBitmask);
        bool reportsKeys = false;
        for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) {
            if (test_bit(physical_keycodes[i], keyBitmask)) {
                reportsKeys = true;
                break;
            }
        }
        if (!reportsKeys) {
            // This device doesn't report any of the relevant keys
            // Skip to the next one
            close(fd);
            continue;
        }

        fds[count++] = fd;

        // Check if one of the keys is currently pressed on this device, to report it to the caller
        memset(keyBitmask, 0, sizeof(keyBitmask));
        ioctl(fd, EVIOCGKEY(sizeof(keyBitmask)), keyBitmask);
        for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) {
            if (test_bit(physical_keycodes[i], keyBitmask)) {
                // One of the relevant keys was detected as held down
                // We'll report it to be pressed, but only if there isn't more than one key being pressed
                if (*pressedKey == 0) {
                    // No key was being pressed, this one will be reported
                    *pressedKey = physical_keycodes[i];
                } else {
                    // Another key was already found to be pressed, report multiple keys to the caller
                    *pressedKey = -1;
                    break;
                }
            }
        }
    }

    closedir(dir);
    return count;
}


/*
 * Computes the remaining time, in ms, from the current time to the supplied expiration moment
 */
int getRemainingTime(struct timespec expiration) {
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    if (now.tv_sec > expiration.tv_sec)
        return 0;
    else
        return (expiration.tv_sec - now.tv_sec) * 1000 + (expiration.tv_nsec - now.tv_nsec) / 1000000;
}



namespace xposed {

bool detectSafemodeTrigger(bool skipInitialDelay) {

    int efd = -1;
    int fds[MAX_DEVICES];
    int deviceCount = 0;
    int pressedKey = 0;
    int triggerPresses = 0;
    bool result = false;

    // Open input devices that report one of the relevant physical keys
    deviceCount = openKeyDevices(fds, sizeof(fds) / sizeof(fds[0]), &pressedKey);
    if (deviceCount == 0)
        // No input devices found, abort detection
        goto leave;

    if (pressedKey < 0)
        // More than one key was initially pressed
        // Immediately report a negative detection, with no further delays
        goto leave;

    if (pressedKey == 0 && skipInitialDelay)
        // A single key wasn't held down and the initial delay is disabled
        // Immediately report a negative detection, with no further delays
        goto leave;

    // Prepare waiting mechanism for received events in all devices
    if ((efd = epoll_create(deviceCount)) < 0)
        // Failed to create the epoll handle, abort
        goto leave;

    // Register each device descriptor in the epoll handle
    struct epoll_event eventPollItems[MAX_DEVICES];
    for (int i = 0; i < deviceCount; i++) {
        memset(&eventPollItems[i], 0, sizeof(eventPollItems[i]));
        eventPollItems[i].events = EPOLLIN;
        eventPollItems[i].data.fd = fds[i];
        if (epoll_ctl(efd, EPOLL_CTL_ADD, fds[i], &eventPollItems[i]))
            // Failed to add device descriptor to the epoll handle, abort
            goto leave;
    }

    int timeout_ms;
    struct timespec expiration;
    clock_gettime(CLOCK_MONOTONIC, &expiration);
    expiration.tv_sec += INITIAL_DELAY;

    // Wait up to INITIAL_DELAY seconds for an initial keypress, it no key was initially down
    while (pressedKey == 0 && (timeout_ms = getRemainingTime(expiration)) > 0) {
        // Wait for next input event in one of the opened devices
        int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms);
        if (pollResult < 0)
            // Failed to wait for event, abort
            goto leave;

        // Loop through the opened devices where a new event is available
        for (int i = 0; i < pollResult; i++) {
            struct input_event evt;
            int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt));
            if (readSize != sizeof(evt))
                // Invalid size read, ignore
                continue;

            if (evt.type != EV_KEY)
                // Only consider key events
                continue;
            if (evt.value != 1)
                // Ignore key releases, we're monitoring presses
                continue;

            for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) {
                if (evt.code == physical_keycodes[j]) {
                    // One of the keys was pressed, end the initial detection
                    // No need to check for duplicate keys, as the events are reported sequentially
                    // and multiple presses can't be reported at once
                    pressedKey = evt.code;
                    break;
                }
            }
        }
    }
    if (pressedKey == 0)
        // No key was pressed during the initial delay or upfront, so the detection has failed
        goto leave;

    // Notify the user that the safemode sequence has been started and we're waiting for
    // the remaining key presses
    vibrate(2, VIBRATION_SHORT, VIBRATION_INTERVAL);


    // Detection will wait at most DETECTION_TIMEOUT seconds
    clock_gettime(CLOCK_MONOTONIC, &expiration);
    expiration.tv_sec += DETECTION_TIMEOUT;

    // Initial key press is counted as well
    triggerPresses++;

    // Loop waiting for the same key to be pressed the appropriate number of times, a different key to
    // be pressed, or the timeout to be reached
    while (triggerPresses < DETECTION_PRESSES && (timeout_ms = getRemainingTime(expiration)) > 0) {
        // Wait for next input event
        int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms);
        if (pollResult < 0)
            // Failed to wait for event, abort
            goto leave;

        // Loop through the opened devices where a new event is available
        for (int i = 0; i < pollResult; i++) {
            struct input_event evt;
            int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt));
            if (readSize != sizeof(evt))
                // Invalid size read, ignore
                continue;

            if (evt.type != EV_KEY)
                // Only consider key events
                continue;
            if (evt.value != 1)
                // Ignore key releases, we're monitoring presses
                continue;

            for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) {
                if (evt.code == physical_keycodes[j]) {
                    if (pressedKey == evt.code) {
                        // The same key was pressed again, increment the counter and notify the user
                        triggerPresses++;
                        // The final key press will be confirmed with a long vibration later
                        if (triggerPresses < DETECTION_PRESSES)
                            vibrate(1, VIBRATION_SHORT, 0);
                    } else {
                        // A key was pressed other than the initial one
                        // Abort the detection and avoid further delays
                        goto leave;
                    }
                    break;
                }
            }
        }
    }

    // Was safemode successfully triggered?
    if (triggerPresses >= DETECTION_PRESSES) {
        vibrate(1, VIBRATION_LONG, 0);
        result = true;
    }

leave:
    if (efd >= 0)
        close(efd);
    for (int i = 0; i < deviceCount; i++)
        close(fds[i]);

    return result;

}

}



================================================
FILE: xposed_safemode.h
================================================
#ifndef XPOSED_SAFEMODE_H_
#define XPOSED_SAFEMODE_H_

namespace xposed {

bool detectSafemodeTrigger(bool skipInitialDelay);

}

#endif  // XPOSED_SAFEMODE_H_



================================================
FILE: xposed_service.cpp
================================================
/**
 * This file includes the Xposed services, which are especially used to work around SELinux restrictions.
 */

#define LOG_TAG "Xposed"

#include "xposed.h"
#include "xposed_service.h"

#include <binder/BpBinder.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <errno.h>
#include <fcntl.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <sys/mman.h>

#define UID_SYSTEM 1000

using namespace android;

namespace xposed {
namespace service {

////////////////////////////////////////////////////////////
// Declarations
////////////////////////////////////////////////////////////

bool running = false;


////////////////////////////////////////////////////////////
// Memory-based communication (used by Zygote)
////////////////////////////////////////////////////////////

namespace membased {

enum State {
    STATE_NOT_RUNNING,
    STATE_IDLE,
    STATE_SERVICE_ACTION,
    STATE_SERVER_RESPONSE,
};

enum Action {
    OP_NONE,
    OP_ACCESS_FILE,
    OP_STAT_FILE,
    OP_READ_FILE,
};

struct AccessFileData {
    // in
    char path[PATH_MAX];
    int mode;
    // out
    int result;
};

struct StatFileData {
    // in
    char path[PATH_MAX];
    // inout
    struct stat st;
    // out
    int result;
};

struct ReadFileData {
    // in
    char path[PATH_MAX];
    int offset;
    // out
    int totalSize;
    int bytesRead;
    bool eof;
    char content[32*1024];
};

struct MemBasedState {
    pthread_mutex_t workerMutex;
    pthread_cond_t workerCond;
    State state;
    Action action;
    int error;
    union {
        AccessFileData accessFile;
        StatFileData statFile;
        ReadFileData readFile;
    } data;
};

MemBasedState* shared = NULL;
pid_t zygotePid = 0;
bool canAlwaysAccessService = false;

inline static void initSharedMutex(pthread_mutex_t* mutex) {
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mutex, &attr);
    pthread_mutexattr_destroy(&attr);
}

inline static void initSharedCond(pthread_cond_t* cond) {
    pthread_condattr_t cattr;
    pthread_condattr_init(&cattr);
    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(cond, &cattr);
    pthread_condattr_destroy(&cattr);
}

static bool init() {
    shared = (MemBasedState*) mmap(NULL, sizeof(MemBasedState), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared == MAP_FAILED) {
        ALOGE("Could not allocate memory for Zygote service: %s", strerror(errno));
        shared = NULL;
        return false;
    }

    zygotePid = getpid();
    canAlwaysAccessService = true;

    initSharedMutex(&shared->workerMutex);
    initSharedCond(&shared->workerCond);
    shared->state = STATE_NOT_RUNNING;
    shared->action = OP_NONE;
    shared->error = 0;
    return true;
}

void restrictMemoryInheritance() {
    madvise(shared, sizeof(MemBasedState), MADV_DONTFORK);
    canAlwaysAccessService = false;
}

static inline bool isServiceAccessible() {
    if (!canAlwaysAccessService && (shared == NULL || zygotePid != getpid())) {
        ALOGE("Zygote service is not accessible from PID %d, UID %d", getpid(), getuid());
        shared = NULL;
        errno = EPERM;
        return false;
    }
    return true;
}

// Server implementation
void* looper(void* unused __attribute__((unused))) {
    pthread_mutex_lock(&shared->workerMutex);
    shared->state = STATE_IDLE;
    pthread_cond_broadcast(&shared->workerCond);
    while (1) {
        while (shared->state != STATE_SERVICE_ACTION) {
            pthread_cond_wait(&shared->workerCond, &shared->workerMutex);
        }

        switch (shared->action) {
            case OP_ACCESS_FILE: {
                struct AccessFileData* data = &shared->data.accessFile;
                data->result = TEMP_FAILURE_RETRY(access(data->path, data->mode));
                if (data->result != 0) {
                    shared->error = errno;
                }
            } break;

            case OP_STAT_FILE: {
                struct StatFileData* data = &shared->data.statFile;
                data->result = TEMP_FAILURE_RETRY(stat(data->path, &data->st));
                if (data->result != 0) {
                    shared->error = errno;
                }
            } break;

            case OP_READ_FILE: {
                struct ReadFileData* data = &shared->data.readFile;
                struct stat st;

                if (stat(data->path, &st) != 0) {
                    shared->error = errno;
                    break;
                }

                data->totalSize = st.st_size;

                FILE *f = fopen(data->path, "r");
                if (f == NULL) {
                    shared->error = errno;
                    break;
                }

                if (data->offset > 0 && fseek(f, data->offset, SEEK_SET) != 0) {
                    shared->error = ferror(f);
                    fclose(f);
                    break;
                }

                data->bytesRead = fread(data->content, 1, sizeof(data->content), f);
                shared->error = ferror(f);
                data->eof = feof(f);

                fclose(f);
            } break;

            case OP_NONE: {
                ALOGE("No-op call to membased service");
                break;
            }

            default: {
                ALOGE("Invalid action in call to membased service");
                break;
            }
        }

        shared->state = STATE_SERVER_RESPONSE;
        pthread_cond_broadcast(&shared->workerCond);
    }

    pthread_mutex_unlock(&shared->workerMutex);
    return NULL;
}

// Client implementation
static inline bool waitForRunning(int timeout) {
    if (shared == NULL || timeout < 0)
        return false;

    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += 5;
    int rc = 0;
    pthread_mutex_lock(&shared->workerMutex);
    while (shared->state == STATE_NOT_RUNNING && rc == 0) {
        rc = pthread_cond_timedwait(&shared->workerCond, &shared->workerMutex, &ts);
    }
    pthread_mutex_unlock(&shared->workerMutex);
    return rc == 0;
}

static inline void waitForIdle() {
    pthread_mutex_lock(&shared->workerMutex);
    while (shared->state != STATE_IDLE) {
        pthread_cond_wait(&shared->workerCond, &shared->workerMutex);
    }
}

static inline void callService(Action action) {
    shared->action = action;
    shared->state = STATE_SERVICE_ACTION;
    shared->error = 0;
    pthread_cond_broadcast(&shared->workerCond);

    while (shared->state != STATE_SERVER_RESPONSE) {
        pthread_cond_wait(&shared->workerCond, &shared->workerMutex);
    }
}

static inline void makeIdle() {
    shared->action = OP_NONE;
    shared->state = STATE_IDLE;
    pthread_cond_broadcast(&shared->workerCond);
    pthread_mutex_unlock(&shared->workerMutex);
}

int accessFile(const char* path, int mode) {
    if (!isServiceAccessible())
        return -1;

    if (strlen(path) > sizeof(AccessFileData::path) - 1) {
        errno = ENAMETOOLONG;
        return -1;
    }

    waitForIdle();

    struct AccessFileData* data = &shared->data.accessFile;
    strcpy(data->path, path);
    data->mode = mode;

    callService(OP_ACCESS_FILE);

    makeIdle();
    errno = shared->error;
    return shared->error ? -1 : data->result;
}

int statFile(const char* path, struct stat* st) {
    if (!isServiceAccessible())
        return -1;

    if (strlen(path) > sizeof(StatFileData::path) - 1) {
        errno = ENAMETOOLONG;
        return -1;
    }

    waitForIdle();

    struct StatFileData* data = &shared->data.statFile;
    strcpy(data->path, path);

    callService(OP_STAT_FILE);

    memcpy(st, &data->st, sizeof(struct stat));

    makeIdle();
    errno = shared->error;
    return shared->error ? -1 : data->result;
}

char* readFile(const char* path, int* bytesRead) {
    if (!isServiceAccessible())
        return NULL;

    char* result = NULL;
    int offset = 0, totalSize = 0;

    if (bytesRead)
        *bytesRead = 0;

    if (strlen(path) > sizeof(ReadFileData::path) - 1) {
        errno = ENAMETOOLONG;
        return NULL;
    }

    waitForIdle();

    struct ReadFileData* data = &shared->data.readFile;
    strcpy(data->path, path);
    data->offset = 0;

    callService(OP_READ_FILE);
    if (shared->error)
        goto bail;

    totalSize = data->totalSize;
    result = (char*) malloc(totalSize + 1);
    result[totalSize] = 0;
    memcpy(result, data->content, data->bytesRead);

    while (!data->eof) {
        offset += data->bytesRead;
        data->offset = offset;

        callService(OP_READ_FILE);
        if (shared->error)
            goto bail;

        if (offset + data->bytesRead > totalSize) {
            shared->error = EBUSY;
            goto bail;
        }

        memcpy(result + offset, data->content, data->bytesRead);
    }

    if (bytesRead)
        *bytesRead = offset + data->bytesRead;

    bail:
    makeIdle();
    if (shared->error && result) {
        free(result);
        result = NULL;
    }
    errno = shared->error;
    return result;
}

}  // namespace membased


////////////////////////////////////////////////////////////
// Binder service
////////////////////////////////////////////////////////////

namespace binder {

#define XPOSED_BINDER_SYSTEM_SERVICE_NAME "user.xposed.system"
#define XPOSED_BINDER_APP_SERVICE_NAME    "user.xposed.app"

class IXposedService: public IInterface {
    public:
        DECLARE_META_INTERFACE(XposedService);
        virtual int test() const = 0;
        virtual status_t addService(const String16& name,
                                    const sp<IBinder>& service,
                                    bool allowIsolated = false) const = 0;
        virtual int accessFile(const String16& filename,
                               int32_t mode) const = 0;
        virtual int statFile(const String16& filename,
                             int64_t* size,
                             int64_t* mtime) const = 0;
        virtual status_t readFile(const String16& filename,
                                  int32_t offset,
                                  int32_t length,
                                  int64_t* size,
                                  int64_t* mtime,
                                  uint8_t** buffer,
                                  int32_t* bytesRead,
                                  String16* errormsg) const = 0;

        enum {
            TEST_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
            ADD_SERVICE_TRANSACTION,
            ACCESS_FILE_TRANSACTION,
            STAT_FILE_TRANSACTION,
            READ_FILE_TRANSACTION,
        };
};

class BnXposedService: public BnInterface<IXposedService> {
    public:
        virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};

class BpXposedService: public BpInterface<IXposedService> {
    public:
        BpXposedService(const sp<IBinder>& impl) : BpInterface<IXposedService>(impl) {}

        virtual int test() const {
            Parcel data, reply;
            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());
            remote()->transact(TEST_TRANSACTION, data, &reply);
            if (reply.readExceptionCode() != 0) return -1;
            return reply.readInt32();
        }

        virtual status_t addService(const String16& name, const sp<IBinder>& service,
                bool allowIsolated = false) const {
            Parcel data, reply;
            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());
            data.writeString16(name);
            data.writeStrongBinder(service);
            data.writeInt32(allowIsolated ? 1 : 0);
            status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
            return err == NO_ERROR ? reply.readExceptionCode() : err;
        }

        virtual status_t accessFile(const String16& name, int32_t mode) const {
            Parcel data, reply;
            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());
            data.writeString16(name);
            data.writeInt32(mode);

            remote()->transact(ACCESS_FILE_TRANSACTION, data, &reply);
            if (reply.readExceptionCode() != 0) return -1;

            errno = reply.readInt32();
            return (errno == 0) ? 0 : -1;
        }

        virtual status_t statFile(const String16& name, int64_t* size, int64_t* mtime) const {
            Parcel data, reply;
            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());
            data.writeString16(name);

            remote()->transact(STAT_FILE_TRANSACTION, data, &reply);
            if (reply.readExceptionCode() != 0) return -1;

            errno = reply.readInt32();
            if (errno != 0) return -1;

            int64_t size1 = reply.readInt64();
            int64_t mtime1 = reply.readInt64();
            if (size != NULL) *size = size1;
            if (mtime != NULL) *mtime = mtime1;
            return 0;
        }

        virtual status_t readFile(const String16& filename, int32_t offset, int32_t length,
                int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const {
            Parcel data, reply;
            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());
            data.writeString16(filename);
            data.writeInt32(offset);
            data.writeInt32(length);
            int64_t size1 = 0;
            int64_t mtime1 = 0;
            if (size != NULL) size1 = *size;
            if (mtime != NULL) mtime1 = *mtime;
            data.writeInt64(size1);
            data.writeInt64(mtime1);

            remote()->transact(READ_FILE_TRANSACTION, data, &reply);
            if (reply.readExceptionCode() != 0) return -1;

            status_t err = reply.readInt32();
            const String16& errormsg1(reply.readString16());
            size1 = reply.readInt64();
            mtime1 = reply.readInt64();
            int32_t bytesRead1 = reply.readInt32();
            if (size != NULL) *size = size1;
            if (mtime != NULL) *mtime = mtime1;
            if (bytesRead != NULL) *bytesRead = bytesRead1;
            if (errormsg) *errormsg = errormsg1;

            if (bytesRead1 > 0 && bytesRead1 <= (int32_t)reply.dataAvail()) {
                *buffer = (uint8_t*) malloc(bytesRead1 + 1);
                *buffer[bytesRead1] = 0;
                reply.read(*buffer, bytesRead1);
            } else {
                *buffer = NULL;
            }

            errno = err;
            return (errno == 0) ? 0 : -1;
        }
};

IMPLEMENT_META_INTERFACE(XposedService, "de.robv.android.xposed.IXposedService");

status_t BnXposedService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  {
    switch (code) {
        case TEST_TRANSACTION: {
            CHECK_INTERFACE(IXposedService, data, reply);
            reply->writeNoException();
            reply->writeInt32(test());
            return NO_ERROR;
        } break;

        case ADD_SERVICE_TRANSACTION: {
            CHECK_INTERFACE(IXposedService, data, reply);
            String16 which = data.readString16();
            sp<IBinder> b = data.readStrongBinder();
            bool allowIsolated = (data.readInt32() != 0);
            reply->writeInt32(addService(which, b, allowIsolated));
            return NO_ERROR;
        } break;

        case ACCESS_FILE_TRANSACTION: {
            CHECK_INTERFACE(IXposedService, data, reply);
            String16 filename = data.readString16();
            int32_t mode = data.readInt32();
            status_t result = accessFile(filename, mode);
            int err = errno;
            reply->writeNoException();
            reply->writeInt32(result == 0 ? 0 : err);
            return NO_ERROR;
        } break;

        case STAT_FILE_TRANSACTION: {
            CHECK_INTERFACE(IXposedService, data, reply);
            String16 filename = data.readString16();
            int64_t size, time;
            status_t result = statFile(filename, &size, &time);
            int err = errno;
            reply->writeNoException();
            if (result == 0) {
                reply->writeInt32(0);
                reply->writeInt64(size);
                reply->writeInt64(time);
            } else {
                reply->writeInt32(err);
            }
            return NO_ERROR;
        } break;

        case READ_FILE_TRANSACTION: {
            CHECK_INTERFACE(IXposedService, data, reply);
            String16 filename = data.readString16();
            int32_t offset = data.readInt32();
            int32_t length = data.readInt32();
            int64_t size = data.readInt64();
            int64_t mtime = data.readInt64();
            uint8_t* buffer = NULL;
            int32_t bytesRead = -1;
            String16 errormsg;

            status_t err = readFile(filename, offset, length, &size, &mtime, &buffer, &bytesRead, &errormsg);

            reply->writeNoException();
            reply->writeInt32(err);
            reply->writeString16(errormsg);
            reply->writeInt64(size);
            reply->writeInt64(mtime);
            if (bytesRead > 0) {
                reply->writeInt32(bytesRead);
                reply->write(buffer, bytesRead);
                free(buffer);
            } else {
                reply->writeInt32(bytesRead); // empty array (0) or null (-1)
            }
            return NO_ERROR;
        } break;

        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

class XposedService : public BnXposedService {
    public:
        XposedService(bool system);

        virtual int test() const;
        virtual status_t addService(const String16& name,
                                    const sp<IBinder>& service,
                                    bool allowIsolated = false) const;
        virtual status_t accessFile(const String16& filename16,
                                    int32_t mode) const;
        virtual status_t statFile(const String16& filename,
                                  int64_t* size,
                                  int64_t* mtime) const;
        virtual status_t readFile(const String16& filename16,
                                  int32_t offset,
                                  int32_t length,
                                  int64_t* size,
                                  int64_t* mtime,
                                  uint8_t** buffer,
                                  int32_t* bytesRead,
                                  String16* errormsg) const;

    private:
        bool isSystem;
};

static String16 formatToString16(const char* fmt, ...) {
    char* message;
    va_list args;
    va_start(args, fmt);
    int size = vasprintf(&message, fmt, args);
    String16 result(message, size);
    free(message);
    va_end(args);
    return result;
}

XposedService::XposedService(bool system)
        : isSystem(system) {}

int XposedService::test() const {
    pid_t pid = IPCThreadState::self()->getCallingPid();
    ALOGD("This is PID %d, test method was called from PID %d", getpid(), pid);
    return getpid();
}

status_t XposedService::addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated) const {
    uid_t uid = IPCThreadState::self()->getCallingUid();
    if (!isSystem || (uid != xposed->installer_uid)) {
        ALOGE("Permission denied, not adding service %s", String8(name).string());
        errno = EPERM;
        return -1;
    }
    sp<IServiceManager> sm = defaultServiceManager();
#if PLATFORM_SDK_VERSION >= 16
    return sm->addService(name, service, allowIsolated);
#else
    return sm->addService(name, service);
#endif
}

status_t XposedService::accessFile(const String16& filename16, int32_t mode) const {
    uid_t caller = IPCThreadState::self()->getCallingUid();
    if (caller != UID_SYSTEM) {
        ALOGE("UID %d is not allowed to use the Xposed service", caller);
        errno = EPERM;
        return -1;
    }
    const char* filename = String8(filename16).string();
    return TEMP_FAILURE_RETRY(access(filename, mode));
}

status_t XposedService::statFile(const String16& filename16, int64_t* size, int64_t* time) const {
    uid_t caller = IPCThreadState::self()->getCallingUid();
    if (caller != UID_SYSTEM) {
        ALOGE("UID %d is not allowed to use the Xposed service", caller);
        errno = EPERM;
        return -1;
    }
    const char* filename = String8(filename16).string();
    struct stat st;
    status_t result = TEMP_FAILURE_RETRY(stat(filename, &st));
    if (result == 0) {
        *size = st.st_size;
        *time = st.st_mtime;
    }
    return result;
}

status_t XposedService::readFile(const String16& filename16, int32_t offset, int32_t length,
        int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const {

    uid_t caller = IPCThreadState::self()->getCallingUid();
    if (caller != UID_SYSTEM) {
        ALOGE("UID %d is not allowed to use the Xposed service", caller);
        return EPERM;
    }

    *buffer = NULL;
    *bytesRead = -1;

    // Get file metadata
    const char* filename = String8(filename16).string();
    struct stat st;
    if (stat(filename, &st) != 0) {
        status_t err = errno;
        if (errormsg) *errormsg = formatToString16("%s during stat() on %s", strerror(err), filename);
        return err;
    }

    if (S_ISDIR(st.st_mode)) {
        if (errormsg) *errormsg = formatToString16("%s is a directory", filename);
        return EISDIR;
    }

    // Don't load again if file is unchanged
    if (*size == st.st_size && *mtime == (int32_t)st.st_mtime) {
        return 0;
    }

    *size = st.st_size;
    *mtime = st.st_mtime;

    // Check range
    if (offset > 0 && offset >= *size) {
        if (errormsg) *errormsg = formatToString16("offset %d >= size %" PRId64 " for %s", offset, *size, filename);
        return EINVAL;
    } else if (offset < 0) {
        offset = 0;
    }

    if (length > 0 && (offset + length) > *size) {
        if (errormsg) *errormsg = formatToString16("offset %d + length %d > size %" PRId64 " for %s", offset, length, *size, filename);
        return EINVAL;
    } else if (*size == 0) {
        *bytesRead = 0;
        return 0;
    } else if (length <= 0) {
        length = *size - offset;
    }

    // Allocate buffer
    *buffer = (uint8_t*) malloc(length + 1);
    if (*buffer == NULL) {
        if (errormsg) *errormsg = formatToString16("allocating buffer with %d bytes failed", length + 1);
        return ENOMEM;
    }
    (*buffer)[length] = 0;

    // Open file
    FILE *f = fopen(filename, "r");
    if (f == NULL) {
        status_t err = errno;
        free(*buffer);
        *buffer = NULL;
        if (errormsg) *errormsg = formatToString16("%s during fopen() on %s", strerror(err), filename);
        return err;
    }

    // Seek to correct offset
    if (offset > 0 && fseek(f, offset, SEEK_SET) != 0) {
        free(*buffer);
        *buffer = NULL;
        status_t err = ferror(f);
        fclose(f);
        if (errormsg) *errormsg = formatToString16("%s during fseek() to offset %d for %s", strerror(err), offset, filename);
        return err;
    }

    // Read the file
    *bytesRead = fread(*buffer, 1, length, f);
    status_t err = ferror(f);
    if (err != 0) {
        free(*buffer);
        *buffer = NULL;
        *bytesRead = -1;
        if (errormsg) *errormsg = formatToString16("%s during fread(), read %d bytes for %s", strerror(err), *bytesRead, filename);
    }

    // Close the file
    fclose(f);

    return err;
}

}  // namespace binder


////////////////////////////////////////////////////////////
// General
////////////////////////////////////////////////////////////

static void systemService() {
    xposed::setProcessName("xposed_service_system");
    xposed::dropCapabilities();

#if XPOSED_WITH_SELINUX
    if (xposed->isSELinuxEnabled) {
        if (setcon(ctx_system) != 0) {
            ALOGE("Could not switch to %s context", ctx_system);
            exit(EXIT_FAILURE);
        }
    }
#endif  // XPOSED_WITH_SELINUX

    // Initialize the system service
    sp<IServiceManager> sm(defaultServiceManager());
#if PLATFORM_SDK_VERSION >= 16
    status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true), true);
#else
    status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true));
#endif
    if (err != NO_ERROR) {
        ALOGE("Error %d while adding system service %s", err, XPOSED_BINDER_SYSTEM_SERVICE_NAME);
        exit(EXIT_FAILURE);
    }

    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
#if PLATFORM_SDK_VERSION >= 18
    ps->giveThreadPoolName();
#endif
    IPCThreadState::self()->joinThreadPool();
}

static void appService() {
    xposed::setProcessName("xposed_service_app");
    if (!xposed::switchToXposedInstallerUidGid()) {
        exit(EXIT_FAILURE);
    }
    xposed::dropCapabilities();

#if XPOSED_WITH_SELINUX
    if (xposed->isSELinuxEnabled) {
        if (setcon(ctx_app) != 0) {
            ALOGE("Could not switch to %s context", ctx_app);
            exit(EXIT_FAILURE);
        }
    }
#endif  // XPOSED_WITH_SELINUX

    // We have to register the app service by using the already running system service as a proxy
    sp<IServiceManager> sm(defaultServiceManager());
    sp<IBinder> systemBinder = sm->getService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME));
    sp<binder::IXposedService> xposedSystemService = interface_cast<binder::IXposedService>(systemBinder);
    status_t err = xposedSystemService->addService(String16(XPOSED_BINDER_APP_SERVICE_NAME), new binder::XposedService(false), true);

    // Check result for the app service registration
    if (err != NO_ERROR) {
        ALOGE("Error %d while adding app service %s", err, XPOSED_BINDER_APP_SERVICE_NAME);
        exit(EXIT_FAILURE);
    }

#if XPOSED_WITH_SELINUX
    // Initialize the memory-based Zygote service
    if (xposed->isSELinuxEnabled) {
        pthread_t thMemBased;
        if (pthread_create(&thMemBased, NULL, &membased::looper, NULL) != 0) {
            ALOGE("Could not create thread for memory-based service: %s", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
#endif  // XPOSED_WITH_SELINUX

    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
#if PLATFORM_SDK_VERSION >= 18
    ps->giveThreadPoolName();
#endif
    IPCThreadState::self()->joinThreadPool();
}

bool checkMembasedRunning() {
    // Ensure that the memory based service is running
    if (!membased::waitForRunning(5)) {
        ALOGE("Xposed's Zygote service is not running, cannot work without it");
        return false;
    }

    return true;
}

bool startAll() {
    if (xposed->isSELinuxEnabled && !membased::init()) {
        return false;
    }

    // system context service
    pid_t pid;
    if ((pid = fork()) < 0) {
        ALOGE("Fork for Xposed service in system context failed: %s", strerror(errno));
        return false;
    } else if (pid == 0) {
        systemService();
        // Should never reach this point
        exit(EXIT_FAILURE);
    }

    // app context service
    if ((pid = fork()) < 0) {
        ALOGE("Fork for Xposed service in app context failed: %s", strerror(errno));
        return false;
    } else if (pid == 0) {
        appService();
        // Should never reach this point
        exit(EXIT_FAILURE);
    }

    if (xposed->isSELinuxEnabled && !checkMembasedRunning()) {
        return false;
    }

    return true;
}

#if XPOSED_WITH_SELINUX
bool startMembased() {
    if (!xposed->isSELinuxEnabled) {
        return true;
    }

    if (!membased::init()) {
        return false;
    }

    pid_t pid;
    if ((pid = fork()) < 0) {
        ALOGE("Fork for Xposed Zygote service failed: %s", strerror(errno));
        return false;
    } else if (pid == 0) {
        xposed::setProcessName("xposed_zygote_service");
        if (!xposed::switchToXposedInstallerUidGid()) {
            exit(EXIT_FAILURE);
        }
        xposed::dropCapabilities();
        if (setcon(ctx_app) != 0) {
            ALOGE("Could not switch to %s context", ctx_app);
            exit(EXIT_FAILURE);
        }
        membased::looper(NULL);
        // Should never reach this point
        exit(EXIT_FAILURE);
    }

    return checkMembasedRunning();
}
#endif  // XPOSED_WITH_SELINUX

}  // namespace service
}  // namespace xposed


================================================
FILE: xposed_service.h
================================================
#ifndef XPOSED_SERVICE_H_
#define XPOSED_SERVICE_H_

#include <sys/stat.h>
#include <unistd.h>

namespace xposed {
namespace service {
    bool startAll();

#if XPOSED_WITH_SELINUX
    bool startMembased();

    namespace membased {
        int accessFile(const char* path, int mode);
        int statFile(const char* path, struct stat* stat);
        char* readFile(const char* path, int* bytesRead);
        void restrictMemoryInheritance();
    }  // namespace membased
#endif  // XPOSED_WITH_SELINUX

}  // namespace service

static inline int zygote_access(const char *pathname, int mode) {
#if XPOSED_WITH_SELINUX
    if (xposed->isSELinuxEnabled)
        return xposed::service::membased::accessFile(pathname, mode);
#endif  // XPOSED_WITH_SELINUX

    return access(pathname, mode);
}

}  // namespace xposed

#endif /* XPOSED_SERVICE_H_ */


================================================
FILE: xposed_shared.h
================================================
/**
 * These declarations are needed for both app_process and the libraries.
 */

#ifndef XPOSED_SHARED_H_
#define XPOSED_SHARED_H_

#include <sys/stat.h>

#include "cutils/log.h"
#include "jni.h"

#ifndef ALOG
#define ALOG  LOG
#define ALOGD LOGD
#define ALOGD LOGD
#define ALOGE LOGE
#define ALOGI LOGI
#define ALOGV LOGV
#endif

#if PLATFORM_SDK_VERSION >= 24
#define XPOSED_DIR "/data/user_de/0/de.robv.android.xposed.installer/"
#else
#define XPOSED_DIR "/data/data/de.robv.android.xposed.installer/"
#endif

namespace xposed {

struct XposedShared {
    // Global variables
    bool zygote;
    bool startSystemServer;
    const char* startClassName;
    uint32_t xposedVersionInt;
    bool isSELinuxEnabled;
    bool isSELinuxEnforcing;
    uid_t installer_uid;
    gid_t installer_gid;

    // Provided by runtime-specific library, used by executable
    void (*onVmCreated)(JNIEnv* env);

#if XPOSED_WITH_SELINUX
    // Provided by the executable, used by runtime-specific library
    int (*zygoteservice_accessFile)(const char* path, int mode);
    int (*zygoteservice_statFile)(const char* path, struct stat* st);
    char* (*zygoteservice_readFile)(const char* path, int* bytesRead);
#endif
};

extern XposedShared* xposed;

} // namespace xposed

#endif // XPOSED_SHARED_H_
Download .txt
gitextract_pu6aq7zs/

├── .gitignore
├── ART.mk
├── Android.mk
├── Dalvik.mk
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── app_main.cpp
├── app_main2.cpp
├── fd_utils-inl.h
├── libxposed_art.cpp
├── libxposed_common.cpp
├── libxposed_common.h
├── libxposed_dalvik.cpp
├── libxposed_dalvik.h
├── xposed.cpp
├── xposed.h
├── xposed_logcat.cpp
├── xposed_logcat.h
├── xposed_offsets.h
├── xposed_safemode.cpp
├── xposed_safemode.h
├── xposed_service.cpp
├── xposed_service.h
└── xposed_shared.h
Download .txt
SYMBOL INDEX (157 symbols across 18 files)

FILE: app_main.cpp
  type android (line 31) | namespace android {
    function app_usage (line 33) | void app_usage()
    function _atrace_set_tracing_enabled (line 40) | void _atrace_set_tracing_enabled(bool enabled)
    class AppRuntime (line 58) | class AppRuntime : public AndroidRuntime
      method AppRuntime (line 61) | AppRuntime()
      method onVmCreated (line 83) | virtual void onVmCreated(JNIEnv* env)
      method onStarted (line 115) | virtual void onStarted()
      method onZygoteInit (line 127) | virtual void onZygoteInit()
      method onExit (line 137) | virtual void onExit(int code)
  function setArgv0 (line 162) | static void setArgv0(const char *argv0, const char *newArgv0)
  function main (line 167) | int main(int argc, char* const argv[])

FILE: app_main2.cpp
  type android (line 32) | namespace android {
    function app_usage (line 34) | static void app_usage()
    class AppRuntime (line 41) | class AppRuntime : public AndroidRuntime
      method AppRuntime (line 44) | AppRuntime(char* argBlockStart, const size_t argBlockLength)
      method setClassNameAndArgs (line 50) | void setClassNameAndArgs(const String8& className, int argc, char * ...
      method onVmCreated (line 57) | virtual void onVmCreated(JNIEnv* env)
      method onStarted (line 90) | virtual void onStarted()
      method onZygoteInit (line 102) | virtual void onZygoteInit()
      method onExit (line 114) | virtual void onExit(int code)
  function computeArgBlockSize (line 134) | static size_t computeArgBlockSize(int argc, char* const argv[]) {
  function maybeCreateDalvikCache (line 154) | static void maybeCreateDalvikCache() {
  function runtimeStart (line 202) | static void runtimeStart(AppRuntime& runtime, const char *classname, con...
  function main (line 230) | int main(int argc, char* const argv[])

FILE: fd_utils-inl.h
  function class (line 51) | class FileDescriptorInfo {
  type stat (line 207) | struct stat
  type stat (line 226) | struct stat
  function Readlink (line 249) | static bool Readlink(const int fd, std::string* result) {
  function GetSocketName (line 279) | static bool GetSocketName(const int fd, std::string* result) {
  function ParseFd (line 388) | static int ParseFd(dirent* e, int dir_fd) {

FILE: libxposed_art.cpp
  type xposed (line 38) | namespace xposed {
    function xposedInitLib (line 46) | bool xposedInitLib(XposedShared* shared) {
    function onVmCreated (line 53) | bool onVmCreated(JNIEnv*) {
    function logExceptionStackTrace (line 64) | void logExceptionStackTrace() {
    function XposedBridge_hookMethodNative (line 78) | void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaRe...
    function jobject (line 98) | jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, j...
    function XposedBridge_setObjectClassNative (line 114) | void XposedBridge_setObjectClassNative(JNIEnv* env, jclass, jobject ja...

FILE: libxposed_common.cpp
  type xposed (line 19) | namespace xposed {
    function readIntConfig (line 51) | int readIntConfig(const char* fileName, int defaultValue) {
    function initXposedBridge (line 68) | bool initXposedBridge(JNIEnv* env) {
    function initZygoteService (line 98) | bool initZygoteService(JNIEnv* env) {
    function onVmCreatedCommon (line 132) | void onVmCreatedCommon(JNIEnv* env) {
    function jboolean (line 150) | jboolean XposedBridge_hadInitErrors(JNIEnv*, jclass) {
    function jobject (line 154) | jobject XposedBridge_getStartClassName(JNIEnv* env, jclass) {
    function jboolean (line 158) | jboolean XposedBridge_startsSystemServer(JNIEnv*, jclass) {
    function jint (line 162) | jint XposedBridge_getXposedVersion(JNIEnv*, jclass) {
    function jboolean (line 166) | jboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass) {
    function XResources_rewriteXmlReferencesNative (line 203) | void XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass,
    function jboolean (line 272) | jboolean ZygoteService_checkFileAccess(JNIEnv* env, jclass, jstring fi...
    function jobject (line 281) | jobject ZygoteService_statFile(JNIEnv* env, jclass, jstring filenameJ) {
    function jbyteArray (line 302) | jbyteArray ZygoteService_readFile(JNIEnv* env, jclass, jstring filenam...
    function register_natives_XposedBridge (line 337) | int register_natives_XposedBridge(JNIEnv* env, jclass clazz) {
    function register_natives_XResources (line 363) | int register_natives_XResources(JNIEnv* env, jclass clazz) {
    function register_natives_ZygoteService (line 370) | int register_natives_ZygoteService(JNIEnv* env, jclass clazz) {

FILE: libxposed_common.h
  function namespace (line 12) | namespace xposed {

FILE: libxposed_dalvik.cpp
  type xposed (line 12) | namespace xposed {
    function xposedInitLib (line 37) | bool xposedInitLib(xposed::XposedShared* shared) {
    function onVmCreated (line 44) | bool onVmCreated(JNIEnv* env) {
    function initMemberOffsets (line 79) | bool initMemberOffsets(JNIEnv* env) {
    function setObjectArrayElement (line 125) | inline void setObjectArrayElement(const ArrayObject* obj, int index, O...
    function logExceptionStackTrace (line 132) | void logExceptionStackTrace() {
    function isMethodHooked (line 137) | inline bool isMethodHooked(const Method* method) {
    function hookedMethodCallback (line 146) | void hookedMethodCallback(const u4* args, JValue* pResult, const Metho...
    function XposedBridge_hookMethodNative (line 238) | void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject ...
    function XposedBridge_invokeOriginalMethodNative (line 288) | void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* p...
    function XposedBridge_setObjectClassNative (line 307) | void XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobj...
    function XposedBridge_dumpObjectNative (line 317) | void XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject ...
    function jobject (line 322) | jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, ...
    function XposedBridge_removeFinalFlagNative (line 341) | void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass ja...
    function jint (line 348) | jint XposedBridge_getRuntime(JNIEnv* env, jclass clazz) {

FILE: libxposed_dalvik.h
  function namespace (line 9) | namespace xposed {

FILE: xposed.cpp
  type xposed (line 30) | namespace xposed {
    function handleOptions (line 49) | bool handleOptions(int argc, char* const argv[]) {
    function initialize (line 79) | bool initialize(bool zygote, bool startSystemServer, const char* class...
    function printRomInfo (line 145) | void printRomInfo() {
    function parseXposedProp (line 182) | void parseXposedProp() {
    function getSdkVersion (line 233) | int getSdkVersion() {
    function isDisabled (line 243) | bool isDisabled() {
    function disableXposed (line 252) | void disableXposed() {
    function isSafemodeDisabled (line 261) | bool isSafemodeDisabled() {
    function shouldSkipSafemodeDelay (line 269) | bool shouldSkipSafemodeDelay() {
    function shouldIgnoreCommand (line 277) | bool shouldIgnoreCommand(int argc, const char* const argv[]) {
    function addPathToEnv (line 307) | static bool addPathToEnv(const char* name, const char* path) {
    function addJarToClasspath (line 324) | bool addJarToClasspath() {
    function determineRuntime (line 352) | static bool determineRuntime(const char** xposedLibPath) {
    function onVmCreated (line 386) | void onVmCreated(JNIEnv* env) {
    function setProcessName (line 424) | void setProcessName(const char* name) {
    function determineXposedInstallerUidGid (line 431) | bool determineXposedInstallerUidGid() {
    function switchToXposedInstallerUidGid (line 486) | bool switchToXposedInstallerUidGid() {
    function dropCapabilities (line 499) | void dropCapabilities(int8_t keep[]) {
    function isMinimalFramework (line 525) | bool isMinimalFramework() {

FILE: xposed.h
  function namespace (line 34) | namespace xposed {

FILE: xposed_logcat.cpp
  type xposed (line 19) | namespace xposed {
    type logcat (line 20) | namespace logcat {
      function execLogcat (line 35) | static void execLogcat() {
      function dprintf (line 62) | static inline int dprintf(int fd, const char *format, ...) {
      function runDaemon (line 76) | static void runDaemon(int pipefd) {
      function printStartupMarker (line 122) | void printStartupMarker() {
      function start (line 127) | void start() {

FILE: xposed_logcat.h
  function namespace (line 9) | namespace xposed {

FILE: xposed_offsets.h
  type xposedOffsetModes (line 23) | enum xposedOffsetModes {

FILE: xposed_safemode.cpp
  function vibrate (line 52) | static void vibrate(int count, int duration_ms, int interval_ms) {
  function openKeyDevices (line 88) | static int openKeyDevices(int *fds, int max_fds, int *pressedKey) {
  function getRemainingTime (line 165) | int getRemainingTime(struct timespec expiration) {
  type xposed (line 176) | namespace xposed {
    function detectSafemodeTrigger (line 178) | bool detectSafemodeTrigger(bool skipInitialDelay) {

FILE: xposed_safemode.h
  function namespace (line 4) | namespace xposed {

FILE: xposed_service.cpp
  type xposed (line 26) | namespace xposed {
    type service (line 27) | namespace service {
      type membased (line 40) | namespace membased {
        type State (line 42) | enum State {
        type Action (line 49) | enum Action {
        type AccessFileData (line 56) | struct AccessFileData {
        type StatFileData (line 64) | struct StatFileData {
          type stat (line 68) | struct stat
        type ReadFileData (line 73) | struct ReadFileData {
        type MemBasedState (line 84) | struct MemBasedState {
        function initSharedMutex (line 101) | inline static void initSharedMutex(pthread_mutex_t* mutex) {
        function initSharedCond (line 109) | inline static void initSharedCond(pthread_cond_t* cond) {
        function init (line 117) | static bool init() {
        function restrictMemoryInheritance (line 136) | void restrictMemoryInheritance() {
        function isServiceAccessible (line 141) | static inline bool isServiceAccessible() {
        type AccessFileData (line 163) | struct AccessFileData
        type StatFileData (line 171) | struct StatFileData
          type stat (line 68) | struct stat
        type ReadFileData (line 179) | struct ReadFileData
        type stat (line 180) | struct stat
        function waitForRunning (line 228) | static inline bool waitForRunning(int timeout) {
        function waitForIdle (line 244) | static inline void waitForIdle() {
        function callService (line 251) | static inline void callService(Action action) {
        function makeIdle (line 262) | static inline void makeIdle() {
        function accessFile (line 269) | int accessFile(const char* path, int mode) {
        function statFile (line 291) | int statFile(const char* path, struct stat* st) {
        type ReadFileData (line 331) | struct ReadFileData
      type binder (line 380) | namespace binder {
        class IXposedService (line 385) | class IXposedService: public IInterface {
        class BnXposedService (line 415) | class BnXposedService: public BnInterface<IXposedService> {
        class BpXposedService (line 420) | class BpXposedService: public BpInterface<IXposedService> {
          method BpXposedService (line 422) | BpXposedService(const sp<IBinder>& impl) : BpInterface<IXposedSe...
          method test (line 424) | virtual int test() const {
          method status_t (line 432) | virtual status_t addService(const String16& name, const sp<IBind...
          method status_t (line 443) | virtual status_t accessFile(const String16& name, int32_t mode) ...
          method status_t (line 456) | virtual status_t statFile(const String16& name, int64_t* size, i...
          method status_t (line 474) | virtual status_t readFile(const String16& filename, int32_t offs...
        function status_t (line 516) | status_t BnXposedService::onTransact(uint32_t code, const Parcel& ...
        class XposedService (line 595) | class XposedService : public BnXposedService {
        function String16 (line 621) | static String16 formatToString16(const char* fmt, ...) {
        function status_t (line 641) | status_t XposedService::addService(const String16& name, const sp<...
        function status_t (line 657) | status_t XposedService::accessFile(const String16& filename16, int...
        function status_t (line 668) | status_t XposedService::statFile(const String16& filename16, int64...
        function status_t (line 685) | status_t XposedService::readFile(const String16& filename16, int32...
      function systemService (line 788) | static void systemService() {
      function appService (line 821) | static void appService() {
      function checkMembasedRunning (line 868) | bool checkMembasedRunning() {
      function startAll (line 878) | bool startAll() {
      function startMembased (line 912) | bool startMembased() {

FILE: xposed_service.h
  function namespace (line 7) | namespace xposed {

FILE: xposed_shared.h
  function namespace (line 28) | namespace xposed {
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (161K chars).
[
  {
    "path": ".gitignore",
    "chars": 3,
    "preview": "*~\n"
  },
  {
    "path": "ART.mk",
    "chars": 1341,
    "preview": "##########################################################\n# Library for ART-specific functions\n########################"
  },
  {
    "path": "Android.mk",
    "chars": 1914,
    "preview": "##########################################################\n# Customized app_process executable\n#########################"
  },
  {
    "path": "Dalvik.mk",
    "chars": 966,
    "preview": "##########################################################\n# Library for Dalvik-specific functions\n#####################"
  },
  {
    "path": "MODULE_LICENSE_APACHE2",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "NOTICE",
    "chars": 10769,
    "preview": "\n   Original work Copyright (c) 2005-2008, The Android Open Source Project\n   Modified work Copyright (c) 2013, rovo89 a"
  },
  {
    "path": "app_main.cpp",
    "chars": 7619,
    "preview": "/*\n * Main entry of app process.\n *\n * Starts the interpreted runtime, then starts up the application.\n *\n */\n\n#define L"
  },
  {
    "path": "app_main2.cpp",
    "chars": 12113,
    "preview": "/*\n * Main entry of app process.\n *\n * Starts the interpreted runtime, then starts up the application.\n *\n */\n\n#define L"
  },
  {
    "path": "fd_utils-inl.h",
    "chars": 12660,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "libxposed_art.cpp",
    "chars": 7227,
    "preview": "/**\n * This file includes functions specific to the ART runtime.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"xposed_shared."
  },
  {
    "path": "libxposed_common.cpp",
    "chars": 13932,
    "preview": "/**\n * This file includes functions shared by different runtimes.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"libxposed_com"
  },
  {
    "path": "libxposed_common.h",
    "chars": 2814,
    "preview": "#ifndef LIBXPOSED_COMMON_H_\n#define LIBXPOSED_COMMON_H_\n\n#include \"xposed_shared.h\"\n\n#ifndef NATIVE_METHOD\n#define NATIV"
  },
  {
    "path": "libxposed_dalvik.cpp",
    "chars": 12616,
    "preview": "/**\n * This file includes functions specific to the Dalvik runtime.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"libxposed_d"
  },
  {
    "path": "libxposed_dalvik.h",
    "chars": 609,
    "preview": "#ifndef LIBXPOSED_DALVIK_H_\n#define LIBXPOSED_DALVIK_H_\n\n#define ANDROID_SMP 1\n#include \"Dalvik.h\"\n\n#include \"libxposed_"
  },
  {
    "path": "xposed.cpp",
    "chars": 16623,
    "preview": "/**\n * This file includes functions called directly from app_main.cpp during startup.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#in"
  },
  {
    "path": "xposed.h",
    "chars": 1999,
    "preview": "#ifndef XPOSED_H_\n#define XPOSED_H_\n\n#include \"xposed_shared.h\"\n\n#define XPOSED_PROP_FILE \"/system/xposed.prop\"\n\n#if def"
  },
  {
    "path": "xposed_logcat.cpp",
    "chars": 5637,
    "preview": "/**\n * This file includes the Xposed service, which is especially used to work around SELinux restrictions.\n */\n\n#define"
  },
  {
    "path": "xposed_logcat.h",
    "chars": 416,
    "preview": "#ifndef XPOSED_LOGCAT_H_\n#define XPOSED_LOGCAT_H_\n\n#define XPOSEDLOG            XPOSED_DIR \"log/error.log\"\n#define XPOSE"
  },
  {
    "path": "xposed_offsets.h",
    "chars": 1763,
    "preview": "/*\n    Certain compile time parameters result in different offsets\n    for members in structures. This file defines the "
  },
  {
    "path": "xposed_safemode.cpp",
    "chars": 11839,
    "preview": "/*\n * Detects input combinations for recovering from bootloops.\n *\n * The safemode trigger is detected if exactly one of"
  },
  {
    "path": "xposed_safemode.h",
    "chars": 161,
    "preview": "#ifndef XPOSED_SAFEMODE_H_\n#define XPOSED_SAFEMODE_H_\n\nnamespace xposed {\n\nbool detectSafemodeTrigger(bool skipInitialDe"
  },
  {
    "path": "xposed_service.cpp",
    "chars": 28801,
    "preview": "/**\n * This file includes the Xposed services, which are especially used to work around SELinux restrictions.\n */\n\n#defi"
  },
  {
    "path": "xposed_service.h",
    "chars": 849,
    "preview": "#ifndef XPOSED_SERVICE_H_\n#define XPOSED_SERVICE_H_\n\n#include <sys/stat.h>\n#include <unistd.h>\n\nnamespace xposed {\nnames"
  },
  {
    "path": "xposed_shared.h",
    "chars": 1287,
    "preview": "/**\n * These declarations are needed for both app_process and the libraries.\n */\n\n#ifndef XPOSED_SHARED_H_\n#define XPOSE"
  }
]

About this extraction

This page contains the full source code of the rovo89/Xposed GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (150.3 KB), approximately 38.2k tokens, and a symbol index with 157 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!