From 0de1453aca42808b74715e85d7770b5826d80d47 Mon Sep 17 00:00:00 2001 From: alufers Date: Sat, 25 Mar 2023 16:34:10 +0100 Subject: [PATCH] Improve error dialog, add clear data button, make unziup and 7zip work on armhf, add fallback to unzip --- .../octo4a/repository/BootstrapRepository.kt | 200 +++++++++--------- .../repository/OctoPrintHandlerRepository.kt | 23 +- .../com/octo4a/ui/InstallationActivity.kt | 21 ++ .../com/octo4a/ui/fragments/ServerFragment.kt | 1 - .../java/com/octo4a/utils/ProcessUtils.kt | 13 ++ .../layout/activity_installation_progress.xml | 15 +- app/app/src/main/res/values/strings.xml | 1 + 7 files changed, 168 insertions(+), 106 deletions(-) diff --git a/app/app/src/main/java/com/octo4a/repository/BootstrapRepository.kt b/app/app/src/main/java/com/octo4a/repository/BootstrapRepository.kt index 5a7f141f..7e4a4c97 100644 --- a/app/app/src/main/java/com/octo4a/repository/BootstrapRepository.kt +++ b/app/app/src/main/java/com/octo4a/repository/BootstrapRepository.kt @@ -60,126 +60,134 @@ class BootstrapRepositoryImpl( } override suspend fun setupBootstrap() { - withContext(Dispatchers.IO) { - val PREFIX_FILE = File(PREFIX_PATH) - if (PREFIX_FILE.isDirectory) { - return@withContext - } + withContext(Dispatchers.IO) { + val PREFIX_FILE = File(PREFIX_PATH) + if (PREFIX_FILE.isDirectory) { + return@withContext + } - try { - val bootstrapReleases = - githubRepository.getNewestReleases("feelfreelinux/android-linux-bootstrap") - val arch = getArchString() + try { + val bootstrapReleases = + githubRepository.getNewestReleases("feelfreelinux/android-linux-bootstrap") + val arch = getArchString() - val release = bootstrapReleases.firstOrNull { - it.assets.any { asset -> asset.name.contains(arch) } - } + val release = bootstrapReleases.firstOrNull { + it.assets.any { asset -> asset.name.contains(arch) } + } - val asset = release?.assets?.first { asset -> asset.name.contains(arch) } - logger.log(this) { "Arch: $arch" } + val asset = release?.assets?.first { asset -> asset.name.contains(arch) } + logger.log(this) { "Arch: $arch" } - val STAGING_PREFIX_PATH = "${FILES_PATH}/bootstrap-staging" - val STAGING_PREFIX_FILE = File(STAGING_PREFIX_PATH) + val STAGING_PREFIX_PATH = "${FILES_PATH}/bootstrap-staging" + val STAGING_PREFIX_FILE = File(STAGING_PREFIX_PATH) - if (STAGING_PREFIX_FILE.exists()) { - deleteFolder(STAGING_PREFIX_FILE) - } + if (STAGING_PREFIX_FILE.exists()) { + deleteFolder(STAGING_PREFIX_FILE) + } - val buffer = ByteArray(8096) - val symlinks = ArrayList>(50) + val buffer = ByteArray(8096) + val symlinks = ArrayList>(50) - val urlPrefix = asset!!.browserDownloadUrl - logger.log(this) { "Downloading bootstrap ${release?.tagName} from ${urlPrefix}" } + val urlPrefix = asset!!.browserDownloadUrl + logger.log(this) { "Downloading bootstrap ${release?.tagName} from ${urlPrefix}" } - val sslcontext = SSLContext.getInstance("TLSv1") - sslcontext.init(null, null, null) - val noSSLv3Factory: SSLSocketFactory = TLSSocketFactory() + val sslcontext = SSLContext.getInstance("TLSv1") + sslcontext.init(null, null, null) + val noSSLv3Factory: SSLSocketFactory = TLSSocketFactory() - HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory) - val connection: HttpsURLConnection = - URL(urlPrefix).openConnection() as HttpsURLConnection - connection.sslSocketFactory = noSSLv3Factory - connection.connect() - val code = connection.responseCode - logger.log(this) { "Request to ${connection.url} returned status code $code" } - if (code > 399) { + HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory) + val connection: HttpsURLConnection = + URL(urlPrefix).openConnection() as HttpsURLConnection + connection.sslSocketFactory = noSSLv3Factory + connection.connect() + val code = connection.responseCode + logger.log(this) { "Request to ${connection.url} returned status code $code" } + if (code > 399) { - throw RuntimeException( - "Fetching ${connection.url} failed with status code $code" - ) - } - ZipInputStream(connection.inputStream).use { zipInput -> - var zipEntry = zipInput.nextEntry - while (zipEntry != null) { - - val zipEntryName = zipEntry.name - val targetFile = File(STAGING_PREFIX_PATH, zipEntryName) - val isDirectory = zipEntry.isDirectory - - ensureDirectoryExists(if (isDirectory) targetFile else targetFile.parentFile) - - if (!isDirectory) { - FileOutputStream(targetFile).use { outStream -> - var readBytes = zipInput.read(buffer) - while ((readBytes) != -1) { - outStream.write(buffer, 0, readBytes) - readBytes = zipInput.read(buffer) - } + throw RuntimeException( + "Fetching ${connection.url} failed with status code $code" + ) + } + ZipInputStream(connection.inputStream).use { zipInput -> + var zipEntry = zipInput.nextEntry + while (zipEntry != null) { + + val zipEntryName = zipEntry.name + val targetFile = File(STAGING_PREFIX_PATH, zipEntryName) + val isDirectory = zipEntry.isDirectory + + ensureDirectoryExists(if (isDirectory) targetFile else targetFile.parentFile) + + if (!isDirectory) { + FileOutputStream(targetFile).use { outStream -> + var readBytes = zipInput.read(buffer) + while ((readBytes) != -1) { + outStream.write(buffer, 0, readBytes) + readBytes = zipInput.read(buffer) } } - zipEntry = zipInput.nextEntry } + zipEntry = zipInput.nextEntry } + } - if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) { - throw RuntimeException("Unable to rename staging folder") - } - logger.log(this) { "Bootstrap extracted, setting it up..." } - runCommand("ls", prooted = false).waitAndPrintOutput(logger) - runCommand("chmod -R 700 .", prooted = false).waitAndPrintOutput(logger) - if (shouldUsePre5Bootstrap()) { - runCommand( - "rm -r root && mv root-pre5 root", - prooted = false - ).waitAndPrintOutput(logger) - } - + if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) { + throw RuntimeException("Unable to rename staging folder") + } + logger.log(this) { "Bootstrap extracted, setting it up..." } + runCommand("ls", prooted = false).waitAndPrintOutput(logger) + runCommand("chmod -R 700 .", prooted = false).waitAndPrintOutput(logger) + if (shouldUsePre5Bootstrap()) { runCommand( - "sh install-bootstrap.sh", + "rm -r root && mv root-pre5 root", prooted = false ).waitAndPrintOutput(logger) - runCommand("sh add-user.sh octoprint", prooted = false).waitAndPrintOutput( - logger - ) - runCommand("cat /etc/motd").waitAndPrintOutput(logger) - runCommand("env").waitAndPrintOutput(logger) - runCommand("ls /").waitAndPrintOutput(logger) + } - // Setup ssh + runCommand( + "sh install-bootstrap.sh", + prooted = false + ).waitAndPrintOutput(logger) + runCommand("sh add-user.sh octoprint", prooted = false).waitAndPrintOutput( + logger + ) + runCommand("cat /etc/motd").waitAndPrintOutput(logger) + runCommand("env").waitAndPrintOutput(logger) + runCommand("ls /").waitAndPrintOutput(logger) + + // Setup ssh + runCommand( + "apk add openssh-server curl bash unzip", + bash = false + ).waitAndPrintOutput(logger) + runCommand("echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config").waitAndPrintOutput( + logger + ) + runCommand("ssh-keygen -A").waitAndPrintOutput(logger) + + logger.log(this) { "Installing p7zip..." } + + try { runCommand( - "apk add openssh-server curl bash unzip", + "apk add p7zip", + bash = false + ).waitAndPrintOutput(logger) + } catch (e: java.lang.Exception) { + logger.log { "Failed to install p7zip from release repository, trying Alpine edge..." } + logger.log { "This may be caused by the fact that p7zip is missing on armhf Alpine 3.17, see: https://gitlab.alpinelinux.org/alpine/aports/-/commits/master/main/p7zip/APKBUILD" } + runCommand( + "apk add p7zip --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main", bash = false ).waitAndPrintOutput(logger) - runCommand("echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config").waitAndPrintOutput( - logger - ) - runCommand("ssh-keygen -A").waitAndPrintOutput(logger) - - logger.log(this) { "Setting p7zip" } - - if (arch == "armhf" || arch == "arm" || arch == "armv7" || arch == "") { - logger.log(this) { "On armhf " } - } - - logger.log(this) { "Bootstrap installation done" } - - return@withContext - } catch (e: Exception) { - throw (e) - } finally { } + + return@withContext + } catch (e: Exception) { + throw (e) + } finally { } + } } @@ -213,7 +221,7 @@ class BootstrapRepositoryImpl( root: Boolean, bash: Boolean ): Process { - logger.run { ">$command" } + logger.log { ">$command" } val FILES = "/data/data/com.octo4a/files" val directory = File(filesPath) if (!directory.exists()) { diff --git a/app/app/src/main/java/com/octo4a/repository/OctoPrintHandlerRepository.kt b/app/app/src/main/java/com/octo4a/repository/OctoPrintHandlerRepository.kt index 1942ee9e..0a958885 100644 --- a/app/app/src/main/java/com/octo4a/repository/OctoPrintHandlerRepository.kt +++ b/app/app/src/main/java/com/octo4a/repository/OctoPrintHandlerRepository.kt @@ -1,24 +1,19 @@ package com.octo4a.repository -import android.R.attr.path -import android.R.attr.start import android.content.Context import android.os.Environment -import android.util.Log -import com.google.gson.JsonParser import com.octo4a.serial.VSPPty -import com.octo4a.serial.VirtualSerialDriver import com.octo4a.utils.* import com.octo4a.utils.preferences.MainPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext -import org.json.JSONObject import org.yaml.snakeyaml.Yaml import java.io.File import java.io.FileWriter -import java.lang.Exception +import java.io.PrintWriter +import java.io.StringWriter import java.lang.reflect.Field import java.util.regex.Matcher import java.util.regex.Pattern @@ -155,7 +150,14 @@ class OctoPrintHandlerRepositoryImpl( logger ) runCommand("ls -lah").waitAndPrintOutput(logger) - runCommand("7z x -y octoprint.zip").waitAndPrintOutput(logger) + try { + runCommand("7z x -y octoprint.zip").waitAndPrintOutput(logger) + } catch(e: java.lang.Exception) { + logger.log { "7zip extraction failed: $e" } + logger.log { "Trying to use unzip" } + runCommand("unzip octoprint.zip").waitAndPrintOutput(logger) + } + } _serverState.emit(ServerStatus.InstallingDependencies) bootstrapRepository.apply { @@ -181,6 +183,11 @@ class OctoPrintHandlerRepositoryImpl( } catch (e: java.lang.Exception) { _serverState.emit(ServerStatus.InstallationError) _installErrorDescription.emit(e.toString()) + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + + logger.log{ "An exception has occurred at:\n$sw\nException:\n${e.toString()}"} } } diff --git a/app/app/src/main/java/com/octo4a/ui/InstallationActivity.kt b/app/app/src/main/java/com/octo4a/ui/InstallationActivity.kt index b3682e67..6fcf487e 100644 --- a/app/app/src/main/java/com/octo4a/ui/InstallationActivity.kt +++ b/app/app/src/main/java/com/octo4a/ui/InstallationActivity.kt @@ -1,6 +1,9 @@ package com.octo4a.ui +import android.app.ActivityManager +import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -63,6 +66,24 @@ class InstallationActivity : AppCompatActivity() { val logsFragment = TerminalSheetDialog() logsFragment.show(supportFragmentManager, logsFragment.tag) } + clearDataAndRestart.setOnClickListener{ + try { + val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + // clearing app data + if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) { + activityManager.clearApplicationUserData() + } else { + val packageName = applicationContext.packageName + val runtime = Runtime.getRuntime(); + runtime.exec("pm clear $packageName"); + } + + + Runtime.getRuntime().exit(0) + } catch (e: Exception) { + e.printStackTrace() + } + } } private fun setItemsState(status: ServerStatus) { diff --git a/app/app/src/main/java/com/octo4a/ui/fragments/ServerFragment.kt b/app/app/src/main/java/com/octo4a/ui/fragments/ServerFragment.kt index 3dbff3cd..ad18844b 100644 --- a/app/app/src/main/java/com/octo4a/ui/fragments/ServerFragment.kt +++ b/app/app/src/main/java/com/octo4a/ui/fragments/ServerFragment.kt @@ -262,6 +262,5 @@ class ServerFragment : Fragment() { val dialog = builder.create() dialog.show() - } } \ No newline at end of file diff --git a/app/app/src/main/java/com/octo4a/utils/ProcessUtils.kt b/app/app/src/main/java/com/octo4a/utils/ProcessUtils.kt index b31ef2e1..c08113ae 100644 --- a/app/app/src/main/java/com/octo4a/utils/ProcessUtils.kt +++ b/app/app/src/main/java/com/octo4a/utils/ProcessUtils.kt @@ -15,6 +15,19 @@ fun Process.waitAndPrintOutput(logger: LoggerRepository, type: LogType = LogType logger.log(this, type) { it } outputStr += it } + + + val exitCode = waitFor() + if (exitCode != 0) { + val logLines = outputStr + // replace useless proot warning + .replace("proot warning: can't sanitize binding \"/data/data/com.octo4a/files/serialpipe\": No such file or directory", "") + .lines() + .takeLast(2) + .joinToString("\n") { it.take(50) } + throw RuntimeException("Process exited with error code ${exitCode}. $logLines") + } + return outputStr } diff --git a/app/app/src/main/res/layout/activity_installation_progress.xml b/app/app/src/main/res/layout/activity_installation_progress.xml index 9a5254e1..686ece61 100644 --- a/app/app/src/main/res/layout/activity_installation_progress.xml +++ b/app/app/src/main/res/layout/activity_installation_progress.xml @@ -52,6 +52,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" + android:visibility="visible" > + + diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index 35508c82..696a1b97 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ Installation error Please attach the log when seeking support on Github. It is vital for troubleshooting. See logs + Clear data and restart