| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package adb |
| |
| import ( |
| "context" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strconv" |
| "strings" |
| |
| "chromiumos/tast/common/testexec" |
| "chromiumos/tast/errors" |
| ) |
| |
| // AndroidTmpDirPath is the path of tmp directory in Android. |
| const AndroidTmpDirPath = "/data/local/tmp" |
| |
| // PullFile copies a file in Android to Chrome OS with adb pull. |
| func (d *Device) PullFile(ctx context.Context, src, dst string) error { |
| return d.Command(ctx, "pull", src, dst).Run(testexec.DumpLogOnError) |
| } |
| |
| // PushFile copies a file in Chrome OS to Android with adb push. |
| func (d *Device) PushFile(ctx context.Context, src, dst string) error { |
| return d.Command(ctx, "push", src, dst).Run(testexec.DumpLogOnError) |
| } |
| |
| // PushFileToTmpDir copies a file in Chrome OS to Android temp directory. |
| // The destination path within the Android is returned. |
| func (d *Device) PushFileToTmpDir(ctx context.Context, src string) (string, error) { |
| dst := filepath.Join(AndroidTmpDirPath, filepath.Base(src)) |
| if err := d.PushFile(ctx, src, dst); err != nil { |
| d.ShellCommand(ctx, "rm", dst).Run(testexec.DumpLogOnError) |
| return "", errors.Wrapf(err, "failed to adb push %v to %v", src, dst) |
| } |
| return dst, nil |
| } |
| |
| // ReadFile reads a file in Android file system with adb pull. |
| func (d *Device) ReadFile(ctx context.Context, filename string) ([]byte, error) { |
| f, err := ioutil.TempFile("", "adb") |
| if err != nil { |
| return nil, err |
| } |
| defer os.Remove(f.Name()) |
| |
| if err = f.Close(); err != nil { |
| return nil, err |
| } |
| |
| if err = d.PullFile(ctx, filename, f.Name()); err != nil { |
| return nil, err |
| } |
| return ioutil.ReadFile(f.Name()) |
| } |
| |
| // WriteFile writes to a file in Android file system with adb push. |
| func (d *Device) WriteFile(ctx context.Context, filename string, data []byte) error { |
| f, err := ioutil.TempFile("", "adb") |
| if err != nil { |
| return err |
| } |
| defer func() { |
| f.Close() |
| os.Remove(f.Name()) |
| }() |
| if err := f.Chmod(0600); err != nil { |
| return err |
| } |
| if _, err := f.Write(data); err != nil { |
| return err |
| } |
| if err := f.Close(); err != nil { |
| return err |
| } |
| |
| return d.PushFile(ctx, f.Name(), filename) |
| } |
| |
| // FileSize returns the size of the specified file in bytes. Returns an error if the file does not exist. |
| // Note: In contrast to PkgFileSize, FileSize accesses files via adb commands. |
| func (d *Device) FileSize(ctx context.Context, filename string) (int64, error) { |
| // `stat -c %s` measures the size of a file in bytes. |
| statOutput, err := d.ShellCommand(ctx, "stat", "-c", "%s", filename).Output(testexec.DumpLogOnError) |
| if err != nil { |
| return 0, errors.Wrapf(err, "could not determine size of file: %s", filename) |
| } |
| |
| fileSize, err := strconv.ParseInt(strings.TrimSpace(string(statOutput)), 10, 64) |
| if err != nil { |
| return 0, errors.Wrapf(err, "failed to parse file size for %q; got: %q want: decimal number", filename, string(statOutput)) |
| } |
| |
| return fileSize, nil |
| } |
| |
| // TempDir creates a temporary directory under AndroidTmpDirPath in Android, |
| // then returns its absolute path. |
| // It is caller's responsibility to remove all the contents in the directory |
| // after its use. One of the typical use cases will be as follows: |
| // |
| // tmpdir, err := a.MktempDir(ctx) |
| // if err != nil { |
| // ... // error handling |
| // } |
| // defer a.RemoveAll(tmpdir) |
| // ... // Main code using tmpdir. |
| func (d *Device) TempDir(ctx context.Context) (string, error) { |
| out, err := d.ShellCommand(ctx, "mktemp", "-d", "-p", AndroidTmpDirPath).Output(testexec.DumpLogOnError) |
| if err != nil { |
| return "", err |
| } |
| return strings.TrimSpace(string(out)), nil |
| } |
| |
| // RemoveAll removes all files and directories under the path in Android. |
| // The path must be abspath. |
| func (d *Device) RemoveAll(ctx context.Context, path string) error { |
| if !filepath.IsAbs(path) { |
| return errors.Errorf("path (%q) needs to be absolute path", path) |
| } |
| return d.ShellCommand(ctx, "rm", "-rf", path).Run(testexec.DumpLogOnError) |
| } |
| |
| // ListContents returns the contents of the given path. |
| func (d *Device) ListContents(ctx context.Context, path string) ([]string, error) { |
| res, err := d.ShellCommand(ctx, "ls", path).Output(testexec.DumpLogOnError) |
| if err != nil { |
| return nil, errors.Wrapf(err, "failed to list contents under %v", path) |
| } |
| if len(res) == 0 { |
| return nil, nil |
| } |
| return strings.Split(strings.TrimRight(string(res), "\n"), "\n"), nil |
| } |
| |
| // RemoveContents removes the contents under the path in Android. |
| // The path must be abspath. |
| func (d *Device) RemoveContents(ctx context.Context, path string) error { |
| if !filepath.IsAbs(path) { |
| return errors.Errorf("path (%q) needs to be absolute path", path) |
| } |
| files, err := d.ListContents(ctx, path) |
| if err != nil { |
| return err |
| } |
| for _, f := range files { |
| if err := d.RemoveAll(ctx, filepath.Join(path, f)); err != nil { |
| return errors.Wrapf(err, "failed to remove %v", filepath.Join(path, f)) |
| } |
| } |
| return nil |
| } |
| |
| // SHA256Sum returns the sha256sum of the specified file as a string. |
| func (d *Device) SHA256Sum(ctx context.Context, filename string) (string, error) { |
| res, err := d.ShellCommand(ctx, "sha256sum", filename).Output(testexec.DumpLogOnError) |
| if err != nil { |
| return "", errors.Wrap(err, "failed to run sha256sum command for the target file") |
| } |
| return strings.Fields(string(res))[0], nil |
| } |