Buckets:
NaTo1000/termux-app-bucket / termux-shared /src /main /java /com /termux /shared /shell /am /AmSocketServer.java
| package com.termux.shared.shell.am; | |
| import android.Manifest; | |
| import android.app.Application; | |
| import android.content.Context; | |
| import androidx.annotation.NonNull; | |
| import androidx.annotation.Nullable; | |
| import com.termux.am.Am; | |
| import com.termux.shared.R; | |
| import com.termux.shared.android.PackageUtils; | |
| import com.termux.shared.android.PermissionUtils; | |
| import com.termux.shared.errors.Error; | |
| import com.termux.shared.logger.Logger; | |
| import com.termux.shared.net.socket.local.ILocalSocketManager; | |
| import com.termux.shared.net.socket.local.LocalClientSocket; | |
| import com.termux.shared.net.socket.local.LocalServerSocket; | |
| import com.termux.shared.net.socket.local.LocalSocketManager; | |
| import com.termux.shared.net.socket.local.LocalSocketManagerClientBase; | |
| import com.termux.shared.net.socket.local.LocalSocketRunConfig; | |
| import com.termux.shared.shell.ArgumentTokenizer; | |
| import com.termux.shared.shell.command.ExecutionCommand; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.PrintStream; | |
| import java.nio.charset.StandardCharsets; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| /** | |
| * A AF_UNIX/SOCK_STREAM local server managed with {@link LocalSocketManager} whose | |
| * {@link LocalServerSocket} receives android activity manager (am) commands from {@link LocalClientSocket} | |
| * and runs them with termux-am-library. It would normally only allow processes belonging to the | |
| * server app's user and root user to connect to it. | |
| * | |
| * The client must send the am command as a string without the initial "am" arg on its output stream | |
| * and then wait for the result on its input stream. The result of the execution or error is sent | |
| * back in the format `exit_code\0stdout\0stderr\0` where `\0` represents a null character. | |
| * Check termux/termux-am-socket for implementation of a native c client. | |
| * | |
| * Usage: | |
| * 1. Optionally extend {@link AmSocketServerClient}, the implementation for | |
| * {@link ILocalSocketManager} that will receive call backs from the server including | |
| * when client connects via {@link ILocalSocketManager#onClientAccepted(LocalSocketManager, LocalClientSocket)}. | |
| * 2. Create a {@link AmSocketServerRunConfig} instance which extends from {@link LocalSocketRunConfig} | |
| * with the run config of the am server. It would be better to use a filesystem socket instead | |
| * of abstract namespace socket for security reasons. | |
| * 3. Call {@link #start(Context, LocalSocketRunConfig)} to start the server and store the {@link LocalSocketManager} | |
| * instance returned. | |
| * 4. Stop server if needed with a call to {@link LocalSocketManager#stop()} on the | |
| * {@link LocalSocketManager} instance returned by start call. | |
| * | |
| * https://github.com/termux/termux-am-library/blob/main/termux-am-library/src/main/java/com/termux/am/Am.java | |
| * https://github.com/termux/termux-am-socket | |
| * https://developer.android.com/studio/command-line/adb#am | |
| * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r32:frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java | |
| */ | |
| public class AmSocketServer { | |
| public static final String LOG_TAG = "AmSocketServer"; | |
| /** | |
| * Create the {@link AmSocketServer} {@link LocalServerSocket} and start listening for new {@link LocalClientSocket}. | |
| * | |
| * @param context The {@link Context} for {@link LocalSocketManager}. | |
| * @param localSocketRunConfig The {@link LocalSocketRunConfig} for {@link LocalSocketManager}. | |
| */ | |
| public static synchronized LocalSocketManager start( Context context, | |
| LocalSocketRunConfig localSocketRunConfig) { | |
| LocalSocketManager localSocketManager = new LocalSocketManager(context, localSocketRunConfig); | |
| Error error = localSocketManager.start(); | |
| if (error != null) { | |
| localSocketManager.onError(error); | |
| return null; | |
| } | |
| return localSocketManager; | |
| } | |
| public static void processAmClient( LocalSocketManager localSocketManager, | |
| LocalClientSocket clientSocket) { | |
| Error error; | |
| // Read amCommandString client sent and close input stream | |
| StringBuilder data = new StringBuilder(); | |
| error = clientSocket.readDataOnInputStream(data, true); | |
| if (error != null) { | |
| sendResultToClient(localSocketManager, clientSocket, 1, null, error.toString()); | |
| return; | |
| } | |
| String amCommandString = data.toString(); | |
| Logger.logVerbose(LOG_TAG, "am command received from peer " + clientSocket.getPeerCred().getMinimalString() + | |
| "\nam command: `" + amCommandString + "`"); | |
| // Parse am command string and convert it to a list of arguments | |
| List<String> amCommandList = new ArrayList<>(); | |
| error = parseAmCommand(amCommandString, amCommandList); | |
| if (error != null) { | |
| sendResultToClient(localSocketManager, clientSocket, 1, null, error.toString()); | |
| return; | |
| } | |
| String[] amCommandArray = amCommandList.toArray(new String[0]); | |
| Logger.logDebug(LOG_TAG, "am command received from peer " + clientSocket.getPeerCred().getMinimalString() + | |
| "\n" + ExecutionCommand.getArgumentsLogString("am command", amCommandArray)); | |
| AmSocketServerRunConfig amSocketServerRunConfig = (AmSocketServerRunConfig) localSocketManager.getLocalSocketRunConfig(); | |
| // Run am command and send its result to the client | |
| StringBuilder stdout = new StringBuilder(); | |
| StringBuilder stderr = new StringBuilder(); | |
| error = runAmCommand(localSocketManager.getContext(), amCommandArray, stdout, stderr, | |
| amSocketServerRunConfig.shouldCheckDisplayOverAppsPermission()); | |
| if (error != null) { | |
| sendResultToClient(localSocketManager, clientSocket, 1, stdout.toString(), | |
| !stderr.toString().isEmpty() ? stderr + "\n\n" + error : error.toString()); | |
| } | |
| sendResultToClient(localSocketManager, clientSocket, 0, stdout.toString(), stderr.toString()); | |
| } | |
| /** | |
| * Send result to {@link LocalClientSocket} that requested the am command to be run. | |
| * | |
| * @param localSocketManager The {@link LocalSocketManager} instance for the local socket. | |
| * @param clientSocket The {@link LocalClientSocket} to which the result is to be sent. | |
| * @param exitCode The exit code value to send. | |
| * @param stdout The stdout value to send. | |
| * @param stderr The stderr value to send. | |
| */ | |
| public static void sendResultToClient( LocalSocketManager localSocketManager, | |
| LocalClientSocket clientSocket, | |
| int exitCode, | |
| String stdout, String stderr) { | |
| StringBuilder result = new StringBuilder(); | |
| result.append(sanitizeExitCode(clientSocket, exitCode)); | |
| result.append('\0'); | |
| result.append(stdout != null ? stdout : ""); | |
| result.append('\0'); | |
| result.append(stderr != null ? stderr : ""); | |
| // Send result to client and close output stream | |
| Error error = clientSocket.sendDataToOutputStream(result.toString(), true); | |
| if (error != null) { | |
| localSocketManager.onError(clientSocket, error); | |
| } | |
| } | |
| /** | |
| * Sanitize exitCode to between 0-255, otherwise it may be considered invalid. | |
| * Out of bound exit codes would return with exit code `44` `Channel number out of range` in shell. | |
| * | |
| * @param clientSocket The {@link LocalClientSocket} to which the exit code will be sent. | |
| * @param exitCode The current exit code. | |
| * @return Returns the sanitized exit code. | |
| */ | |
| public static int sanitizeExitCode( LocalClientSocket clientSocket, int exitCode) { | |
| if (exitCode < 0 || exitCode > 255) { | |
| Logger.logWarn(LOG_TAG, "Ignoring invalid peer " + clientSocket.getPeerCred().getMinimalString() + " result value \"" + exitCode + "\" and force setting it to \"" + 1 + "\""); | |
| exitCode = 1; | |
| } | |
| return exitCode; | |
| } | |
| /** | |
| * Parse amCommandString into a list of arguments like normally done on shells like bourne shell. | |
| * Arguments are split on whitespaces unless quoted with single or double quotes. | |
| * Double quotes and backslashes can be escaped with backslashes in arguments surrounded. | |
| * Double quotes and backslashes can be escaped with backslashes in arguments surrounded with | |
| * double quotes. | |
| * | |
| * @param amCommandString The am command {@link String}. | |
| * @param amCommandList The {@link List<String>} to set list of arguments in. | |
| * @return Returns the {@code error} if parsing am command failed, otherwise {@code null}. | |
| */ | |
| public static Error parseAmCommand(String amCommandString, List<String> amCommandList) { | |
| if (amCommandString == null || amCommandString.isEmpty()) { | |
| return null; | |
| } | |
| try { | |
| amCommandList.addAll(ArgumentTokenizer.tokenize(amCommandString)); | |
| } catch (Exception e) { | |
| return AmSocketServerErrno.ERRNO_PARSE_AM_COMMAND_FAILED_WITH_EXCEPTION.getError(e, amCommandString, e.getMessage()); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Call termux-am-library to run the am command. | |
| * | |
| * @param context The {@link Context} to run am command with. | |
| * @param amCommandArray The am command array. | |
| * @param stdout The {@link StringBuilder} to set stdout in that is returned by the am command. | |
| * @param stderr The {@link StringBuilder} to set stderr in that is returned by the am command. | |
| * @param checkDisplayOverAppsPermission Check if {@link Manifest.permission#SYSTEM_ALERT_WINDOW} | |
| * has been granted if running on Android `>= 10` and | |
| * starting activity or service. | |
| * @return Returns the {@code error} if am command failed, otherwise {@code null}. | |
| */ | |
| public static Error runAmCommand( Context context, | |
| String[] amCommandArray, | |
| StringBuilder stdout, StringBuilder stderr, | |
| boolean checkDisplayOverAppsPermission) { | |
| try (ByteArrayOutputStream stdoutByteStream = new ByteArrayOutputStream(); | |
| PrintStream stdoutPrintStream = new PrintStream(stdoutByteStream); | |
| ByteArrayOutputStream stderrByteStream = new ByteArrayOutputStream(); | |
| PrintStream stderrPrintStream = new PrintStream(stderrByteStream)) { | |
| if (checkDisplayOverAppsPermission && amCommandArray.length >= 1 && | |
| (amCommandArray[0].equals("start") || amCommandArray[0].equals("startservice")) && | |
| !PermissionUtils.validateDisplayOverOtherAppsPermissionForPostAndroid10(context, true)) { | |
| throw new IllegalStateException(context.getString(R.string.error_display_over_other_apps_permission_not_granted, | |
| PackageUtils.getAppNameForPackage(context))); | |
| } | |
| new Am(stdoutPrintStream, stderrPrintStream, (Application) context.getApplicationContext()).run(amCommandArray); | |
| // Set stdout to value set by am command in stdoutPrintStream | |
| stdoutPrintStream.flush(); | |
| stdout.append(stdoutByteStream.toString(StandardCharsets.UTF_8.name())); | |
| // Set stderr to value set by am command in stderrPrintStream | |
| stderrPrintStream.flush(); | |
| stderr.append(stderrByteStream.toString(StandardCharsets.UTF_8.name())); | |
| } catch (Exception e) { | |
| return AmSocketServerErrno.ERRNO_RUN_AM_COMMAND_FAILED_WITH_EXCEPTION.getError(e, Arrays.toString(amCommandArray), e.getMessage()); | |
| } | |
| return null; | |
| } | |
| /** Implementation for {@link ILocalSocketManager} for {@link AmSocketServer}. */ | |
| public abstract static class AmSocketServerClient extends LocalSocketManagerClientBase { | |
| public void onClientAccepted( LocalSocketManager localSocketManager, | |
| LocalClientSocket clientSocket) { | |
| AmSocketServer.processAmClient(localSocketManager, clientSocket); | |
| super.onClientAccepted(localSocketManager, clientSocket); | |
| } | |
| } | |
| } | |
Xet Storage Details
- Size:
- 12.5 kB
- Xet hash:
- ed58ea8149a26d19650ac04d4814c9dcd3a4b6f83f7da2c63db0f30e979d360e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.