diff --git a/Lite_version/SKRoot_Lite(2026-4-8).apk b/Lite_version/SKRoot_Lite(2026-4-8).apk
new file mode 100644
index 00000000..e4da4448
Binary files /dev/null and b/Lite_version/SKRoot_Lite(2026-4-8).apk differ
diff --git a/Lite_version/patch_kernel_root(2026-4-8).exe b/Lite_version/patch_kernel_root(2026-4-8).exe
new file mode 100644
index 00000000..96755c6d
Binary files /dev/null and b/Lite_version/patch_kernel_root(2026-4-8).exe differ
diff --git a/Lite_version/src/PermissionManager/.gitignore b/Lite_version/src/PermissionManager/.gitignore
new file mode 100644
index 00000000..a3d88905
--- /dev/null
+++ b/Lite_version/src/PermissionManager/.gitignore
@@ -0,0 +1,16 @@
+*.iml
+.idea
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Lite_version/src/PermissionManager/app/.gitignore b/Lite_version/src/PermissionManager/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/build.gradle b/Lite_version/src/PermissionManager/app/build.gradle
new file mode 100644
index 00000000..56f79c85
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/build.gradle
@@ -0,0 +1,53 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk 31
+
+ defaultConfig {
+ applicationId "com.linux.permissionmanager"
+ minSdk 26
+ targetSdk 31
+ versionCode 1
+ versionName "2026-4-8"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags '-std=c++20'
+ abiFilters "arm64-v8a"
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ externalNativeBuild {
+ cmake {
+ path file('src/main/cpp/CMakeLists.txt')
+ version '3.18.1'
+ }
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation 'com.google.android.material:material:1.5.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/proguard-rules.pro b/Lite_version/src/PermissionManager/app/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java b/Lite_version/src/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..e63884ad
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.linux.permissionmanager;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.linux.permissionmanager", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/AndroidManifest.xml b/Lite_version/src/PermissionManager/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..ac7de7ed
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/cpp/CMakeLists.txt b/Lite_version/src/PermissionManager/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 00000000..8bdb5531
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,59 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.18.1)
+
+# Declares and names the project.
+
+project("permissionmanager")
+
+set(OTHER_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/cJSON.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/native-lib.cpp"
+ )
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+
+add_library( # Sets the name of the library.
+ permissionmanager
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ ${OTHER_SRC})
+
+# Searches for a specified prebuilt library and stores the path as a
+# variable. Because CMake includes system libraries in the search path by
+# default, you only need to specify the name of the public NDK library
+# you want to add. CMake verifies that the library exists before
+# completing its build.
+
+find_library( # Sets the name of the path variable.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # you want CMake to locate.
+ log)
+
+# Specifies libraries CMake should link to your target library. You
+# can link multiple libraries, such as libraries you define in this
+# build script, prebuilt third-party libraries, or system libraries.
+
+target_include_directories(
+ permissionmanager
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../testRoot/
+)
+
+target_link_libraries( # Specifies the target library.
+ permissionmanager
+
+ # Links the target library to the log library
+ # included in the NDK.
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../testRoot/kernel_root_kit/lib/libkernel_root_kit_static.a
+ ${log-lib})
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.cpp b/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.cpp
new file mode 100644
index 00000000..faa3e297
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.cpp
@@ -0,0 +1,3129 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef ENABLE_LOCALES
+#include
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0/0.0
+#endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+ if (!cJSON_IsString(item))
+ {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+ if (!cJSON_IsNumber(item))
+ {
+ return (double) NAN;
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 16)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void (CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+ return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+ free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*)hooks->allocate(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (hooks == NULL)
+ {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL)
+ {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL)
+ {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+ {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+ if (node)
+ {
+ memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL)
+ {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+ {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+ {
+ global_hooks.deallocate(item->valuestring);
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ global_hooks.deallocate(item->string);
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char) lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
+ * of the current locale (for strtod)
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
+ break;
+
+ case '.':
+ number_c_string[i] = decimal_point;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ number_c_string[i] = '\0';
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
+ {
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= INT_MAX)
+ {
+ object->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ object->valueint = INT_MIN;
+ }
+ else
+ {
+ object->valueint = (int)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+ if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+ {
+ return NULL;
+ }
+ /* return NULL if the object is corrupted */
+ if (object->valuestring == NULL)
+ {
+ return NULL;
+ }
+ if (strlen(valuestring) <= strlen(object->valuestring))
+ {
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ if (object->valuestring != NULL)
+ {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct
+{
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL))
+ {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length))
+ {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > INT_MAX)
+ {
+ /* sizes bigger than INT_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length)
+ {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (INT_MAX / 2))
+ {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX)
+ {
+ newsize = INT_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ }
+ else
+ {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+ if (!newbuffer)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL))
+ {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d))
+ {
+ length = sprintf((char*)number_buffer, "null");
+ }
+ else if(d == (double)item->valueint)
+ {
+ length = sprintf((char*)number_buffer, "%d", item->valueint);
+ }
+ else
+ {
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+ length = sprintf((char*)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+ {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char*)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+ {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++)
+ {
+ if (number_buffer[i] == decimal_point)
+ {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9'))
+ {
+ h += (unsigned int) input[i] - '0';
+ }
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
+ {
+ h += (unsigned int) 10 + input[i] - 'A';
+ }
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
+ {
+ h += (unsigned int) 10 + input[i] - 'a';
+ }
+ else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3)
+ {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+ {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+ {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+ {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+ {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ }
+ else
+ {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
+ {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ }
+ else if (codepoint < 0x800)
+ {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ }
+ else if (codepoint < 0x10000)
+ {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ }
+ else
+ {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+ {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1)
+ {
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ }
+ else
+ {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
+ {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+ {
+ /* is escape sequence */
+ if (input_end[0] == '\\')
+ {
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+ {
+ /* prevent buffer overflow when last input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+ {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end)
+ {
+ if (*input_pointer != '\\')
+ {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else
+ {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
+ {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
+ {
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char*)output;
+
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL)
+ {
+ input_buffer->hooks.deallocate(output);
+ }
+
+ if (input_pointer != NULL)
+ {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL)
+ {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
+ {
+ switch (*input_pointer)
+ {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32)
+ {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
+ {
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+ {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+ {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ }
+ else
+ {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
+ {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+ return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0))
+ {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+ {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+ {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+ {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value)
+ {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length)
+ {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char*)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+ {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+ {
+ goto fail;
+ }
+ }
+ if (return_parse_end)
+ {
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL)
+ {
+ error local_error;
+ local_error.json = (const unsigned char*)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length)
+ {
+ local_error.position = buffer.offset;
+ }
+ else if (buffer.length > 0)
+ {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL)
+ {
+ *return_parse_end = (const char*)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer)
+ {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p))
+ {
+ global_hooks.deallocate(p.buffer);
+ return NULL;
+ }
+
+ return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if ((length < 0) || (buffer == NULL))
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+ {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+ {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+ {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+ {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+ {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+ {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL))
+ {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF)
+ {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw:
+ {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL)
+ {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[')
+ {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+ {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+ {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next)
+ {
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if(output_buffer->format)
+ {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+ {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+ {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+ {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item)
+ {
+ if (output_buffer->format)
+ {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (current_item->next)
+ {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (output_buffer->format)
+ {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL)
+ {
+ return 0;
+ }
+
+ child = array->child;
+
+ while(child != NULL)
+ {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL)
+ {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0))
+ {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0)
+ {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive)
+ {
+ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL)
+ {
+ return NULL;
+ }
+
+ memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item))
+ {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL)
+ {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ }
+ else
+ {
+ /* append to the end */
+ if (child->prev)
+ {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+ return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+ {
+ return false;
+ }
+
+ if (constant_key)
+ {
+ new_key = (char*)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ }
+ else
+ {
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+ if (new_key == NULL)
+ {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL)
+ {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false))
+ {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
+ {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
+ {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+ {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
+ {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
+ {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+ {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
+ {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false))
+ {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+ if ((parent == NULL) || (item == NULL))
+ {
+ return NULL;
+ }
+
+ if (item != parent->child)
+ {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL)
+ {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child)
+ {
+ /* first element */
+ parent->child = item->next;
+ }
+ else if (item->next == NULL)
+ {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0 || newitem == NULL)
+ {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL)
+ {
+ return add_item_to_array(array, newitem);
+ }
+
+ if (after_inserted != array->child && newitem->prev == NULL) {
+ /* return false if after_inserted is a corrupted array item */
+ return false;
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+ if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
+ {
+ return false;
+ }
+
+ if (replacement == item)
+ {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL)
+ {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item)
+ {
+ if (parent->child->prev == parent->child)
+ {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ }
+ else
+ { /*
+ * To find the last item in array quickly, we use prev in array.
+ * We can't modify the last item's next pointer where this item was the parent's child
+ */
+ if (replacement->prev != NULL)
+ {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL)
+ {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+ {
+ cJSON_free(replacement->string);
+ }
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if (replacement->string == NULL)
+ {
+ return false;
+ }
+
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (num <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL)
+ {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char*)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Raw;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type=cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item)
+ {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateString(strings[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p,n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item)
+ {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem)
+ {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+ if (!newitem->valuestring)
+ {
+ goto fail;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+ if (!newitem->string)
+ {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL)
+ {
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild)
+ {
+ goto fail;
+ }
+ if (next != NULL)
+ {
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child)
+ {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
+ {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output) {
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL)
+ {
+ return;
+ }
+
+ while (json[0] != '\0')
+ {
+ switch (json[0])
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/')
+ {
+ skip_oneline_comment(&json);
+ }
+ else if (json[1] == '*')
+ {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char**)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+ {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF)
+ {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b)
+ {
+ return true;
+ }
+
+ switch (a->type & 0xFF)
+ {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble))
+ {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
+ {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0)
+ {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array:
+ {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);)
+ {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.h b/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.h
new file mode 100644
index 00000000..2628d763
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/cpp/cJSON.h
@@ -0,0 +1,300 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 16
+
+#include
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ char *valuestring;
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+ int valueint;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
+#define cJSON_SetBoolValue(object, boolValue) ( \
+ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
+ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
+ cJSON_Invalid\
+)
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Lite_version/src/PermissionManager/app/src/main/cpp/native-lib.cpp b/Lite_version/src/PermissionManager/app/src/main/cpp/native-lib.cpp
new file mode 100644
index 00000000..218aea17
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/cpp/native-lib.cpp
@@ -0,0 +1,280 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "kernel_root_kit/include/rootkit_umbrella.h"
+#include "urlEncodeUtils.h"
+#include "cJSON.h"
+using namespace std;
+
+std::string g_last_su_file_path;
+
+static string jstringToStr(JNIEnv* env, jstring jstring1) {
+ const char *str1 = env->GetStringUTFChars(jstring1, 0);
+ string s = str1;
+ env->ReleaseStringUTFChars(jstring1, str1);
+ return s;
+}
+
+static string urlEncodeToStr(string str) {
+ size_t len = str.length();
+ size_t max_encoded_len = 3 * len + 1;
+ std::vector buf(max_encoded_len);
+ url_encode(const_cast(str.c_str()), (char*)buf.data());
+ return (char*)buf.data();
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_testRoot(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey) {
+ string strRootKey = jstringToStr(env, rootKey);
+
+ std::string result = kernel_root::get_root_test_report(strRootKey.c_str());
+ return env->NewStringUTF(result.c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_runRootCmd(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring cmd) {
+ string strRootKey = jstringToStr(env, rootKey);
+ string strCmd = jstringToStr(env, cmd);
+
+ string result;
+ KRootErr err = kernel_root::run_root_cmd(strRootKey.c_str(), strCmd.c_str(), result);
+ stringstream sstr;
+ sstr << "run_root_cmd " << to_string(err).c_str() << ", result:" << result;
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_rootExecProcessCmd(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring cmd) {
+ string strRootKey = jstringToStr(env, rootKey);
+ string strCmd = jstringToStr(env, cmd);
+
+ KRootErr err = kernel_root::root_exec_process(strRootKey.c_str(), strCmd.c_str());
+
+ stringstream sstr;
+ sstr << "root_exec_process " << to_string(err).c_str();
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_installSu(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey) {
+ string strRootKey = jstringToStr(env, rootKey);
+
+ //安装su工具套件
+ std::string su_hide_full_path;
+ KRootErr err = kernel_root::install_su(strRootKey.c_str(), su_hide_full_path);
+
+ stringstream sstr;
+ sstr << "install su " << to_string(err).c_str() << ", su_hide_full_path:" << su_hide_full_path << std::endl;
+ g_last_su_file_path = su_hide_full_path;
+ if (is_ok(err)) sstr << "install_su done."<< std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_getLastSuFilePath(
+ JNIEnv* env,
+ jclass /* this */) {
+ return env->NewStringUTF(g_last_su_file_path.c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_uninstallSu(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey) {
+ string strRootKey = jstringToStr(env, rootKey);
+
+ stringstream sstr;
+
+ KRootErr err = kernel_root::uninstall_su(strRootKey.c_str());
+ sstr << "uninstall_su " << to_string(err).c_str() << std::endl;
+ if (is_failed(err)) return env->NewStringUTF(sstr.str().c_str());
+ g_last_su_file_path.clear();
+ sstr << "uninstall_su done.";
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_autoSuEnvInject(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring targetProcessCmdline) {
+
+ if(g_last_su_file_path.empty()) return env->NewStringUTF("【错误】请先安装部署su");
+ string strRootKey = jstringToStr(env, rootKey);
+ string strTargetProcessCmdline = jstringToStr(env, targetProcessCmdline);
+
+ stringstream sstr;
+
+ //杀光所有历史进程
+ std::set out;
+ KRootErr err = kernel_root::find_all_cmdline_process(strRootKey.c_str(), strTargetProcessCmdline.c_str(), out);
+ sstr << "find_all_cmdline_process " << to_string(err).c_str() << ", cnt:"<NewStringUTF(sstr.str().c_str());
+ std::string kill_cmd;
+ for (pid_t t : out) {
+ err = kernel_root::kill_process(strRootKey.c_str(), t);
+ sstr << "kill_ret " << to_string(err).c_str() << std::endl;
+ if (is_failed(err)) return env->NewStringUTF(sstr.str().c_str());
+ }
+ pid_t pid;
+ err = kernel_root::wait_and_find_cmdline_process(strRootKey.c_str(), strTargetProcessCmdline.c_str(), 60*1000, pid);
+ std::string su_dir_path = is_ok(err) ? (std::filesystem::path(g_last_su_file_path).parent_path().string() + "/") : "";
+ sstr << "auto_su_env_inject("<< to_string(err).c_str() <<", " << su_dir_path <<")" << std::endl;
+ if (is_failed(err)) return env->NewStringUTF(sstr.str().c_str());
+ err = kernel_root::inject_process_env64_PATH_wrapper(strRootKey.c_str(), pid, su_dir_path.c_str());
+ sstr << "auto_su_env_inject ret val:" << to_string(err).c_str() << std::endl;
+ if (is_failed(err)) return env->NewStringUTF(sstr.str().c_str());
+ sstr << "auto_su_env_inject done.";
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_getAllCmdlineProcess(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey) {
+ string strRootKey = jstringToStr(env, rootKey);
+
+ std::stringstream ss;
+ std::map pid_map;
+ KRootErr err = kernel_root::get_all_cmdline_process(strRootKey.c_str(), pid_map);
+ if (is_failed(err)) {
+ ss << "get_all_cmdline_process " << to_string(err).c_str() << std::endl;
+ return env->NewStringUTF(ss.str().c_str());
+ }
+ cJSON *root = cJSON_CreateArray();
+ for (auto &iter : pid_map) {
+ cJSON *item = cJSON_CreateObject();
+ string encodeName = urlEncodeToStr(iter.second);
+ cJSON_AddNumberToObject(item, "pid", iter.first);
+ cJSON_AddStringToObject(item, "name", encodeName.c_str());
+ cJSON_AddItemToArray(root, item);
+ }
+ ss << cJSON_Print(root);
+ cJSON_Delete(root);
+ return env->NewStringUTF(ss.str().c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_parasitePrecheckApp(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring targetProcessCmdline) {
+ string strRootKey = jstringToStr(env, rootKey);
+ string strTargetProcessCmdline = jstringToStr(env, targetProcessCmdline);
+
+ stringstream sstr;
+ std::set test_pid;
+ KRootErr err = kernel_root::find_all_cmdline_process(strRootKey.c_str(), strTargetProcessCmdline.c_str(), test_pid);
+ if (is_failed(err)) {
+ sstr << "find_all_cmdline_process " << to_string(err).c_str() << ", cnt:"<< test_pid.size() << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ if (test_pid.size() == 0) {
+ sstr << "目标进程不存在" << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+
+ std::map dynlibPathList;
+ err = kernel_root::parasite_precheck_app(strRootKey.c_str(), strTargetProcessCmdline.c_str(), dynlibPathList);
+ if (is_failed(err)) {
+ sstr << "parasite_precheck_app ret val:" << to_string(err).c_str() << std::endl;
+ if(err == KRootErr::ERR_EXIST_32BIT) sstr << "此目标APP为32位应用,无法寄生" << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+
+ if (!dynlibPathList.size()) {
+ sstr << "无法检测到目标APP的JNI环境,目标APP暂不可被寄生;您可重新运行目标APP后重试;或将APP进行手动加固(加壳),因为加固(加壳)APP后,APP会被产生JNI环境,方可寄生!" << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+
+ cJSON *root = cJSON_CreateArray();
+ for (auto &iter : dynlibPathList) {
+ cJSON *item = cJSON_CreateObject();
+ string encodeName = urlEncodeToStr(iter.first);
+ cJSON_AddStringToObject(item, "name", encodeName.c_str());
+ cJSON_AddNumberToObject(item, "status", iter.second);
+ cJSON_AddItemToArray(root, item);
+ }
+ sstr << cJSON_Print(root);
+ cJSON_Delete(root);
+ return env->NewStringUTF(sstr.str().c_str());
+
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_parasiteImplantApp(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring targetProcessCmdline,
+ jstring targetSoFullPath) {
+ string strRootKey = jstringToStr(env, rootKey);
+ string strTargetProcessCmdline = jstringToStr(env, targetProcessCmdline);
+ string strTargetSoFullPath = jstringToStr(env, targetSoFullPath);
+
+ stringstream sstr;
+ KRootErr err = kernel_root::parasite_implant_app(strRootKey.c_str(), strTargetProcessCmdline.c_str(), strTargetSoFullPath.c_str());
+ if (is_failed(err)) {
+ sstr << "parasite_implant_app " << to_string(err).c_str() << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ sstr << "parasite_implant_app done.";
+ return env->NewStringUTF(sstr.str().c_str());
+
+}
+
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_bridge_NativeBridge_parasiteImplantSuEnv(
+ JNIEnv* env,
+ jclass /* this */,
+ jstring rootKey,
+ jstring targetProcessCmdline,
+ jstring targetSoFullPath) {
+ if(g_last_su_file_path.empty()) {
+ return env->NewStringUTF("【错误】请先安装部署su");
+ }
+ string strRootKey = jstringToStr(env, rootKey);
+ string strTargetProcessCmdline = jstringToStr(env, targetProcessCmdline);
+ string strTargetSoFullPath = jstringToStr(env, targetSoFullPath);
+
+ std::string su_dir_path = std::filesystem::path(g_last_su_file_path).parent_path().string() + "/";
+
+ stringstream sstr;
+ KRootErr err = kernel_root::parasite_implant_su_env(strRootKey.c_str(), strTargetProcessCmdline.c_str(), strTargetSoFullPath.c_str(), su_dir_path.c_str());
+ if (is_failed(err)) {
+ sstr << "parasite_implant_su_env " << to_string(err).c_str() << std::endl;
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ sstr << "parasite_implant_su_env done.";
+ return env->NewStringUTF(sstr.str().c_str());
+
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/cpp/urlEncodeUtils.h b/Lite_version/src/PermissionManager/app/src/main/cpp/urlEncodeUtils.h
new file mode 100644
index 00000000..0d1482f7
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/cpp/urlEncodeUtils.h
@@ -0,0 +1,74 @@
+#ifndef URL_ENCODE_UTILS_H_
+#define URL_ENCODE_UTILS_H_
+#include
+#include
+
+static inline char to_hex(char code) {
+ static char hex[] = "0123456789ABCDEF";
+ return hex[code & 15];
+}
+static inline char from_hex(char ch) {
+ return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
+}
+
+/*
+//使用例子
+int main() {
+ char str[] = "你好,世界";
+ char encoded_str[256];
+ url_encode(str, encoded_str);
+ printf("Encoded URL: %s\n", encoded_str);
+ return 0;
+}
+*/
+static void url_encode(char *str, char *encoded_str) {
+ char *pstr = str, *buf = encoded_str;
+ while (*pstr) {
+ unsigned char c = *pstr;
+ if (c <= 0x7F) { // ASCII
+ if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
+ *buf++ = c;
+ } else if (c == ' ') {
+ *buf++ = '+';
+ } else {
+ *buf++ = '%', *buf++ = to_hex(c >> 4), *buf++ = to_hex(c & 15);
+ }
+ } else { // Non-ASCII
+ while (c) {
+ *buf++ = '%', *buf++ = to_hex(c >> 4), *buf++ = to_hex(c & 15);
+ c = *(++pstr);
+ }
+ continue;
+ }
+ pstr++;
+ }
+ *buf = '\0';
+}
+/*
+//使用例子
+int main() {
+ char url[] = "%E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C"; // "你好,世界"的URL编码
+ char decoded_str[256];
+ url_decode(url, decoded_str);
+ printf("Decoded URL: %s\n", decoded_str);
+ return 0;
+}
+*/
+static void url_decode(char *str, char *decoded_str) {
+ char *pstr = str, *buf = decoded_str;
+ while (*pstr) {
+ if (*pstr == '%') {
+ if (pstr[1] && pstr[2]) {
+ *buf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
+ pstr += 2;
+ }
+ } else if (*pstr == '+') {
+ *buf++ = ' ';
+ } else {
+ *buf++ = *pstr;
+ }
+ pstr++;
+ }
+ *buf = '\0';
+}
+#endif
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/ic_launcher-playstore.png b/Lite_version/src/PermissionManager/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 00000000..c8e58bd5
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/ic_launcher-playstore.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/AppSettings.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/AppSettings.java
new file mode 100644
index 00000000..c065f5e7
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/AppSettings.java
@@ -0,0 +1,69 @@
+package com.linux.permissionmanager;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class AppSettings {
+ private static SharedPreferences preferences;
+
+ public static void init(Context context) {
+ if (preferences == null) {
+ preferences = context.getSharedPreferences("AppSettings", Context.MODE_PRIVATE);
+ }
+ }
+
+ public static void setBoolean(String key, boolean value) {
+ preferences.edit().putBoolean(key, value).apply();
+ }
+
+ public static boolean getBoolean(String key, boolean defaultValue) {
+ try {
+ return preferences.getBoolean(key, defaultValue);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return defaultValue;
+ }
+
+ public static void setString(String key, String value) {
+ preferences.edit().putString(key, value).apply();
+ }
+
+ public static String getString(String key, String defaultValue) {
+ try {
+ return preferences.getString(key, defaultValue);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return defaultValue;
+ }
+
+ public static void setInt(String key, int value) {
+ preferences.edit().putInt(key, value).apply();
+ }
+
+ public static int getInt(String key, int defaultValue) {
+ try {
+ return preferences.getInt(key, defaultValue);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return defaultValue;
+ }
+
+ public static void setStringSet(String key, Set value) {
+ preferences.edit().putStringSet(key, value).apply();
+ }
+
+ public static Set getStringSet(String key, Set defaultValue) {
+ try {
+ return preferences.getStringSet(key, defaultValue);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return defaultValue != null ? defaultValue : Collections.emptySet();
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java
new file mode 100644
index 00000000..c27147d3
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java
@@ -0,0 +1,419 @@
+package com.linux.permissionmanager;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.linux.permissionmanager.bridge.NativeBridge;
+import com.linux.permissionmanager.helper.SelectAppDlg;
+import com.linux.permissionmanager.helper.SelectFileDlg;
+import com.linux.permissionmanager.model.SelectAppItem;
+import com.linux.permissionmanager.model.SelectFileItem;
+import com.linux.permissionmanager.utils.ClipboardUtils;
+import com.linux.permissionmanager.utils.DialogUtils;
+import com.linux.permissionmanager.utils.GetAppListPermissionHelper;
+import com.linux.permissionmanager.utils.UrlIntentUtils;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MainActivity extends AppCompatActivity implements View.OnClickListener {
+ private String rootKey = "";
+ private String lastInputCmd = "id";
+ private String lastInputRootExecPath = "";
+ private ProgressDialog m_loadingDlg = null;
+
+ private EditText console_edit;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ AppSettings.init(this);
+
+ rootKey = AppSettings.getString("rootKey", rootKey);
+ lastInputCmd = AppSettings.getString("lastInputCmd", lastInputCmd);
+ lastInputRootExecPath = AppSettings.getString("lastInputRootExecPath", lastInputRootExecPath);
+ checkGetAppListPermission();
+ showInputRootKeyDlg();
+
+ Button test_root_btn = findViewById(R.id.test_root_btn);
+ Button run_root_cmd_btn = findViewById(R.id.run_root_cmd_btn);
+ Button root_exec_process_btn = findViewById(R.id.root_exec_process_btn);
+ Button su_env_install_btn = findViewById(R.id.su_env_install_btn);
+ Button su_env_inject_btn = findViewById(R.id.su_env_inject_btn);
+ Button clean_su_btn = findViewById(R.id.clean_su_btn);
+ Button implant_app_btn = findViewById(R.id.implant_app_btn);
+ Button copy_info_btn = findViewById(R.id.copy_info_btn);
+ Button clean_info_btn = findViewById(R.id.clean_info_btn);
+ console_edit = findViewById(R.id.console_edit);
+
+ test_root_btn.setOnClickListener(this);
+ run_root_cmd_btn.setOnClickListener(this);
+ root_exec_process_btn.setOnClickListener(this);
+ su_env_install_btn.setOnClickListener(this);
+ su_env_inject_btn.setOnClickListener(this);
+ clean_su_btn.setOnClickListener(this);
+ implant_app_btn.setOnClickListener(this);
+ copy_info_btn.setOnClickListener(this);
+ clean_info_btn.setOnClickListener(this);
+ initLink();
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.test_root_btn:
+ appendConsoleMsg(NativeBridge.testRoot(rootKey));
+ break;
+ case R.id.run_root_cmd_btn:
+ showInputRootCmdDlg();
+ break;
+ case R.id.root_exec_process_btn:
+ showInputRootExecProcessPathDlg();
+ break;
+ case R.id.su_env_install_btn:
+ onClickSuEnvInstallBtn();
+ break;
+ case R.id.su_env_inject_btn:
+ showSelectSuInjectModeDlg();
+ break;
+ case R.id.clean_su_btn:
+ onClickSuEnvUninstallBtn();
+ break;
+ case R.id.implant_app_btn:
+ onClickImplantAppBtn();
+ break;
+ case R.id.copy_info_btn:
+ copyConsoleMsg();
+ break;
+ case R.id.clean_info_btn:
+ cleanConsoleMsg();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void initLink() {
+ TextView link_tv = findViewById(R.id.link_tv);
+ TextView tg_tv = findViewById(R.id.tg_tv);
+ link_tv.setOnClickListener(v -> { UrlIntentUtils.openUrl(this, link_tv.getText().toString()); });
+ tg_tv.setOnClickListener(v -> { UrlIntentUtils.openUrl(this, link_tv.getText().toString()); });
+ }
+
+ private void showSkrootStatus() {
+ appendConsoleMsg("版本:" + BuildConfig.VERSION_NAME);
+ }
+
+ public void showInputRootKeyDlg() {
+ Handler inputCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ String text = (String)msg.obj;
+ rootKey = text;
+ AppSettings.setString("rootKey", rootKey);
+ showSkrootStatus();
+ super.handleMessage(msg);
+ }
+ };
+ DialogUtils.showInputDlg(this, rootKey,"请输入Root权限的KEY", null, inputCallback, null);
+ }
+ private void checkGetAppListPermission() {
+ if(GetAppListPermissionHelper.getPermissions(this)) return;
+ DialogUtils.showCustomDialog(this,"权限申请","请授予读取APP列表权限,再重新打开",null,"确定", (dialog, which) -> {
+ dialog.dismiss();
+ finish();
+ },null, null
+ );
+ }
+
+ public void showInputRootCmdDlg() {
+ Handler inputCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ String text = (String)msg.obj;
+ lastInputCmd = text;
+ AppSettings.setString("lastInputCmd", lastInputCmd);
+ appendConsoleMsg(text + "\n" + NativeBridge.runRootCmd(rootKey, text));
+ super.handleMessage(msg);
+ }
+ };
+ DialogUtils.showInputDlg(this, lastInputCmd, "请输入Root命令", null, inputCallback, null);
+ }
+
+ public void showInputRootExecProcessPathDlg() {
+ Handler inputCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ String text = (String)msg.obj;
+
+ lastInputRootExecPath = text;
+ AppSettings.setString("lastInputRootExecPath", lastInputRootExecPath);
+ appendConsoleMsg(text + "\n" + NativeBridge.rootExecProcessCmd(rootKey, text));
+ super.handleMessage(msg);
+ }
+ };
+ Handler helperCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ DialogUtils.showMsgDlg(MainActivity.this,"帮助", "请将JNI可执行文件放入/data内任意目录并且赋予777权限,如/data/app/com.xx,然后输入文件路径,即可直接执行,如:\n/data/com.xx/aaa\n", null);
+ super.handleMessage(msg);
+ }
+ };
+ DialogUtils.showInputDlg(this, lastInputRootExecPath, "请输入Linux可运行文件的位置", "指导", inputCallback, helperCallback);
+ DialogUtils.showMsgDlg(this,"提示", "本功能是以Root身份直接运行程序,可避免产生su、sh等多余驻留后台进程,能最大程度上避免侦测", null);
+ }
+
+ public void onClickSuEnvInstallBtn() {
+ String insRet = NativeBridge.installSu(rootKey);
+ appendConsoleMsg(insRet);
+ if(insRet.indexOf("install_su done.") != -1) {
+ String suFilePath = NativeBridge.getLastSuFilePath();
+ appendConsoleMsg("last_su_file_path:" + suFilePath);
+ DialogUtils.showMsgDlg(this,"温馨提示","安装部署su成功,su路径已复制到剪贴板。", null);
+ ClipboardUtils.copyText(this, suFilePath);
+ appendConsoleMsg("安装部署su成功,su路径已复制到剪贴板");
+ }
+ }
+
+ public void onClickSuEnvUninstallBtn() {
+ appendConsoleMsg(NativeBridge.uninstallSu(rootKey));
+ ClipboardUtils.copyText(this, "");
+ }
+
+ private void suTempInject() {
+ Handler selectInjectSuAppCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ SelectAppItem app = (SelectAppItem) msg.obj;
+ if (m_loadingDlg == null) {
+ m_loadingDlg = new ProgressDialog(MainActivity.this);
+ m_loadingDlg.setCancelable(false);
+ }
+ m_loadingDlg.setTitle("");
+ m_loadingDlg.setMessage("请现在手动启动APP [" + app.getShowName(MainActivity.this) + "]");
+ m_loadingDlg.show();
+ startSuTempInjectThread(app);
+ super.handleMessage(msg);
+ }
+ };
+ SelectAppDlg.showSelectAppDlg(MainActivity.this, rootKey, selectInjectSuAppCallback);
+ }
+
+ private void startSuTempInjectThread(SelectAppItem app) {
+ new Thread() {
+ public void run() {
+ String autoSuEnvInjectRet = NativeBridge.autoSuEnvInject(rootKey, app.getPackageName());
+ runOnUiThread(new Runnable() {
+ public void run() {
+ appendConsoleMsg(autoSuEnvInjectRet);
+ m_loadingDlg.cancel();
+
+ if(autoSuEnvInjectRet.indexOf("auto_su_env_inject done.") != -1) {
+ DialogUtils.showMsgDlg(MainActivity.this, "提示","已授予Root权限至APP [" + app.getShowName(MainActivity.this) + "]",
+ app.getDrawable(MainActivity.this));
+ }
+ }
+ });
+ }
+ }.start();
+ }
+
+ private void suForeverInject() {
+ Handler selectImplantSuEnvCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ SelectAppItem app = (SelectAppItem) msg.obj;
+ if (m_loadingDlg == null) {
+ m_loadingDlg = new ProgressDialog(MainActivity.this);
+ m_loadingDlg.setCancelable(false);
+ }
+ m_loadingDlg.setTitle("");
+ m_loadingDlg.setMessage("请现在手动启动APP [" + app.getShowName(MainActivity.this) + "]");
+ m_loadingDlg.show();
+ startSuForeverInjectThread(app);
+ super.handleMessage(msg);
+ }
+ };
+ View view = SelectAppDlg.showSelectAppDlg(MainActivity.this, rootKey, selectImplantSuEnvCallback);
+ CheckBox show_system_app_ckbox = view.findViewById(R.id.show_system_app_ckbox);
+ CheckBox show_thirty_app_ckbox = view.findViewById(R.id.show_thirty_app_ckbox);
+ CheckBox show_running_app_ckbox = view.findViewById(R.id.show_running_app_ckbox);
+ show_system_app_ckbox.setChecked(false);
+ show_system_app_ckbox.setEnabled(false);
+ show_thirty_app_ckbox.setChecked(true);
+ show_thirty_app_ckbox.setEnabled(false);
+ show_running_app_ckbox.setChecked(true);
+ show_running_app_ckbox.setEnabled(false);
+ }
+
+ private void startSuForeverInjectThread(SelectAppItem app) {
+ new Thread() {
+ public void run() {
+ String parasitePrecheckAppRet = NativeBridge.parasitePrecheckApp(rootKey, app.getPackageName());
+ runOnUiThread(new Runnable() {
+ public void run() {
+ m_loadingDlg.cancel();
+ Map fileList = parseSoFullPathInfo(parasitePrecheckAppRet);
+ if (fileList.size() == 0 && !parasitePrecheckAppRet.isEmpty()) {
+ appendConsoleMsg(parasitePrecheckAppRet);
+ return;
+ }
+ Handler selectFileCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ SelectFileItem fileItem = (SelectFileItem) msg.obj;
+ String parasiteImplantSuEnvRet = NativeBridge.parasiteImplantSuEnv(rootKey, app.getPackageName(), fileItem.getFilePath());
+ appendConsoleMsg(parasiteImplantSuEnvRet);
+ if(parasiteImplantSuEnvRet.indexOf("parasite_implant_su_env done.")!= -1) {
+ DialogUtils.showMsgDlg(MainActivity.this, "提示","已永久寄生su环境至APP [" + app.getShowName(MainActivity.this) + "]",
+ app.getDrawable(MainActivity.this));
+ }
+ super.handleMessage(msg);
+ }
+ };
+ SelectFileDlg.showSelectFileDlg(MainActivity.this, fileList, selectFileCallback);
+ }
+ });
+ }
+ }.start();
+ }
+
+ public void showSelectSuInjectModeDlg() {
+ final String[] items = {"临时授权su", "永久授权su"};
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("请选择一个选项");
+ builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ if(which == 0) suTempInject();
+ else if(which == 1) suForeverInject();
+ }
+ });
+ builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+
+ private void onClickImplantAppBtn() {
+ Handler selectImplantAppCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ SelectAppItem app = (SelectAppItem) msg.obj;
+ if (m_loadingDlg == null) {
+ m_loadingDlg = new ProgressDialog(MainActivity.this);
+ m_loadingDlg.setCancelable(false);
+ }
+ m_loadingDlg.setTitle("");
+ m_loadingDlg.setMessage("请现在手动启动APP [" + app.getShowName(MainActivity.this) + "]");
+ m_loadingDlg.show();
+ startImplantAppThread(app);
+ super.handleMessage(msg);
+ }
+ };
+ View view = SelectAppDlg.showSelectAppDlg(MainActivity.this, rootKey, selectImplantAppCallback);
+ CheckBox show_system_app_ckbox = view.findViewById(R.id.show_system_app_ckbox);
+ CheckBox show_thirty_app_ckbox = view.findViewById(R.id.show_thirty_app_ckbox);
+ CheckBox show_running_app_ckbox = view.findViewById(R.id.show_running_app_ckbox);
+ show_system_app_ckbox.setChecked(false);
+ show_system_app_ckbox.setEnabled(false);
+ show_thirty_app_ckbox.setChecked(true);
+ show_thirty_app_ckbox.setEnabled(false);
+ show_running_app_ckbox.setChecked(true);
+ show_running_app_ckbox.setEnabled(false);
+ }
+
+ private void startImplantAppThread(SelectAppItem app) {
+ new Thread() {
+ public void run() {
+ String parasitePrecheckAppRet = NativeBridge.parasitePrecheckApp(rootKey, app.getPackageName());
+ runOnUiThread(new Runnable() {
+ public void run() {
+ m_loadingDlg.cancel();
+ Map fileList = parseSoFullPathInfo(parasitePrecheckAppRet);
+ if (fileList.size() == 0 && !parasitePrecheckAppRet.isEmpty()) {
+ appendConsoleMsg(parasitePrecheckAppRet);
+ return;
+ }
+ Handler selectFileCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ SelectFileItem fileItem = (SelectFileItem) msg.obj;
+ String parasiteImplantAppRet = NativeBridge.parasiteImplantApp(rootKey, app.getPackageName(), fileItem.getFilePath());
+ appendConsoleMsg(parasiteImplantAppRet);
+ if(parasiteImplantAppRet.indexOf("parasite_implant_app done.")!= -1) {
+ DialogUtils.showMsgDlg(MainActivity.this, "提示",
+ "已经寄生到APP [" + app.getShowName(MainActivity.this) + "]",
+ app.getDrawable(MainActivity.this));
+ }
+ super.handleMessage(msg);
+ }
+ };
+ SelectFileDlg.showSelectFileDlg(MainActivity.this, fileList, selectFileCallback);
+ }
+ });
+ }
+ }.start();
+ }
+
+ private void appendConsoleMsg(String msg) {
+ StringBuffer txt = new StringBuffer();
+ txt.append(console_edit.getText().toString());
+ if (txt.length() != 0) txt.append("\n");
+ txt.append(msg);
+ txt.append("\n");
+ console_edit.setText(txt.toString());
+ console_edit.setSelection(txt.length());
+ }
+
+ public void copyConsoleMsg() {
+ ClipboardUtils.copyText(this, console_edit.getText().toString());
+ Toast.makeText(this, "复制成功", Toast.LENGTH_SHORT).show();
+ }
+
+ public void cleanConsoleMsg() {
+ console_edit.setText("");
+ }
+
+ private Map parseSoFullPathInfo(String jsonStr) {
+ Map soPathMap = new HashMap<>();
+ try {
+ JSONArray jsonArray = new JSONArray(jsonStr);
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject jsonObject = jsonArray.getJSONObject(i);
+ String name = URLDecoder.decode(jsonObject.getString("name"), "UTF-8");
+ int n = jsonObject.getInt("status");
+ SelectFileDlg.FileStatus status = SelectFileDlg.FileStatus.Unknown;
+ status = n == 1 ? SelectFileDlg.FileStatus.Running : status;
+ status = n == 2 ? SelectFileDlg.FileStatus.NotRunning : status;
+ soPathMap.put(name, status);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return soPathMap;
+ }
+
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectAppAdapter.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectAppAdapter.java
new file mode 100644
index 00000000..7af6f333
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectAppAdapter.java
@@ -0,0 +1,102 @@
+package com.linux.permissionmanager.adapter;
+
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.model.SelectAppItem;
+import com.linux.permissionmanager.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class SelectAppAdapter extends RecyclerView.Adapter {
+ public interface OnAppSelectedListener {
+ void onAppSelected(@NonNull SelectAppItem item);
+ }
+
+ private final int itemLayoutId;
+ private final List items = new ArrayList<>();
+ @NonNull private final OnAppSelectedListener listener;
+
+ public SelectAppAdapter(int itemLayoutId, @NonNull List initialItems, @NonNull OnAppSelectedListener listener) {
+ this.itemLayoutId = itemLayoutId;
+ this.listener = listener;
+ setData(initialItems);
+ }
+
+ public void setData(@Nullable List newList) {
+ items.clear();
+ if (newList != null) items.addAll(newList);
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ public List getData() {
+ return Collections.unmodifiableList(items);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(itemLayoutId, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ SelectAppItem item = items.get(position);
+
+ Drawable icon = item.getDrawable(holder.itemView.getContext());
+ String showName = item.getShowName(holder.itemView.getContext());
+ String packageName = item.getPackageName();
+
+ holder.icon.setImageDrawable(icon);
+ holder.name.setText(buildColoredTitle(showName, packageName));
+ holder.pkg.setText(packageName);
+ holder.itemView.setOnClickListener(v -> listener.onAppSelected(item));
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ private CharSequence buildColoredTitle(String showName, String packageName) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+ int startName = ssb.length();
+ ssb.append(showName);
+ ssb.setSpan(new ForegroundColorSpan(Color.parseColor("#88CC88")), startName, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(" ");
+ int startPkg = ssb.length();
+ ssb.append("(").append(packageName).append(")");
+ ssb.setSpan(new ForegroundColorSpan(Color.parseColor("#88CCCC")), startPkg, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return ssb;
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ final ImageView icon;
+ final TextView name;
+ final TextView pkg;
+
+ ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ icon = itemView.findViewById(R.id.select_app_icon);
+ name = itemView.findViewById(R.id.select_app_text);
+ pkg = itemView.findViewById(R.id.select_package_name);
+ }
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectFileAdapter.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectFileAdapter.java
new file mode 100644
index 00000000..464daf44
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/adapter/SelectFileAdapter.java
@@ -0,0 +1,93 @@
+package com.linux.permissionmanager.adapter;
+
+import android.graphics.Color;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.model.SelectFileItem;
+import com.linux.permissionmanager.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+public class SelectFileAdapter extends RecyclerView.Adapter {
+
+ public interface OnFileSelectedListener {
+ void onFileSelected(@NonNull SelectFileItem item);
+ }
+ private final int itemLayoutId;
+ private final List items = new ArrayList<>();
+ @NonNull private final OnFileSelectedListener listener;
+
+ public SelectFileAdapter(int itemLayoutId, @NonNull List initialItems, @NonNull OnFileSelectedListener listener) {
+ this.itemLayoutId = itemLayoutId;
+ this.listener = listener;
+ setData(initialItems);
+ }
+
+ public void setData(@Nullable List newList) {
+ items.clear();
+ if (newList != null) items.addAll(newList);
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ public List getData() {
+ return Collections.unmodifiableList(items);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(itemLayoutId, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ SelectFileItem item = items.get(position);
+
+ String fileName = item.getFileName();
+ String fileDesc = item.getFileDesc();
+
+ Color fileDescColor = item.getFileDescColor();
+ @ColorInt int descColor = fileDescColor.toArgb();
+
+ holder.name.setText(buildColoredText(fileName, 0xFF88CC88));
+ holder.desc.setText(buildColoredText(fileDesc, descColor));
+
+ holder.itemView.setOnClickListener(v -> listener.onFileSelected(item));
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ private CharSequence buildColoredText(String text, @ColorInt int color) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+ ssb.setSpan(new ForegroundColorSpan(color), 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return ssb;
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ final TextView name;
+ final TextView desc;
+
+ ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ name = itemView.findViewById(R.id.select_file_name);
+ desc = itemView.findViewById(R.id.select_file_desc);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/bridge/NativeBridge.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/bridge/NativeBridge.java
new file mode 100644
index 00000000..94dcf69f
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/bridge/NativeBridge.java
@@ -0,0 +1,30 @@
+package com.linux.permissionmanager.bridge;
+
+public class NativeBridge {
+ static {
+ System.loadLibrary("permissionmanager");
+ }
+
+ public static native String testRoot(String rootKey);
+
+ public static native String runRootCmd(String rootKey, String cmd);
+
+ public static native String rootExecProcessCmd(String rootKey, String cmd);
+
+ public static native String installSu(String rootKey);
+
+ public static native String getLastSuFilePath();
+
+ public static native String uninstallSu(String rootKey);
+
+ public static native String autoSuEnvInject(String rootKey, String targetProcessCmdline);
+
+ public static native String getAllCmdlineProcess(String rootKey);
+
+ public static native String parasitePrecheckApp(String rootKey, String targetProcessCmdline);
+
+ public static native String parasiteImplantApp(String rootKey, String targetProcessCmdline, String targetSoFullPath);
+
+ public static native String parasiteImplantSuEnv(String rootKey, String targetProcessCmdline, String targetSoFullPath);
+
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectAppDlg.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectAppDlg.java
new file mode 100644
index 00000000..f0c8ccf8
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectAppDlg.java
@@ -0,0 +1,184 @@
+package com.linux.permissionmanager.helper;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.R;
+import com.linux.permissionmanager.adapter.SelectAppAdapter;
+import com.linux.permissionmanager.bridge.NativeBridge;
+import com.linux.permissionmanager.model.SelectAppItem;
+import com.linux.permissionmanager.utils.ScreenInfoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SelectAppDlg {
+ public static View showSelectAppDlg(Activity activity, String rootKey, Handler selectAppCallback) {
+ final PopupWindow popupWindow = new PopupWindow(activity);
+ View view = View.inflate(activity, R.layout.select_app_wnd, null);
+ popupWindow.setContentView(view);
+ popupWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setBackgroundDrawable(new ColorDrawable(0x9B000000));
+ popupWindow.setOutsideTouchable(true);
+ popupWindow.setFocusable(true);
+ popupWindow.setTouchable(true);
+ //全屏
+ View parent = View.inflate(activity, R.layout.activity_main, null);
+ popupWindow.showAtLocation(parent, Gravity.NO_GRAVITY, 0, 0);
+ popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
+ @Override
+ public void onDismiss() {}
+ });
+ final int screenWidth = ScreenInfoUtils.getRealWidth(activity);
+ final int screenHeight = ScreenInfoUtils.getRealHeight(activity);
+
+ final double centerWidth = ((double) screenWidth) * 0.80;
+ final double centerHeight = ((double) screenHeight) * 0.90;
+
+ LinearLayout center_layout = (LinearLayout) view.findViewById(R.id.center_layout);
+ ViewGroup.LayoutParams lp = center_layout.getLayoutParams();
+ lp.width = (int) centerWidth;
+ lp.height = (int) centerHeight;
+ center_layout.setLayoutParams(lp);
+
+ // 外层容器:点到阴影就关闭弹窗
+ View outside = view.findViewById(R.id.popup_outside_container);
+ outside.setOnClickListener(v -> popupWindow.dismiss());
+ // 中间内容区域:消费点击,不往外传(防止点内容也被当成点阴影)
+ center_layout.setOnClickListener(v -> {
+ // 什么都不做,只是阻止事件传到 outside
+ });
+
+ List appList = new ArrayList<>();
+ List packages = activity.getPackageManager().getInstalledPackages(0);
+
+ for (int i = 0; i < packages.size(); i++) {
+ PackageInfo packageInfo = packages.get(i);
+ String packageName = packageInfo.applicationInfo.packageName;
+ if(packageName.equals(activity.getPackageName())) continue;
+ appList.add(new SelectAppItem(packageInfo));
+ }
+
+ SelectAppAdapter adapter = new SelectAppAdapter(R.layout.select_app_recycler_item, appList,
+ item -> {
+ popupWindow.dismiss();
+ Message msg = new Message(); msg.obj = item;
+ selectAppCallback.sendMessage(msg);
+ }
+ );
+ RecyclerView select_app_recycler_view = (RecyclerView) view.findViewById(R.id.select_app_recycler_view);
+ LinearLayoutManager linearLayoutManager = new LinearLayoutManager(activity);
+ linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ select_app_recycler_view.setLayoutManager(linearLayoutManager);
+ select_app_recycler_view.setAdapter(adapter);
+
+ // 获取正在运行的APP
+ String runningApp = NativeBridge.getAllCmdlineProcess(rootKey);
+ Map processMap = parseProcessInfo(runningApp);
+ TextView clear_search_btn = view.findViewById(R.id.clear_search_btn);
+ EditText search_edit = view.findViewById(R.id.search_edit);
+ CheckBox show_system_app_ckbox = view.findViewById(R.id.show_system_app_ckbox);
+ CheckBox show_thirty_app_ckbox = view.findViewById(R.id.show_thirty_app_ckbox);
+ CheckBox show_running_app_ckbox = view.findViewById(R.id.show_running_app_ckbox);
+ show_system_app_ckbox.setEnabled(true);
+ show_thirty_app_ckbox.setEnabled(true);
+ show_running_app_ckbox.setEnabled(true);
+ Map finalProcessMap = processMap;
+ @SuppressLint("HandlerLeak") Handler updateAppListFunc = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ List newAppList = new ArrayList<>();
+ String filterText = search_edit.getText().toString();
+ for(SelectAppItem item : appList) {
+ PackageInfo pack = item.getPackageInfo();
+ if(!show_system_app_ckbox.isChecked()) {
+ if ((pack.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) continue; //系统应用
+ }
+ if(!show_thirty_app_ckbox.isChecked()) {
+ if ((pack.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) continue; // 第三方应用
+ }
+ if (show_running_app_ckbox.isChecked()) {
+ boolean isFound = finalProcessMap.values().stream().anyMatch(value -> value.contains(item.getPackageName()));
+ if (!isFound) continue;
+ }
+ if(item.getPackageName().indexOf(filterText) != -1 || item.getShowName(activity).indexOf(filterText) != -1) newAppList.add(item);
+ }
+ adapter.setData(newAppList);
+ super.handleMessage(msg);
+ }
+ };
+ search_edit.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ clear_search_btn.setVisibility(s.toString().length() > 0 ? View.VISIBLE : View.GONE);
+ updateAppListFunc.sendMessage(new Message());
+ }
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+ clear_search_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) { search_edit.setText(""); }
+ });
+
+ show_system_app_ckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { updateAppListFunc.sendMessage(new Message()); }
+ });
+ show_thirty_app_ckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { updateAppListFunc.sendMessage(new Message()); }
+ });
+ show_running_app_ckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { updateAppListFunc.sendMessage(new Message()); }
+ });
+ updateAppListFunc.sendMessage(new Message());
+ return view;
+ }
+
+ private static Map parseProcessInfo(String jsonStr) {
+ Map processMap = new HashMap<>();
+ try {
+ JSONArray jsonArray = new JSONArray(jsonStr);
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject jsonObject = jsonArray.getJSONObject(i);
+ int pid = jsonObject.getInt("pid");
+ String encodedValue = jsonObject.getString("name");
+ String name = URLDecoder.decode(encodedValue, "UTF-8");
+ processMap.put(pid, name);
+ }
+ } catch (Exception e) { e.printStackTrace(); }
+ return processMap;
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectFileDlg.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectFileDlg.java
new file mode 100644
index 00000000..95b19602
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/helper/SelectFileDlg.java
@@ -0,0 +1,151 @@
+package com.linux.permissionmanager.helper;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.R;
+import com.linux.permissionmanager.adapter.SelectAppAdapter;
+import com.linux.permissionmanager.adapter.SelectFileAdapter;
+import com.linux.permissionmanager.model.SelectFileItem;
+import com.linux.permissionmanager.utils.ScreenInfoUtils;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class SelectFileDlg {
+
+ private static String[] RECOMMEND_FILES = {"libc++_shared.so"};
+
+ public enum FileStatus {
+ Unknown,
+ Running,
+ NotRunning,
+ };
+ public static View showSelectFileDlg(Activity activity, Map filePath, Handler selectFileCallback) {
+ final PopupWindow popupWindow = new PopupWindow(activity);
+ View view = View.inflate(activity, R.layout.select_file_wnd, null);
+ popupWindow.setContentView(view);
+ popupWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setBackgroundDrawable(new ColorDrawable(0x9B000000));
+ popupWindow.setOutsideTouchable(true);
+ popupWindow.setFocusable(true);
+ popupWindow.setTouchable(true);
+
+ //全屏
+ View parent = View.inflate(activity, R.layout.activity_main, null);
+ popupWindow.showAtLocation(parent, Gravity.NO_GRAVITY, 0, 0);
+ popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
+ @Override
+ public void onDismiss() {}
+ });
+ final int screenWidth = ScreenInfoUtils.getRealWidth(activity);
+ final int screenHeight = ScreenInfoUtils.getRealHeight(activity);
+ final double centerWidth = ((double) screenWidth) * 0.80;
+ final double centerHeight = ((double) screenHeight) * 0.90;
+ LinearLayout center_layout = (LinearLayout) view.findViewById(R.id.center_layout);
+ ViewGroup.LayoutParams lp = center_layout.getLayoutParams();
+ lp.width = (int) centerWidth;
+ lp.height = (int) centerHeight;
+ center_layout.setLayoutParams(lp);
+ // 外层容器:点到阴影就关闭弹窗
+ View outside = view.findViewById(R.id.popup_outside_container);
+ outside.setOnClickListener(v -> popupWindow.dismiss());
+ // 中间内容区域:消费点击,不往外传(防止点内容也被当成点阴影)
+ center_layout.setOnClickListener(v -> {
+ // 什么都不做,只是阻止事件传到 outside
+ });
+
+ List fileList = new ArrayList<>();
+ for (Map.Entry entry : filePath.entrySet()) {
+ String strFilePath = entry.getKey();
+ FileStatus status = entry.getValue();
+ if(status != FileStatus.Running) continue;
+ String strFileDesc = checkIsRecommendFile(strFilePath) ? "(推荐,正在运行)" : "(正在运行)";
+ fileList.add(new SelectFileItem(strFilePath, strFileDesc, Color.valueOf(0xFFFFFFFF)));
+ }
+ for (Map.Entry entry : filePath.entrySet()) {
+ String strFilePath = entry.getKey();
+ FileStatus status = entry.getValue();
+ if(status != FileStatus.NotRunning) continue;
+ String strFileDesc ="(未运行)";
+ fileList.add(new SelectFileItem(strFilePath, strFileDesc, Color.valueOf(Color.GRAY)));
+ }
+ SelectFileAdapter adapter = new SelectFileAdapter(R.layout.select_file_recycler_item, fileList,
+ item -> {
+ popupWindow.dismiss();
+ Message msg = new Message(); msg.obj = item;
+ selectFileCallback.sendMessage(msg);
+ }
+ );
+ RecyclerView select_file_recycler_view = (RecyclerView) view.findViewById(R.id.select_file_recycler_view);
+ LinearLayoutManager linearLayoutManager = new LinearLayoutManager(activity);
+ linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ select_file_recycler_view.setLayoutManager(linearLayoutManager);
+ select_file_recycler_view.setAdapter(adapter);
+ TextView clear_search_btn = view.findViewById(R.id.clear_search_btn);
+ EditText search_edit = view.findViewById(R.id.search_edit);
+ @SuppressLint("HandlerLeak") Handler updateFileListFunc = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ List newFileList = new ArrayList<>();
+ String filterText = search_edit.getText().toString();
+ for(SelectFileItem item : fileList) {
+ String fileName = item.getFileName();
+ if(fileName.indexOf(filterText) != -1) newFileList.add(item);
+ }
+ adapter.setData(newFileList);
+ super.handleMessage(msg);
+ }
+ };
+ search_edit.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ clear_search_btn.setVisibility(s.toString().length() > 0 ? View.VISIBLE : View.GONE);
+ updateFileListFunc.sendMessage(new Message());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {}
+ });
+ clear_search_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) { search_edit.setText(""); }
+ });
+ updateFileListFunc.sendMessage(new Message());
+ return view;
+ }
+
+ private static boolean checkIsRecommendFile(String filePath) {
+ Path path = Paths.get(filePath);
+ String fileName = path.getFileName().toString();
+ for (String recommendFile : RECOMMEND_FILES) {
+ if (recommendFile.equals(fileName)) return true;
+ }
+ return false;
+ }
+
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectAppItem.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectAppItem.java
new file mode 100644
index 00000000..205dc4e9
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectAppItem.java
@@ -0,0 +1,31 @@
+package com.linux.permissionmanager.model;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.graphics.drawable.Drawable;
+
+public class SelectAppItem {
+ private PackageInfo packageInfo;
+
+ public SelectAppItem(PackageInfo packageInfo){
+ this.packageInfo = packageInfo;
+ }
+
+ public PackageInfo getPackageInfo() {
+ return packageInfo;
+ }
+
+ public String getShowName(Context ctx) {
+ String showName = this.packageInfo.applicationInfo.loadLabel(ctx.getPackageManager()).toString();
+ return showName;
+ }
+ public String getPackageName() {
+ String packageName = this.packageInfo.applicationInfo.packageName;
+ return packageName;
+ }
+ public Drawable getDrawable(Context ctx) {
+ Drawable icon = this.packageInfo.applicationInfo.loadIcon(ctx.getPackageManager());
+ return icon;
+ }
+
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectFileItem.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectFileItem.java
new file mode 100644
index 00000000..be366ef8
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/model/SelectFileItem.java
@@ -0,0 +1,34 @@
+package com.linux.permissionmanager.model;
+
+import android.graphics.Color;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class SelectFileItem {
+ private String filePath;
+ private String fileDesc;
+ private Color fileDescColor;
+
+ public SelectFileItem(String filePath, String fileDesc, Color fileDescColor){
+ this.filePath = filePath;
+ this.fileDesc = fileDesc;
+ this.fileDescColor = fileDescColor;
+ }
+
+ public String getFilePath() {
+ return this.filePath;
+ }
+
+ public String getFileName() {
+ Path path = Paths.get(filePath);
+ Path fileName = path.getFileName();
+ return fileName.toString();
+ }
+ public String getFileDesc() {
+ return this.fileDesc;
+ }
+ public Color getFileDescColor() {
+ return this.fileDescColor;
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ClipboardUtils.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ClipboardUtils.java
new file mode 100644
index 00000000..354f8a1e
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ClipboardUtils.java
@@ -0,0 +1,16 @@
+package com.linux.permissionmanager.utils;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+
+public class ClipboardUtils {
+ public static void copyText(Context ctx, String text) {
+ //获取剪贴板管理器:
+ ClipboardManager cm = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE);
+ // 创建普通字符型ClipData
+ ClipData mClipData = ClipData.newPlainText("Label", text);
+ // 将ClipData内容放到系统剪贴板里。
+ cm.setPrimaryClip(mClipData);
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/DialogUtils.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/DialogUtils.java
new file mode 100644
index 00000000..a3e9a7e1
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/DialogUtils.java
@@ -0,0 +1,191 @@
+package com.linux.permissionmanager.utils;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import java.util.List;
+
+public class DialogUtils {
+ public static void showCustomDialog(Context context, String title, String message,
+ Drawable icon,
+ String positiveButtonText, DialogInterface.OnClickListener positiveClickListener,
+ String negativeButtonText, DialogInterface.OnClickListener negativeClickListener) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title)
+ .setMessage(message)
+ .setCancelable(false);
+
+ if (icon != null) {
+ builder.setIcon(icon);
+ }
+
+ if (positiveButtonText != null && positiveClickListener != null) {
+ builder.setPositiveButton(positiveButtonText, positiveClickListener);
+ }
+ if (negativeButtonText != null && negativeClickListener != null) {
+ builder.setNegativeButton(negativeButtonText, negativeClickListener);
+ }
+ builder.show();
+ }
+
+ public static void showNeedPermissionDialog(Context context) {
+ DialogUtils.showCustomDialog(
+ context,
+ "权限申请",
+ "请授予权限后重新操作",
+ null,
+ "确定", (dialog, which) -> {
+ dialog.dismiss();
+ },
+ null, null
+ );
+ }
+
+ /**
+ * 显示带有消息的对话框。
+ *
+ * @param context 上下文
+ * @param title 对话框标题
+ * @param msg 对话框内容
+ * @param icon 对话框图标(可为 null)
+ */
+ public static void showMsgDlg(Context context, String title, String msg, Drawable icon) {
+ showCustomDialog(
+ context,
+ title,
+ msg,
+ icon,
+ "确定", (dialog, which) -> dialog.dismiss(),
+ null, null
+ );
+ }
+
+ /**
+ * 显示带有三个按钮的输入对话框。
+ *
+ * @param context 上下文
+ * @param defaultText 默认文本
+ * @param title 对话框标题
+ * @param thirdButtonText 第三个按钮的文本
+ * @param confirmCallback 点击确定按钮时的回调
+ * @param thirdButtonCallback 第三个按钮的回调
+ */
+ public static void showInputDlg(Context context, String defaultText, String title, final String thirdButtonText,
+ final Handler confirmCallback, final Handler thirdButtonCallback) {
+ final EditText inputTxt = new EditText(context);
+ inputTxt.setText(defaultText);
+ inputTxt.setFocusable(true);
+ inputTxt.setSelection(defaultText.length(), 0);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(title)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setView(inputTxt)
+ .setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setPositiveButton("确定", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ String text = inputTxt.getText().toString();
+ Message msg = new Message();
+ msg.obj = text;
+ confirmCallback.sendMessage(msg);
+ }
+ });
+
+ // 添加第三个按钮
+ if (thirdButtonText != null && !thirdButtonText.isEmpty()) {
+ builder.setNeutralButton(thirdButtonText, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // 自定义回调
+ if (thirdButtonCallback != null) {
+ thirdButtonCallback.sendMessage(new Message());
+ }
+ }
+ });
+ }
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+
+ public static void showLogDialog(Context context, List logs) {
+ String logText = logs.isEmpty() ? "No logs" : android.text.TextUtils.join("\n", logs);
+ // 创建全屏 Dialog
+ Dialog dialog = new Dialog(context);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ // 创建一个外部的线性布局(垂直方向)
+ LinearLayout layout = new LinearLayout(context);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(50, 50, 50, 50);
+
+ // 创建 TextView 作为日志显示区域
+ TextView textView = new TextView(context);
+ textView.setTextSize(14);
+ textView.setText(logText);
+ textView.setTextIsSelectable(true); // 允许选中复制
+ textView.setVerticalScrollBarEnabled(true);
+ textView.setSingleLine(false); // 允许多行显示
+ textView.setMaxLines(Integer.MAX_VALUE); // 让其支持无限行
+ textView.setLineSpacing(1.5f, 1.2f); // 增加行间距,增强可读性
+
+ // ScrollView 使日志可以滚动
+ ScrollView scrollView = new ScrollView(context);
+ scrollView.addView(textView);
+ scrollView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ 0, 1 // 设置权重,让日志区域填满大部分屏幕
+ ));
+
+ // 让 ScrollView 自动滚动到底部
+ scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
+ scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
+ });
+
+
+ // 创建一个底部的“关闭”按钮
+ Button closeButton = new Button(context);
+ closeButton.setText("关闭");
+ closeButton.setOnClickListener(v -> {
+ dialog.dismiss();
+ });
+
+ // 将 ScrollView 和按钮添加到主布局
+ layout.addView(scrollView);
+ layout.addView(closeButton);
+
+ // 设置 Dialog 的内容
+ dialog.setContentView(layout);
+
+ // 设置全屏属性
+ Window window = dialog.getWindow();
+ if (window != null) {
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ window.setGravity(Gravity.CENTER);
+ }
+
+ dialog.show();
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/GetAppListPermissionHelper.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/GetAppListPermissionHelper.java
new file mode 100644
index 00000000..080314f6
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/GetAppListPermissionHelper.java
@@ -0,0 +1,13 @@
+package com.linux.permissionmanager.utils;
+
+import android.app.Activity;
+import android.content.pm.PackageInfo;
+
+import java.util.List;
+
+public class GetAppListPermissionHelper {
+ public static boolean getPermissions(Activity activity) {
+ List packages = activity.getPackageManager().getInstalledPackages(0);
+ return packages.size() > 0;
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ScreenInfoUtils.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ScreenInfoUtils.java
new file mode 100644
index 00000000..97dcd2f9
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/ScreenInfoUtils.java
@@ -0,0 +1,187 @@
+package com.linux.permissionmanager.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * Get Screen Information Utils
+ *
+ * @author yh
+ * @date 2018/12/18.
+ */
+public class ScreenInfoUtils {
+
+ private static final String TAG = "ScreenInfoUtils";
+
+ /**
+ * Get Screen Width
+ */
+ public static int getScreenWidth(Context context) {
+ return getDisplayMetrics(context).widthPixels;
+ }
+
+ /**
+ * Get Screen Height
+ */
+ public static int getScreenHeight(Context context) {
+ return getDisplayMetrics(context).heightPixels;
+ }
+
+
+ /**
+ * Get Screen Real Height
+ *
+ * @param context Context
+ * @return Real Height
+ */
+ public static int getRealHeight(Context context) {
+ Display display = getDisplay(context);
+ if (display == null) {
+ return 0;
+ }
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getRealMetrics(dm);
+ return dm.heightPixels;
+ }
+
+ /**
+ * Get Screen Real Width
+ *
+ * @param context Context
+ * @return Real Width
+ */
+ public static int getRealWidth(Context context) {
+ Display display = getDisplay(context);
+ if (display == null) {
+ return 0;
+ }
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getRealMetrics(dm);
+ return dm.widthPixels;
+ }
+
+ /**
+ * Get StatusBar Height
+ */
+ public static int getStatusBarHeight(Context mContext) {
+ int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ return mContext.getResources().getDimensionPixelSize(resourceId);
+ }
+ return 0;
+ }
+
+ /**
+ * Get ActionBar Height
+ */
+ public static int getActionBarHeight(Context mContext) {
+ TypedValue tv = new TypedValue();
+ if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
+ return TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics());
+ }
+ return 0;
+ }
+
+ /**
+ * Get NavigationBar Height
+ */
+ public static int getNavigationBarHeight(Context mContext) {
+ Resources resources = mContext.getResources();
+ int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ return resources.getDimensionPixelSize(resourceId);
+ }
+ return 0;
+ }
+
+ /**
+ * Get Density
+ */
+ private static float getDensity(Context context) {
+ return getDisplayMetrics(context).density;
+ }
+
+ /**
+ * Get Dpi
+ */
+ private static int getDpi(Context context) {
+ return getDisplayMetrics(context).densityDpi;
+ }
+
+ /**
+ * Get Display
+ *
+ * @param context Context for get WindowManager
+ * @return Display
+ */
+ private static Display getDisplay(Context context) {
+ WindowManager wm;
+ if (context instanceof Activity) {
+ Activity activity = (Activity) context;
+ wm = activity.getWindowManager();
+ } else {
+ wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ }
+ if (wm != null) {
+ return wm.getDefaultDisplay();
+ }
+ return null;
+ }
+
+ /**
+ * Get DisplayMetrics
+ *
+ * @param context Context for get Resources
+ * @return DisplayMetrics
+ */
+ private static DisplayMetrics getDisplayMetrics(Context context) {
+ return context.getResources().getDisplayMetrics();
+ }
+
+
+ /**
+ * Get ScreenInfo
+ */
+ private static String getScreenInfo(Context context) {
+ return " \n" +
+ "--------ScreenInfo--------" + "\n" +
+ "Screen Width : " + getScreenWidth(context) + "px\n" +
+ "Screen RealWidth :" + getRealWidth(context) + "px\n" +
+ "Screen Height: " + getScreenHeight(context) + "px\n" +
+ "Screen RealHeight: " + getRealHeight(context) + "px\n" +
+ "Screen StatusBar Height: " + getStatusBarHeight(context)+ "px\n" +
+ "Screen ActionBar Height: " + getActionBarHeight(context)+ "px\n" +
+ "Screen NavigationBar Height: " + getNavigationBarHeight(context)+ "px\n" +
+ "Screen Dpi: " + getDpi(context) + "\n" +
+ "Screen Density: " + getDensity(context) + "\n" +
+ "--------------------------";
+ }
+
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ public static int dip2px(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale);
+ }
+
+ /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */
+ public static int px2dip(Context context, float pxValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (pxValue / scale);
+ }
+
+ /**
+ * Print screenInfo to logcat
+ */
+ public static void printScreenInfo(Context context) {
+ Log.d(TAG, getScreenInfo(context));
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/UrlIntentUtils.java b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/UrlIntentUtils.java
new file mode 100644
index 00000000..e7102c2e
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/java/com/linux/permissionmanager/utils/UrlIntentUtils.java
@@ -0,0 +1,47 @@
+package com.linux.permissionmanager.utils;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+public final class UrlIntentUtils {
+ private UrlIntentUtils() {}
+
+ /** 在外部浏览器打开链接;支持传 "example.com" 自动补全 https:// */
+ public static boolean openUrl(Context context, String url) {
+ if (context == null) return false;
+ if (TextUtils.isEmpty(url)) {
+ Toast.makeText(context, "链接为空", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ String u = url.trim();
+ // 没有 scheme 的话补 https://
+ if (!u.startsWith("http://") && !u.startsWith("https://")) {
+ u = "https://" + u;
+ }
+
+ Uri uri = Uri.parse(u);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+
+ // 非 Activity context 需要 NEW_TASK
+ if (!(context instanceof Activity)) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ try {
+ context.startActivity(intent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, "没有可打开链接的应用", Toast.LENGTH_SHORT).show();
+ return false;
+ } catch (Exception e) {
+ Toast.makeText(context, "打开链接失败", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ }
+}
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/line.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/line.xml
new file mode 100644
index 00000000..597142f2
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/line.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/line_drawable.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/line_drawable.xml
new file mode 100644
index 00000000..4f79f470
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/line_drawable.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/ripple_grey.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/ripple_grey.xml
new file mode 100644
index 00000000..ac736de4
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/ripple_grey.xml
@@ -0,0 +1,6 @@
+
+//点击时波纹的颜色
+ //未点击时控件的背景(可以是图片,可以是颜色,也可以是drawable里的xml背景(比如圆角))
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml
new file mode 100644
index 00000000..6bc80841
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb.xml
new file mode 100644
index 00000000..0646431b
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml b/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml
new file mode 100644
index 00000000..a93b8b83
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/layout/activity_main.xml b/Lite_version/src/PermissionManager/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..95a23b66
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml
new file mode 100644
index 00000000..425d9500
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_wnd.xml b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_wnd.xml
new file mode 100644
index 00000000..34ec5c56
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_app_wnd.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_recycler_item.xml b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_recycler_item.xml
new file mode 100644
index 00000000..355ce92a
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_recycler_item.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_wnd.xml b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_wnd.xml
new file mode 100644
index 00000000..93c87230
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/layout/select_file_wnd.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..c9ad5f98
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..036d09bc
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..93cee9bc
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..7cddca62
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..f82b8f60
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..cfb2eef3
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..2e8cfa78
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..789dde33
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..8a6d5b78
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..7803d04e
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..9197bd59
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..9bde40e0
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..1a92f50c
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..791a79f5
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..df2cf50c
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..b702d667
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..5cb6b51f
Binary files /dev/null and b/Lite_version/src/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/values-night/themes.xml b/Lite_version/src/PermissionManager/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..98a30f56
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,17 @@
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/values/colors.xml b/Lite_version/src/PermissionManager/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..bbb834e9
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/values/colors.xml
@@ -0,0 +1,13 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #424242
+ #FF767676
+ #00000000
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/values/ic_launcher_background.xml b/Lite_version/src/PermissionManager/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 00000000..b0b6fb52
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #232326
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/values/strings.xml b/Lite_version/src/PermissionManager/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..d8737c4a
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ SKRoot(Lite)
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/main/res/values/themes.xml b/Lite_version/src/PermissionManager/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..3886d309
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/main/res/values/themes.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java b/Lite_version/src/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java
new file mode 100644
index 00000000..bd51fc00
--- /dev/null
+++ b/Lite_version/src/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.linux.permissionmanager;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/build.gradle b/Lite_version/src/PermissionManager/build.gradle
new file mode 100644
index 00000000..f757f23e
--- /dev/null
+++ b/Lite_version/src/PermissionManager/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/gradle.properties b/Lite_version/src/PermissionManager/gradle.properties
new file mode 100644
index 00000000..9a4c7cc9
--- /dev/null
+++ b/Lite_version/src/PermissionManager/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.jar b/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..e708b1c0
Binary files /dev/null and b/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.properties b/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..5ac2865d
--- /dev/null
+++ b/Lite_version/src/PermissionManager/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Feb 19 21:34:04 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/Lite_version/src/PermissionManager/gradlew b/Lite_version/src/PermissionManager/gradlew
new file mode 100644
index 00000000..4f906e0c
--- /dev/null
+++ b/Lite_version/src/PermissionManager/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Lite_version/src/PermissionManager/gradlew.bat b/Lite_version/src/PermissionManager/gradlew.bat
new file mode 100644
index 00000000..107acd32
--- /dev/null
+++ b/Lite_version/src/PermissionManager/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Lite_version/src/PermissionManager/settings.gradle b/Lite_version/src/PermissionManager/settings.gradle
new file mode 100644
index 00000000..1f40e56f
--- /dev/null
+++ b/Lite_version/src/PermissionManager/settings.gradle
@@ -0,0 +1,20 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+ plugins {
+ id 'com.android.application' version '7.1.0-alpha11'
+ id 'com.android.library' version '7.1.0-alpha11'
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "PermissionManager"
+include ':app'
diff --git a/Lite_version/src/patch_kernel_root/3rdparty/aarch64_asm_helper.h b/Lite_version/src/patch_kernel_root/3rdparty/aarch64_asm_helper.h
new file mode 100644
index 00000000..19ce69f2
--- /dev/null
+++ b/Lite_version/src/patch_kernel_root/3rdparty/aarch64_asm_helper.h
@@ -0,0 +1,500 @@
+#pragma once
+#include
+#include