/* -*-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 -------------------------------------------------*/
}