• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.sdkext.extensions;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assume.assumeTrue;
26 import org.junit.Ignore;
27 
28 import android.cts.install.lib.host.InstallUtilsHost;
29 
30 import com.android.os.ext.testing.CurrentVersion;
31 import com.android.tests.rollback.host.AbandonSessionsRule;
32 import com.android.tradefed.device.ITestDevice.ApexInfo;
33 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
34 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
35 import com.android.tradefed.util.CommandResult;
36 
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Rule;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 
43 import java.io.File;
44 import java.lang.NumberFormatException;
45 import java.time.Duration;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48 
49 @RunWith(DeviceJUnit4ClassRunner.class)
50 public class SdkExtensionsHostTest extends BaseHostJUnit4Test {
51 
52     private static final String APP_FILENAME = "sdkextensions_e2e_test_app.apk";
53     private static final String APP_PACKAGE = "com.android.sdkext.extensions.apps";
54     private static final String APP_R12_FILENAME = "sdkextensions_e2e_test_app_req_r12.apk";
55     private static final String APP_R12_PACKAGE = "com.android.sdkext.extensions.apps.r12";
56     private static final String APP_S12_FILENAME = "sdkextensions_e2e_test_app_req_s12.apk";
57     private static final String APP_S12_PACKAGE = "com.android.sdkext.extensions.apps.s12";
58     private static final String APP_R45_FILENAME = "sdkextensions_e2e_test_app_req_r45.apk";
59     private static final String APP_R45_PACKAGE = "com.android.sdkext.extensions.apps.r45";
60     private static final String APP_S45_FILENAME = "sdkextensions_e2e_test_app_req_s45.apk";
61     private static final String APP_S45_PACKAGE = "com.android.sdkext.extensions.apps.s45";
62     private static final String MEDIA_FILENAME = "test_com.android.media.apex";
63     private static final String SDKEXTENSIONS_FILENAME = "test_com.android.sdkext.apex";
64 
65     private static final Duration BOOT_COMPLETE_TIMEOUT = Duration.ofMinutes(2);
66 
67     private final InstallUtilsHost mInstallUtils = new InstallUtilsHost(this);
68 
69     private Boolean mIsAtLeastS = null;
70     private Boolean mIsAtLeastT = null;
71 
72     @Rule
73     public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
74 
75     @Before
setUp()76     public void setUp() throws Exception {
77         assumeTrue("Updating APEX is not supported", mInstallUtils.isApexUpdateSupported());
78     }
79 
80     @Before
installTestApp()81     public void installTestApp() throws Exception {
82         File testAppFile = mInstallUtils.getTestFile(APP_FILENAME);
83         String installResult = getDevice().installPackage(testAppFile, true);
84         assertNull(installResult);
85     }
86 
87     @Before // Generally not needed, but local test devices are sometimes in a "bad" start state.
88     @After
cleanup()89     public void cleanup() throws Exception {
90         getDevice().uninstallPackage(APP_PACKAGE);
91         uninstallApexes(SDKEXTENSIONS_FILENAME, MEDIA_FILENAME);
92     }
93 
94     @Test
95     @Ignore("b/274764792")
testDefault()96     public void testDefault() throws Exception {
97         assertVersionDefault();
98     }
99 
100     @Test
101     @Ignore("b/274764792")
upgradeOneApexWithBump()102     public void upgradeOneApexWithBump()  throws Exception {
103         assertVersionDefault();
104         mInstallUtils.installApexes(SDKEXTENSIONS_FILENAME);
105         reboot();
106 
107         // Version 12 requires sdkext, which is fulfilled
108         // Version 45 requires sdkext + media, which isn't fulfilled
109         assertRVersionEquals(12);
110         assertSVersionEquals(12);
111         assertTestMethodsPresent(); // 45 APIs are available on 12 too.
112     }
113 
114     @Test
115     @Ignore("b/274764792")
upgradeOneApex()116     public void upgradeOneApex() throws Exception {
117         // Version 45 requires updated sdkext and media, so updating just media changes nothing.
118         assertVersionDefault();
119         mInstallUtils.installApexes(MEDIA_FILENAME);
120         reboot();
121         assertVersionDefault();
122     }
123 
124     @Test
125     @Ignore("b/274764792")
upgradeTwoApexes()126     public void upgradeTwoApexes() throws Exception {
127         // Updating sdkext and media bumps the version to 45.
128         assertVersionDefault();
129         mInstallUtils.installApexes(MEDIA_FILENAME, SDKEXTENSIONS_FILENAME);
130         reboot();
131         assertVersion45();
132     }
133 
canInstallApp(String filename, String packageName)134     private boolean canInstallApp(String filename, String packageName) throws Exception {
135         File appFile = mInstallUtils.getTestFile(filename);
136         String installResult = getDevice().installPackage(appFile, true);
137         if (installResult != null) {
138             return false;
139         }
140         assertNull(getDevice().uninstallPackage(packageName));
141         return true;
142     }
143 
getExtensionVersionFromSysprop(String v)144     private String getExtensionVersionFromSysprop(String v) throws Exception {
145         String command = "getprop build.version.extensions." + v;
146         CommandResult res = getDevice().executeShellV2Command(command);
147         assertEquals(0, (int) res.getExitCode());
148         return res.getStdout().replace("\n", "");
149     }
150 
broadcast(String action, String extra)151     private String broadcast(String action, String extra) throws Exception {
152         String command = getBroadcastCommand(action, extra);
153         CommandResult res = getDevice().executeShellV2Command(command);
154         assertEquals(0, (int) res.getExitCode());
155         Matcher matcher = Pattern.compile("data=\"([^\"]+)\"").matcher(res.getStdout());
156         assertTrue("Unexpected output from am broadcast: " + res.getStdout(), matcher.find());
157         return matcher.group(1);
158     }
159 
broadcastForBoolean(String action, String extra)160     private boolean broadcastForBoolean(String action, String extra) throws Exception {
161         String result = broadcast(action, extra);
162         if (result.equals("true") || result.equals("false")) {
163             return result.equals("true");
164         }
165         throw getAppParsingError(result);
166     }
167 
broadcastForInt(String action, String extra)168     private int broadcastForInt(String action, String extra) throws Exception {
169         String result = broadcast(action, extra);
170         try {
171             return Integer.parseInt(result);
172         } catch (NumberFormatException e) {
173             throw getAppParsingError(result);
174         }
175     }
176 
getAppParsingError(String result)177     private Error getAppParsingError(String result) {
178         String message = "App error! Full stack trace in logcat (grep for SdkExtensionsE2E): ";
179         return new AssertionError(message + result);
180     }
181 
assertVersionDefault()182     private void assertVersionDefault() throws Exception {
183         int expected = isAtLeastT() ? CurrentVersion.T_BASE_VERSION
184             : isAtLeastS() ? CurrentVersion.S_BASE_VERSION
185             : CurrentVersion.R_BASE_VERSION;
186         assertRVersionEquals(expected);
187         assertSVersionEquals(expected);
188         assertTestMethodsNotPresent();
189     }
190 
assertVersion45()191     private void assertVersion45() throws Exception {
192         assertRVersionEquals(45);
193         assertSVersionEquals(45);
194         assertTestMethodsPresent();
195     }
196 
assertTestMethodsNotPresent()197     private void assertTestMethodsNotPresent() throws Exception {
198         assertTrue(broadcastForBoolean("MAKE_CALLS_DEFAULT", null));
199     }
200 
assertTestMethodsPresent()201     private void assertTestMethodsPresent() throws Exception {
202         if (isAtLeastS()) {
203             assertTrue(broadcastForBoolean("MAKE_CALLS_45", null));
204         } else {
205             // The APIs in the test apex are not currently getting installed correctly
206             // on Android R devices because they rely on the dynamic classpath feature.
207             // TODO(b/234361913): fix this
208             assertTestMethodsNotPresent();
209         }
210     }
211 
assertRVersionEquals(int version)212     private void assertRVersionEquals(int version) throws Exception {
213         int appValue = broadcastForInt("GET_SDK_VERSION", "r");
214         String syspropValue = getExtensionVersionFromSysprop("r");
215         assertEquals(version, appValue);
216         assertEquals(String.valueOf(version), syspropValue);
217         assertEquals(version >= 12, canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
218         assertEquals(version >= 45, canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
219     }
220 
assertSVersionEquals(int version)221     private void assertSVersionEquals(int version) throws Exception {
222         int appValue = broadcastForInt("GET_SDK_VERSION", "s");
223         String syspropValue = getExtensionVersionFromSysprop("s");
224         if (isAtLeastS()) {
225             assertEquals(version, appValue);
226             assertEquals(String.valueOf(version), syspropValue);
227 
228             // These APKs require the same R version as they do S version.
229             int minVersion = Math.min(version, broadcastForInt("GET_SDK_VERSION", "r"));
230             assertEquals(minVersion >= 12, canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
231             assertEquals(minVersion >= 45, canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
232         } else {
233             assertEquals(0, appValue);
234             assertEquals("", syspropValue);
235             assertFalse(canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
236             assertFalse(canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
237         }
238     }
239 
getBroadcastCommand(String action, String extra)240     private static String getBroadcastCommand(String action, String extra) {
241         String cmd = "am broadcast";
242         cmd += " -a com.android.sdkext.extensions.apps." + action;
243         if (extra != null) {
244             cmd += " -e extra " + extra;
245         }
246         cmd += " -n com.android.sdkext.extensions.apps/.Receiver";
247         return cmd;
248     }
249 
isAtLeastS()250     private boolean isAtLeastS() throws Exception {
251         if (mIsAtLeastS == null) {
252             mIsAtLeastS = broadcastForBoolean("IS_AT_LEAST", "s");
253         }
254         return mIsAtLeastS;
255     }
256 
isAtLeastT()257     private boolean isAtLeastT() throws Exception {
258         if (mIsAtLeastT == null) {
259             mIsAtLeastT = broadcastForBoolean("IS_AT_LEAST", "t");
260         }
261         return mIsAtLeastT;
262     }
263 
uninstallApexes(String... filenames)264     private boolean uninstallApexes(String... filenames) throws Exception {
265         boolean reboot = false;
266         for (String filename : filenames) {
267             ApexInfo apex = mInstallUtils.getApexInfo(mInstallUtils.getTestFile(filename));
268             String res = getDevice().uninstallPackage(apex.name);
269             // res is null for successful uninstalls (non-null likely implesfactory version).
270             reboot |= res == null;
271         }
272         if (reboot) {
273             reboot();
274             return true;
275         }
276         return false;
277     }
278 
reboot()279     private void reboot() throws Exception {
280         getDevice().reboot();
281         boolean success = getDevice().waitForBootComplete(BOOT_COMPLETE_TIMEOUT.toMillis());
282         assertWithMessage("Device didn't boot in %s", BOOT_COMPLETE_TIMEOUT).that(success).isTrue();
283     }
284 }
285