Add TWRP app install via TWRP recovery

Note: I will have to add a build flag for excluding the app later for
watches and maybe Android TV. I will also have to add support in the
tablet and watch layouts later. I will merge this for the initial roll out
to select devices and finish up later.

Change-Id: Ia4ce5522fae542afa1539b10c0691315392a19ab
diff --git a/gui/action.cpp b/gui/action.cpp
index 8600186..fe57369 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -198,6 +198,7 @@
 		ADD_ACTION(checkpartitionlifetimewrites);
 		ADD_ACTION(mountsystemtoggle);
 		ADD_ACTION(setlanguage);
+		ADD_ACTION(checkforapp);
 
 		// remember actions that run in the caller thread
 		for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it)
@@ -229,6 +230,7 @@
 		ADD_ACTION(flashimage);
 		ADD_ACTION(twcmd);
 		ADD_ACTION(setbootslot);
+		ADD_ACTION(installapp);
 	}
 
 	// First, get the action
@@ -1879,3 +1881,157 @@
 	operation_end(0);
 	return 0;
 }
+
+int GUIAction::checkforapp(std::string arg __unused)
+{
+	int op_status = 1;
+	operation_start("Check for TWRP App");
+	if (!simulate)
+	{
+		string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
+		int sdkver = 0;
+		if (!sdkverstr.empty()) {
+			sdkver = atoi(sdkverstr.c_str());
+		}
+		if (sdkver <= 13) {
+			if (sdkver == 0)
+				LOGINFO("Unable to read sdk version from build prop\n");
+			else
+				LOGINFO("SDK version too low for TWRP app (%i < 14)\n", sdkver);
+			DataManager::SetValue("tw_app_install_status", 1); // 0 = no status, 1 = not installed, 2 = already installed
+			goto exit;
+		}
+		if (PartitionManager.Mount_By_Path("/system", false)) {
+			string base_path = "/system";
+			if (TWFunc::Path_Exists("/system/system"))
+				base_path += "/system"; // For devices with system as a root file system (e.g. Pixel)
+			string install_path = base_path + "/priv-app";
+			if (!TWFunc::Path_Exists(install_path))
+				install_path = base_path + "/app";
+			install_path += "/twrpapp";
+			if (TWFunc::Path_Exists(install_path)) {
+				LOGINFO("App found at '%s'\n", install_path.c_str());
+				DataManager::SetValue("tw_app_install_status", 2); // 0 = no status, 1 = not installed, 2 = already installed
+				goto exit;
+			}
+		} else if (PartitionManager.Mount_By_Path("/data", false)) {
+			string parent_path = "/data/app";
+			DIR *d = opendir("/data/app");
+			struct dirent *p;
+			int len = strlen("me.twrp.twrpapp-");
+			while ((p = readdir(d))) {
+				if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+					continue;
+				if (p->d_type == DT_DIR && strlen(p->d_name) >= len && strncmp(p->d_name, "me.twrp.twrpapp-", len) == 0) {
+					LOGINFO("App found at %s/%s\n", parent_path.c_str(), p->d_name);
+					closedir(d);
+					DataManager::SetValue("tw_app_install_status", 2); // 0 = no status, 1 = not installed, 2 = already installed
+					goto exit;
+				}
+			}
+			closedir(d);
+		}
+	} else
+		simulate_progress_bar();
+	LOGINFO("App not installed\n");
+	DataManager::SetValue("tw_app_install_status", 1); // 0 = no status, 1 = not installed, 2 = already installed
+exit:
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::installapp(std::string arg __unused)
+{
+	int op_status = 1;
+	operation_start("Install TWRP App");
+	if (!simulate)
+	{
+		if (DataManager::GetIntValue("tw_mount_system_ro") > 0 || DataManager::GetIntValue("tw_app_install_system") == 0) {
+			if (PartitionManager.Mount_By_Path("/data", true)) {
+				string install_path = "/data/app";
+				string context = "u:object_r:apk_data_file:s0";
+				if (!TWFunc::Path_Exists(install_path)) {
+					if (mkdir(install_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
+						LOGERR("Error making %s directory: %s\n", install_path.c_str(), strerror(errno));
+						goto exit;
+					}
+					if (chown(install_path.c_str(), 1000, 1000)) {
+						LOGERR("chown %s error: %s\n", install_path.c_str(), strerror(errno));
+						goto exit;
+					}
+					if (setfilecon(install_path.c_str(), context.c_str()) < 0) {
+						LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno));
+						goto exit;
+					}
+				}
+				install_path += "/me.twrp.twrpapp-1";
+				if (mkdir(install_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
+					LOGERR("Error making %s directory: %s\n", install_path.c_str(), strerror(errno));
+					goto exit;
+				}
+				if (chown(install_path.c_str(), 1000, 1000)) {
+					LOGERR("chown %s error: %s\n", install_path.c_str(), strerror(errno));
+					goto exit;
+				}
+				if (setfilecon(install_path.c_str(), context.c_str()) < 0) {
+					LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno));
+					goto exit;
+				}
+				install_path += "/base.apk";
+				if (TWFunc::copy_file("/sbin/me.twrp.twrpapp.apk", install_path, 0644)) {
+					LOGERR("Error copying apk file\n");
+					goto exit;
+				}
+				if (chown(install_path.c_str(), 1000, 1000)) {
+					LOGERR("chown %s error: %s\n", install_path.c_str(), strerror(errno));
+					goto exit;
+				}
+				if (setfilecon(install_path.c_str(), context.c_str()) < 0) {
+					LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno));
+					goto exit;
+				}
+				sync();
+				sync();
+			}
+		} else {
+			if (PartitionManager.Mount_By_Path("/system", true)) {
+				string base_path = "/system";
+				if (TWFunc::Path_Exists("/system/system"))
+					base_path += "/system"; // For devices with system as a root file system (e.g. Pixel)
+				string install_path = base_path + "/priv-app";
+				string context = "u:object_r:system_file:s0";
+				if (!TWFunc::Path_Exists(install_path))
+					install_path = base_path + "/app";
+				if (TWFunc::Path_Exists(install_path)) {
+					install_path += "/twrpapp";
+					LOGINFO("Installing app to '%s'\n", install_path.c_str());
+					if (mkdir(install_path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0) {
+						if (setfilecon(install_path.c_str(), context.c_str()) < 0) {
+							LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno));
+							goto exit;
+						}
+						install_path += "/me.twrp.twrpapp.apk";
+						if (TWFunc::copy_file("/sbin/me.twrp.twrpapp.apk", install_path, 0644)) {
+							LOGERR("Error copying apk file\n");
+							goto exit;
+						}
+						if (setfilecon(install_path.c_str(), context.c_str()) < 0) {
+							LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno));
+							goto exit;
+						}
+						sync();
+						sync();
+						PartitionManager.UnMount_By_Path("/system", true);
+						op_status = 0;
+					} else {
+						LOGERR("Error making app directory '%s': %s\n", strerror(errno));
+					}
+				}
+			}
+		}
+	} else
+		simulate_progress_bar();
+exit:
+	operation_end(0);
+	return 0;
+}