Added: Add `ShellCommandShellEnvironment` and `TermuxShellCommandShellEnvironment` to export `ExecutionCommand` variables

This adds onto f102ea20 to build termux environment. Variables for `ExecutionCommand` app have the `SHELL_CMD__` scope. Docs will be provided for details of the variables.

- `SHELL_CMD__SHELL_ID`
- `SHELL_CMD__SHELL_NAME`
- `SHELL_CMD__APP_SHELL_NUMBER_SINCE_BOOT`
- `SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT`
- `SHELL_CMD__APP_SHELL_NUMBER_SINCE_APP_START`
- `SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START`

The commit also adds `SystemEventReceiver` to Termux app that will receive `ACTION_BOOT_COMPLETED`.
This commit is contained in:
agnostic-apollo 2022-06-12 00:31:38 +05:00
parent ebdab0e59c
commit 150b1ff99c
12 changed files with 317 additions and 3 deletions

View File

@ -34,6 +34,7 @@
<uses-permission android:name="android.permission.DUMP" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
<application
@ -169,6 +170,14 @@
android:name=".app.TermuxOpenReceiver"
android:exported="false" />
<receiver
android:name=".app.event.SystemEventReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".shared.activities.ReportActivity$ReportActivityBroadcastReceiver"
android:exported="false" />

View File

