X-Git-Url: https://git.distorted.org.uk/~mdw/tripe-android/blobdiff_plain/7eb3f62e2ea97fab63080e92ebf95a65c95714a1..ad64fbfa301eaddc1a067e78743adcd4c1d1dd8d:/app.scala?ds=sidebyside diff --git a/app.scala b/app.scala new file mode 100644 index 0000000..f82846c --- /dev/null +++ b/app.scala @@ -0,0 +1,141 @@ +/* -*-scala-*- + * + * Setting up the Android environment + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Trivial IP Encryption (TrIPE) Android app. + * + * TrIPE is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE. If not, see . + */ + +package uk.org.distorted.tripe; package object app { + +/*----- Imports -----------------------------------------------------------*/ + +import java.io.{File, IOException}; + +import scala.collection.mutable.HashMap; + +import android.content.Context; import Context.MODE_WORLD_READABLE; +import android.os.Build; import Build.{CPU_ABI, CPU_ABI2}; +import android.util.Log; + +import sys.FileImplicits._; + +/*----- Regular expressions for parsing the `.installed file --------------*/ + +private final val RX_COMMENT = """(?x) ^ \s* (?: \# .* )? $""".r; +private final val RX_KEYVAL = """(?x) ^ \s* + ([-\w]+) + (?:\s+(?!=)|\s*=\s*) + (|\S|\S.*\S) + \s* $""".r; + +/*----- Main code ---------------------------------------------------------*/ + +private final val TAG = "TrIPE"; + +var root: File = null; + +private def install(ctx: Context, inst: HashMap[String, String]) { + + /* First, figure out which ABIs are wanted on this device. Unfortunately, + * the good way of doing this isn't available in our minimum API level, so + * we must use reflection. + */ + val abis = try { + classOf[Build].getField("SUPPORTED_ABIS").get(null). + asInstanceOf[Array[String]] + } catch { case _: NoSuchFieldException => + Array(CPU_ABI, CPU_ABI2) flatMap { + case null | "" => None + case s => Some(s) + } + } + Log.d(TAG, s"abis = ${abis.mkString(", ")}"); + + /* Clear out whatever might be there already. */ + val bindir = root/"bin"; + bindir.rmTree(); + bindir.mkdir_!(); + + /* Now extract each of our binaries using the best available ABI. */ + val assets = ctx.getAssets; + for (abi <- abis) { + val binsrc = s"bin/$abi"; + for (base <- assets.list(binsrc)) { + val outfile = bindir/base; + if (!outfile.exists_!) { + Log.d(TAG, s"install: extract `$base' using abi `$abi'"); + outfile.withOutput { out => + closing(assets.open(s"$binsrc/$base")) { in => + for ((buf, n) <- blocks(in)) out.write(buf, 0, n); + } + } + } + outfile.chmod_!(0x1ed); + } + } + + /* Write out a new install file. */ + val infofile = root/".installed"; + val newinfofile = root/".installed.new"; + newinfofile.withWriter { out => + out.write(s"""### -*-conf-*- + +uuid = ${ctx.getString(R.string.auto_build_uuid)} +"""); + } + newinfofile.rename_!(infofile); +} + +def setup(ctx: Context) { + + /* Make our root directory and remember where it is. */ + root = ctx.getFilesDir; + if (!root.isdir_!) { + throw new IOException("system failed to create `files' " + + "(but didn't tell us)"); + } + + /* Find out which build, if any, corresponds to what's there already. */ + val inst = HashMap[String, String](); + try { root/".installed" withReader { in => + var lno = 1; + for (line <- lines(in)) { + line match { + case RX_COMMENT() => ok; + case RX_KEYVAL(k, v) => inst(k) = v; + case _ => Log.w(TAG, s".installed:$lno: ignored unparseable line"); + } + lno += 1; + } + } } catch { + case e: IOException => + Log.w(TAG, s".installed: I/O error: ${e.getMessage}"); + } + + /* If this doesn't match, then we have some work to do. */ + if (inst.getOrElse("uuid", "") != + ctx.getString(R.string.auto_build_uuid)) + install(ctx, inst); +} + +/*----- That's all, folks -------------------------------------------------*/ + +}