1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.device;
18 
19 import static org.hamcrest.CoreMatchers.is;
20 import static org.hamcrest.MatcherAssert.assertThat;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 import static org.junit.Assume.assumeTrue;
26 
27 import com.android.tradefed.invoker.TestInformation;
28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
29 import com.android.tradefed.testtype.IDeviceTest;
30 import com.android.tradefed.testtype.ITestInformationReceiver;
31 import com.android.tradefed.util.CommandResult;
32 import com.android.tradefed.util.CommandStatus;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.io.File;
39 import java.io.FileNotFoundException;
40 import java.util.Arrays;
41 
42 /**
43  * Functional tests for creating Microdroid {@link TestDevice}.
44  *
45  * <p>Requires a physical device to be connected.
46  */
47 @RunWith(DeviceJUnit4ClassRunner.class)
48 public class MicrodroidFuncTest implements IDeviceTest, ITestInformationReceiver {
49     private static final String APK_NAME = "MicrodroidTestApp.apk";
50     private static final String CONFIG_PATH = "assets/vm_config.json"; // path inside the APK
51 
52     private TestDevice mTestDevice;
53     private TestInformation mTestInformation;
54 
55     @Override
setDevice(ITestDevice device)56     public void setDevice(ITestDevice device) {
57         mTestDevice = (TestDevice) device;
58     }
59 
60     @Override
getDevice()61     public ITestDevice getDevice() {
62         return mTestDevice;
63     }
64 
65     @Override
setTestInformation(TestInformation testInformation)66     public void setTestInformation(TestInformation testInformation) {
67         mTestInformation = testInformation;
68     }
69 
70     @Override
getTestInformation()71     public TestInformation getTestInformation() {
72         return mTestInformation;
73     }
74 
75     @Before
setup()76     public void setup() throws Exception {
77         mTestDevice.waitForDeviceAvailable();
78         assumeTrue(mTestDevice.supportsMicrodroid());
79         mTestDevice.executeShellV2Command("logcat -c");
80     }
81 
82     // tests if a microdroid TestDevice is created and stopped properly
83     @Test
testStartAndShutdownMicrodroid()84     public void testStartAndShutdownMicrodroid()
85             throws DeviceNotAvailableException, FileNotFoundException {
86         final ITestDevice microdroid =
87                 TestDevice.MicrodroidBuilder.fromFile(
88                                 findTestFile(mTestInformation, APK_NAME), CONFIG_PATH)
89                         .debugLevel("full")
90                         .memoryMib(0)
91                         .numCpus(1)
92                         .build(mTestDevice);
93         assertNotNull(microdroid);
94 
95         MicrodroidHelper microdroidHelper = new MicrodroidHelper();
96 
97         // Test writing to /data partition
98         runOnDevice(microdroid, "echo MicrodroidTest > /data/local/tmp/test.txt");
99         assertThat(runOnDevice(microdroid, "cat /data/local/tmp/test.txt"), is("MicrodroidTest"));
100 
101         // Check if the APK & its idsig partitions exist
102         final String apkPartition = "/dev/block/by-name/microdroid-apk";
103         assertTrue(microdroid.doesFileExist(apkPartition));
104         final String apkIdsigPartition = "/dev/block/by-name/microdroid-apk-idsig";
105         assertTrue(microdroid.doesFileExist(apkIdsigPartition));
106         // Check the vm-instance partition as well
107         final String vmInstancePartition = "/dev/block/by-name/vm-instance";
108         assertTrue(microdroid.doesFileExist(vmInstancePartition));
109 
110         // Check if the native library in the APK is has correct filesystem info
111         final String[] abis =
112                 runOnDevice(microdroid, "getprop", "ro.product.cpu.abilist").split(",");
113         assertThat(abis.length, is(1));
114         final String testLib = "/mnt/apk/lib/" + abis[0] + "/MicrodroidTestNativeLib.so";
115         final String label = "u:object_r:system_file:s0";
116         assertThat(runOnDevice(microdroid, "ls", "-Z", testLib), is(label + " " + testLib));
117 
118         // Check if the command in vm_config.json was executed by examining the side effect of the
119         // command
120         assertThat(runOnDevice(microdroid, "getprop", "debug.microdroid.app.run"), is("true"));
121         assertThat(
122                 runOnDevice(microdroid, "getprop", "debug.microdroid.app.sublib.run"), is("true"));
123 
124         // Check that no denials have happened so far
125         assertThat(runOnDevice(microdroid, "logcat -d -e 'avc:[[:space:]]{1,2}denied'"), is(""));
126         assertThat(
127                 runOnDevice(microdroid, "cat /proc/cpuinfo | grep processor | wc -l"),
128                 is(Integer.toString(1)));
129 
130         assertThat(
131                 microdroidHelper.runOnMicrodroid(microdroid.getSerialNumber(), "echo true"),
132                 is("true"));
133         mTestDevice.shutdownMicrodroid(microdroid);
134         assertNull(microdroidHelper.tryRunOnMicrodroid(microdroid.getSerialNumber(), "echo true"));
135     }
136 
runOnDevice(ITestDevice device, String... cmd)137     private String runOnDevice(ITestDevice device, String... cmd)
138             throws DeviceNotAvailableException {
139         CommandResult result = device.executeShellV2Command(join(cmd));
140         if (result.getStatus() != CommandStatus.SUCCESS) {
141             fail(join(cmd) + " has failed: " + result);
142         }
143         return result.getStdout().trim();
144     }
145 
join(String... strs)146     private static String join(String... strs) {
147         return String.join(" ", Arrays.asList(strs));
148     }
149 
findTestFile(TestInformation testInformation, String name)150     private static File findTestFile(TestInformation testInformation, String name)
151             throws FileNotFoundException {
152         return testInformation.getDependencyFile(name, false);
153     }
154 }
155