blob: 90a12e20759814435c3ef78f18c63974c505a66a [file] [log] [blame]
// 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.
// TODO(crbug/1142830) - Change all native terms when Android's subcomponents
// name are changed.
// Modify the filename to match the substitute term.
package arc
import (
"context"
"time"
"chromiumos/tast/common/testexec"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/sysutil"
"chromiumos/tast/testing"
)
type nativeBridgeConfig struct {
// ARM executables to be run.
armExecutables []string
// Binary translation library to be used.
binaryTranslationLibrary string
// Extra arguments to be passed to Chrome.New().
chromeExtraArgs []string
}
const (
// Android native ARM executables expected to be runnable via native bridge.
// They are generated by ArcNativeBridgeTestHelper in the Android source tree.
armExec = "hello_world_arm"
arm64Exec = "hello_world_arm64"
// Names of the binary translation libraries.
houdiniLib = "libhoudini.so"
ndkTranslationLib = "libndk_translation.so"
// Chrome feature for switching the binary translation library on a dual-translator board.
nativeBridgeExperimentFeature = "ArcNativeBridgeExperiment"
)
func init() {
testing.AddTest(&testing.Test{
Func: NativeBridge,
Desc: "Checks whether native bridge is properly set up for ARCVM",
Contacts: []string{"youkichihosoi@chromium.org", "arcvm-eng@google.com"},
SoftwareDeps: []string{"android_vm", "chrome"},
Attr: []string{"group:mainline", "informational"},
Params: []testing.Param{{
Name: "houdini",
Val: nativeBridgeConfig{
armExecutables: []string{armExec},
binaryTranslationLibrary: houdiniLib,
chromeExtraArgs: []string{"--disable-features=" + nativeBridgeExperimentFeature},
},
ExtraData: []string{armExec},
ExtraSoftwareDeps: []string{"houdini"},
}, {
Name: "houdini64",
Val: nativeBridgeConfig{
armExecutables: []string{arm64Exec},
binaryTranslationLibrary: houdiniLib,
chromeExtraArgs: []string{"--disable-features=" + nativeBridgeExperimentFeature},
},
ExtraData: []string{arm64Exec},
ExtraSoftwareDeps: []string{"houdini64"},
}, {
Name: "ndk_translation",
Val: nativeBridgeConfig{
armExecutables: []string{armExec},
binaryTranslationLibrary: ndkTranslationLib,
chromeExtraArgs: []string{"--enable-features=" + nativeBridgeExperimentFeature},
},
ExtraData: []string{armExec},
ExtraSoftwareDeps: []string{"ndk_translation"},
}, {
Name: "ndk_translation64",
Val: nativeBridgeConfig{
armExecutables: []string{arm64Exec},
binaryTranslationLibrary: ndkTranslationLib,
chromeExtraArgs: []string{"--enable-features=" + nativeBridgeExperimentFeature},
},
ExtraData: []string{arm64Exec},
ExtraSoftwareDeps: []string{"ndk_translation64"},
}},
Timeout: 7 * time.Minute,
})
}
func NativeBridge(ctx context.Context, s *testing.State) {
cfg := s.Param().(nativeBridgeConfig)
// Shorten the total context to make room for cleanup jobs.
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 30*time.Second)
defer cancel()
cr, err := chrome.New(ctx, chrome.ARCEnabled(), chrome.ExtraArgs(cfg.chromeExtraArgs...))
if err != nil {
s.Fatal("Failed to connect to Chrome: ", err)
}
defer func() {
if err := cr.Close(cleanupCtx); err != nil {
s.Fatal("Failed to close Chrome: ", err)
}
}()
a, err := arc.New(ctx, s.OutDir())
if err != nil {
s.Fatal("Failed to start ARC: ", err)
}
defer func() {
if a != nil {
a.Close(ctx)
}
}()
// Check whether binfmt_misc has been successfully unmounted.
ms, err := mountInfoForARCVM(ctx, a)
if err != nil {
s.Fatal("Failed to get mount info for ARCVM: ", err)
}
const binfmtMiscPath = "/proc/sys/fs/binfmt_misc"
for _, m := range ms {
if m.MountPath == binfmtMiscPath {
s.Fatalf("Failure: %q is not unmounted", binfmtMiscPath)
}
}
// Check whether the property for the binary translation library has been properly set.
const nativeBridgeProp = "ro.boot.native_bridge"
nb, err := a.GetProp(ctx, nativeBridgeProp)
if err != nil {
s.Fatalf("Failed to getprop %q: %v", nativeBridgeProp, err)
}
nbExpected := cfg.binaryTranslationLibrary
if nb != nbExpected {
s.Fatalf("Failure: the property %q is set to %q; expected to be %q", nativeBridgeProp, nb, nbExpected)
}
// Check whether ARM executables can be successfully run.
for _, exec := range cfg.armExecutables {
if err := pushAndExecute(ctx, a, s.DataPath(exec)); err != nil {
s.Fatalf("Failed to run ARM executable %q: %v", exec, err)
}
}
}
// pushAndExecute pushes an executable to Android's temporary directory and executes it.
func pushAndExecute(ctx context.Context, a *arc.ARC, execPath string) (retErr error) {
tmpExecPath, err := a.PushFileToTmpDir(ctx, execPath)
if err != nil {
return errors.Wrapf(err, "failed to push %q to tmpdir", execPath)
}
defer func() {
if err := a.Command(ctx, "rm", "-f", tmpExecPath).Run(testexec.DumpLogOnError); err != nil {
if retErr == nil {
retErr = errors.Wrapf(err, "failed to remove %q", tmpExecPath)
} else {
testing.ContextLogf(ctx, "Failed to remove %q: %v", tmpExecPath, err)
}
}
}()
if err := a.Command(ctx, "chmod", "0755", tmpExecPath).Run(testexec.DumpLogOnError); err != nil {
return errors.Wrapf(err, "failed to change the permission of %q", tmpExecPath)
}
if err := a.Command(ctx, tmpExecPath).Run(testexec.DumpLogOnError); err != nil {
return errors.Wrapf(err, "failed to execute %q", tmpExecPath)
}
return nil
}
// mountInfoForARCVM returns a list of mount point info for ARCVM.
func mountInfoForARCVM(ctx context.Context, a *arc.ARC) ([]sysutil.MountInfo, error) {
const mountInfoPath = "/proc/self/mountinfo"
mi, err := a.ReadFile(ctx, mountInfoPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to read mount info file %q", mountInfoPath)
}
return sysutil.ParseMountInfo(mi)
}