parent
8da216a719
commit
9f868aeddf
|
|
@ -144,22 +144,10 @@ public class SettingsFragment extends BaseFragment {
|
||||||
boolean installed = ConfigManager.isBinderAlive();
|
boolean installed = ConfigManager.isBinderAlive();
|
||||||
SwitchPreference prefVerboseLogs = findPreference("disable_verbose_log");
|
SwitchPreference prefVerboseLogs = findPreference("disable_verbose_log");
|
||||||
if (prefVerboseLogs != null) {
|
if (prefVerboseLogs != null) {
|
||||||
if (requireActivity().getApplicationInfo().uid / 100000 != 0) {
|
prefVerboseLogs.setEnabled(installed);
|
||||||
prefVerboseLogs.setVisible(false);
|
prefVerboseLogs.setChecked(!installed || !ConfigManager.isVerboseLogEnabled());
|
||||||
} else {
|
prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) ->
|
||||||
prefVerboseLogs.setEnabled(installed);
|
ConfigManager.setVerboseLogEnabled(!(boolean) newValue));
|
||||||
prefVerboseLogs.setChecked(!installed || !ConfigManager.isVerboseLogEnabled());
|
|
||||||
prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> {
|
|
||||||
boolean result = ConfigManager.setVerboseLogEnabled(!(boolean) newValue);
|
|
||||||
SettingsFragment fragment = (SettingsFragment) getParentFragment();
|
|
||||||
if (result && fragment != null) {
|
|
||||||
Snackbar.make(fragment.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT)
|
|
||||||
.setAction(R.string.reboot, v -> ConfigManager.reboot(false))
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchPreference prefEnableResources = findPreference("enable_resources");
|
SwitchPreference prefEnableResources = findPreference("enable_resources");
|
||||||
|
|
|
||||||
|
|
@ -252,8 +252,14 @@ val pushLspd = task("pushLspd", Exec::class) {
|
||||||
workingDir("$buildDir/intermediates/dex/debug/mergeDexDebug")
|
workingDir("$buildDir/intermediates/dex/debug/mergeDexDebug")
|
||||||
commandLine(adb, "push", "classes.dex", "/data/local/tmp/lspd.dex")
|
commandLine(adb, "push", "classes.dex", "/data/local/tmp/lspd.dex")
|
||||||
}
|
}
|
||||||
|
val pushLspdNative = task("pushLspdNative", Exec::class) {
|
||||||
|
dependsOn("mergeDebugNativeLibs")
|
||||||
|
workingDir("$buildDir/intermediates/merged_native_libs/debug/out/lib/arm64-v8a")
|
||||||
|
commandLine(adb, "push", "libdaemon.so", "/data/local/tmp/libdaemon.so")
|
||||||
|
}
|
||||||
task("reRunLspd", Exec::class) {
|
task("reRunLspd", Exec::class) {
|
||||||
dependsOn(pushLspd)
|
dependsOn(pushLspd)
|
||||||
|
dependsOn(pushLspdNative)
|
||||||
dependsOn(killLspd)
|
dependsOn(killLspd)
|
||||||
commandLine(adb, "shell", "su", "-c", "sh /data/adb/modules/riru_lsposed/service.sh&")
|
commandLine(adb, "shell", "su", "-c", "sh /data/adb/modules/riru_lsposed/service.sh&")
|
||||||
isIgnoreExitValue = true
|
isIgnoreExitValue = true
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
if [ "$IS64BIT" = true ]; then
|
if [ "$IS64BIT" = true ]; then
|
||||||
ui_print "- Extracting arm64 libraries"
|
ui_print "- Extracting arm64 libraries"
|
||||||
extract "$ZIPFILE" "lib/arm64-v8a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true
|
extract "$ZIPFILE" "lib/arm64-v8a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true
|
||||||
|
extract "$ZIPFILE" 'lib/arm64-v8a/libdaemon.so' "$MODPATH" true
|
||||||
|
else
|
||||||
|
extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -93,21 +96,24 @@ if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
||||||
if [ "$IS64BIT" = true ]; then
|
if [ "$IS64BIT" = true ]; then
|
||||||
ui_print "- Extracting x64 libraries"
|
ui_print "- Extracting x64 libraries"
|
||||||
extract "$ZIPFILE" "lib/x86_64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true
|
extract "$ZIPFILE" "lib/x86_64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true
|
||||||
|
extract "$ZIPFILE" 'lib/x86_64/libdaemon.so' "$MODPATH" true
|
||||||
|
else
|
||||||
|
extract "$ZIPFILE" 'lib/x86/libdaemon.so' "$MODPATH" true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$RIRU_MODULE_DEBUG" = true ]; then
|
if [ "$RIRU_MODULE_DEBUG" = true ]; then
|
||||||
mv "$MODPATH/riru" "$MODPATH/system"
|
mv "$MODPATH/riru" "$MODPATH/system"
|
||||||
mv "$MODPATH/system/lib/liblspd.so" "$MODPATH/system/lib/libriru_lspd.so"
|
mv "$MODPATH/system/lib/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib/libriru_$RIRU_MODULE_LIB_NAME.so"
|
||||||
mv "$MODPATH/system/lib64/liblspd.so" "$MODPATH/system/lib64/libriru_lspd.so"
|
mv "$MODPATH/system/lib64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib64/libriru_$RIRU_MODULE_LIB_NAME.so"
|
||||||
mv "$MODPATH/framework" "$MODPATH/system/framework"
|
mv "$MODPATH/framework" "$MODPATH/system/framework"
|
||||||
if [ "$RIRU_API" -ge 26 ]; then
|
if [ "$RIRU_API" -ge 26 ]; then
|
||||||
mkdir -p "$MODPATH/riru/lib"
|
mkdir -p "$MODPATH/riru/lib"
|
||||||
mkdir -p "$MODPATH/riru/lib64"
|
mkdir -p "$MODPATH/riru/lib64"
|
||||||
touch "$MODPATH/riru/lib/libriru_lspd"
|
touch "$MODPATH/riru/lib/libriru_$RIRU_MODULE_LIB_NAME"
|
||||||
touch "$MODPATH/riru/lib64/libriru_lspd"
|
touch "$MODPATH/riru/lib64/libriru_$RIRU_MODULE_LIB_NAME"
|
||||||
else
|
else
|
||||||
mkdir -p "/data/adb/riru/modules/lspd"
|
mkdir -p "/data/adb/riru/modules/$RIRU_MODULE_LIB_NAME"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,15 @@ debug="false"
|
||||||
|
|
||||||
if [ -r $tmpLspdDex ]; then
|
if [ -r $tmpLspdDex ]; then
|
||||||
java_options="-Djava.class.path=$tmpLspdDex"
|
java_options="-Djava.class.path=$tmpLspdDex"
|
||||||
|
java_options="$java_options -Djava.library.path=/data/local/tmp"
|
||||||
debug="true"
|
debug="true"
|
||||||
elif [ -d "$dir/system" ]; then
|
elif [ -d "$dir/system" ]; then
|
||||||
java_options="-Djava.class.path=$dir/system/framework/lspd.dex"
|
java_options="-Djava.class.path=$dir/system/framework/lspd.dex"
|
||||||
|
java_options="$java_options -Djava.library.path=$dir"
|
||||||
debug="true"
|
debug="true"
|
||||||
else
|
else
|
||||||
java_options="-Djava.class.path=$dir/framework/lspd.dex"
|
java_options="-Djava.class.path=$dir/framework/lspd.dex"
|
||||||
|
java_options="$java_options -Djava.library.path=$dir"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $debug = "true" ]; then
|
if [ $debug = "true" ]; then
|
||||||
|
|
|
||||||
|
|
@ -20,105 +20,12 @@
|
||||||
# Copyright (C) 2021 LSPosed Contributors
|
# Copyright (C) 2021 LSPosed Contributors
|
||||||
#
|
#
|
||||||
|
|
||||||
grep_prop() {
|
|
||||||
local REGEX="s/^$1=//p"
|
|
||||||
shift
|
|
||||||
local FILES="$@"
|
|
||||||
[ -z "$FILES" ] && FILES='/system/build.prop'
|
|
||||||
sed -n "$REGEX" ${FILES} 2>/dev/null | head -n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
MODDIR=${0%/*}
|
MODDIR=${0%/*}
|
||||||
|
|
||||||
MAGISK_VERSION=$(magisk -v)
|
|
||||||
MAGISK_VER_CODE=$(magisk -V)
|
|
||||||
|
|
||||||
[ ! -f $(magisk --path)/.magisk/modules/riru-core/util_functions.sh ] && exit 1
|
|
||||||
. $(magisk --path)/.magisk/modules/riru-core/util_functions.sh
|
|
||||||
|
|
||||||
LSPD_VERSION=$(grep_prop version "${MODDIR}/module.prop")
|
|
||||||
|
|
||||||
ANDROID_SDK=$(getprop ro.build.version.sdk)
|
|
||||||
BUILD_DESC=$(getprop ro.build.description)
|
|
||||||
PRODUCT=$(getprop ro.build.product)
|
|
||||||
MODEL=$(getprop ro.product.model)
|
|
||||||
MANUFACTURER=$(getprop ro.product.manufacturer)
|
|
||||||
BRAND=$(getprop ro.product.brand)
|
|
||||||
FINGERPRINT=$(getprop ro.build.fingerprint)
|
|
||||||
ARCH=$(getprop ro.product.cpu.abi)
|
|
||||||
DEVICE=$(getprop ro.product.device)
|
|
||||||
ANDROID=$(getprop ro.build.version.release)
|
|
||||||
BUILD=$(getprop ro.build.id)
|
|
||||||
|
|
||||||
MISC_PATH=$(cat /data/adb/lspd/misc_path)
|
MISC_PATH=$(cat /data/adb/lspd/misc_path)
|
||||||
BASE_PATH="/data/misc/$MISC_PATH"
|
BASE_PATH="/data/misc/$MISC_PATH"
|
||||||
|
|
||||||
LOG_PATH="/data/adb/lspd/log"
|
LOG_PATH="/data/adb/lspd/log"
|
||||||
ENABLE_VERBOSE_LOG_FILE="/data/adb/lspd/config/verbose_log"
|
|
||||||
LOG_VERBOSE=false
|
|
||||||
|
|
||||||
if [ "$(cat "${ENABLE_VERBOSE_LOG_FILE}")" = "1" ]; then
|
|
||||||
LOG_VERBOSE=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If logcat client is kicked out by klogd server, we'll restart it.
|
|
||||||
# However, if it is killed manually or by LSPosed Manager, we'll exit.
|
|
||||||
loop_logcat() {
|
|
||||||
while true; do
|
|
||||||
logcat $*
|
|
||||||
if [ $? -ne 1 ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
print_log_head() {
|
|
||||||
LOG_FILE=$1
|
|
||||||
touch "${LOG_FILE}"
|
|
||||||
chmod 666 "${LOG_FILE}"
|
|
||||||
{
|
|
||||||
echo "LSPosed Log"
|
|
||||||
echo "--------- beginning of information"
|
|
||||||
echo "Manufacturer: ${MANUFACTURER}"
|
|
||||||
echo "Brand: ${BRAND}"
|
|
||||||
echo "Device: ${DEVICE}"
|
|
||||||
echo "Product: ${PRODUCT}"
|
|
||||||
echo "Model: ${MODEL}"
|
|
||||||
echo "Fingerprint: ${FINGERPRINT}"
|
|
||||||
echo "ROM description: ${BUILD_DESC}"
|
|
||||||
echo "Architecture: ${ARCH}"
|
|
||||||
echo "Android build: ${BUILD}"
|
|
||||||
echo "Android version: ${ANDROID}"
|
|
||||||
echo "Android sdk: ${ANDROID_SDK}"
|
|
||||||
echo "LSPosed version: ${LSPD_VERSION}"
|
|
||||||
echo "Riru version: ${RIRU_VERSION_NAME} (${RIRU_VERSION_CODE})"
|
|
||||||
echo "Riru api: ${RIRU_API}"
|
|
||||||
echo "Magisk: ${MAGISK_VERSION%:*} (${MAGISK_VER_CODE})"
|
|
||||||
} >>"${LOG_FILE}"
|
|
||||||
}
|
|
||||||
|
|
||||||
start_log_catcher() {
|
|
||||||
LOG_FILE_NAME=$1
|
|
||||||
LOG_TAG_FILTERS=$2
|
|
||||||
CLEAN_OLD=$3
|
|
||||||
START_NEW=$4
|
|
||||||
LOG_FILE="${LOG_PATH}/${LOG_FILE_NAME}.log"
|
|
||||||
PID_FILE="${LOG_PATH}/${LOG_FILE_NAME}.pid"
|
|
||||||
mkdir -p ${LOG_PATH}
|
|
||||||
if [ ${CLEAN_OLD} == true ]; then
|
|
||||||
rm "${LOG_FILE}.old"
|
|
||||||
mv "${LOG_FILE}" "${LOG_FILE}.old"
|
|
||||||
fi
|
|
||||||
rm "${LOG_PATH}/${LOG_FILE_NAME}.pid"
|
|
||||||
if [ ${START_NEW} == false ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
touch "${PID_FILE}"
|
|
||||||
print_log_head "${LOG_FILE}"
|
|
||||||
loop_logcat -f "${LOG_FILE}" *:S "${LOG_TAG_FILTERS}" &
|
|
||||||
LOG_PID=$!
|
|
||||||
echo "${LOG_PID}" >"${LOG_PATH}/${LOG_FILE_NAME}.pid"
|
|
||||||
}
|
|
||||||
|
|
||||||
chcon -R u:object_r:system_file:s0 "${MODDIR}"
|
chcon -R u:object_r:system_file:s0 "${MODDIR}"
|
||||||
chcon -R u:object_r:system_file:s0 "/data/adb/lspd"
|
chcon -R u:object_r:system_file:s0 "/data/adb/lspd"
|
||||||
|
|
@ -130,9 +37,6 @@ chcon -R u:object_r:magisk_file:s0 ${LOG_PATH}
|
||||||
if [ ! -z "${MISC_PATH}" ]; then
|
if [ ! -z "${MISC_PATH}" ]; then
|
||||||
chcon -R u:object_r:magisk_file:s0 "${BASE_PATH}"
|
chcon -R u:object_r:magisk_file:s0 "${BASE_PATH}"
|
||||||
chmod 771 "${BASE_PATH}"
|
chmod 771 "${BASE_PATH}"
|
||||||
print_log_head "${LOG_PATH}/modules.log"
|
|
||||||
# start_verbose_log_catcher
|
|
||||||
start_log_catcher all "LSPosed:V XSharedPreferences:V LSPosed-Bridge:V LSPosedManager:V LSPosedService:V *:F" true ${LOG_VERBOSE}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "/data/local/tmp/lspd.dex"
|
rm -f "/data/local/tmp/lspd.dex"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@
|
||||||
public static boolean execTransact(int, long, long, int);
|
public static boolean execTransact(int, long, long, int);
|
||||||
public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder);
|
public static android.os.IBinder getApplicationServiceForSystemServer(android.os.IBinder, android.os.IBinder);
|
||||||
}
|
}
|
||||||
|
-keepclasseswithmembers class org.lsposed.lspd.service.LogcatService {
|
||||||
|
private int refreshFd();
|
||||||
|
}
|
||||||
-assumenosideeffects class android.util.Log {
|
-assumenosideeffects class android.util.Log {
|
||||||
public static *** v(...);
|
public static *** v(...);
|
||||||
public static *** d(...);
|
public static *** d(...);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
include src/main/cpp/external/DexBuilder/Android.mk
|
include src/main/cpp/external/DexBuilder/Android.mk
|
||||||
include src/main/cpp/external/yahfa/Android.mk
|
include src/main/cpp/external/yahfa/Android.mk
|
||||||
include src/main/cpp/main/Android.mk
|
include src/main/cpp/main/Android.mk
|
||||||
|
include src/main/cpp/daemon/Android.mk
|
||||||
|
|
||||||
$(call import-module,prefab/cxx)
|
$(call import-module,prefab/cxx)
|
||||||
$(call import-module,prefab/riru)
|
$(call import-module,prefab/riru)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := daemon
|
||||||
|
LOCAL_SRC_FILES := logcat.cpp
|
||||||
|
LOCAL_STATIC_LIBRARIES := cxx
|
||||||
|
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
|
||||||
|
LOCAL_LDLIBS := -llog
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
#include <jni.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "logcat.h"
|
||||||
|
|
||||||
|
class UniqueFile : public std::unique_ptr<FILE, std::function<void(FILE *)>> {
|
||||||
|
inline static deleter_type deleter = [](auto f) { f && f != stdout && fclose(f); };
|
||||||
|
public:
|
||||||
|
explicit UniqueFile(FILE *f) : std::unique_ptr<FILE, std::function<void(FILE *)>>(f, deleter) {}
|
||||||
|
|
||||||
|
UniqueFile(int fd, const char *mode) : UniqueFile(fd > 0 ? fdopen(fd, mode) : stdout) {};
|
||||||
|
|
||||||
|
UniqueFile() : UniqueFile(nullptr) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class Logcat {
|
||||||
|
public:
|
||||||
|
explicit Logcat(JNIEnv *env, jobject thiz, jmethodID method, jlong tid) :
|
||||||
|
env_(env), thiz_(thiz), refresh_fd_method_(method), tid_(tid) { RefreshFd(); };
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RefreshFd();
|
||||||
|
|
||||||
|
bool ProcessBuffer(struct log_msg *buf);
|
||||||
|
|
||||||
|
static char FilterPriToChar(android_LogPriority pri);
|
||||||
|
|
||||||
|
static void PrintLogLine(FILE *out, const AndroidLogEntry *entry);
|
||||||
|
|
||||||
|
JNIEnv *env_;
|
||||||
|
jobject thiz_;
|
||||||
|
jmethodID refresh_fd_method_;
|
||||||
|
jlong tid_;
|
||||||
|
|
||||||
|
UniqueFile out_file_{};
|
||||||
|
size_t print_count_ = 0;
|
||||||
|
|
||||||
|
constexpr static size_t kMaxCount = 500;
|
||||||
|
constexpr static uint32_t kSetLogSize = 32 * 1024 * 1024;
|
||||||
|
};
|
||||||
|
|
||||||
|
char Logcat::FilterPriToChar(android_LogPriority pri) {
|
||||||
|
switch (pri) {
|
||||||
|
case ANDROID_LOG_VERBOSE:
|
||||||
|
return 'V';
|
||||||
|
case ANDROID_LOG_DEBUG:
|
||||||
|
return 'D';
|
||||||
|
case ANDROID_LOG_INFO:
|
||||||
|
return 'I';
|
||||||
|
case ANDROID_LOG_WARN:
|
||||||
|
return 'W';
|
||||||
|
case ANDROID_LOG_ERROR:
|
||||||
|
return 'E';
|
||||||
|
case ANDROID_LOG_FATAL:
|
||||||
|
return 'F';
|
||||||
|
case ANDROID_LOG_SILENT:
|
||||||
|
return 'S';
|
||||||
|
case ANDROID_LOG_DEFAULT:
|
||||||
|
case ANDROID_LOG_UNKNOWN:
|
||||||
|
default:
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logcat::PrintLogLine(FILE *out, const AndroidLogEntry *entry) {
|
||||||
|
if (!out) return;
|
||||||
|
constexpr static size_t kMaxTimeBuff = 64;
|
||||||
|
struct tm tm{};
|
||||||
|
std::array<char, kMaxTimeBuff> time_buff;
|
||||||
|
|
||||||
|
auto now = entry->tv_sec;
|
||||||
|
auto nsec = entry->tv_nsec;
|
||||||
|
if (now < 0) {
|
||||||
|
nsec = NS_PER_SEC - nsec;
|
||||||
|
}
|
||||||
|
localtime_r(&now, &tm);
|
||||||
|
strftime(time_buff.data(), time_buff.size(), "%Y-%m-%dT%H:%M:%S", &tm);
|
||||||
|
fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n", time_buff.data(), nsec / MS_PER_NSEC,
|
||||||
|
entry->uid, entry->pid, entry->tid,
|
||||||
|
FilterPriToChar(entry->priority), static_cast<int>(entry->tagLen), entry->tag,
|
||||||
|
static_cast<int>(entry->messageLen), entry->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logcat::RefreshFd() {
|
||||||
|
out_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_), "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logcat::ProcessBuffer(struct log_msg *buf) {
|
||||||
|
int err;
|
||||||
|
AndroidLogEntry entry;
|
||||||
|
err = android_log_processLogBuffer(&buf->entry, &entry);
|
||||||
|
if (err < 0) return false;
|
||||||
|
|
||||||
|
std::string_view tag(entry.tag);
|
||||||
|
if (buf->id() == log_id::LOG_ID_CRASH ||
|
||||||
|
tag == "Magisk" ||
|
||||||
|
tag.starts_with("Riru") ||
|
||||||
|
tag.starts_with("LSPosed") ||
|
||||||
|
tag == "XSharedPreferences") {
|
||||||
|
++print_count_;
|
||||||
|
PrintLogLine(out_file_.get(), &entry);
|
||||||
|
}
|
||||||
|
return entry.pid == getpid() &&
|
||||||
|
tag == "LSPosedLogcat" &&
|
||||||
|
std::string_view(entry.message) == "!!stop!!" + std::to_string(tid_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logcat::Run() {
|
||||||
|
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
|
||||||
|
nullptr, &android_logger_list_free};
|
||||||
|
|
||||||
|
logger_list.reset(android_logger_list_alloc(0, 0, 0));
|
||||||
|
|
||||||
|
for (log_id id:{LOG_ID_MAIN, LOG_ID_CRASH}) {
|
||||||
|
auto *logger = android_logger_open(logger_list.get(), id);
|
||||||
|
if (logger == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
android_logger_set_log_size(logger, kSetLogSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct log_msg msg{};
|
||||||
|
int ret = android_logger_list_read(logger_list.get(), &msg);
|
||||||
|
|
||||||
|
if (ret <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProcessBuffer(&msg)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(out_file_.get());
|
||||||
|
|
||||||
|
if (print_count_ > kMaxCount) {
|
||||||
|
RefreshFd();
|
||||||
|
print_count_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_lsposed_lspd_service_LogcatService_runLogcat(JNIEnv *env, jobject thiz, jlong tid) {
|
||||||
|
jclass clazz = env->GetObjectClass(thiz);
|
||||||
|
jmethodID method = env->GetMethodID(clazz, "refreshFd", "()I");
|
||||||
|
Logcat logcat(env, thiz, method, tid);
|
||||||
|
logcat.Run();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
#define NS_PER_SEC 1000000000ULL
|
||||||
|
#define MS_PER_NSEC 1000000
|
||||||
|
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct AndroidLogEntry_t {
|
||||||
|
time_t tv_sec;
|
||||||
|
long tv_nsec;
|
||||||
|
android_LogPriority priority;
|
||||||
|
int32_t uid;
|
||||||
|
int32_t pid;
|
||||||
|
int32_t tid;
|
||||||
|
const char *tag;
|
||||||
|
size_t tagLen;
|
||||||
|
size_t messageLen;
|
||||||
|
const char *message;
|
||||||
|
} AndroidLogEntry;
|
||||||
|
|
||||||
|
struct logger_entry {
|
||||||
|
uint16_t len; /* length of the payload */
|
||||||
|
uint16_t hdr_size; /* sizeof(struct logger_entry) */
|
||||||
|
int32_t pid; /* generating process's pid */
|
||||||
|
uint32_t tid; /* generating process's tid */
|
||||||
|
uint32_t sec; /* seconds since Epoch */
|
||||||
|
uint32_t nsec; /* nanoseconds */
|
||||||
|
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
|
||||||
|
uint32_t uid; /* generating process's uid */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct log_msg {
|
||||||
|
union alignas(4) {
|
||||||
|
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
|
||||||
|
struct logger_entry entry;
|
||||||
|
};
|
||||||
|
#ifdef __cplusplus
|
||||||
|
log_id_t id() {
|
||||||
|
return static_cast<log_id_t>(entry.lid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
struct logger;
|
||||||
|
struct logger_list;
|
||||||
|
|
||||||
|
int android_logger_set_log_size(struct logger *logger, unsigned long size);
|
||||||
|
struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid);
|
||||||
|
void android_logger_list_free(struct logger_list *logger_list);
|
||||||
|
int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg);
|
||||||
|
struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id);
|
||||||
|
int android_log_processLogBuffer(struct logger_entry *buf, AndroidLogEntry *entry);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -22,7 +22,6 @@ package org.lsposed.lspd.core;
|
||||||
|
|
||||||
import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
import static org.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.app.LoadedApk;
|
import android.app.LoadedApk;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
@ -48,7 +47,6 @@ import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void startBootstrapHook(boolean isSystem, String appDataDir) {
|
public static void startBootstrapHook(boolean isSystem, String appDataDir) {
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,7 @@ public class ConfigManager {
|
||||||
private static final File resourceHookSwitch = new File(configPath, "enable_resources");
|
private static final File resourceHookSwitch = new File(configPath, "enable_resources");
|
||||||
private boolean resourceHook = false;
|
private boolean resourceHook = false;
|
||||||
|
|
||||||
private static final File verboseLogSwitch = new File(configPath, "verbose_log");
|
private boolean logcat = true;
|
||||||
private boolean verboseLog = false;
|
|
||||||
|
|
||||||
private static final File managerPath = new File(configPath, "manager");
|
private static final File managerPath = new File(configPath, "manager");
|
||||||
private String manager = null;
|
private String manager = null;
|
||||||
|
|
@ -107,9 +106,8 @@ public class ConfigManager {
|
||||||
private String miscPath = null;
|
private String miscPath = null;
|
||||||
|
|
||||||
private static final File logPath = new File(basePath, "log");
|
private static final File logPath = new File(basePath, "log");
|
||||||
private static final File modulesLog = new File(logPath, "modules.log");
|
private static final File modulesLog = new File(logPath, "modules.txt");
|
||||||
private static final File oldModulesLog = new File(logPath, "modules.old.log");
|
private static final File oldModulesLog = new File(logPath, "modules.old.txt");
|
||||||
private static final File verboseLogPath = new File(logPath, "all.log");
|
|
||||||
|
|
||||||
static class FileLocker {
|
static class FileLocker {
|
||||||
private final FileChannel lockChannel;
|
private final FileChannel lockChannel;
|
||||||
|
|
@ -136,7 +134,7 @@ public class ConfigManager {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(basePath.toPath());
|
Files.createDirectories(logPath.toPath());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
}
|
}
|
||||||
|
|
@ -310,7 +308,6 @@ public class ConfigManager {
|
||||||
|
|
||||||
private synchronized void updateConfig() {
|
private synchronized void updateConfig() {
|
||||||
resourceHook = readInt(resourceHookSwitch, 0) == 1;
|
resourceHook = readInt(resourceHookSwitch, 0) == 1;
|
||||||
verboseLog = readInt(verboseLogSwitch, 0) == 1;
|
|
||||||
miscPath = "/data/misc/" + readText(miscFile, "lspd");
|
miscPath = "/data/misc/" + readText(miscFile, "lspd");
|
||||||
updateManager();
|
updateManager();
|
||||||
}
|
}
|
||||||
|
|
@ -870,9 +867,14 @@ public class ConfigManager {
|
||||||
this.resourceHook = resourceHook;
|
this.resourceHook = resourceHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerboseLog(boolean verboseLog) {
|
public void setVerboseLog(boolean on) {
|
||||||
writeInt(verboseLogSwitch, verboseLog ? 1 : 0);
|
var logcatService = ServiceManager.getLogcatService();
|
||||||
this.verboseLog = verboseLog;
|
if (on) {
|
||||||
|
logcatService.start();
|
||||||
|
} else {
|
||||||
|
logcatService.stop();
|
||||||
|
}
|
||||||
|
logcat = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean resourceHook() {
|
public boolean resourceHook() {
|
||||||
|
|
@ -880,7 +882,11 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verboseLog() {
|
public boolean verboseLog() {
|
||||||
return verboseLog;
|
return logcat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getLogPath() {
|
||||||
|
return logPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParcelFileDescriptor getModulesLog(int mode) {
|
public ParcelFileDescriptor getModulesLog(int mode) {
|
||||||
|
|
@ -898,7 +904,8 @@ public class ConfigManager {
|
||||||
|
|
||||||
public ParcelFileDescriptor getVerboseLog() {
|
public ParcelFileDescriptor getVerboseLog() {
|
||||||
try {
|
try {
|
||||||
return ParcelFileDescriptor.open(verboseLogPath, ParcelFileDescriptor.MODE_READ_ONLY);
|
var logcat = ServiceManager.getLogcatService().getLog();
|
||||||
|
return ParcelFileDescriptor.open(logcat, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(e));
|
Log.e(TAG, Log.getStackTraceString(e));
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -907,7 +914,8 @@ public class ConfigManager {
|
||||||
|
|
||||||
public boolean clearLogs(boolean verbose) {
|
public boolean clearLogs(boolean verbose) {
|
||||||
try {
|
try {
|
||||||
OutputStream os = new FileOutputStream(verbose ? verboseLogPath : modulesLog);
|
var logcat = ServiceManager.getLogcatService().getLog();
|
||||||
|
OutputStream os = new FileOutputStream(verbose ? logcat : modulesLog);
|
||||||
os.close();
|
os.close();
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class LogcatService implements Runnable {
|
||||||
|
private static final String TAG = "LSPosedLogcat";
|
||||||
|
private Thread thread;
|
||||||
|
private final File logPath;
|
||||||
|
private File log = null;
|
||||||
|
private static DateTimeFormatter logTimeFormat;
|
||||||
|
|
||||||
|
public LogcatService(File logPath) {
|
||||||
|
System.loadLibrary("daemon");
|
||||||
|
this.logPath = logPath;
|
||||||
|
var zone = ZoneId.of(SystemProperties.get("persist.sys.timezone", "GMT"));
|
||||||
|
logTimeFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Log.i(TAG, "start running");
|
||||||
|
runLogcat(thread.getId());
|
||||||
|
Log.i(TAG, "stoped");
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void runLogcat(long tid);
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private int refreshFd() {
|
||||||
|
if (log == null || log.length() > 32 * 1024 * 1024) {
|
||||||
|
log = new File(logPath, logTimeFormat.format(Instant.now()) + ".log");
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE |
|
||||||
|
ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_APPEND;
|
||||||
|
try {
|
||||||
|
return ParcelFileDescriptor.open(log, mode).detachFd();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.w(TAG, "someone chattr +i ?", e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (isRunning()) return;
|
||||||
|
thread = new Thread(this);
|
||||||
|
thread.setName("logcat");
|
||||||
|
thread.setUncaughtExceptionHandler((t, e) -> {
|
||||||
|
Log.e(TAG, "Crash unexpectedly: ", e);
|
||||||
|
thread = null;
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
// logcat thread is listening for this keyword
|
||||||
|
Log.i(TAG, "!!stop!!" + thread.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return thread != null && thread.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getLog() {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,13 +37,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import hidden.HiddenApiBridge;
|
import hidden.HiddenApiBridge;
|
||||||
|
|
||||||
public class ServiceManager {
|
public class ServiceManager {
|
||||||
|
public static final String TAG = "LSPosedService";
|
||||||
|
private static final ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
||||||
|
private static final File globalNamespace = new File("/proc/1/root");
|
||||||
private static LSPosedService mainService = null;
|
private static LSPosedService mainService = null;
|
||||||
final private static ConcurrentHashMap<String, LSPModuleService> moduleServices = new ConcurrentHashMap<>();
|
|
||||||
private static LSPApplicationService applicationService = null;
|
private static LSPApplicationService applicationService = null;
|
||||||
private static LSPManagerService managerService = null;
|
private static LSPManagerService managerService = null;
|
||||||
private static LSPSystemServerService systemServerService = null;
|
private static LSPSystemServerService systemServerService = null;
|
||||||
public static final String TAG = "LSPosedService";
|
private static LogcatService logcatService = null;
|
||||||
private static final File globalNamespace = new File("/proc/1/root");
|
|
||||||
|
|
||||||
private static void waitSystemService(String name) {
|
private static void waitSystemService(String name) {
|
||||||
while (android.os.ServiceManager.getService(name) == null) {
|
while (android.os.ServiceManager.getService(name) == null) {
|
||||||
|
|
@ -77,6 +78,9 @@ public class ServiceManager {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logcatService = new LogcatService(ConfigManager.getLogPath());
|
||||||
|
logcatService.start();
|
||||||
|
|
||||||
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
|
||||||
Looper.prepareMainLooper();
|
Looper.prepareMainLooper();
|
||||||
mainService = new LSPosedService();
|
mainService = new LSPosedService();
|
||||||
|
|
@ -137,6 +141,10 @@ public class ServiceManager {
|
||||||
return managerService;
|
return managerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LogcatService getLogcatService() {
|
||||||
|
return logcatService;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean systemServerRequested() {
|
public static boolean systemServerRequested() {
|
||||||
return systemServerService.systemServerRequested();
|
return systemServerService.systemServerRequested();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class ModuleLogger {
|
public class ModuleLogger {
|
||||||
private static DateTimeFormatter logDateFormat;
|
private static DateTimeFormatter logDateFormat;
|
||||||
|
|
@ -39,7 +40,8 @@ public class ModuleLogger {
|
||||||
if (fd == null && fileDescriptor != null) {
|
if (fd == null && fileDescriptor != null) {
|
||||||
fd = fileDescriptor;
|
fd = fileDescriptor;
|
||||||
var zone = ZoneId.of(SystemProperties.get("persist.sys.timezone", "GMT"));
|
var zone = ZoneId.of(SystemProperties.get("persist.sys.timezone", "GMT"));
|
||||||
logDateFormat = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(zone);
|
var pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS"; // DateTimeFormatter.ISO_LOCAL_DATE_TIME
|
||||||
|
logDateFormat = DateTimeFormatter.ofPattern(pattern, Locale.ROOT).withZone(zone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,24 +50,16 @@ public class ModuleLogger {
|
||||||
Utils.logE("Logger is not initialized");
|
Utils.logE("Logger is not initialized");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
String processName = ActivityThread.currentProcessName();
|
String processName = ActivityThread.currentProcessName();
|
||||||
|
var log = String.format(Locale.ROOT, "[ %s %5d:%5d:%5d %c/%s ] %s\n",
|
||||||
sb.append(logDateFormat.format(Instant.now()));
|
logDateFormat.format(Instant.now()),
|
||||||
sb.append(' ');
|
Process.myUid(),
|
||||||
sb.append(isThrowable ? "E" : "I");
|
Process.myPid(),
|
||||||
sb.append('/');
|
Process.myTid(),
|
||||||
sb.append(processName == null ? "android" : processName);
|
isThrowable ? 'E' : 'I',
|
||||||
sb.append('(');
|
processName == null ? "android" : processName,
|
||||||
sb.append(Process.myPid());
|
str);
|
||||||
sb.append('-');
|
|
||||||
sb.append(Process.myTid());
|
|
||||||
sb.append(')');
|
|
||||||
sb.append(": ");
|
|
||||||
sb.append(str);
|
|
||||||
sb.append('\n');
|
|
||||||
try {
|
try {
|
||||||
var log = sb.toString();
|
|
||||||
var writer = new FileWriter(fd.getFileDescriptor());
|
var writer = new FileWriter(fd.getFileDescriptor());
|
||||||
writer.write(log, 0, log.length());
|
writer.write(log, 0, log.length());
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue