// image.cpp - GUIImage object #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../partitions.hpp" #include "../twrp-functions.hpp" #include "../openrecoveryscript.hpp" #include "../ui.h" #include "../adb_install.h" extern "C" { #include "../common.h" #include "../minuitwrp/minui.h" #include "../recovery_ui.h" #include "../variables.h" #include "../twinstall.h" #include "../minadbd/adb.h" int TWinstall_zip(const char* path, int* wipe_cache); void run_script(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5, const char *str6, const char *str7, int request_confirm); int gui_console_only(); int gui_start(); }; #include "rapidxml.hpp" #include "objects.hpp" extern RecoveryUI* ui; void curtainClose(void); GUIAction::GUIAction(xml_node<>* node) : Conditional(node) { xml_node<>* child; xml_node<>* actions; xml_attribute<>* attr; mKey = 0; if (!node) return; // First, get the action actions = node->first_node("actions"); if (actions) child = actions->first_node("action"); else child = node->first_node("action"); if (!child) return; while (child) { Action action; attr = child->first_attribute("function"); if (!attr) return; action.mFunction = attr->value(); action.mArg = child->value(); mActions.push_back(action); child = child->next_sibling("action"); } // Now, let's get either the key or region child = node->first_node("touch"); if (child) { attr = child->first_attribute("key"); if (attr) { std::string key = attr->value(); mKey = getKeyByName(key); } else { attr = child->first_attribute("x"); if (!attr) return; mActionX = atol(attr->value()); attr = child->first_attribute("y"); if (!attr) return; mActionY = atol(attr->value()); attr = child->first_attribute("w"); if (!attr) return; mActionW = atol(attr->value()); attr = child->first_attribute("h"); if (!attr) return; mActionH = atol(attr->value()); } } } int GUIAction::NotifyTouch(TOUCH_STATE state, int x, int y) { if (state == TOUCH_RELEASE) doActions(); return 0; } int GUIAction::NotifyKey(int key) { if (!mKey || key != mKey) return 1; doActions(); return 0; } int GUIAction::NotifyVarChange(std::string varName, std::string value) { if (varName.empty() && !isConditionValid() && !mKey && !mActionW) doActions(); // This handles notifying the condition system of page start if (varName.empty() && isConditionValid()) NotifyPageSet(); if ((varName.empty() || IsConditionVariable(varName)) && isConditionValid() && isConditionTrue()) doActions(); return 0; } void GUIAction::simulate_progress_bar(void) { ui_print("Simulating actions...\n"); for (int i = 0; i < 5; i++) { usleep(500000); DataManager::SetValue("ui_progress", i * 20); } } int GUIAction::flash_zip(std::string filename, std::string pageName, const int simulate, int* wipe_cache) { int ret_val = 0; DataManager::SetValue("ui_progress", 0); if (filename.empty()) { LOGE("No file specified.\n"); return -1; } // We're going to jump to this page first, like a loading page gui_changePage(pageName); int fd = -1; ZipArchive zip; if (!PartitionManager.Mount_By_Path(filename, true)) return -1; if (mzOpenZipArchive(filename.c_str(), &zip)) { LOGE("Unable to open zip file.\n"); return -1; } // Check the zip to see if it has a custom installer theme const ZipEntry* twrp = mzFindZipEntry(&zip, "META-INF/teamwin/twrp.zip"); if (twrp != NULL) { unlink("/tmp/twrp.zip"); fd = creat("/tmp/twrp.zip", 0666); } if (fd >= 0 && twrp != NULL && mzExtractZipEntryToFile(&zip, twrp, fd) && !PageManager::LoadPackage("install", "/tmp/twrp.zip", "main")) { mzCloseZipArchive(&zip); PageManager::SelectPackage("install"); gui_changePage("main"); } else { // In this case, we just use the default page mzCloseZipArchive(&zip); gui_changePage(pageName); } if (fd >= 0) close(fd); if (simulate) { simulate_progress_bar(); } else { ret_val = TWinstall_zip(filename.c_str(), wipe_cache); // Now, check if we need to ensure TWRP remains installed... struct stat st; if (stat("/sbin/installTwrp", &st) == 0) { DataManager::SetValue("tw_operation", "Configuring TWRP"); DataManager::SetValue("tw_partition", ""); ui_print("Configuring TWRP...\n"); if (system("/sbin/installTwrp reinstall") < 0) { ui_print("Unable to configure TWRP with this kernel.\n"); } } } // Done DataManager::SetValue("ui_progress", 100); DataManager::SetValue("ui_progress", 0); return ret_val; } int GUIAction::doActions() { if (mActions.size() < 1) return -1; if (mActions.size() == 1) return doAction(mActions.at(0), 0); // For multi-action, we always use a thread pthread_t t; pthread_create(&t, NULL, thread_start, this); return 0; } void* GUIAction::thread_start(void *cookie) { GUIAction* ourThis = (GUIAction*) cookie; DataManager::SetValue(TW_ACTION_BUSY, 1); if (ourThis->mActions.size() > 1) { std::vector::iterator iter; for (iter = ourThis->mActions.begin(); iter != ourThis->mActions.end(); iter++) ourThis->doAction(*iter, 1); } else { ourThis->doAction(ourThis->mActions.at(0), 1); } int check = 0; DataManager::GetValue("tw_background_thread_running", check); if (check == 0) DataManager::SetValue(TW_ACTION_BUSY, 0); return NULL; } void GUIAction::operation_start(const string operation_name) { DataManager::SetValue(TW_ACTION_BUSY, 1); DataManager::SetValue("ui_progress", 0); DataManager::SetValue("tw_operation", operation_name); DataManager::SetValue("tw_operation_status", 0); DataManager::SetValue("tw_operation_state", 0); } void GUIAction::operation_end(const int operation_status, const int simulate) { int simulate_fail; DataManager::SetValue("ui_progress", 100); if (simulate) { DataManager::GetValue(TW_SIMULATE_FAIL, simulate_fail); if (simulate_fail != 0) DataManager::SetValue("tw_operation_status", 1); else DataManager::SetValue("tw_operation_status", 0); } else { if (operation_status != 0) DataManager::SetValue("tw_operation_status", 1); else DataManager::SetValue("tw_operation_status", 0); } DataManager::SetValue("tw_operation_state", 1); DataManager::SetValue(TW_ACTION_BUSY, 0); } int GUIAction::doAction(Action action, int isThreaded /* = 0 */) { static string zip_queue[10]; static int zip_queue_index; static pthread_t terminal_command; int simulate; std::string arg = gui_parse_text(action.mArg); std::string function = gui_parse_text(action.mFunction); DataManager::GetValue(TW_SIMULATE_ACTIONS, simulate); if (function == "reboot") { //curtainClose(); this sometimes causes a crash sync(); if (arg == "recovery") TWFunc::tw_reboot(rb_recovery); else if (arg == "poweroff") TWFunc::tw_reboot(rb_poweroff); else if (arg == "bootloader") TWFunc::tw_reboot(rb_bootloader); else if (arg == "download") TWFunc::tw_reboot(rb_download); else TWFunc::tw_reboot(rb_system); // This should never occur return -1; } if (function == "home") { PageManager::SelectPackage("TWRP"); gui_changePage("main"); return 0; } if (function == "key") { PageManager::NotifyKey(getKeyByName(arg)); return 0; } if (function == "page") { std::string page_name = gui_parse_text(arg); return gui_changePage(page_name); } if (function == "reload") { int check = 0, ret_val = 0; std::string theme_path; operation_start("Reload Theme"); theme_path = DataManager::GetSettingsStoragePath(); if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) { LOGE("Unable to mount %s during reload function startup.\n", theme_path.c_str()); check = 1; } theme_path += "/TWRP/theme/ui.zip"; if (check != 0 || PageManager::ReloadPackage("TWRP", theme_path) != 0) { // Loading the custom theme failed - try loading the stock theme LOGI("Attempting to reload stock theme...\n"); if (PageManager::ReloadPackage("TWRP", "/res/ui.xml")) { LOGE("Failed to load base packages.\n"); ret_val = 1; } } operation_end(ret_val, simulate); } if (function == "readBackup") { string Restore_Name; DataManager::GetValue("tw_restore", Restore_Name); PartitionManager.Set_Restore_Files(Restore_Name); return 0; } if (function == "set") { if (arg.find('=') != string::npos) { string varName = arg.substr(0, arg.find('=')); string value = arg.substr(arg.find('=') + 1, string::npos); DataManager::GetValue(value, value); DataManager::SetValue(varName, value); } else DataManager::SetValue(arg, "1"); return 0; } if (function == "clear") { DataManager::SetValue(arg, "0"); return 0; } if (function == "mount") { if (arg == "usb") { DataManager::SetValue(TW_ACTION_BUSY, 1); if (!simulate) PartitionManager.usb_storage_enable(); else ui_print("Simulating actions...\n"); } else if (!simulate) { string cmd; if (arg == "EXTERNAL") PartitionManager.Mount_By_Path(DataManager::GetStrValue(TW_EXTERNAL_MOUNT), true); else if (arg == "INTERNAL") PartitionManager.Mount_By_Path(DataManager::GetStrValue(TW_INTERNAL_MOUNT), true); else PartitionManager.Mount_By_Path(arg, true); } else ui_print("Simulating actions...\n"); return 0; } if (function == "umount" || function == "unmount") { if (arg == "usb") { if (!simulate) PartitionManager.usb_storage_disable(); else ui_print("Simulating actions...\n"); DataManager::SetValue(TW_ACTION_BUSY, 0); } else if (!simulate) { string cmd; if (arg == "EXTERNAL") PartitionManager.UnMount_By_Path(DataManager::GetStrValue(TW_EXTERNAL_MOUNT), true); else if (arg == "INTERNAL") PartitionManager.UnMount_By_Path(DataManager::GetStrValue(TW_INTERNAL_MOUNT), true); else PartitionManager.UnMount_By_Path(arg, true); } else ui_print("Simulating actions...\n"); return 0; } if (function == "restoredefaultsettings") { operation_start("Restore Defaults"); if (simulate) // Simulated so that people don't accidently wipe out the "simulation is on" setting ui_print("Simulating actions...\n"); else { DataManager::ResetDefaults(); PartitionManager.Update_System_Details(); PartitionManager.Mount_Current_Storage(true); } operation_end(0, simulate); } if (function == "copylog") { operation_start("Copy Log"); if (!simulate) { char command[255]; PartitionManager.Mount_Current_Storage(true); sprintf(command, "cp /tmp/recovery.log %s", DataManager::GetCurrentStoragePath().c_str()); system(command); sync(); ui_print("Copied recovery log to %s.\n", DataManager::GetCurrentStoragePath().c_str()); } else simulate_progress_bar(); operation_end(0, simulate); return 0; } if (function == "compute" || function == "addsubtract") { if (arg.find("+") != string::npos) { string varName = arg.substr(0, arg.find('+')); string string_to_add = arg.substr(arg.find('+') + 1, string::npos); int amount_to_add = atoi(string_to_add.c_str()); int value; DataManager::GetValue(varName, value); DataManager::SetValue(varName, value + amount_to_add); return 0; } if (arg.find("-") != string::npos) { string varName = arg.substr(0, arg.find('-')); string string_to_subtract = arg.substr(arg.find('-') + 1, string::npos); int amount_to_subtract = atoi(string_to_subtract.c_str()); int value; DataManager::GetValue(varName, value); value -= amount_to_subtract; if (value <= 0) value = 0; DataManager::SetValue(varName, value); return 0; } } if (function == "setguitimezone") { string SelectedZone; DataManager::GetValue(TW_TIME_ZONE_GUISEL, SelectedZone); // read the selected time zone into SelectedZone string Zone = SelectedZone.substr(0, SelectedZone.find(';')); // parse to get time zone string DSTZone = SelectedZone.substr(SelectedZone.find(';') + 1, string::npos); // parse to get DST component int dst; DataManager::GetValue(TW_TIME_ZONE_GUIDST, dst); // check wether user chose to use DST string offset; DataManager::GetValue(TW_TIME_ZONE_GUIOFFSET, offset); // pull in offset string NewTimeZone = Zone; if (offset != "0") NewTimeZone += ":" + offset; if (dst != 0) NewTimeZone += DSTZone; DataManager::SetValue(TW_TIME_ZONE_VAR, NewTimeZone); DataManager::update_tz_environment_variables(); return 0; } if (function == "togglestorage") { if (arg == "internal") { DataManager::SetValue(TW_USE_EXTERNAL_STORAGE, 0); } else if (arg == "external") { DataManager::SetValue(TW_USE_EXTERNAL_STORAGE, 1); } if (PartitionManager.Mount_Current_Storage(true)) { if (arg == "internal") { string zip_path, zip_root; DataManager::GetValue(TW_ZIP_INTERNAL_VAR, zip_path); zip_root = TWFunc::Get_Root_Path(zip_path); #ifdef RECOVERY_SDCARD_ON_DATA #ifndef TW_EXTERNAL_STORAGE_PATH if (zip_root != "/sdcard") DataManager::SetValue(TW_ZIP_INTERNAL_VAR, "/sdcard"); #else if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) { if (zip_root != "/emmc") DataManager::SetValue(TW_ZIP_INTERNAL_VAR, "/emmc"); } else { if (zip_root != "/sdcard") DataManager::SetValue(TW_ZIP_INTERNAL_VAR, "/sdcard"); } #endif #else if (zip_root != DataManager::GetCurrentStoragePath()) DataManager::SetValue(TW_ZIP_LOCATION_VAR, DataManager::GetCurrentStoragePath()); #endif // Save the current zip location to the external variable DataManager::SetValue(TW_ZIP_EXTERNAL_VAR, DataManager::GetStrValue(TW_ZIP_LOCATION_VAR)); // Change the current zip location to the internal variable DataManager::SetValue(TW_ZIP_LOCATION_VAR, DataManager::GetStrValue(TW_ZIP_INTERNAL_VAR)); } else if (arg == "external") { string zip_path, zip_root; DataManager::GetValue(TW_ZIP_EXTERNAL_VAR, zip_path); zip_root = TWFunc::Get_Root_Path(zip_path); if (zip_root != DataManager::GetCurrentStoragePath()) { DataManager::SetValue(TW_ZIP_EXTERNAL_VAR, DataManager::GetCurrentStoragePath()); } // Save the current zip location to the internal variable DataManager::SetValue(TW_ZIP_INTERNAL_VAR, DataManager::GetStrValue(TW_ZIP_LOCATION_VAR)); // Change the current zip location to the external variable DataManager::SetValue(TW_ZIP_LOCATION_VAR, DataManager::GetStrValue(TW_ZIP_EXTERNAL_VAR)); } } else { // We weren't able to toggle for some reason, restore original setting if (arg == "internal") { DataManager::SetValue(TW_USE_EXTERNAL_STORAGE, 1); } else if (arg == "external") { DataManager::SetValue(TW_USE_EXTERNAL_STORAGE, 0); } } return 0; } if (function == "overlay") return gui_changeOverlay(arg); if (function == "queuezip") { if (zip_queue_index >= 10) { ui_print("Maximum zip queue reached!\n"); return 0; } DataManager::GetValue("tw_filename", zip_queue[zip_queue_index]); if (strlen(zip_queue[zip_queue_index].c_str()) > 0) { zip_queue_index++; DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index); } return 0; } if (function == "cancelzip") { if (zip_queue_index <= 0) { ui_print("Minimum zip queue reached!\n"); return 0; } else { zip_queue_index--; DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index); } return 0; } if (function == "queueclear") { zip_queue_index = 0; DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index); return 0; } if (function == "sleep") { operation_start("Sleep"); usleep(atoi(arg.c_str())); operation_end(0, simulate); return 0; } if (isThreaded) { if (function == "fileexists") { struct stat st; string newpath = arg + "/."; operation_start("FileExists"); if (stat(arg.c_str(), &st) == 0 || stat(newpath.c_str(), &st) == 0) operation_end(0, simulate); else operation_end(1, simulate); } if (function == "flash") { int i, ret_val = 0, wipe_cache = 0; for (i=0; i&1";; LOGI("Actual command is: '%s'\n", command.c_str()); DataManager::SetValue("tw_terminal_command_thread", command); DataManager::SetValue("tw_terminal_state", 1); DataManager::SetValue("tw_background_thread_running", 1); op_status = pthread_create(&terminal_command, NULL, command_thread, NULL); if (op_status != 0) { LOGE("Error starting terminal command thread, %i.\n", op_status); DataManager::SetValue("tw_terminal_state", 0); DataManager::SetValue("tw_background_thread_running", 0); operation_end(1, simulate); } } return 0; } if (function == "killterminal") { int op_status = 0; LOGI("Sending kill command...\n"); operation_start("KillCommand"); DataManager::SetValue("tw_operation_status", 0); DataManager::SetValue("tw_operation_state", 1); DataManager::SetValue("tw_terminal_state", 0); DataManager::SetValue("tw_background_thread_running", 0); DataManager::SetValue(TW_ACTION_BUSY, 0); return 0; } if (function == "reinjecttwrp") { int op_status = 0; operation_start("ReinjectTWRP"); ui_print("Injecting TWRP into boot image...\n"); if (simulate) { simulate_progress_bar(); } else { system("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash"); ui_print("TWRP injection complete.\n"); } operation_end(op_status, simulate); return 0; } if (function == "checkbackupname") { int op_status = 0; operation_start("CheckBackupName"); if (simulate) { simulate_progress_bar(); } else { op_status = PartitionManager.Check_Backup_Name(true); if (op_status != 0) op_status = 1; } operation_end(op_status, simulate); return 0; } if (function == "decrypt") { int op_status = 0; operation_start("Decrypt"); if (simulate) { simulate_progress_bar(); } else { string Password; DataManager::GetValue("tw_crypto_password", Password); op_status = PartitionManager.Decrypt_Device(Password); if (op_status != 0) op_status = 1; else { int load_theme = 1; DataManager::SetValue(TW_IS_ENCRYPTED, 0); DataManager::ReadSettingsFile(); TWFunc::Output_Version(); if (OpenRecoveryScript::check_for_script_file()) { ui_print("Processing OpenRecoveryScript file...\n"); if (OpenRecoveryScript::run_script_file() == 0) { usleep(2000000); // Sleep for 2 seconds before rebooting TWFunc::tw_reboot(rb_system); load_theme = 0; } } if (load_theme) { int has_datamedia; // Check for a custom theme and load it if exists DataManager::GetValue(TW_HAS_DATA_MEDIA, has_datamedia); if (has_datamedia != 0) { struct stat st; int check = 0; std::string theme_path; theme_path = DataManager::GetSettingsStoragePath(); if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) { LOGE("Unable to mount %s during reload function startup.\n", theme_path.c_str()); check = 1; } theme_path += "/TWRP/theme/ui.zip"; if (check == 0 && stat(theme_path.c_str(), &st) == 0) { if (PageManager::ReloadPackage("TWRP", theme_path) != 0) { // Loading the custom theme failed - try loading the stock theme LOGI("Attempting to reload stock theme...\n"); if (PageManager::ReloadPackage("TWRP", "/res/ui.xml")) { LOGE("Failed to load base packages.\n"); } } } } } } } operation_end(op_status, simulate); return 0; } if (function == "adbsideload") { int ret = 0; operation_start("Sideload"); if (simulate) { simulate_progress_bar(); } else { int wipe_cache = 0; string Command, Sideload_File; if (!PartitionManager.Mount_Current_Storage(true)) { operation_end(1, simulate); return 0; } Sideload_File = DataManager::GetCurrentStoragePath() + "/sideload.zip"; if (TWFunc::Path_Exists(Sideload_File)) { Command = "rm " + Sideload_File; system(Command.c_str()); } ui_print("Starting ADB sideload feature...\n"); ret = apply_from_adb(ui, &wipe_cache, Sideload_File.c_str()); if (ret != 0) ret = 1; // failure else if (wipe_cache) PartitionManager.Wipe_By_Path("/cache"); } operation_end(ret, simulate); return 0; } if (function == "adbsideloadcancel") { int child_pid; string Command, Sideload_File; Sideload_File = DataManager::GetCurrentStoragePath() + "/sideload.zip"; Command = "rm " + Sideload_File; system(Command.c_str()); DataManager::GetValue("tw_child_pid", child_pid); ui_print("Cancelling ADB sideload...\n"); kill(child_pid, SIGTERM); return 0; } } else { pthread_t t; pthread_create(&t, NULL, thread_start, this); return 0; } return -1; } int GUIAction::getKeyByName(std::string key) { if (key == "home") return KEY_HOME; else if (key == "menu") return KEY_MENU; else if (key == "back") return KEY_BACK; else if (key == "search") return KEY_SEARCH; else if (key == "voldown") return KEY_VOLUMEDOWN; else if (key == "volup") return KEY_VOLUMEUP; else if (key == "power") { int ret_val; DataManager::GetValue(TW_POWER_BUTTON, ret_val); if (!ret_val) return KEY_POWER; else return ret_val; } return atol(key.c_str()); } void* GUIAction::command_thread(void *cookie) { string command; FILE* fp; char line[512]; DataManager::GetValue("tw_terminal_command_thread", command); fp = popen(command.c_str(), "r"); if (fp == NULL) { LOGE("Error opening command to run.\n"); } else { int fd = fileno(fp), has_data = 0, check = 0, keep_going = -1, bytes_read = 0; struct timeval timeout; fd_set fdset; while(keep_going) { FD_ZERO(&fdset); FD_SET(fd, &fdset); timeout.tv_sec = 0; timeout.tv_usec = 400000; has_data = select(fd+1, &fdset, NULL, NULL, &timeout); if (has_data == 0) { // Timeout reached DataManager::GetValue("tw_terminal_state", check); if (check == 0) { keep_going = 0; } } else if (has_data < 0) { // End of execution keep_going = 0; } else { // Try to read output memset(line, 0, sizeof(line)); bytes_read = read(fd, line, sizeof(line)); if (bytes_read > 0) ui_print("%s", line); // Display output else keep_going = 0; // Done executing } } fclose(fp); } DataManager::SetValue("tw_operation_status", 0); DataManager::SetValue("tw_operation_state", 1); DataManager::SetValue("tw_terminal_state", 0); DataManager::SetValue("tw_background_thread_running", 0); DataManager::SetValue(TW_ACTION_BUSY, 0); return NULL; }