170 lines
7.2 KiB
Java
170 lines
7.2 KiB
Java
package io.neoterm.installer;
|
|
|
|
import android.app.Activity;
|
|
import android.app.ProgressDialog;
|
|
import android.os.Build;
|
|
import android.system.Os;
|
|
import android.util.Log;
|
|
import android.util.Pair;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipInputStream;
|
|
|
|
import io.neoterm.backend.EmulatorDebug;
|
|
import io.neoterm.customize.NeoTermPath;
|
|
import io.neoterm.preference.NeoTermPreference;
|
|
|
|
public final class BaseFileInstaller {
|
|
public interface ResultListener {
|
|
void onResult(Exception error);
|
|
}
|
|
|
|
public static void installBaseFiles(final Activity activity, final ResultListener resultListener) {
|
|
final File PREFIX_FILE = new File(NeoTermPath.USR_PATH);
|
|
if (PREFIX_FILE.isDirectory()) {
|
|
resultListener.onResult(null);
|
|
return;
|
|
}
|
|
|
|
final ProgressDialog progress = ProgressDialog.show(activity, null, "Installing", true, false);
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
final String STAGING_PREFIX_PATH = NeoTermPath.ROOT_PATH + "/usr-staging";
|
|
final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH);
|
|
|
|
if (STAGING_PREFIX_FILE.exists()) {
|
|
deleteFolder(STAGING_PREFIX_FILE);
|
|
}
|
|
|
|
final byte[] buffer = new byte[8096];
|
|
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
|
|
|
|
final URL zipUrl = determineZipUrl();
|
|
try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) {
|
|
ZipEntry zipEntry;
|
|
while ((zipEntry = zipInput.getNextEntry()) != null) {
|
|
if (zipEntry.getName().equals("SYMLINKS.txt")) {
|
|
BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput));
|
|
String line;
|
|
while ((line = symlinksReader.readLine()) != null) {
|
|
if (line.isEmpty()) {
|
|
continue;
|
|
}
|
|
String[] parts = line.split("←");
|
|
if (parts.length != 2)
|
|
throw new RuntimeException("Malformed symlink line: " + line);
|
|
String oldPath = parts[0];
|
|
String newPath = STAGING_PREFIX_PATH + "/" + parts[1];
|
|
symlinks.add(Pair.create(oldPath, newPath));
|
|
}
|
|
} else {
|
|
String zipEntryName = zipEntry.getName();
|
|
File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName);
|
|
if (zipEntry.isDirectory()) {
|
|
if (!targetFile.mkdirs())
|
|
throw new RuntimeException("Failed to create directory: " + targetFile.getAbsolutePath());
|
|
} else {
|
|
try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
|
|
int readBytes;
|
|
while ((readBytes = zipInput.read(buffer)) != -1)
|
|
outStream.write(buffer, 0, readBytes);
|
|
}
|
|
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) {
|
|
//noinspection OctalInteger
|
|
Os.chmod(targetFile.getAbsolutePath(), 0700);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (symlinks.isEmpty())
|
|
throw new RuntimeException("No SYMLINKS.txt encountered");
|
|
for (Pair<String, String> symlink : symlinks) {
|
|
Os.symlink(symlink.first, symlink.second);
|
|
}
|
|
|
|
if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
|
|
throw new RuntimeException("Unable to rename staging folder");
|
|
}
|
|
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
resultListener.onResult(null);
|
|
}
|
|
});
|
|
} catch (final Exception e) {
|
|
Log.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
resultListener.onResult(e);
|
|
} catch (RuntimeException e) {
|
|
// Activity already dismissed - ignore.
|
|
}
|
|
}
|
|
});
|
|
} finally {
|
|
activity.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
progress.dismiss();
|
|
} catch (RuntimeException e) {
|
|
// Activity already dismissed - ignore.
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
private static URL determineZipUrl() throws MalformedURLException {
|
|
String archName = determineArchName();
|
|
return new URL("https://kernel19.cc/neoterm/boot/" + archName + ".zip");
|
|
}
|
|
|
|
public static String determineArchName() {
|
|
for (String androidArch : Build.SUPPORTED_ABIS) {
|
|
switch (androidArch) {
|
|
case "arm64-v8a":
|
|
return "aarch64";
|
|
case "armeabi-v7a":
|
|
return "arm";
|
|
case "x86_64":
|
|
return "x86_64";
|
|
case "x86":
|
|
return "i686";
|
|
}
|
|
}
|
|
throw new RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " +
|
|
Arrays.toString(Build.SUPPORTED_ABIS));
|
|
}
|
|
|
|
private static void deleteFolder(File fileOrDirectory) {
|
|
File[] children = fileOrDirectory.listFiles();
|
|
if (children != null) {
|
|
for (File child : children) {
|
|
deleteFolder(child);
|
|
}
|
|
}
|
|
if (!fileOrDirectory.delete()) {
|
|
throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath());
|
|
}
|
|
}
|
|
}
|