@ -170,6 +170,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
actionReleaseWakeLock(false);
if (!mWantsToStop)
killAllTermuxExecutionCommands();
TermuxShellManager.onAppExit(this);
runStopForeground();
}
@ -463,6 +465,8 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
return null;
}
executionCommand.setShellCommandShellEnvironment = true;
if (Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
Logger.logVerboseExtended(LOG_TAG, executionCommand.toString());
@ -571,13 +575,15 @@ public final class TermuxService extends Service implements AppShell.AppShellCli
return null;
}
executionCommand.setShellCommandShellEnvironment = true;
executionCommand.terminalTranscriptRows = mProperties.getTerminalTranscriptRows();
if (Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
Logger.logVerboseExtended(LOG_TAG, executionCommand.toString());
// If the execution command was started for a plugin, only then will the stdout be set
// Otherwise if command was manually started by the user like by adding a new terminal session,
// then no need to set stdout
executionCommand.terminalTranscriptRows = mProperties.getTerminalTranscriptRows();
TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(),
this, new TermuxShellEnvironment(), null, executionCommand.isPluginExecutionCommand);
if (newTermuxSession == null) {

View File

@ -0,0 +1,47 @@
package com.termux.app.event;
import android.content.BroadcastReceiver;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.termux.shell.TermuxShellManager;
public class SystemEventReceiver extends BroadcastReceiver {
private static SystemEventReceiver mInstance;
private static final String LOG_TAG = "SystemEventReceiver";
public static synchronized SystemEventReceiver getInstance() {
if (mInstance == null) {
mInstance = new SystemEventReceiver();
}
return mInstance;
}
@Override
public void onReceive(@NonNull Context context, @Nullable Intent intent) {
if (intent == null) return;
Logger.logDebug(LOG_TAG, "Intent Received:\n" + IntentUtils.getIntentString(intent));
String action = intent.getAction();
if (action == null) return;
switch (action) {
case Intent.ACTION_BOOT_COMPLETED:
onActionBootCompleted(context, intent);
break;
default:
Logger.logError(LOG_TAG, "Invalid action \"" + action + "\" passed to " + LOG_TAG);
}
}
public synchronized void onActionBootCompleted(@NonNull Context context, @NonNull Intent intent) {
TermuxShellManager.onActionBootCompleted(context, intent);
}
}

View File

@ -188,6 +188,38 @@ public class SharedPreferenceUtils {
sharedPreferences.edit().putInt(key, value).apply();
}
/**
* Set an {@code int} in {@link SharedPreferences}.
*
* @param sharedPreferences The {@link SharedPreferences} to set the value in.
* @param key The key for the value.
* @param def The default value if failed to read a valid value.
* @param commitToFile If set to {@code true}, then value will be set to shared preferences
* in-memory cache and the file synchronously. Ideally, only to be used for
* multi-process use-cases.
* @param resetValue The value if not {@code null} that should be set if current or incremented
* value is less than 0.
* @return Returns the {@code int} value stored in {@link SharedPreferences} before increment,
* otherwise returns default if failed to read a valid value, like in case of an exception.
*/
@SuppressLint("ApplySharedPref")
public static int getAndIncrementInt(SharedPreferences sharedPreferences, String key, int def,
boolean commitToFile, Integer resetValue) {
if (sharedPreferences == null) {
Logger.logError(LOG_TAG, "Ignoring incrementing int value for the \"" + key + "\" key into null shared preferences.");
return def;
}
int curValue = getInt(sharedPreferences, key, def);
if (resetValue != null && (curValue < 0)) curValue = resetValue;
int newValue = curValue + 1;
if (resetValue != null && newValue < 0) newValue = resetValue;
setInt(sharedPreferences, key, newValue, commitToFile);
return curValue;
}
/**

View File

@ -195,6 +195,10 @@ public class ExecutionCommand {
/** The {@link ShellCreateMode} of commands. */
public String shellCreateMode;
/** Whether to set {@link ExecutionCommand} shell environment. */
public boolean setShellCommandShellEnvironment;
/** The command label for the {@link ExecutionCommand}. */
@ -396,6 +400,8 @@ public class ExecutionCommand {
logString.append("\n").append(executionCommand.getShellCreateModeLogString());
}
logString.append("\n").append(executionCommand.getSetRunnerShellEnvironmentLogString());
if (!ignoreNull || executionCommand.commandIntent != null)
logString.append("\n").append(executionCommand.getCommandIntentLogString());
@ -490,7 +496,7 @@ public class ExecutionCommand {
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Shell Name", executionCommand.shellName, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Shell Create Mode", executionCommand.shellCreateMode, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Set Shell Command Shell Environment", executionCommand.setShellCommandShellEnvironment, "-"));
markdownString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("isPluginExecutionCommand", executionCommand.isPluginExecutionCommand, "-"));
@ -588,6 +594,10 @@ public class ExecutionCommand {
return Logger.getSingleLineLogStringEntry("Shell Create Mode", shellCreateMode, "-");
}
public String getSetRunnerShellEnvironmentLogString() {
return "Set Shell Command Shell Environment: `" + setShellCommandShellEnvironment + "`";
}
public String getCommandDescriptionLogString() {
return Logger.getSingleLineLogStringEntry("Command Description", commandDescription, "-");
}

View File

@ -20,6 +20,12 @@ import java.util.HashMap;
*/
public class AndroidShellEnvironment extends UnixShellEnvironment {
protected ShellCommandShellEnvironment shellCommandShellEnvironment;
public AndroidShellEnvironment() {
shellCommandShellEnvironment = new ShellCommandShellEnvironment();
}
/** Get shell environment for Android. */
@NonNull
@Override
@ -85,6 +91,9 @@ public class AndroidShellEnvironment extends UnixShellEnvironment {
getDefaultWorkingDirectoryPath());
ShellEnvironmentUtils.createHomeDir(environment);
if (executionCommand.setShellCommandShellEnvironment && shellCommandShellEnvironment != null)
environment.putAll(shellCommandShellEnvironment.getEnvironment(currentPackageContext, executionCommand));
return environment;
}

View File

@ -0,0 +1,62 @@
package com.termux.shared.shell.command.environment;
import android.content.Context;
import androidx.annotation.NonNull;
import com.termux.shared.shell.command.ExecutionCommand;
import java.util.HashMap;
/**
* Environment for {@link ExecutionCommand}.
*/
public class ShellCommandShellEnvironment {
/** Environment variable prefix for the {@link ExecutionCommand}. */
public static final String SHELL_CMD_ENV_PREFIX = "SHELL_CMD__";
/** Environment variable for the {@link ExecutionCommand.Runner} name. */
public static final String ENV_SHELL_CMD__RUNNER_NAME = SHELL_CMD_ENV_PREFIX + "RUNNER_NAME";
/** Environment variable for the package name running the {@link ExecutionCommand}. */
public static final String ENV_SHELL_CMD__PACKAGE_NAME = SHELL_CMD_ENV_PREFIX + "PACKAGE_NAME";
/** Environment variable for the {@link ExecutionCommand#id}/TermuxShellManager.SHELL_ID name.
* This will be common for all runners. */
public static final String ENV_SHELL_CMD__SHELL_ID = SHELL_CMD_ENV_PREFIX + "SHELL_ID";
/** Environment variable for the {@link ExecutionCommand#shellName} name. */
public static final String ENV_SHELL_CMD__SHELL_NAME = SHELL_CMD_ENV_PREFIX + "SHELL_NAME";
/** Environment variable for the {@link ExecutionCommand.Runner#APP_SHELL} number since boot. */
public static final String ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_BOOT = SHELL_CMD_ENV_PREFIX + "APP_SHELL_NUMBER_SINCE_BOOT";
/** Environment variable for the {{@link ExecutionCommand.Runner#TERMINAL_SESSION} number since boot. */
public static final String ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT = SHELL_CMD_ENV_PREFIX + "TERMINAL_SESSION_NUMBER_SINCE_BOOT";
/** Environment variable for the {@link ExecutionCommand.Runner#APP_SHELL} number since app start. */
public static final String ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_APP_START = SHELL_CMD_ENV_PREFIX + "APP_SHELL_NUMBER_SINCE_APP_START";
/** Environment variable for the {@link ExecutionCommand.Runner#TERMINAL_SESSION} number since app start. */
public static final String ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START = SHELL_CMD_ENV_PREFIX + "TERMINAL_SESSION_NUMBER_SINCE_APP_START";
/** Get shell environment containing info for {@link ExecutionCommand}. */
@NonNull
public HashMap<String, String> getEnvironment(@NonNull Context currentPackageContext,
@NonNull ExecutionCommand executionCommand) {
HashMap<String, String> environment = new HashMap<>();
ExecutionCommand.Runner runner = ExecutionCommand.Runner.runnerOf(executionCommand.runner);
if (runner == null) return environment;
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__RUNNER_NAME, runner.getName());
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__PACKAGE_NAME, currentPackageContext.getPackageName());
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__SHELL_ID, String.valueOf(executionCommand.id));
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__SHELL_NAME, executionCommand.shellName);
return environment;
}
}

