[
  {
    "path": ".gitignore",
    "content": "*~\n"
  },
  {
    "path": "ART.mk",
    "content": "##########################################################\n# Library for ART-specific functions\n##########################################################\n\ninclude $(CLEAR_VARS)\n\ninclude art/build/Android.common_build.mk\n$(eval $(call set-target-local-clang-vars))\n$(eval $(call set-target-local-cflags-vars,ndebug))\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 23)))\n  LOCAL_C_INCLUDES += \\\n    external/valgrind \\\n    external/valgrind/include\nelse\n  include external/libcxx/libcxx.mk\n  LOCAL_C_INCLUDES += \\\n    external/valgrind/main \\\n    external/valgrind/main/include\nendif\n\nLOCAL_SRC_FILES += \\\n  libxposed_common.cpp \\\n  libxposed_art.cpp\n\nLOCAL_C_INCLUDES += \\\n  art/runtime \\\n  external/gtest/include\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 24)))\n  LOCAL_C_INCLUDES += bionic/libc/private\nendif\n\nLOCAL_SHARED_LIBRARIES += \\\n  libart \\\n  liblog \\\n  libcutils \\\n  libandroidfw \\\n  libnativehelper\n\nLOCAL_CFLAGS += \\\n  -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) \\\n  -DXPOSED_WITH_SELINUX=1\n\nLOCAL_MODULE := libxposed_art\nLOCAL_MODULE_TAGS := optional\nLOCAL_STRIP_MODULE := keep_symbols\nLOCAL_MULTILIB := both\n\n# Always build both architectures (if applicable)\nifeq ($(TARGET_IS_64_BIT),true)\n  $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX)\nendif\n\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "Android.mk",
    "content": "##########################################################\n# Customized app_process executable\n##########################################################\n\nLOCAL_PATH:= $(call my-dir)\ninclude $(CLEAR_VARS)\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 21)))\n  LOCAL_SRC_FILES := app_main2.cpp\n  LOCAL_MULTILIB := both\n  LOCAL_MODULE_STEM_32 := app_process32_xposed\n  LOCAL_MODULE_STEM_64 := app_process64_xposed\nelse\n  LOCAL_SRC_FILES := app_main.cpp\n  LOCAL_MODULE_STEM := app_process_xposed\nendif\n\nLOCAL_SRC_FILES += \\\n  xposed.cpp \\\n  xposed_logcat.cpp \\\n  xposed_service.cpp \\\n  xposed_safemode.cpp\n\nLOCAL_SHARED_LIBRARIES := \\\n  libcutils \\\n  libutils \\\n  liblog \\\n  libbinder \\\n  libandroid_runtime \\\n  libdl\n\nLOCAL_CFLAGS += -Wall -Werror -Wextra -Wunused\nLOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 17)))\n  LOCAL_SHARED_LIBRARIES += libselinux\n  LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1\nendif\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 22)))\n  LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain\n  LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic\nendif\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 23)))\n  LOCAL_SHARED_LIBRARIES += libwilhelm\nendif\n\nLOCAL_MODULE := xposed\nLOCAL_MODULE_TAGS := optional\nLOCAL_STRIP_MODULE := keep_symbols\n\n# Always build both architectures (if applicable)\nifeq ($(TARGET_IS_64_BIT),true)\n  $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX)\nendif\n\ninclude $(BUILD_EXECUTABLE)\n\n##########################################################\n# Library for Dalvik-/ART-specific functions\n##########################################################\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 21)))\n  include frameworks/base/cmds/xposed/ART.mk\nelse\n  include frameworks/base/cmds/xposed/Dalvik.mk\nendif\n"
  },
  {
    "path": "Dalvik.mk",
    "content": "##########################################################\n# Library for Dalvik-specific functions\n##########################################################\n\ninclude $(CLEAR_VARS)\n\nLOCAL_SRC_FILES := \\\n  libxposed_common.cpp \\\n  libxposed_dalvik.cpp\n\nLOCAL_C_INCLUDES += \\\n  dalvik \\\n  dalvik/vm \\\n  external/stlport/stlport \\\n  bionic \\\n  bionic/libstdc++/include \\\n  libcore/include\n\nLOCAL_SHARED_LIBRARIES := \\\n  libdvm \\\n  liblog \\\n  libdl \\\n  libnativehelper\n\nifeq ($(PLATFORM_SDK_VERSION),15)\n  LOCAL_SHARED_LIBRARIES += libutils\nelse\n  LOCAL_SHARED_LIBRARIES += libandroidfw\nendif\n\nLOCAL_CFLAGS := -Wall -Werror -Wextra -Wunused -Wno-unused-parameter\nLOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)\n\nifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \\>= 17)))\n  LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1\nendif\n\nLOCAL_MODULE := libxposed_dalvik\nLOCAL_MODULE_TAGS := optional\nLOCAL_STRIP_MODULE := keep_symbols\n\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "NOTICE",
    "content": "\n   Original work Copyright (c) 2005-2008, The Android Open Source Project\n   Modified work Copyright (c) 2013, rovo89 and Tungstwenty\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "app_main.cpp",
    "content": "/*\n * Main entry of app process.\n *\n * Starts the interpreted runtime, then starts up the application.\n *\n */\n\n#define LOG_TAG \"appproc\"\n\n#include <cutils/properties.h>\n#include <binder/IPCThreadState.h>\n#include <binder/ProcessState.h>\n#include <utils/Log.h>\n#include <cutils/process_name.h>\n#include <cutils/memory.h>\n#include <android_runtime/AndroidRuntime.h>\n\n#if PLATFORM_SDK_VERSION >= 16\n#include <sys/personality.h>\n#endif\n\n#include <stdio.h>\n#include <unistd.h>\n\n#include \"xposed.h\"\n#include <dlfcn.h>\n\nstatic bool isXposedLoaded = false;\n\n\nnamespace android {\n\nvoid app_usage()\n{\n    fprintf(stderr,\n        \"Usage: app_process [java-options] cmd-dir start-class-name [options]\\n\");\n    fprintf(stderr, \"   with Xposed support\\n\");\n}\n\nvoid _atrace_set_tracing_enabled(bool enabled)\n{\n    if (xposed::getSdkVersion() < 18)\n        return;\n\n    dlerror(); // Clear existing errors\n\n    void (*PTR_atrace_set_tracing_enabled)(bool);\n    *(void **) (&PTR_atrace_set_tracing_enabled) = dlsym(RTLD_DEFAULT, \"atrace_set_tracing_enabled\");\n\n    const char *error;\n    if ((error = dlerror()) != NULL) {\n        ALOGE(\"Could not find address for function atrace_set_tracing_enabled: %s\", error);\n    } else {\n        PTR_atrace_set_tracing_enabled(enabled);\n    }\n}\n\nclass AppRuntime : public AndroidRuntime\n{\npublic:\n    AppRuntime()\n        : mParentDir(NULL)\n        , mClassName(NULL)\n        , mClass(NULL)\n        , mArgC(0)\n        , mArgV(NULL)\n    {\n    }\n\n#if 0\n    // this appears to be unused\n    const char* getParentDir() const\n    {\n        return mParentDir;\n    }\n#endif\n\n    const char* getClassName() const\n    {\n        return mClassName;\n    }\n\n    virtual void onVmCreated(JNIEnv* env)\n    {\n        if (isXposedLoaded)\n            xposed::onVmCreated(env);\n\n        if (mClassName == NULL) {\n            return; // Zygote. Nothing to do here.\n        }\n\n        /*\n         * This is a little awkward because the JNI FindClass call uses the\n         * class loader associated with the native method we're executing in.\n         * If called in onStarted (from RuntimeInit.finishInit because we're\n         * launching \"am\", for example), FindClass would see that we're calling\n         * from a boot class' native method, and so wouldn't look for the class\n         * we're trying to look up in CLASSPATH. Unfortunately it needs to,\n         * because the \"am\" classes are not boot classes.\n         *\n         * The easiest fix is to call FindClass here, early on before we start\n         * executing boot class Java code and thereby deny ourselves access to\n         * non-boot classes.\n         */\n        char* slashClassName = toSlashClassName(mClassName);\n        mClass = env->FindClass(slashClassName);\n        if (mClass == NULL) {\n            ALOGE(\"ERROR: could not find class '%s'\\n\", mClassName);\n        }\n        free(slashClassName);\n\n        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));\n    }\n\n    virtual void onStarted()\n    {\n        sp<ProcessState> proc = ProcessState::self();\n        ALOGV(\"App process: starting thread pool.\\n\");\n        proc->startThreadPool();\n\n        AndroidRuntime* ar = AndroidRuntime::getRuntime();\n        ar->callMain(mClassName, mClass, mArgC, mArgV);\n\n        IPCThreadState::self()->stopProcess();\n    }\n\n    virtual void onZygoteInit()\n    {\n        // Re-enable tracing now that we're no longer in Zygote.\n        _atrace_set_tracing_enabled(true);\n\n        sp<ProcessState> proc = ProcessState::self();\n        ALOGV(\"App process: starting thread pool.\\n\");\n        proc->startThreadPool();\n    }\n\n    virtual void onExit(int code)\n    {\n        if (mClassName == NULL) {\n            // if zygote\n            IPCThreadState::self()->stopProcess();\n        }\n\n        AndroidRuntime::onExit(code);\n    }\n\n\n    const char* mParentDir;\n    const char* mClassName;\n    jclass mClass;\n    int mArgC;\n    const char* const* mArgV;\n};\n\n}\n\nusing namespace android;\n\n/*\n * sets argv0 to as much of newArgv0 as will fit\n */\nstatic void setArgv0(const char *argv0, const char *newArgv0)\n{\n    strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0));\n}\n\nint main(int argc, char* const argv[])\n{\n    if (xposed::handleOptions(argc, argv))\n        return 0;\n\n#if PLATFORM_SDK_VERSION >= 16\n#ifdef __arm__\n    /*\n     * b/7188322 - Temporarily revert to the compat memory layout\n     * to avoid breaking third party apps.\n     *\n     * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.\n     *\n     * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466\n     * changes the kernel mapping from bottom up to top-down.\n     * This breaks some programs which improperly embed\n     * an out of date copy of Android's linker.\n     */\n    char value[PROPERTY_VALUE_MAX];\n    property_get(\"ro.kernel.qemu\", value, \"\");\n    bool is_qemu = (strcmp(value, \"1\") == 0);\n    if ((getenv(\"NO_ADDR_COMPAT_LAYOUT_FIXUP\") == NULL) && !is_qemu) {\n        int current = personality(0xFFFFFFFF);\n        if ((current & ADDR_COMPAT_LAYOUT) == 0) {\n            personality(current | ADDR_COMPAT_LAYOUT);\n            setenv(\"NO_ADDR_COMPAT_LAYOUT_FIXUP\", \"1\", 1);\n            execv(\"/system/bin/app_process\", argv);\n            return -1;\n        }\n    }\n    unsetenv(\"NO_ADDR_COMPAT_LAYOUT_FIXUP\");\n#endif\n#endif\n\n    // These are global variables in ProcessState.cpp\n    mArgC = argc;\n    mArgV = argv;\n\n    mArgLen = 0;\n    for (int i=0; i<argc; i++) {\n        mArgLen += strlen(argv[i]) + 1;\n    }\n    mArgLen--;\n\n    AppRuntime runtime;\n    const char* argv0 = argv[0];\n\n    // Process command line arguments\n    // ignore argv[0]\n    argc--;\n    argv++;\n\n    // Everything up to '--' or first non '-' arg goes to the vm\n\n    int i = runtime.addVmArguments(argc, argv);\n\n    // Parse runtime arguments.  Stop at first unrecognized option.\n    bool zygote = false;\n    bool startSystemServer = false;\n    bool application = false;\n    const char* parentDir = NULL;\n    const char* niceName = NULL;\n    const char* className = NULL;\n    while (i < argc) {\n        const char* arg = argv[i++];\n        if (!parentDir) {\n            parentDir = arg;\n        } else if (strcmp(arg, \"--zygote\") == 0) {\n            zygote = true;\n            niceName = \"zygote\";\n        } else if (strcmp(arg, \"--start-system-server\") == 0) {\n            startSystemServer = true;\n        } else if (strcmp(arg, \"--application\") == 0) {\n            application = true;\n        } else if (strncmp(arg, \"--nice-name=\", 12) == 0) {\n            niceName = arg + 12;\n        } else {\n            className = arg;\n            break;\n        }\n    }\n\n    if (niceName && *niceName) {\n        setArgv0(argv0, niceName);\n        set_process_name(niceName);\n    }\n\n    runtime.mParentDir = parentDir;\n\n    isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);\n    if (zygote) {\n        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : \"com.android.internal.os.ZygoteInit\",\n                startSystemServer ? \"start-system-server\" : \"\");\n    } else if (className) {\n        // Remainder of args get passed to startup class main()\n        runtime.mClassName = className;\n        runtime.mArgC = argc - i;\n        runtime.mArgV = argv + i;\n        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : \"com.android.internal.os.RuntimeInit\",\n                application ? \"application\" : \"tool\");\n    } else {\n        fprintf(stderr, \"Error: no class name or --zygote supplied.\\n\");\n        app_usage();\n        LOG_ALWAYS_FATAL(\"app_process: no class name or --zygote supplied.\");\n        return 10;\n    }\n}\n"
  },
  {
    "path": "app_main2.cpp",
    "content": "/*\n * Main entry of app process.\n *\n * Starts the interpreted runtime, then starts up the application.\n *\n */\n\n#define LOG_TAG \"appproc\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/prctl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <binder/IPCThreadState.h>\n#include <binder/ProcessState.h>\n#include <utils/Log.h>\n#include <cutils/memory.h>\n#include <cutils/process_name.h>\n#include <cutils/properties.h>\n#include <cutils/trace.h>\n#include <android_runtime/AndroidRuntime.h>\n#include <private/android_filesystem_config.h>  // for AID_SYSTEM\n\n#include \"xposed.h\"\n#include <dlfcn.h>\n\nstatic bool isXposedLoaded = false;\n\n\nnamespace android {\n\nstatic void app_usage()\n{\n    fprintf(stderr,\n        \"Usage: app_process [java-options] cmd-dir start-class-name [options]\\n\");\n    fprintf(stderr, \"   with Xposed support\\n\");\n}\n\nclass AppRuntime : public AndroidRuntime\n{\npublic:\n    AppRuntime(char* argBlockStart, const size_t argBlockLength)\n        : AndroidRuntime(argBlockStart, argBlockLength)\n        , mClass(NULL)\n    {\n    }\n\n    void setClassNameAndArgs(const String8& className, int argc, char * const *argv) {\n        mClassName = className;\n        for (int i = 0; i < argc; ++i) {\n             mArgs.add(String8(argv[i]));\n        }\n    }\n\n    virtual void onVmCreated(JNIEnv* env)\n    {\n        if (isXposedLoaded)\n            xposed::onVmCreated(env);\n\n        if (mClassName.isEmpty()) {\n            return; // Zygote. Nothing to do here.\n        }\n\n        /*\n         * This is a little awkward because the JNI FindClass call uses the\n         * class loader associated with the native method we're executing in.\n         * If called in onStarted (from RuntimeInit.finishInit because we're\n         * launching \"am\", for example), FindClass would see that we're calling\n         * from a boot class' native method, and so wouldn't look for the class\n         * we're trying to look up in CLASSPATH. Unfortunately it needs to,\n         * because the \"am\" classes are not boot classes.\n         *\n         * The easiest fix is to call FindClass here, early on before we start\n         * executing boot class Java code and thereby deny ourselves access to\n         * non-boot classes.\n         */\n        char* slashClassName = toSlashClassName(mClassName.string());\n        mClass = env->FindClass(slashClassName);\n        if (mClass == NULL) {\n            ALOGE(\"ERROR: could not find class '%s'\\n\", mClassName.string());\n            env->ExceptionDescribe();\n        }\n        free(slashClassName);\n\n        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));\n    }\n\n    virtual void onStarted()\n    {\n        sp<ProcessState> proc = ProcessState::self();\n        ALOGV(\"App process: starting thread pool.\\n\");\n        proc->startThreadPool();\n\n        AndroidRuntime* ar = AndroidRuntime::getRuntime();\n        ar->callMain(mClassName, mClass, mArgs);\n\n        IPCThreadState::self()->stopProcess();\n    }\n\n    virtual void onZygoteInit()\n    {\n#if PLATFORM_SDK_VERSION <= 22\n        // Re-enable tracing now that we're no longer in Zygote.\n        atrace_set_tracing_enabled(true);\n#endif\n\n        sp<ProcessState> proc = ProcessState::self();\n        ALOGV(\"App process: starting thread pool.\\n\");\n        proc->startThreadPool();\n    }\n\n    virtual void onExit(int code)\n    {\n        if (mClassName.isEmpty()) {\n            // if zygote\n            IPCThreadState::self()->stopProcess();\n        }\n\n        AndroidRuntime::onExit(code);\n    }\n\n\n    String8 mClassName;\n    Vector<String8> mArgs;\n    jclass mClass;\n};\n\n}\n\nusing namespace android;\n\nstatic size_t computeArgBlockSize(int argc, char* const argv[]) {\n    // TODO: This assumes that all arguments are allocated in\n    // contiguous memory. There isn't any documented guarantee\n    // that this is the case, but this is how the kernel does it\n    // (see fs/exec.c).\n    //\n    // Also note that this is a constant for \"normal\" android apps.\n    // Since they're forked from zygote, the size of their command line\n    // is the size of the zygote command line.\n    //\n    // We change the process name of the process by over-writing\n    // the start of the argument block (argv[0]) with the new name of\n    // the process, so we'd mysteriously start getting truncated process\n    // names if the zygote command line decreases in size.\n    uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);\n    uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);\n    end += strlen(argv[argc - 1]) + 1;\n    return (end - start);\n}\n\nstatic void maybeCreateDalvikCache() {\n#if defined(__aarch64__)\n    static const char kInstructionSet[] = \"arm64\";\n#elif defined(__x86_64__)\n    static const char kInstructionSet[] = \"x86_64\";\n#elif defined(__arm__)\n    static const char kInstructionSet[] = \"arm\";\n#elif defined(__i386__)\n    static const char kInstructionSet[] = \"x86\";\n#elif defined (__mips__) && !defined(__LP64__)\n    static const char kInstructionSet[] = \"mips\";\n#elif defined (__mips__) && defined(__LP64__)\n    static const char kInstructionSet[] = \"mips64\";\n#else\n#error \"Unknown instruction set\"\n#endif\n    const char* androidRoot = getenv(\"ANDROID_DATA\");\n    LOG_ALWAYS_FATAL_IF(androidRoot == NULL, \"ANDROID_DATA environment variable unset\");\n\n    char dalvikCacheDir[PATH_MAX];\n    const int numChars = snprintf(dalvikCacheDir, PATH_MAX,\n            \"%s/dalvik-cache/%s\", androidRoot, kInstructionSet);\n    LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),\n            \"Error constructing dalvik cache : %s\", strerror(errno));\n\n    int result = mkdir(dalvikCacheDir, 0711);\n    LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),\n            \"Error creating cache dir %s : %s\", dalvikCacheDir, strerror(errno));\n\n    // We always perform these steps because the directory might\n    // already exist, with wider permissions and a different owner\n    // than we'd like.\n    result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);\n    LOG_ALWAYS_FATAL_IF((result < 0), \"Error changing dalvik-cache ownership : %s\", strerror(errno));\n\n    result = chmod(dalvikCacheDir, 0711);\n    LOG_ALWAYS_FATAL_IF((result < 0),\n            \"Error changing dalvik-cache permissions : %s\", strerror(errno));\n}\n\n#if defined(__LP64__)\nstatic const char ABI_LIST_PROPERTY[] = \"ro.product.cpu.abilist64\";\nstatic const char ZYGOTE_NICE_NAME[] = \"zygote64\";\n#else\nstatic const char ABI_LIST_PROPERTY[] = \"ro.product.cpu.abilist32\";\nstatic const char ZYGOTE_NICE_NAME[] = \"zygote\";\n#endif\n\nstatic void runtimeStart(AppRuntime& runtime, const char *classname, const Vector<String8>& options, bool zygote)\n{\n#if PLATFORM_SDK_VERSION >= 23\n  runtime.start(classname, options, zygote);\n#else\n  // try newer variant (5.1.1_r19 and later) first\n  void (*ptr1)(AppRuntime&, const char*, const Vector<String8>&, bool);\n  *(void **) (&ptr1) = dlsym(RTLD_DEFAULT, \"_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEEb\");\n\n  if (ptr1 != NULL) {\n    ptr1(runtime, classname, options, zygote);\n    return;\n  }\n\n  // fall back to older variant\n  void (*ptr2)(AppRuntime&, const char*, const Vector<String8>&);\n  *(void **) (&ptr2) = dlsym(RTLD_DEFAULT, \"_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEE\");\n\n  if (ptr2 != NULL) {\n    ptr2(runtime, classname, options);\n    return;\n  }\n\n  // should not happen\n  LOG_ALWAYS_FATAL(\"app_process: could not locate AndroidRuntime::start() method.\");\n#endif\n}\n\nint main(int argc, char* const argv[])\n{\n    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {\n        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return\n        // EINVAL. Don't die on such kernels.\n        if (errno != EINVAL) {\n            LOG_ALWAYS_FATAL(\"PR_SET_NO_NEW_PRIVS failed: %s\", strerror(errno));\n            return 12;\n        }\n    }\n\n    if (xposed::handleOptions(argc, argv)) {\n        return 0;\n    }\n\n    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));\n    // Process command line arguments\n    // ignore argv[0]\n    argc--;\n    argv++;\n\n    // Everything up to '--' or first non '-' arg goes to the vm.\n    //\n    // The first argument after the VM args is the \"parent dir\", which\n    // is currently unused.\n    //\n    // After the parent dir, we expect one or more the following internal\n    // arguments :\n    //\n    // --zygote : Start in zygote mode\n    // --start-system-server : Start the system server.\n    // --application : Start in application (stand alone, non zygote) mode.\n    // --nice-name : The nice name for this process.\n    //\n    // For non zygote starts, these arguments will be followed by\n    // the main class name. All remaining arguments are passed to\n    // the main method of this class.\n    //\n    // For zygote starts, all remaining arguments are passed to the zygote.\n    // main function.\n    //\n    // Note that we must copy argument string values since we will rewrite the\n    // entire argument block when we apply the nice name to argv0.\n\n    int i;\n    for (i = 0; i < argc; i++) {\n        if (argv[i][0] != '-') {\n            break;\n        }\n        if (argv[i][1] == '-' && argv[i][2] == 0) {\n            ++i; // Skip --.\n            break;\n        }\n        runtime.addOption(strdup(argv[i]));\n    }\n\n    // Parse runtime arguments.  Stop at first unrecognized option.\n    bool zygote = false;\n    bool startSystemServer = false;\n    bool application = false;\n    String8 niceName;\n    String8 className;\n\n    ++i;  // Skip unused \"parent dir\" argument.\n    while (i < argc) {\n        const char* arg = argv[i++];\n        if (strcmp(arg, \"--zygote\") == 0) {\n            zygote = true;\n            niceName = ZYGOTE_NICE_NAME;\n        } else if (strcmp(arg, \"--start-system-server\") == 0) {\n            startSystemServer = true;\n        } else if (strcmp(arg, \"--application\") == 0) {\n            application = true;\n        } else if (strncmp(arg, \"--nice-name=\", 12) == 0) {\n            niceName.setTo(arg + 12);\n        } else if (strncmp(arg, \"--\", 2) != 0) {\n            className.setTo(arg);\n            break;\n        } else {\n            --i;\n            break;\n        }\n    }\n\n    Vector<String8> args;\n    if (!className.isEmpty()) {\n        // We're not in zygote mode, the only argument we need to pass\n        // to RuntimeInit is the application argument.\n        //\n        // The Remainder of args get passed to startup class main(). Make\n        // copies of them before we overwrite them with the process name.\n        args.add(application ? String8(\"application\") : String8(\"tool\"));\n        runtime.setClassNameAndArgs(className, argc - i, argv + i);\n    } else {\n        // We're in zygote mode.\n        maybeCreateDalvikCache();\n\n        if (startSystemServer) {\n            args.add(String8(\"start-system-server\"));\n        }\n\n        char prop[PROP_VALUE_MAX];\n        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {\n            LOG_ALWAYS_FATAL(\"app_process: Unable to determine ABI list from property %s.\",\n                ABI_LIST_PROPERTY);\n            return 11;\n        }\n\n        String8 abiFlag(\"--abi-list=\");\n        abiFlag.append(prop);\n        args.add(abiFlag);\n\n        // In zygote mode, pass all remaining arguments to the zygote\n        // main() method.\n        for (; i < argc; ++i) {\n            args.add(String8(argv[i]));\n        }\n    }\n\n    if (!niceName.isEmpty()) {\n        runtime.setArgv0(niceName.string());\n        set_process_name(niceName.string());\n    }\n\n    if (zygote) {\n        isXposedLoaded = xposed::initialize(true, startSystemServer, NULL, argc, argv);\n        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : \"com.android.internal.os.ZygoteInit\", args, zygote);\n    } else if (className) {\n        isXposedLoaded = xposed::initialize(false, false, className, argc, argv);\n        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : \"com.android.internal.os.RuntimeInit\", args, zygote);\n    } else {\n        fprintf(stderr, \"Error: no class name or --zygote supplied.\\n\");\n        app_usage();\n        LOG_ALWAYS_FATAL(\"app_process: no class name or --zygote supplied.\");\n        return 10;\n    }\n}\n"
  },
  {
    "path": "fd_utils-inl.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <unordered_map>\n#include <set>\n#include <vector>\n#include <algorithm>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <grp.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include <cutils/log.h>\n#include \"JNIHelp.h\"\n#include \"ScopedPrimitiveArray.h\"\n\nstatic const char* kPathPrefixWhitelist[] = {\n  \"/data/app/\",\n  \"/data/app-private/\",\n  \"/system/app/\",\n  \"/system/priv-app/\",\n  \"/vendor/app/\",\n  \"/vendor/priv-app/\",\n};\n\nstatic const char* kFdPath = \"/proc/self/fd\";\n\n// Keeps track of all relevant information (flags, offset etc.) of an\n// open zygote file descriptor.\nclass FileDescriptorInfo {\n public:\n  // Create a FileDescriptorInfo for a given file descriptor. Returns\n  // |NULL| if an error occurred.\n  static FileDescriptorInfo* createFromFd(int fd) {\n    struct stat f_stat;\n    // This should never happen; the zygote should always have the right set\n    // of permissions required to stat all its open files.\n    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {\n      ALOGE(\"Unable to stat fd %d : %s\", fd, strerror(errno));\n      return NULL;\n    }\n\n    if (S_ISSOCK(f_stat.st_mode)) {\n      std::string socket_name;\n      if (!GetSocketName(fd, &socket_name)) {\n        return NULL;\n      }\n\n      if (!IsWhitelisted(socket_name)) {\n        //ALOGE(\"Socket name not whitelisted : %s (fd=%d)\", socket_name.c_str(), fd);\n        return NULL;\n      }\n\n      return new FileDescriptorInfo(fd);\n    }\n\n    std::string file_path;\n    if (!Readlink(fd, &file_path)) {\n      return NULL;\n    }\n\n    if (!IsWhitelisted(file_path)) {\n      //ALOGE(\"Not whitelisted : %s\", file_path.c_str());\n      return NULL;\n    }\n\n    // We only handle whitelisted regular files and character devices. Whitelisted\n    // character devices must provide a guarantee of sensible behaviour when\n    // reopened.\n    //\n    // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).\n    // S_ISLINK : Not supported.\n    // S_ISBLK : Not supported.\n    // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate\n    // with the child process across forks but those should have been closed\n    // before we got to this point.\n    if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {\n      ALOGE(\"Unsupported st_mode %d\", f_stat.st_mode);\n      return NULL;\n    }\n\n    // File descriptor flags : currently on FD_CLOEXEC. We can set these\n    // using F_SETFD - we're single threaded at this point of execution so\n    // there won't be any races.\n    const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));\n    if (fd_flags == -1) {\n      ALOGE(\"Failed fcntl(%d, F_GETFD) : %s\", fd, strerror(errno));\n      return NULL;\n    }\n\n    // File status flags :\n    // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through\n    //   to the open() call.\n    //\n    // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can\n    //   do about these, since the file has already been created. We shall ignore\n    //   them here.\n    //\n    // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL\n    //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.\n    //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for\n    //   their presence and pass them in to open().\n    int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));\n    if (fs_flags == -1) {\n      ALOGE(\"Failed fcntl(%d, F_GETFL) : %s\", fd, strerror(errno));\n      return NULL;\n    }\n\n    // File offset : Ignore the offset for non seekable files.\n    const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));\n\n    // We pass the flags that open accepts to open, and use F_SETFL for\n    // the rest of them.\n    static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);\n    int open_flags = fs_flags & (kOpenFlags);\n    fs_flags = fs_flags & (~(kOpenFlags));\n\n    return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);\n  }\n\n  bool Detach() const {\n    const int dev_null_fd = open(\"/dev/null\", O_RDWR);\n    if (dev_null_fd < 0) {\n      ALOGE(\"Failed to open /dev/null : %s\", strerror(errno));\n      return false;\n    }\n\n    if (dup2(dev_null_fd, fd) == -1) {\n      ALOGE(\"Failed dup2 on socket descriptor %d : %s\", fd, strerror(errno));\n      return false;\n    }\n\n    if (close(dev_null_fd) == -1) {\n      ALOGE(\"Failed close(%d) : %s\", dev_null_fd, strerror(errno));\n      return false;\n    }\n\n    return true;\n  }\n\n  bool Reopen() const {\n    if (is_sock) {\n      return true;\n    }\n\n    // NOTE: This might happen if the file was unlinked after being opened.\n    // It's a common pattern in the case of temporary files and the like but\n    // we should not allow such usage from the zygote.\n    const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));\n\n    if (new_fd == -1) {\n      ALOGE(\"Failed open(%s, %d) : %s\", file_path.c_str(), open_flags, strerror(errno));\n      return false;\n    }\n\n    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {\n      close(new_fd);\n      ALOGE(\"Failed fcntl(%d, F_SETFD, %x) : %s\", new_fd, fd_flags, strerror(errno));\n      return false;\n    }\n\n    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {\n      close(new_fd);\n      ALOGE(\"Failed fcntl(%d, F_SETFL, %x) : %s\", new_fd, fs_flags, strerror(errno));\n      return false;\n    }\n\n    if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {\n      close(new_fd);\n      ALOGE(\"Failed lseek64(%d, SEEK_SET) : %s\", new_fd, strerror(errno));\n      return false;\n    }\n\n    if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {\n      close(new_fd);\n      ALOGE(\"Failed dup2(%d, %d) : %s\", fd, new_fd, strerror(errno));\n      return false;\n    }\n\n    close(new_fd);\n\n    return true;\n  }\n\n  const int fd;\n  const struct stat stat;\n  const std::string file_path;\n  const int open_flags;\n  const int fd_flags;\n  const int fs_flags;\n  const off_t offset;\n  const bool is_sock;\n\n private:\n  FileDescriptorInfo(int pfd) :\n    fd(pfd),\n    stat(),\n    open_flags(0),\n    fd_flags(0),\n    fs_flags(0),\n    offset(0),\n    is_sock(true) {\n  }\n\n  FileDescriptorInfo(struct stat pstat, const std::string& pfile_path, int pfd, int popen_flags,\n                     int pfd_flags, int pfs_flags, off_t poffset) :\n    fd(pfd),\n    stat(pstat),\n    file_path(pfile_path),\n    open_flags(popen_flags),\n    fd_flags(pfd_flags),\n    fs_flags(pfs_flags),\n    offset(poffset),\n    is_sock(false) {\n  }\n\n  // Returns true iff. a given path is whitelisted.\n  static bool IsWhitelisted(const std::string& path) {\n    for (size_t i = 0; i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) {\n      if (path.compare(0, strlen(kPathPrefixWhitelist[i]), kPathPrefixWhitelist[i]) == 0) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  // TODO: Call android::base::Readlink instead of copying the code here.\n  static bool Readlink(const int fd, std::string* result) {\n    char path[64];\n    snprintf(path, sizeof(path), \"/proc/self/fd/%d\", fd);\n\n    // Code copied from android::base::Readlink starts here :\n\n    // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,\n    // and truncates to whatever size you do supply, so it can't be used to query.\n    // We could call lstat first, but that would introduce a race condition that\n    // we couldn't detect.\n    // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.\n    char* buf = new char[4096];\n    ssize_t len = readlink(path, buf, 4096);\n    if (len == -1) {\n      delete[] buf;\n      return false;\n    }\n\n    result->assign(buf, len);\n    delete[] buf;\n    return true;\n  }\n\n  // Returns the locally-bound name of the socket |fd|. Returns true\n  // iff. all of the following hold :\n  //\n  // - the socket's sa_family is AF_UNIX.\n  // - the length of the path is greater than zero (i.e, not an unnamed socket).\n  // - the first byte of the path isn't zero (i.e, not a socket with an abstract\n  //   address).\n  static bool GetSocketName(const int fd, std::string* result) {\n    sockaddr_storage ss;\n    sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);\n    socklen_t addr_len = sizeof(ss);\n\n    if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {\n      ALOGE(\"Failed getsockname(%d) : %s\", fd, strerror(errno));\n      return false;\n    }\n\n#if PLATFORM_SDK_VERSION <= 23\n    if (addr->sa_family == AF_NETLINK) {\n      (*result) = \"@netlink@\";\n      return true;\n    }\n#endif\n\n    if (addr->sa_family != AF_UNIX) {\n      //ALOGE(\"Unsupported socket (fd=%d) with family %d\", fd, addr->sa_family);\n      return false;\n    }\n\n    const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);\n\n    size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);\n    // This is an unnamed local socket, we do not accept it.\n    if (path_len == 0) {\n      //ALOGE(\"Unsupported AF_UNIX socket (fd=%d) with empty path.\", fd);\n      return false;\n    }\n\n    // This is a local socket with an abstract address, we do not accept it.\n    if (unix_addr->sun_path[0] == '\\0') {\n      //ALOGE(\"Unsupported AF_UNIX socket (fd=%d) with abstract address.\", fd);\n      return false;\n    }\n\n    // If we're here, sun_path must refer to a null terminated filesystem\n    // pathname (man 7 unix). Remove the terminator before assigning it to an\n    // std::string.\n    if (unix_addr->sun_path[path_len - 1] ==  '\\0') {\n      --path_len;\n    }\n\n    result->assign(unix_addr->sun_path, path_len);\n    return true;\n  }\n\n\n  // DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);\n  FileDescriptorInfo(const FileDescriptorInfo&);\n  void operator=(const FileDescriptorInfo&);\n};\n\n// A FileDescriptorTable is a collection of FileDescriptorInfo objects\n// keyed by their FDs.\nclass FileDescriptorTable {\n public:\n  // Creates a new FileDescriptorTable. This function scans\n  // /proc/self/fd for the list of open file descriptors and collects\n  // information about them. Returns NULL if an error occurs.\n  static FileDescriptorTable* Create() {\n    DIR* d = opendir(kFdPath);\n    if (d == NULL) {\n      ALOGE(\"Unable to open directory %s: %s\", kFdPath, strerror(errno));\n      return NULL;\n    }\n    int dir_fd = dirfd(d);\n    dirent* e;\n\n    std::unordered_map<int, FileDescriptorInfo*> open_fd_map;\n    while ((e = readdir(d)) != NULL) {\n      const int fd = ParseFd(e, dir_fd);\n      if (fd == -1) {\n        continue;\n      }\n\n      FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);\n      if (info == NULL) {\n        continue;\n      }\n      info->Detach();\n      open_fd_map[fd] = info;\n    }\n\n    if (closedir(d) == -1) {\n      ALOGE(\"Unable to close directory : %s\", strerror(errno));\n      return NULL;\n    }\n    return new FileDescriptorTable(open_fd_map);\n  }\n\n  // Reopens all file descriptors that are contained in the table.\n  void Reopen() {\n    std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;\n    for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {\n      const FileDescriptorInfo* info = it->second;\n      if (info != NULL) {\n        info->Reopen();\n        delete info;\n      }\n    }\n  }\n\n private:\n  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)\n      : open_fd_map_(map) {\n  }\n\n  static int ParseFd(dirent* e, int dir_fd) {\n    char* end;\n    const int fd = strtol(e->d_name, &end, 10);\n    if ((*end) != '\\0') {\n      return -1;\n    }\n\n    // Don't bother with the standard input/output/error, they're handled\n    // specially post-fork anyway.\n    if (fd <= STDERR_FILENO || fd == dir_fd) {\n      return -1;\n    }\n\n    return fd;\n  }\n\n  // Invariant: All values in this unordered_map are non-NULL.\n  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;\n\n  // DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);\n  FileDescriptorTable(const FileDescriptorTable&);\n  void operator=(const FileDescriptorTable&);\n};\n"
  },
  {
    "path": "libxposed_art.cpp",
    "content": "/**\n * This file includes functions specific to the ART runtime.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"xposed_shared.h\"\n#include \"libxposed_common.h\"\n#if PLATFORM_SDK_VERSION >= 21\n#include \"fd_utils-inl.h\"\n#endif\n\n#include \"thread.h\"\n#include \"common_throws.h\"\n#if PLATFORM_SDK_VERSION >= 23\n#include \"art_method-inl.h\"\n#else\n#include \"mirror/art_method-inl.h\"\n#endif\n#include \"mirror/object-inl.h\"\n#include \"mirror/throwable.h\"\n#include \"native/scoped_fast_native_object_access.h\"\n#include \"reflection.h\"\n#include \"scoped_thread_state_change.h\"\n#include \"well_known_classes.h\"\n\n#if PLATFORM_SDK_VERSION >= 24\n#include \"mirror/abstract_method.h\"\n#include \"thread_list.h\"\n#endif\n\nusing namespace art;\n\n#if PLATFORM_SDK_VERSION < 23\nusing art::mirror::ArtMethod;\n#endif\n\nnamespace xposed {\n\n\n////////////////////////////////////////////////////////////\n// Library initialization\n////////////////////////////////////////////////////////////\n\n/** Called by Xposed's app_process replacement. */\nbool xposedInitLib(XposedShared* shared) {\n    xposed = shared;\n    xposed->onVmCreated = &onVmCreatedCommon;\n    return true;\n}\n\n/** Called very early during VM startup. */\nbool onVmCreated(JNIEnv*) {\n    // TODO: Handle CLASS_MIUI_RESOURCES?\n    ArtMethod::xposed_callback_class = classXposedBridge;\n    ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod;\n    return true;\n}\n\n\n////////////////////////////////////////////////////////////\n// Utility methods\n////////////////////////////////////////////////////////////\nvoid logExceptionStackTrace() {\n    Thread* self = Thread::Current();\n    ScopedObjectAccess soa(self);\n#if PLATFORM_SDK_VERSION >= 23\n    XLOG(ERROR) << self->GetException()->Dump();\n#else\n    XLOG(ERROR) << self->GetException(nullptr)->Dump();\n#endif\n}\n\n////////////////////////////////////////////////////////////\n// JNI methods\n////////////////////////////////////////////////////////////\n\nvoid XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,\n            jobject, jint, jobject javaAdditionalInfo) {\n    // Detect usage errors.\n    ScopedObjectAccess soa(env);\n    if (javaReflectedMethod == nullptr) {\n#if PLATFORM_SDK_VERSION >= 23\n        ThrowIllegalArgumentException(\"method must not be null\");\n#else\n        ThrowIllegalArgumentException(nullptr, \"method must not be null\");\n#endif\n        return;\n    }\n\n    // Get the ArtMethod of the method to be hooked.\n    ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);\n\n    // Hook the method\n    artMethod->EnableXposedHook(soa, javaAdditionalInfo);\n}\n\njobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod,\n            jint isResolved, jobjectArray, jclass, jobject javaReceiver, jobjectArray javaArgs) {\n    ScopedFastNativeObjectAccess soa(env);\n    if (UNLIKELY(!isResolved)) {\n        ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaMethod);\n        if (LIKELY(artMethod->IsXposedHookedMethod())) {\n            javaMethod = artMethod->GetXposedHookInfo()->reflected_method;\n        }\n    }\n#if PLATFORM_SDK_VERSION >= 23\n    return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);\n#else\n    return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, true);\n#endif\n}\n\nvoid XposedBridge_setObjectClassNative(JNIEnv* env, jclass, jobject javaObj, jclass javaClazz) {\n    ScopedObjectAccess soa(env);\n    StackHandleScope<3> hs(soa.Self());\n    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));\n#if PLATFORM_SDK_VERSION >= 23\n    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), clazz, true, true)) {\n#else\n    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(clazz, true, true)) {\n#endif\n        XLOG(ERROR) << \"Could not initialize class \" << PrettyClass(clazz.Get());\n        return;\n    }\n    Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(javaObj)));\n    Handle<mirror::Class> currentClass(hs.NewHandle(obj->GetClass()));\n    if (clazz->GetObjectSize() != currentClass->GetObjectSize()) {\n        std::string msg = StringPrintf(\"Different object sizes: %s (%d) vs. %s (%d)\",\n                PrettyClass(clazz.Get()).c_str(), clazz->GetObjectSize(),\n                PrettyClass(currentClass.Get()).c_str(), currentClass->GetObjectSize());\n#if PLATFORM_SDK_VERSION >= 23\n        ThrowIllegalArgumentException(msg.c_str());\n#else\n        ThrowIllegalArgumentException(nullptr, msg.c_str());\n#endif\n        return;\n    }\n    obj->SetClass(clazz.Get());\n}\n\nvoid XposedBridge_dumpObjectNative(JNIEnv*, jclass, jobject) {\n    // TODO Can be useful for debugging\n    UNIMPLEMENTED(ERROR|LOG_XPOSED);\n}\n\njobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass, jobject javaObject, jclass javaClazz) {\n    ScopedObjectAccess soa(env);\n    StackHandleScope<3> hs(soa.Self());\n    Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(javaObject)));\n    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));\n    Handle<mirror::Object> dest(hs.NewHandle(obj->Clone(soa.Self(), clazz.Get())));\n    return soa.AddLocalReference<jobject>(dest.Get());\n}\n\nvoid XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) {\n    ScopedObjectAccess soa(env);\n    StackHandleScope<1> hs(soa.Self());\n    Handle<mirror::Class> clazz(hs.NewHandle(soa.Decode<mirror::Class*>(javaClazz)));\n    uint32_t flags = clazz->GetAccessFlags();\n    if ((flags & kAccFinal) != 0) {\n        clazz->SetAccessFlags(flags & ~kAccFinal);\n    }\n}\n\njint XposedBridge_getRuntime(JNIEnv*, jclass) {\n    return 2; // RUNTIME_ART\n}\n\n#if PLATFORM_SDK_VERSION >= 21\nstatic FileDescriptorTable* gClosedFdTable = NULL;\n\nvoid XposedBridge_closeFilesBeforeForkNative(JNIEnv*, jclass) {\n    gClosedFdTable = FileDescriptorTable::Create();\n}\n\nvoid XposedBridge_reopenFilesAfterForkNative(JNIEnv*, jclass) {\n    gClosedFdTable->Reopen();\n    delete gClosedFdTable;\n    gClosedFdTable = NULL;\n}\n#endif\n\n#if PLATFORM_SDK_VERSION >= 24\nvoid XposedBridge_invalidateCallersNative(JNIEnv* env, jclass, jobjectArray javaMethods) {\n    ScopedObjectAccess soa(env);\n    auto* runtime = Runtime::Current();\n    auto* cl = runtime->GetClassLinker();\n\n    // Invalidate callers of the given methods.\n    auto* abstract_methods = soa.Decode<mirror::ObjectArray<mirror::AbstractMethod>*>(javaMethods);\n    size_t count = abstract_methods->GetLength();\n    for (size_t i = 0; i < count; i++) {\n        auto* abstract_method = abstract_methods->Get(i);\n        if (abstract_method == nullptr) {\n            continue;\n        }\n        ArtMethod* method = abstract_method->GetArtMethod();\n        cl->InvalidateCallersForMethod(soa.Self(), method);\n    }\n\n    // Now instrument the stack to deoptimize methods which are being called right now.\n    ScopedThreadSuspension sts(soa.Self(), kSuspended);\n    ScopedSuspendAll ssa(__FUNCTION__);\n    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);\n    runtime->GetThreadList()->ForEach([](Thread* thread, void*) SHARED_REQUIRES(Locks::mutator_lock_) {\n        Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(thread);\n    }, nullptr);\n}\n#endif\n\n}  // namespace xposed\n"
  },
  {
    "path": "libxposed_common.cpp",
    "content": "/**\n * This file includes functions shared by different runtimes.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"libxposed_common.h\"\n#include \"JNIHelp.h\"\n#include <ScopedUtfChars.h>\n\n#define private public\n#if PLATFORM_SDK_VERSION == 15\n#include <utils/ResourceTypes.h>\n#else\n#include <androidfw/ResourceTypes.h>\n#endif\n#undef private\n\nnamespace xposed {\n\n////////////////////////////////////////////////////////////\n// Variables\n////////////////////////////////////////////////////////////\n\nbool xposedLoadedSuccessfully = false;\nxposed::XposedShared* xposed = NULL;\njclass classXposedBridge = NULL;\nstatic jclass classXResources = NULL;\nstatic jclass classFileResult = NULL;\n\njmethodID methodXposedBridgeHandleHookedMethod = NULL;\nstatic jmethodID methodXResourcesTranslateResId = NULL;\nstatic jmethodID methodXResourcesTranslateAttrId = NULL;\nstatic jmethodID constructorFileResult = NULL;\n\n\n////////////////////////////////////////////////////////////\n// Forward declarations\n////////////////////////////////////////////////////////////\n\nstatic int register_natives_XposedBridge(JNIEnv* env, jclass clazz);\nstatic int register_natives_XResources(JNIEnv* env, jclass clazz);\nstatic int register_natives_ZygoteService(JNIEnv* env, jclass clazz);\n\n\n////////////////////////////////////////////////////////////\n// Utility methods\n////////////////////////////////////////////////////////////\n\n/** Read an integer value from a configuration file. */\nint readIntConfig(const char* fileName, int defaultValue) {\n    FILE *fp = fopen(fileName, \"r\");\n    if (fp == NULL)\n        return defaultValue;\n\n    int result;\n    int success = fscanf(fp, \"%i\", &result);\n    fclose(fp);\n\n    return (success >= 1) ? result : defaultValue;\n}\n\n\n////////////////////////////////////////////////////////////\n// Library initialization\n////////////////////////////////////////////////////////////\n\nbool initXposedBridge(JNIEnv* env) {\n    classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);\n    if (classXposedBridge == NULL) {\n        ALOGE(\"Error while loading Xposed class '%s':\", CLASS_XPOSED_BRIDGE);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n    classXposedBridge = reinterpret_cast<jclass>(env->NewGlobalRef(classXposedBridge));\n\n    ALOGI(\"Found Xposed class '%s', now initializing\", CLASS_XPOSED_BRIDGE);\n    if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {\n        ALOGE(\"Could not register natives for '%s'\", CLASS_XPOSED_BRIDGE);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, \"handleHookedMethod\",\n        \"(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\");\n    if (methodXposedBridgeHandleHookedMethod == NULL) {\n        ALOGE(\"ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])\", CLASS_XPOSED_BRIDGE);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    return true;\n}\n\nbool initZygoteService(JNIEnv* env) {\n    jclass zygoteServiceClass = env->FindClass(CLASS_ZYGOTE_SERVICE);\n    if (zygoteServiceClass == NULL) {\n        ALOGE(\"Error while loading ZygoteService class '%s':\", CLASS_ZYGOTE_SERVICE);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n    if (register_natives_ZygoteService(env, zygoteServiceClass) != JNI_OK) {\n        ALOGE(\"Could not register natives for '%s'\", CLASS_ZYGOTE_SERVICE);\n        env->ExceptionClear();\n        return false;\n    }\n\n    classFileResult = env->FindClass(CLASS_FILE_RESULT);\n    if (classFileResult == NULL) {\n        ALOGE(\"Error while loading FileResult class '%s':\", CLASS_FILE_RESULT);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n    classFileResult = reinterpret_cast<jclass>(env->NewGlobalRef(classFileResult));\n\n    constructorFileResult = env->GetMethodID(classFileResult, \"<init>\", \"(JJ)V\");\n    if (constructorFileResult == NULL) {\n        ALOGE(\"ERROR: could not find constructor %s(long, long)\", CLASS_FILE_RESULT);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    return true;\n}\n\nvoid onVmCreatedCommon(JNIEnv* env) {\n    if (!initXposedBridge(env) || !initZygoteService(env)) {\n        return;\n    }\n\n    if (!onVmCreated(env)) {\n        return;\n    }\n\n    xposedLoadedSuccessfully = true;\n    return;\n}\n\n\n////////////////////////////////////////////////////////////\n// JNI methods\n////////////////////////////////////////////////////////////\n\njboolean XposedBridge_hadInitErrors(JNIEnv*, jclass) {\n    return !xposedLoadedSuccessfully;\n}\n\njobject XposedBridge_getStartClassName(JNIEnv* env, jclass) {\n    return env->NewStringUTF(xposed->startClassName);\n}\n\njboolean XposedBridge_startsSystemServer(JNIEnv*, jclass) {\n    return xposed->startSystemServer;\n}\n\njint XposedBridge_getXposedVersion(JNIEnv*, jclass) {\n    return xposed->xposedVersionInt;\n}\n\njboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass) {\n    classXResources = env->FindClass(CLASS_XRESOURCES);\n    if (classXResources == NULL) {\n        ALOGE(\"Error while loading XResources class '%s':\", CLASS_XRESOURCES);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n    classXResources = reinterpret_cast<jclass>(env->NewGlobalRef(classXResources));\n\n    if (register_natives_XResources(env, classXResources) != JNI_OK) {\n        ALOGE(\"Could not register natives for '%s'\", CLASS_XRESOURCES);\n        env->ExceptionClear();\n        return false;\n    }\n\n    methodXResourcesTranslateResId = env->GetStaticMethodID(classXResources, \"translateResId\",\n        \"(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I\");\n    if (methodXResourcesTranslateResId == NULL) {\n        ALOGE(\"ERROR: could not find method %s.translateResId(int, XResources, Resources)\", CLASS_XRESOURCES);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    methodXResourcesTranslateAttrId = env->GetStaticMethodID(classXResources, \"translateAttrId\",\n        \"(Ljava/lang/String;Landroid/content/res/XResources;)I\");\n    if (methodXResourcesTranslateAttrId == NULL) {\n        ALOGE(\"ERROR: could not find method %s.findAttrId(String, XResources)\", CLASS_XRESOURCES);\n        logExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    return true;\n}\n\nvoid XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass,\n            jlong parserPtr, jobject origRes, jobject repRes) {\n\n    using namespace android;\n\n    ResXMLParser* parser = (ResXMLParser*)parserPtr;\n    const ResXMLTree& mTree = parser->mTree;\n    uint32_t* mResIds = (uint32_t*)mTree.mResIds;\n    ResXMLTree_attrExt* tag;\n    int attrCount;\n\n    if (parser == NULL)\n        return;\n\n    do {\n        switch (parser->next()) {\n            case ResXMLParser::START_TAG:\n                tag = (ResXMLTree_attrExt*)parser->mCurExt;\n                attrCount = dtohs(tag->attributeCount);\n                for (int idx = 0; idx < attrCount; idx++) {\n                    ResXMLTree_attribute* attr = (ResXMLTree_attribute*)\n                        (((const uint8_t*)tag)\n                         + dtohs(tag->attributeStart)\n                         + (dtohs(tag->attributeSize)*idx));\n\n                    // find resource IDs for attribute names\n                    int32_t attrNameID = parser->getAttributeNameID(idx);\n                    // only replace attribute name IDs for app packages\n                    if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) {\n                        size_t attNameLen;\n                        const char16_t* attrName = mTree.mStrings.stringAt(attrNameID, &attNameLen);\n                        jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId,\n                            env->NewString((const jchar*)attrName, attNameLen), origRes);\n                        if (env->ExceptionCheck())\n                            goto leave;\n\n                        mResIds[attrNameID] = htodl(attrResID);\n                    }\n\n                    // find original resource IDs for reference values (app packages only)\n                    if (attr->typedValue.dataType != Res_value::TYPE_REFERENCE)\n                        continue;\n\n                    jint oldValue = dtohl(attr->typedValue.data);\n                    if (oldValue < 0x7f000000)\n                        continue;\n\n                    jint newValue = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateResId,\n                        oldValue, origRes, repRes);\n                    if (env->ExceptionCheck())\n                        goto leave;\n\n                    if (newValue != oldValue)\n                        attr->typedValue.data = htodl(newValue);\n                }\n                continue;\n            case ResXMLParser::END_DOCUMENT:\n            case ResXMLParser::BAD_DOCUMENT:\n                goto leave;\n            default:\n                continue;\n        }\n    } while (true);\n\n    leave:\n    parser->restart();\n}\n\n\njboolean ZygoteService_checkFileAccess(JNIEnv* env, jclass, jstring filenameJ, jint mode) {\n#if XPOSED_WITH_SELINUX\n    ScopedUtfChars filename(env, filenameJ);\n    return xposed->zygoteservice_accessFile(filename.c_str(), mode) == 0;\n#else  // XPOSED_WITH_SELINUX\n    return false;\n#endif  // XPOSED_WITH_SELINUX\n}\n\njobject ZygoteService_statFile(JNIEnv* env, jclass, jstring filenameJ) {\n#if XPOSED_WITH_SELINUX\n    ScopedUtfChars filename(env, filenameJ);\n\n    struct stat st;\n    int result = xposed->zygoteservice_statFile(filename.c_str(), &st);\n    if (result != 0) {\n        if (errno == ENOENT) {\n            jniThrowExceptionFmt(env, \"java/io/FileNotFoundException\", \"No such file or directory: %s\", filename.c_str());\n        } else {\n            jniThrowExceptionFmt(env, \"java/io/IOException\", \"%s while reading %s\", strerror(errno), filename.c_str());\n        }\n        return NULL;\n    }\n\n    return env->NewObject(classFileResult, constructorFileResult, (jlong) st.st_size, (jlong) st.st_mtime);\n#else  // XPOSED_WITH_SELINUX\n    return NULL;\n#endif  // XPOSED_WITH_SELINUX\n}\n\njbyteArray ZygoteService_readFile(JNIEnv* env, jclass, jstring filenameJ) {\n#if XPOSED_WITH_SELINUX\n    ScopedUtfChars filename(env, filenameJ);\n\n    int bytesRead = 0;\n    char* content = xposed->zygoteservice_readFile(filename.c_str(), &bytesRead);\n    if (content == NULL) {\n        if (errno == ENOENT) {\n            jniThrowExceptionFmt(env, \"java/io/FileNotFoundException\", \"No such file or directory: %s\", filename.c_str());\n        } else {\n            jniThrowExceptionFmt(env, \"java/io/IOException\", \"%s while reading %s\", strerror(errno), filename.c_str());\n        }\n        return NULL;\n    }\n\n    jbyteArray ret = env->NewByteArray(bytesRead);\n    if (ret != NULL) {\n        jbyte* arrptr = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);\n        if (arrptr) {\n            memcpy(arrptr, content, bytesRead);\n            env->ReleasePrimitiveArrayCritical(ret, arrptr, 0);\n        }\n    }\n\n    free(content);\n    return ret;\n#else  // XPOSED_WITH_SELINUX\n    return NULL;\n#endif  // XPOSED_WITH_SELINUX\n}\n\n////////////////////////////////////////////////////////////\n// JNI methods registrations\n////////////////////////////////////////////////////////////\n\nint register_natives_XposedBridge(JNIEnv* env, jclass clazz) {\n    const JNINativeMethod methods[] = {\n        NATIVE_METHOD(XposedBridge, hadInitErrors, \"()Z\"),\n        NATIVE_METHOD(XposedBridge, getStartClassName, \"()Ljava/lang/String;\"),\n        NATIVE_METHOD(XposedBridge, getRuntime, \"()I\"),\n        NATIVE_METHOD(XposedBridge, startsSystemServer, \"()Z\"),\n        NATIVE_METHOD(XposedBridge, getXposedVersion, \"()I\"),\n        NATIVE_METHOD(XposedBridge, initXResourcesNative, \"()Z\"),\n        NATIVE_METHOD(XposedBridge, hookMethodNative, \"(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V\"),\n        NATIVE_METHOD(XposedBridge, setObjectClassNative, \"(Ljava/lang/Object;Ljava/lang/Class;)V\"),\n        NATIVE_METHOD(XposedBridge, dumpObjectNative, \"(Ljava/lang/Object;)V\"),\n        NATIVE_METHOD(XposedBridge, cloneToSubclassNative, \"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;\"),\n        NATIVE_METHOD(XposedBridge, removeFinalFlagNative, \"(Ljava/lang/Class;)V\"),\n#if PLATFORM_SDK_VERSION >= 21\n        NATIVE_METHOD(XposedBridge, invokeOriginalMethodNative,\n            \"!(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\"),\n        NATIVE_METHOD(XposedBridge, closeFilesBeforeForkNative, \"()V\"),\n        NATIVE_METHOD(XposedBridge, reopenFilesAfterForkNative, \"()V\"),\n#endif\n#if PLATFORM_SDK_VERSION >= 24\n        NATIVE_METHOD(XposedBridge, invalidateCallersNative, \"([Ljava/lang/reflect/Member;)V\"),\n#endif\n    };\n    return env->RegisterNatives(clazz, methods, NELEM(methods));\n}\n\nint register_natives_XResources(JNIEnv* env, jclass clazz) {\n    const JNINativeMethod methods[] = {\n        NATIVE_METHOD(XResources, rewriteXmlReferencesNative, \"(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V\"),\n    };\n    return env->RegisterNatives(clazz, methods, NELEM(methods));\n}\n\nint register_natives_ZygoteService(JNIEnv* env, jclass clazz) {\n    const JNINativeMethod methods[] = {\n        NATIVE_METHOD(ZygoteService, checkFileAccess, \"(Ljava/lang/String;I)Z\"),\n        NATIVE_METHOD(ZygoteService, statFile, \"(Ljava/lang/String;)L\" CLASS_FILE_RESULT \";\"),\n        NATIVE_METHOD(ZygoteService, readFile, \"(Ljava/lang/String;)[B\"),\n    };\n    return env->RegisterNatives(clazz, methods, NELEM(methods));\n}\n\n}  // namespace xposed\n"
  },
  {
    "path": "libxposed_common.h",
    "content": "#ifndef LIBXPOSED_COMMON_H_\n#define LIBXPOSED_COMMON_H_\n\n#include \"xposed_shared.h\"\n\n#ifndef NATIVE_METHOD\n#define NATIVE_METHOD(className, functionName, signature) \\\n  { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }\n#endif\n#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))\n\nnamespace xposed {\n\n#define CLASS_XPOSED_BRIDGE  \"de/robv/android/xposed/XposedBridge\"\n#define CLASS_XRESOURCES     \"android/content/res/XResources\"\n#define CLASS_MIUI_RESOURCES \"android/content/res/MiuiResources\"\n#define CLASS_ZYGOTE_SERVICE \"de/robv/android/xposed/services/ZygoteService\"\n#define CLASS_FILE_RESULT    \"de/robv/android/xposed/services/FileResult\"\n\n\n/////////////////////////////////////////////////////////////////\n// Provided by common part, used by runtime-specific implementation\n/////////////////////////////////////////////////////////////////\nextern jclass classXposedBridge;\nextern jmethodID methodXposedBridgeHandleHookedMethod;\n\nextern int readIntConfig(const char* fileName, int defaultValue);\nextern void onVmCreatedCommon(JNIEnv* env);\n\n\n/////////////////////////////////////////////////////////////////\n// To be provided by runtime-specific implementation\n/////////////////////////////////////////////////////////////////\nextern \"C\" bool xposedInitLib(xposed::XposedShared* shared);\nextern bool onVmCreated(JNIEnv* env);\nextern void prepareSubclassReplacement(JNIEnv* env, jclass clazz);\nextern void logExceptionStackTrace();\n\nextern jint    XposedBridge_getRuntime(JNIEnv* env, jclass clazz);\nextern void    XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,\n                                             jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect);\nextern void    XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect);\nextern jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect);\nextern void    XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect);\nextern void    XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass clazz, jclass javaClazz);\n\n#if PLATFORM_SDK_VERSION >= 21\nextern jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod, jint, jobjectArray,\n                                                       jclass, jobject javaReceiver, jobjectArray javaArgs);\nextern void    XposedBridge_closeFilesBeforeForkNative(JNIEnv* env, jclass clazz);\nextern void    XposedBridge_reopenFilesAfterForkNative(JNIEnv* env, jclass clazz);\n#endif\n#if PLATFORM_SDK_VERSION >= 24\nextern void    XposedBridge_invalidateCallersNative(JNIEnv*, jclass, jobjectArray javaMethods);\n#endif\n\n}  // namespace xposed\n\n#endif  // LIBXPOSED_COMMON_H_\n"
  },
  {
    "path": "libxposed_dalvik.cpp",
    "content": "/**\n * This file includes functions specific to the Dalvik runtime.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"libxposed_dalvik.h\"\n#include \"xposed_offsets.h\"\n\n#include <dlfcn.h>\n\nnamespace xposed {\n\n////////////////////////////////////////////////////////////\n// Forward declarations\n////////////////////////////////////////////////////////////\n\nbool initMemberOffsets(JNIEnv* env);\nvoid hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self);\nvoid XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult, const Method* method, ::Thread* self);\n\n\n////////////////////////////////////////////////////////////\n// Variables\n////////////////////////////////////////////////////////////\n\nstatic ClassObject* objectArrayClass = NULL;\nstatic size_t arrayContentsOffset = 0;\nstatic void* PTR_gDvmJit = NULL;\n\n\n////////////////////////////////////////////////////////////\n// Library initialization\n////////////////////////////////////////////////////////////\n\n/** Called by Xposed's app_process replacement. */\nbool xposedInitLib(xposed::XposedShared* shared) {\n    xposed = shared;\n    xposed->onVmCreated = &onVmCreatedCommon;\n    return true;\n}\n\n/** Called very early during VM startup. */\nbool onVmCreated(JNIEnv* env) {\n    if (!initMemberOffsets(env))\n        return false;\n\n    jclass classMiuiResources = env->FindClass(CLASS_MIUI_RESOURCES);\n    if (classMiuiResources != NULL) {\n        ClassObject* clazz = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), classMiuiResources);\n        if (dvmIsFinalClass(clazz)) {\n            ALOGD(\"Removing final flag for class '%s'\", CLASS_MIUI_RESOURCES);\n            clazz->accessFlags &= ~ACC_FINAL;\n        }\n    }\n    env->ExceptionClear();\n\n    Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(classXposedBridge, \"invokeOriginalMethodNative\",\n        \"(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;\");\n    if (xposedInvokeOriginalMethodNative == NULL) {\n        ALOGE(\"ERROR: could not find method %s.invokeOriginalMethodNative(Member, int, Class[], Class, Object, Object[])\", CLASS_XPOSED_BRIDGE);\n        dvmLogExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n    dvmSetNativeFunc(xposedInvokeOriginalMethodNative, XposedBridge_invokeOriginalMethodNative, NULL);\n\n    objectArrayClass = dvmFindArrayClass(\"[Ljava/lang/Object;\", NULL);\n    if (objectArrayClass == NULL) {\n        ALOGE(\"Error while loading Object[] class\");\n        dvmLogExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    return true;\n}\n\nbool initMemberOffsets(JNIEnv* env) {\n    PTR_gDvmJit = dlsym(RTLD_DEFAULT, \"gDvmJit\");\n\n    if (PTR_gDvmJit == NULL) {\n        offsetMode = MEMBER_OFFSET_MODE_NO_JIT;\n    } else {\n        offsetMode = MEMBER_OFFSET_MODE_WITH_JIT;\n    }\n    ALOGD(\"Using structure member offsets for mode %s\", xposedOffsetModesDesc[offsetMode]);\n\n    MEMBER_OFFSET_COPY(DvmJitGlobals, codeCacheFull);\n\n    int overrideCodeCacheFull = readIntConfig(XPOSED_OVERRIDE_JIT_RESET_OFFSET, -1);\n    if (overrideCodeCacheFull > 0 && overrideCodeCacheFull < 0x400) {\n        ALOGI(\"Offset for DvmJitGlobals.codeCacheFull is overridden, new value is 0x%x\", overrideCodeCacheFull);\n        MEMBER_OFFSET_VAR(DvmJitGlobals, codeCacheFull) = overrideCodeCacheFull;\n    }\n\n    // detect offset of ArrayObject->contents\n    jintArray dummyArray = env->NewIntArray(1);\n    if (dummyArray == NULL) {\n        ALOGE(\"Could allocate int array for testing\");\n        dvmLogExceptionStackTrace();\n        env->ExceptionClear();\n        return false;\n    }\n\n    jint* dummyArrayElements = env->GetIntArrayElements(dummyArray, NULL);\n    arrayContentsOffset = (size_t)dummyArrayElements - (size_t)dvmDecodeIndirectRef(dvmThreadSelf(), dummyArray);\n    env->ReleaseIntArrayElements(dummyArray,dummyArrayElements, 0);\n    env->DeleteLocalRef(dummyArray);\n\n    if (arrayContentsOffset < 12 || arrayContentsOffset > 128) {\n        ALOGE(\"Detected strange offset %d of ArrayObject->contents\", arrayContentsOffset);\n        return false;\n    }\n\n    return true;\n}\n\n\n////////////////////////////////////////////////////////////\n// Utility methods\n////////////////////////////////////////////////////////////\n\n/** Portable clone of dvmSetObjectArrayElement() */\ninline void setObjectArrayElement(const ArrayObject* obj, int index, Object* val) {\n    uintptr_t arrayContents = (uintptr_t)obj + arrayContentsOffset;\n    ((Object **)arrayContents)[index] = val;\n    dvmWriteBarrierArray(obj, index, index + 1);\n}\n\n/** Wrapper used by the common part of the library. */\nvoid logExceptionStackTrace() {\n    dvmLogExceptionStackTrace();\n}\n\n/** Check whether a method is already hooked. */\ninline bool isMethodHooked(const Method* method) {\n    return (method->nativeFunc == &hookedMethodCallback);\n}\n\n////////////////////////////////////////////////////////////\n// JNI methods\n////////////////////////////////////////////////////////////\n\n/** This is called when a hooked method is executed. */\nvoid hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {\n    if (!isMethodHooked(method)) {\n        dvmThrowNoSuchMethodError(\"Could not find Xposed original method - how did you even get here?\");\n        return;\n    }\n\n    XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns;\n    Method* original = (Method*) hookInfo;\n    Object* originalReflected = hookInfo->reflectedMethod;\n    Object* additionalInfo = hookInfo->additionalInfo;\n\n    // convert/box arguments\n    const char* desc = &method->shorty[1]; // [0] is the return type.\n    Object* thisObject = NULL;\n    size_t srcIndex = 0;\n    size_t dstIndex = 0;\n\n    // for non-static methods determine the \"this\" pointer\n    if (!dvmIsStaticMethod(original)) {\n        thisObject = (Object*) args[0];\n        srcIndex++;\n    }\n\n    ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT);\n    if (argsArray == NULL) {\n        return;\n    }\n\n    while (*desc != '\\0') {\n        char descChar = *(desc++);\n        JValue value;\n        Object* obj;\n\n        switch (descChar) {\n        case 'Z':\n        case 'C':\n        case 'F':\n        case 'B':\n        case 'S':\n        case 'I':\n            value.i = args[srcIndex++];\n            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));\n            dvmReleaseTrackedAlloc(obj, self);\n            break;\n        case 'D':\n        case 'J':\n            value.j = dvmGetArgLong(args, srcIndex);\n            srcIndex += 2;\n            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));\n            dvmReleaseTrackedAlloc(obj, self);\n            break;\n        case '[':\n        case 'L':\n            obj  = (Object*) args[srcIndex++];\n            break;\n        default:\n            ALOGE(\"Unknown method signature description character: %c\", descChar);\n            obj = NULL;\n            srcIndex++;\n        }\n        setObjectArrayElement(argsArray, dstIndex++, obj);\n    }\n\n    // call the Java handler function\n    JValue result;\n    dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,\n        originalReflected, (int) original, additionalInfo, thisObject, argsArray);\n\n    dvmReleaseTrackedAlloc(argsArray, self);\n\n    // exceptions are thrown to the caller\n    if (dvmCheckException(self)) {\n        return;\n    }\n\n    // return result with proper type\n    ClassObject* returnType = dvmGetBoxedReturnType(method);\n    if (returnType->primitiveType == PRIM_VOID) {\n        // ignored\n    } else if (result.l == NULL) {\n        if (dvmIsPrimitiveClass(returnType)) {\n            dvmThrowNullPointerException(\"null result when primitive expected\");\n        }\n        pResult->l = NULL;\n    } else {\n        if (!dvmUnboxPrimitive(result.l, returnType, pResult)) {\n            dvmThrowClassCastException(result.l->clazz, returnType);\n        }\n    }\n}\n\n\nvoid XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,\n            jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {\n    // Usage errors?\n    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {\n        dvmThrowIllegalArgumentException(\"method and declaredClass must not be null\");\n        return;\n    }\n\n    // Find the internal representation of the method\n    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);\n    Method* method = dvmSlotToMethod(declaredClass, slot);\n    if (method == NULL) {\n        dvmThrowNoSuchMethodError(\"Could not get internal representation for method\");\n        return;\n    }\n\n    if (isMethodHooked(method)) {\n        // already hooked\n        return;\n    }\n\n    // Save a copy of the original method and other hook info\n    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));\n    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));\n    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));\n    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));\n\n    // Replace method with our own code\n    SET_METHOD_FLAG(method, ACC_NATIVE);\n    method->nativeFunc = &hookedMethodCallback;\n    method->insns = (const u2*) hookInfo;\n    method->registersSize = method->insSize;\n    method->outsSize = 0;\n\n    if (PTR_gDvmJit != NULL) {\n        // reset JIT cache\n        char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));\n        if (currentValue == 0 || currentValue == 1) {\n            MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;\n        } else {\n            ALOGE(\"Unexpected current value for codeCacheFull: %d\", currentValue);\n        }\n    }\n}\n\n\n/**\n * Simplified copy of Method.invokeNative(), but calls the original (non-hooked) method\n * and has no access checks. Used to call the real implementation of hooked methods.\n */\nvoid XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult,\n            const Method* method, ::Thread* self) {\n    Method* meth = (Method*) args[1];\n    if (meth == NULL) {\n        meth = dvmGetMethodFromReflectObj((Object*) args[0]);\n        if (isMethodHooked(meth)) {\n            meth = (Method*) meth->insns;\n        }\n    }\n    ArrayObject* params = (ArrayObject*) args[2];\n    ClassObject* returnType = (ClassObject*) args[3];\n    Object* thisObject = (Object*) args[4]; // null for static methods\n    ArrayObject* argList = (ArrayObject*) args[5];\n\n    // invoke the method\n    pResult->l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true);\n    return;\n}\n\nvoid XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) {\n    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);\n    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect);\n    if (clz->status < CLASS_INITIALIZED && !dvmInitClass(clz)) {\n        ALOGE(\"Could not initialize class %s\", clz->descriptor);\n        return;\n    }\n    obj->clazz = clz;\n}\n\nvoid XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect) {\n    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);\n    dvmDumpObject(obj);\n}\n\njobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) {\n    Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect);\n    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect);\n\n    jobject copyIndirect = env->AllocObject(clzIndirect);\n    if (copyIndirect == NULL)\n        return NULL;\n\n    Object* copy = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), copyIndirect);\n    size_t size = obj->clazz->objectSize;\n    size_t offset = sizeof(Object);\n    memcpy((char*)copy + offset, (char*)obj + offset, size - offset);\n\n    if (IS_CLASS_FLAG_SET(clz, CLASS_ISFINALIZABLE))\n        dvmSetFinalizable(copy);\n\n    return copyIndirect;\n}\n\nvoid XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) {\n    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), javaClazz);\n    if (dvmIsFinalClass(clazz)) {\n        clazz->accessFlags &= ~ACC_FINAL;\n    }\n}\n\njint XposedBridge_getRuntime(JNIEnv* env, jclass clazz) {\n    return 1; // RUNTIME_DALVIK\n}\n\n} // namespace android\n"
  },
  {
    "path": "libxposed_dalvik.h",
    "content": "#ifndef LIBXPOSED_DALVIK_H_\n#define LIBXPOSED_DALVIK_H_\n\n#define ANDROID_SMP 1\n#include \"Dalvik.h\"\n\n#include \"libxposed_common.h\"\n\nnamespace xposed {\n\n#define XPOSED_OVERRIDE_JIT_RESET_OFFSET XPOSED_DIR \"conf/jit_reset_offset\"\n\nstruct XposedHookInfo {\n    struct {\n        Method originalMethod;\n        // copy a few bytes more than defined for Method in AOSP\n        // to accomodate for (rare) extensions by the target ROM\n        int dummyForRomExtensions[4];\n    } originalMethodStruct;\n\n    Object* reflectedMethod;\n    Object* additionalInfo;\n};\n\n}  // namespace xposed\n\n#endif  // LIBXPOSED_DALVIK_H_\n"
  },
  {
    "path": "xposed.cpp",
    "content": "/**\n * This file includes functions called directly from app_main.cpp during startup.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"xposed.h\"\n#include \"xposed_logcat.h\"\n#include \"xposed_safemode.h\"\n#include \"xposed_service.h\"\n\n#include <cstring>\n#include <ctype.h>\n#include <cutils/process_name.h>\n#include <cutils/properties.h>\n#include <dlfcn.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <sys/wait.h>\n\n#if PLATFORM_SDK_VERSION >= 18\n#include <sys/capability.h>\n#else\n#include <linux/capability.h>\n#endif\n\nnamespace xposed {\n\n////////////////////////////////////////////////////////////\n// Variables\n////////////////////////////////////////////////////////////\n\nXposedShared* xposed = new XposedShared;\nstatic int sdkVersion = -1;\nstatic char* argBlockStart;\nstatic size_t argBlockLength;\n\nconst char* xposedVersion = \"unknown (invalid \" XPOSED_PROP_FILE \")\";\nuint32_t xposedVersionInt = 0;\n\n////////////////////////////////////////////////////////////\n// Functions\n////////////////////////////////////////////////////////////\n\n/** Handle special command line options. */\nbool handleOptions(int argc, char* const argv[]) {\n    parseXposedProp();\n\n    if (argc == 2 && strcmp(argv[1], \"--xposedversion\") == 0) {\n        printf(\"Xposed version: %s\\n\", xposedVersion);\n        return true;\n    }\n\n    if (argc == 2 && strcmp(argv[1], \"--xposedtestsafemode\") == 0) {\n        printf(\"Testing Xposed safemode trigger\\n\");\n\n        if (detectSafemodeTrigger(shouldSkipSafemodeDelay())) {\n            printf(\"Safemode triggered\\n\");\n        } else {\n            printf(\"Safemode not triggered\\n\");\n        }\n        return true;\n    }\n\n    // From Lollipop coding, used to override the process name\n    argBlockStart = argv[0];\n    uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);\n    uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);\n    end += strlen(argv[argc - 1]) + 1;\n    argBlockLength = end - start;\n\n    return false;\n}\n\n/** Initialize Xposed (unless it is disabled). */\nbool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {\n#if !defined(XPOSED_ENABLE_FOR_TOOLS)\n    if (!zygote)\n        return false;\n#endif\n\n    if (isMinimalFramework()) {\n        ALOGI(\"Not loading Xposed for minimal framework (encrypted device)\");\n        return false;\n    }\n\n    xposed->zygote = zygote;\n    xposed->startSystemServer = startSystemServer;\n    xposed->startClassName = className;\n    xposed->xposedVersionInt = xposedVersionInt;\n\n#if XPOSED_WITH_SELINUX\n    xposed->isSELinuxEnabled   = is_selinux_enabled() == 1;\n    xposed->isSELinuxEnforcing = xposed->isSELinuxEnabled && security_getenforce() == 1;\n#else\n    xposed->isSELinuxEnabled   = false;\n    xposed->isSELinuxEnforcing = false;\n#endif  // XPOSED_WITH_SELINUX\n\n    if (startSystemServer) {\n        xposed::logcat::printStartupMarker();\n    } else if (zygote) {\n        // TODO Find a better solution for this\n        // Give the primary Zygote process a little time to start first.\n        // This also makes the log easier to read, as logs for the two Zygotes are not mixed up.\n        sleep(10);\n    }\n\n    printRomInfo();\n\n    if (startSystemServer) {\n        if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) {\n            return false;\n        }\n        xposed::logcat::start();\n#if XPOSED_WITH_SELINUX\n    } else if (xposed->isSELinuxEnabled) {\n        if (!xposed::service::startMembased()) {\n            return false;\n        }\n#endif  // XPOSED_WITH_SELINUX\n    }\n\n#if XPOSED_WITH_SELINUX\n    // Don't let any further forks access the Zygote service\n    if (xposed->isSELinuxEnabled) {\n        xposed::service::membased::restrictMemoryInheritance();\n    }\n#endif  // XPOSED_WITH_SELINUX\n\n    // FIXME Zygote has no access to input devices, this would need to be check in system_server context\n    if (zygote && !isSafemodeDisabled() && detectSafemodeTrigger(shouldSkipSafemodeDelay()))\n        disableXposed();\n\n    if (isDisabled() || (!zygote && shouldIgnoreCommand(argc, argv)))\n        return false;\n\n    return addJarToClasspath();\n}\n\n/** Print information about the used ROM into the log */\nvoid printRomInfo() {\n    char release[PROPERTY_VALUE_MAX];\n    char sdk[PROPERTY_VALUE_MAX];\n    char manufacturer[PROPERTY_VALUE_MAX];\n    char model[PROPERTY_VALUE_MAX];\n    char rom[PROPERTY_VALUE_MAX];\n    char fingerprint[PROPERTY_VALUE_MAX];\n    char platform[PROPERTY_VALUE_MAX];\n#if defined(__LP64__)\n    const int bit = 64;\n#else\n    const int bit = 32;\n#endif\n\n    property_get(\"ro.build.version.release\", release, \"n/a\");\n    property_get(\"ro.build.version.sdk\", sdk, \"n/a\");\n    property_get(\"ro.product.manufacturer\", manufacturer, \"n/a\");\n    property_get(\"ro.product.model\", model, \"n/a\");\n    property_get(\"ro.build.display.id\", rom, \"n/a\");\n    property_get(\"ro.build.fingerprint\", fingerprint, \"n/a\");\n    property_get(\"ro.product.cpu.abi\", platform, \"n/a\");\n\n    ALOGI(\"-----------------\");\n    ALOGI(\"Starting Xposed version %s, compiled for SDK %d\", xposedVersion, PLATFORM_SDK_VERSION);\n    ALOGI(\"Device: %s (%s), Android version %s (SDK %s)\", model, manufacturer, release, sdk);\n    ALOGI(\"ROM: %s\", rom);\n    ALOGI(\"Build fingerprint: %s\", fingerprint);\n    ALOGI(\"Platform: %s, %d-bit binary, system server: %s\", platform, bit, xposed->startSystemServer ? \"yes\" : \"no\");\n    if (!xposed->zygote) {\n        ALOGI(\"Class name: %s\", xposed->startClassName);\n    }\n    ALOGI(\"SELinux enabled: %s, enforcing: %s\",\n            xposed->isSELinuxEnabled ? \"yes\" : \"no\",\n            xposed->isSELinuxEnforcing ? \"yes\" : \"no\");\n}\n\n/** Parses /system/xposed.prop and stores selected values in variables */\nvoid parseXposedProp() {\n    FILE *fp = fopen(XPOSED_PROP_FILE, \"r\");\n    if (fp == NULL) {\n        ALOGE(\"Could not read %s: %s\", XPOSED_PROP_FILE, strerror(errno));\n        return;\n    }\n\n    char buf[512];\n    while (fgets(buf, sizeof(buf), fp) != NULL) {\n        char* key = buf;\n        // Ignore leading spaces for the key\n        while (isspace(*key)) key++;\n\n        // Skip comments\n        if (*key == '#')\n            continue;\n\n        // Find the key/value separator\n        char* value = strchr(buf, '=');\n        if (value == NULL)\n            continue;\n\n        // Ignore trailing spaces for the key\n        char* tmp = value;\n        do { *tmp = 0; tmp--; } while (isspace(*tmp));\n\n        // Ignore leading spaces for the value\n        do { value++; } while (isspace(*value));\n\n        // Remove trailing newline\n        tmp = strpbrk(value, \"\\n\\r\");\n        if (tmp != NULL)\n            *tmp = 0;\n\n        // Handle this entry\n        if (!strcmp(\"version\", key)) {\n            int len = strlen(value);\n            if (len == 0)\n                continue;\n            tmp = (char*) malloc(len + 1);\n            strlcpy(tmp, value, len + 1);\n            xposedVersion = tmp;\n            xposedVersionInt = atoi(xposedVersion);\n        }\n    }\n    fclose(fp);\n\n    return;\n}\n\n/** Returns the SDK version of the system */\nint getSdkVersion() {\n    if (sdkVersion < 0) {\n        char sdkString[PROPERTY_VALUE_MAX];\n        property_get(\"ro.build.version.sdk\", sdkString, \"0\");\n        sdkVersion = atoi(sdkString);\n    }\n    return sdkVersion;\n}\n\n/** Check whether Xposed is disabled by a flag file */\nbool isDisabled() {\n    if (zygote_access(XPOSED_LOAD_BLOCKER, F_OK) == 0) {\n        ALOGE(\"Found %s, not loading Xposed\", XPOSED_LOAD_BLOCKER);\n        return true;\n    }\n    return false;\n}\n\n/** Create a flag file to disable Xposed. */\nvoid disableXposed() {\n    int fd;\n    // FIXME add a \"touch\" operation to xposed::service::membased\n    fd = open(XPOSED_LOAD_BLOCKER, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);\n    if (fd >= 0)\n        close(fd);\n}\n\n/** Check whether safemode is disabled. */\nbool isSafemodeDisabled() {\n    if (zygote_access(XPOSED_SAFEMODE_DISABLE, F_OK) == 0)\n        return true;\n    else\n        return false;\n}\n\n/** Check whether the delay for safemode should be skipped. */\nbool shouldSkipSafemodeDelay() {\n    if (zygote_access(XPOSED_SAFEMODE_NODELAY, F_OK) == 0)\n        return true;\n    else\n        return false;\n}\n\n/** Ignore the broadcasts by various Superuser implementations to avoid spamming the Xposed log. */\nbool shouldIgnoreCommand(int argc, const char* const argv[]) {\n    if (argc < 4 || strcmp(xposed->startClassName, \"com.android.commands.am.Am\") != 0)\n        return false;\n\n    if (strcmp(argv[2], \"broadcast\") != 0 && strcmp(argv[2], \"start\") != 0)\n        return false;\n\n    bool mightBeSuperuser = false;\n    for (int i = 3; i < argc; i++) {\n        if (strcmp(argv[i], \"com.noshufou.android.su.RESULT\") == 0\n         || strcmp(argv[i], \"eu.chainfire.supersu.NativeAccess\") == 0)\n            return true;\n\n        if (mightBeSuperuser && strcmp(argv[i], \"--user\") == 0)\n            return true;\n\n        const char* lastComponent = strrchr(argv[i], '.');\n        if (!lastComponent)\n            continue;\n\n        if (strcmp(lastComponent, \".RequestActivity\") == 0\n         || strcmp(lastComponent, \".NotifyActivity\") == 0\n         || strcmp(lastComponent, \".SuReceiver\") == 0)\n            mightBeSuperuser = true;\n    }\n\n    return false;\n}\n\n/** Adds a path to the beginning of an environment variable. */\nstatic bool addPathToEnv(const char* name, const char* path) {\n    char* oldPath = getenv(name);\n    if (oldPath == NULL) {\n        setenv(name, path, 1);\n    } else {\n        char newPath[4096];\n        int neededLength = snprintf(newPath, sizeof(newPath), \"%s:%s\", path, oldPath);\n        if (neededLength >= (int)sizeof(newPath)) {\n            ALOGE(\"ERROR: %s would exceed %\" PRIuPTR \" characters\", name, sizeof(newPath));\n            return false;\n        }\n        setenv(name, newPath, 1);\n    }\n    return true;\n}\n\n/** Add XposedBridge.jar to the Java classpath. */\nbool addJarToClasspath() {\n    ALOGI(\"-----------------\");\n\n    // Do we have a new version and are (re)starting zygote? Then load it!\n    /*\n    FIXME if you can\n    if (xposed->startSystemServer && access(XPOSED_JAR_NEWVERSION, R_OK) == 0) {\n        ALOGI(\"Found new Xposed jar version, activating it\");\n        if (rename(XPOSED_JAR_NEWVERSION, XPOSED_JAR) != 0) {\n            ALOGE(\"Could not move %s to %s\", XPOSED_JAR_NEWVERSION, XPOSED_JAR);\n            return false;\n        }\n    }\n    */\n\n    if (access(XPOSED_JAR, R_OK) == 0) {\n        if (!addPathToEnv(\"CLASSPATH\", XPOSED_JAR))\n            return false;\n\n        ALOGI(\"Added Xposed (%s) to CLASSPATH\", XPOSED_JAR);\n        return true;\n    } else {\n        ALOGE(\"ERROR: Could not access Xposed jar '%s'\", XPOSED_JAR);\n        return false;\n    }\n}\n\n/** Callback which checks the loaded shared libraries for libdvm/libart. */\nstatic bool determineRuntime(const char** xposedLibPath) {\n    FILE *fp = fopen(\"/proc/self/maps\", \"r\");\n    if (fp == NULL) {\n        ALOGE(\"Could not open /proc/self/maps: %s\", strerror(errno));\n        return false;\n    }\n\n    bool success = false;\n    char line[256];\n    while (fgets(line, sizeof(line), fp) != NULL) {\n        char* libname = strrchr(line, '/');\n        if (!libname)\n            continue;\n        libname++;\n\n        if (strcmp(\"libdvm.so\\n\", libname) == 0) {\n            ALOGI(\"Detected Dalvik runtime\");\n            *xposedLibPath = XPOSED_LIB_DALVIK;\n            success = true;\n            break;\n\n        } else if (strcmp(\"libart.so\\n\", libname) == 0) {\n            ALOGI(\"Detected ART runtime\");\n            *xposedLibPath = XPOSED_LIB_ART;\n            success = true;\n            break;\n        }\n    }\n\n    fclose(fp);\n    return success;\n}\n\n/** Load the libxposed_*.so library for the currently active runtime. */\nvoid onVmCreated(JNIEnv* env) {\n    // Determine the currently active runtime\n    const char* xposedLibPath = NULL;\n    if (!determineRuntime(&xposedLibPath)) {\n        ALOGE(\"Could not determine runtime, not loading Xposed\");\n        return;\n    }\n\n    // Load the suitable libxposed_*.so for it\n    void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW);\n    if (!xposedLibHandle) {\n        ALOGE(\"Could not load libxposed: %s\", dlerror());\n        return;\n    }\n\n    // Clear previous errors\n    dlerror();\n\n    // Initialize the library\n    bool (*xposedInitLib)(XposedShared* shared) = NULL;\n    *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, \"xposedInitLib\");\n    if (!xposedInitLib)  {\n        ALOGE(\"Could not find function xposedInitLib\");\n        return;\n    }\n\n#if XPOSED_WITH_SELINUX\n    xposed->zygoteservice_accessFile = &service::membased::accessFile;\n    xposed->zygoteservice_statFile   = &service::membased::statFile;\n    xposed->zygoteservice_readFile   = &service::membased::readFile;\n#endif  // XPOSED_WITH_SELINUX\n\n    if (xposedInitLib(xposed)) {\n        xposed->onVmCreated(env);\n    }\n}\n\n/** Set the process name */\nvoid setProcessName(const char* name) {\n    memset(argBlockStart, 0, argBlockLength);\n    strlcpy(argBlockStart, name, argBlockLength);\n    set_process_name(name);\n}\n\n/** Determine the UID/GID of Xposed Installer. */\nbool determineXposedInstallerUidGid() {\n    if (xposed->isSELinuxEnabled) {\n        struct stat* st = (struct stat*) mmap(NULL, sizeof(struct stat), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n        if (st == MAP_FAILED) {\n            ALOGE(\"Could not allocate memory in determineXposedInstallerUidGid(): %s\", strerror(errno));\n            return false;\n        }\n\n        pid_t pid;\n        if ((pid = fork()) < 0) {\n            ALOGE(\"Fork in determineXposedInstallerUidGid() failed: %s\", strerror(errno));\n            munmap(st, sizeof(struct stat));\n            return false;\n        } else if (pid == 0) {\n            // Child.\n#if XPOSED_WITH_SELINUX\n            if (setcon(ctx_app) != 0) {\n                ALOGE(\"Could not switch to %s context\", ctx_app);\n                exit(EXIT_FAILURE);\n            }\n#endif  // XPOSED_WITH_SELINUX\n\n            if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, st)) != 0) {\n                ALOGE(\"Could not stat %s: %s\", XPOSED_DIR, strerror(errno));\n                exit(EXIT_FAILURE);\n            }\n\n            exit(EXIT_SUCCESS);\n        }\n\n        // Parent.\n        int status;\n        if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {\n            munmap(st, sizeof(struct stat));\n            return false;\n        }\n\n        xposed->installer_uid = st->st_uid;\n        xposed->installer_gid = st->st_gid;\n        munmap(st, sizeof(struct stat));\n        return true;\n    } else {\n        struct stat st;\n        if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, &st)) != 0) {\n            ALOGE(\"Could not stat %s: %s\", XPOSED_DIR, strerror(errno));\n            return false;\n        }\n\n        xposed->installer_uid = st.st_uid;\n        xposed->installer_gid = st.st_gid;\n        return true;\n    }\n}\n\n/** Switch UID/GID to the ones of Xposed Installer. */\nbool switchToXposedInstallerUidGid() {\n    if (setresgid(xposed->installer_gid, xposed->installer_gid, xposed->installer_gid) != 0) {\n        ALOGE(\"Could not setgid(%d): %s\", xposed->installer_gid, strerror(errno));\n        return false;\n    }\n    if (setresuid(xposed->installer_uid, xposed->installer_uid, xposed->installer_uid) != 0) {\n        ALOGE(\"Could not setuid(%d): %s\", xposed->installer_uid, strerror(errno));\n        return false;\n    }\n    return true;\n}\n\n/** Drop all capabilities except for the mentioned ones */\nvoid dropCapabilities(int8_t keep[]) {\n    struct __user_cap_header_struct header;\n    struct __user_cap_data_struct cap[2];\n    memset(&header, 0, sizeof(header));\n    memset(&cap, 0, sizeof(cap));\n    header.version = _LINUX_CAPABILITY_VERSION_3;\n    header.pid = 0;\n\n    if (keep != NULL) {\n      for (int i = 0; keep[i] >= 0; i++) {\n        cap[CAP_TO_INDEX(keep[i])].permitted |= CAP_TO_MASK(keep[i]);\n      }\n      cap[0].effective = cap[0].inheritable = cap[0].permitted;\n      cap[1].effective = cap[1].inheritable = cap[1].permitted;\n    }\n\n    capset(&header, &cap[0]);\n}\n\n/**\n * Checks whether the system is booting into a minimal Android framework.\n * This is the case when the device is encrypted with a password that\n * has to be entered on boot. /data is a tmpfs in that case, so we\n * can't load any modules anyway.\n * The system will reboot later with the full framework.\n */\nbool isMinimalFramework() {\n    char voldDecrypt[PROPERTY_VALUE_MAX];\n    property_get(\"vold.decrypt\", voldDecrypt, \"\");\n    return ((strcmp(voldDecrypt, \"trigger_restart_min_framework\") == 0) ||\n            (strcmp(voldDecrypt, \"1\") == 0));\n}\n\n}  // namespace xposed\n"
  },
  {
    "path": "xposed.h",
    "content": "#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 defined(__LP64__)\n  #define XPOSED_LIB_DIR \"/system/lib64/\"\n#else\n  #define XPOSED_LIB_DIR \"/system/lib/\"\n#endif\n#define XPOSED_LIB_DALVIK        XPOSED_LIB_DIR \"libxposed_dalvik.so\"\n#define XPOSED_LIB_ART           XPOSED_LIB_DIR \"libxposed_art.so\"\n#define XPOSED_JAR               \"/system/framework/XposedBridge.jar\"\n#define XPOSED_JAR_NEWVERSION    XPOSED_DIR \"bin/XposedBridge.jar.newversion\"\n#define XPOSED_LOAD_BLOCKER      XPOSED_DIR \"conf/disabled\"\n#define XPOSED_SAFEMODE_NODELAY  XPOSED_DIR \"conf/safemode_nodelay\"\n#define XPOSED_SAFEMODE_DISABLE  XPOSED_DIR \"conf/safemode_disable\"\n\n#define XPOSED_CLASS_DOTS_ZYGOTE \"de.robv.android.xposed.XposedBridge\"\n#define XPOSED_CLASS_DOTS_TOOLS  \"de.robv.android.xposed.XposedBridge$ToolEntryPoint\"\n\n#if XPOSED_WITH_SELINUX\n#include <selinux/selinux.h>\n#define ctx_system ((security_context_t) \"u:r:system_server:s0\")\n#if PLATFORM_SDK_VERSION >= 23\n#define ctx_app    ((security_context_t) \"u:r:untrusted_app:s0:c512,c768\")\n#else\n#define ctx_app    ((security_context_t) \"u:r:untrusted_app:s0\")\n#endif  // PLATFORM_SDK_VERSION >= 23\n#endif  // XPOSED_WITH_SELINUX\n\nnamespace xposed {\n\n    bool handleOptions(int argc, char* const argv[]);\n    bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]);\n    void printRomInfo();\n    void parseXposedProp();\n    int getSdkVersion();\n    bool isDisabled();\n    void disableXposed();\n    bool isSafemodeDisabled();\n    bool shouldSkipSafemodeDelay();\n    bool shouldIgnoreCommand(int argc, const char* const argv[]);\n    bool addJarToClasspath();\n    void onVmCreated(JNIEnv* env);\n    void setProcessName(const char* name);\n    bool determineXposedInstallerUidGid();\n    bool switchToXposedInstallerUidGid();\n    void dropCapabilities(int8_t keep[] = NULL);\n    bool isMinimalFramework();\n\n}  // namespace xposed\n\n#endif  // XPOSED_H_\n"
  },
  {
    "path": "xposed_logcat.cpp",
    "content": "/**\n * This file includes the Xposed service, which is especially used to work around SELinux restrictions.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include <cstring>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/prctl.h>\n#include <unistd.h>\n\n#include \"xposed.h\"\n#include \"xposed_service.h\"\n#include \"xposed_logcat.h\"\n\n\nnamespace xposed {\nnamespace logcat {\n\n////////////////////////////////////////////////////////////\n// Declarations\n////////////////////////////////////////////////////////////\n\n#define AID_LOG      1007\n#define CAP_SYSLOG   34\nchar marker[50];\n\n\n////////////////////////////////////////////////////////////\n// Functions\n////////////////////////////////////////////////////////////\n\nstatic void execLogcat() {\n    int8_t keep[] = { CAP_SYSLOG, -1 };\n    xposed::dropCapabilities(keep);\n\n    // Execute a logcat command that will keep running in the background\n    if (zygote_access(XPOSEDLOG_CONF_ALL, F_OK) == 0) {\n        execl(\"/system/bin/logcat\", \"logcat\",\n            \"-v\", \"time\",            // include timestamps in the log\n            (char*) 0);\n    } else {\n        execl(\"/system/bin/logcat\", \"logcat\",\n            \"-v\", \"time\",            // include timestamps in the log\n            \"-s\",                    // be silent by default, except for the following tags\n            \"XposedStartupMarker:D\", // marks the beginning of the current log\n            \"Xposed:I\",              // Xposed framework and default logging\n            \"appproc:I\",             // app_process\n            \"XposedInstaller:I\",     // Xposed Installer\n            \"art:F\",                 // ART crashes\n            (char*) 0);\n    }\n\n    // We only get here in case of errors\n    ALOGE(\"Could not execute logcat: %s\", strerror(errno));\n    exit(EXIT_FAILURE);\n}\n\n#ifndef dprintf\nstatic inline int dprintf(int fd, const char *format, ...) {\n    char* message;\n    va_list args;\n    va_start(args, format);\n    int size = vasprintf(&message, format, args);\n    if (size > 0) {\n        write(fd, message, size);\n        free(message);\n    }\n    va_end(args);\n    return size;\n}\n#endif\n\nstatic void runDaemon(int pipefd) {\n    xposed::setProcessName(\"xposed_logcat\");\n    xposed::dropCapabilities();\n\n    umask(0);\n    int logfile = open(XPOSEDLOG, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);\n    if (logfile < 0) {\n        ALOGE(\"Could not open %s: %s\", XPOSEDLOG, strerror(errno));\n        exit(EXIT_FAILURE);\n    }\n\n    FILE* pipe = fdopen(pipefd, \"r\");\n    if (pipe == NULL) {\n        ALOGE(\"fdopen failed for pipe file descriptor %d: %s\", pipefd, strerror(errno));\n        exit(EXIT_FAILURE);\n    }\n\n    char buf[512];\n    bool foundMarker = false;\n    long totalSize = 0;\n    while (fgets(buf, sizeof(buf), pipe) != NULL) {\n        if (buf[0] == '-')\n            continue; // beginning of <logbuffer type>\n\n        if (!foundMarker) {\n            if (strstr(buf, \"XposedStartupMarker\") != NULL && strstr(buf, marker) != NULL) {\n                foundMarker = true;\n            }\n            continue;\n        }\n\n        int len = strlen(buf);\n        write(logfile, buf, len);\n\n        totalSize += len;\n        if (totalSize > XPOSEDLOG_MAX_SIZE) {\n            dprintf(logfile, \"\\nReached maximum log size (%'d kB), further lines won't be logged.\\n\", XPOSEDLOG_MAX_SIZE / 1024);\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    ALOGE(\"Broken pipe to logcat: %s\", strerror(ferror(pipe)));\n    close(logfile);\n    exit(EXIT_FAILURE);\n}\n\nvoid printStartupMarker() {\n    sprintf(marker, \"Current time: %d, PID: %d\", (int) time(NULL), getpid());\n    ALOG(LOG_DEBUG, \"XposedStartupMarker\", marker, NULL);\n}\n\nvoid start() {\n    // Fork to create a daemon\n    pid_t pid;\n    if ((pid = fork()) < 0) {\n        ALOGE(\"Fork for Xposed logcat daemon failed: %s\", strerror(errno));\n        return;\n    } else if (pid != 0) {\n        return;\n    }\n\n    // Ensure that we're allowed to read all log entries\n    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {\n        ALOGE(\"Failed to keep capabilities: %s\", strerror(errno));\n    }\n    const gid_t groups[] = { AID_LOG };\n    setgroups(1, groups);\n    if (!xposed::switchToXposedInstallerUidGid()) {\n        exit(EXIT_FAILURE);\n    }\n\n#if XPOSED_WITH_SELINUX\n    if (xposed->isSELinuxEnabled) {\n        if (setcon(ctx_app) != 0) {\n            ALOGE(\"Could not switch to %s context\", ctx_app);\n            exit(EXIT_FAILURE);\n        }\n    }\n#endif  // XPOSED_WITH_SELINUX\n\n    int err = rename(XPOSEDLOG, XPOSEDLOG_OLD);\n    if (err < 0 && errno != ENOENT) {\n        ALOGE(\"%s while renaming log file %s -> %s\", strerror(errno), XPOSEDLOG, XPOSEDLOG_OLD);\n        exit(EXIT_FAILURE);\n    }\n\n    int pipeFds[2];\n    if (pipe(pipeFds) < 0) {\n        ALOGE(\"Could not allocate pipe for logcat output: %s\", strerror(errno));\n        exit(EXIT_FAILURE);\n    }\n    fcntl(pipeFds[0], F_SETPIPE_SZ, 1048576);\n\n    if ((pid = fork()) < 0) {\n        ALOGE(\"Fork for logcat execution failed: %s\", strerror(errno));\n        exit(EXIT_FAILURE);\n    } else if (pid == 0) {\n        close(pipeFds[0]);\n        if (dup2(pipeFds[1], STDOUT_FILENO) == -1) {\n            ALOGE(\"Could not redirect stdout: %s\", strerror(errno));\n            exit(EXIT_FAILURE);\n        }\n        if (dup2(pipeFds[1], STDERR_FILENO) == -1) {\n            ALOGE(\"Could not redirect stdout: %s\", strerror(errno));\n            exit(EXIT_FAILURE);\n        }\n        execLogcat();\n    } else {\n        close(pipeFds[1]);\n        runDaemon(pipeFds[0]);\n    }\n\n    // Should never reach this point\n    exit(EXIT_FAILURE);\n}\n\n}  // namespace logcat\n}  // namespace xposed\n"
  },
  {
    "path": "xposed_logcat.h",
    "content": "#ifndef XPOSED_LOGCAT_H_\n#define XPOSED_LOGCAT_H_\n\n#define XPOSEDLOG            XPOSED_DIR \"log/error.log\"\n#define XPOSEDLOG_OLD        XPOSEDLOG \".old\"\n#define XPOSEDLOG_CONF_ALL   XPOSED_DIR \"conf/log_all\"\n#define XPOSEDLOG_MAX_SIZE   5*1024*1024\n\nnamespace xposed {\nnamespace logcat {\n\n    void printStartupMarker();\n    void start();\n\n}  // namespace logcat\n}  // namespace xposed\n\n#endif /* XPOSED_LOGCAT_H_ */\n"
  },
  {
    "path": "xposed_offsets.h",
    "content": "/*\n    Certain compile time parameters result in different offsets\n    for members in structures. This file defines the offsets for\n    members which cannot be accessed otherwise and some macros\n    to simplify accessing them.\n*/\n\n#define MEMBER_OFFSET_ARRAY(type,member) offsets_array_ ## type ## _ ## member\n#define MEMBER_OFFSET_VAR(type,member) offset_ ## type ## _ ## member\n#define MEMBER_TYPE(type,member) offset_type_ ## type ## _ ## member\n\n#define MEMBER_PTR(obj,type,member) \\\n    ( (MEMBER_TYPE(type,member)*)  ( (char*)(obj) + MEMBER_OFFSET_VAR(type,member) ) )\n#define MEMBER_VAL(obj,type,member) *MEMBER_PTR(obj,type,member)\n\n#define MEMBER_OFFSET_DEFINE(type,member,offsets...) \\\n    static int MEMBER_OFFSET_ARRAY(type,member)[] = { offsets }; \\\n    static int MEMBER_OFFSET_VAR(type,member);\n#define MEMBER_OFFSET_COPY(type,member) MEMBER_OFFSET_VAR(type,member) = MEMBER_OFFSET_ARRAY(type,member)[offsetMode]\n\n\n// here are the definitions of the modes and offsets\nenum xposedOffsetModes {\n    MEMBER_OFFSET_MODE_WITH_JIT,\n    MEMBER_OFFSET_MODE_NO_JIT,\n};\nstatic xposedOffsetModes offsetMode;\nconst char* xposedOffsetModesDesc[] = {\n    \"WITH_JIT\",\n    \"NO_JIT\",\n};\n\nMEMBER_OFFSET_DEFINE(DvmJitGlobals, codeCacheFull, 120, 0)\n#define offset_type_DvmJitGlobals_codeCacheFull bool\n\n\n\n// helper to determine the required values (compile with XPOSED_SHOW_OFFSET=true)\n#ifdef XPOSED_SHOW_OFFSETS\n    template<int s> struct RESULT;\n    #ifdef WITH_JIT\n        #pragma message \"WITH_JIT is defined\"\n    #else\n       #pragma message \"WITH_JIT is not defined\"\n    #endif\n    RESULT<sizeof(Method)> SIZEOF_Method;\n    RESULT<sizeof(Thread)> SIZEOF_Thread;\n    RESULT<offsetof(DvmJitGlobals, codeCacheFull)> OFFSETOF_DvmJitGlobals_codeCacheFull;\n#endif\n\n\n"
  },
  {
    "path": "xposed_safemode.cpp",
    "content": "/*\n * Detects input combinations for recovering from bootloops.\n *\n * The safemode trigger is detected if exactly one of the physical keys is pressed in\n * the first 2 seconds after detection startup (or already held down), and a total of\n * 5 consecutive presses of that same key are performed in the subsequent 5 seconds.\n *\n * 2 short vibrations are performed when the first key is pressed; an additional\n * vibration is performed for each subsequent press of the same key, and a final\n * long vibration is performed if the trigger was successful.\n *\n * The initial 2-second delay can be disabled through configuration; in that case,\n * one of the keys must already be pressed when the detection starts, otherwise\n * the detection fails and no delays are introduced.\n *\n * References:\n *   /frameworks/base/services/input/EventHub.cpp (AOSP)\n *   /include/uapi/linux/input.h (Linux)\n *   Using the Input Subsystem, Linux Journal\n */\n#include \"xposed_safemode.h\"\n\n#include <cstring>\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/input.h>\n#include <sys/epoll.h>\n#include <time.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#define INITIAL_DELAY 2\n#define DETECTION_TIMEOUT 5\n\n#define DETECTION_PRESSES 5\n\n#define VIBRATOR_CONTROL \"/sys/class/timed_output/vibrator/enable\"\n#define VIBRATION_SHORT 150\n#define VIBRATION_LONG 500\n#define VIBRATION_INTERVAL 200\n\nstatic const char *DEVICE_PATH = \"/dev/input\";\n#define MAX_DEVICES 4\n\n#define test_bit(bit, array)    (array[bit/8] & (1<<(bit%8)))\n\nstatic const int physical_keycodes[] = { KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_POWER,\n                                         KEY_HOME, KEY_BACK, KEY_MENU, KEY_CAMERA };\n\n\n\nstatic void vibrate(int count, int duration_ms, int interval_ms) {\n    int fd;\n    int len;\n    char value[30];\n\n    if ((fd = open(VIBRATOR_CONTROL, O_RDWR)) < 0)\n        // Failed to open the control file, ignore it\n        return;\n\n    len = sprintf(value, \"%d\\n\", duration_ms);\n    for (int i = 0; i < count; i++) {\n        if (i != 0)\n            // Pause between the several vibrations\n            usleep((duration_ms + interval_ms) * 1000);\n        // Vibrate (asynchronously)\n        write(fd, value, len);\n    }\n    close(fd);\n}\n\n\n/*\n * Enumerates the existing input devices and opens handles for the ones that\n * report the relevant keys.\n *\n * Arguments:\n * - *fds: is filled on output with the file handles for the opened devices\n * - max_fds: maximum available entries in the fds array\n * - *pressedKey: is filled on output with\n *        0 if no key was found being held down at this instant\n *       -1 if more than one key was found being held down\n *       id of the pressed key, if only a single one was being held down\n * Returns:\n * - the number of opened device handles, filled in the *fds output parameter\n * - 0 if no devices were opened\n */\nstatic int openKeyDevices(int *fds, int max_fds, int *pressedKey) {\n    char devname[PATH_MAX];\n    char *filename;\n    DIR *dir;\n    struct dirent *de;\n\n    int count = 0;\n    // No key was detected as pressed, for the moment\n    *pressedKey = 0;\n\n    dir = opendir(DEVICE_PATH);\n    if(dir == NULL)\n        return 0;\n\n    strcpy(devname, DEVICE_PATH);\n    filename = devname + strlen(devname);\n    *filename++ = '/';\n    while (count < max_fds && (de = readdir(dir))) {\n        // Skip '.' and '..'\n        if(de->d_name[0] == '.' &&\n           (de->d_name[1] == '\\0' ||\n            (de->d_name[1] == '.' && de->d_name[2] == '\\0')))\n            continue;\n\n        strcpy(filename, de->d_name);\n        int fd = open(devname, O_RDWR | O_CLOEXEC);\n        if(fd < 0)\n            // Skip files that could not be opened\n            continue;\n\n        // Check if this device reports one of the relevant keys\n        uint8_t keyBitmask[(KEY_MAX + 1) / 8];\n        memset(keyBitmask, 0, sizeof(keyBitmask));\n        ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBitmask)), keyBitmask);\n        bool reportsKeys = false;\n        for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) {\n            if (test_bit(physical_keycodes[i], keyBitmask)) {\n                reportsKeys = true;\n                break;\n            }\n        }\n        if (!reportsKeys) {\n            // This device doesn't report any of the relevant keys\n            // Skip to the next one\n            close(fd);\n            continue;\n        }\n\n        fds[count++] = fd;\n\n        // Check if one of the keys is currently pressed on this device, to report it to the caller\n        memset(keyBitmask, 0, sizeof(keyBitmask));\n        ioctl(fd, EVIOCGKEY(sizeof(keyBitmask)), keyBitmask);\n        for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) {\n            if (test_bit(physical_keycodes[i], keyBitmask)) {\n                // One of the relevant keys was detected as held down\n                // We'll report it to be pressed, but only if there isn't more than one key being pressed\n                if (*pressedKey == 0) {\n                    // No key was being pressed, this one will be reported\n                    *pressedKey = physical_keycodes[i];\n                } else {\n                    // Another key was already found to be pressed, report multiple keys to the caller\n                    *pressedKey = -1;\n                    break;\n                }\n            }\n        }\n    }\n\n    closedir(dir);\n    return count;\n}\n\n\n/*\n * Computes the remaining time, in ms, from the current time to the supplied expiration moment\n */\nint getRemainingTime(struct timespec expiration) {\n    struct timespec now;\n    clock_gettime(CLOCK_MONOTONIC, &now);\n    if (now.tv_sec > expiration.tv_sec)\n        return 0;\n    else\n        return (expiration.tv_sec - now.tv_sec) * 1000 + (expiration.tv_nsec - now.tv_nsec) / 1000000;\n}\n\n\n\nnamespace xposed {\n\nbool detectSafemodeTrigger(bool skipInitialDelay) {\n\n    int efd = -1;\n    int fds[MAX_DEVICES];\n    int deviceCount = 0;\n    int pressedKey = 0;\n    int triggerPresses = 0;\n    bool result = false;\n\n    // Open input devices that report one of the relevant physical keys\n    deviceCount = openKeyDevices(fds, sizeof(fds) / sizeof(fds[0]), &pressedKey);\n    if (deviceCount == 0)\n        // No input devices found, abort detection\n        goto leave;\n\n    if (pressedKey < 0)\n        // More than one key was initially pressed\n        // Immediately report a negative detection, with no further delays\n        goto leave;\n\n    if (pressedKey == 0 && skipInitialDelay)\n        // A single key wasn't held down and the initial delay is disabled\n        // Immediately report a negative detection, with no further delays\n        goto leave;\n\n    // Prepare waiting mechanism for received events in all devices\n    if ((efd = epoll_create(deviceCount)) < 0)\n        // Failed to create the epoll handle, abort\n        goto leave;\n\n    // Register each device descriptor in the epoll handle\n    struct epoll_event eventPollItems[MAX_DEVICES];\n    for (int i = 0; i < deviceCount; i++) {\n        memset(&eventPollItems[i], 0, sizeof(eventPollItems[i]));\n        eventPollItems[i].events = EPOLLIN;\n        eventPollItems[i].data.fd = fds[i];\n        if (epoll_ctl(efd, EPOLL_CTL_ADD, fds[i], &eventPollItems[i]))\n            // Failed to add device descriptor to the epoll handle, abort\n            goto leave;\n    }\n\n    int timeout_ms;\n    struct timespec expiration;\n    clock_gettime(CLOCK_MONOTONIC, &expiration);\n    expiration.tv_sec += INITIAL_DELAY;\n\n    // Wait up to INITIAL_DELAY seconds for an initial keypress, it no key was initially down\n    while (pressedKey == 0 && (timeout_ms = getRemainingTime(expiration)) > 0) {\n        // Wait for next input event in one of the opened devices\n        int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms);\n        if (pollResult < 0)\n            // Failed to wait for event, abort\n            goto leave;\n\n        // Loop through the opened devices where a new event is available\n        for (int i = 0; i < pollResult; i++) {\n            struct input_event evt;\n            int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt));\n            if (readSize != sizeof(evt))\n                // Invalid size read, ignore\n                continue;\n\n            if (evt.type != EV_KEY)\n                // Only consider key events\n                continue;\n            if (evt.value != 1)\n                // Ignore key releases, we're monitoring presses\n                continue;\n\n            for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) {\n                if (evt.code == physical_keycodes[j]) {\n                    // One of the keys was pressed, end the initial detection\n                    // No need to check for duplicate keys, as the events are reported sequentially\n                    // and multiple presses can't be reported at once\n                    pressedKey = evt.code;\n                    break;\n                }\n            }\n        }\n    }\n    if (pressedKey == 0)\n        // No key was pressed during the initial delay or upfront, so the detection has failed\n        goto leave;\n\n    // Notify the user that the safemode sequence has been started and we're waiting for\n    // the remaining key presses\n    vibrate(2, VIBRATION_SHORT, VIBRATION_INTERVAL);\n\n\n    // Detection will wait at most DETECTION_TIMEOUT seconds\n    clock_gettime(CLOCK_MONOTONIC, &expiration);\n    expiration.tv_sec += DETECTION_TIMEOUT;\n\n    // Initial key press is counted as well\n    triggerPresses++;\n\n    // Loop waiting for the same key to be pressed the appropriate number of times, a different key to\n    // be pressed, or the timeout to be reached\n    while (triggerPresses < DETECTION_PRESSES && (timeout_ms = getRemainingTime(expiration)) > 0) {\n        // Wait for next input event\n        int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms);\n        if (pollResult < 0)\n            // Failed to wait for event, abort\n            goto leave;\n\n        // Loop through the opened devices where a new event is available\n        for (int i = 0; i < pollResult; i++) {\n            struct input_event evt;\n            int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt));\n            if (readSize != sizeof(evt))\n                // Invalid size read, ignore\n                continue;\n\n            if (evt.type != EV_KEY)\n                // Only consider key events\n                continue;\n            if (evt.value != 1)\n                // Ignore key releases, we're monitoring presses\n                continue;\n\n            for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) {\n                if (evt.code == physical_keycodes[j]) {\n                    if (pressedKey == evt.code) {\n                        // The same key was pressed again, increment the counter and notify the user\n                        triggerPresses++;\n                        // The final key press will be confirmed with a long vibration later\n                        if (triggerPresses < DETECTION_PRESSES)\n                            vibrate(1, VIBRATION_SHORT, 0);\n                    } else {\n                        // A key was pressed other than the initial one\n                        // Abort the detection and avoid further delays\n                        goto leave;\n                    }\n                    break;\n                }\n            }\n        }\n    }\n\n    // Was safemode successfully triggered?\n    if (triggerPresses >= DETECTION_PRESSES) {\n        vibrate(1, VIBRATION_LONG, 0);\n        result = true;\n    }\n\nleave:\n    if (efd >= 0)\n        close(efd);\n    for (int i = 0; i < deviceCount; i++)\n        close(fds[i]);\n\n    return result;\n\n}\n\n}\n\n"
  },
  {
    "path": "xposed_safemode.h",
    "content": "#ifndef XPOSED_SAFEMODE_H_\n#define XPOSED_SAFEMODE_H_\n\nnamespace xposed {\n\nbool detectSafemodeTrigger(bool skipInitialDelay);\n\n}\n\n#endif  // XPOSED_SAFEMODE_H_\n\n"
  },
  {
    "path": "xposed_service.cpp",
    "content": "/**\n * This file includes the Xposed services, which are especially used to work around SELinux restrictions.\n */\n\n#define LOG_TAG \"Xposed\"\n\n#include \"xposed.h\"\n#include \"xposed_service.h\"\n\n#include <binder/BpBinder.h>\n#include <binder/IInterface.h>\n#include <binder/IPCThreadState.h>\n#include <binder/IServiceManager.h>\n#include <binder/Parcel.h>\n#include <binder/ProcessState.h>\n#include <errno.h>\n#include <fcntl.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n#include <sys/mman.h>\n\n#define UID_SYSTEM 1000\n\nusing namespace android;\n\nnamespace xposed {\nnamespace service {\n\n////////////////////////////////////////////////////////////\n// Declarations\n////////////////////////////////////////////////////////////\n\nbool running = false;\n\n\n////////////////////////////////////////////////////////////\n// Memory-based communication (used by Zygote)\n////////////////////////////////////////////////////////////\n\nnamespace membased {\n\nenum State {\n    STATE_NOT_RUNNING,\n    STATE_IDLE,\n    STATE_SERVICE_ACTION,\n    STATE_SERVER_RESPONSE,\n};\n\nenum Action {\n    OP_NONE,\n    OP_ACCESS_FILE,\n    OP_STAT_FILE,\n    OP_READ_FILE,\n};\n\nstruct AccessFileData {\n    // in\n    char path[PATH_MAX];\n    int mode;\n    // out\n    int result;\n};\n\nstruct StatFileData {\n    // in\n    char path[PATH_MAX];\n    // inout\n    struct stat st;\n    // out\n    int result;\n};\n\nstruct ReadFileData {\n    // in\n    char path[PATH_MAX];\n    int offset;\n    // out\n    int totalSize;\n    int bytesRead;\n    bool eof;\n    char content[32*1024];\n};\n\nstruct MemBasedState {\n    pthread_mutex_t workerMutex;\n    pthread_cond_t workerCond;\n    State state;\n    Action action;\n    int error;\n    union {\n        AccessFileData accessFile;\n        StatFileData statFile;\n        ReadFileData readFile;\n    } data;\n};\n\nMemBasedState* shared = NULL;\npid_t zygotePid = 0;\nbool canAlwaysAccessService = false;\n\ninline static void initSharedMutex(pthread_mutex_t* mutex) {\n    pthread_mutexattr_t attr;\n    pthread_mutexattr_init(&attr);\n    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);\n    pthread_mutex_init(mutex, &attr);\n    pthread_mutexattr_destroy(&attr);\n}\n\ninline static void initSharedCond(pthread_cond_t* cond) {\n    pthread_condattr_t cattr;\n    pthread_condattr_init(&cattr);\n    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);\n    pthread_cond_init(cond, &cattr);\n    pthread_condattr_destroy(&cattr);\n}\n\nstatic bool init() {\n    shared = (MemBasedState*) mmap(NULL, sizeof(MemBasedState), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n    if (shared == MAP_FAILED) {\n        ALOGE(\"Could not allocate memory for Zygote service: %s\", strerror(errno));\n        shared = NULL;\n        return false;\n    }\n\n    zygotePid = getpid();\n    canAlwaysAccessService = true;\n\n    initSharedMutex(&shared->workerMutex);\n    initSharedCond(&shared->workerCond);\n    shared->state = STATE_NOT_RUNNING;\n    shared->action = OP_NONE;\n    shared->error = 0;\n    return true;\n}\n\nvoid restrictMemoryInheritance() {\n    madvise(shared, sizeof(MemBasedState), MADV_DONTFORK);\n    canAlwaysAccessService = false;\n}\n\nstatic inline bool isServiceAccessible() {\n    if (!canAlwaysAccessService && (shared == NULL || zygotePid != getpid())) {\n        ALOGE(\"Zygote service is not accessible from PID %d, UID %d\", getpid(), getuid());\n        shared = NULL;\n        errno = EPERM;\n        return false;\n    }\n    return true;\n}\n\n// Server implementation\nvoid* looper(void* unused __attribute__((unused))) {\n    pthread_mutex_lock(&shared->workerMutex);\n    shared->state = STATE_IDLE;\n    pthread_cond_broadcast(&shared->workerCond);\n    while (1) {\n        while (shared->state != STATE_SERVICE_ACTION) {\n            pthread_cond_wait(&shared->workerCond, &shared->workerMutex);\n        }\n\n        switch (shared->action) {\n            case OP_ACCESS_FILE: {\n                struct AccessFileData* data = &shared->data.accessFile;\n                data->result = TEMP_FAILURE_RETRY(access(data->path, data->mode));\n                if (data->result != 0) {\n                    shared->error = errno;\n                }\n            } break;\n\n            case OP_STAT_FILE: {\n                struct StatFileData* data = &shared->data.statFile;\n                data->result = TEMP_FAILURE_RETRY(stat(data->path, &data->st));\n                if (data->result != 0) {\n                    shared->error = errno;\n                }\n            } break;\n\n            case OP_READ_FILE: {\n                struct ReadFileData* data = &shared->data.readFile;\n                struct stat st;\n\n                if (stat(data->path, &st) != 0) {\n                    shared->error = errno;\n                    break;\n                }\n\n                data->totalSize = st.st_size;\n\n                FILE *f = fopen(data->path, \"r\");\n                if (f == NULL) {\n                    shared->error = errno;\n                    break;\n                }\n\n                if (data->offset > 0 && fseek(f, data->offset, SEEK_SET) != 0) {\n                    shared->error = ferror(f);\n                    fclose(f);\n                    break;\n                }\n\n                data->bytesRead = fread(data->content, 1, sizeof(data->content), f);\n                shared->error = ferror(f);\n                data->eof = feof(f);\n\n                fclose(f);\n            } break;\n\n            case OP_NONE: {\n                ALOGE(\"No-op call to membased service\");\n                break;\n            }\n\n            default: {\n                ALOGE(\"Invalid action in call to membased service\");\n                break;\n            }\n        }\n\n        shared->state = STATE_SERVER_RESPONSE;\n        pthread_cond_broadcast(&shared->workerCond);\n    }\n\n    pthread_mutex_unlock(&shared->workerMutex);\n    return NULL;\n}\n\n// Client implementation\nstatic inline bool waitForRunning(int timeout) {\n    if (shared == NULL || timeout < 0)\n        return false;\n\n    struct timespec ts;\n    clock_gettime(CLOCK_REALTIME, &ts);\n    ts.tv_sec += 5;\n    int rc = 0;\n    pthread_mutex_lock(&shared->workerMutex);\n    while (shared->state == STATE_NOT_RUNNING && rc == 0) {\n        rc = pthread_cond_timedwait(&shared->workerCond, &shared->workerMutex, &ts);\n    }\n    pthread_mutex_unlock(&shared->workerMutex);\n    return rc == 0;\n}\n\nstatic inline void waitForIdle() {\n    pthread_mutex_lock(&shared->workerMutex);\n    while (shared->state != STATE_IDLE) {\n        pthread_cond_wait(&shared->workerCond, &shared->workerMutex);\n    }\n}\n\nstatic inline void callService(Action action) {\n    shared->action = action;\n    shared->state = STATE_SERVICE_ACTION;\n    shared->error = 0;\n    pthread_cond_broadcast(&shared->workerCond);\n\n    while (shared->state != STATE_SERVER_RESPONSE) {\n        pthread_cond_wait(&shared->workerCond, &shared->workerMutex);\n    }\n}\n\nstatic inline void makeIdle() {\n    shared->action = OP_NONE;\n    shared->state = STATE_IDLE;\n    pthread_cond_broadcast(&shared->workerCond);\n    pthread_mutex_unlock(&shared->workerMutex);\n}\n\nint accessFile(const char* path, int mode) {\n    if (!isServiceAccessible())\n        return -1;\n\n    if (strlen(path) > sizeof(AccessFileData::path) - 1) {\n        errno = ENAMETOOLONG;\n        return -1;\n    }\n\n    waitForIdle();\n\n    struct AccessFileData* data = &shared->data.accessFile;\n    strcpy(data->path, path);\n    data->mode = mode;\n\n    callService(OP_ACCESS_FILE);\n\n    makeIdle();\n    errno = shared->error;\n    return shared->error ? -1 : data->result;\n}\n\nint statFile(const char* path, struct stat* st) {\n    if (!isServiceAccessible())\n        return -1;\n\n    if (strlen(path) > sizeof(StatFileData::path) - 1) {\n        errno = ENAMETOOLONG;\n        return -1;\n    }\n\n    waitForIdle();\n\n    struct StatFileData* data = &shared->data.statFile;\n    strcpy(data->path, path);\n\n    callService(OP_STAT_FILE);\n\n    memcpy(st, &data->st, sizeof(struct stat));\n\n    makeIdle();\n    errno = shared->error;\n    return shared->error ? -1 : data->result;\n}\n\nchar* readFile(const char* path, int* bytesRead) {\n    if (!isServiceAccessible())\n        return NULL;\n\n    char* result = NULL;\n    int offset = 0, totalSize = 0;\n\n    if (bytesRead)\n        *bytesRead = 0;\n\n    if (strlen(path) > sizeof(ReadFileData::path) - 1) {\n        errno = ENAMETOOLONG;\n        return NULL;\n    }\n\n    waitForIdle();\n\n    struct ReadFileData* data = &shared->data.readFile;\n    strcpy(data->path, path);\n    data->offset = 0;\n\n    callService(OP_READ_FILE);\n    if (shared->error)\n        goto bail;\n\n    totalSize = data->totalSize;\n    result = (char*) malloc(totalSize + 1);\n    result[totalSize] = 0;\n    memcpy(result, data->content, data->bytesRead);\n\n    while (!data->eof) {\n        offset += data->bytesRead;\n        data->offset = offset;\n\n        callService(OP_READ_FILE);\n        if (shared->error)\n            goto bail;\n\n        if (offset + data->bytesRead > totalSize) {\n            shared->error = EBUSY;\n            goto bail;\n        }\n\n        memcpy(result + offset, data->content, data->bytesRead);\n    }\n\n    if (bytesRead)\n        *bytesRead = offset + data->bytesRead;\n\n    bail:\n    makeIdle();\n    if (shared->error && result) {\n        free(result);\n        result = NULL;\n    }\n    errno = shared->error;\n    return result;\n}\n\n}  // namespace membased\n\n\n////////////////////////////////////////////////////////////\n// Binder service\n////////////////////////////////////////////////////////////\n\nnamespace binder {\n\n#define XPOSED_BINDER_SYSTEM_SERVICE_NAME \"user.xposed.system\"\n#define XPOSED_BINDER_APP_SERVICE_NAME    \"user.xposed.app\"\n\nclass IXposedService: public IInterface {\n    public:\n        DECLARE_META_INTERFACE(XposedService);\n        virtual int test() const = 0;\n        virtual status_t addService(const String16& name,\n                                    const sp<IBinder>& service,\n                                    bool allowIsolated = false) const = 0;\n        virtual int accessFile(const String16& filename,\n                               int32_t mode) const = 0;\n        virtual int statFile(const String16& filename,\n                             int64_t* size,\n                             int64_t* mtime) const = 0;\n        virtual status_t readFile(const String16& filename,\n                                  int32_t offset,\n                                  int32_t length,\n                                  int64_t* size,\n                                  int64_t* mtime,\n                                  uint8_t** buffer,\n                                  int32_t* bytesRead,\n                                  String16* errormsg) const = 0;\n\n        enum {\n            TEST_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,\n            ADD_SERVICE_TRANSACTION,\n            ACCESS_FILE_TRANSACTION,\n            STAT_FILE_TRANSACTION,\n            READ_FILE_TRANSACTION,\n        };\n};\n\nclass BnXposedService: public BnInterface<IXposedService> {\n    public:\n        virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);\n};\n\nclass BpXposedService: public BpInterface<IXposedService> {\n    public:\n        BpXposedService(const sp<IBinder>& impl) : BpInterface<IXposedService>(impl) {}\n\n        virtual int test() const {\n            Parcel data, reply;\n            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());\n            remote()->transact(TEST_TRANSACTION, data, &reply);\n            if (reply.readExceptionCode() != 0) return -1;\n            return reply.readInt32();\n        }\n\n        virtual status_t addService(const String16& name, const sp<IBinder>& service,\n                bool allowIsolated = false) const {\n            Parcel data, reply;\n            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());\n            data.writeString16(name);\n            data.writeStrongBinder(service);\n            data.writeInt32(allowIsolated ? 1 : 0);\n            status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);\n            return err == NO_ERROR ? reply.readExceptionCode() : err;\n        }\n\n        virtual status_t accessFile(const String16& name, int32_t mode) const {\n            Parcel data, reply;\n            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());\n            data.writeString16(name);\n            data.writeInt32(mode);\n\n            remote()->transact(ACCESS_FILE_TRANSACTION, data, &reply);\n            if (reply.readExceptionCode() != 0) return -1;\n\n            errno = reply.readInt32();\n            return (errno == 0) ? 0 : -1;\n        }\n\n        virtual status_t statFile(const String16& name, int64_t* size, int64_t* mtime) const {\n            Parcel data, reply;\n            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());\n            data.writeString16(name);\n\n            remote()->transact(STAT_FILE_TRANSACTION, data, &reply);\n            if (reply.readExceptionCode() != 0) return -1;\n\n            errno = reply.readInt32();\n            if (errno != 0) return -1;\n\n            int64_t size1 = reply.readInt64();\n            int64_t mtime1 = reply.readInt64();\n            if (size != NULL) *size = size1;\n            if (mtime != NULL) *mtime = mtime1;\n            return 0;\n        }\n\n        virtual status_t readFile(const String16& filename, int32_t offset, int32_t length,\n                int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const {\n            Parcel data, reply;\n            data.writeInterfaceToken(IXposedService::getInterfaceDescriptor());\n            data.writeString16(filename);\n            data.writeInt32(offset);\n            data.writeInt32(length);\n            int64_t size1 = 0;\n            int64_t mtime1 = 0;\n            if (size != NULL) size1 = *size;\n            if (mtime != NULL) mtime1 = *mtime;\n            data.writeInt64(size1);\n            data.writeInt64(mtime1);\n\n            remote()->transact(READ_FILE_TRANSACTION, data, &reply);\n            if (reply.readExceptionCode() != 0) return -1;\n\n            status_t err = reply.readInt32();\n            const String16& errormsg1(reply.readString16());\n            size1 = reply.readInt64();\n            mtime1 = reply.readInt64();\n            int32_t bytesRead1 = reply.readInt32();\n            if (size != NULL) *size = size1;\n            if (mtime != NULL) *mtime = mtime1;\n            if (bytesRead != NULL) *bytesRead = bytesRead1;\n            if (errormsg) *errormsg = errormsg1;\n\n            if (bytesRead1 > 0 && bytesRead1 <= (int32_t)reply.dataAvail()) {\n                *buffer = (uint8_t*) malloc(bytesRead1 + 1);\n                *buffer[bytesRead1] = 0;\n                reply.read(*buffer, bytesRead1);\n            } else {\n                *buffer = NULL;\n            }\n\n            errno = err;\n            return (errno == 0) ? 0 : -1;\n        }\n};\n\nIMPLEMENT_META_INTERFACE(XposedService, \"de.robv.android.xposed.IXposedService\");\n\nstatus_t BnXposedService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)  {\n    switch (code) {\n        case TEST_TRANSACTION: {\n            CHECK_INTERFACE(IXposedService, data, reply);\n            reply->writeNoException();\n            reply->writeInt32(test());\n            return NO_ERROR;\n        } break;\n\n        case ADD_SERVICE_TRANSACTION: {\n            CHECK_INTERFACE(IXposedService, data, reply);\n            String16 which = data.readString16();\n            sp<IBinder> b = data.readStrongBinder();\n            bool allowIsolated = (data.readInt32() != 0);\n            reply->writeInt32(addService(which, b, allowIsolated));\n            return NO_ERROR;\n        } break;\n\n        case ACCESS_FILE_TRANSACTION: {\n            CHECK_INTERFACE(IXposedService, data, reply);\n            String16 filename = data.readString16();\n            int32_t mode = data.readInt32();\n            status_t result = accessFile(filename, mode);\n            int err = errno;\n            reply->writeNoException();\n            reply->writeInt32(result == 0 ? 0 : err);\n            return NO_ERROR;\n        } break;\n\n        case STAT_FILE_TRANSACTION: {\n            CHECK_INTERFACE(IXposedService, data, reply);\n            String16 filename = data.readString16();\n            int64_t size, time;\n            status_t result = statFile(filename, &size, &time);\n            int err = errno;\n            reply->writeNoException();\n            if (result == 0) {\n                reply->writeInt32(0);\n                reply->writeInt64(size);\n                reply->writeInt64(time);\n            } else {\n                reply->writeInt32(err);\n            }\n            return NO_ERROR;\n        } break;\n\n        case READ_FILE_TRANSACTION: {\n            CHECK_INTERFACE(IXposedService, data, reply);\n            String16 filename = data.readString16();\n            int32_t offset = data.readInt32();\n            int32_t length = data.readInt32();\n            int64_t size = data.readInt64();\n            int64_t mtime = data.readInt64();\n            uint8_t* buffer = NULL;\n            int32_t bytesRead = -1;\n            String16 errormsg;\n\n            status_t err = readFile(filename, offset, length, &size, &mtime, &buffer, &bytesRead, &errormsg);\n\n            reply->writeNoException();\n            reply->writeInt32(err);\n            reply->writeString16(errormsg);\n            reply->writeInt64(size);\n            reply->writeInt64(mtime);\n            if (bytesRead > 0) {\n                reply->writeInt32(bytesRead);\n                reply->write(buffer, bytesRead);\n                free(buffer);\n            } else {\n                reply->writeInt32(bytesRead); // empty array (0) or null (-1)\n            }\n            return NO_ERROR;\n        } break;\n\n        default:\n            return BBinder::onTransact(code, data, reply, flags);\n    }\n}\n\nclass XposedService : public BnXposedService {\n    public:\n        XposedService(bool system);\n\n        virtual int test() const;\n        virtual status_t addService(const String16& name,\n                                    const sp<IBinder>& service,\n                                    bool allowIsolated = false) const;\n        virtual status_t accessFile(const String16& filename16,\n                                    int32_t mode) const;\n        virtual status_t statFile(const String16& filename,\n                                  int64_t* size,\n                                  int64_t* mtime) const;\n        virtual status_t readFile(const String16& filename16,\n                                  int32_t offset,\n                                  int32_t length,\n                                  int64_t* size,\n                                  int64_t* mtime,\n                                  uint8_t** buffer,\n                                  int32_t* bytesRead,\n                                  String16* errormsg) const;\n\n    private:\n        bool isSystem;\n};\n\nstatic String16 formatToString16(const char* fmt, ...) {\n    char* message;\n    va_list args;\n    va_start(args, fmt);\n    int size = vasprintf(&message, fmt, args);\n    String16 result(message, size);\n    free(message);\n    va_end(args);\n    return result;\n}\n\nXposedService::XposedService(bool system)\n        : isSystem(system) {}\n\nint XposedService::test() const {\n    pid_t pid = IPCThreadState::self()->getCallingPid();\n    ALOGD(\"This is PID %d, test method was called from PID %d\", getpid(), pid);\n    return getpid();\n}\n\nstatus_t XposedService::addService(const String16& name, const sp<IBinder>& service,\n        bool allowIsolated) const {\n    uid_t uid = IPCThreadState::self()->getCallingUid();\n    if (!isSystem || (uid != xposed->installer_uid)) {\n        ALOGE(\"Permission denied, not adding service %s\", String8(name).string());\n        errno = EPERM;\n        return -1;\n    }\n    sp<IServiceManager> sm = defaultServiceManager();\n#if PLATFORM_SDK_VERSION >= 16\n    return sm->addService(name, service, allowIsolated);\n#else\n    return sm->addService(name, service);\n#endif\n}\n\nstatus_t XposedService::accessFile(const String16& filename16, int32_t mode) const {\n    uid_t caller = IPCThreadState::self()->getCallingUid();\n    if (caller != UID_SYSTEM) {\n        ALOGE(\"UID %d is not allowed to use the Xposed service\", caller);\n        errno = EPERM;\n        return -1;\n    }\n    const char* filename = String8(filename16).string();\n    return TEMP_FAILURE_RETRY(access(filename, mode));\n}\n\nstatus_t XposedService::statFile(const String16& filename16, int64_t* size, int64_t* time) const {\n    uid_t caller = IPCThreadState::self()->getCallingUid();\n    if (caller != UID_SYSTEM) {\n        ALOGE(\"UID %d is not allowed to use the Xposed service\", caller);\n        errno = EPERM;\n        return -1;\n    }\n    const char* filename = String8(filename16).string();\n    struct stat st;\n    status_t result = TEMP_FAILURE_RETRY(stat(filename, &st));\n    if (result == 0) {\n        *size = st.st_size;\n        *time = st.st_mtime;\n    }\n    return result;\n}\n\nstatus_t XposedService::readFile(const String16& filename16, int32_t offset, int32_t length,\n        int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const {\n\n    uid_t caller = IPCThreadState::self()->getCallingUid();\n    if (caller != UID_SYSTEM) {\n        ALOGE(\"UID %d is not allowed to use the Xposed service\", caller);\n        return EPERM;\n    }\n\n    *buffer = NULL;\n    *bytesRead = -1;\n\n    // Get file metadata\n    const char* filename = String8(filename16).string();\n    struct stat st;\n    if (stat(filename, &st) != 0) {\n        status_t err = errno;\n        if (errormsg) *errormsg = formatToString16(\"%s during stat() on %s\", strerror(err), filename);\n        return err;\n    }\n\n    if (S_ISDIR(st.st_mode)) {\n        if (errormsg) *errormsg = formatToString16(\"%s is a directory\", filename);\n        return EISDIR;\n    }\n\n    // Don't load again if file is unchanged\n    if (*size == st.st_size && *mtime == (int32_t)st.st_mtime) {\n        return 0;\n    }\n\n    *size = st.st_size;\n    *mtime = st.st_mtime;\n\n    // Check range\n    if (offset > 0 && offset >= *size) {\n        if (errormsg) *errormsg = formatToString16(\"offset %d >= size %\" PRId64 \" for %s\", offset, *size, filename);\n        return EINVAL;\n    } else if (offset < 0) {\n        offset = 0;\n    }\n\n    if (length > 0 && (offset + length) > *size) {\n        if (errormsg) *errormsg = formatToString16(\"offset %d + length %d > size %\" PRId64 \" for %s\", offset, length, *size, filename);\n        return EINVAL;\n    } else if (*size == 0) {\n        *bytesRead = 0;\n        return 0;\n    } else if (length <= 0) {\n        length = *size - offset;\n    }\n\n    // Allocate buffer\n    *buffer = (uint8_t*) malloc(length + 1);\n    if (*buffer == NULL) {\n        if (errormsg) *errormsg = formatToString16(\"allocating buffer with %d bytes failed\", length + 1);\n        return ENOMEM;\n    }\n    (*buffer)[length] = 0;\n\n    // Open file\n    FILE *f = fopen(filename, \"r\");\n    if (f == NULL) {\n        status_t err = errno;\n        free(*buffer);\n        *buffer = NULL;\n        if (errormsg) *errormsg = formatToString16(\"%s during fopen() on %s\", strerror(err), filename);\n        return err;\n    }\n\n    // Seek to correct offset\n    if (offset > 0 && fseek(f, offset, SEEK_SET) != 0) {\n        free(*buffer);\n        *buffer = NULL;\n        status_t err = ferror(f);\n        fclose(f);\n        if (errormsg) *errormsg = formatToString16(\"%s during fseek() to offset %d for %s\", strerror(err), offset, filename);\n        return err;\n    }\n\n    // Read the file\n    *bytesRead = fread(*buffer, 1, length, f);\n    status_t err = ferror(f);\n    if (err != 0) {\n        free(*buffer);\n        *buffer = NULL;\n        *bytesRead = -1;\n        if (errormsg) *errormsg = formatToString16(\"%s during fread(), read %d bytes for %s\", strerror(err), *bytesRead, filename);\n    }\n\n    // Close the file\n    fclose(f);\n\n    return err;\n}\n\n}  // namespace binder\n\n\n////////////////////////////////////////////////////////////\n// General\n////////////////////////////////////////////////////////////\n\nstatic void systemService() {\n    xposed::setProcessName(\"xposed_service_system\");\n    xposed::dropCapabilities();\n\n#if XPOSED_WITH_SELINUX\n    if (xposed->isSELinuxEnabled) {\n        if (setcon(ctx_system) != 0) {\n            ALOGE(\"Could not switch to %s context\", ctx_system);\n            exit(EXIT_FAILURE);\n        }\n    }\n#endif  // XPOSED_WITH_SELINUX\n\n    // Initialize the system service\n    sp<IServiceManager> sm(defaultServiceManager());\n#if PLATFORM_SDK_VERSION >= 16\n    status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true), true);\n#else\n    status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true));\n#endif\n    if (err != NO_ERROR) {\n        ALOGE(\"Error %d while adding system service %s\", err, XPOSED_BINDER_SYSTEM_SERVICE_NAME);\n        exit(EXIT_FAILURE);\n    }\n\n    sp<ProcessState> ps(ProcessState::self());\n    ps->startThreadPool();\n#if PLATFORM_SDK_VERSION >= 18\n    ps->giveThreadPoolName();\n#endif\n    IPCThreadState::self()->joinThreadPool();\n}\n\nstatic void appService() {\n    xposed::setProcessName(\"xposed_service_app\");\n    if (!xposed::switchToXposedInstallerUidGid()) {\n        exit(EXIT_FAILURE);\n    }\n    xposed::dropCapabilities();\n\n#if XPOSED_WITH_SELINUX\n    if (xposed->isSELinuxEnabled) {\n        if (setcon(ctx_app) != 0) {\n            ALOGE(\"Could not switch to %s context\", ctx_app);\n            exit(EXIT_FAILURE);\n        }\n    }\n#endif  // XPOSED_WITH_SELINUX\n\n    // We have to register the app service by using the already running system service as a proxy\n    sp<IServiceManager> sm(defaultServiceManager());\n    sp<IBinder> systemBinder = sm->getService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME));\n    sp<binder::IXposedService> xposedSystemService = interface_cast<binder::IXposedService>(systemBinder);\n    status_t err = xposedSystemService->addService(String16(XPOSED_BINDER_APP_SERVICE_NAME), new binder::XposedService(false), true);\n\n    // Check result for the app service registration\n    if (err != NO_ERROR) {\n        ALOGE(\"Error %d while adding app service %s\", err, XPOSED_BINDER_APP_SERVICE_NAME);\n        exit(EXIT_FAILURE);\n    }\n\n#if XPOSED_WITH_SELINUX\n    // Initialize the memory-based Zygote service\n    if (xposed->isSELinuxEnabled) {\n        pthread_t thMemBased;\n        if (pthread_create(&thMemBased, NULL, &membased::looper, NULL) != 0) {\n            ALOGE(\"Could not create thread for memory-based service: %s\", strerror(errno));\n            exit(EXIT_FAILURE);\n        }\n    }\n#endif  // XPOSED_WITH_SELINUX\n\n    sp<ProcessState> ps(ProcessState::self());\n    ps->startThreadPool();\n#if PLATFORM_SDK_VERSION >= 18\n    ps->giveThreadPoolName();\n#endif\n    IPCThreadState::self()->joinThreadPool();\n}\n\nbool checkMembasedRunning() {\n    // Ensure that the memory based service is running\n    if (!membased::waitForRunning(5)) {\n        ALOGE(\"Xposed's Zygote service is not running, cannot work without it\");\n        return false;\n    }\n\n    return true;\n}\n\nbool startAll() {\n    if (xposed->isSELinuxEnabled && !membased::init()) {\n        return false;\n    }\n\n    // system context service\n    pid_t pid;\n    if ((pid = fork()) < 0) {\n        ALOGE(\"Fork for Xposed service in system context failed: %s\", strerror(errno));\n        return false;\n    } else if (pid == 0) {\n        systemService();\n        // Should never reach this point\n        exit(EXIT_FAILURE);\n    }\n\n    // app context service\n    if ((pid = fork()) < 0) {\n        ALOGE(\"Fork for Xposed service in app context failed: %s\", strerror(errno));\n        return false;\n    } else if (pid == 0) {\n        appService();\n        // Should never reach this point\n        exit(EXIT_FAILURE);\n    }\n\n    if (xposed->isSELinuxEnabled && !checkMembasedRunning()) {\n        return false;\n    }\n\n    return true;\n}\n\n#if XPOSED_WITH_SELINUX\nbool startMembased() {\n    if (!xposed->isSELinuxEnabled) {\n        return true;\n    }\n\n    if (!membased::init()) {\n        return false;\n    }\n\n    pid_t pid;\n    if ((pid = fork()) < 0) {\n        ALOGE(\"Fork for Xposed Zygote service failed: %s\", strerror(errno));\n        return false;\n    } else if (pid == 0) {\n        xposed::setProcessName(\"xposed_zygote_service\");\n        if (!xposed::switchToXposedInstallerUidGid()) {\n            exit(EXIT_FAILURE);\n        }\n        xposed::dropCapabilities();\n        if (setcon(ctx_app) != 0) {\n            ALOGE(\"Could not switch to %s context\", ctx_app);\n            exit(EXIT_FAILURE);\n        }\n        membased::looper(NULL);\n        // Should never reach this point\n        exit(EXIT_FAILURE);\n    }\n\n    return checkMembasedRunning();\n}\n#endif  // XPOSED_WITH_SELINUX\n\n}  // namespace service\n}  // namespace xposed\n"
  },
  {
    "path": "xposed_service.h",
    "content": "#ifndef XPOSED_SERVICE_H_\n#define XPOSED_SERVICE_H_\n\n#include <sys/stat.h>\n#include <unistd.h>\n\nnamespace xposed {\nnamespace service {\n    bool startAll();\n\n#if XPOSED_WITH_SELINUX\n    bool startMembased();\n\n    namespace membased {\n        int accessFile(const char* path, int mode);\n        int statFile(const char* path, struct stat* stat);\n        char* readFile(const char* path, int* bytesRead);\n        void restrictMemoryInheritance();\n    }  // namespace membased\n#endif  // XPOSED_WITH_SELINUX\n\n}  // namespace service\n\nstatic inline int zygote_access(const char *pathname, int mode) {\n#if XPOSED_WITH_SELINUX\n    if (xposed->isSELinuxEnabled)\n        return xposed::service::membased::accessFile(pathname, mode);\n#endif  // XPOSED_WITH_SELINUX\n\n    return access(pathname, mode);\n}\n\n}  // namespace xposed\n\n#endif /* XPOSED_SERVICE_H_ */\n"
  },
  {
    "path": "xposed_shared.h",
    "content": "/**\n * These declarations are needed for both app_process and the libraries.\n */\n\n#ifndef XPOSED_SHARED_H_\n#define XPOSED_SHARED_H_\n\n#include <sys/stat.h>\n\n#include \"cutils/log.h\"\n#include \"jni.h\"\n\n#ifndef ALOG\n#define ALOG  LOG\n#define ALOGD LOGD\n#define ALOGD LOGD\n#define ALOGE LOGE\n#define ALOGI LOGI\n#define ALOGV LOGV\n#endif\n\n#if PLATFORM_SDK_VERSION >= 24\n#define XPOSED_DIR \"/data/user_de/0/de.robv.android.xposed.installer/\"\n#else\n#define XPOSED_DIR \"/data/data/de.robv.android.xposed.installer/\"\n#endif\n\nnamespace xposed {\n\nstruct XposedShared {\n    // Global variables\n    bool zygote;\n    bool startSystemServer;\n    const char* startClassName;\n    uint32_t xposedVersionInt;\n    bool isSELinuxEnabled;\n    bool isSELinuxEnforcing;\n    uid_t installer_uid;\n    gid_t installer_gid;\n\n    // Provided by runtime-specific library, used by executable\n    void (*onVmCreated)(JNIEnv* env);\n\n#if XPOSED_WITH_SELINUX\n    // Provided by the executable, used by runtime-specific library\n    int (*zygoteservice_accessFile)(const char* path, int mode);\n    int (*zygoteservice_statFile)(const char* path, struct stat* st);\n    char* (*zygoteservice_readFile)(const char* path, int* bytesRead);\n#endif\n};\n\nextern XposedShared* xposed;\n\n} // namespace xposed\n\n#endif // XPOSED_SHARED_H_\n"
  }
]