From 07ae244aa8214c3e10fd3171203ce1b3aa99d354 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Wed, 15 Jan 2020 03:07:35 +0100 Subject: [PATCH] Example of support for packages in APK --- .../java/com/termux/app/TermuxInstaller.java | 2 +- .../termux/app/TermuxPackageInstaller.java | 131 ++++++++++++++++++ .../java/com/termux/app/TermuxService.java | 13 ++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/termux/app/TermuxPackageInstaller.java diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java index 6e50b22dd6..0723f31af1 100644 --- a/app/src/main/java/com/termux/app/TermuxInstaller.java +++ b/app/src/main/java/com/termux/app/TermuxInstaller.java @@ -158,7 +158,7 @@ public void run() { }.start(); } - private static void ensureDirectoryExists(File directory) { + static void ensureDirectoryExists(File directory) { if (!directory.isDirectory() && !directory.mkdirs()) { throw new RuntimeException("Unable to create directory: " + directory.getAbsolutePath()); } diff --git a/app/src/main/java/com/termux/app/TermuxPackageInstaller.java b/app/src/main/java/com/termux/app/TermuxPackageInstaller.java new file mode 100644 index 0000000000..0602870f4b --- /dev/null +++ b/app/src/main/java/com/termux/app/TermuxPackageInstaller.java @@ -0,0 +1,131 @@ +package com.termux.app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Process; +import android.system.Os; +import android.util.Log; + +import com.termux.terminal.EmulatorDebug; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +public class TermuxPackageInstaller extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + try { + String packageName = intent.getData().getSchemeSpecificPart(); + String action = intent.getAction(); + PackageManager packageManager = context.getPackageManager(); + + if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + ApplicationInfo info = packageManager.getApplicationInfo(packageName, 0); + if (Process.myUid() == info.uid) { + installPackage(info); + } + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + if (Process.myUid() == intent.getIntExtra(Intent.EXTRA_UID, -1)) { + uninstallPackage(packageName); + } + + } + } catch (Exception e) { + Log.e("termux", "Error in package management: " + e); + } + } + + private static void installPackage(ApplicationInfo info) throws Exception { + File filesMappingFile = new File(info.nativeLibraryDir, "files.so"); + if (!filesMappingFile.exists()) { + Log.e("termux", "No file mapping at " + filesMappingFile.getAbsolutePath()); + return; + } + + Log.e("termux", "Installing: " + info.packageName); + BufferedReader reader = new BufferedReader(new FileReader(filesMappingFile)); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("←"); + if (parts.length != 2) { + Log.e(EmulatorDebug.LOG_TAG, "Malformed line " + line + " in " + filesMappingFile.getAbsolutePath()); + continue; + } + + String oldPath = info.nativeLibraryDir + "/" + parts[0]; + String newPath = TermuxService.PREFIX_PATH + "/" + parts[1]; + + TermuxInstaller.ensureDirectoryExists(new File(newPath).getParentFile()); + + Log.e(EmulatorDebug.LOG_TAG, "About to setup link: " + oldPath + " ← " + newPath); + new File(newPath).delete(); + Os.symlink(oldPath, newPath); + } + + File symlinksFile = new File(info.nativeLibraryDir, "symlinks.so"); + if (!symlinksFile.exists()) { + Log.e("termux", "No symlinks mapping at " + symlinksFile.getAbsolutePath()); + } + + reader = new BufferedReader(new FileReader(symlinksFile)); + while ((line = reader.readLine()) != null) { + String[] parts = line.split("←"); + if (parts.length != 2) { + Log.e(EmulatorDebug.LOG_TAG, "Malformed line " + line + " in " + symlinksFile.getAbsolutePath()); + continue; + } + + String oldPath = parts[0]; + String newPath = TermuxService.PREFIX_PATH + "/" + parts[1]; + + TermuxInstaller.ensureDirectoryExists(new File(newPath).getParentFile()); + + Log.e(EmulatorDebug.LOG_TAG, "About to setup link: " + oldPath + " ← " + newPath); + new File(newPath).delete(); + Os.symlink(oldPath, newPath); + } + } + + private static void uninstallPackage(String packageName) throws IOException { + Log.e("termux", "Uninstalling: " + packageName); + // We're currently visiting the whole $PREFIX. + // If we store installed symlinks in installPackage() we could just visit those, + // at the cost of increased complexity and risk for errors. + File prefixDir = new File(TermuxService.PREFIX_PATH); + removeBrokenSymlinks(prefixDir); + } + + private static void removeBrokenSymlinks(File parentDir) throws IOException { + for (File child : parentDir.listFiles()) { + if (!child.exists()) { + Log.e("termux", "Removing broken symlink: " + child.getAbsolutePath()); + child.delete(); + } else if (child.isDirectory()) { + removeBrokenSymlinks(child); + } + } + } + + public static void setupAllInstalledPackages(Context context) { + try { + removeBrokenSymlinks(new File(TermuxService.PREFIX_PATH)); + + PackageManager packageManager = context.getPackageManager(); + for (PackageInfo info : packageManager.getInstalledPackages(0)) { + if (info.sharedUserId != null && info.sharedUserId.equals("com.termux")) { + installPackage(info.applicationInfo); + } + } + } catch (Exception e) { + Log.e("termux", "Error setting up all packages", e); + } + + } +} diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index bbaf87c548..95c2bf5095 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -9,6 +9,7 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.net.Uri; import android.net.wifi.WifiManager; @@ -94,6 +95,8 @@ class LocalBinder extends Binder { /** If the user has executed the {@link #ACTION_STOP_SERVICE} intent. */ boolean mWantsToStop = false; + private final TermuxPackageInstaller packageInstaller = new TermuxPackageInstaller(); + @SuppressLint("Wakelock") @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -185,8 +188,16 @@ public IBinder onBind(Intent intent) { @Override public void onCreate() { + TermuxPackageInstaller.setupAllInstalledPackages(this); setupNotificationChannel(); startForeground(NOTIFICATION_ID, buildNotification()); + + IntentFilter addedFilter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + addedFilter.addDataScheme("package"); + IntentFilter removedFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); + removedFilter.addDataScheme("package"); + this.registerReceiver(packageInstaller, addedFilter); + this.registerReceiver(packageInstaller, removedFilter); } /** Update the shown foreground service notification after making any changes that affect it. */ @@ -254,6 +265,8 @@ private Notification buildNotification() { @Override public void onDestroy() { + unregisterReceiver(packageInstaller); + File termuxTmpDir = new File(TermuxService.PREFIX_PATH + "/tmp"); if (termuxTmpDir.exists()) {