blob: 6da10da29d32734d4f24bb2903d9409b595b4ecc [file] [edit]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package androidtools
import (
"context"
"strings"
"time"
"go.chromium.org/chromiumos/config/go/test/api"
"go.chromium.org/luci/common/errors"
"go.chromium.org/infra/cros/lib/provisions/common"
"go.chromium.org/infra/cros/lib/provisions/fastboot"
"go.chromium.org/infra/cros/recovery/internal/env"
"go.chromium.org/infra/cros/recovery/internal/localtlw/localproxy"
"go.chromium.org/infra/cros/recovery/internal/log"
"go.chromium.org/infra/cros/recovery/tlw"
)
// FastbootDeviceRun runs a command against the device specified as DUT.
func FastbootDeviceRun(ctx context.Context, dut *tlw.Dut, timeout time.Duration, command string, args ...string) (Response, error) {
deviceSerial := FastbootDeviceSerial(dut)
if deviceSerial == "" {
return nil, errors.New("fastboot device run: serial does not exist")
}
return FastbootRun(ctx, dut, deviceSerial, timeout, command, args...)
}
// FastbootRun runs a fastboot command by adb-base.
func FastbootRun(ctx context.Context, dut *tlw.Dut, deviceSerial string, timeout time.Duration, command string, args ...string) (Response, error) {
client, err := adbClient(ctx, dut)
if err != nil {
return emptyResponse(), errors.WrapIf(err, "fastboot run")
}
if command == "" {
return nil, errors.Fmt("fastboot run: command is empty")
}
var newCommand string
var newArgs []string
if deviceSerial != "" {
// Adjust the command to target the specific device using its serial number.
// Some fastboot commands don't require a device serial, as they operate on the fastboot server itself.
newCommand = serialParam
newArgs = append([]string{deviceSerial, command}, args...)
} else {
// User is responsible for the command and we do not need to modify it.
newCommand = command
newArgs = args
}
res, err := fastbootRun(ctx, client, timeout, newCommand, newArgs...)
if err != nil {
return res, errors.WrapIf(err, "fastboot run")
}
if res.GetExitCode() != 0 {
return res, errors.Fmt("fastboot run: failed with exit code: %d", res.GetExitCode())
}
return res, nil
}
// fastbootRun runs a raw command by base-adb service and returns the result.
//
// An error is provided only if the service fails to execute the command or times out.
// Command execution status is always represented by the exit code in the response.
func fastbootRun(ctx context.Context, client api.ToolsServiceClient, timeout time.Duration, command string, args ...string) (Response, error) {
if command == "" {
return nil, errors.Fmt("fastboot run: command is empty")
}
// Fastboot commands always run with serial.
fullCmd := "fastboot " + command
if len(args) > 0 {
fullCmd += " " + strings.Join(args, " ")
}
log.Debugf(ctx, "Prepare to run fastboot command: %q.", fullCmd)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
var err error
var response Response
if client != nil {
response, err = client.FastbootRun(ctx, &api.FastbootRunRequest{
Command: command,
Params: args,
})
} else {
response, err = localRun(ctx, env.FastbootPath(), command, args...)
}
if response != nil {
printResponse(ctx, response)
}
if err != nil {
err = errors.Fmt("fastboot run: failed to execute command %q, finished with error: %v", fullCmd, err)
}
return response, errors.WrapIf(err, "fastboot run: command %q", fullCmd)
}
// FastbootDeviceSerial provides the serial address for the fastboot connection.
func FastbootDeviceSerial(dut *tlw.Dut) string {
if dut == nil || dut.Name == "" {
return ""
}
return "tcp:" + localproxy.BuildAddrPort(dut.Name, env.FastbootPort())
}
// NewCommonRequest creates a new CommonRequest for Fastboot.
func NewCommonRequest(ctx context.Context, dut *tlw.Dut, ping common.PingFunc) *fastboot.CommonRequest {
return &fastboot.CommonRequest{
Serial: ADBSerial(dut),
Ping: ping,
ADBRun: func(ctx context.Context, timeout time.Duration, command string, params ...string) (common.Response, error) {
return ADBRun(ctx, dut, timeout, command, params...)
},
FastbootRun: func(ctx context.Context, timeout time.Duration, command string, params ...string) (common.Response, error) {
return FastbootRun(ctx, dut, ADBSerial(dut), timeout, command, params...)
},
Logger: log.Get(ctx),
}
}