| // 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), |
| } |
| } |