Changed: Refactor am socket server

The `AmSocketServer` now handles the entire logic for processing of am commands sent by clients and its results. This can be used by other apps as well to run their own am servers. The server started by `termux-app` will be managed by `TermuxAmSocketServer`. Read their javadocs for details.

The extended implementation `TermuxAmSocketServerClient` of `AmSocketServer.AmSocketServerClient`/`ILocalSocketManager` will also send a plugin error notification for all errors to the user instead of just logging to logcat since users are not very good at checking those, this should save dev time debugging problems. We may need to ignore notifications for some errors like broken pipe, based on their `Error` objects if they are normally expected, this requires further investigation.

The `TERMUX_APP_AM_SOCKET_SERVER_ENABLED` env variable will also be exported for all shell sessions and tasks for whether the server was successfully started on app startup. The user can disable the server by adding "run-termux-am-socket-server=false" to the "~/.termux/termux.properties" as implemented in 5f8a9222. The env variable will be checked by `$PREFIX/bin/termux-am` before attempting to connect.

The new path for the server socket is `/data/data/com.termux/files/apps/termux-app/termux-am/am.sock` as per `TERMUX_APP.APPS_DIR_PATH` added in bcd8f4c4.
This commit is contained in:
agnostic-apollo
2022-04-18 04:49:47 +05:00
parent 2aa7f43d1c
commit 53f26c9659
8 changed files with 482 additions and 18 deletions

View File

@@ -25,10 +25,6 @@ android {
implementation project(":terminal-view")
implementation project(":termux-shared")
implementation 'com.github.termux:termux-am-library:1.0'
}
defaultConfig {

View File

@@ -3,15 +3,14 @@ package com.termux.app;
import android.app.Application;
import android.content.Context;
import com.termux.am.Am;
import com.termux.shared.errors.Error;
import com.termux.shared.logger.Logger;
import com.termux.shared.shell.LocalSocketListener;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.termux.crash.TermuxCrashUtils;
import com.termux.shared.termux.file.TermuxFileUtils;
import com.termux.shared.termux.settings.preferences.TermuxAppSharedPreferences;
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
import com.termux.shared.termux.shell.am.TermuxAmSocketServer;
import com.termux.shared.termux.theme.TermuxThemeUtils;
public class TermuxApplication extends Application {
@@ -50,17 +49,8 @@ public class TermuxApplication extends Application {
Logger.logErrorExtended(LOG_TAG, "Create apps/termux-app directory failed\n" + error);
return;
}
}
if (LocalSocketListener.tryEstablishLocalSocketListener(this, (args, out, err) -> {
try {
new Am(out, err, this).run(args);
return 0;
} catch (Exception e) {
return 1;
}
}, TermuxConstants.TERMUX_FILES_DIR_PATH+"/api/am-socket", 100, 1000) == null) {
Logger.logWarn("TermuxApplication", "am socket cannot be created");
TermuxAmSocketServer.setupTermuxAmSocketServer(context);
}
}
@@ -74,4 +64,3 @@ public class TermuxApplication extends Application {
}
}

View File

