blob: 0438d546bc54b8f9a4cf687898ba61f0eef324fa [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adblib
import com.android.adblib.utils.LineBatchShellV2Collector
import com.android.adblib.utils.LineShellV2Collector
import com.android.adblib.utils.TextShellV2Collector
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import java.time.Duration
import java.util.concurrent.TimeoutException
/**
* Supports customization of various aspects of the execution of a shell command on a device,
* including automatically falling back from [AdbDeviceServices.shellV2] to legacy protocols
* on older devices.
*
* Once a [ShellCommand] is configured with various `withXxx` methods, use the [execute]
* method to launch the shell command execution, returning a [Flow<T>][Flow].
*
* @see [AdbDeviceServices.shellCommand]
* @see [AdbDeviceServices.shellV2]
* @see [AdbDeviceServices.exec]
* @see [AdbDeviceServices.shell]
*/
interface ShellCommand<T> {
/**
* Applies a [ShellV2Collector] to transfer the raw binary shell command output.
* This change the type of this [ShellCommand] from [T] to the final target type [U].
*/
fun <U> withCollector(collector: ShellV2Collector<U>): ShellCommand<U>
/**
* Applies a legacy [ShellCollector] to transfer the raw binary shell command output.
* This change the type of this [ShellCommand] from [T] to the final target type [U].
*/
fun <U> withLegacyCollector(collector: ShellCollector<U>): ShellCommand<U>
/**
* The [AdbInputChannel] to send to the device for `stdin`.
*
* The default value is `null`.
*/
fun withStdin(stdinChannel: AdbInputChannel?): ShellCommand<T>
/**
* Applies a [timeout] that triggers [TimeoutException] exception if the shell command
* does not terminate within the specified [Duration].
*
* The default value is [INFINITE_DURATION].
*/
fun withCommandTimeout(timeout: Duration): ShellCommand<T>
/**
* Applies a [timeout] that triggers a [TimeoutException] exception when the command does
* not generate any output (`stdout` or `stderr`) for the specified [Duration].
*
* The default value is [INFINITE_DURATION].
*/
fun withCommandOutputTimeout(timeout: Duration): ShellCommand<T>
/**
* Overrides the default buffer size used for buffering `stdout`, `stderr` and `stdin`.
*
* The default value is [DEFAULT_SHELL_BUFFER_SIZE].
*/
fun withBufferSize(size: Int): ShellCommand<T>
/**
* Allows [execute] to use [AdbDeviceServices.shellV2] if available.
*
* The default value is `true`.
*/
fun allowShellV2(value: Boolean): ShellCommand<T>
/**
* Allows [execute] to fall back to [AdbDeviceServices.exec] if [AdbDeviceServices.shellV2]
* is not available or not allowed.
*
* The default value is `true`.
*/
fun allowLegacyExec(value: Boolean): ShellCommand<T>
/**
* Allows [execute] to fall back to [AdbDeviceServices.shell] if [AdbDeviceServices.shellV2]
* and [AdbDeviceServices.exec] are not available or not allowed.
*
* The default value is `true`.
*/
fun allowLegacyShell(value: Boolean): ShellCommand<T>
/**
* Allows overriding the shell command to [execute] on the device just before
* execution starts, when the [Protocol] to be used is known.
*
* This can be useful, for example, for providing custom shell handling in case
* [AdbDeviceServices.shellV2] is not supported and [execute] has to fall back to
* [AdbDeviceServices.exec].
*
* The default value is a `no-op`.
*/
fun withCommandOverride(commandOverride: (String, Protocol) -> String): ShellCommand<T>
/**
* Returns a [Flow] that executes the shell command on the device, according to the
* various customization rules set by the `withXxx` methods.
*
* If [withCollector] or [withLegacyCollector] was not invoked before [execute],
* an [IllegalArgumentException] is thrown, as a shell collector is mandatory.
*
* Once [execute] is called, further customization is not allowed.
*/
fun execute(): Flow<T>
/**
* The protocol used for [executing][execute] a [ShellCommand]
*/
enum class Protocol {
SHELL_V2,
SHELL,
EXEC
}
}
fun <T> ShellCommand<T>.withLineCollector(): ShellCommand<ShellCommandOutputElement> {
return this.withCollector(LineShellV2Collector())
}
fun <T> ShellCommand<T>.withLineBatchCollector(): ShellCommand<BatchShellCommandOutputElement> {
return this.withCollector(LineBatchShellV2Collector())
}
fun <T> ShellCommand<T>.withTextCollector(): ShellCommand<ShellCommandOutput> {
return this.withCollector(TextShellV2Collector())
}