View File

@ -215,6 +215,28 @@ public class TermuxAppSharedPreferences {
}
public synchronized int getAndIncrementAppShellNumberSinceBoot() {
// Keep value at MAX_VALUE on integer overflow and not 0, since not first shell
return SharedPreferenceUtils.getAndIncrementInt(mSharedPreferences, TERMUX_APP.KEY_APP_SHELL_NUMBER_SINCE_BOOT,
TERMUX_APP.DEFAULT_VALUE_APP_SHELL_NUMBER_SINCE_BOOT, true, Integer.MAX_VALUE);
}
public synchronized void resetAppShellNumberSinceBoot() {
SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_APP.KEY_APP_SHELL_NUMBER_SINCE_BOOT,
TERMUX_APP.DEFAULT_VALUE_APP_SHELL_NUMBER_SINCE_BOOT, true);
}
public synchronized int getAndIncrementTerminalSessionNumberSinceBoot() {
// Keep value at MAX_VALUE on integer overflow and not 0, since not first shell
return SharedPreferenceUtils.getAndIncrementInt(mSharedPreferences, TERMUX_APP.KEY_TERMINAL_SESSION_NUMBER_SINCE_BOOT,
TERMUX_APP.DEFAULT_VALUE_TERMINAL_SESSION_NUMBER_SINCE_BOOT, true, Integer.MAX_VALUE);
}
public synchronized void resetTerminalSessionNumberSinceBoot() {
SharedPreferenceUtils.setInt(mSharedPreferences, TERMUX_APP.KEY_TERMINAL_SESSION_NUMBER_SINCE_BOOT,
TERMUX_APP.DEFAULT_VALUE_TERMINAL_SESSION_NUMBER_SINCE_BOOT, true);
}
public boolean isTerminalViewKeyLoggingEnabled() {
return SharedPreferenceUtils.getBoolean(mSharedPreferences, TERMUX_APP.KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED, TERMUX_APP.DEFAULT_VALUE_TERMINAL_VIEW_KEY_LOGGING_ENABLED);

View File

@ -1,7 +1,7 @@
package com.termux.shared.termux.settings.preferences;
/*
* Version: v0.15.0
* Version: v0.16.0
*
* Changelog
*
@ -65,8 +65,14 @@ package com.termux.shared.termux.settings.preferences;
* - 0.15.0 (2021-09-05)
* - Added following to `TERMUX_TASKER_APP`:
* `KEY_LAST_PENDING_INTENT_REQUEST_CODE` and `DEFAULT_VALUE_KEY_LAST_PENDING_INTENT_REQUEST_CODE`.
*
* - 0.16.0 (2022-06-11)
* - Added following to `TERMUX_APP`:
* `KEY_APP_SHELL_NUMBER_SINCE_BOOT` and `KEY_TERMINAL_SESSION_NUMBER_SINCE_BOOT`.
*/
import com.termux.shared.shell.command.ExecutionCommand;
/**
* A class that defines shared constants of the SharedPreferences used by Termux app and its plugins.
* This class will be hosted by termux-shared lib and should be imported by other termux plugin
@ -143,6 +149,18 @@ public final class TermuxPreferenceConstants {
public static final String KEY_LAST_NOTIFICATION_ID = "last_notification_id";
public static final int DEFAULT_VALUE_KEY_LAST_NOTIFICATION_ID = 0;
/**
* The {@link ExecutionCommand.Runner#APP_SHELL} number after termux app process since boot.
*/
public static final String KEY_APP_SHELL_NUMBER_SINCE_BOOT = "app_shell_number_since_boot";
public static final int DEFAULT_VALUE_APP_SHELL_NUMBER_SINCE_BOOT = 0;
/**
* The {@link ExecutionCommand.Runner#TERMINAL_SESSION} number after termux app process since boot.
*/
public static final String KEY_TERMINAL_SESSION_NUMBER_SINCE_BOOT = "terminal_session_number_since_boot";
public static final int DEFAULT_VALUE_TERMINAL_SESSION_NUMBER_SINCE_BOOT = 0;
/**
* Defines the key for whether termux terminal view key logging is enabled or not

View File

@ -40,6 +40,16 @@ public class TermuxShellManager {
*/
public final List<ExecutionCommand> mPendingPluginExecutionCommands = new ArrayList<>();
/**
* The {@link ExecutionCommand.Runner#APP_SHELL} number after app process was started/restarted.
*/
public static int APP_SHELL_NUMBER_SINCE_APP_START;
/**
* The {@link ExecutionCommand.Runner#TERMINAL_SESSION} number after app process was started/restarted.
*/
public static int TERMINAL_SESSION_NUMBER_SINCE_APP_START;
public TermuxShellManager(@NonNull Context context) {
@ -69,8 +79,45 @@ public class TermuxShellManager {
}
public synchronized static void onActionBootCompleted(@NonNull Context context, @NonNull Intent intent) {
TermuxAppSharedPreferences preferences = TermuxAppSharedPreferences.build(context);
if (preferences == null) return;
// Ensure any shells started after boot have valid ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_BOOT and
// ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT exported
preferences.resetAppShellNumberSinceBoot();
preferences.resetTerminalSessionNumberSinceBoot();
}
public static void onAppExit(@NonNull Context context) {
// Ensure any shells started after boot have valid ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_APP_START and
// ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START exported
APP_SHELL_NUMBER_SINCE_APP_START = 0;
TERMINAL_SESSION_NUMBER_SINCE_APP_START = 0;
}
public static synchronized int getNextShellId() {
return SHELL_ID++;
}
public static synchronized int getAndIncrementAppShellNumberSinceAppStart() {
// Keep value at MAX_VALUE on integer overflow and not 0, since not first shell
int curValue = APP_SHELL_NUMBER_SINCE_APP_START;
if (curValue < 0) curValue = Integer.MAX_VALUE;
APP_SHELL_NUMBER_SINCE_APP_START = curValue + 1;
if (APP_SHELL_NUMBER_SINCE_APP_START < 0) APP_SHELL_NUMBER_SINCE_APP_START = Integer.MAX_VALUE;
return curValue;
}
public static synchronized int getAndIncrementTerminalSessionNumberSinceAppStart() {
// Keep value at MAX_VALUE on integer overflow and not 0, since not first shell
int curValue = TERMINAL_SESSION_NUMBER_SINCE_APP_START;
if (curValue < 0) curValue = Integer.MAX_VALUE;
TERMINAL_SESSION_NUMBER_SINCE_APP_START = curValue + 1;
if (TERMINAL_SESSION_NUMBER_SINCE_APP_START < 0) TERMINAL_SESSION_NUMBER_SINCE_APP_START = Integer.MAX_VALUE;
return curValue;
}
}

View File

@ -0,0 +1,48 @@
package com.termux.shared.termux.shell.command.environment;
import android.content.Context;
import androidx.annotation.NonNull;
import com.termux.shared.shell.command.ExecutionCommand;
import com.termux.shared.shell.command.environment.ShellCommandShellEnvironment;
import com.termux.shared.shell.command.environment.ShellEnvironmentUtils;
import com.termux.shared.termux.settings.preferences.TermuxAppSharedPreferences;
import com.termux.shared.termux.shell.TermuxShellManager;
import java.util.HashMap;
/**
* Environment for Termux {@link ExecutionCommand}.
*/
public class TermuxShellCommandShellEnvironment extends ShellCommandShellEnvironment {
/** Get shell environment containing info for Termux {@link ExecutionCommand}. */
@NonNull
@Override
public HashMap<String, String> getEnvironment(@NonNull Context currentPackageContext,
@NonNull ExecutionCommand executionCommand) {
HashMap<String, String> environment = super.getEnvironment(currentPackageContext, executionCommand);
TermuxAppSharedPreferences preferences = TermuxAppSharedPreferences.build(currentPackageContext);
if (preferences == null) return environment;
if (ExecutionCommand.Runner.APP_SHELL.equalsRunner(executionCommand.runner)) {
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_BOOT,
String.valueOf(preferences.getAndIncrementAppShellNumberSinceBoot()));
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__APP_SHELL_NUMBER_SINCE_APP_START,
String.valueOf(TermuxShellManager.getAndIncrementAppShellNumberSinceAppStart()));
} else if (ExecutionCommand.Runner.TERMINAL_SESSION.equalsRunner(executionCommand.runner)) {
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_BOOT,
String.valueOf(preferences.getAndIncrementTerminalSessionNumberSinceBoot()));
ShellEnvironmentUtils.putToEnvIfSet(environment, ENV_SHELL_CMD__TERMINAL_SESSION_NUMBER_SINCE_APP_START,
String.valueOf(TermuxShellManager.getAndIncrementTerminalSessionNumberSinceAppStart()));
} else {
return environment;
}
return environment;
}
}

View File

@ -23,6 +23,10 @@ public class TermuxShellEnvironment extends AndroidShellEnvironment {
/** Environment variable for the termux {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH}. */
public static final String ENV_PREFIX = "PREFIX";
public TermuxShellEnvironment() {
super();
shellCommandShellEnvironment = new TermuxShellCommandShellEnvironment();
}
/** Get shell environment for Termux. */
@NonNull