@@ -26,6 +26,8 @@ android {
implementation "commons-io:commons-io:2.5"
implementation project(":terminal-view")
implementation 'com.github.termux:termux-am-library:1.0'
}
defaultConfig {

View File

@@ -0,0 +1,239 @@
package com.termux.shared.shell.am;
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.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 LocalSocketRunConfig} instance 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(@NonNull Context context,
@NonNull 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(@NonNull LocalSocketManager localSocketManager,
@NonNull 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));
// 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);
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(@NonNull LocalSocketManager localSocketManager,
@NonNull LocalClientSocket clientSocket,
int exitCode,
@Nullable String stdout, @Nullable 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(@NonNull 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.
* @return Returns the {@code error} if am command failed, otherwise {@code null}.
*/
public static Error runAmCommand(@NonNull Context context,
String[] amCommandArray,
@NonNull StringBuilder stdout, @NonNull StringBuilder stderr) {
try (ByteArrayOutputStream stdoutByteStream = new ByteArrayOutputStream();
PrintStream stdoutPrintStream = new PrintStream(stdoutByteStream);
ByteArrayOutputStream stderrByteStream = new ByteArrayOutputStream();
PrintStream stderrPrintStream = new PrintStream(stderrByteStream)) {
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 {
@Override
public void onClientAccepted(@NonNull LocalSocketManager localSocketManager,
@NonNull LocalClientSocket clientSocket) {
AmSocketServer.processAmClient(localSocketManager, clientSocket);
super.onClientAccepted(localSocketManager, clientSocket);
}
}
}

View File

@@ -0,0 +1,18 @@
package com.termux.shared.shell.am;
import com.termux.shared.errors.Errno;
public class AmSocketServerErrno extends Errno {
public static final String TYPE = "AmSocketServer Error";
/** Errors for {@link AmSocketServer} (100-150) */
public static final Errno ERRNO_PARSE_AM_COMMAND_FAILED_WITH_EXCEPTION = new Errno(TYPE, 100, "Parse am command `%1$s` failed.\nException: %2$s");
public static final Errno ERRNO_RUN_AM_COMMAND_FAILED_WITH_EXCEPTION = new Errno(TYPE, 101, "Run am command `%1$s` failed.\nException: %2$s");
AmSocketServerErrno(final String type, final int code, final String message) {
super(type, code, message);
}
}

View File

@@ -11,7 +11,7 @@ import java.util.Formatter;
import java.util.List;
/*
* Version: v0.40.0
* Version: v0.41.0
* SPDX-License-Identifier: MIT
*
* Changelog
@@ -236,6 +236,9 @@ import java.util.List;
*
* - 0.40.0 (2022-04-17)
* - Added `TERMUX_APPS_DIR_PATH` and `TERMUX_APP.APPS_DIR_PATH`.
*
* - 0.41.0 (2022-04-17)
* - Added `TERMUX_APP.TERMUX_AM_SOCKET_FILE_PATH`.
*/
/**
@@ -883,6 +886,9 @@ public final class TermuxConstants {
/** Termux apps directory path */
public static final String APPS_DIR_PATH = TERMUX_APPS_DIR_PATH + "/termux-app"; // Default: "/data/data/com.termux/files/apps/termux-app"
/** termux-am socket file path */
public static final String TERMUX_AM_SOCKET_FILE_PATH = APPS_DIR_PATH + "/termux-am/am.sock"; // Default: "/data/data/com.termux/files/apps/termux-app/termux-am/am.sock"
/** Termux app core activity name. */
public static final String TERMUX_ACTIVITY_NAME = TERMUX_PACKAGE_NAME + ".app.TermuxActivity"; // Default: "com.termux.app.TermuxActivity"

View File

@@ -28,9 +28,11 @@ public class TermuxShellUtils {
public static String TERMUX_IS_DEBUGGABLE_BUILD;
public static String TERMUX_APP_PID;
public static String TERMUX_APK_RELEASE;
public static Boolean TERMUX_APP_AM_SOCKET_SERVER_ENABLED;
public static String TERMUX_API_VERSION_NAME;
private static final String LOG_TAG = "TermuxShellUtils";
public static String getDefaultWorkingDirectoryPath() {
@@ -59,6 +61,8 @@ public class TermuxShellUtils {
environment.add("TERMUX_APP_PID=" + TERMUX_APP_PID);
if (TERMUX_APK_RELEASE != null)
environment.add("TERMUX_APK_RELEASE=" + TERMUX_APK_RELEASE);
if (TERMUX_APP_AM_SOCKET_SERVER_ENABLED != null)
environment.add("TERMUX_APP_AM_SOCKET_SERVER_ENABLED=" + TERMUX_APP_AM_SOCKET_SERVER_ENABLED);
if (TERMUX_API_VERSION_NAME != null)
environment.add("TERMUX_API_VERSION=" + TERMUX_API_VERSION_NAME);

View File

@@ -0,0 +1,210 @@
package com.termux.shared.termux.shell.am;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.termux.shared.errors.Error;
import com.termux.shared.logger.Logger;
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.am.AmSocketServer;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.termux.crash.TermuxCrashUtils;
import com.termux.shared.termux.plugins.TermuxPluginUtils;
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
import com.termux.shared.termux.settings.properties.TermuxPropertyConstants;
import com.termux.shared.termux.shell.TermuxShellUtils;
/**
* A wrapper for {@link AmSocketServer} for termux-app usage.
*
* The static {@link #termuxAmSocketServer} variable stores the {@link LocalSocketManager} for the
* {@link AmSocketServer}.
*
* The {@link TermuxAmSocketServerClient} extends the {@link AmSocketServer.AmSocketServerClient}
* class to also show plugin error notifications for errors and disallowed client connections in
* addition to logging the messages to logcat, which are only logged by {@link LocalSocketManagerClientBase}
* if log level is debug or higher for privacy issues.
*
* It uses a filesystem socket server with the socket file at
* {@link TermuxConstants.TERMUX_APP#TERMUX_AM_SOCKET_FILE_PATH}. It would normally only allow
* processes belonging to the termux user and root user to connect to it. If commands are sent by the
* root user, then the am commands executed will be run as the termux user and its permissions,
* capabilities and selinux context instead of root.
*
* The `$PREFIX/bin/termux-am` client connects to the server via `$PREFIX/bin/termux-am-socket` to
* run the am commands. It provides similar functionality to "$PREFIX/bin/am"
* (and "/system/bin/am"), but should be faster since it does not require starting a dalvik vm for
* every command as done by "am" via termux/TermuxAm.
*
* The server is started by termux-app Application class but is not started if
* {@link TermuxPropertyConstants#KEY_RUN_TERMUX_AM_SOCKET_SERVER} is `false` which can be done by
* adding the prop with value "false" to the "~/.termux/termux.properties" file. Changes
* require termux-app to be force stopped and restarted.
*
* The current state of the server can be checked with the
* {@link TermuxShellUtils#TERMUX_APP_AM_SOCKET_SERVER_ENABLED} env variable, which is exported
* for all shell sessions and tasks.
*
* https://github.com/termux/termux-am-socket
* https://github.com/termux/TermuxAm
*/
public class TermuxAmSocketServer {
public static final String LOG_TAG = "TermuxAmSocketServer";
public static final String TITLE = "TermuxAm";
/** The static instance for the {@link TermuxAmSocketServer} {@link LocalSocketManager}. */
private static LocalSocketManager termuxAmSocketServer;
/**
* Setup the {@link AmSocketServer} {@link LocalServerSocket} and start listening for
* new {@link LocalClientSocket} if enabled.
*
* @param context The {@link Context} for {@link LocalSocketManager}.
*/
public static void setupTermuxAmSocketServer(@NonNull Context context) {
// Start termux-am-socket server if enabled by user
boolean enabled = false;
if (TermuxAppSharedProperties.getProperties().shouldRunTermuxAmSocketServer()) {
Logger.logDebug(LOG_TAG, "Starting " + TITLE + " socket server since its enabled");
start(context);
if (termuxAmSocketServer != null && termuxAmSocketServer.isRunning()) {
enabled = true;
Logger.logDebug(LOG_TAG, TITLE + " socket server successfully started");
}
} else {
Logger.logDebug(LOG_TAG, "Not starting " + TITLE + " socket server since its not enabled");
}
// Once termux-app has started, the server state must not be changed since the variable is
// exported in shell sessions and tasks and if state is changed, then env of older shells will
// retain invalid value. User should force stop the app to update state after changing prop.
TermuxShellUtils.TERMUX_APP_AM_SOCKET_SERVER_ENABLED = enabled;
}
/**
* Create the {@link AmSocketServer} {@link LocalServerSocket} and start listening for new {@link LocalClientSocket}.
*/
public static synchronized void start(@NonNull Context context) {
stop();
LocalSocketRunConfig localSocketRunConfig = new LocalSocketRunConfig(TITLE,
TermuxConstants.TERMUX_APP.TERMUX_AM_SOCKET_FILE_PATH, new TermuxAmSocketServerClient());
termuxAmSocketServer = AmSocketServer.start(context, localSocketRunConfig);
}
/**
* Stop the {@link AmSocketServer} {@link LocalServerSocket} and stop listening for new {@link LocalClientSocket}.
*/
public static synchronized void stop() {
if (termuxAmSocketServer != null) {
Error error = termuxAmSocketServer.stop();
if (error != null) {
termuxAmSocketServer.onError(error);
}
termuxAmSocketServer = null;
}
}
/**
* Update the state of the {@link AmSocketServer} {@link LocalServerSocket} depending on current
* value of {@link TermuxPropertyConstants#KEY_RUN_TERMUX_AM_SOCKET_SERVER}.
*/
public static synchronized void updateState(@NonNull Context context) {
TermuxAppSharedProperties properties = TermuxAppSharedProperties.getProperties();
if (properties.shouldRunTermuxAmSocketServer()) {
if (termuxAmSocketServer == null) {
Logger.logDebug(LOG_TAG, "updateState: Starting " + TITLE + " socket server");
start(context);
}
} else {
if (termuxAmSocketServer != null) {
Logger.logDebug(LOG_TAG, "updateState: Disabling " + TITLE + " socket server");
stop();
}
}
}
/**
* Get {@link #termuxAmSocketServer}.
*/
public static synchronized LocalSocketManager getTermuxAmSocketServer() {
return termuxAmSocketServer;
}
/**
* Show an error notification on the {@link TermuxConstants#TERMUX_PLUGIN_COMMAND_ERRORS_NOTIFICATION_CHANNEL_ID}
* {@link TermuxConstants#TERMUX_PLUGIN_COMMAND_ERRORS_NOTIFICATION_CHANNEL_NAME} with a call
* to {@link TermuxPluginUtils#sendPluginCommandErrorNotification(Context, String, CharSequence, String, String)}.
*
* @param context The {@link Context} to send the notification with.
* @param error The {@link Error} generated.
* @param localSocketRunConfig The {@link LocalSocketRunConfig} for {@link LocalSocketManager}.
* @param clientSocket The optional {@link LocalClientSocket} for which the error was generated.
*/
public static synchronized void showErrorNotification(@NonNull Context context, @NonNull Error error,
@NonNull LocalSocketRunConfig localSocketRunConfig,
@Nullable LocalClientSocket clientSocket) {
TermuxPluginUtils.sendPluginCommandErrorNotification(context, LOG_TAG,
localSocketRunConfig.getTitle() + " Socket Server Error", error.getMinimalErrorString(),
LocalSocketManager.getErrorMarkdownString(error, localSocketRunConfig, clientSocket));
}
/** Enhanced implementation for {@link AmSocketServer.AmSocketServerClient} for {@link TermuxAmSocketServer}. */
public static class TermuxAmSocketServerClient extends AmSocketServer.AmSocketServerClient {
public static final String LOG_TAG = "TermuxAmSocketServerClient";
@Nullable
@Override
public Thread.UncaughtExceptionHandler getLocalSocketManagerClientThreadUEH(
@NonNull LocalSocketManager localSocketManager) {
// Use termux crash handler for socket listener thread just like used for main app process thread.
return TermuxCrashUtils.getCrashHandler(localSocketManager.getContext());
}
@Override
public void onError(@NonNull LocalSocketManager localSocketManager,
@Nullable LocalClientSocket clientSocket, @NonNull Error error) {
// Don't show notification if server is not running since errors may be triggered
// when server is stopped and server and client sockets are closed.
if (localSocketManager.isRunning()) {
TermuxAmSocketServer.showErrorNotification(localSocketManager.getContext(), error,
localSocketManager.getLocalSocketRunConfig(), clientSocket);
}
// But log the exception
super.onError(localSocketManager, clientSocket, error);
}
@Override
public void onDisallowedClientConnected(@NonNull LocalSocketManager localSocketManager,
@NonNull LocalClientSocket clientSocket, @NonNull Error error) {
// Always show notification and log error regardless of if server is running or not
TermuxAmSocketServer.showErrorNotification(localSocketManager.getContext(), error,
localSocketManager.getLocalSocketRunConfig(), clientSocket);
super.onDisallowedClientConnected(localSocketManager, clientSocket, error);
}
@Override
protected String getLogTag() {
return LOG_TAG;
}
}
}