Skip to content

Commit

Permalink
Example of support for packages in APK
Browse files Browse the repository at this point in the history
  • Loading branch information
fornwall committed Feb 19, 2020
1 parent 93724b7 commit 07ae244
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 1 deletion.
2 changes: 1 addition & 1 deletion app/src/main/java/com/termux/app/TermuxInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
131 changes: 131 additions & 0 deletions app/src/main/java/com/termux/app/TermuxPackageInstaller.java
Original file line number Diff line number Diff line change
@@ -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);
}

}
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/termux/app/TermuxService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -254,6 +265,8 @@ private Notification buildNotification() {

@Override
public void onDestroy() {
unregisterReceiver(packageInstaller);

File termuxTmpDir = new File(TermuxService.PREFIX_PATH + "/tmp");

if (termuxTmpDir.exists()) {
Expand Down

0 comments on commit 07ae244

Please sign in to comment.