├── .gitignore ├── Frozen-Main ├── Frozen │ ├── build_pack.ps1 │ └── src │ │ ├── doze.hpp │ │ ├── freezeit.hpp │ │ ├── freezer.hpp │ │ ├── main.cpp │ │ ├── managedApp.hpp │ │ ├── server.hpp │ │ ├── settings.hpp │ │ ├── systemTools.hpp │ │ ├── utils.hpp │ │ └── vpopen.hpp ├── README.md ├── doze.hpp ├── freezeit.hpp ├── freezer.hpp ├── main.cpp ├── managedApp.hpp ├── server.hpp ├── settings.hpp ├── systemTools.hpp ├── utils.hpp └── vpopen.hpp ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof 34 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/build_pack.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoWei-2077/Frozen/5908dedb23c4583871bd8f29555fc32503c501b3/Frozen-Main/Frozen/build_pack.ps1 -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/doze.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "vpopen.hpp" 5 | #include "managedApp.hpp" 6 | #include "freezeit.hpp" 7 | #include "systemTools.hpp" 8 | 9 | class Doze { 10 | private: 11 | Freezeit& freezeit; 12 | ManagedApp& managedApp; 13 | SystemTools& systemTools; 14 | Settings& settings; 15 | 16 | time_t enterDozeTimeStamp = 0; 17 | uint32_t enterDozeCycleStamp = 0; 18 | time_t lastInteractiveTime = time(nullptr); // 上次检查为 亮屏或充电 的时间戳 19 | 20 | void updateDozeWhitelist() { 21 | START_TIME_COUNT; 22 | 23 | const char* cmdList[] = { "/system/bin/dumpsys", "dumpsys", "deviceidle", "whitelist", 24 | nullptr }; 25 | char buf[1024 * 32]; 26 | VPOPEN::vpopen(cmdList[0], cmdList + 1, buf, sizeof(buf)); 27 | 28 | stringstream ss; 29 | ss << buf; 30 | 31 | string tmp, tmpLabel, line; 32 | set existSet; 33 | 34 | // https://cs.android.com/android/platform/superproject/+/android-12.1.0_r27:frameworks/base/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java;l=485 35 | // "system-excidle,xxx,uid" 该名单在Doze模式会失效 36 | // "system,xxx,uid" 37 | // "user,xxx,uid" 38 | while (getline(ss, line)) { 39 | if (!line.starts_with("system,") && !line.starts_with("user")) continue; 40 | if (line.length() < 10)continue; 41 | if (line[line.length() - 6] != ',')continue; 42 | 43 | int uid = atoi(line.c_str() + line.length() - 5); 44 | if (!managedApp.contains(uid))continue; 45 | 46 | auto& appInfo = managedApp[uid]; 47 | if (appInfo.isBlacklist()) { 48 | tmp += "dumpsys deviceidle whitelist -" + appInfo.package + ";"; 49 | tmpLabel += appInfo.label + " "; 50 | } 51 | else 52 | existSet.insert(uid); 53 | } 54 | 55 | if (tmp.length()) { 56 | freezeit.logFmt("移除电池优化白名单: %s", tmpLabel.c_str()); 57 | system(tmp.c_str()); 58 | } 59 | 60 | tmp.clear(); 61 | tmpLabel.clear(); 62 | for (const auto& appInfo : managedApp.appInfoMap) { 63 | if (appInfo.uid < ManagedApp::UID_START || appInfo.isSystemApp) continue; 64 | 65 | if (appInfo.isWhitelist() && !existSet.contains(appInfo.uid)) { 66 | tmp += "dumpsys deviceidle whitelist +" + appInfo.package + ";"; 67 | tmpLabel += appInfo.label + " "; 68 | } 69 | } 70 | if (tmp.length()) { 71 | freezeit.logFmt("加入电池优化白名单: %s", tmpLabel.c_str()); 72 | system(tmp.c_str()); 73 | } 74 | 75 | if (settings.enableDebug) { 76 | tmp.clear(); 77 | for (const auto uid : existSet) 78 | tmp += managedApp[uid].label + " "; 79 | if (tmp.length()) 80 | freezeit.logFmt("已在白名单: %s", tmp.c_str()); 81 | } 82 | 83 | END_TIME_COUNT; 84 | } 85 | 86 | // 0获取失败 1息屏 2亮屏 87 | int getScreenByLocalSocket() { 88 | START_TIME_COUNT; 89 | 90 | int buff[64]; 91 | int recvLen = Utils::localSocketRequest(XPOSED_CMD::GET_SCREEN, nullptr, 0, buff, 92 | sizeof(buff)); 93 | 94 | if (recvLen == 0) { 95 | freezeit.logFmt("%s() 工作异常, 请确认LSPosed中Frozen勾选系统框架, 然后重启", __FUNCTION__); 96 | END_TIME_COUNT; 97 | return 0; 98 | } 99 | else if (recvLen != 4) { 100 | freezeit.logFmt("%s() 屏幕数据异常 recvLen[%d]", __FUNCTION__, recvLen); 101 | if (recvLen > 0 && recvLen < 64 * 4) 102 | freezeit.logFmt("DumpHex: [%s]", Utils::bin2Hex(buff, recvLen).c_str()); 103 | END_TIME_COUNT; 104 | return 0; 105 | } 106 | 107 | if (settings.enableDebug) { 108 | const char* str[3] = { "Doze调试: Xposed 获取屏幕状态失败", 109 | "Doze调试: Xposed 息屏中", 110 | "Doze调试: Xposed 亮屏中" }; 111 | freezeit.log(str[buff[0] < 3 ? buff[0] : 1]); 112 | } 113 | 114 | 115 | END_TIME_COUNT; 116 | return buff[0]; 117 | } 118 | 119 | bool isInteractive() { 120 | /* 121 | [debug.tracing.screen_brightness]: [0.05468459] 0-1 / 0-16384 122 | [debug.tracing.screen_state]: [2] 亮屏[2] 息屏[1] 123 | https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/Display.java;l=387 124 | enum DisplayStateEnum 125 | public static final int DISPLAY_STATE_UNKNOWN = 0; 126 | public static final int DISPLAY_STATE_OFF = 1; 127 | public static final int DISPLAY_STATE_ON = 2; 128 | public static final int DISPLAY_STATE_DOZE = 3; //亮屏但处于Doze的非交互状态状态 129 | public static final int DISPLAY_STATE_DOZE_SUSPEND = 4; // 同上,但CPU不控制显示,由协处理器或其他控制 130 | public static final int DISPLAY_STATE_VR = 5; 131 | public static final int DISPLAY_STATE_ON_SUSPEND = 6; //非Doze, 类似4 132 | */ 133 | do { 134 | // MAX LEN: 96 135 | 136 | int mScreenState = systemTools.getScreenProperty(); 137 | if (mScreenState < 1) 138 | mScreenState = getScreenByLocalSocket(); 139 | 140 | if (settings.enableDebug) 141 | if (mScreenState != 1 && mScreenState != 2) 142 | freezeit.logFmt("Doze调试: 屏幕其他状态 mScreenState[%d]", mScreenState); 143 | 144 | if (mScreenState == 2 || mScreenState == 5 || mScreenState == 6) { 145 | if (settings.enableDebug) 146 | freezeit.logFmt("Doze调试: 亮屏中 mScreenState[%d]", mScreenState); 147 | break; 148 | } 149 | 150 | if (mScreenState <= 0) { 151 | freezeit.logFmt("屏幕状态获取失败 mScreenState[%d] 若开机至今未曾息屏,则无法获取屏幕状态", mScreenState); 152 | break; 153 | } 154 | 155 | // 以下则是息屏: 1 3 4 156 | 157 | if (systemTools.isAudioPlaying.load(std::memory_order_relaxed)) { 158 | if (settings.enableDebug) 159 | freezeit.log("Doze调试: 息屏, 播放中"); 160 | break; 161 | } 162 | 163 | // "Unknown", "Charging", "Discharging", "Not charging", "Full" 164 | // https://cs.android.com/android/kernel/superproject/+/common-android-mainline-kleaf:common/drivers/power/supply/power_supply_sysfs.c;l=75 165 | char res[64]; 166 | Utils::readString("/sys/class/power_supply/battery/status", res, sizeof(res)); 167 | if (!strncmp(res, "Charging", 4) || !strncmp(res, "Full", 4)) { 168 | if (settings.enableDebug) 169 | freezeit.log("Doze调试: 息屏, 充电中"); 170 | break; 171 | } 172 | 173 | if (!strncmp(res, "Discharging", 4) || !strncmp(res, "Not charging", 4)) { 174 | if (settings.enableDebug) 175 | freezeit.log("Doze调试: 息屏, 未充电"); 176 | return false; 177 | } 178 | 179 | if (settings.enableDebug) 180 | freezeit.logFmt("Doze调试: 息屏, 电池状态未知 [%s]", res); 181 | 182 | } while (false); 183 | 184 | lastInteractiveTime = time(nullptr); 185 | return true; 186 | } 187 | 188 | public: 189 | Doze& operator=(Doze&&) = delete; 190 | 191 | bool isScreenOffStandby = false; 192 | 193 | Doze(Freezeit& freezeit, Settings& settings, ManagedApp& managedApp, SystemTools& systemTools) : 194 | freezeit(freezeit), managedApp(managedApp), systemTools(systemTools), settings(settings) { 195 | updateUidTime(); 196 | } 197 | 198 | bool checkIfNeedToExit() { 199 | START_TIME_COUNT; 200 | if (!isInteractive()) { 201 | if (settings.enableDebug) 202 | freezeit.log("Doze调试: 息屏中, 发现有活动"); 203 | 204 | END_TIME_COUNT; 205 | return false; 206 | } 207 | 208 | isScreenOffStandby = false; 209 | 210 | if (settings.enableDoze) { 211 | //if (!Utils::popenShell("dumpsys deviceidle unforce")) freezeit.log("退出Standby休眠模式失败"); 212 | system("dumpsys deviceidle unforce"); 213 | 214 | int deltaTime = time(nullptr) - enterDozeTimeStamp; 215 | const int activeRate = 216 | deltaTime > 0 ? (1000 * (systemTools.cycleCnt - enterDozeCycleStamp) / 217 | deltaTime) : 0; //CPU 活跃率 218 | 219 | if (deltaTime < 300) { 220 | if (deltaTime >= 60) 221 | freezeit.logFmt("退出Doze 小睡了 %d分%d秒", deltaTime / 60, deltaTime % 60); 222 | else 223 | freezeit.logFmt("退出Doze 小睡了 %d秒", deltaTime % 60); 224 | } 225 | else { 226 | stackString<1024 * 16> tmp; 227 | 228 | if (activeRate <= 85) 229 | tmp.append("🤪 退出深度Doze 时长 "); 230 | else 231 | tmp.append("🤪 这段时间未能进入深度Doze, 请检查应用的唤醒锁使用情况 时长 "); 232 | 233 | if (deltaTime >= 3600) { 234 | tmp.appendFmt("%d时", deltaTime / 3600); 235 | deltaTime %= 3600; 236 | } 237 | if (deltaTime >= 60) { 238 | tmp.appendFmt("%d分", deltaTime / 60); 239 | deltaTime %= 60; 240 | } 241 | if (deltaTime) tmp.appendFmt("%d秒", deltaTime); 242 | tmp.appendFmt(" 唤醒率 %d.%d %%", activeRate / 10, activeRate % 10); 243 | freezeit.log(tmp.c_str()); 244 | 245 | struct st { 246 | int uid; 247 | int delta; 248 | }; 249 | vector uidTimeSort; 250 | uidTimeSort.reserve(32); 251 | for (const auto& [uid, timeList] : updateUidTime()) { 252 | int delta = (timeList.total - timeList.lastTotal); // 毫秒 253 | if (delta <= 100)continue; // 过滤 100毫秒 254 | uidTimeSort.emplace_back(st{ uid, delta }); 255 | } 256 | 257 | std::sort(uidTimeSort.begin(), uidTimeSort.end(), 258 | [](const st& a, const st& b) { return a.delta > b.delta; }); 259 | 260 | tmp.clear(); 261 | for (auto& [uid, delta] : uidTimeSort) { 262 | tmp.append("[", 1); 263 | const int minutesMilliSec = 60 * 1000; 264 | if (delta >= minutesMilliSec) { 265 | tmp.appendFmt("%d分", delta / minutesMilliSec); 266 | delta %= minutesMilliSec; 267 | } 268 | tmp.appendFmt("%d.%03d秒] ", delta / 1000, delta % 1000); 269 | tmp.appendFmt("%s\n", managedApp.getLabel(uid).c_str()); 270 | } 271 | 272 | if (tmp.length) 273 | freezeit.logFmt("Doze期间应用的CPU活跃时间:\n\n%s", *tmp); 274 | } 275 | } 276 | END_TIME_COUNT; 277 | return true; 278 | } 279 | 280 | bool checkIfNeedToEnter() { 281 | constexpr int TIMEOUT = 60; 282 | static int secCnt = 30; 283 | 284 | if (isScreenOffStandby || ++secCnt < TIMEOUT) 285 | return false; 286 | 287 | secCnt = 0; 288 | 289 | if (isInteractive()) 290 | return false; 291 | 292 | const time_t nowTimeStamp = time(nullptr); 293 | if ((nowTimeStamp - lastInteractiveTime) < (TIMEOUT + 60L)) 294 | return false; 295 | 296 | if (settings.enableDebug) 297 | freezeit.log("息屏状态已超时,正在确认息屏状态"); 298 | 299 | // 如果系统之前已经自行进入轻度Doze, 退出Doze的瞬间(此时可能还没亮屏)导致现在才执行时间判断 300 | // 此时进入Doze不合理,需等等,再确认一遍 301 | usleep(1000 * 200); // 休眠 200ms 302 | if (isInteractive()) { 303 | if (settings.enableDebug) 304 | freezeit.log("确认新状态:已亮屏或充电中, 退出息屏"); 305 | return false; 306 | } 307 | 308 | isScreenOffStandby = true; 309 | 310 | if (settings.enableDoze) { 311 | if (settings.enableDebug) 312 | freezeit.log("开始准备深度Doze"); 313 | if (settings.enableClearBatteryList) { 314 | updateDozeWhitelist(); 315 | } 316 | updateUidTime(); 317 | 318 | freezeit.log("😴 进入深度Doze"); 319 | enterDozeTimeStamp = nowTimeStamp; 320 | enterDozeCycleStamp = systemTools.cycleCnt; 321 | 322 | system( 323 | "dumpsys deviceidle enable all;" 324 | "dumpsys deviceidle force-idle deep" 325 | ); 326 | } 327 | return true; 328 | } 329 | 330 | 331 | map uidTime; // ms 微秒 332 | map& updateUidTime() { 333 | 334 | START_TIME_COUNT; 335 | 336 | stringstream ss; 337 | ss << ifstream("/proc/uid_cputime/show_uid_stat").rdbuf(); 338 | 339 | string line; 340 | while (getline(ss, line)) { 341 | int uid; 342 | long long userTime, systemTime; // us 微秒 343 | sscanf(line.c_str(), "%d: %lld %lld", &uid, &userTime, &systemTime); 344 | if (managedApp.contains(uid) && (userTime >= 1000 || systemTime >= 1000)) { 345 | auto& appTime = uidTime[uid]; 346 | appTime.lastTotal = appTime.total; 347 | appTime.total = static_cast((systemTime + userTime) / 1000); // ms 取毫秒 348 | } 349 | } 350 | 351 | END_TIME_COUNT; 352 | return uidTime; 353 | } 354 | }; 355 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/freezeit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "vpopen.hpp" 5 | 6 | class Freezeit { 7 | private: 8 | const char* LOG_PATH = "/sdcard/Android/Frozen.log"; 9 | 10 | constexpr static int LINE_SIZE = 1024 * 32; // 32 KiB 11 | constexpr static int BUFF_SIZE = 1024 * 128; // 128 KiB 12 | 13 | mutex logPrintMutex; 14 | bool toFileFlag = false; 15 | size_t position = 0; 16 | char lineCache[LINE_SIZE] = "[00:00:00] "; 17 | char logCache[BUFF_SIZE]; 18 | 19 | string propPath; 20 | 21 | map prop{ 22 | {"id", "Unknown"}, 23 | {"name", "Unknown"}, 24 | {"version", "Unknown"}, 25 | {"versionCode", "0"}, 26 | {"author", "Unknown"}, 27 | {"description", "Unknown"}, 28 | }; 29 | 30 | // "Jul 28 2022" --> "2022-07-28" 31 | const char compilerDate[12] = { 32 | __DATE__[7], 33 | __DATE__[8], 34 | __DATE__[9], 35 | __DATE__[10],// YYYY year 36 | '-', 37 | 38 | // First month letter, Oct Nov Dec = '1' otherwise '0' 39 | (__DATE__[0] == 'O' || __DATE__[0] == 'N' || __DATE__[0] == 'D') ? '1' : '0', 40 | 41 | // Second month letter Jan, Jun or Jul 42 | (__DATE__[0] == 'J') ? ((__DATE__[1] == 'a') ? '1' 43 | : ((__DATE__[2] == 'n') ? '6' : '7')) 44 | : (__DATE__[0] == 'F') ? '2'// Feb 45 | : (__DATE__[0] == 'M') ? (__DATE__[2] == 'r') ? '3' : '5'// Mar or May 46 | : (__DATE__[0] == 'A') ? (__DATE__[1] == 'p') ? '4' : '8'// Apr or Aug 47 | : (__DATE__[0] == 'S') ? '9'// Sep 48 | : (__DATE__[0] == 'O') ? '0'// Oct 49 | : (__DATE__[0] == 'N') ? '1'// Nov 50 | : (__DATE__[0] == 'D') ? '2'// Dec 51 | : 'X', 52 | 53 | '-', 54 | __DATE__[4] == ' ' ? '0' : __DATE__[4],// First day letter, replace space with digit 55 | __DATE__[5],// Second day letter 56 | '\0', 57 | }; 58 | 59 | 60 | void toMem(const char* logStr, const int len) { 61 | if ((position + len) >= BUFF_SIZE) 62 | position = 0; 63 | 64 | memcpy(logCache + position, logStr, len); 65 | position += len; 66 | } 67 | 68 | void toFile(const char* logStr, const int len) { 69 | auto fp = fopen(LOG_PATH, "ab"); 70 | if (!fp) { 71 | fprintf(stderr, "日志输出(追加模式)失败 [%d][%s]", errno, strerror(errno)); 72 | return; 73 | } 74 | 75 | auto fileSize = ftell(fp); 76 | if ((fileSize + len) >= BUFF_SIZE) { 77 | fclose(fp); 78 | usleep(1000); 79 | fp = fopen(LOG_PATH, "wb"); 80 | if (!fp) { 81 | fprintf(stderr, "日志输出(超额清理模式)失败 [%d][%s]", errno, strerror(errno)); 82 | return; 83 | } 84 | } 85 | 86 | fwrite(logStr, 1, len, fp); 87 | fclose(fp); 88 | } 89 | 90 | public: 91 | 92 | bool isSamsung{ false }; 93 | bool isOppoVivo{ false }; 94 | 95 | int ANDROID_VER = 0; 96 | int SDK_INT_VER = 0; 97 | KernelVersionStruct kernelVersion; 98 | 99 | string modulePath; 100 | string moduleEnv{ "Unknown" }; 101 | string workMode{ "Unknown" }; 102 | string kernelVerStr{ "Unknown" }; 103 | string androidVerStr{ "Unknown" }; 104 | 105 | uint32_t extMemorySize{ 0 }; 106 | 107 | Freezeit& operator=(Freezeit&&) = delete; 108 | 109 | Freezeit(int argc, string& fullPath) { 110 | 111 | modulePath = Utils::parentDir(fullPath); 112 | 113 | int versionCode = -1; 114 | if (!access("/system/bin/magisk", F_OK)) { 115 | moduleEnv = "Magisk"; 116 | versionCode = MAGISK::get_version_code(); 117 | if (versionCode <= 0) { 118 | sleep(2); 119 | versionCode = MAGISK::get_version_code(); 120 | } 121 | } 122 | else if (!access("/data/adb/ksud", F_OK)) { 123 | moduleEnv = "KernelSU"; 124 | versionCode = KSU::get_version_code(); 125 | if (versionCode <= 0) { 126 | sleep(2); 127 | versionCode = KSU::get_version_code(); 128 | } 129 | } 130 | else if (!access("/data/adb/ap/bin/apd", F_OK)) { 131 | moduleEnv = "APatch"; 132 | versionCode = APatch::get_version_code(); 133 | if (versionCode <= 0) { 134 | sleep(2); 135 | versionCode = APatch::get_version_code(); 136 | } 137 | } 138 | if (versionCode > 0) 139 | moduleEnv += " (" + to_string(versionCode) + ")"; 140 | 141 | toFileFlag = argc > 1; 142 | if (toFileFlag) { 143 | if (position)toFile(logCache, position); 144 | const char tips[] = "日志已通过文件输出: /sdcard/Android/Frozen.log"; 145 | toMem(tips, sizeof(tips) - 1); 146 | } 147 | 148 | propPath = modulePath + "/module.prop"; 149 | auto fp = fopen(propPath.c_str(), "r"); 150 | if (!fp) { 151 | fprintf(stderr, "找不到模块属性文件 [%s]", propPath.c_str()); 152 | exit(-1); 153 | } 154 | 155 | char tmp[1024*4]; 156 | while (!feof(fp)) { 157 | fgets(tmp, sizeof(tmp), fp); 158 | if (!isalpha(tmp[0])) continue; 159 | tmp[sizeof(tmp) - 1] = 0; 160 | auto ptr = strchr(tmp, '='); 161 | if (!ptr)continue; 162 | 163 | *ptr = 0; 164 | for (size_t i = (ptr - tmp) + 1; i < sizeof(tmp); i++) { 165 | if (tmp[i] == '\n' || tmp[i] == '\r') { 166 | tmp[i] = 0; 167 | break; 168 | } 169 | } 170 | prop[string(tmp)] = string(ptr + 1); 171 | } 172 | fclose(fp); 173 | 174 | logFmt("模块版本 %s", prop["version"].c_str()); 175 | logFmt("编译时间 %s %s UTC+8", compilerDate, __TIME__); 176 | 177 | fprintf(stderr, "version %s", prop["version"].c_str()); // 发送当前版本信息给监控进程 178 | 179 | ANDROID_VER = __system_property_get("ro.build.version.release", tmp) > 0 ? atoi(tmp) : 0; 180 | SDK_INT_VER = __system_property_get("ro.build.version.sdk", tmp) > 0 ? atoi(tmp) : 0; 181 | androidVerStr = to_string(ANDROID_VER) + " (API " + to_string(SDK_INT_VER) + ")"; 182 | 183 | utsname kernelInfo{}; 184 | if (!uname(&kernelInfo)) { 185 | sscanf(kernelInfo.release, "%d.%d.%d", &kernelVersion.main, &kernelVersion.sub, 186 | &kernelVersion.patch); 187 | kernelVerStr = 188 | to_string(kernelVersion.main) + "." + to_string(kernelVersion.sub) + "." + 189 | to_string(kernelVersion.patch); 190 | logFmt("内核版本 %d.%d.%d", kernelVersion.main, kernelVersion.sub, kernelVersion.patch); 191 | } 192 | else { 193 | log("内核版本 获取异常"); 194 | } 195 | 196 | char res[256]; 197 | if (__system_property_get("gsm.operator.alpha", res) > 0 && res[0] != ',') 198 | logFmt("运营信息 %s", res); 199 | if (__system_property_get("gsm.network.type", res) > 0) logFmt("网络类型 %s", res); 200 | if (__system_property_get("ro.product.brand", res) > 0) { 201 | logFmt("设备厂商 %s", res); 202 | 203 | //for (int i = 0; i < 8; i++)res[i] |= 32; 204 | *((uint64_t*)res) |= 0x20202020'20202020ULL; // 转为小写 205 | if (!strncmp(res, "samsung", 7)) 206 | isSamsung = true; 207 | else if (!strncmp(res, "oppo", 4) || !strncmp(res, "vivo", 4) || 208 | !strncmp(res, "realme", 6) || !strncmp(res, "iqoo", 4)) 209 | isOppoVivo = true; 210 | } 211 | if (__system_property_get("ro.product.marketname", res) > 0) logFmt("设备型号 %s", res); 212 | if (__system_property_get("persist.sys.device_name", res) > 0) logFmt("设备名称 %s", res); 213 | if (__system_property_get("ro.system.build.version.incremental", res) > 0) 214 | logFmt("系统版本 %s", res); 215 | if (__system_property_get("ro.soc.manufacturer", res) > 0 && 216 | __system_property_get("ro.soc.model", res + 100) > 0) 217 | logFmt("硬件平台 %s %s", res, res + 100); 218 | } 219 | 220 | bool saveProp() { 221 | auto fp = fopen(propPath.c_str(), "wb"); 222 | if (!fp) 223 | return false; 224 | 225 | char tmp[1024]; 226 | size_t len = snprintf(tmp, sizeof(tmp), 227 | "id=%s\nname=%s\nversion=%s\nversionCode=%s\nauthor=%s\ndescription=%s\nupdateJson=%s\n", 228 | prop["id"].c_str(), prop["name"].c_str(), prop["version"].c_str(), 229 | prop["versionCode"].c_str(), 230 | prop["author"].c_str(), prop["description"].c_str(), 231 | prop["updateJson"].c_str()); 232 | 233 | size_t writeLen = fwrite(tmp, 1, len, fp); 234 | fclose(fp); 235 | 236 | return (writeLen == len); 237 | } 238 | 239 | void setWorkMode(const string& mode) { 240 | workMode = mode; 241 | } 242 | 243 | size_t formatProp(char* ptr, const size_t maxSize, const int cpuCluster) { 244 | return snprintf(ptr, maxSize, "%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n%s\n%u", 245 | prop["id"].c_str(), prop["name"].c_str(), prop["version"].c_str(), 246 | prop["versionCode"].c_str(), prop["author"].c_str(), 247 | cpuCluster, moduleEnv.c_str(), workMode.c_str(), 248 | androidVerStr.c_str(), kernelVerStr.c_str(), extMemorySize); 249 | } 250 | 251 | void log(const string& logContent) { 252 | log(logContent.c_str()); 253 | } 254 | 255 | void logview(const string_view& str) { 256 | lock_guard lock(logPrintMutex); 257 | 258 | const int prefixLen = formatTime(); 259 | 260 | int len = str.length() + prefixLen; 261 | memcpy(lineCache + prefixLen, str.data(), str.length()); 262 | 263 | lineCache[len++] = '\n'; 264 | 265 | if (toFileFlag) 266 | toFile(lineCache, len); 267 | else 268 | toMem(lineCache, len); 269 | } 270 | 271 | 272 | int formatTime() { 273 | time_t timeStamp = time(nullptr) + 8 * 3600L; 274 | int hour = (timeStamp / 3600) % 24; 275 | int min = (timeStamp % 3600) / 60; 276 | int sec = timeStamp % 60; 277 | 278 | //lineCache[LINE_SIZE] = "[00:00:00] "; 279 | lineCache[1] = (hour / 10) + '0'; 280 | lineCache[2] = (hour % 10) + '0'; 281 | lineCache[4] = (min / 10) + '0'; 282 | lineCache[5] = (min % 10) + '0'; 283 | lineCache[7] = (sec / 10) + '0'; 284 | lineCache[8] = (sec % 10) + '0'; 285 | 286 | return 11; 287 | } 288 | void log(const char* str) { 289 | lock_guard lock(logPrintMutex); 290 | 291 | const int prefixLen = formatTime(); 292 | int len = strlen(str) + prefixLen; 293 | 294 | memcpy(lineCache + prefixLen, str, strlen(str)); 295 | 296 | lineCache[len++] = '\n'; 297 | 298 | if (toFileFlag) 299 | toFile(lineCache, len); 300 | else 301 | toMem(lineCache, len); 302 | } 303 | 304 | template 305 | void logFmt(const char* fmt, Args&&... args) { 306 | lock_guard lock(logPrintMutex); 307 | 308 | const int prefixLen = formatTime(); 309 | 310 | int len = snprintf(lineCache + prefixLen, (size_t)(LINE_SIZE - prefixLen), fmt, std::forward(args)...) + prefixLen; 311 | 312 | if (len <= 11 || LINE_SIZE <= (len + 1)) { 313 | lineCache[11] = 0; 314 | fprintf(stderr, "日志异常: len[%d] lineCache[%s]", len, lineCache); 315 | return; 316 | } 317 | 318 | lineCache[len++] = '\n'; 319 | 320 | if (toFileFlag) 321 | toFile(lineCache, len); 322 | else 323 | toMem(lineCache, len); 324 | } 325 | 326 | void clearLog() { 327 | logCache[0] = '\n'; 328 | position = 1; 329 | } 330 | 331 | char* getLogPtr() { 332 | return logCache; 333 | } 334 | 335 | size_t getLoglen() { 336 | return position; 337 | } 338 | }; 339 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* Freezeit: 冻它模块 2 | * Copyright (c) 2023 JARK006 3 | * 4 | * 命令行编译: 5 | * clang++.exe --target=aarch64-none-linux-android29 --sysroot=D:/AndroidSDK/ndk/25.2.9519653/toolchains/llvm/prebuilt/windows-x86_64/sysroot -std=c++20 -static -s -Ofast -Wall -Wextra -Wshadow -fno-exceptions -fno-rtti -DNDEBUG -fPIE -Iinclude src/main.cpp -o build/freezeit 6 | * 7 | * 主线程8MiB 子线程 栈深约 1016Kib 8 | * 9 | * 在左下角 Build Variants 选择 Release 10 | * 在任意 cpp头文件或源码文件中,菜单 Build -> Recompile "xxx" 11 | * 编译输出 module\\build\intermediates\cmake\release\obj\arm64-v8a 12 | * 13 | */ 14 | 15 | #include "freezeit.hpp" 16 | #include "settings.hpp" 17 | #include "managedApp.hpp" 18 | #include "systemTools.hpp" 19 | #include "doze.hpp" 20 | #include "freezer.hpp" 21 | #include "server.hpp" 22 | 23 | int main(int argc, char **argv) { 24 | 25 | char tmp[1024]; 26 | string fullPath(realpath(argv[0], tmp)); 27 | 28 | Utils::Init(); 29 | 30 | Freezeit freezeit(argc, std::move(fullPath)); 31 | Settings settings(freezeit); 32 | ManagedApp managedApp(freezeit, settings); 33 | SystemTools systemTools(freezeit, settings); 34 | Doze doze(freezeit, settings, managedApp, systemTools); 35 | Freezer freezer(freezeit, settings, managedApp, systemTools, doze); 36 | Server server(freezeit, settings, managedApp, systemTools, doze, freezer); 37 | 38 | sleep(3600 * 24 * 365);//放年假 39 | return 0; 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "freezeit.hpp" 5 | #include "managedApp.hpp" 6 | #include "systemTools.hpp" 7 | #include "freezer.hpp" 8 | #include "doze.hpp" 9 | 10 | class Server { 11 | private: 12 | Freezeit& freezeit; 13 | Settings& settings; 14 | ManagedApp& managedApp; 15 | SystemTools& systemTools; 16 | Freezer& freezer; 17 | Doze& doze; 18 | 19 | thread serverThread; 20 | 21 | static const int RECV_BUF_SIZE = 2 * 1024 * 1024; // 2 MiB TCP通信接收缓存大小 22 | static const int REPLY_BUF_SIZE = 8 * 1024 * 1024; // 8 MiB TCP通信回应缓存大小 23 | unique_ptr recvBuf, replyBuf; 24 | 25 | public: 26 | Server& operator=(Server&&) = delete; 27 | 28 | Server(Freezeit& freezeit, Settings& settings, ManagedApp& managedApp, SystemTools& systemTools, 29 | Doze& doze, Freezer& freezer) : 30 | freezeit(freezeit), settings(settings), managedApp(managedApp), 31 | systemTools(systemTools), freezer(freezer), doze(doze) { 32 | serverThread = thread(&Server::serverThreadFunc, this); 33 | } 34 | 35 | void serverThreadFunc() { 36 | /* LOCAL_SOCKET *******************************************************************/ 37 | // Socket 位于Linux抽象命名空间, 而不是文件路径 38 | // https://blog.csdn.net/howellzhu/article/details/111597734 39 | // https://blog.csdn.net/shanzhizi/article/details/16882087 一种是路径方式 一种是抽象命名空间 40 | //const int addrLen = offsetof(sockaddr_un, sun_path) + 15; // addrLen大小是 首个占位符 '\0' 加 "FreezeitServer" 的字符长度 41 | //const sockaddr_un serv_addr{ AF_UNIX, "\0FreezeitServer" }; // 首位为空[0]=0,位于Linux抽象命名空间 42 | //sockaddr_un clnt_addr{}; 43 | //socklen_t clnt_addr_size = sizeof(sockaddr_un); 44 | // 45 | // 终端执行 setenforce 0 ,即设置 SELinux 为宽容模式, 普通安卓应用才可以使用 LocalSocket 46 | //system("setenforce 0"); 47 | /* LOCAL_SOCKET *******************************************************************/ 48 | 49 | constexpr socklen_t addrLen = sizeof(sockaddr); 50 | const sockaddr_in serv_addr{ AF_INET, htons(60666), {inet_addr("127.0.0.1")}, {} }; 51 | 52 | 53 | recvBuf = make_unique(RECV_BUF_SIZE); 54 | replyBuf = make_unique(REPLY_BUF_SIZE); 55 | 56 | while (true) { 57 | static int failTcpCnt = 0; 58 | if (failTcpCnt) { 59 | fprintf(stderr, "Socket 失败%d次, [%d]:[%s]", failTcpCnt, errno, strerror(errno)); 60 | if (failTcpCnt > 100) { 61 | fprintf(stderr, "Socket 彻底失败, 已退出。[%d]:[%s]", errno, strerror(errno)); 62 | exit(-1); 63 | } 64 | sleep(5); 65 | } 66 | failTcpCnt++; 67 | 68 | int serv_sock; 69 | 70 | /* LOCAL_SOCKET *******************************************************************/ 71 | //if ((serv_sock = socket(AF_UNIX, SOCK_STREAM, 0)) <= 0) { 72 | // fprintf(stderr, "socket() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, strerror(errno)); 73 | // continue; 74 | //} 75 | /* LOCAL_SOCKET *******************************************************************/ 76 | 77 | 78 | /* NORMAL_SOCKET ******************************************************************/ 79 | if ((serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0) { 80 | fprintf(stderr, "socket() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, 81 | strerror(errno)); 82 | continue; 83 | } 84 | 85 | int opt = 1; //地址和端口 释放后可立即重用 否则几分钟后才可使用 86 | if (setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { 87 | fprintf(stderr, "setsockopt() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, 88 | strerror(errno)); 89 | continue; 90 | } 91 | /* NORMAL_SOCKET ******************************************************************/ 92 | 93 | 94 | 95 | if (bind(serv_sock, (sockaddr*)&serv_addr, addrLen) < 0) { 96 | fprintf(stderr, "bind() Fail, [%d]:[%s]", errno, strerror(errno)); 97 | continue; 98 | } 99 | 100 | if (listen(serv_sock, 4) < 0) { 101 | fprintf(stderr, "listen() Fail, [%d]:[%s]", errno, strerror(errno)); 102 | continue; 103 | } 104 | 105 | while (true) { 106 | sockaddr_in clnt_addr{}; 107 | socklen_t clnt_addr_size = sizeof(sockaddr_in); 108 | int clnt_sock = accept(serv_sock, (sockaddr*)&clnt_addr, &clnt_addr_size); 109 | if (clnt_sock < 0) { 110 | static int failCnt = 1; 111 | 112 | fprintf(stderr, "accept() 第%d次错误 servFd[%d] clntFd[%d] size[%d]; [%d]:[%s]", 113 | failCnt, serv_sock, clnt_sock, clnt_addr_size, errno, strerror(errno)); 114 | 115 | if (++failCnt > 10) break; 116 | 117 | sleep(2); 118 | continue; 119 | } 120 | 121 | //设置接收超时 122 | timeval timeout = { 1, 0 }; // 1秒 超时 123 | if (setsockopt(clnt_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, 124 | sizeof(timeval))) { 125 | fprintf(stderr, "setsockopt 超时设置出错 servFd[%d] clntFd[%d] [%d]:[%s]", serv_sock, 126 | clnt_sock, errno, strerror(errno)); 127 | close(clnt_sock); 128 | continue; 129 | } 130 | 131 | uint8_t dataHeader[6]; 132 | uint32_t recvLen = recv(clnt_sock, dataHeader, sizeof(dataHeader), MSG_WAITALL); 133 | if (recvLen != sizeof(dataHeader)) { 134 | close(clnt_sock); 135 | fprintf(stderr, "clnt_sock recv dataHeader len[%u]", recvLen); 136 | continue; 137 | } 138 | 139 | recvLen = *((uint32_t*)dataHeader); 140 | uint32_t appCommand = dataHeader[4]; 141 | uint32_t XOR_value = dataHeader[5]; 142 | 143 | // "\0AUTH\n" B站发的,前4字节: 大端 4281684, 小端 1414873344 144 | if (recvLen == 1414873344 || recvLen == 4281684) { 145 | close(clnt_sock); 146 | continue; 147 | } 148 | else if (recvLen >= RECV_BUF_SIZE) { 149 | freezeit.logFmt("数据格式异常 recvLen[%u] HEX[%s]", recvLen, 150 | Utils::bin2Hex(dataHeader, 6).c_str()); 151 | close(clnt_sock); 152 | continue; 153 | } 154 | 155 | if (recvLen) { 156 | uint32_t lenTmp = recv(clnt_sock, recvBuf.get(), recvLen, MSG_WAITALL); 157 | if (lenTmp != recvLen) { 158 | fprintf(stderr, "附带数据接收错误, appCommand[%u], 要求[%u], 实际接收[%u]", 159 | appCommand, recvLen, lenTmp); 160 | close(clnt_sock); 161 | continue; 162 | } 163 | 164 | uint8_t XOR_cal = 0; 165 | for (uint32_t i = 0; i < recvLen; i++) 166 | XOR_cal ^= (uint8_t)recvBuf[i]; 167 | 168 | if (XOR_value != XOR_cal) { 169 | fprintf(stderr, "数据校验错误, 提供值[0x%2x], 接收数据计算值[0x%2x]", XOR_value, XOR_cal); 170 | close(clnt_sock); 171 | continue; 172 | } 173 | } 174 | 175 | recvBuf[recvLen] = 0; 176 | handleCmd(static_cast(appCommand), recvLen, clnt_sock); 177 | } 178 | close(serv_sock); 179 | } 180 | } 181 | 182 | void handleCmd(const MANAGER_CMD appCommand, const int recvLen, const int clnt_sock) { 183 | char* replyPtr; 184 | uint32_t replyLen; 185 | switch (appCommand) { 186 | case MANAGER_CMD::getPropInfo: { 187 | replyPtr = replyBuf.get(); 188 | replyLen = freezeit.formatProp(replyBuf.get(), REPLY_BUF_SIZE, 189 | systemTools.cpuCluster); 190 | } break; 191 | 192 | case MANAGER_CMD::getLog: { 193 | replyPtr = freezeit.getLogPtr(); 194 | replyLen = freezeit.getLoglen(); 195 | } break; 196 | 197 | case MANAGER_CMD::getXpLog: { 198 | const int len = Utils::localSocketRequest(XPOSED_CMD::GET_XP_LOG, nullptr, 0, (int*)replyBuf.get(), REPLY_BUF_SIZE); 199 | if (len == 0) { 200 | freezeit.log("getXpLog 工作异常, 请确认LSPosed中Frozen是否已经勾选系统框架"); 201 | replyPtr = const_cast("Frozen's Xposed log is empty. "); 202 | replyLen = 32; 203 | } 204 | else { 205 | replyPtr = replyBuf.get(); 206 | replyLen = len; 207 | } 208 | } break; 209 | 210 | case MANAGER_CMD::getAppCfg: { 211 | uint32_t intLen = 0; 212 | const auto ptr = reinterpret_cast(replyBuf.get()); 213 | for (const auto& appInfo : managedApp.appInfoMap) { 214 | if (appInfo.uid < 0)continue; 215 | 216 | ptr[intLen++] = appInfo.uid; 217 | ptr[intLen++] = static_cast(appInfo.freezeMode); 218 | ptr[intLen++] = appInfo.isPermissive ? 1 : 0; 219 | } 220 | 221 | replyPtr = replyBuf.get(); 222 | replyLen = intLen << 2; // intLen*sizeof(int) 223 | } break; 224 | 225 | case MANAGER_CMD::getRealTimeInfo: { 226 | if (recvLen != 12) { 227 | replyPtr = replyBuf.get(); 228 | replyLen = snprintf(replyBuf.get(), 128, "实时信息需要12字节, 实际收到[%u]", 229 | recvLen); 230 | break; 231 | } 232 | 233 | uint32_t height = ((uint32_t*)recvBuf.get())[0]; 234 | uint32_t width = ((uint32_t*)recvBuf.get())[1]; 235 | 236 | if (height < 20 || width < 20) { 237 | replyPtr = replyBuf.get(); 238 | replyLen = snprintf(replyBuf.get(), 128, "宽高不符合, height[%u] width[%u]", 239 | height, width); 240 | break; 241 | } 242 | 243 | auto& availableMiB = ((uint32_t*)recvBuf.get())[2]; // Unit: MiB 244 | 245 | systemTools.getCPU_realtime(availableMiB); 246 | replyLen = systemTools.drawChart((uint32_t*)replyBuf.get(), height, 247 | width); 248 | replyLen += systemTools.formatRealTime( 249 | reinterpret_cast(replyBuf.get() + replyLen)); 250 | replyPtr = replyBuf.get(); 251 | } break; 252 | 253 | case MANAGER_CMD::getUidTime: { 254 | int intLen = 0; 255 | int* ptr = reinterpret_cast(replyBuf.get()); 256 | struct st { 257 | int uid; 258 | int total; 259 | int lastTotal; 260 | }; 261 | vector uidTimeSort; 262 | uidTimeSort.reserve(256); 263 | 264 | /* 按总时间排序 ***********************************************/ 265 | 266 | for (const auto& [uid, timeList] : doze.updateUidTime()) 267 | uidTimeSort.emplace_back(st{ uid, timeList.total, timeList.lastTotal }); 268 | std::sort(uidTimeSort.begin(), uidTimeSort.end(), 269 | [](const st& a, const st& b) { return a.total > b.total; }); 270 | 271 | 272 | /* 先按跳动时间排序,再按总时间排序。跳动太大,不好查看 *********/ 273 | 274 | //for (const auto& [uid, timeList] : doze.updateUidTime()) 275 | // if (timeList.total != timeList.lastTotal) 276 | // uidTimeSort.emplace_back(st{ uid, timeList.total, timeList.lastTotal }); 277 | 278 | //const auto size = uidTimeSort.size(); 279 | 280 | //for (const auto& [uid, timeList] : doze.updateUidTime()) 281 | // if (timeList.total == timeList.lastTotal) 282 | // uidTimeSort.emplace_back(st{ uid, timeList.total, timeList.lastTotal }); 283 | 284 | //if (size) 285 | // std::sort(uidTimeSort.begin(), uidTimeSort.begin() + size, 286 | // [](const st& a, const st& b) { return (a.total - a.lastTotal) > (b.total - b.lastTotal); }); 287 | 288 | //std::sort(uidTimeSort.begin() + size, uidTimeSort.end(), 289 | // [](const st& a, const st& b) { return a.total > b.total; }); 290 | 291 | /*************************************************************/ 292 | 293 | 294 | for (const auto& [uid, total, lastTotal] : uidTimeSort) { 295 | ptr[intLen++] = uid; 296 | ptr[intLen++] = total - lastTotal; 297 | ptr[intLen++] = total; 298 | } 299 | 300 | replyPtr = replyBuf.get(); 301 | replyLen = intLen << 2; // intLen*sizeof(int) 302 | } break; 303 | 304 | case MANAGER_CMD::getSettings: { 305 | replyPtr = reinterpret_cast(settings.get()); 306 | replyLen = settings.size(); 307 | } break; 308 | 309 | case MANAGER_CMD::setAppCfg: { 310 | if (recvLen == 0 || (recvLen % 12)) { 311 | replyPtr = replyBuf.get(); 312 | replyLen = snprintf(replyBuf.get(), 128, "需要12字节的倍数, 实际收到[%u]", 313 | recvLen); 314 | freezeit.log(replyBuf.get()); 315 | break; 316 | } 317 | 318 | managedApp.updateAppList(); 319 | 320 | const int intSize = recvLen >> 2; // recvLen/4 321 | const int* ptr = reinterpret_cast(recvBuf.get()); 322 | map newCfg; 323 | 324 | for (int i = 0; i < intSize;) { 325 | const int uid = ptr[i++]; 326 | const FREEZE_MODE freezeMode = static_cast(ptr[i++]); 327 | const bool isTolerant = ptr[i++] != 0; 328 | if (managedApp.contains(uid)) { 329 | if (managedApp.FREEZE_MODE_SET.contains(freezeMode)) 330 | newCfg[uid] = { freezeMode, isTolerant }; 331 | else 332 | freezeit.logFmt("错误配置: UID:%d freezeMode:%d isTolerant:%d", uid, 333 | freezeMode, isTolerant); 334 | } 335 | else 336 | freezeit.logFmt("特殊应用: UID:%d, 已强制自由后台", uid); 337 | } 338 | 339 | string tips; 340 | set changeUidSet; 341 | for (const auto& appInfo : managedApp.appInfoMap) { 342 | if (appInfo.uid < ManagedApp::UID_START || appInfo.freezeMode == FREEZE_MODE::WHITEFORCE || 343 | !newCfg.contains(appInfo.uid) || appInfo.freezeMode == newCfg[appInfo.uid].freezeMode) 344 | continue; 345 | 346 | changeUidSet.insert(appInfo.uid); 347 | tips += freezer.getModeText(appInfo.freezeMode) + "->" + 348 | freezer.getModeText(newCfg[appInfo.uid].freezeMode) + " [" + 349 | appInfo.label + "]\n"; 350 | } 351 | if (tips.length()) 352 | freezeit.logFmt("配置变化:\n\n%s", tips.c_str()); 353 | 354 | auto runningUids = freezer.getRunningUids(changeUidSet); 355 | if (runningUids.size()) { 356 | freezer.unFreezerTemporary(runningUids); 357 | tips.clear(); 358 | for (auto& uid : runningUids) { 359 | tips.append(managedApp[uid].label); 360 | tips.append(" "); 361 | } 362 | freezeit.logFmt("解冻策略变更的应用: %s", tips.c_str()); 363 | } 364 | 365 | managedApp.loadConfig2CfgTemp(newCfg); 366 | managedApp.updateIME2CfgTemp(); 367 | managedApp.applyCfgTemp(); 368 | managedApp.saveConfig(); 369 | managedApp.update2xposedByLocalSocket(); 370 | 371 | replyPtr = const_cast("success"); 372 | replyLen = 7; 373 | } break; 374 | 375 | case MANAGER_CMD::setAppLabel: { 376 | managedApp.updateAppList(); // 先更新应用列表 377 | 378 | map labelList; 379 | for (const string& str : Utils::splitString(string(recvBuf.get(), recvLen), 380 | "\n")) { 381 | const int uid = atoi(str.c_str()); 382 | if (!managedApp.contains(uid) || str.length() <= 6) 383 | freezeit.logFmt("解析名称错误 [%s]", str.c_str()); 384 | else labelList[uid] = str.substr(6); 385 | } 386 | 387 | string labelStr; 388 | labelStr.reserve(1024L * 4); 389 | for (const auto& [uid, label] : labelList) { 390 | labelStr += " ["; 391 | labelStr += label; 392 | labelStr += ']'; 393 | } 394 | freezeit.logFmt("更新 %lu 款应用名称:\n\n%s\n", labelList.size(), labelStr.c_str()); 395 | 396 | managedApp.loadLabel(labelList); 397 | managedApp.update2xposedByLocalSocket(); 398 | managedApp.saveLabel(); 399 | 400 | replyPtr = const_cast("success"); 401 | replyLen = 7; 402 | } break; 403 | 404 | case MANAGER_CMD::clearLog: { 405 | freezeit.clearLog(); 406 | replyPtr = freezeit.getLogPtr(); 407 | replyLen = freezeit.getLoglen(); 408 | } break; 409 | 410 | case MANAGER_CMD::getProcState: { 411 | freezer.printProcState(); 412 | replyPtr = freezeit.getLogPtr(); 413 | replyLen = freezeit.getLoglen(); 414 | } break; 415 | 416 | case MANAGER_CMD::setSettingsVar: { 417 | replyPtr = replyBuf.get(); 418 | 419 | if (recvLen != 2) { 420 | replyLen = snprintf(replyBuf.get(), REPLY_BUF_SIZE, 421 | "数据长度不正确, 正常:2, 收到:%d", recvLen); 422 | break; 423 | } 424 | 425 | int len = settings.checkAndSet(recvBuf[0], recvBuf[1], replyBuf.get()); 426 | 427 | if (len <= 0) { 428 | memcpy(replyBuf.get(), "未知设置错误", 18); 429 | replyLen = 18; 430 | } 431 | else { 432 | replyLen = len; 433 | } 434 | } break; 435 | 436 | default: { 437 | replyPtr = const_cast("非法命令"); 438 | replyLen = 12; 439 | } break; 440 | } 441 | 442 | if (replyLen) { 443 | uint32_t header[2] = { replyLen , 0 }; 444 | send(clnt_sock, header, 6, MSG_DONTROUTE); 445 | send(clnt_sock, replyPtr, replyLen, MSG_DONTROUTE); 446 | } 447 | close(clnt_sock); 448 | } 449 | }; 450 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "freezeit.hpp" 5 | 6 | class Settings { 7 | private: 8 | Freezeit& freezeit; 9 | mutex writeSettingMutex; 10 | 11 | string settingsPath; 12 | 13 | const static size_t SETTINGS_SIZE = 256; 14 | uint8_t settingsVar[SETTINGS_SIZE] = { 15 | 13, //[0] 设置文件版本 16 | 0, //[1] 绑定到 CPU核心簇 17 | 10, //[2] freezeTimeout sec 18 | 20, //[3] wakeupTimeoutMin min 19 | 20, //[4] terminateTimeout sec 20 | 5, //[5] setMode 21 | 2, //[6] refreezeTimeout 22 | 60, //[7] 内存阈值 百分比 23 | 2, //[8] MemoryRecycle Mode 内存回收 0: all全部内存, 1: anon 匿名内存 2:file 文件内存 24 | 0, //[9] 25 | 0, //[10] 26 | 0, //[11] 27 | 0, //[12] 28 | 1, //[13] 电池监控 29 | 0, //[14] 电流校准 30 | 0, //[15] 双电芯 31 | 0, //[16] 调整 lmk 参数 仅安卓10-16 32 | 1, //[17] 深度Doze 33 | 0, //[18] 扩展前台 34 | 0, //[19] 内存回收 35 | 0, //[20] Binder冻结 36 | 0, //[21] 开机冻结 37 | 0, //[23] 全局断网 38 | 0, //[24] 清理电池优化白名单 39 | 0, //[25] ReKernel临时解冻 40 | 0, //[26] 备用前台识别 41 | 0, //[27] 极简模式 42 | 0, //[30] 详细日志 43 | 0, //[31] 44 | 0, //[32] 45 | }; 46 | 47 | public: 48 | uint8_t& settingsVer = settingsVar[0]; // 设置文件版本 49 | //uint8_t& unknown = settingsVar[1]; // 50 | uint8_t& freezeTimeout = settingsVar[2]; // 单位 秒 51 | uint8_t& wakeupTimeoutMin = settingsVar[3]; // 单位 分 52 | uint8_t& terminateTimeout = settingsVar[4]; // 单位 秒 53 | uint8_t& setMode = settingsVar[5]; // Freezer模式 54 | // uint8_t& refreezeTimeoutIdx = settingsVar[6];// 定时压制 参数索引 0-4 55 | uint8_t& memoryRecycle = settingsVar[7]; // 内存阈值 百分比 56 | uint8_t& memoryRecycleMode = settingsVar[8]; // 内存回收 模式 0: all全部内存, 1: anon 匿名内存 2:file 文件内存 57 | uint8_t& enableBatteryMonitor = settingsVar[13]; // 电池监控 58 | uint8_t& enableCurrentFix = settingsVar[14]; // 电池电流校准 59 | uint8_t& enableDoubleCell = settingsVar[15]; // 双电芯 60 | uint8_t& enableLMK = settingsVar[16]; // 调整 lmk 参数 仅安卓11-15 61 | uint8_t& enableDoze = settingsVar[17]; // 深度Doze 62 | uint8_t& enableWindows = settingsVar[18]; // 扩展前台 63 | uint8_t& enableMemoryRecycle = settingsVar[19]; // 内存回收 64 | uint8_t& enableBinderFreeze = settingsVar[20]; // Binder冻结 65 | uint8_t& enableBreakNetwork = settingsVar[23]; // 全局断网 66 | uint8_t& enableClearBatteryList = settingsVar[24]; // 清理电池优化白名单 67 | uint8_t& enableReKernel = settingsVar[25]; // ReKernel临时解冻 68 | uint8_t& enableBackupTopAPPrecognition = settingsVar[26]; // 备用前台识别 69 | uint8_t& enableEzMode = settingsVar[27]; // 极简模式 70 | uint8_t& enableDebug = settingsVar[30]; // 详细日志 71 | 72 | Settings& operator=(Settings&&) = delete; 73 | 74 | Settings(Freezeit& freezeit) : freezeit(freezeit) { 75 | settingsPath = freezeit.modulePath + "/settings.db"; 76 | 77 | auto fd = open(settingsPath.c_str(), O_RDONLY); 78 | if (fd > 0) { 79 | uint8_t tmp[SETTINGS_SIZE] = { 0 }; 80 | int readSize = read(fd, tmp, SETTINGS_SIZE); 81 | close(fd); 82 | 83 | if (readSize != SETTINGS_SIZE) { 84 | freezeit.log("设置文件校验失败, 将使用默认设置参数, 并更新设置文件"); 85 | freezeit.logFmt("读取大小: %d Bytes. 要求大小: 256 Bytes.", readSize); 86 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 87 | } 88 | else if (tmp[0] != settingsVer) { 89 | freezeit.log("设置文件版本不兼容, 将使用默认设置参数, 并更新设置文件"); 90 | freezeit.logFmt("读取版本: V%d 要求版本: V%d", static_cast(tmp[0]), 91 | static_cast(settingsVer)); 92 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 93 | } 94 | else { 95 | memcpy(settingsVar, tmp, SETTINGS_SIZE); 96 | 97 | bool isError = false; 98 | if (setMode > 5) { 99 | freezeit.logFmt("冻结模式参数[%d]错误, 已重设为 全局SIGSTOP", static_cast(setMode)); 100 | setMode = 0; 101 | isError = true; 102 | } 103 | /* if (refreezeTimeoutIdx > 4) { 104 | freezeit.logFmt("定时压制参数[%d]错误, 已重设为 30分钟", static_cast(refreezeTimeoutIdx)); 105 | refreezeTimeoutIdx = 2; 106 | isError = true; 107 | } 108 | */ 109 | if (freezeTimeout < 1 || freezeTimeout > 60) { 110 | freezeit.logFmt("超时冻结参数[%d]错误, 已重置为10秒", static_cast(freezeTimeout)); 111 | freezeTimeout = 10; 112 | isError = true; 113 | } 114 | if (wakeupTimeoutMin < 3 || wakeupTimeoutMin > 120) { 115 | freezeit.logFmt("定时解冻参数[%d]错误, 已重置为30分", static_cast(wakeupTimeoutMin)); 116 | wakeupTimeoutMin = 30; 117 | isError = true; 118 | } 119 | if (terminateTimeout < 3 || terminateTimeout > 120) { 120 | freezeit.logFmt("超时杀死参数[%d]错误, 已重置为30秒", static_cast(terminateTimeout)); 121 | terminateTimeout = 30; 122 | isError = true; 123 | } 124 | if (isError) 125 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 126 | } 127 | } 128 | else { 129 | if (freezeit.isOppoVivo) { 130 | freezeit.log("开启扩展识别 OPPO/VIVO/IQOO/REALME"); 131 | enableWindows = true; 132 | } 133 | freezeit.log("设置文件不存在, 将初始化设置文件"); 134 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 135 | } 136 | } 137 | 138 | uint8_t& operator[](const int key) { 139 | return settingsVar[key]; 140 | } 141 | 142 | uint8_t* get() { 143 | return settingsVar; 144 | } 145 | 146 | size_t size() { 147 | return SETTINGS_SIZE; 148 | } 149 | 150 | /* int getRefreezeTimeout() { 151 | constexpr int timeoutList[5] = { 86400 * 365, 900, 1800, 3600, 7200 }; 152 | return timeoutList[refreezeTimeoutIdx < 5 ? refreezeTimeoutIdx : 0]; 153 | } 154 | */ 155 | bool save() { 156 | lock_guard lock(writeSettingMutex); 157 | int fd = open(settingsPath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666); 158 | if (fd > 0) { 159 | int writeSize = write(fd, settingsVar, SETTINGS_SIZE); 160 | close(fd); 161 | if (writeSize == SETTINGS_SIZE) 162 | return true; 163 | 164 | freezeit.logFmt("设置异常, 文件实际写入[%d]Bytes", writeSize); 165 | } 166 | return false; 167 | } 168 | 169 | int checkAndSet(const int idx, const int val, char* replyBuf) { 170 | const size_t REPLY_BUF_SIZE = 2048; 171 | 172 | switch (idx) { 173 | case 2: { // freezeTimeout sec 174 | if (val < 1 || 60 < val) 175 | return snprintf(replyBuf, REPLY_BUF_SIZE, "超时冻结参数错误, 欲设为:%d", val); 176 | } 177 | break; 178 | 179 | case 3: { // wakeupTimeoutMin min 180 | if (val < 3 || 120 < val) 181 | return snprintf(replyBuf, REPLY_BUF_SIZE, "定时解冻参数错误, 正常范围:3~120, 欲设为:%d", val); 182 | } 183 | break; 184 | 185 | case 4: { // TERMINATE sec 186 | if (val < 3 || 120 < val) 187 | return snprintf(replyBuf, REPLY_BUF_SIZE, "超时杀死参数错误, 正常范围:3~120, 欲设为:%d", val); 188 | } 189 | break; 190 | 191 | case 5: { // setMode 0-5 192 | if (5 < val) 193 | return snprintf(replyBuf, REPLY_BUF_SIZE, "冻结模式参数错误, 正常范围:0~5, 欲设为:%d", val); 194 | } 195 | break; 196 | 197 | case 7: { // memoryRecycle 198 | if (val < 0 || 100 < val) 199 | return snprintf(replyBuf, REPLY_BUF_SIZE, "内存阈值参数错误, 欲设为:%d", val); 200 | } 201 | break; 202 | 203 | case 8: { // memoryRecycleMode 204 | if (val > 2) 205 | return snprintf(replyBuf, REPLY_BUF_SIZE, "内存回收模式参数错误, 欲设为:%d", val); 206 | } 207 | break; 208 | 209 | case 10: // xxx 210 | case 11: // xxx 211 | case 13: // 电池监控 212 | case 14: // 电流校准 213 | case 15: // 双电芯 214 | case 16: // lmk 215 | case 17: // doze 216 | case 18: // 扩展前台 217 | case 19: // 内存回收 218 | case 20: // Binder冻结 219 | case 23: // 全局断网 220 | case 24: // 清理电池白名单 221 | case 25: // ReKernel临时解冻 222 | case 26: // 备用前台识别 223 | case 27: // 极简模式 224 | case 30: // 详细日志 225 | { 226 | if (val != 0 && val != 1) 227 | return snprintf(replyBuf, REPLY_BUF_SIZE, "开关值错误, 正常范围:0/1, 欲设为:%d", val); 228 | } 229 | break; 230 | 231 | default: { 232 | freezeit.logFmt("🔧设置失败,设置项不存在, [%d]:[%d]", idx, val); 233 | return snprintf(replyBuf, REPLY_BUF_SIZE, "设置项不存在, [%d]:[%d]", idx, val); 234 | } 235 | } 236 | 237 | settingsVar[idx] = val; 238 | if (save()) { 239 | freezeit.log("⚙️设置成功"); 240 | return snprintf(replyBuf, REPLY_BUF_SIZE, "success"); 241 | } 242 | else { 243 | freezeit.logFmt("🔧设置失败,写入设置文件失败, [%d]:%d", idx, val); 244 | return snprintf(replyBuf, REPLY_BUF_SIZE, "写入设置文件失败, [%d]:%d", idx, val); 245 | } 246 | } 247 | }; 248 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/systemTools.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "settings.hpp" 5 | #include "freezeit.hpp" 6 | 7 | class SystemTools { 8 | private: 9 | Freezeit& freezeit; 10 | Settings& settings; 11 | 12 | thread sndThread; 13 | 14 | constexpr static uint32_t COLOR_E = 0XFF22BB44; // efficiency 15 | constexpr static uint32_t COLOR_M = 0XFFDD6622; // performance 16 | constexpr static uint32_t COLOR_P = 0XFF2266BB; // performance+ 17 | constexpr static uint32_t COLOR_PRIME = 0XFF2238EE; // Prime 18 | 19 | constexpr static uint32_t COLOR_CLUSTER[6][8] = { 20 | {COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_PRIME, COLOR_PRIME, COLOR_PRIME, COLOR_PRIME}, // 44 21 | {COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_M, COLOR_M, COLOR_M, COLOR_PRIME}, // 431 22 | {COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_M, COLOR_M, COLOR_PRIME, COLOR_PRIME}, // 422 23 | {COLOR_E, COLOR_E, COLOR_E, COLOR_M, COLOR_M, COLOR_P, COLOR_P, COLOR_PRIME}, // 3221 24 | {COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_E, COLOR_PRIME, COLOR_PRIME}, // 62 25 | {COLOR_E, COLOR_E, COLOR_M, COLOR_M, COLOR_M, COLOR_M, COLOR_M, COLOR_PRIME}, // 251 26 | }; 27 | 28 | public: 29 | // 各核心 曲线颜色 ABGR 30 | const uint32_t* COLOR_CPU = COLOR_CLUSTER[0]; 31 | int cpuCluster = 0; 32 | int cpuCoreAll = 0; 33 | int cpuCoreValid = 0; 34 | uint32_t cycleCnt = 0; 35 | 36 | MemInfoStruct memInfo; 37 | 38 | int cpuTemperature{ 0 }; 39 | int batteryWatt{ 0 }; 40 | 41 | int cpuBucketIdx{ 0 }; 42 | static constexpr int maxBucketSize = 32; 43 | cpuRealTimeStruct cpuRealTime[maxBucketSize][9] = {}; // idx[8] 是CPU总使用率 // 最大 100 100% 44 | 45 | char cpuTempPath[256] = "/sys/class/thermal/thermal_zone0/temp"; 46 | 47 | atomic isAudioPlaying{false}; 48 | // bool isMicrophoneRecording = false; 49 | 50 | 51 | SystemTools& operator=(SystemTools&&) = delete; 52 | 53 | SystemTools(Freezeit& freezeit, Settings& settings) : 54 | freezeit(freezeit), settings(settings) { 55 | 56 | getCpuTempPath(); 57 | InitCPU(); 58 | 59 | InitLMK(); 60 | 61 | EZMode(); 62 | 63 | sndThread = thread(&SystemTools::sndThreadFunc, this); 64 | 65 | freezeit.extMemorySize = getExtMemorySize(); 66 | } 67 | 68 | void EZMode() { 69 | if (!settings.enableEzMode) return; 70 | freezeit.log("⚠您已启用EZ模式,您自定义的部分参数将被修改,此模式专为开箱即用的小白打造"); 71 | settings.enableClearBatteryList = 1; 72 | settings.enableBreakNetwork = 1; 73 | settings.freezeTimeout = 3; 74 | settings.enableBinderFreeze = 0; 75 | } 76 | size_t formatRealTime(int* ptr) { 77 | 78 | int i = 0; 79 | ptr[i++] = memInfo.totalRam; 80 | ptr[i++] = memInfo.availRam; 81 | ptr[i++] = memInfo.totalSwap; 82 | ptr[i++] = memInfo.freeSwap; 83 | 84 | for (int coreIdx = 0; coreIdx < 8; coreIdx++) 85 | ptr[i++] = cpuRealTime[cpuBucketIdx][coreIdx].freq; 86 | for (int coreIdx = 0; coreIdx < 8; coreIdx++) 87 | ptr[i++] = cpuRealTime[cpuBucketIdx][coreIdx].usage; 88 | 89 | ptr[i++] = cpuRealTime[cpuBucketIdx][8].usage; 90 | ptr[i++] = cpuTemperature; 91 | ptr[i] = batteryWatt; 92 | 93 | return 4L * 23; 94 | } 95 | 96 | uint32_t getExtMemorySize() { 97 | const char* filePathMIUI = "/data/extm/extm_file"; 98 | const char* filePathCOS = "/data/nandswap/swapfile"; 99 | 100 | struct stat statBuf { }; 101 | if (!access(filePathMIUI, F_OK)) { 102 | stat(filePathMIUI, &statBuf); 103 | if (statBuf.st_size > 1024) 104 | return statBuf.st_size >> 20;// bytes -> MiB 105 | } 106 | else if (!access(filePathCOS, F_OK)) { 107 | stat(filePathCOS, &statBuf); 108 | if (statBuf.st_size > 1024) 109 | return statBuf.st_size >> 20;// bytes -> MiB 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | void InitLMK() { 116 | if (!settings.enableLMK) 117 | return; 118 | 119 | // https://cs.android.com/android/platform/superproject/+/master:system/memory/lmkd/lmkd.cpp 120 | // https://source.android.com/devices/tech/perf/lmkd 121 | 122 | // page(1 page = 4KB) 123 | // 18432:0,23040:100,27648:200,32256:250,55296:900,80640:950 124 | // 8192:0,12288:100,16384:200,32768:250,65536:900,96000:950 125 | // 4096:0,5120:100,8192:200,32768:250,65536:900,96000:950 126 | const char* lmkdParameter[] = { 127 | "ro.lmk.low", "1001", 128 | "ro.lmk.medium", "1001", 129 | "ro.lmk.critical", "100", 130 | "ro.lmk.super_critical", "1001", 131 | "ro.lmk.reclaim_scan_threshold", "1024", 132 | "ro.lmk.use_new_strategy", "true", 133 | "ro.lmk.debug", "false", 134 | "ro.config.low_ram", "false", 135 | "ro.lmk.critical_upgrade", "false", 136 | "ro.lmk.use_minfree_levels", "false", 137 | "ro.lmk.kill_heaviest_task", "false", 138 | "ro.lmk.kill_timeout_ms", "2147483647", 139 | "ro.lmk.thrashing_limit", "100", 140 | "ro.lmk.enable_adaptive_lmk", "false", 141 | "ro.lmk.use_psi", "true", 142 | "ro.lmk.thrashing_limit_decay", "10", 143 | "ro.lmk.psi_partial_stall_ms", "800", 144 | "ro.lmk.psi_complete_stall_ms", "1000", 145 | "ro.lmk.downgrade_pressure", "100", 146 | "persist.sys.lmk.reportkills", "false", 147 | "ro.lmk.swap_free_low_percentage", "10", 148 | "ro.lmk.direct_reclaim_pressure", "100", 149 | "sys.lmk.minfree_levels", 150 | "4096:1001,5120:1001,8192:1001,32768:1001,96000:1001,131072:1001", 151 | }; 152 | // const char* adj = "0,100,200,250,900,950"; //另有 0,1,2,4,9,12 153 | const char minfree[] = "8192,12288,16384,32768,55296,80640"; 154 | 155 | int len = 14; 156 | if (!access("/sys/module/lowmemorykiller/parameters", F_OK)) { 157 | len -= 2; 158 | 159 | if (!Utils::writeString("/sys/module/lowmemorykiller/parameters/enable_lmk", 160 | "1", 2)) 161 | freezeit.log("调整lmk参数: 设置 enable_lmk 失败"); 162 | if (!Utils::writeString("/sys/module/lowmemorykiller/parameters/minfree", 163 | minfree, sizeof(minfree))) 164 | freezeit.log("调整lmk参数: 设置 minfree 失败"); 165 | } 166 | if (freezeit.moduleEnv == "Magisk") { 167 | string cmd; 168 | for (int i = 0; i < len; i += 2) 169 | cmd += string("magisk resetprop ") + lmkdParameter[i] + " " + lmkdParameter[i + 1] + ";"; 170 | cmd += "sleep 1;lmkd --reinit"; 171 | system(cmd.c_str()); 172 | freezeit.log("更新参数 LMK"); 173 | } 174 | else if (freezeit.moduleEnv == "KernelSU") { 175 | if (!access("/data/adb/ksu/resetprop", F_OK)) { 176 | string cmd; 177 | for (int i = 0; i < len; i += 2) 178 | cmd += string("/data/adb/ksu/resetprop ") + lmkdParameter[i] + " " + lmkdParameter[i + 1] + ";"; 179 | cmd += "sleep 1;lmkd --reinit"; 180 | system(cmd.c_str()); 181 | freezeit.log("更新参数 LMK"); 182 | } 183 | else { 184 | freezeit.log("未找到 KSU resetprop"); 185 | } 186 | } 187 | else if (freezeit.moduleEnv == "Apatch") { 188 | if (!access("/data/adb/ap/bin/resetprop", F_OK)) { 189 | string cmd; 190 | for (int i = 0; i < len; i += 2) 191 | cmd += string("/data/adb/ap/bin/resetprop") + lmkdParameter[i] + " " + lmkdParameter[i + 1] + ";"; 192 | cmd += "sleep 1;lmkd --reinit"; 193 | system(cmd.c_str()); 194 | freezeit.log("更新参数 LMK"); 195 | } 196 | else { 197 | freezeit.log("未找到 APatch resetprop"); 198 | } 199 | } 200 | } 201 | 202 | void getCpuTempPath() { 203 | // 主板温度 /sys/class/thermal/thermal_message/board_sensor_temp 204 | for (int i = 0; i < 32; i++) { 205 | char path[256]; 206 | snprintf(path, sizeof(path), "/sys/class/thermal/thermal_zone%d/type", i); 207 | 208 | char type[64] = {}; 209 | Utils::readString(path, type, sizeof(type)); 210 | 211 | if (!strncmp(type, "soc_max", 6) || !strncmp(type, "mtktscpu", 8) || 212 | !strncmp(type, "cpu", 3)) { 213 | snprintf(cpuTempPath, sizeof(path), "/sys/class/thermal/thermal_zone%d/temp", i); 214 | break; 215 | } 216 | } 217 | } 218 | 219 | map getCpuCluster() { 220 | map < uint32_t, uint32_t > freqMap; 221 | char path[] = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; 222 | for (int coreIdx = 0; coreIdx < 8; coreIdx++) { 223 | path[27] = '0' + coreIdx; 224 | freqMap[Utils::readInt(path)]++; 225 | } 226 | return freqMap; 227 | } 228 | 229 | 230 | int readBatteryWatt() { 231 | int voltage = Utils::readInt("/sys/class/power_supply/battery/voltage_now"); 232 | int current = Utils::readInt("/sys/class/power_supply/battery/current_now"); 233 | 234 | if (2'000'000 < voltage) voltage >>= 10; //2-10V 串并联 uV -> mV 235 | if (settings.enableCurrentFix) { 236 | if (abs(current) > 100'000) { // 如果单位是毫安,那么电流大于100A不现实,所以单位应该是微安,这种情况不能开启电流校准 237 | current /= 1000; // uA -> mA 238 | settings.enableCurrentFix = 0; 239 | freezeit.log("电流校准不应开启, 已自动关闭"); 240 | freezeit.log(settings.save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 241 | } 242 | } 243 | else { 244 | current /= 1000; // uA -> mA 245 | } 246 | 247 | if (settings.enableDoubleCell)//双电芯 248 | current *= 2; 249 | 250 | return (voltage * current) / (freezeit.isSamsung ? 1000 : -1000); 251 | } 252 | 253 | void checkBattery() { 254 | const int TIMEOUT = 60; 255 | static int secCnt = 58; 256 | static int lastCapacity = 0; 257 | static int lastMinute = 0; 258 | 259 | START_TIME_COUNT; 260 | 261 | if (settings.enableBatteryMonitor == 0 || (++secCnt < TIMEOUT)) 262 | return; 263 | 264 | secCnt = 0; 265 | 266 | const int nowCapacity = Utils::readInt("/sys/class/power_supply/battery/capacity"); 267 | if (lastCapacity == nowCapacity) 268 | return; 269 | 270 | if (lastCapacity == 0) { // 开机时 271 | lastCapacity = nowCapacity; 272 | lastMinute = static_cast(time(nullptr) / 60); 273 | 274 | // 电池内核状态 字符串 275 | // https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/drivers/power/supply/power_supply_sysfs.c 276 | const int charge_full_design = Utils::readInt("/sys/class/power_supply/battery/charge_full_design"); 277 | const int charge_full = Utils::readInt("/sys/class/power_supply/battery/charge_full"); 278 | const int cycle_count = Utils::readInt("/sys/class/power_supply/battery/cycle_count"); 279 | const int battery_soh = Utils::readInt("/sys/class/oplus_chg/battery/battery_soh"); 280 | 281 | if (charge_full_design) { 282 | freezeit.logFmt("🔋电池 设计容量: %dmAh", charge_full_design / 1000); 283 | int health = 100 * charge_full / charge_full_design; 284 | if (40 < health && health <= 100) { 285 | freezeit.logFmt("🔋电池 当前容量: %dmAh", charge_full / 1000); 286 | freezeit.logFmt("🔋电池 健康程度: %d%%", health); 287 | } 288 | } 289 | 290 | if (40 < battery_soh && battery_soh <= 100) 291 | freezeit.logFmt("🔋电池 健康程度(内置): %d%%", battery_soh); 292 | 293 | if (cycle_count) 294 | freezeit.logFmt("🔋电池 循环次数: %d", cycle_count); 295 | 296 | freezeit.log("🔋电池 数据由系统提供, 仅供参考"); 297 | } 298 | else { 299 | const int mWatt = abs(readBatteryWatt()); 300 | const int nowMinute = static_cast(time(nullptr) / 60); 301 | const int deltaMinute = nowMinute - lastMinute; 302 | const int deltaCapacity = nowCapacity - lastCapacity; 303 | const int temperature = Utils::readInt("/sys/class/power_supply/battery/temp"); 304 | 305 | stackString<64> timeStr; 306 | if (deltaMinute >= 60) 307 | timeStr.appendFmt("%d时", deltaMinute / 60); 308 | timeStr.appendFmt("%d分钟", deltaMinute % 60); 309 | 310 | freezeit.logFmt("%s到 %d%% %s%s了%d%% %.2fw %.1f℃", 311 | deltaCapacity < 0 ? (deltaMinute == 1 ? "❗耗电" : "🔋放电") : 312 | ((mWatt > 20'000 || deltaCapacity >= 3) ? "⚡快充" : "🔌充电"), 313 | nowCapacity, *timeStr, deltaCapacity < 0 ? "用" : "充", 314 | abs(deltaCapacity), mWatt / 1e3, temperature / 1e1); 315 | 316 | lastMinute = nowMinute; 317 | lastCapacity = nowCapacity; 318 | } 319 | END_TIME_COUNT; 320 | } 321 | 322 | 323 | void InitCPU() { 324 | 325 | const auto res = getCpuCluster(); 326 | cpuCluster = 0; 327 | for (const auto& [freq, num] : res) 328 | cpuCluster = cpuCluster * 10 + num; 329 | 330 | if (cpuCluster && res.size() < 10) { 331 | stackString<256> str("核心频率"); 332 | for (const auto& [freq, cnt] : res) 333 | str.appendFmt(" %.2fGHz*%d", freq / (freq > 1e8 ? 1e9 : 1e6), cnt); 334 | freezeit.log(*str); 335 | } 336 | 337 | switch (cpuCluster) { 338 | case 431: 339 | COLOR_CPU = COLOR_CLUSTER[1]; 340 | break; 341 | case 422: 342 | COLOR_CPU = COLOR_CLUSTER[2]; 343 | break; 344 | case 3221: 345 | COLOR_CPU = COLOR_CLUSTER[3]; 346 | break; 347 | case 62: 348 | COLOR_CPU = COLOR_CLUSTER[4]; 349 | break; 350 | case 251: 351 | COLOR_CPU = COLOR_CLUSTER[5]; 352 | break; 353 | } 354 | 355 | cpuCoreAll = sysconf(_SC_NPROCESSORS_CONF); 356 | cpuCoreValid = sysconf(_SC_NPROCESSORS_ONLN); 357 | freezeit.logFmt("全部核心 %d 可用核心 %d", cpuCoreAll, cpuCoreValid); 358 | if (cpuCoreAll != cpuCoreValid) { 359 | stackString<128> tips("当前离线核心 "); 360 | char tmp[64]; 361 | for (int i = 0; i < cpuCoreAll; i++) { 362 | snprintf(tmp, sizeof(tmp), "/sys/devices/system/cpu/cpu%d/online", i); 363 | auto fd = open(tmp, O_RDONLY); 364 | if (fd < 0)continue; 365 | read(fd, tmp, 1); 366 | close(fd); 367 | if (tmp[0] == '0') 368 | tips.append("[", 1).append(i).append("]", 1); 369 | } 370 | freezeit.log(tips.c_str()); 371 | } 372 | if (cpuCoreValid > 16) { 373 | cpuCoreValid = 16; 374 | freezeit.log("核心数量超过16, 部分功能可能不受支持"); 375 | } 376 | } 377 | 378 | uint32_t drawChart(uint32_t* imgBuf, uint32_t height, uint32_t width) { 379 | START_TIME_COUNT; 380 | 381 | while (height * width > 1024 * 1024) { 382 | height /= 2; 383 | width /= 2; 384 | } 385 | 386 | const uint32_t imgSize = sizeof(uint32_t) * height * width; 387 | memset(imgBuf, 0, imgSize); 388 | const uint32_t imgHeight = height * 4 / 5; // 0.8; 389 | 390 | // ABGR 391 | constexpr uint32_t COLOR_BLUE = 0xBBFF8000; 392 | constexpr uint32_t COLOR_GRAY = 0x01808080; 393 | 394 | const uint32_t percent25 = (height / 5) * width; 395 | const uint32_t percent50 = (height * 2 / 5) * width; 396 | const uint32_t percent75 = (height * 3 / 5) * width; 397 | for (uint32_t x = 0; x < width; x++) { //横线 398 | imgBuf[percent25 + x] = COLOR_GRAY; 399 | imgBuf[percent50 + x] = COLOR_GRAY; 400 | imgBuf[percent75 + x] = COLOR_GRAY; 401 | } 402 | 403 | uint32_t line_x_pos[10]{ 0 }; 404 | for (int i = 1; i < 10; ++i) 405 | line_x_pos[i] = width * i / 10; 406 | for (uint32_t y = 0; y < imgHeight; y++) { //中间竖线 407 | const uint32_t heightBase = width * y; 408 | for (int i = 1; i < 10; ++i) 409 | imgBuf[heightBase + line_x_pos[i]] = COLOR_GRAY; 410 | } 411 | 412 | // 横轴坐标 物理,虚拟,内存条的 起点 占用点 终点 413 | const uint32_t mem_x_pos[6] = { 414 | width * 5 / 100, 415 | width * 5 / 100 + 416 | (memInfo.totalRam ? (width * 4 / 10 * (memInfo.totalRam - memInfo.availRam) / 417 | memInfo.totalRam) : 0), 418 | width * 45 / 100, 419 | 420 | width * 55 / 100, 421 | width * 55 / 100 + 422 | (memInfo.totalSwap ? (width * 4 / 10 * (memInfo.totalSwap - memInfo.freeSwap) / 423 | memInfo.totalSwap) : 0), 424 | width * 95 / 100, 425 | }; 426 | 427 | //内存 进度条 428 | if (memInfo.totalSwap == 0) { // 0.85 429 | for (uint32_t y = (height * 218) >> 8; y < height; y++) { 430 | const uint32_t heightBase = width * y; 431 | for (uint32_t x = mem_x_pos[0]; x < mem_x_pos[2]; x++) 432 | imgBuf[heightBase + x] = x < mem_x_pos[1] ? COLOR_BLUE : COLOR_GRAY; 433 | } 434 | } 435 | else { 436 | for (uint32_t y = (height * 218) >> 8; y < height; y++) { 437 | const uint32_t heightBase = width * y; 438 | for (uint32_t x = mem_x_pos[0]; x < mem_x_pos[2]; x++) 439 | imgBuf[heightBase + x] = x < mem_x_pos[1] ? COLOR_BLUE : COLOR_GRAY; 440 | for (uint32_t x = mem_x_pos[3]; x < mem_x_pos[5]; x++) 441 | imgBuf[heightBase + x] = x < mem_x_pos[4] ? COLOR_BLUE : COLOR_GRAY; 442 | } 443 | } 444 | 445 | for (int minuteIdx = 1; minuteIdx < maxBucketSize; minuteIdx++) { 446 | for (int coreIdx = 0; coreIdx < 8; coreIdx++) { 447 | uint32_t y0 = 448 | (100 - cpuRealTime[(cpuBucketIdx + minuteIdx) & 0x1f][coreIdx].usage) * 449 | imgHeight / 100; 450 | uint32_t y1 = 451 | (100 - cpuRealTime[(cpuBucketIdx + minuteIdx + 1) & 0x1f][coreIdx].usage) * 452 | imgHeight / 100; 453 | 454 | if (y0 <= 0) y0 = 1; 455 | else if (y0 >= imgHeight) y0 = imgHeight - 1; 456 | 457 | if (y1 <= 0) y1 = 1; 458 | else if (y1 >= imgHeight) y1 = imgHeight - 1; 459 | 460 | drawLine(imgBuf, width, COLOR_CPU[coreIdx], (width * (minuteIdx - 1)) / 31, y0, 461 | (width * minuteIdx) / 31, y1); 462 | } 463 | } 464 | 465 | const uint32_t bottomLine = imgHeight * width; 466 | for (uint32_t x = 0; x < width; x++) { //上下横线 467 | imgBuf[x] = COLOR_BLUE; 468 | imgBuf[bottomLine + x] = COLOR_BLUE; 469 | } 470 | 471 | for (uint32_t y = 0; y < imgHeight; y++) { //两侧竖线 472 | imgBuf[width * y] = COLOR_BLUE; 473 | imgBuf[width * (y + 1) - 1] = COLOR_BLUE; 474 | } 475 | 476 | END_TIME_COUNT; 477 | return imgSize; 478 | } 479 | 480 | // https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C 481 | // Bresenham s_line_algorithm 482 | void drawLine(uint32_t* imgBuf, const uint32_t width, const uint32_t COLOR, 483 | int x0, int y0, const int x1, const int y1) { 484 | 485 | //delta x y, step x y 486 | const int dx = abs(x1 - x0); 487 | const int dy = abs(y1 - y0); 488 | const int sx = x0 < x1 ? 1 : -1; 489 | const int sy = y0 < y1 ? 1 : -1; 490 | int err = (dx > dy ? dx : -dy) / 2; 491 | 492 | while (true) { 493 | if (y0 == y1) { 494 | int minValue = x0 <= x1 ? x0 : x1; 495 | const int maxValue = x0 > x1 ? x0 : x1; 496 | for (; minValue <= maxValue; minValue++) 497 | imgBuf[width * y0 + minValue] = COLOR; 498 | return; 499 | } 500 | else if (x0 == x1) { 501 | int minValue = y0 <= y1 ? y0 : y1; 502 | const int maxValue = y0 > y1 ? y0 : y1; 503 | for (; minValue <= maxValue; minValue++) 504 | imgBuf[width * minValue + x0] = COLOR; 505 | return; 506 | } 507 | 508 | imgBuf[width * y0 + x0] = COLOR; 509 | 510 | const int e2 = err; 511 | if (e2 > -dx) { 512 | err -= dy; 513 | x0 += sx; 514 | } 515 | if (e2 < dy) { 516 | err += dx; 517 | y0 += sy; 518 | } 519 | } 520 | } 521 | 522 | void getCPU_realtime(const uint32_t availableMiB) { 523 | static char path[] = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"; 524 | static uint32_t jiffiesSumLast[9] = {}; 525 | static uint32_t jiffiesIdleLast[9] = {}; 526 | 527 | struct sysinfo s_info; 528 | if (!sysinfo(&s_info)) { 529 | memInfo.totalRam = s_info.totalram >> 20; // convert to MiB 530 | memInfo.availRam = availableMiB; 531 | 532 | memInfo.totalSwap = s_info.totalswap >> 20; 533 | memInfo.freeSwap = s_info.freeswap >> 20; 534 | } 535 | 536 | cpuBucketIdx = (cpuBucketIdx + 1) & 0b1'1111; // %32 maxBucketSize 537 | 538 | // read frequency 539 | for (int coreIdx = 0; coreIdx < 8; coreIdx++) { 540 | path[27] = '0' + coreIdx; 541 | cpuRealTime[cpuBucketIdx][coreIdx].freq = Utils::readInt(path) / 1000;// MHz 542 | } 543 | 544 | // read occupy 545 | auto fp = fopen("/proc/stat", "rb"); 546 | if (fp) { 547 | char buff[256]; 548 | uint32_t jiffiesList[8]{ 0 }; 549 | 550 | while (true) { 551 | fgets(buff, sizeof(buff), fp); 552 | if (strncmp(buff, "cpu", 3)) 553 | break; 554 | 555 | uint32_t coreIdx = 8; // 默认 总CPU数据放到 最后索引位置 556 | if (buff[3] == ' ') // 总CPU数据 557 | sscanf(buff + 4, "%u %u %u %u %u %u %u", 558 | jiffiesList + 0, jiffiesList + 1, jiffiesList + 2, jiffiesList + 3, 559 | jiffiesList + 4, jiffiesList + 5, jiffiesList + 6); 560 | else 561 | sscanf(buff + 3, "%u %u %u %u %u %u %u %u", &coreIdx, 562 | jiffiesList + 0, jiffiesList + 1, jiffiesList + 2, jiffiesList + 3, 563 | jiffiesList + 4, jiffiesList + 5, jiffiesList + 6); 564 | 565 | if (coreIdx > 8) { 566 | freezeit.logFmt("CPU可能超过8核, 暂不支持: coreIdx:%d", coreIdx); 567 | break; 568 | } 569 | 570 | // user, nice, system, idle, iowait, irq, softirq 571 | uint32_t jiffiesSum{ 0 }; 572 | for (int jiffIdx = 0; jiffIdx < 7; jiffIdx++) 573 | jiffiesSum += jiffiesList[jiffIdx]; 574 | 575 | uint32_t& jiffiesIdle = jiffiesList[3]; 576 | if (jiffiesSumLast[coreIdx] == 0) { 577 | jiffiesSumLast[coreIdx] = jiffiesSum; 578 | jiffiesIdleLast[coreIdx] = jiffiesIdle; 579 | } 580 | else { 581 | const uint32_t sumDelta = jiffiesSum - jiffiesSumLast[coreIdx]; 582 | const uint32_t idleDelta = jiffiesIdle - jiffiesIdleLast[coreIdx]; 583 | const int usage = (sumDelta == 0 || idleDelta == 0 || idleDelta > sumDelta) ? 584 | 0 : (100 * (sumDelta - idleDelta) / sumDelta); 585 | 586 | cpuRealTime[cpuBucketIdx][coreIdx].usage = usage; 587 | jiffiesSumLast[coreIdx] = jiffiesSum; 588 | jiffiesIdleLast[coreIdx] = jiffiesIdle; 589 | } 590 | } 591 | fclose(fp); 592 | } 593 | 594 | cpuTemperature = Utils::readInt(cpuTempPath); 595 | batteryWatt = readBatteryWatt(); 596 | } 597 | 598 | 599 | // 0获取失败 1失败 2成功 600 | int breakNetworkByLocalSocket(const int uid) { 601 | START_TIME_COUNT; 602 | 603 | int buff[64]; 604 | const int recvLen = Utils::localSocketRequest(XPOSED_CMD::BREAK_NETWORK, &uid, 4, buff, 605 | sizeof(buff)); 606 | 607 | if (recvLen == 0) { 608 | freezeit.logFmt("%s() 工作异常, 请确认LSPosed中Frozen勾选系统框架, 然后重启", __FUNCTION__); 609 | END_TIME_COUNT; 610 | return 0; 611 | } 612 | else if (recvLen != 4) { 613 | freezeit.logFmt("%s() 返回数据异常 recvLen[%d]", __FUNCTION__, recvLen); 614 | if (recvLen > 0 && recvLen < 64 * 4) 615 | freezeit.logFmt("DumpHex: %s", Utils::bin2Hex(buff, recvLen).c_str()); 616 | END_TIME_COUNT; 617 | return 0; 618 | } 619 | END_TIME_COUNT; 620 | return buff[0]; 621 | } 622 | 623 | 624 | int GetProperty(const char* key, char* res) { 625 | const prop_info* pi = __system_property_find(key); //如果频繁使用,建议缓存 对应Key的 prop_info 626 | if (pi == nullptr) { 627 | res[0] = 0; 628 | return -1; 629 | } 630 | 631 | __system_property_read_callback(pi, 632 | [](void* cookie, const char*, const char* value, unsigned) { 633 | if (value[0]) 634 | strncpy((char*)cookie, value, PROP_VALUE_MAX); 635 | else ((char*)cookie)[0] = 0; 636 | }, 637 | res); 638 | 639 | return res[0] ? 1 : -1; 640 | } 641 | 642 | int getScreenProperty() { 643 | static const prop_info* pi = nullptr; 644 | 645 | if (pi == nullptr) { 646 | pi = __system_property_find("debug.tracing.screen_state"); 647 | if (pi == nullptr) { 648 | return -1; 649 | } 650 | } 651 | 652 | char res[PROP_VALUE_MAX] = { 0 }; 653 | __system_property_read_callback(pi, 654 | [](void* cookie, const char*, const char* value, unsigned) { 655 | if (value[0]) 656 | strncpy((char*)cookie, value, PROP_VALUE_MAX); 657 | else ((char*)cookie)[0] = 0; 658 | }, 659 | res); 660 | 661 | return res[0] ? res[0] - '0' : -1; 662 | } 663 | 664 | // https://blog.csdn.net/meccaendless/article/details/80238997 665 | void sndThreadFunc() { 666 | const int SND_BUF_SIZE = 8192; 667 | const char* sndPath = "/dev/snd"; 668 | 669 | // const char *event_str[EVENT_NUM] = 670 | // { 671 | // "IN_ACCESS", 672 | // "IN_MODIFY", 673 | // "IN_ATTRIB", 674 | // "IN_CLOSE_WRITE", 675 | // "IN_CLOSE_NOWRITE", 676 | // "IN_OPEN", 677 | // "IN_MOVED_FROM", 678 | // "IN_MOVED_TO", 679 | // "IN_CREATE", 680 | // "IN_DELETE", 681 | // "IN_DELETE_SELF", 682 | // "IN_MOVE_SELF" 683 | // }; 684 | 685 | sleep(2); 686 | //FGS 687 | 688 | char buf[SND_BUF_SIZE]; 689 | 690 | int inotifyFd = inotify_init(); 691 | if (inotifyFd < 0) { 692 | fprintf(stderr, "同步事件: 0xC0 (1/2)失败 [%d]:[%s]", errno, strerror(errno)); 693 | exit(-1); 694 | } 695 | 696 | int watch_d = inotify_add_watch(inotifyFd, sndPath, 697 | IN_OPEN | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE); 698 | if (watch_d < 0) { 699 | fprintf(stderr, "同步事件: 0xC0 (2/2)失败 [%d]:[%s]", errno, strerror(errno)); 700 | exit(-1); 701 | } 702 | 703 | freezeit.log("监控音频播放事件成功"); 704 | 705 | int playbackDevicesCnt = 0; 706 | ssize_t readLen; 707 | 708 | while ((readLen = read(inotifyFd, buf, SND_BUF_SIZE)) > 0) { 709 | int readCnt{ 0 }; 710 | while (readCnt < readLen) { 711 | inotify_event* event{ reinterpret_cast(buf + readCnt) }; 712 | readCnt += sizeof(inotify_event) + event->len; 713 | 714 | if (strncmp(event->name, "pcm", 3) || Utils::lastChar(event->name + 4) != 'p') 715 | continue; 716 | 717 | if ((event->mask) & IN_OPEN) { 718 | playbackDevicesCnt++; 719 | } 720 | else if (event->mask & (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)) { 721 | if (playbackDevicesCnt > 0) 722 | playbackDevicesCnt--; 723 | } 724 | } 725 | isAudioPlaying.store(playbackDevicesCnt > 0, std::memory_order_relaxed); 726 | //isAudioPlaying = playbackDevicesCnt > 0; 727 | usleep(250 * 1000); 728 | } 729 | 730 | inotify_rm_watch(inotifyFd, watch_d); 731 | close(inotifyFd); 732 | 733 | fprintf(stderr, "同步事件: 0xC0 异常退出 [%d]:[%s]", errno, strerror(errno)); 734 | exit(-1); 735 | } 736 | 737 | }; 738 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | using std::atomic; 54 | using std::set; 55 | using std::unordered_set; 56 | using std::map; 57 | using std::multimap; 58 | using std::stringstream; 59 | using std::lock_guard; 60 | using std::unique_ptr; 61 | using std::ifstream; 62 | using std::vector; 63 | using std::string; 64 | using std::string_view; 65 | using std::thread; 66 | using std::mutex; 67 | 68 | using std::make_unique; 69 | using std::to_string; 70 | using std::move; 71 | 72 | 73 | // 配置编译选项 ***************** 74 | constexpr auto FORK_DOUBLE = 1; 75 | 76 | #define DEBUG_LOG 1 77 | #define DEBUG_DURATION 0 78 | // ***************************** 79 | 80 | #if DEBUG_LOG 81 | #define DLOG(...) freezeit.logFmt(__VA_ARGS__) 82 | #else 83 | #define DLOG(...) ((void)0) 84 | #endif 85 | 86 | #if DEBUG_DURATION 87 | #define START_TIME_COUNT auto start_clock = clock() 88 | #if CLOCKS_PER_SEC == 1000000 89 | #define END_TIME_COUNT int duration_us=clock()-start_clock;freezeit.logFmt("%s(): %d.%03d ms", __FUNCTION__, duration_us/1000, duration_us%1000) 90 | #elif CLOCKS_PER_SEC == 1000 91 | #define END_TIME_COUNT int duration_ms=clock()-start_clock;freezeit.logFmt("%s(): %d ms", __FUNCTION__, duration_ms) 92 | #else 93 | #error CLOCKS_PER_SEC value is not support 94 | #endif 95 | #else 96 | #define START_TIME_COUNT ((void)0) 97 | #define END_TIME_COUNT ((void)0) 98 | #endif 99 | 100 | #define SYNC_RECEIVED_WHILE_FROZEN (1) 101 | #define ASYNC_RECEIVED_WHILE_FROZEN (2) 102 | #define TXNS_PENDING_WHILE_FROZEN (4) 103 | 104 | #define BITS_PER_LONG (sizeof(long) * 8) 105 | #define test_bit(array, bit) ((array[bit / BITS_PER_LONG] >> bit % BITS_PER_LONG) & 1) 106 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) 107 | 108 | enum class WORK_MODE : uint32_t { 109 | GLOBAL_SIGSTOP = 0, 110 | V1 = 1, 111 | V1_ST = 2, 112 | V2UID = 3, 113 | V2FROZEN = 4, 114 | }; 115 | 116 | enum class MemoryRecycleMode : uint32_t { 117 | ALL = 1, 118 | ANON = 2, 119 | FILE = 3, 120 | }; 121 | enum class FREEZE_MODE : uint32_t { 122 | TERMINATE = 10, 123 | SIGNAL = 20, 124 | SIGNAL_BREAK = 21, 125 | FREEZER = 30, 126 | FREEZER_BREAK = 31, 127 | WHITELIST = 40, 128 | WHITEFORCE = 50, 129 | }; 130 | 131 | const int baseCode = 1668424211; 132 | 133 | enum class XPOSED_CMD : uint32_t { 134 | // 1359322925 是 "Frozen" 的10进制CRC32值 135 | GET_FOREGROUND = baseCode + 1, 136 | GET_SCREEN = baseCode + 2, 137 | GET_XP_LOG = baseCode + 3, 138 | 139 | SET_CONFIG = baseCode + 20, 140 | SET_WAKEUP_LOCK = baseCode + 21, 141 | 142 | BREAK_NETWORK = baseCode + 41, 143 | 144 | UPDATE_PENDING = baseCode + 60, // 更新待冻结应用 145 | UPDATE_PENDINGINTENT = baseCode + 80, // 后台意图 146 | }; 147 | 148 | enum class REPLY : uint32_t { 149 | SUCCESS = 2, // 成功 150 | FAILURE = 0, // 失败 151 | }; 152 | 153 | enum class WAKEUP_LOCK : uint32_t { 154 | IGNORE = 1, 155 | DEFAULT = 3, 156 | }; 157 | 158 | 159 | enum class MANAGER_CMD : uint32_t { 160 | // 获取信息 无附加数据 No additional data required 161 | getPropInfo = 2, // return string: "ID\nName\nVersion\nVersionCode\nAuthor\nclusterNum" 162 | getChangelog = 3, // return string: "changelog" 163 | getLog = 4, // return string: "log" 164 | getAppCfg = 5, // return string: "package x\npackage x\n... 165 | getRealTimeInfo = 6, // return ImgBytes[h*w*4]+String: (rawBitmap + 内存 频率 使用率 电流) 166 | getSettings = 8, // return bytes[256]: all settings parameter 167 | getUidTime = 9, // return "uid last_user_time last_sys_time user_time sys_time\n..." 168 | getXpLog = 10, 169 | 170 | // 设置 需附加数据 171 | setAppCfg = 21, // send "package x\npackage x\npackage x\n..." 172 | setAppLabel = 22, // send "uid label\nuid label\nuid label\n..." 173 | setSettingsVar = 23, // send bytes[2]: [0]index [1]value 174 | 175 | // 其他命令 无附加数据 No additional data required 176 | clearLog = 61, // return string: "log" //清理并返回log 177 | getProcState = 62, // return string: "log" //打印冻结状态并返回log 178 | 179 | }; 180 | 181 | struct binder_state { 182 | int fd; 183 | void* mapped; 184 | size_t mapSize; 185 | }; 186 | 187 | struct KernelVersionStruct { 188 | int main = 0; 189 | int sub = 0; 190 | int patch = 0; 191 | }; 192 | 193 | struct MemInfoStruct { // Unit: MiB 194 | int totalRam = 1; 195 | int availRam = 1; 196 | int totalSwap = 1; 197 | int freeSwap = 1; 198 | }; 199 | 200 | struct cpuRealTimeStruct { 201 | int freq; // Mhz 202 | int usage; // % 203 | }; 204 | 205 | struct uidTimeStruct { 206 | int lastTotal = 0; 207 | int total = 0; 208 | }; 209 | 210 | struct appInfoStruct { 211 | int uid = -1; 212 | atomic FreezeStat{false}; 213 | FREEZE_MODE freezeMode = FREEZE_MODE::FREEZER; // [10]:杀死 [20]:SIGSTOP [30]:freezer [40]:配置 [50]:内置 214 | bool isPermissive = true; // 宽容的 有前台服务也算前台 215 | int delayCnt = 0; // 冻结失败计数 216 | bool isSystemApp = true; // 是否系统应用 217 | time_t startTimestamp = 0; // 某次开始运行时刻 218 | time_t stopTimestamp = 0; // 某次冻结运行时刻 219 | time_t totalRunningTime = 0; // 运行时长 220 | string package; // 包名 221 | string label; // 名称 222 | vector pids; // PID列表 223 | bool needBreakNetwork() const { 224 | return freezeMode == FREEZE_MODE::SIGNAL_BREAK || freezeMode == FREEZE_MODE::FREEZER_BREAK; 225 | } 226 | bool isSignalMode() const { 227 | return freezeMode == FREEZE_MODE::SIGNAL_BREAK || freezeMode == FREEZE_MODE::SIGNAL; 228 | } 229 | bool isFreezeMode() const { 230 | return freezeMode == FREEZE_MODE::FREEZER_BREAK || freezeMode == FREEZE_MODE::FREEZER; 231 | } 232 | bool isSignalOrFreezer() const { 233 | return freezeMode <= FREEZE_MODE::SIGNAL && freezeMode < FREEZE_MODE::WHITELIST; 234 | } 235 | bool isWhitelist() const { 236 | return freezeMode >= FREEZE_MODE::WHITELIST; 237 | } 238 | bool isBlacklist() const { 239 | return freezeMode < FREEZE_MODE::WHITELIST; 240 | } 241 | bool isTerminateMode() const { 242 | return freezeMode == FREEZE_MODE::TERMINATE; 243 | } 244 | bool isFreezeStat() const { 245 | return FreezeStat.load(); 246 | } 247 | }; 248 | 249 | struct cfgStruct { 250 | FREEZE_MODE freezeMode = FREEZE_MODE::FREEZER; 251 | bool isPermissive = true; 252 | }; 253 | 254 | template 255 | class stackString { 256 | public: 257 | size_t length{ 0 }; 258 | char data[CAPACITY]; 259 | 260 | const char* c_str() { return data; } 261 | const char* operator* () { return data; } 262 | 263 | stackString() { data[0] = 0; } 264 | stackString(const string_view& s) { 265 | memcpy(data, s.data(), s.length()); 266 | length = s.length(); 267 | data[length] = 0; 268 | } 269 | stackString(const char* s, const size_t len) { 270 | memcpy(data, s, len); 271 | length = len; 272 | data[length] = 0; 273 | } 274 | 275 | stackString& append(const int n) { 276 | char tmp[16]; 277 | return append(tmp, static_cast(snprintf(tmp, sizeof(tmp), "%d", n))); 278 | } 279 | 280 | stackString& append(const char* s) { 281 | return append(s, strlen(s)); 282 | } 283 | 284 | stackString& append(const char c) { 285 | if (length + 1 >= CAPACITY) // 预留最后一位 填 '\0' 286 | return *this; 287 | 288 | data[length++] = c; 289 | data[length] = 0; 290 | return *this; 291 | } 292 | 293 | stackString& append(const char* s, const size_t len) { 294 | if (length + len >= CAPACITY) // 预留最后一位 填 '\0' 295 | return *this; 296 | 297 | memcpy(data + length, s, len); 298 | length += len; 299 | data[length] = 0; 300 | return *this; 301 | } 302 | 303 | // 注意长度,可能溢出,不安全 304 | template 305 | stackString& appendFmt(const char* fmt, Args&&... args) { 306 | if (length < CAPACITY) 307 | length += snprintf(data + length, CAPACITY - length, fmt, std::forward(args)...); 308 | return *this; 309 | } 310 | 311 | void clear() { length = 0; data[0] = 0; } 312 | }; 313 | 314 | 315 | namespace Utils { 316 | 317 | vector splitString(const string& str, const string& delim) { 318 | if (str.empty()) return {}; 319 | if (delim.empty()) return { str }; 320 | 321 | vector res; 322 | size_t nextDelimIdx, targetBeginIdx = 0; 323 | while ((nextDelimIdx = str.find(delim, targetBeginIdx)) != string::npos) { 324 | if (nextDelimIdx == targetBeginIdx) { 325 | targetBeginIdx += delim.length(); 326 | continue; 327 | } 328 | res.emplace_back(str.substr(targetBeginIdx, nextDelimIdx - targetBeginIdx)); 329 | targetBeginIdx = nextDelimIdx + delim.length(); 330 | } 331 | if (targetBeginIdx < str.length()) 332 | res.emplace_back(str.substr(targetBeginIdx, str.length() - targetBeginIdx)); 333 | return res; 334 | } 335 | 336 | void strReplace(string& src, const string& oldBlock, const string& newBlock) { 337 | if (oldBlock.empty())return; 338 | 339 | size_t nextBeginIdx = 0, foundIdx; 340 | while ((foundIdx = src.find(oldBlock, nextBeginIdx)) != string::npos) { 341 | src.replace(foundIdx, oldBlock.length(), newBlock); 342 | 343 | // 替换后,在新区块【后面】开始搜索 344 | nextBeginIdx = foundIdx + newBlock.length(); 345 | 346 | // 替换后,以新区块【起点】开始搜索。即:替换之后的新区块连同【后续】内容仍有可能满足条件而被继续替换 347 | // nextBeginIdx = foundIdx; 348 | 349 | // 替换后,在新区块【前后】搜索,新区块连同【前-后】内容仍有可能满足条件而被继续替换 350 | // nextBeginIdx = foundIdx > oldBlock.length() ? (foundIdx - oldBlock.length()) : 0; 351 | } 352 | return; 353 | } 354 | 355 | string bin2Hex(const void* bytes, const int len) { 356 | auto charList = "0123456789ABCDEF"; 357 | if (len == 0) return ""; 358 | string res(len * 3, ' '); 359 | for (int i = 0; i < len; i++) { 360 | const uint8_t value = reinterpret_cast(bytes)[i]; 361 | res[i * 3L] = charList[value >> 4]; 362 | res[i * 3L + 1] = charList[value & 0x0f]; 363 | } 364 | return res; 365 | } 366 | 367 | //"2022-01-01 00:00:00" 368 | time_t timeFormat2Timestamp(const char* strTimeFormat) { 369 | // strTimeFormat should be such as "2001-11-12 18:31:01" 370 | struct tm timeinfo; 371 | memset((void*)&timeinfo, 0, sizeof(struct tm)); 372 | 373 | // strptime("1970:01:01 08:00:00", "%Y:%m:%d %H:%M:%S", timeinfo); 374 | strptime(strTimeFormat, "%Y-%m-%d %H:%M:%S", &timeinfo); 375 | 376 | return mktime(&timeinfo); 377 | } 378 | 379 | // https://blog.csdn.net/lanmanck/article/details/8423669 380 | vector getTouchEventNum() { 381 | vector res; 382 | 383 | for (int i = 0; i < 16; i++) { 384 | char path[64]; 385 | snprintf(path, 64, "/dev/input/event%d", i); 386 | auto fd = open(path, O_RDONLY, 0); 387 | if (fd < 0)continue; 388 | 389 | uint32_t flagBit = 0; 390 | constexpr uint32_t cmd = EVIOCGBIT(0, sizeof(uint32_t)); 391 | ioctl(fd, cmd, &flagBit); 392 | if (flagBit & (1 << EV_ABS)) res.emplace_back(i); 393 | close(fd); 394 | } 395 | if (res.size() == 0) { 396 | fprintf(stderr, "前台任务同步事件获取失败"); 397 | exit(-1); 398 | } 399 | return res; 400 | } 401 | 402 | int Is_Event(const struct dirent* Dir) { 403 | return strncmp("event", Dir->d_name, 5) == 0; 404 | } 405 | 406 | string GetTouchScreenDevice() { 407 | struct dirent** namelist; 408 | int ndev = scandir("/dev/input", &namelist, Is_Event, alphasort); 409 | if (ndev <= 0) { 410 | return ""; 411 | } 412 | for (int i = 0; i < ndev; i++) { 413 | char fname[64]; 414 | int fd = -1; 415 | unsigned long keybit[NBITS(KEY_CNT)]; 416 | unsigned long propbit[INPUT_PROP_MAX]; 417 | snprintf(fname, sizeof(fname), "%s/%s", "/dev/input", namelist[i]->d_name); 418 | fd = open(fname, O_RDONLY | O_NONBLOCK); 419 | if (fd < 0) { 420 | continue; 421 | } 422 | memset(keybit, 0, sizeof(keybit)); 423 | memset(propbit, 0, sizeof(propbit)); 424 | ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit); 425 | ioctl(fd, EVIOCGPROP(INPUT_PROP_MAX), propbit); 426 | close(fd); 427 | free(namelist[i]); 428 | if (test_bit(propbit, INPUT_PROP_DIRECT) && (test_bit(keybit, BTN_TOUCH) || test_bit(keybit, BTN_TOOL_FINGER))) { 429 | return string(fname); 430 | } 431 | else if (test_bit(keybit, BTN_TOUCH) || test_bit(keybit, BTN_TOOL_FINGER)) { 432 | return string(fname); 433 | } 434 | } 435 | return ""; 436 | } 437 | 438 | 439 | int readInt(const char* path) { 440 | auto fd = open(path, O_RDONLY); 441 | if (fd < 0) return 0; 442 | char buff[16] = { 0 }; 443 | auto len = read(fd, buff, sizeof(buff)); 444 | close(fd); 445 | 446 | if (len <= 0)return 0; 447 | buff[15] = 0; 448 | return atoi(buff); 449 | } 450 | 451 | size_t readString(const char* path, char* buff, const size_t maxLen) { 452 | auto fd = open(path, O_RDONLY); 453 | if (fd <= 0) { 454 | buff[0] = 0; 455 | return 0; 456 | } 457 | ssize_t len = read(fd, buff, maxLen); 458 | close(fd); 459 | if (len <= 0) { 460 | buff[0] = 0; 461 | return 0; 462 | } 463 | buff[len] = 0; // 终止符 464 | return static_cast(len); 465 | } 466 | 467 | size_t popenRead(const char* cmd, char* buf, const size_t maxLen) { 468 | auto fp = popen(cmd, "r"); 469 | if (!fp) return 0; 470 | auto readLen = fread(buf, 1, maxLen, fp); 471 | pclose(fp); 472 | return readLen; 473 | } 474 | 475 | bool popenShell(const char* cmd) { 476 | auto fp = popen(cmd,"r"); 477 | if (fp == nullptr) { 478 | fprintf(stderr, " 创建popen管道失败", errno, strerror(errno)); 479 | return false; 480 | } 481 | pclose(fp); 482 | return true; 483 | } 484 | 485 | // 最大读取 64 KiB 486 | string readString(const char* path) { 487 | char buff[64 * 1024]; 488 | readString(path, buff, sizeof(buff)); 489 | return string(buff); 490 | } 491 | 492 | bool writeInt(const char* path, const int value) { 493 | auto fd = open(path, O_WRONLY); 494 | if (fd <= 0) return false; 495 | 496 | char tmp[16]; 497 | auto len = snprintf(tmp, sizeof(tmp), "%d", value); 498 | write(fd, tmp, len); 499 | close(fd); 500 | return true; 501 | } 502 | 503 | bool writeString(const char* path, const char* buff, size_t len = 0) noexcept { 504 | if (len == 0)len = strlen(buff); 505 | if (len == 0)return true; 506 | 507 | auto fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666); 508 | if (fd <= 0) return false; 509 | 510 | write(fd, buff, len); 511 | close(fd); 512 | return true; 513 | } 514 | 515 | 516 | void WriteFile(const char* filePath, const char* content) noexcept { 517 | int fd = open(filePath, O_CREAT | O_WRONLY | O_TRUNC, 0666); 518 | 519 | if (fd < 0) { 520 | chmod(filePath, 0666); 521 | fd = open(filePath, O_WRONLY | O_NONBLOCK); 522 | } 523 | 524 | if (fd >= 0) { 525 | write(fd, content, strlen(content)); 526 | close(fd); 527 | } 528 | } 529 | 530 | void sleep_ms(int ms) { 531 | usleep(1000 * ms); 532 | } 533 | 534 | char lastChar(char* ptr) { 535 | if (!ptr)return 0; 536 | while (*ptr) ptr++; 537 | return *(ptr - 1); 538 | } 539 | 540 | bool startWith(const char* prefix, const char* target) { 541 | int idx = 0; 542 | while (prefix[idx]) { 543 | if (prefix[idx] != target[idx]) 544 | return false; 545 | idx++; 546 | } 547 | return true; 548 | } 549 | 550 | bool endWith(const string& suffix, const string& target) { 551 | if (suffix.empty() || suffix.length() > target.length()) return false; 552 | for (int i = suffix.length() - 1, j = target.length() - 1; i >= 0; i--, j--) { 553 | if (suffix[i] != target[j]) return false; 554 | } 555 | return true; 556 | } 557 | 558 | string parentDir(string path) { 559 | if (path.empty())return ""; 560 | if (path.back() == '/') path.pop_back(); 561 | auto idx = path.find_last_of('/'); 562 | return idx == string::npos ? path : path.substr(0, idx); 563 | } 564 | 565 | int localSocketRequest( 566 | const XPOSED_CMD requestCode, 567 | const void* payloadBuff, 568 | const int payloadLen, 569 | int* recvBuff, 570 | const size_t maxRecvLen) { 571 | 572 | // Socket 位于Linux抽象命名空间, 而不是文件路径 573 | // https://blog.csdn.net/howellzhu/article/details/111597734 574 | // https://blog.csdn.net/shanzhizi/article/details/16882087 一种是路径方式 一种是抽象命名空间 575 | constexpr int addrLen = 576 | offsetof(sockaddr_un, sun_path) + 19; // addrLen大小是 "\0FreezeitXposedServer" 的字符长度 577 | constexpr sockaddr_un srv_addr{ AF_UNIX, "\0FrozenXposedServer" }; // 首位为空[0]=0,位于Linux抽象命名空间 578 | 579 | 580 | auto fd = socket(AF_UNIX, SOCK_STREAM, 0); 581 | if (fd < 0) 582 | return -10; 583 | 584 | if (connect(fd, (sockaddr*)&srv_addr, addrLen) < 0) { 585 | close(fd); 586 | return 0; 587 | } 588 | 589 | int header[2] = { static_cast(requestCode), payloadLen }; 590 | send(fd, header, sizeof(header), 0); 591 | 592 | int sendCnt = 0; 593 | while (sendCnt < payloadLen) { 594 | int len = send(fd, static_cast(payloadBuff) + sendCnt, 595 | static_cast(payloadLen - sendCnt), 0); 596 | if (len < 0) { 597 | close(fd); 598 | return -20; 599 | } 600 | sendCnt += len; 601 | } 602 | 603 | int recvLen = recv(fd, recvBuff, maxRecvLen, MSG_WAITALL); 604 | close(fd); 605 | return recvLen; 606 | } 607 | 608 | 609 | void printException( 610 | const char* versionStr, 611 | const int exceptionCnt, 612 | const char* exceptionBuf, 613 | const size_t bufSize) { 614 | auto fp = fopen("/sdcard/Android/Frozen_crash_log.txt", "ab"); 615 | if (!fp) return; 616 | 617 | auto timeStamp = time(nullptr); 618 | auto tm = localtime(&timeStamp); 619 | 620 | fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d] ", 621 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 622 | 623 | if (versionStr)fprintf(fp, "[%s] ", versionStr); 624 | if (exceptionCnt) fprintf(fp, "第%d次异常 ", exceptionCnt); 625 | 626 | fwrite(exceptionBuf, 1, bufSize, fp); 627 | if (exceptionBuf[bufSize - 1] != '\n') 628 | fwrite("\n", 1, 1, fp); 629 | fclose(fp); 630 | } 631 | 632 | 633 | void Init() { 634 | auto now = std::chrono::system_clock::now(); 635 | auto ns = std::chrono::duration_cast(now.time_since_epoch()).count(); 636 | srand(ns); 637 | 638 | // 检查是否还有其他Frozen进程,防止进程多开 639 | char buf[256] = { 0 }; 640 | if (popenRead("pidof Frozen", buf, sizeof(buf)) == 0) { 641 | printException(nullptr, 0, "进程检测失败", 18); 642 | exit(-1); 643 | } 644 | 645 | auto ptr = strchr(buf, ' '); 646 | if (ptr) { // "pidNum1 pidNum2 ..." 如果存在多个pid就退出 647 | *ptr = 0; 648 | char tips[256]; 649 | auto len = snprintf(tips, sizeof(tips), 650 | "Frozen已经在运行(pid: %s), 当前进程(pid:%d)即将退出," 651 | "请勿手动启动Frozen, 也不要在多个框架同时安装Frozen模块", buf, getpid()); 652 | printf("\n!!! \n!!! %s\n!!!\n\n", tips); 653 | printException(nullptr, 0, tips, len); 654 | exit(-2); 655 | } 656 | 657 | 658 | if (FORK_DOUBLE == 0) 659 | return; 660 | 661 | pid_t pid = fork(); 662 | 663 | if (pid < 0) { //创建失败 664 | printException(nullptr, 0, "脱离终端Fork失败", 22); 665 | exit(-1); 666 | } 667 | else if (pid > 0) { //父进程返回的是 子进程的pid 668 | exit(0);//父进程直接退出,然后子进程将由init托管 669 | } 670 | 671 | setsid();// 子进程 建立新会话 672 | umask(0); 673 | chdir("/"); 674 | 675 | // signal(SIGCHLD, SIG_IGN);//屏蔽SIGCHLD信号 通知内核对子进程的结束不关心,由内核回收 676 | int fd_response[2]; 677 | pipe(fd_response); 678 | 679 | pid = fork(); //成为守护进程后再次Fork, 父进程监控, 子进程工作 680 | if (pid < 0) { 681 | printException(nullptr, 0, "创建工作进程Fork失败", 28); 682 | exit(-1); 683 | } 684 | else if (pid > 0) { //父进程 监控子进程输出的异常信息,并写到异常日志 685 | close(fd_response[1]); // 1 关闭写端 686 | 687 | char versionStr[16] ="Unknown"; 688 | char exceptionBuf[4096] = {}; 689 | int exceptionCnt = 0; 690 | int zeroCnt = 0; 691 | 692 | while (true) { 693 | 694 | auto readLen = read(fd_response[0], exceptionBuf, sizeof(exceptionBuf)); 695 | if (readLen <= 0) { 696 | readLen = snprintf(exceptionBuf, 64, "[第%d次无效日志]", ++zeroCnt); 697 | } 698 | else 699 | if (!strncmp(exceptionBuf, "version ", 8)) { 700 | memcpy(versionStr, exceptionBuf + 8, sizeof(versionStr)); 701 | continue; 702 | } 703 | 704 | printException(versionStr, ++exceptionCnt, exceptionBuf, readLen); 705 | 706 | if (zeroCnt >= 3 || exceptionCnt >= 1000) { 707 | if (zeroCnt >= 3) 708 | printException(versionStr, 0, "工作进程已异常退出", 27); 709 | else 710 | printException(versionStr, 0, "工作进程已达最大异常次数, 即将强制关闭", 56); 711 | 712 | if (kill(pid, SIGKILL) < 0) { 713 | char tips[128]; 714 | auto len = snprintf(tips, sizeof(tips), "杀死 [工作进程 pid:%d] 失败", pid); 715 | printException(versionStr, 0, tips, len); 716 | } 717 | 718 | 719 | int status = 0; 720 | if (waitpid(pid, &status, __WALL) != pid) { 721 | char tips[128]; 722 | auto len = snprintf(tips, sizeof(tips), "waitpid 异常: [%d] HEX[%s]", status, 723 | bin2Hex(&status, 4).c_str()); 724 | exit(-1); 725 | printException(versionStr, 0, tips, len); 726 | } 727 | } 728 | } 729 | } 730 | 731 | //工作进程 732 | close(fd_response[0]); // 0 关闭读端 733 | 734 | // 标准输出和错误均指向父进程管道 735 | // dup2(fd_response[1], STDOUT_FILENO); // 把 system() shell 标准输出到异常日志 736 | dup2(fd_response[1], STDERR_FILENO); 737 | 738 | auto nullFd = open("/dev/null", O_RDWR); 739 | if (nullFd > 0) { 740 | dup2(nullFd, STDIN_FILENO); 741 | dup2(nullFd, STDOUT_FILENO); 742 | } 743 | else { 744 | close(STDIN_FILENO); 745 | close(STDOUT_FILENO); 746 | } 747 | } 748 | } 749 | 750 | 751 | namespace MAGISK { 752 | int get_version_code() { 753 | char buff[32] = { 0 }; 754 | Utils::popenRead("/system/bin/magisk -V", buff, sizeof(buff)); 755 | return isdigit(buff[0]) ? atoi(buff) : -1; 756 | } 757 | } 758 | namespace APatch { 759 | int get_version_code() { 760 | int version = -1; 761 | version = Utils::readInt("/data/adb/ap/version"); 762 | return version; 763 | } 764 | } 765 | // https://github.com/tiann/KernelSU/blob/main/manager/app/src/main/cpp/ksu.cc 766 | namespace KSU { 767 | const int CMD_GRANT_ROOT = 0; 768 | const int CMD_BECOME_MANAGER = 1; 769 | const int CMD_GET_VERSION = 2; 770 | const int CMD_ALLOW_SU = 3; 771 | const int CMD_DENY_SU = 4; 772 | const int CMD_GET_ALLOW_LIST = 5; 773 | const int CMD_GET_DENY_LIST = 6; 774 | const int CMD_CHECK_SAFEMODE = 9; 775 | 776 | bool ksuctl(int cmd, void* arg1, void* arg2) { 777 | const uint32_t KERNEL_SU_OPTION{ 0xDEADBEEF }; 778 | uint32_t result = 0; 779 | prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); 780 | return result == KERNEL_SU_OPTION; 781 | } 782 | 783 | int get_version_code() { 784 | int version = -1; 785 | ksuctl(CMD_GET_VERSION, &version, nullptr); 786 | return version; 787 | } 788 | 789 | bool allow_su(uint64_t uid, bool allow) { 790 | return ksuctl(allow ? CMD_ALLOW_SU : CMD_DENY_SU, (void*)uid, nullptr); 791 | } 792 | 793 | bool get_allow_list(int* uids, int* size) { 794 | return ksuctl(CMD_GET_ALLOW_LIST, uids, size); 795 | } 796 | 797 | bool get_deny_list(int* uids, int* size) { 798 | return ksuctl(CMD_GET_DENY_LIST, uids, size); 799 | } 800 | 801 | bool is_safe_mode() { 802 | return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); 803 | } 804 | }; 805 | -------------------------------------------------------------------------------- /Frozen-Main/Frozen/src/vpopen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * popen_noshell: A faster implementation of popen() and system() for Linux. 5 | * Copyright (c) 2009 Ivan Zahariev (famzah) 6 | * Version: 1.0 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as published by 10 | * the Free Software Foundation; under version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // _GNU_SOURCE must be defined as early as possible 22 | #ifndef _GNU_SOURCE 23 | #define _GNU_SOURCE 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // because of C++, we can't call err() or errx() within the child, because they call exit(), 33 | // and _exit() is what must be called; so we wrap 34 | #define _ERR(EVAL, FMT, ...) {warn(FMT, ##__VA_ARGS__); _exit(EVAL);} 35 | 36 | struct vpopenStruct { 37 | FILE *fp = nullptr; 38 | pid_t pid = 0; 39 | } pclose_arg; 40 | 41 | extern char **environ; 42 | 43 | namespace VPOPEN { 44 | 45 | int popen_noshell_reopen_fd_to_dev_null(int fd, posix_spawn_file_actions_t *file_actions) { 46 | if (posix_spawn_file_actions_addclose(file_actions, fd) != 0) return -1; 47 | if (posix_spawn_file_actions_addopen(file_actions, fd, "/dev/null", O_RDWR, 0600) < 0) 48 | return -1; 49 | return 0; 50 | } 51 | 52 | int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd, 53 | posix_spawn_file_actions_t *file_actions) { 54 | int dupped_pipefd = (closed_pipefd == 0 ? 1 : 0); // get the FD of the other end of the pipe 55 | 56 | if (posix_spawn_file_actions_addclose(file_actions, pipefd[closed_pipefd]) != 0) return -1; 57 | if (posix_spawn_file_actions_addclose(file_actions, target_fd) != 0) return -1; 58 | if (posix_spawn_file_actions_adddup2(file_actions, pipefd[dupped_pipefd], target_fd) < 0) 59 | return -1; 60 | if (posix_spawn_file_actions_addclose(file_actions, pipefd[dupped_pipefd]) != 0) return -1; 61 | return 0; 62 | } 63 | 64 | // returns the new PID if called in POPEN_NOSHELL_MODE_POSIX_SPAWN 65 | // otherwise returns 0 66 | pid_t _popen_noshell_child_process(int pipefd_0, int pipefd_1, const char *file, 67 | const char *const *argv) { 68 | posix_spawn_file_actions_t file_actions_obj; 69 | if (posix_spawn_file_actions_init(&file_actions_obj) != 0) { 70 | _ERR(255, "posix_spawn_file_actions_init()"); 71 | } 72 | 73 | int closed_child_fd = STDIN_FILENO; /* re-open STDIN to /dev/null */ 74 | int closed_pipe_fd = 0; /* close read end of pipe */ 75 | int dupped_child_fd = STDOUT_FILENO; /* dup the other pipe end to STDOUT */ 76 | int pipefd[2] = {pipefd_0, pipefd_1}; 77 | 78 | if (popen_noshell_reopen_fd_to_dev_null(closed_child_fd, &file_actions_obj) != 0) { 79 | _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", closed_child_fd); 80 | } 81 | if (_popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd, 82 | &file_actions_obj) != 0) { 83 | _ERR(255, "_popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); 84 | } 85 | 86 | pid_t child_pid; 87 | if (posix_spawn(&child_pid, file, &file_actions_obj, nullptr, (char *const *) argv, 88 | environ) < 0) { 89 | warn("posix_spawn(\"%s\") inside the child", file); 90 | if (posix_spawn_file_actions_destroy(&file_actions_obj) != 0) { 91 | warn("posix_spawn_file_actions_destroy()"); 92 | } 93 | return 0; 94 | } 95 | if (posix_spawn_file_actions_destroy(&file_actions_obj) != 0) { 96 | warn("posix_spawn_file_actions_destroy()"); 97 | } 98 | return child_pid; 99 | } 100 | 101 | FILE *popen_noshell(const char *file, const char *const *argv) { 102 | int pipefd[2]; // 0 -> READ, 1 -> WRITE ends 103 | if (pipe2(pipefd, O_CLOEXEC) != 0) return nullptr; 104 | 105 | pid_t pid = _popen_noshell_child_process(pipefd[0], pipefd[1], file, argv); 106 | if (pid == 0) return nullptr; 107 | if (close(pipefd[1/*write*/]) != 0) return nullptr; 108 | 109 | auto fp = fdopen(pipefd[0/*read*/], "r"); 110 | if (fp) { 111 | pclose_arg.fp = fp; 112 | pclose_arg.pid = pid; 113 | } 114 | return fp; 115 | } 116 | 117 | int pclose_noshell() { 118 | int status = 0; 119 | if (fclose(pclose_arg.fp) != 0) return -1; 120 | if (waitpid(pclose_arg.pid, &status, __WALL) != pclose_arg.pid) return -2; 121 | return status; 122 | } 123 | 124 | void vpopen(const char *absPath, const char *argv[], char *buf, const size_t len) { 125 | auto fp = popen_noshell(absPath, (const char *const *) argv); 126 | if (!fp) { 127 | buf[0] = 0; 128 | fprintf(stderr, "%s() open 失败 [%d]:[%s]", __FUNCTION__, errno, strerror(errno)); 129 | return; 130 | } 131 | 132 | auto resLen = fread(buf, 1, len, fp); 133 | if (resLen <= 0) { 134 | buf[0] = 0; 135 | } else if (buf[resLen - 1] == '\n') { 136 | buf[resLen - 1] = 0; 137 | resLen--; 138 | } else { 139 | buf[resLen] = 0; 140 | } 141 | 142 | // https://man7.org/linux/man-pages/man2/waitpid.2.html 143 | // wait 的 status参数 只用了低 16位 144 | // 高8位 记录进程调用exit退出的状态(正常退出) 145 | // 低8位 记录进程接受到的信号 (非正常退出) 146 | auto status = pclose_noshell(); 147 | if (status < 0 || (status & 0xff)) 148 | fprintf(stderr, "%s() close 异常status[%d] [%d]:[%s]", __FUNCTION__, status, errno, 149 | strerror(errno)); 150 | } 151 | }; 152 | -------------------------------------------------------------------------------- /Frozen-Main/README.md: -------------------------------------------------------------------------------- 1 | # Frozen 2 | 3 | **[面具模块]** 实现类似IOS的完美墓碑机制 4 | 5 | **[MagiskModule]** Implement a perfect tombstone mechanism similar to IOS. 6 | 7 | -------------------------------------------------------------------------------- /Frozen-Main/doze.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "vpopen.hpp" 5 | #include "managedApp.hpp" 6 | #include "freezeit.hpp" 7 | #include "systemTools.hpp" 8 | 9 | class Doze { 10 | private: 11 | Freezeit& freezeit; 12 | ManagedApp& managedApp; 13 | SystemTools& systemTools; 14 | Settings& settings; 15 | 16 | time_t enterDozeTimeStamp = 0; 17 | uint32_t enterDozeCycleStamp = 0; 18 | time_t lastInteractiveTime = time(nullptr); // 上次检查为 亮屏或充电 的时间戳 19 | 20 | void updateDozeWhitelist() { 21 | START_TIME_COUNT; 22 | 23 | const char* cmdList[] = { "/system/bin/dumpsys", "dumpsys", "deviceidle", "whitelist", 24 | nullptr }; 25 | char buf[1024 * 32]; 26 | VPOPEN::vpopen(cmdList[0], cmdList + 1, buf, sizeof(buf)); 27 | 28 | stringstream ss; 29 | ss << buf; 30 | 31 | string tmp, tmpLabel, line; 32 | set existSet; 33 | 34 | // https://cs.android.com/android/platform/superproject/+/android-12.1.0_r27:frameworks/base/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java;l=485 35 | // "system-excidle,xxx,uid" 该名单在Doze模式会失效 36 | // "system,xxx,uid" 37 | // "user,xxx,uid" 38 | while (getline(ss, line)) { 39 | if (!line.starts_with("system,") && !line.starts_with("user")) continue; 40 | if (line.length() < 10)continue; 41 | if (line[line.length() - 6] != ',')continue; 42 | 43 | int uid = atoi(line.c_str() + line.length() - 5); 44 | if (!managedApp.contains(uid))continue; 45 | 46 | auto& appInfo = managedApp[uid]; 47 | if (appInfo.isBlacklist()) { 48 | tmp += "dumpsys deviceidle whitelist -" + appInfo.package + ";"; 49 | tmpLabel += appInfo.label + " "; 50 | } 51 | else 52 | existSet.insert(uid); 53 | } 54 | 55 | if (tmp.length()) { 56 | freezeit.logFmt("移除电池优化白名单: %s", tmpLabel.c_str()); 57 | system(tmp.c_str()); 58 | } 59 | 60 | END_TIME_COUNT; 61 | } 62 | 63 | // 0获取失败 1息屏 2亮屏 64 | int getScreenByLocalSocket() { 65 | START_TIME_COUNT; 66 | 67 | int buff[64]; 68 | int recvLen = Utils::localSocketRequest(XPOSED_CMD::GET_SCREEN, nullptr, 0, buff, 69 | sizeof(buff)); 70 | 71 | if (recvLen == 0) { 72 | freezeit.logFmt("%s() 工作异常, 请确认LSPosed中冻它勾选系统框架, 然后重启", __FUNCTION__); 73 | END_TIME_COUNT; 74 | return 0; 75 | } 76 | else if (recvLen != 4) { 77 | freezeit.logFmt("%s() 屏幕数据异常 recvLen[%d]", __FUNCTION__, recvLen); 78 | if (recvLen > 0 && recvLen < 64 * 4) 79 | freezeit.logFmt("DumpHex: [%s]", Utils::bin2Hex(buff, recvLen).c_str()); 80 | END_TIME_COUNT; 81 | return 0; 82 | } 83 | 84 | const string_view str[3] = { "Xposed 获取屏幕状态失败", "Xposed 息屏中", "Xposed 亮屏中" }; 85 | freezeit.debug(str[buff[0] < 3 ? buff[0] : 1]); 86 | 87 | 88 | END_TIME_COUNT; 89 | return buff[0]; 90 | } 91 | 92 | bool isInteractive() { 93 | /* 94 | [debug.tracing.screen_brightness]: [0.05468459] 0-1 / 0-16384 95 | [debug.tracing.screen_state]: [2] 亮屏[2] 息屏[1] 96 | https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/Display.java;l=387 97 | enum DisplayStateEnum 98 | public static final int DISPLAY_STATE_UNKNOWN = 0; 99 | public static final int DISPLAY_STATE_OFF = 1; 100 | public static final int DISPLAY_STATE_ON = 2; 101 | public static final int DISPLAY_STATE_DOZE = 3; //亮屏但处于Doze的非交互状态状态 102 | public static final int DISPLAY_STATE_DOZE_SUSPEND = 4; // 同上,但CPU不控制显示,由协处理器或其他控制 103 | public static final int DISPLAY_STATE_VR = 5; 104 | public static final int DISPLAY_STATE_ON_SUSPEND = 6; //非Doze, 类似4 105 | */ 106 | do { 107 | //char res[128]; // MAX LEN: 96 108 | //int mScreenState; 109 | //if (__system_property_get("debug.tracing.screen_state", res) < 1) 110 | // mScreenState = getScreenByLocalSocket(); 111 | //else mScreenState = res[0] - '0'; 112 | 113 | int mScreenState = systemTools.getScreenProperty(); 114 | if (mScreenState < 0) 115 | mScreenState = getScreenByLocalSocket(); 116 | 117 | if (mScreenState != 1 && mScreenState != 2) 118 | freezeit.debugFmt("屏幕其他状态 mScreenState[%d]", mScreenState); 119 | 120 | // 设备活跃状态 121 | if (mScreenState == 2 || mScreenState == 5 || mScreenState == 6) { 122 | freezeit.debugFmt("亮屏中 mScreenState[%d]", mScreenState); 123 | break; 124 | } 125 | 126 | // 认定设备活跃状态 127 | if (mScreenState <= 0) { 128 | freezeit.debugFmt("屏幕状态获取失败 mScreenState[%d] 若开机至今未曾息屏,则无法获取屏幕状态", mScreenState); 129 | break; 130 | } 131 | 132 | // 以下则是息屏: 1 3 4 133 | // 134 | // 认定设备活跃状态 135 | if (systemTools.isAudioPlaying) { 136 | freezeit.debug("息屏, 播放中"); 137 | break; 138 | } 139 | 140 | // "Unknown", "Charging", "Discharging", "Not charging", "Full" 141 | // https://cs.android.com/android/kernel/superproject/+/common-android-mainline-kleaf:common/drivers/power/supply/power_supply_sysfs.c;l=75 142 | char res[64]; 143 | Utils::readString("/sys/class/power_supply/battery/status", res, sizeof(res)); 144 | if (!strncmp(res, "Charging", 4) || !strncmp(res, "Full", 4)) { 145 | freezeit.debug("息屏, 充电中"); 146 | break; 147 | } 148 | 149 | if (!strncmp(res, "Discharging", 4) || !strncmp(res, "Not charging", 4)) { 150 | freezeit.debug("息屏, 未充电"); 151 | return false; 152 | } 153 | 154 | freezeit.debugFmt("息屏, 电池状态未知 [%s]", res); 155 | 156 | } while (false); 157 | 158 | lastInteractiveTime = time(nullptr); 159 | return true; 160 | } 161 | 162 | public: 163 | Doze& operator=(Doze&&) = delete; 164 | 165 | bool isScreenOffStandby = false; 166 | 167 | Doze(Freezeit& freezeit, Settings& settings, ManagedApp& managedApp, SystemTools& systemTools) : 168 | freezeit(freezeit), managedApp(managedApp), systemTools(systemTools), settings(settings) { 169 | updateUidTime(); 170 | } 171 | 172 | bool checkIfNeedToExit() { 173 | START_TIME_COUNT; 174 | if (!isInteractive()) { 175 | freezeit.debug("息屏中, 发现有活动"); 176 | 177 | END_TIME_COUNT; 178 | return false; 179 | } 180 | 181 | isScreenOffStandby = false; 182 | 183 | if (settings.enablePowersaveMode) { 184 | freezeit.log("🤪 已关闭Standby模式"); 185 | system( 186 | "settings put global app_auto_restriction_enabled false" 187 | "settings put global forced_app_standby_enabled 0" 188 | "settings put global app_standby_enabled 0" 189 | "settings put global forced_app_standby_for_small_battery_enabled 0" 190 | ); 191 | } 192 | if (settings.enableDoze) { 193 | system("dumpsys deviceidle unforce"); 194 | 195 | int deltaTime = time(nullptr) - enterDozeTimeStamp; 196 | const int activeRate = deltaTime <= 0 ? 0 : 197 | (100 * (systemTools.cycleCnt - enterDozeCycleStamp) / deltaTime); //CPU 活跃率 198 | 199 | if (deltaTime < 300) { 200 | if (deltaTime >= 60) 201 | freezeit.logFmt("退出Doze 小睡了 %d分%d秒", deltaTime / 60, deltaTime % 60); 202 | else 203 | freezeit.logFmt("退出Doze 小睡了 %d秒", deltaTime % 60); 204 | } 205 | else { 206 | stackString<1024 * 16> tmp; 207 | 208 | if (activeRate <= 85) 209 | tmp.append("🤪 退出深度Doze 时长 "); 210 | else 211 | tmp.append("🤪 这段时间未能进入深度Doze, 请检查应用的唤醒锁使用情况 时长 "); 212 | 213 | if (deltaTime >= 3600) { 214 | tmp.appendFmt("%d时", deltaTime / 3600); 215 | deltaTime %= 3600; 216 | } 217 | if (deltaTime >= 60) { 218 | tmp.appendFmt("%d分", deltaTime / 60); 219 | deltaTime %= 60; 220 | } 221 | if (deltaTime) tmp.appendFmt("%d秒", deltaTime); 222 | tmp.appendFmt(" 唤醒率 %d%%", activeRate); 223 | freezeit.log(string_view(tmp.c_str(), tmp.length)); 224 | 225 | struct st { 226 | int uid; 227 | int delta; 228 | }; 229 | vector uidTimeSort; 230 | uidTimeSort.reserve(32); 231 | for (const auto& [uid, timeList] : updateUidTime()) { 232 | int delta = (timeList.total - timeList.lastTotal); // 毫秒 233 | if (delta <= 100)continue; // 过滤 100毫秒 234 | uidTimeSort.emplace_back(st{ uid, delta }); 235 | } 236 | 237 | std::sort(uidTimeSort.begin(), uidTimeSort.end(), 238 | [](const st& a, const st& b) { return a.delta > b.delta; }); 239 | 240 | tmp.clear(); 241 | for (auto& [uid, delta] : uidTimeSort) { 242 | tmp.append('['); 243 | const int minutesMilliSec = 60 * 1000; 244 | if (delta >= minutesMilliSec) { 245 | tmp.appendFmt("%d分", delta / minutesMilliSec); 246 | delta %= minutesMilliSec; 247 | } 248 | tmp.appendFmt("%d.%03d秒] ", delta / 1000, delta % 1000); 249 | tmp.appendFmt("%s\n", managedApp.getLabel(uid).c_str()); 250 | } 251 | 252 | if (tmp.length) 253 | freezeit.logFmt("Doze期间应用的CPU活跃时间:\n\n%s", *tmp); 254 | } 255 | } 256 | END_TIME_COUNT; 257 | return true; 258 | } 259 | 260 | bool checkIfNeedToEnter() { 261 | constexpr int TIMEOUT = 3 * 60; 262 | static int secCnt = 30; 263 | 264 | if (isScreenOffStandby || ++secCnt < TIMEOUT) 265 | return false; 266 | 267 | secCnt = 0; 268 | 269 | if (isInteractive()) 270 | return false; 271 | 272 | const time_t nowTimeStamp = time(nullptr); 273 | if ((nowTimeStamp - lastInteractiveTime) < (TIMEOUT + 60L)) 274 | return false; 275 | 276 | freezeit.debug("息屏状态已超时,正在确认息屏状态"); 277 | 278 | // 如果系统之前已经自行进入轻度Doze, 退出Doze的瞬间(此时可能还没亮屏)导致现在才执行时间判断 279 | // 此时进入Doze不合理,需等等,再确认一遍 280 | usleep(1000 * 200); // 休眠 200ms 281 | if (isInteractive()) { 282 | freezeit.debug("确认新状态:已亮屏或充电中, 退出息屏"); 283 | return false; 284 | } 285 | 286 | isScreenOffStandby = true; 287 | if (settings.enablePowersaveMode){ 288 | freezeit.log("🥱 已开启Standby模式"); 289 | system( 290 | "settings put global app_auto_restriction_enabled true" 291 | "settings put global forced_app_standby_enabled 1" 292 | "settings put global app_standby_enabled 1" 293 | "settings put global forced_app_standby_for_small_battery_enabled 1" 294 | ); 295 | } 296 | if (settings.enableDoze) { 297 | freezeit.debug("开始准备深度Doze"); 298 | updateDozeWhitelist(); 299 | updateUidTime(); 300 | 301 | freezeit.log("😴 进入深度Doze"); 302 | enterDozeTimeStamp = nowTimeStamp; 303 | enterDozeCycleStamp = systemTools.cycleCnt; 304 | 305 | system( 306 | "dumpsys deviceidle enable all;" 307 | "dumpsys deviceidle force-idle deep" 308 | ); 309 | } 310 | return true; 311 | } 312 | 313 | 314 | map uidTime; // ms 微秒 315 | map& updateUidTime() { 316 | 317 | START_TIME_COUNT; 318 | 319 | stringstream ss; 320 | ss << ifstream("/proc/uid_cputime/show_uid_stat").rdbuf(); 321 | 322 | string line; 323 | while (getline(ss, line)) { 324 | int uid; 325 | long long userTime, systemTime; // us 微秒 326 | sscanf(line.c_str(), "%d: %lld %lld", &uid, &userTime, &systemTime); 327 | if (managedApp.isBlackList(uid) && (userTime >= 1000 || systemTime >= 1000)) { 328 | auto& appTime = uidTime[uid]; 329 | appTime.lastTotal = appTime.total; 330 | appTime.total = static_cast((systemTime + userTime) / 1000); // ms 取毫秒 331 | } 332 | } 333 | 334 | END_TIME_COUNT; 335 | return uidTime; 336 | } 337 | }; 338 | -------------------------------------------------------------------------------- /Frozen-Main/freezeit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "vpopen.hpp" 5 | 6 | class Freezeit { 7 | private: 8 | const char* LOG_PATH = "/sdcard/Android/Frozen.log"; 9 | 10 | constexpr static int LINE_SIZE = 1024 * 32; // 32 KiB 11 | constexpr static int BUFF_SIZE = 1024 * 128; // 128 KiB 12 | 13 | mutex logPrintMutex; 14 | bool toFileFlag = false; 15 | size_t position = 0; 16 | char lineCache[LINE_SIZE] = "[00:00:00] "; 17 | char logCache[BUFF_SIZE]; 18 | 19 | string propPath; 20 | string changelog{ "无" }; 21 | 22 | uint8_t* deBugFlagPtr = nullptr; 23 | 24 | // "Jul 28 2022" --> "2022-07-28" 25 | const char compilerDate[12] = { 26 | __DATE__[7], 27 | __DATE__[8], 28 | __DATE__[9], 29 | __DATE__[10],// YYYY year 30 | '-', 31 | 32 | // First month letter, Oct Nov Dec = '1' otherwise '0' 33 | (__DATE__[0] == 'O' || __DATE__[0] == 'N' || __DATE__[0] == 'D') ? '1' : '0', 34 | 35 | // Second month letter Jan, Jun or Jul 36 | (__DATE__[0] == 'J') ? ((__DATE__[1] == 'a') ? '1' 37 | : ((__DATE__[2] == 'n') ? '6' : '7')) 38 | : (__DATE__[0] == 'F') ? '2'// Feb 39 | : (__DATE__[0] == 'M') ? (__DATE__[2] == 'r') ? '3' : '5'// Mar or May 40 | : (__DATE__[0] == 'A') ? (__DATE__[1] == 'p') ? '4' : '8'// Apr or Aug 41 | : (__DATE__[0] == 'S') ? '9'// Sep 42 | : (__DATE__[0] == 'O') ? '0'// Oct 43 | : (__DATE__[0] == 'N') ? '1'// Nov 44 | : (__DATE__[0] == 'D') ? '2'// Dec 45 | : 'X', 46 | 47 | '-', 48 | __DATE__[4] == ' ' ? '0' : __DATE__[4],// First day letter, replace space with digit 49 | __DATE__[5],// Second day letter 50 | '\0', 51 | }; 52 | 53 | void toMem(const char* logStr, const int len) { 54 | if ((position + len) >= BUFF_SIZE) 55 | position = 0; 56 | 57 | memcpy(logCache + position, logStr, len); 58 | position += len; 59 | } 60 | 61 | void toFile(const char* logStr, const int len) { 62 | auto fp = fopen(LOG_PATH, "ab"); 63 | if (!fp) { 64 | fprintf(stderr, "日志输出(追加模式)失败 [%d][%s]", errno, strerror(errno)); 65 | return; 66 | } 67 | 68 | auto fileSize = ftell(fp); 69 | if ((fileSize + len) >= BUFF_SIZE) { 70 | fclose(fp); 71 | usleep(1000); 72 | fp = fopen(LOG_PATH, "wb"); 73 | if (!fp) { 74 | fprintf(stderr, "日志输出(超额清理模式)失败 [%d][%s]", errno, strerror(errno)); 75 | return; 76 | } 77 | } 78 | 79 | fwrite(logStr, 1, len, fp); 80 | fclose(fp); 81 | } 82 | 83 | 84 | public: 85 | 86 | bool isSamsung{ false }; 87 | bool isOppoVivo{ false }; 88 | 89 | 90 | string modulePath; 91 | string moduleEnv{ "Unknown" }; 92 | 93 | map prop{ 94 | {"id", "Unknown"}, 95 | {"name", "Unknown"}, 96 | {"version", "Unknown"}, 97 | {"versionCode", "0"}, 98 | {"author", "Unknown"}, 99 | {"description", "Unknown"}, 100 | }; 101 | 102 | Freezeit& operator=(Freezeit&&) = delete; 103 | 104 | Freezeit(int argc, const string& fullPath) { 105 | 106 | modulePath = Utils::parentDir(fullPath); 107 | 108 | int versionCode = -1; 109 | if (!access("/system/bin/magisk", F_OK)) { 110 | moduleEnv = "Magisk"; 111 | versionCode = MAGISK::get_version_code(); 112 | if (versionCode <= 0) { 113 | sleep(2); 114 | versionCode = MAGISK::get_version_code(); 115 | } 116 | } 117 | else if (!access("/data/adb/ksud", F_OK)) { 118 | moduleEnv = "KernelSU"; 119 | versionCode = KSU::get_version_code(); 120 | if (versionCode <= 0) { 121 | sleep(2); 122 | versionCode = KSU::get_version_code(); 123 | } 124 | } else if (!access("/data/adb/apd", F_OK)) { 125 | moduleEnv = "APath"; 126 | versionCode = Apath::get_version_code(); 127 | if (versionCode <= 0) { 128 | sleep(2); 129 | versionCode = Apath::get_version_code(); 130 | } 131 | } 132 | if (versionCode > 0) 133 | moduleEnv += " (" + to_string(versionCode) + ")"; 134 | 135 | auto fp = fopen((modulePath + "/boot.log").c_str(), "rb"); 136 | if (fp) { 137 | auto len = fread(logCache, 1, BUFF_SIZE, fp); 138 | if (len > 0) 139 | position = len; 140 | fclose(fp); 141 | } 142 | 143 | toFileFlag = argc > 1; 144 | if (toFileFlag) { 145 | if (position)toFile(logCache, position); 146 | const char tips[] = "日志已通过文件输出: /sdcard/Android/Frozen.log"; 147 | toMem(tips, sizeof(tips) - 1); 148 | } 149 | 150 | propPath = modulePath + "/module.prop"; 151 | fp = fopen(propPath.c_str(), "r"); 152 | if (!fp) { 153 | fprintf(stderr, "找不到模块属性文件 [%s]", propPath.c_str()); 154 | exit(-1); 155 | } 156 | 157 | char tmp[1024 * 4]; 158 | while (!feof(fp)) { 159 | fgets(tmp, sizeof(tmp), fp); 160 | if (!isalpha(tmp[0])) continue; 161 | tmp[sizeof(tmp) - 1] = 0; 162 | auto ptr = strchr(tmp, '='); 163 | if (!ptr)continue; 164 | 165 | *ptr = 0; 166 | for (size_t i = (ptr - tmp) + 1; i < sizeof(tmp); i++) { 167 | if (tmp[i] == '\n' || tmp[i] == '\r') { 168 | tmp[i] = 0; 169 | break; 170 | } 171 | } 172 | prop[string(tmp)] = string(ptr + 1); 173 | } 174 | fclose(fp); 175 | 176 | 177 | changelog = Utils::readString((modulePath + "/changelog.txt").c_str()); 178 | 179 | 180 | logFmt("模块版本 %s(%s)", prop["version"].c_str(), prop["versionCode"].c_str()); 181 | logFmt("编译时间 %s %s UTC+8", compilerDate, __TIME__); 182 | 183 | fprintf(stderr, "version %s", prop["version"].c_str()); // 发送当前版本信息给监控进程 184 | 185 | char res[256]; 186 | if (__system_property_get("ro.product.brand", res) > 0) { 187 | logFmt("设备厂商 %s", res); 188 | 189 | //for (int i = 0; i < 8; i++)res[i] |= 32; 190 | *((uint64_t*)res) |= 0x20202020'20202020ULL; // 转为小写 191 | if (!strncmp(res, "samsung", 7)) 192 | isSamsung = true; 193 | else if (!strncmp(res, "oppo", 4) || !strncmp(res, "vivo", 4) || 194 | !strncmp(res, "realme", 6) || !strncmp(res, "iqoo", 4)) 195 | isOppoVivo = true; 196 | } 197 | } 198 | 199 | void setDebugPtr(uint8_t* ptr) { 200 | deBugFlagPtr = ptr; 201 | } 202 | bool isDebugOn() { 203 | if (deBugFlagPtr == nullptr) 204 | return false; 205 | 206 | return *deBugFlagPtr; 207 | } 208 | 209 | char* getChangelogPtr() { return (char*)changelog.c_str(); } 210 | 211 | size_t getChangelogLen() { return changelog.length(); } 212 | 213 | bool saveProp() { 214 | auto fp = fopen(propPath.c_str(), "wb"); 215 | if (!fp) 216 | return false; 217 | 218 | char tmp[1024]; 219 | size_t len = snprintf(tmp, sizeof(tmp), 220 | "id=%s\nname=%s\nversion=%s\nversionCode=%s\nauthor=%s\ndescription=%s\nupdateJson=%s\n", 221 | prop["id"].c_str(), prop["name"].c_str(), prop["version"].c_str(), 222 | prop["versionCode"].c_str(), 223 | prop["author"].c_str(), prop["description"].c_str(), 224 | prop["updateJson"].c_str()); 225 | 226 | size_t writeLen = fwrite(tmp, 1, len, fp); 227 | fclose(fp); 228 | 229 | return (writeLen == len); 230 | } 231 | 232 | 233 | /* int formatTimePrefix() { 234 | time_t timeStamp = time(nullptr) + 8 * 3600L; 235 | int hour = (timeStamp / 3600) % 24; 236 | int min = (timeStamp % 3600) / 60; 237 | int sec = timeStamp % 60; 238 | 239 | //lineCache[LINE_SIZE] = "[00:00:00] "; 240 | lineCache[1] = (hour / 10) + '0'; 241 | lineCache[2] = (hour % 10) + '0'; 242 | lineCache[4] = (min / 10) + '0'; 243 | lineCache[5] = (min % 10) + '0'; 244 | lineCache[7] = (sec / 10) + '0'; 245 | lineCache[8] = (sec % 10) + '0'; 246 | 247 | return 11; 248 | } 249 | */ 250 | int formatTimePrefix() { 251 | time_t timeStamp = time(nullptr) + 8 * 3600L; 252 | struct tm* timeInfo = localtime(&timeStamp); 253 | 254 | int year = timeInfo->tm_year + 1900; 255 | int month = timeInfo->tm_mon + 1; 256 | int day = timeInfo->tm_mday; 257 | int hour = (timeStamp / 3600) % 24; 258 | int min = (timeStamp % 3600) / 60; 259 | int sec = timeStamp % 60; 260 | 261 | lineCache[0] = (year / 1000) % 10 + '0'; 262 | lineCache[1] = (year / 100) % 10 + '0'; 263 | lineCache[2] = (year / 10) % 10 + '0'; 264 | lineCache[3] = year % 10 + '0'; 265 | lineCache[4] = '.'; 266 | lineCache[5] = (month / 10) + '0'; 267 | lineCache[6] = month % 10 + '0'; 268 | lineCache[7] = '.'; 269 | lineCache[8] = (day / 10) + '0'; 270 | lineCache[9] = day % 10 + '0'; 271 | lineCache[10] = '.'; 272 | lineCache[11] = (hour / 10) + '0'; 273 | lineCache[12] = hour % 10 + '0'; 274 | lineCache[13] = ':'; 275 | lineCache[14] = (min / 10) + '0'; 276 | lineCache[15] = min % 10 + '0'; 277 | lineCache[16] = ':'; 278 | lineCache[17] = (sec / 10) + '0'; 279 | lineCache[18] = sec % 10 + '0'; 280 | lineCache[19] = ' '; 281 | 282 | return 20; 283 | } 284 | 285 | int formatTimeDebug() { 286 | time_t timeStamp = time(nullptr) + 8 * 3600L; 287 | int hour = (timeStamp / 3600) % 24; 288 | int min = (timeStamp % 3600) / 60; 289 | int sec = timeStamp % 60; 290 | 291 | //lineCache[LINE_SIZE] = "[00:00:00] DEBUG "; 292 | lineCache[1] = (hour / 10) + '0'; 293 | lineCache[2] = (hour % 10) + '0'; 294 | lineCache[4] = (min / 10) + '0'; 295 | lineCache[5] = (min % 10) + '0'; 296 | lineCache[7] = (sec / 10) + '0'; 297 | lineCache[8] = (sec % 10) + '0'; 298 | 299 | memcpy(lineCache + 11, "DEBUG ", 6); 300 | 301 | return 17; 302 | } 303 | 304 | void log(const string_view& str) { 305 | lock_guard lock(logPrintMutex); 306 | 307 | const int prefixLen = formatTimePrefix(); 308 | 309 | int len = str.length() + prefixLen; 310 | memcpy(lineCache + prefixLen, str.data(), str.length()); 311 | 312 | lineCache[len++] = '\n'; 313 | 314 | if (toFileFlag) 315 | toFile(lineCache, len); 316 | else 317 | toMem(lineCache, len); 318 | } 319 | 320 | 321 | template 322 | void logFmt(const char* fmt, Args&&... args) { 323 | lock_guard lock(logPrintMutex); 324 | 325 | const int prefixLen = formatTimePrefix(); 326 | 327 | int len = snprintf(lineCache + prefixLen, (size_t)(LINE_SIZE - prefixLen), fmt, std::forward(args)...) + prefixLen; 328 | 329 | if (len <= 11 || LINE_SIZE <= (len + 1)) { 330 | lineCache[11] = 0; 331 | fprintf(stderr, "日志异常: len[%d] lineCache[%s]", len, lineCache); 332 | return; 333 | } 334 | 335 | lineCache[len++] = '\n'; 336 | 337 | if (toFileFlag) 338 | toFile(lineCache, len); 339 | else 340 | toMem(lineCache, len); 341 | } 342 | 343 | void debug(const string_view& str) { 344 | if (!isDebugOn())return; 345 | 346 | lock_guard lock(logPrintMutex); 347 | 348 | const int prefixLen = formatTimeDebug(); 349 | 350 | int len = str.length() + prefixLen; 351 | memcpy(lineCache + prefixLen, str.data(), str.length()); 352 | 353 | lineCache[len++] = '\n'; 354 | 355 | if (toFileFlag) 356 | toFile(lineCache, len); 357 | else 358 | toMem(lineCache, len); 359 | } 360 | 361 | template 362 | void debugFmt(const char* fmt, Args&&... args) { 363 | if (!isDebugOn())return; 364 | 365 | lock_guard lock(logPrintMutex); 366 | 367 | const int prefixLen = formatTimeDebug(); 368 | 369 | int len = snprintf(lineCache + prefixLen, (size_t)(LINE_SIZE - prefixLen), fmt, std::forward(args)...) + prefixLen; 370 | 371 | if (len <= 13 || LINE_SIZE <= (len + 1)) { 372 | lineCache[13] = 0; 373 | fprintf(stderr, "日志异常: len[%d] lineCache[%s]", len, lineCache); 374 | return; 375 | } 376 | 377 | lineCache[len++] = '\n'; 378 | 379 | if (toFileFlag) 380 | toFile(lineCache, len); 381 | else 382 | toMem(lineCache, len); 383 | } 384 | 385 | void clearLog() { 386 | logCache[0] = '\n'; 387 | position = 1; 388 | } 389 | 390 | char* getLogPtr() { 391 | return logCache; 392 | } 393 | 394 | size_t getLoglen() { 395 | return position; 396 | } 397 | }; 398 | -------------------------------------------------------------------------------- /Frozen-Main/main.cpp: -------------------------------------------------------------------------------- 1 | // Freezeit 冻它模块 By JARK006 2 | 3 | #include "freezeit.hpp" 4 | #include "settings.hpp" 5 | #include "managedApp.hpp" 6 | #include "systemTools.hpp" 7 | #include "doze.hpp" 8 | #include "freezer.hpp" 9 | #include "server.hpp" 10 | 11 | int main(int argc, char **argv) { 12 | //先获取模块当前目录,Init()开启守护线程后, 工作目录将切换到根目录 "/" 13 | char fullPath[1024] = {}; 14 | auto pathPtr = realpath(argv[0], fullPath); 15 | 16 | Utils::Init(); 17 | 18 | Freezeit freezeit(argc, string(pathPtr)); 19 | Settings settings(freezeit); 20 | SystemTools systemTools(freezeit, settings); 21 | ManagedApp managedApp(freezeit, settings); 22 | Doze doze(freezeit, settings, managedApp, systemTools); 23 | Freezer freezer(freezeit, settings, managedApp, systemTools, doze); 24 | Server server(freezeit, settings, managedApp, systemTools, doze, freezer); 25 | 26 | sleep(3600 * 24 * 365);//放年假 27 | return 0; 28 | } 29 | 30 | /* 31 | TODO 32 | 33 | 1. 识别状态栏播放控件为前台 参考开源APP listen1 lxmusic 34 | 2. 进程冻结状态整合 35 | 3. 一些应用解冻后无法进入下一个activity 36 | 4. QQ解冻无网络 37 | 38 | 5. 抖音短时间解冻后重载:上次正在看的视频没了 39 | 6. 偶尔亮屏后,系统手势(返回,主页,最近任务)控制失灵,约3-5秒后恢复 40 | */ 41 | 42 | -------------------------------------------------------------------------------- /Frozen-Main/server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "freezeit.hpp" 5 | #include "managedApp.hpp" 6 | #include "systemTools.hpp" 7 | #include "freezer.hpp" 8 | #include "doze.hpp" 9 | 10 | class Server { 11 | private: 12 | Freezeit& freezeit; 13 | Settings& settings; 14 | ManagedApp& managedApp; 15 | SystemTools& systemTools; 16 | Freezer& freezer; 17 | Doze& doze; 18 | 19 | thread serverThread; 20 | 21 | static const int RECV_BUF_SIZE = 2 * 1024 * 1024; // 2 MiB TCP通信接收缓存大小 22 | static const int REPLY_BUF_SIZE = 8 * 1024 * 1024; // 8 MiB TCP通信回应缓存大小 23 | unique_ptr recvBuf, replyBuf; 24 | 25 | public: 26 | Server& operator=(Server&&) = delete; 27 | 28 | Server(Freezeit& freezeit, Settings& settings, ManagedApp& managedApp, SystemTools& systemTools, 29 | Doze& doze, Freezer& freezer) : 30 | freezeit(freezeit), settings(settings), managedApp(managedApp), 31 | systemTools(systemTools), freezer(freezer), doze(doze) { 32 | serverThread = thread(&Server::serverThreadFunc, this); 33 | } 34 | 35 | void serverThreadFunc() { 36 | /* LOCAL_SOCKET *******************************************************************/ 37 | // Socket 位于Linux抽象命名空间, 而不是文件路径 38 | // https://blog.csdn.net/howellzhu/article/details/111597734 39 | // https://blog.csdn.net/shanzhizi/article/details/16882087 一种是路径方式 一种是抽象命名空间 40 | //const int addrLen = offsetof(sockaddr_un, sun_path) + 15; // addrLen大小是 首个占位符 '\0' 加 "FreezeitServer" 的字符长度 41 | //const sockaddr_un serv_addr{ AF_UNIX, "\0FreezeitServer" }; // 首位为空[0]=0,位于Linux抽象命名空间 42 | //sockaddr_un clnt_addr{}; 43 | //socklen_t clnt_addr_size = sizeof(sockaddr_un); 44 | // 45 | // 终端执行 setenforce 0 ,即设置 SELinux 为宽容模式, 普通安卓应用才可以使用 LocalSocket 46 | //system("setenforce 0"); 47 | /* LOCAL_SOCKET *******************************************************************/ 48 | 49 | constexpr socklen_t addrLen = sizeof(sockaddr); 50 | const sockaddr_in serv_addr{ AF_INET, htons(60613), {inet_addr("127.0.0.1")}, {} }; 51 | 52 | 53 | recvBuf = make_unique(RECV_BUF_SIZE); 54 | replyBuf = make_unique(REPLY_BUF_SIZE); 55 | 56 | while (true) { 57 | static int failTcpCnt = 0; 58 | if (failTcpCnt) { 59 | fprintf(stderr, "Socket 失败%d次, [%d]:[%s]", failTcpCnt, errno, strerror(errno)); 60 | if (failTcpCnt > 100) { 61 | fprintf(stderr, "Socket 彻底失败, 已退出。[%d]:[%s]", errno, strerror(errno)); 62 | exit(-1); 63 | } 64 | sleep(5); 65 | } 66 | failTcpCnt++; 67 | 68 | int serv_sock; 69 | 70 | /* LOCAL_SOCKET *******************************************************************/ 71 | //if ((serv_sock = socket(AF_UNIX, SOCK_STREAM, 0)) <= 0) { 72 | // fprintf(stderr, "socket() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, strerror(errno)); 73 | // continue; 74 | //} 75 | /* LOCAL_SOCKET *******************************************************************/ 76 | 77 | 78 | /* NORMAL_SOCKET ******************************************************************/ 79 | if ((serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0) { 80 | fprintf(stderr, "socket() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, 81 | strerror(errno)); 82 | continue; 83 | } 84 | 85 | int opt = 1; //地址和端口 释放后可立即重用 否则几分钟后才可使用 86 | if (setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { 87 | fprintf(stderr, "setsockopt() Fail serv_sock[%d], [%d]:[%s]", serv_sock, errno, 88 | strerror(errno)); 89 | continue; 90 | } 91 | /* NORMAL_SOCKET ******************************************************************/ 92 | 93 | 94 | 95 | if (bind(serv_sock, (sockaddr*)&serv_addr, addrLen) < 0) { 96 | fprintf(stderr, "bind() Fail, [%d]:[%s]", errno, strerror(errno)); 97 | continue; 98 | } 99 | 100 | if (listen(serv_sock, 4) < 0) { 101 | fprintf(stderr, "listen() Fail, [%d]:[%s]", errno, strerror(errno)); 102 | continue; 103 | } 104 | 105 | while (true) { 106 | sockaddr_in clnt_addr{}; 107 | socklen_t clnt_addr_size = sizeof(sockaddr_in); 108 | int clnt_sock = accept(serv_sock, (sockaddr*)&clnt_addr, &clnt_addr_size); 109 | if (clnt_sock < 0) { 110 | static int failCnt = 1; 111 | 112 | fprintf(stderr, "accept() 第%d次错误 servFd[%d] clntFd[%d] size[%d]; [%d]:[%s]", 113 | failCnt, serv_sock, clnt_sock, clnt_addr_size, errno, strerror(errno)); 114 | 115 | if (++failCnt > 10) break; 116 | 117 | sleep(2); 118 | continue; 119 | } 120 | 121 | //设置接收超时 122 | timeval timeout = { 5, 0 }; // 5秒 超时 123 | if (setsockopt(clnt_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, 124 | sizeof(timeval))) { 125 | fprintf(stderr, "setsockopt 超时设置出错 servFd[%d] clntFd[%d] [%d]:[%s]", serv_sock, 126 | clnt_sock, errno, strerror(errno)); 127 | close(clnt_sock); 128 | continue; 129 | } 130 | 131 | uint8_t dataHeader[6]; 132 | auto headerLen = recv(clnt_sock, dataHeader, sizeof(dataHeader), MSG_WAITALL); 133 | if (headerLen != sizeof(dataHeader)) { 134 | close(clnt_sock); 135 | fprintf(stderr, "clnt_sock recv dataHeader len[%ld]", headerLen); 136 | continue; 137 | } 138 | 139 | uint32_t payloadLen = *((uint32_t*)dataHeader); 140 | uint32_t appCommand = dataHeader[4]; 141 | uint32_t XOR_value = dataHeader[5]; 142 | 143 | // "\0AUTH\n" Bilibili客户端乱发的,前4字节: 大端 4281684, 小端 1414873344 144 | if (payloadLen == 1414873344 || payloadLen == 4281684) { 145 | close(clnt_sock); 146 | continue; 147 | } 148 | else if (payloadLen >= RECV_BUF_SIZE) { 149 | //freezeit.logFmt("数据格式异常 payloadLen[%u] dataHeaderHEX[%s]", payloadLen, 150 | // Utils::bin2Hex(dataHeader, 6).c_str()); // 直接忽略 151 | close(clnt_sock); 152 | continue; 153 | } 154 | 155 | if (payloadLen) { 156 | uint32_t lenTmp = recv(clnt_sock, recvBuf.get(), payloadLen, MSG_WAITALL); 157 | if (lenTmp != payloadLen) { 158 | fprintf(stderr, "附带数据接收错误, appCommand[%u], 要求[%u], 实际接收[%u]", 159 | appCommand, payloadLen, lenTmp); 160 | close(clnt_sock); 161 | continue; 162 | } 163 | 164 | uint8_t XOR_cal = 0; 165 | for (uint32_t i = 0; i < payloadLen; i++) 166 | XOR_cal ^= (uint8_t)recvBuf[i]; 167 | 168 | if (XOR_value != XOR_cal) { 169 | fprintf(stderr, "数据校验错误, 提供值[0x%2x], 接收数据计算值[0x%2x]", XOR_value, XOR_cal); 170 | close(clnt_sock); 171 | continue; 172 | } 173 | } 174 | 175 | recvBuf[payloadLen] = 0; 176 | handleCmd(static_cast(appCommand), payloadLen, clnt_sock); 177 | } 178 | close(serv_sock); 179 | } 180 | } 181 | 182 | void handleCmd(const MANAGER_CMD appCommand, const int recvLen, const int clnt_sock) { 183 | const char* replyPtr = nullptr; 184 | uint32_t replyLen = 0; 185 | switch (appCommand) { 186 | case MANAGER_CMD::getPropInfo: { 187 | replyPtr = replyBuf.get(); 188 | replyLen = snprintf(replyBuf.get(), REPLY_BUF_SIZE, 189 | "%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n%s\n%u", 190 | freezeit.prop["id"].c_str(), freezeit.prop["name"].c_str(), 191 | freezeit.prop["version"].c_str(), freezeit.prop["versionCode"].c_str(), 192 | freezeit.prop["author"].c_str(), 193 | systemTools.cpuCluster, freezeit.moduleEnv.c_str(), freezer.getCurWorkModeStr(), 194 | systemTools.androidVerStr.c_str(), systemTools.kernelVerStr.c_str(), systemTools.extMemorySize); 195 | } break; 196 | 197 | case MANAGER_CMD::getChangelog: { 198 | replyPtr = freezeit.getChangelogPtr(); 199 | replyLen = freezeit.getChangelogLen(); 200 | } break; 201 | 202 | case MANAGER_CMD::getLog: { 203 | replyPtr = freezeit.getLogPtr(); 204 | replyLen = freezeit.getLoglen(); 205 | } break; 206 | 207 | case MANAGER_CMD::getAppCfg: { 208 | uint32_t intCnt = 0; 209 | const auto ptr = reinterpret_cast(replyBuf.get()); 210 | for (const auto& appInfo : managedApp.appInfoMap) { 211 | if (appInfo.uid < 0)continue; 212 | 213 | ptr[intCnt++] = appInfo.uid; 214 | ptr[intCnt++] = static_cast(appInfo.freezeMode); 215 | ptr[intCnt++] = appInfo.isPermissive ? 1 : 0; 216 | } 217 | 218 | replyPtr = replyBuf.get(); 219 | replyLen = intCnt * sizeof(int); 220 | } break; 221 | 222 | case MANAGER_CMD::getRealTimeInfo: { 223 | if (recvLen != 12) { 224 | replyPtr = replyBuf.get(); 225 | replyLen = snprintf(replyBuf.get(), 128, "实时信息需要12字节, 实际收到[%u]", 226 | recvLen); 227 | break; 228 | } 229 | 230 | uint32_t height = ((uint32_t*)recvBuf.get())[0]; 231 | uint32_t width = ((uint32_t*)recvBuf.get())[1]; 232 | 233 | if (height < 20 || width < 20) { 234 | replyPtr = replyBuf.get(); 235 | replyLen = snprintf(replyBuf.get(), 128, "宽高不符合, height[%u] width[%u]", 236 | height, width); 237 | break; 238 | } 239 | 240 | uint32_t availableMiB = ((uint32_t*)recvBuf.get())[2]; // Unit: MiB 241 | 242 | systemTools.getCPU_realtime(availableMiB); 243 | replyLen = systemTools.drawChart((uint32_t*)replyBuf.get(), height, width); 244 | replyLen += systemTools.formatRealTime(reinterpret_cast(replyBuf.get() + replyLen)); 245 | replyPtr = replyBuf.get(); 246 | } break; 247 | 248 | case MANAGER_CMD::getUidTime: { 249 | int* ptr = reinterpret_cast(replyBuf.get()); 250 | struct st { 251 | int uid; 252 | int total; 253 | int lastTotal; 254 | }; 255 | vector uidTimeSort; 256 | uidTimeSort.reserve(256); 257 | 258 | for (const auto& [uid, timeList] : doze.updateUidTime()) 259 | uidTimeSort.emplace_back(st{ uid, timeList.total, timeList.lastTotal }); 260 | std::sort(uidTimeSort.begin(), uidTimeSort.end(), 261 | [](const st& a, const st& b) { return a.total > b.total; }); 262 | 263 | int intCnt = 0; 264 | for (const auto& [uid, total, lastTotal] : uidTimeSort) { 265 | ptr[intCnt++] = uid; 266 | ptr[intCnt++] = total - lastTotal; 267 | ptr[intCnt++] = total; 268 | } 269 | 270 | replyPtr = replyBuf.get(); 271 | replyLen = intCnt * sizeof(int); 272 | } break; 273 | 274 | case MANAGER_CMD::getXpLog: { 275 | const int len = Utils::localSocketRequest(XPOSED_CMD::GET_XP_LOG, nullptr, 0, (int*)replyBuf.get(), REPLY_BUF_SIZE); 276 | if (len == 0) { 277 | freezeit.log("getXpLog 工作异常, 请确认LSPosed中冻它勾选系统框架, 然后重启"); 278 | replyPtr = "Freezeit's Xposed log is empty. "; 279 | replyLen = 32; 280 | } 281 | else { 282 | replyPtr = replyBuf.get(); 283 | replyLen = len; 284 | } 285 | } break; 286 | 287 | case MANAGER_CMD::getSettings: { 288 | replyPtr = settings.get(); 289 | replyLen = settings.size(); 290 | } break; 291 | 292 | case MANAGER_CMD::setAppCfg: { 293 | if (recvLen == 0 || (recvLen % 12)) { 294 | replyPtr = replyBuf.get(); 295 | replyLen = snprintf(replyBuf.get(), 128, "需要12字节的倍数, 实际收到 %d 字节", 296 | recvLen); 297 | freezeit.log(string_view(replyBuf.get(), replyLen)); 298 | break; 299 | } 300 | 301 | managedApp.updateAppList(); 302 | 303 | const int intSize = recvLen >> 2; // recvLen/4 304 | const int* ptr = reinterpret_cast(recvBuf.get()); 305 | map newCfg; 306 | 307 | for (int i = 0; i < intSize;) { 308 | const int uid = ptr[i++]; 309 | const FREEZE_MODE freezeMode = static_cast(ptr[i++]); 310 | const bool isPermissive = ptr[i++] != 0; 311 | if (managedApp.contains(uid)) { 312 | if (managedApp.FREEZE_MODE_SET.contains(freezeMode)) 313 | newCfg[uid] = { freezeMode, isPermissive }; 314 | else 315 | freezeit.logFmt("错误配置: UID:%d freezeMode:%d isPermissive:%d", uid, 316 | freezeMode, isPermissive); 317 | } 318 | else 319 | freezeit.logFmt("特殊应用: UID:%d, 已强制自由后台", uid); 320 | } 321 | 322 | string tips; 323 | set changeUidSet; // 发生配置变化的应用 324 | for (const auto& appInfo : managedApp.appInfoMap) { 325 | if (appInfo.uid < ManagedApp::UID_START || appInfo.freezeMode == FREEZE_MODE::WHITEFORCE || 326 | !newCfg.contains(appInfo.uid) || appInfo.freezeMode == newCfg[appInfo.uid].freezeMode) 327 | continue; 328 | 329 | changeUidSet.insert(appInfo.uid); 330 | tips += freezer.getModeText(appInfo.freezeMode) + "->" + 331 | freezer.getModeText(newCfg[appInfo.uid].freezeMode) + " [" + 332 | appInfo.label + "]\n"; 333 | } 334 | if (tips.length()) 335 | freezeit.logFmt("配置变化:\n\n%s", tips.c_str()); 336 | 337 | tips.clear(); 338 | for (const auto& [uid, cfg] : newCfg) { 339 | if (!managedApp.contains(uid)) { 340 | tips += to_string(uid) + ", "; 341 | } 342 | } 343 | if (tips.length()) 344 | freezeit.logFmt("以下UID的应用不受Frozen管理:[%s] 可在Frozen配置页搜索UID查看是哪些应用", tips.c_str()); 345 | 346 | //auto runningPids = freezer.getRunningPids(changeUidSet); 347 | //tips.clear(); 348 | //for (auto& [uid, pids] : runningPids) { 349 | // auto& appInfo = managedApp[uid]; 350 | // tips += "\n" + appInfo.label; 351 | // for (const int pid : pids) 352 | // tips += " " + to_string(pid); 353 | // appInfo.pids = std::move(pids); 354 | // freezer.handleSignal(appInfo, SIGKILL); // 已包含 V1 前置解冻 355 | //} 356 | //if (tips.length()) 357 | // freezeit.logFmt("杀死策略变更的应用: \n%s\n", tips.c_str()); 358 | 359 | auto runningUids = freezer.getRunningUids(changeUidSet); 360 | if (runningUids.size()) { 361 | freezer.unFreezerTemporary(runningUids); 362 | tips.clear(); 363 | for (auto& uid : runningUids) { 364 | tips.append(managedApp[uid].label); 365 | tips.append(" "); 366 | } 367 | freezeit.logFmt("解冻策略变更的应用: %s", tips.c_str()); 368 | } 369 | 370 | managedApp.loadConfig2CfgTemp(newCfg); 371 | managedApp.updateIME2CfgTemp(); 372 | managedApp.applyCfgTemp(); 373 | managedApp.saveConfig(); 374 | managedApp.update2xposedByLocalSocket(); 375 | 376 | replyPtr = "success"; 377 | replyLen = 7; 378 | } break; 379 | 380 | case MANAGER_CMD::setAppLabel: { 381 | managedApp.updateAppList(); // 先更新应用列表 382 | 383 | map labelList; 384 | for (const string& str : Utils::splitString(string(recvBuf.get(), recvLen), 385 | "\n")) { 386 | const int uid = atoi(str.c_str()); 387 | if (!managedApp.contains(uid) || str.length() <= 6) 388 | freezeit.logFmt("解析名称错误 [%s]", str.c_str()); 389 | else labelList[uid] = str.substr(6); 390 | } 391 | 392 | string labelStr; 393 | labelStr.reserve(1024L * 4); 394 | for (const auto& [uid, label] : labelList) { 395 | labelStr += " ["; 396 | labelStr += label; 397 | labelStr += ']'; 398 | } 399 | freezeit.logFmt("更新 %lu 款应用名称:\n\n%s\n", labelList.size(), labelStr.c_str()); 400 | 401 | managedApp.loadLabel(labelList); 402 | managedApp.update2xposedByLocalSocket(); 403 | managedApp.saveLabel(); 404 | 405 | replyPtr = "success"; 406 | replyLen = 7; 407 | } break; 408 | 409 | case MANAGER_CMD::clearLog: { 410 | freezeit.clearLog(); 411 | replyPtr = freezeit.getLogPtr(); 412 | replyLen = freezeit.getLoglen(); 413 | } break; 414 | 415 | case MANAGER_CMD::getProcState: { 416 | freezer.printProcState(); 417 | replyPtr = freezeit.getLogPtr(); 418 | replyLen = freezeit.getLoglen(); 419 | } break; 420 | 421 | case MANAGER_CMD::setSettingsVar: { 422 | replyPtr = replyBuf.get(); 423 | 424 | if (recvLen != 2) { 425 | replyLen = snprintf(replyBuf.get(), REPLY_BUF_SIZE, 426 | "数据长度不正确, 正常:2, 收到:%d", recvLen); 427 | break; 428 | } 429 | 430 | int len = settings.checkAndSet(recvBuf[0], recvBuf[1], replyBuf.get()); 431 | 432 | if (len <= 0) { 433 | memcpy(replyBuf.get(), "未知设置错误", 18); 434 | replyLen = 18; 435 | } 436 | else { 437 | replyLen = len; 438 | } 439 | } break; 440 | 441 | default: { 442 | replyPtr = "非法命令"; 443 | replyLen = 12; 444 | } break; 445 | } 446 | 447 | if (replyLen) { 448 | uint32_t header[2] = { replyLen , 0 }; 449 | send(clnt_sock, header, 6, MSG_DONTROUTE); 450 | send(clnt_sock, replyPtr, replyLen, MSG_DONTROUTE); 451 | } 452 | close(clnt_sock); 453 | } 454 | }; 455 | -------------------------------------------------------------------------------- /Frozen-Main/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils.hpp" 4 | #include "freezeit.hpp" 5 | 6 | class Settings { 7 | private: 8 | Freezeit& freezeit; 9 | mutex writeSettingMutex; 10 | 11 | string settingsPath; 12 | 13 | const static size_t SETTINGS_SIZE = 256; 14 | uint8_t settingsVar[SETTINGS_SIZE] = { 15 | 8, //[0] 设置文件版本 16 | 0, //[1] 17 | 10, //[2] freezeTimeout sec 18 | 4, //[3] wakeupTimeoutIdx 定时唤醒 参数索引 0-5:关闭, 5m, 15m, 30m, 1h, 2h 19 | 20, //[4] terminateTimeout sec 20 | 1, //[5] setMode 设置Freezer模式 0: v2frozen(默认), 1: v2uid, 2:v1frozen, 3: 全局SIGSTOP 21 | 2, //[6] refreezeTimeoutIdx 定时压制 参数索引 0-3:关闭, 30m, 1h, 2h 22 | 0, //[7] 23 | 0, //[8] 24 | 0, //[9] 25 | 1, //[10] 26 | 0, //[11] 27 | 0, //[12] 28 | 0, //[13] 电池监控 29 | 0, //[14] 电流校准 30 | 0, //[15] 双电芯 31 | 0, //[16] 调整 lmk 参数 仅安卓12-16 32 | 0, //[17] 深度Doze 33 | 0, //[18] ReKernel 34 | 0, //[19] 内存压缩 35 | 0, //[20] 开机冻结 36 | 0, //[21] 网络解冻 37 | 0, //[22] 省电模式 38 | 0, //[13] 39 | 0, //[24] 40 | 0, //[25] 41 | 0, //[26] 42 | 0, //[27] 43 | 0, //[28] 44 | 0, //[29] 45 | 0, //[30] 调试日志 46 | 0, //[31] 47 | 0, //[32] 48 | }; 49 | 50 | 51 | // 关闭, 30m, 1h, 2h 52 | static constexpr int refreezeTimeoutList[] = { 86400 * 365, 60 * 30, 3600, 3600 * 2 }; 53 | // 最大索引 54 | static constexpr int refreezeTimeoutIdxMax = sizeof(refreezeTimeoutList) / sizeof(refreezeTimeoutList[0]) - 1; 55 | 56 | // 关闭, 5m, 15m, 30m, 1h, 2h 57 | static constexpr int wakeupTimeoutList[] = { 86400 * 365, 60 * 5, 60 * 15, 60 * 30, 3600, 3600 * 2 }; 58 | // 最大索引 59 | static constexpr int wakeupTimeoutIdxMax = sizeof(wakeupTimeoutList) / sizeof(wakeupTimeoutList[0]) - 1; 60 | 61 | 62 | public: 63 | uint8_t& settingsVer = settingsVar[0]; // 设置文件版本 64 | //uint8_t& unknown = settingsVar[1]; // 65 | uint8_t& freezeTimeout = settingsVar[2]; // 超时冻结 单位 秒 66 | uint8_t& wakeupTimeoutIdx = settingsVar[3]; // 定时唤醒 参数索引 0-5:关闭, 5m, 15m, 30m, 1h, 2h 67 | uint8_t& terminateTimeout = settingsVar[4]; // 超时杀死 单位 秒 68 | uint8_t& setMode = settingsVar[5]; // Freezer模式 69 | uint8_t& refreezeTimeoutIdx = settingsVar[6]; // 定时压制 参数索引 0-3:关闭, 30m, 1h, 2h 70 | 71 | uint8_t& enableBatteryMonitor = settingsVar[13]; // 电池监控 72 | uint8_t& enableCurrentFix = settingsVar[14]; // 电池电流校准 73 | uint8_t& enableDoubleCell = settingsVar[15]; // 双电芯 电流翻倍 74 | uint8_t& enableLMK = settingsVar[16]; // 调整 lmk 参数 仅安卓11-15 75 | uint8_t& enableDoze = settingsVar[17]; // 深度Doze 76 | uint8_t& enableReKernel = settingsVar[18]; // ReKernel 77 | uint8_t& enableMemoryCompress = settingsVar[19]; // 内存压缩 78 | uint8_t& enableBootFreezer = settingsVar[20]; // 开机冻结 79 | uint8_t& enableNetworkUnfreezer = settingsVar[21]; // 网络解冻 80 | uint8_t& enablePowersaveMode = settingsVar[22]; // 省电模式 81 | //uint8_t& unknown = settingsVar[18]; // 82 | 83 | uint8_t& enableDebug = settingsVar[30]; // 调试日志 84 | 85 | Settings& operator=(Settings&&) = delete; 86 | 87 | Settings(Freezeit& freezeit) : freezeit(freezeit) { 88 | 89 | freezeit.setDebugPtr(settingsVar+30); 90 | 91 | settingsPath = freezeit.modulePath + "/settings.db"; 92 | 93 | auto fd = open(settingsPath.c_str(), O_RDONLY); 94 | if (fd > 0) { 95 | uint8_t tmp[SETTINGS_SIZE] = { 0 }; 96 | int readSize = read(fd, tmp, SETTINGS_SIZE); 97 | close(fd); 98 | 99 | if (readSize != SETTINGS_SIZE) { 100 | freezeit.log("设置文件校验失败, 将使用默认设置参数, 并更新设置文件"); 101 | freezeit.logFmt("读取大小: %d Bytes. 要求大小: 256 Bytes.", readSize); 102 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 103 | } 104 | else if (tmp[0] != settingsVer) { 105 | freezeit.logFmt("设置文件当前版本: V%d 要求版本: V%d,版本不兼容, 将使用新版默认设置参数, 请根据情况自行重新调整设置", 106 | static_cast(tmp[0]), static_cast(settingsVer)); 107 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 108 | } 109 | else { 110 | memcpy(settingsVar, tmp, SETTINGS_SIZE); 111 | 112 | bool isError = false; 113 | if (setMode > 2) { 114 | isError = true; 115 | setMode = 0; 116 | freezeit.logFmt("冻结模式参数[%d]错误, 已重设为 FreezerV2 (FROZEN)", (int)setMode); 117 | } 118 | if (refreezeTimeoutIdx > refreezeTimeoutIdxMax) { 119 | isError = true; 120 | refreezeTimeoutIdx = 1; 121 | freezeit.logFmt("定时压制参数[%d]错误, 已重设为 %d 分钟", 122 | static_cast(refreezeTimeoutIdx), refreezeTimeoutList[refreezeTimeoutIdx] / 60); 123 | } 124 | if (wakeupTimeoutIdx > wakeupTimeoutIdxMax) { 125 | isError = true; 126 | wakeupTimeoutIdx = 4; 127 | freezeit.logFmt("定时解冻参数[%d]错误, 已重置为 %d 分钟", 128 | static_cast(wakeupTimeoutIdx), wakeupTimeoutList[wakeupTimeoutIdx] / 60); 129 | } 130 | if (freezeTimeout < 1 || 60 < freezeTimeout) { 131 | isError = true; 132 | freezeTimeout = 10; 133 | freezeit.logFmt("超时冻结参数[%d]错误, 已重置为 %d 秒", 134 | static_cast(freezeTimeout), (int)freezeTimeout); 135 | } 136 | if (terminateTimeout < 3 || 120 < terminateTimeout) { 137 | isError = true; 138 | terminateTimeout = 30; 139 | freezeit.logFmt("超时杀死参数[%d]错误, 已重置为 %d 秒", 140 | static_cast(terminateTimeout), (int)terminateTimeout); 141 | } 142 | if (isError) { 143 | freezeit.log("新版本可能会调整部分设置,可能需要重新设置"); 144 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 145 | } 146 | } 147 | } 148 | else { 149 | freezeit.log("设置文件不存在, 将初始化设置文件"); 150 | freezeit.log(save() ? "⚙️设置成功" : "🔧设置文件写入失败"); 151 | } 152 | } 153 | 154 | uint8_t& operator[](const int key) { 155 | return settingsVar[key]; 156 | } 157 | 158 | const char* get() { 159 | return (const char*)settingsVar; 160 | } 161 | 162 | size_t size() { 163 | return SETTINGS_SIZE; 164 | } 165 | 166 | bool isRefreezeEnable() const { 167 | return 0 < refreezeTimeoutIdx && refreezeTimeoutIdx <= refreezeTimeoutIdxMax; 168 | } 169 | int getRefreezeTimeout() const { 170 | return refreezeTimeoutList[refreezeTimeoutIdx <= refreezeTimeoutIdxMax ? refreezeTimeoutIdx : 0]; 171 | } 172 | 173 | bool isWakeupEnable() const { 174 | return 0 < wakeupTimeoutIdx && wakeupTimeoutIdx <= wakeupTimeoutIdxMax; 175 | } 176 | int getWakeupTimeout() const { 177 | return wakeupTimeoutList[wakeupTimeoutIdx <= wakeupTimeoutIdxMax ? wakeupTimeoutIdx : 0]; 178 | } 179 | 180 | bool save() { 181 | lock_guard lock(writeSettingMutex); 182 | auto fd = open(settingsPath.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666); 183 | if (fd > 0) { 184 | int writeSize = write(fd, settingsVar, SETTINGS_SIZE); 185 | close(fd); 186 | if (writeSize == SETTINGS_SIZE) 187 | return true; 188 | 189 | freezeit.logFmt("设置异常, 文件实际写入[%d]Bytes", writeSize); 190 | } 191 | return false; 192 | } 193 | 194 | int checkAndSet(const int idx, const int val, char* replyBuf) { 195 | const size_t REPLY_BUF_SIZE = 2048; 196 | 197 | switch (idx) { 198 | case 2: { // freezeTimeout sec 199 | if (val < 1 || 60 < val) 200 | return snprintf(replyBuf, REPLY_BUF_SIZE, "超时冻结参数错误, 欲设为:%d", val); 201 | } 202 | break; 203 | 204 | case 3: { // wakeupTimeoutIdx 205 | if (val > wakeupTimeoutIdxMax) 206 | return snprintf(replyBuf, REPLY_BUF_SIZE, "定时解冻参数错误 欲设为:%d", val); 207 | } 208 | break; 209 | 210 | case 4: { // wakeupTimeoutIdx sec 211 | if (val < 3 || 120 < val) 212 | return snprintf(replyBuf, REPLY_BUF_SIZE, "超时杀死参数错误, 欲设为:%d", val); 213 | } 214 | break; 215 | 216 | case 5: { // setMode 0-1-2 217 | if (val > 2) 218 | return snprintf(replyBuf, REPLY_BUF_SIZE, "冻结模式参数错误, 欲设为:%d", val); 219 | } 220 | break; 221 | 222 | case 6: { // refreezeTimeoutIdx 223 | if (val > refreezeTimeoutIdxMax) 224 | return snprintf(replyBuf, REPLY_BUF_SIZE, "定时压制参数错误, 欲设为:%d", val); 225 | } 226 | break; 227 | 228 | case 10: // xxx 229 | case 11: // xxx 230 | case 12: // xxx 231 | case 13: // 电池监控 232 | case 14: // 电流校准 233 | case 15: // 双电芯 234 | case 16: // lmk 235 | case 17: // doze 236 | case 18: // ReKernel 237 | case 19: // 内存压缩 238 | case 20: // 开机冻结 239 | case 21: // 网络解冻 240 | case 22: // 省电模式 241 | case 23: // 242 | case 24: // 243 | case 25: // 244 | case 26: // 245 | case 27: // 246 | case 28: // 247 | case 29: // 248 | case 30: // 调试日志 249 | { 250 | if (val != 0 && val != 1) 251 | return snprintf(replyBuf, REPLY_BUF_SIZE, "开关值错误, 正常范围:0/1, 欲设为:%d", val); 252 | } 253 | break; 254 | 255 | default: { 256 | freezeit.logFmt("🔧设置失败,设置项不存在, [%d]:[%d]", idx, val); 257 | return snprintf(replyBuf, REPLY_BUF_SIZE, "设置项不存在, [%d]:[%d]", idx, val); 258 | } 259 | } 260 | 261 | settingsVar[idx] = val; 262 | if (save()) { 263 | return snprintf(replyBuf, REPLY_BUF_SIZE, "success"); 264 | } 265 | else { 266 | freezeit.logFmt("🔧设置失败,写入设置文件失败, [%d]:%d", idx, val); 267 | return snprintf(replyBuf, REPLY_BUF_SIZE, "写入设置文件失败, [%d]:%d", idx, val); 268 | } 269 | } 270 | }; 271 | -------------------------------------------------------------------------------- /Frozen-Main/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #define BITS_PER_LONG (sizeof(long) * 8) 58 | #define test_bit(array, bit) ((array[bit / BITS_PER_LONG] >> bit % BITS_PER_LONG) & 1) 59 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) 60 | 61 | using std::set; 62 | using std::unordered_set; 63 | using std::map; 64 | using std::multimap; 65 | using std::stringstream; 66 | using std::lock_guard; 67 | using std::unique_ptr; 68 | using std::ifstream; 69 | using std::vector; 70 | using std::string; 71 | using std::string_view; 72 | using std::thread; 73 | using std::mutex; 74 | 75 | using std::make_unique; 76 | using std::to_string; 77 | 78 | 79 | // 配置编译选项 ***************** 80 | constexpr auto FORK_DOUBLE = 1; 81 | 82 | #define DEBUG_LOG 1 83 | #define DEBUG_DURATION 0 84 | // ***************************** 85 | 86 | #if DEBUG_LOG 87 | #define DLOG(...) freezeit.logFmt(__VA_ARGS__) 88 | #else 89 | #define DLOG(...) ((void)0) 90 | #endif 91 | 92 | #if DEBUG_DURATION 93 | #define START_TIME_COUNT auto start_clock = clock() 94 | #if CLOCKS_PER_SEC == 1000000 95 | #define END_TIME_COUNT int duration_us=clock()-start_clock;freezeit.logFmt("%s(): %d.%03d ms", __FUNCTION__, duration_us/1000, duration_us%1000) 96 | #elif CLOCKS_PER_SEC == 1000 97 | #define END_TIME_COUNT int duration_ms=clock()-start_clock;freezeit.logFmt("%s(): %d ms", __FUNCTION__, duration_ms) 98 | #else 99 | #error CLOCKS_PER_SEC value is not support 100 | #endif 101 | #else 102 | #define START_TIME_COUNT ((void)0) 103 | #define END_TIME_COUNT ((void)0) 104 | #endif 105 | 106 | #define SYNC_RECEIVED_WHILE_FROZEN (1) 107 | #define ASYNC_RECEIVED_WHILE_FROZEN (2) 108 | #define TXNS_PENDING_WHILE_FROZEN (4) 109 | 110 | enum class WORK_MODE : uint32_t { 111 | V2FROZEN = 0, 112 | V2UID = 1, 113 | V1FROZEN = 2, 114 | GLOBAL_SIGSTOP = 3, 115 | }; 116 | 117 | enum class FREEZE_MODE : uint32_t { 118 | TERMINATE = 10, 119 | SIGNAL = 20, 120 | SIGNAL_BREAK = 21, 121 | FREEZER = 30, 122 | FREEZER_BREAK = 31, 123 | WHITELIST = 40, 124 | WHITEFORCE = 50, 125 | }; 126 | 127 | // 1359322925 是 "Freezeit" 的10进制CRC32值 128 | const int baseCode = 1359322925; 129 | 130 | enum class XPOSED_CMD : uint32_t { 131 | GET_FOREGROUND = baseCode + 1, 132 | GET_SCREEN = baseCode + 2, 133 | GET_XP_LOG = baseCode + 3, 134 | 135 | SET_CONFIG = baseCode + 20, 136 | SET_WAKEUP_LOCK = baseCode + 21, 137 | 138 | BREAK_NETWORK = baseCode + 41, 139 | 140 | UPDATE_PENDING = baseCode + 60, // 更新待冻结应用 141 | }; 142 | 143 | enum class REPLY : uint32_t { 144 | SUCCESS = 2, // 成功 145 | FAILURE = 0, // 失败 146 | }; 147 | 148 | enum class WAKEUP_LOCK : uint32_t { 149 | IGNORE = 1, 150 | DEFAULT = 3, 151 | }; 152 | 153 | 154 | enum class MANAGER_CMD : uint32_t { 155 | // 获取信息 无附加数据 No additional data required 156 | getPropInfo = 2, // return string: "ID\nName\nVersion\nVersionCode\nAuthor\nclusterNum" 157 | getChangelog = 3, // return string: "changelog" 158 | getLog = 4, // return string: "log" 159 | getAppCfg = 5, // return string: "package x\npackage x\n... 160 | getRealTimeInfo = 6, // return ImgBytes[h*w*4]+String: (rawBitmap + 内存 频率 使用率 电流) 161 | getSettings = 8, // return bytes[256]: all settings parameter 162 | getUidTime = 9, // return "uid last_user_time last_sys_time user_time sys_time\n..." 163 | getXpLog = 10, 164 | 165 | // 设置 需附加数据 166 | setAppCfg = 21, // send "package x\npackage x\npackage x\n..." 167 | setAppLabel = 22, // send "uid label\nuid label\nuid label\n..." 168 | setSettingsVar = 23, // send bytes[2]: [0]index [1]value 169 | 170 | // 其他命令 无附加数据 No additional data required 171 | clearLog = 61, // return string: "log" //清理并返回log 172 | getProcState = 62, // return string: "log" //打印冻结状态并返回log 173 | 174 | }; 175 | 176 | struct binder_state { 177 | int fd; 178 | void* mapped; 179 | size_t mapSize; 180 | }; 181 | 182 | struct KernelVersionStruct { 183 | int main = 0; 184 | int sub = 0; 185 | int patch = 0; 186 | }; 187 | 188 | struct MemInfoStruct { // Unit: MiB 189 | int totalRam = 1; 190 | int availRam = 1; 191 | int totalSwap = 1; 192 | int freeSwap = 1; 193 | }; 194 | 195 | struct cpuRealTimeStruct { 196 | int freq; // Mhz 197 | int usage; // % 198 | }; 199 | 200 | struct uidTimeStruct { 201 | int lastTotal = 0; 202 | int total = 0; 203 | }; 204 | 205 | struct appInfoStruct { 206 | int uid = -1; 207 | FREEZE_MODE freezeMode = FREEZE_MODE::FREEZER; // [10]:杀死 [20]:SIGSTOP [30]:freezer [40]:配置 [50]:内置 208 | bool isPermissive = true; // 宽容的 有前台服务也算前台 209 | int timelineUnfrozenIdx = -1; // 解冻时间线索引 210 | bool isSystemApp = true; // 是否系统应用 211 | time_t startTimestamp = 0; // 某次开始运行时刻 212 | time_t stopTimestamp = 0; // 某次冻结运行时刻 213 | time_t totalRunningTime = 0; // 运行时长 214 | string package; // 包名 215 | string label; // 名称 216 | vector pids; // PID列表 217 | 218 | bool needBreakNetwork() const { 219 | return freezeMode == FREEZE_MODE::SIGNAL_BREAK || freezeMode == FREEZE_MODE::FREEZER_BREAK; 220 | } 221 | bool isSignalMode() const { 222 | return freezeMode == FREEZE_MODE::SIGNAL_BREAK || freezeMode == FREEZE_MODE::SIGNAL; 223 | } 224 | bool isFreezeMode() const { 225 | return freezeMode == FREEZE_MODE::FREEZER_BREAK || freezeMode == FREEZE_MODE::FREEZER; 226 | } 227 | bool isSignalOrFreezer() const { 228 | return freezeMode <= FREEZE_MODE::SIGNAL && freezeMode < FREEZE_MODE::WHITELIST; 229 | } 230 | bool isWhitelist() const { 231 | return freezeMode >= FREEZE_MODE::WHITELIST; 232 | } 233 | bool isBlacklist() const { 234 | return freezeMode < FREEZE_MODE::WHITELIST; 235 | } 236 | bool isTerminateMode() const { 237 | return freezeMode == FREEZE_MODE::TERMINATE; 238 | } 239 | }; 240 | 241 | struct cfgStruct { 242 | FREEZE_MODE freezeMode = FREEZE_MODE::FREEZER; 243 | bool isPermissive = true; 244 | }; 245 | 246 | template 247 | class stackString { 248 | public: 249 | size_t length{ 0 }; 250 | char data[CAPACITY]; 251 | 252 | const char* c_str() { return data; } 253 | const char* operator* () { return data; } 254 | 255 | stackString() { data[0] = 0; } 256 | stackString(const string_view& s) { 257 | if (s.length() >= CAPACITY - 1) { 258 | memcpy(data, s.data(), CAPACITY - 1); 259 | length = CAPACITY - 1; 260 | data[CAPACITY - 1] = 0; 261 | } 262 | else { 263 | memcpy(data, s.data(), s.length()); 264 | length = s.length(); 265 | data[length] = 0; 266 | } 267 | } 268 | stackString(const char* s, const size_t len) { 269 | if (len >= CAPACITY - 1) { 270 | memcpy(data, s, CAPACITY - 1); 271 | length = CAPACITY - 1; 272 | data[CAPACITY - 1] = 0; 273 | } 274 | else { 275 | memcpy(data, s, len); 276 | length = len; 277 | data[length] = 0; 278 | } 279 | } 280 | 281 | stackString& append(const int n) { 282 | char tmp[16] = {}; 283 | return append(tmp, static_cast(snprintf(tmp, sizeof(tmp), "%d", n))); 284 | } 285 | 286 | stackString& append(const char* s) { 287 | return append(s, strlen(s)); 288 | } 289 | 290 | stackString& append(const char c) { 291 | if (length + 1 >= CAPACITY) // 预留最后一位 填 '\0' 292 | return *this; 293 | 294 | data[length++] = c; 295 | data[length] = 0; 296 | return *this; 297 | } 298 | 299 | stackString& append(const char* s, const size_t len) { 300 | if (length + len >= CAPACITY) // 预留最后一位 填 '\0' 301 | return *this; 302 | 303 | memcpy(data + length, s, len); 304 | length += len; 305 | data[length] = 0; 306 | return *this; 307 | } 308 | 309 | // 注意长度,可能溢出,不安全 310 | template 311 | stackString& appendFmt(const char* fmt, Args&&... args) { 312 | if (length < CAPACITY) 313 | length += snprintf(data + length, CAPACITY - length, fmt, std::forward(args)...); 314 | return *this; 315 | } 316 | 317 | void clear() { length = 0; data[0] = 0; } 318 | }; 319 | 320 | 321 | namespace Utils { 322 | 323 | vector splitString(const string& str, const string& delim) { 324 | if (str.empty()) return {}; 325 | if (delim.empty()) return { str }; 326 | 327 | vector res; 328 | size_t nextDelimIdx, targetBeginIdx = 0; 329 | while ((nextDelimIdx = str.find(delim, targetBeginIdx)) != string::npos) { 330 | if (nextDelimIdx == targetBeginIdx) { 331 | targetBeginIdx += delim.length(); 332 | continue; 333 | } 334 | res.emplace_back(str.substr(targetBeginIdx, nextDelimIdx - targetBeginIdx)); 335 | targetBeginIdx = nextDelimIdx + delim.length(); 336 | } 337 | if (targetBeginIdx < str.length()) 338 | res.emplace_back(str.substr(targetBeginIdx, str.length() - targetBeginIdx)); 339 | return res; 340 | } 341 | 342 | void strReplace(string& src, const string& oldBlock, const string& newBlock) { 343 | if (oldBlock.empty())return; 344 | 345 | size_t nextBeginIdx = 0, foundIdx; 346 | while ((foundIdx = src.find(oldBlock, nextBeginIdx)) != string::npos) { 347 | src.replace(foundIdx, oldBlock.length(), newBlock); 348 | 349 | // 替换后,在新区块【后面】开始搜索 350 | nextBeginIdx = foundIdx + newBlock.length(); 351 | 352 | // 替换后,以新区块【起点】开始搜索。即:替换之后的新区块连同【后续】内容仍有可能满足条件而被继续替换 353 | // nextBeginIdx = foundIdx; 354 | 355 | // 替换后,在新区块【前后】搜索,新区块连同【前-后】内容仍有可能满足条件而被继续替换 356 | // nextBeginIdx = foundIdx > oldBlock.length() ? (foundIdx - oldBlock.length()) : 0; 357 | } 358 | return; 359 | } 360 | 361 | string bin2Hex(const void* bytes, const int len) { 362 | auto charList = "0123456789ABCDEF"; 363 | if (len == 0) return ""; 364 | string res(len * 3, ' '); 365 | for (int i = 0; i < len; i++) { 366 | const uint8_t value = reinterpret_cast(bytes)[i]; 367 | res[i * 3L] = charList[value >> 4]; 368 | res[i * 3L + 1] = charList[value & 0x0f]; 369 | } 370 | return res; 371 | } 372 | 373 | //"2022-01-01 00:00:00" 374 | time_t timeFormat2Timestamp(const char* strTimeFormat) { 375 | // strTimeFormat should be such as "2001-11-12 18:31:01" 376 | struct tm timeinfo; 377 | memset((void*)&timeinfo, 0, sizeof(struct tm)); 378 | 379 | // strptime("1970:01:01 08:00:00", "%Y:%m:%d %H:%M:%S", timeinfo); 380 | strptime(strTimeFormat, "%Y-%m-%d %H:%M:%S", &timeinfo); 381 | 382 | return mktime(&timeinfo); 383 | } 384 | 385 | // https://blog.csdn.net/lanmanck/article/details/8423669 386 | vector getTouchEventNum() { 387 | vector res; 388 | 389 | for (int i = 0; i < 16; i++) { 390 | char path[64]; 391 | snprintf(path, 64, "/dev/input/event%d", i); 392 | auto fd = open(path, O_RDONLY, 0); 393 | if (fd < 0)continue; 394 | 395 | uint32_t flagBit = 0; 396 | constexpr uint32_t cmd = EVIOCGBIT(0, sizeof(uint32_t)); 397 | ioctl(fd, cmd, &flagBit); 398 | if (flagBit & (1 << EV_ABS)) res.emplace_back(i); 399 | close(fd); 400 | } 401 | if (res.size() == 0) { 402 | fprintf(stderr, "前台任务同步事件获取失败"); 403 | exit(-1); 404 | } 405 | return res; 406 | } 407 | std::string getNumberedFiles(const char* directoryPath) { 408 | std::string result; 409 | DIR* dir = opendir(directoryPath); 410 | 411 | if (!dir) { 412 | fprintf(stderr, "无法打开目录: %s" ,directoryPath); 413 | return result; 414 | } 415 | 416 | struct dirent* entry; 417 | while ((entry = readdir(dir)) != nullptr) { 418 | if (entry->d_type == DT_REG && entry->d_name[0] >= '2' && entry->d_name[0] <= '2') { 419 | int fileNum = atoi(entry->d_name); 420 | if (fileNum >= 22 && fileNum <= 26) { 421 | if (!result.empty()) { 422 | result += ", "; 423 | } 424 | result += entry->d_name; 425 | } 426 | } 427 | } 428 | 429 | closedir(dir); 430 | return result; 431 | } 432 | 433 | int readInt(const char* path) { 434 | auto fd = open(path, O_RDONLY); 435 | if (fd < 0) return 0; 436 | char buff[16] = { 0 }; 437 | auto len = read(fd, buff, sizeof(buff)); 438 | close(fd); 439 | 440 | if (len <= 0)return 0; 441 | buff[15] = 0; 442 | return atoi(buff); 443 | } 444 | 445 | size_t readString(const char* path, char* buff, const size_t maxLen) { 446 | auto fd = open(path, O_RDONLY); 447 | if (fd <= 0) { 448 | buff[0] = 0; 449 | return 0; 450 | } 451 | ssize_t len = read(fd, buff, maxLen); 452 | close(fd); 453 | if (len <= 0) { 454 | buff[0] = 0; 455 | return 0; 456 | } 457 | buff[len] = 0; // 终止符 458 | return static_cast(len); 459 | } 460 | 461 | size_t popenRead(const char* cmd, char* buf, const size_t maxLen) { 462 | auto fp = popen(cmd, "r"); 463 | if (!fp) return 0; 464 | auto readLen = fread(buf, 1, maxLen, fp); 465 | pclose(fp); 466 | return readLen; 467 | } 468 | 469 | // 最大读取 64 KiB 470 | string readString(const char* path) { 471 | char buff[64 * 1024]; 472 | readString(path, buff, sizeof(buff)); 473 | return string(buff); 474 | } 475 | bool RemoveFile(const char* path){ 476 | std::string cmd = "rm -rf "; 477 | cmd += path; 478 | 479 | char buffer[128]; 480 | size_t maxLen = sizeof(buffer); 481 | 482 | size_t len = popenRead(cmd.c_str(), buffer, maxLen); 483 | 484 | if (len == 0){ 485 | return true; 486 | } 487 | 488 | return false; 489 | } 490 | bool writeInt(const char* path, const int value) { 491 | auto fd = open(path, O_WRONLY); 492 | if (fd <= 0){ 493 | return false; 494 | } 495 | 496 | char tmp[16]; 497 | auto len = snprintf(tmp, sizeof(tmp), "%d", value); 498 | write(fd, tmp, len); 499 | close(fd); 500 | return true; 501 | } 502 | 503 | bool writeString(const char* path, const char* buff, size_t len = 0) { 504 | if (len == 0)len = strlen(buff); 505 | if (len == 0)return true; 506 | 507 | auto fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666); 508 | if (fd <= 0) return false; 509 | 510 | write(fd, buff, len); 511 | close(fd); 512 | return true; 513 | } 514 | 515 | char lastChar(char* ptr) { 516 | if (!ptr)return 0; 517 | while (*ptr) ptr++; 518 | return *(ptr - 1); 519 | } 520 | 521 | bool startWith(const char* prefix, const char* target) { 522 | int idx = 0; 523 | while (prefix[idx]) { 524 | if (prefix[idx] != target[idx]) 525 | return false; 526 | idx++; 527 | } 528 | return true; 529 | } 530 | 531 | bool endWith(const string& suffix, const string& target) { 532 | if (suffix.empty() || suffix.length() > target.length()) return false; 533 | for (int i = suffix.length() - 1, j = target.length() - 1; i >= 0; i--, j--) { 534 | if (suffix[i] != target[j]) return false; 535 | } 536 | return true; 537 | } 538 | 539 | string parentDir(string path) { 540 | if (path.empty())return ""; 541 | if (path.back() == '/') path.pop_back(); 542 | auto idx = path.find_last_of('/'); 543 | return idx == string::npos ? path : path.substr(0, idx); 544 | } 545 | 546 | int localSocketRequest( 547 | const XPOSED_CMD requestCode, 548 | const void* payloadBuff, 549 | const int payloadLen, 550 | int* recvBuff, 551 | const size_t maxRecvLen) { 552 | 553 | // Socket 位于Linux抽象命名空间, 而不是文件路径 554 | // https://blog.csdn.net/howellzhu/article/details/111597734 555 | // https://blog.csdn.net/shanzhizi/article/details/16882087 一种是路径方式 一种是抽象命名空间 556 | constexpr int addrLen = 557 | offsetof(sockaddr_un, sun_path) + 21; // addrLen大小是 "\0FreezeitXposedServer" 的字符长度 558 | constexpr sockaddr_un srv_addr{ AF_UNIX, "\0FreezeitXposedServer" }; // 首位为空[0]=0,位于Linux抽象命名空间 559 | 560 | auto fd = socket(AF_UNIX, SOCK_STREAM, 0); 561 | if (fd < 0) 562 | return -10; 563 | 564 | if (connect(fd, (sockaddr*)&srv_addr, addrLen) < 0) { 565 | close(fd); 566 | return 0; 567 | } 568 | 569 | int header[2] = { static_cast(requestCode), payloadLen }; 570 | send(fd, header, sizeof(header), 0); 571 | 572 | int sendCnt = 0; 573 | while (sendCnt < payloadLen) { 574 | int len = send(fd, static_cast(payloadBuff) + sendCnt, 575 | static_cast(payloadLen - sendCnt), 0); 576 | if (len < 0) { 577 | close(fd); 578 | return -20; 579 | } 580 | sendCnt += len; 581 | } 582 | 583 | int recvLen = recv(fd, recvBuff, maxRecvLen, MSG_WAITALL); 584 | close(fd); 585 | return recvLen; 586 | } 587 | 588 | 589 | void printException( 590 | const char* versionStr, 591 | const int exceptionCnt, 592 | const char* exceptionBuf, 593 | size_t bufSize = 0) { 594 | 595 | if (bufSize == 0) 596 | bufSize = strlen(exceptionBuf); 597 | 598 | auto fp = fopen("/sdcard/Android/Frozen_crash.log", "ab"); 599 | if (!fp) return; 600 | 601 | auto timeStamp = time(nullptr); 602 | auto tm = localtime(&timeStamp); 603 | 604 | fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d] ", 605 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); 606 | 607 | if (versionStr)fprintf(fp, "[%s] ", versionStr); 608 | if (exceptionCnt) fprintf(fp, "第%d次异常 ", exceptionCnt); 609 | 610 | fwrite(exceptionBuf, 1, bufSize, fp); 611 | if (exceptionBuf[bufSize - 1] != '\n') 612 | fwrite("\n", 1, 1, fp); 613 | fclose(fp); 614 | } 615 | static int Is_Event(const struct dirent* Dir){ 616 | return strncmp("event", Dir->d_name, 5) == 0; 617 | } 618 | 619 | std::string GetTouchScreenDevice(){ 620 | struct dirent** namelist; 621 | int ndev = scandir("/dev/input", &namelist, Is_Event, alphasort); 622 | if(ndev <= 0){ 623 | return ""; 624 | } 625 | for(int i = 0; i < ndev; i++){ 626 | char fname[64]; 627 | int fd = -1; 628 | unsigned long keybit[NBITS(KEY_CNT)]; 629 | unsigned long propbit[INPUT_PROP_MAX]; 630 | snprintf(fname, sizeof(fname), "%s/%s", "/dev/input", namelist[i]->d_name); 631 | fd = open(fname, O_RDONLY | O_NONBLOCK); 632 | if(fd < 0){ 633 | continue; 634 | } 635 | memset(keybit, 0, sizeof(keybit)); 636 | memset(propbit, 0, sizeof(propbit)); 637 | ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit); 638 | ioctl(fd, EVIOCGPROP(INPUT_PROP_MAX), propbit); 639 | close(fd); 640 | free(namelist[i]); 641 | if(test_bit(propbit, INPUT_PROP_DIRECT) && (test_bit(keybit, BTN_TOUCH) || test_bit(keybit, BTN_TOOL_FINGER))){ 642 | return std::string(fname); 643 | } else if(test_bit(keybit, BTN_TOUCH) || test_bit(keybit, BTN_TOOL_FINGER)){ 644 | return std::string(fname); 645 | } 646 | } 647 | return ""; 648 | } 649 | 650 | void Init() { 651 | srand(std::chrono::system_clock::now().time_since_epoch().count()); 652 | usleep(200); //休眠 200ms 653 | 654 | // 防止进程多开 655 | char buf[256] = { 0 }; 656 | if (popenRead("pidof Frozen", buf, sizeof(buf)) == 0) { 657 | printException(nullptr, 0, "进程检测失败"); 658 | exit(-1); 659 | } 660 | 661 | auto ptr = strchr(buf, ' '); 662 | if (ptr) { // "pidNum1 pidNum2 ..." 如果存在多个pid就退出 663 | char tips[256]; 664 | auto len = snprintf(tips, sizeof(tips), 665 | "Frozen已经在运行(pid: %s), 当前进程(pid:%d)即将退出", buf, getpid()); 666 | printf("\n!!! \n!!! %s\n!!!\n\n", tips); 667 | printException(nullptr, 0, tips, len); 668 | exit(-2); 669 | } 670 | 671 | 672 | if (FORK_DOUBLE == 0) 673 | return; 674 | 675 | pid_t pid = fork(); 676 | 677 | if (pid < 0) { //创建失败 678 | printException(nullptr, 0, "脱离终端Fork失败"); 679 | exit(-1); 680 | } 681 | else if (pid > 0) { //父进程返回的是 子进程的pid 682 | exit(0);//父进程直接退出,然后子进程将由init托管 683 | } 684 | 685 | setsid();// 子进程 建立新会话 686 | umask(0); 687 | chdir("/"); 688 | 689 | // signal(SIGCHLD, SIG_IGN);//屏蔽SIGCHLD信号 通知内核对子进程的结束不关心,由内核回收 690 | int fd_response[2]; 691 | pipe(fd_response); 692 | 693 | pid = fork(); //成为守护进程后再次Fork, 父进程监控, 子进程工作 694 | if (pid < 0) { 695 | printException(nullptr, 0, "创建工作进程Fork失败"); 696 | exit(-1); 697 | } 698 | else if (pid > 0) { //父进程 监控子进程输出的异常信息,并写到异常日志 699 | close(fd_response[1]); // 1 关闭写端 700 | 701 | char versionStr[16] ="Unknown"; 702 | char exceptionBuf[4096] = {}; 703 | int exceptionCnt = 0; 704 | int zeroCnt = 0; 705 | 706 | while (true) { 707 | 708 | auto readLen = read(fd_response[0], exceptionBuf, sizeof(exceptionBuf)); 709 | if (readLen <= 0) { 710 | readLen = snprintf(exceptionBuf, 64, "[第%d次无效日志]", ++zeroCnt); 711 | } 712 | else if (!strncmp(exceptionBuf, "version ", 8)) { 713 | memcpy(versionStr, exceptionBuf + 8, sizeof(versionStr)); 714 | continue; 715 | } 716 | 717 | printException(versionStr, ++exceptionCnt, exceptionBuf, readLen); 718 | 719 | if (zeroCnt >= 3 || exceptionCnt >= 1000) { 720 | if (zeroCnt >= 3) 721 | printException(versionStr, 0, "工作进程已异常退出"); 722 | else 723 | printException(versionStr, 0, "工作进程已达最大异常次数, 即将强制关闭"); 724 | 725 | if (kill(pid, SIGKILL) < 0) { 726 | char tips[128]; 727 | auto len = snprintf(tips, sizeof(tips), "杀死 [工作进程 pid:%d] 失败", pid); 728 | printException(versionStr, 0, tips, len); 729 | } 730 | 731 | int status = 0; 732 | if (waitpid(pid, &status, __WALL) != pid) { 733 | char tips[128]; 734 | auto len = snprintf(tips, sizeof(tips), "waitpid 异常: [%d] HEX[%s]", status, 735 | bin2Hex(&status, 4).c_str()); 736 | printException(versionStr, 0, tips, len); 737 | } 738 | 739 | exit(-1); 740 | } 741 | } 742 | } 743 | 744 | //工作进程 745 | close(fd_response[0]); // 0 关闭读端 746 | 747 | // 标准输出和错误均指向父进程管道 748 | // dup2(fd_response[1], STDOUT_FILENO); // 把 system() shell 标准输出到异常日志 749 | dup2(fd_response[1], STDERR_FILENO); 750 | 751 | auto nullFd = open("/dev/null", O_RDWR); 752 | if (nullFd > 0) { 753 | dup2(nullFd, STDIN_FILENO); 754 | dup2(nullFd, STDOUT_FILENO); 755 | } 756 | else { 757 | close(STDIN_FILENO); 758 | close(STDOUT_FILENO); 759 | } 760 | } 761 | } 762 | 763 | 764 | namespace MAGISK { 765 | int get_version_code() { 766 | char buff[32] = { 0 }; 767 | Utils::popenRead("/system/bin/magisk -V", buff, sizeof(buff)); 768 | return isdigit(buff[0]) ? atoi(buff) : -1; 769 | } 770 | } 771 | namespace Apath { 772 | int get_version_code() { 773 | int version = -1; 774 | Utils::readString("/data/adb/ap/version"); 775 | return version; 776 | } 777 | } 778 | // https://github.com/tiann/KernelSU/blob/main/manager/app/src/main/cpp/ksu.cc 779 | namespace KSU { 780 | const int CMD_GRANT_ROOT = 0; 781 | const int CMD_BECOME_MANAGER = 1; 782 | const int CMD_GET_VERSION = 2; 783 | const int CMD_ALLOW_SU = 3; 784 | const int CMD_DENY_SU = 4; 785 | const int CMD_GET_ALLOW_LIST = 5; 786 | const int CMD_GET_DENY_LIST = 6; 787 | const int CMD_CHECK_SAFEMODE = 9; 788 | 789 | bool ksuctl(int cmd, void* arg1, void* arg2) { 790 | const uint32_t KERNEL_SU_OPTION{ 0xDEADBEEF }; 791 | uint32_t result = 0; 792 | prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); 793 | return result == KERNEL_SU_OPTION; 794 | } 795 | 796 | int get_version_code() { 797 | int version = -1; 798 | ksuctl(CMD_GET_VERSION, &version, nullptr); 799 | return version; 800 | } 801 | 802 | bool allow_su(uint64_t uid, bool allow) { 803 | return ksuctl(allow ? CMD_ALLOW_SU : CMD_DENY_SU, (void*)uid, nullptr); 804 | } 805 | 806 | bool get_allow_list(int* uids, int* size) { 807 | return ksuctl(CMD_GET_ALLOW_LIST, uids, size); 808 | } 809 | 810 | bool get_deny_list(int* uids, int* size) { 811 | return ksuctl(CMD_GET_DENY_LIST, uids, size); 812 | } 813 | 814 | bool is_safe_mode() { 815 | return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); 816 | } 817 | }; 818 | -------------------------------------------------------------------------------- /Frozen-Main/vpopen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * popen_noshell: A faster implementation of popen() and system() for Linux. 5 | * Copyright (c) 2009 Ivan Zahariev (famzah) 6 | * Version: 1.0 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as published by 10 | * the Free Software Foundation; under version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // _GNU_SOURCE must be defined as early as possible 22 | #ifndef _GNU_SOURCE 23 | #define _GNU_SOURCE 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // because of C++, we can't call err() or errx() within the child, because they call exit(), 33 | // and _exit() is what must be called; so we wrap 34 | #define _ERR(EVAL, FMT, ...) {warn(FMT, ##__VA_ARGS__); _exit(EVAL);} 35 | 36 | struct vpopenStruct { 37 | FILE *fp = nullptr; 38 | pid_t pid = 0; 39 | } pclose_arg; 40 | 41 | extern char **environ; 42 | 43 | namespace VPOPEN { 44 | 45 | int popen_noshell_reopen_fd_to_dev_null(int fd, posix_spawn_file_actions_t *file_actions) { 46 | if (posix_spawn_file_actions_addclose(file_actions, fd) != 0) return -1; 47 | if (posix_spawn_file_actions_addopen(file_actions, fd, "/dev/null", O_RDWR, 0600) < 0) 48 | return -1; 49 | return 0; 50 | } 51 | 52 | int _popen_noshell_close_and_dup(int pipefd[2], int closed_pipefd, int target_fd, 53 | posix_spawn_file_actions_t *file_actions) { 54 | int dupped_pipefd = (closed_pipefd == 0 ? 1 : 0); // get the FD of the other end of the pipe 55 | 56 | if (posix_spawn_file_actions_addclose(file_actions, pipefd[closed_pipefd]) != 0) return -1; 57 | if (posix_spawn_file_actions_addclose(file_actions, target_fd) != 0) return -1; 58 | if (posix_spawn_file_actions_adddup2(file_actions, pipefd[dupped_pipefd], target_fd) < 0) 59 | return -1; 60 | if (posix_spawn_file_actions_addclose(file_actions, pipefd[dupped_pipefd]) != 0) return -1; 61 | return 0; 62 | } 63 | 64 | // returns the new PID if called in POPEN_NOSHELL_MODE_POSIX_SPAWN 65 | // otherwise returns 0 66 | pid_t _popen_noshell_child_process(int pipefd_0, int pipefd_1, const char *file, 67 | const char *const *argv) { 68 | posix_spawn_file_actions_t file_actions_obj; 69 | if (posix_spawn_file_actions_init(&file_actions_obj) != 0) { 70 | _ERR(255, "posix_spawn_file_actions_init()"); 71 | } 72 | 73 | int closed_child_fd = STDIN_FILENO; /* re-open STDIN to /dev/null */ 74 | int closed_pipe_fd = 0; /* close read end of pipe */ 75 | int dupped_child_fd = STDOUT_FILENO; /* dup the other pipe end to STDOUT */ 76 | int pipefd[2] = {pipefd_0, pipefd_1}; 77 | 78 | if (popen_noshell_reopen_fd_to_dev_null(closed_child_fd, &file_actions_obj) != 0) { 79 | _ERR(255, "popen_noshell_reopen_fd_to_dev_null(%d)", closed_child_fd); 80 | } 81 | if (_popen_noshell_close_and_dup(pipefd, closed_pipe_fd, dupped_child_fd, 82 | &file_actions_obj) != 0) { 83 | _ERR(255, "_popen_noshell_close_and_dup(%d ,%d)", closed_pipe_fd, dupped_child_fd); 84 | } 85 | 86 | pid_t child_pid; 87 | if (posix_spawn(&child_pid, file, &file_actions_obj, nullptr, (char *const *) argv, 88 | environ) < 0) { 89 | warn("posix_spawn(\"%s\") inside the child", file); 90 | if (posix_spawn_file_actions_destroy(&file_actions_obj) != 0) { 91 | warn("posix_spawn_file_actions_destroy()"); 92 | } 93 | return 0; 94 | } 95 | if (posix_spawn_file_actions_destroy(&file_actions_obj) != 0) { 96 | warn("posix_spawn_file_actions_destroy()"); 97 | } 98 | return child_pid; 99 | } 100 | 101 | FILE *popen_noshell(const char *file, const char *const *argv) { 102 | int pipefd[2]; // 0 -> READ, 1 -> WRITE ends 103 | if (pipe2(pipefd, O_CLOEXEC) != 0) return nullptr; 104 | 105 | pid_t pid = _popen_noshell_child_process(pipefd[0], pipefd[1], file, argv); 106 | if (pid == 0) return nullptr; 107 | if (close(pipefd[1/*write*/]) != 0) return nullptr; 108 | 109 | auto fp = fdopen(pipefd[0/*read*/], "r"); 110 | if (fp) { 111 | pclose_arg.fp = fp; 112 | pclose_arg.pid = pid; 113 | } 114 | return fp; 115 | } 116 | 117 | int pclose_noshell() { 118 | int status = 0; 119 | if (fclose(pclose_arg.fp) != 0) return -1; 120 | if (waitpid(pclose_arg.pid, &status, __WALL) != pclose_arg.pid) return -2; 121 | return status; 122 | } 123 | 124 | void vpopen(const char *absPath, const char *argv[], char *buf, const size_t len) { 125 | auto fp = popen_noshell(absPath, (const char *const *) argv); 126 | if (!fp) { 127 | buf[0] = 0; 128 | fprintf(stderr, "%s() open 失败 [%d]:[%s]", __FUNCTION__, errno, strerror(errno)); 129 | return; 130 | } 131 | 132 | auto resLen = fread(buf, 1, len, fp); 133 | if (resLen <= 0) { 134 | buf[0] = 0; 135 | } else if (buf[resLen - 1] == '\n') { 136 | buf[resLen - 1] = 0; 137 | resLen--; 138 | } else { 139 | buf[resLen] = 0; 140 | } 141 | 142 | // https://man7.org/linux/man-pages/man2/waitpid.2.html 143 | // wait 的 status参数 只用了低 16位 144 | // 高8位 记录进程调用exit退出的状态(正常退出) 145 | // 低8位 记录进程接受到的信号 (非正常退出) 146 | auto status = pclose_noshell(); 147 | if (status < 0 || (status & 0xff)) 148 | fprintf(stderr, "%s() close 异常status[%d] [%d]:[%s]", __FUNCTION__, status, errno, 149 | strerror(errno)); 150 | } 151 | }; 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frozen 2 | 基于CoolApk@JARK006的冻它二改而来的墓碑
3 | 4 | 该改版希望在原版的基础上修改一个"性能" "稳定" "更多自定义功能" "流畅"的墓碑 5 | --------------------------------------------------------------------------------