1 /*
2  * Copyright (C) 2017 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 package android.appsecurity.cts;
17 
18 import static org.junit.Assert.assertTrue;
19 import static org.junit.Assert.assertFalse;
20 
21 import android.platform.test.annotations.AppModeFull;
22 
23 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
24 
25 import org.junit.After;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 
30 @RunWith(DeviceJUnit4ClassRunner.class)
31 @AppModeFull(reason = "Overlays cannot be instant apps")
32 public class OverlayHostTest extends BaseAppSecurityTest {
33 
34     // Test applications
35     private static final String TARGET_OVERLAYABLE_APK = "CtsOverlayTarget.apk";
36     private static final String TARGET_NO_OVERLAYABLE_APK = "CtsOverlayTargetNoOverlayable.apk";
37 
38     private static final String OVERLAY_ANDROID_APK = "CtsOverlayAndroid.apk";
39     private static final String OVERLAY_ALL_APK = "CtsOverlayPolicyAll.apk";
40     private static final String OVERLAY_ALL_HAS_CODE_APK = "CtsOverlayPolicyAllHasCode.apk";
41     private static final String OVERLAY_ALL_NO_NAME_APK = "CtsOverlayPolicyAllNoName.apk";
42     private static final String OVERLAY_ALL_NO_NAME_DIFFERENT_CERT_APK =
43             "CtsOverlayPolicyAllNoNameDifferentCert.apk";
44     private static final String OVERLAY_ALL_PIE_APK = "CtsOverlayPolicyAllPie.apk";
45     private static final String OVERLAY_PRODUCT_APK = "CtsOverlayPolicyProduct.apk";
46     private static final String OVERLAY_SYSTEM_APK = "CtsOverlayPolicySystem.apk";
47     private static final String OVERLAY_VENDOR_APK = "CtsOverlayPolicyVendor.apk";
48     private static final String OVERLAY_DIFFERENT_SIGNATURE_APK = "CtsOverlayPolicySignatureDifferent.apk";
49 
50     // Test application package names
51     private static final String TARGET_PACKAGE = "com.android.cts.overlay.target";
52     private static final String OVERLAY_ANDROID_PACKAGE = "com.android.cts.overlay.android";
53     private static final String OVERLAY_ALL_PACKAGE = "com.android.cts.overlay.all";
54     private static final String OVERLAY_PRODUCT_PACKAGE = "com.android.cts.overlay.policy.product";
55     private static final String OVERLAY_SYSTEM_PACKAGE = "com.android.cts.overlay.policy.system";
56     private static final String OVERLAY_VENDOR_PACKAGE = "com.android.cts.overlay.policy.vendor";
57     private static final String OVERLAY_DIFFERENT_SIGNATURE_PACKAGE = "com.android.cts.overlay.policy.signature";
58 
59     // Test application test class
60     private static final String TEST_APP_APK = "CtsOverlayApp.apk";
61     private static final String TEST_APP_PACKAGE = "com.android.cts.overlay.app";
62     private static final String TEST_APP_CLASS = "com.android.cts.overlay.app.OverlayableTest";
63 
64     // Overlay states
65     private static final String STATE_DISABLED = "STATE_DISABLED";
66     private static final String STATE_ENABLED = "STATE_ENABLED";
67     private static final String STATE_NO_IDMAP = "STATE_NO_IDMAP";
68 
69     private static final long OVERLAY_WAIT_TIMEOUT = 10000; // 10 seconds
70 
71     @Before
setUp()72     public void setUp() throws Exception {
73         new InstallMultiple().addFile(TEST_APP_APK).run();
74     }
75 
76     @After
tearDown()77     public void tearDown() throws Exception {
78         getDevice().uninstallPackage(TEST_APP_PACKAGE);
79     }
80 
getStateForOverlay(String overlayPackage)81     private String getStateForOverlay(String overlayPackage) throws Exception {
82         String result = getDevice().executeShellCommand("cmd overlay dump");
83 
84         String overlayPackageForCurrentUser = overlayPackage + ":" + getDevice().getCurrentUser();
85 
86         int startIndex = result.indexOf(overlayPackageForCurrentUser);
87         if (startIndex < 0) {
88             return null;
89         }
90 
91         int endIndex = result.indexOf('}', startIndex);
92         assertTrue(endIndex > startIndex);
93 
94         int stateIndex = result.indexOf("mState", startIndex);
95         assertTrue(startIndex < stateIndex && stateIndex < endIndex);
96 
97         int colonIndex = result.indexOf(':', stateIndex);
98         assertTrue(stateIndex < colonIndex && colonIndex < endIndex);
99 
100         int endLineIndex = result.indexOf('\n', colonIndex);
101         assertTrue(colonIndex < endLineIndex && endLineIndex < endIndex);
102         return result.substring(colonIndex + 2, endLineIndex);
103     }
104 
105     private void waitForOverlayState(String overlayPackage, String state) throws Exception {
106         boolean overlayFound = false;
107         long startTime = System.currentTimeMillis();
108 
109         while (!overlayFound && (System.currentTimeMillis() - startTime < OVERLAY_WAIT_TIMEOUT)) {
110             String result = getStateForOverlay(overlayPackage);
111             overlayFound = state.equals(result);
112         }
113 
114         assertTrue(overlayFound);
115     }
116 
117     private void assertFailToGenerateIdmap(String overlayApk, String overlayPackage)
118             throws Exception {
119         try {
120             getDevice().uninstallPackage(TARGET_PACKAGE);
121             getDevice().uninstallPackage(overlayPackage);
122             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE));
123             assertFalse(getDevice().getInstalledPackageNames().contains(overlayPackage));
124 
125             new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run();
126             new InstallMultiple().addFile(overlayApk).run();
127 
128             waitForOverlayState(overlayPackage, STATE_NO_IDMAP);
129             getDevice().executeShellCommand("cmd overlay enable  --user current " + overlayPackage);
130             waitForOverlayState(overlayPackage, STATE_NO_IDMAP);
131         } finally {
132             getDevice().uninstallPackage(TARGET_PACKAGE);
133             getDevice().uninstallPackage(overlayPackage);
134         }
135     }
136 
137     private void runOverlayDeviceTest(String targetApk, String overlayApk, String overlayPackage,
138             String testMethod)
139             throws Exception {
140         try {
141             getDevice().uninstallPackage(TARGET_PACKAGE);
142             getDevice().uninstallPackage(overlayPackage);
143             assertFalse(getDevice().getInstalledPackageNames().contains(TARGET_PACKAGE));
144             assertFalse(getDevice().getInstalledPackageNames().contains(overlayPackage));
145 
146             new InstallMultiple().addFile(overlayApk).run();
147             new InstallMultiple().addFile(targetApk).run();
148 
149             waitForOverlayState(overlayPackage, STATE_DISABLED);
150             getDevice().executeShellCommand("cmd overlay enable --user current " + overlayPackage);
151             waitForOverlayState(overlayPackage, STATE_ENABLED);
152 
153             runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
154         } finally {
155             getDevice().uninstallPackage(TARGET_PACKAGE);
156             getDevice().uninstallPackage(overlayPackage);
157         }
158     }
159 
160     /**
161      * Overlays that target android and are not signed with the platform signature must not be
162      * installed successfully.
163      */
164     @Test
165     public void testCannotInstallTargetAndroidNotPlatformSigned() throws Exception {
166         try {
167             getDevice().uninstallPackage(OVERLAY_ANDROID_PACKAGE);
168             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ANDROID_PACKAGE));
169 
170             // Try to install the overlay, but expect an error.
171             new InstallMultiple().addFile(OVERLAY_ANDROID_APK).runExpectingFailure();
172 
173             // The install should have failed.
174             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ANDROID_PACKAGE));
175 
176             // The package of the installed overlay should not appear in the overlay manager list.
177             assertFalse(getDevice().executeShellCommand("cmd overlay list --user current ")
178                     .contains(" " + OVERLAY_ANDROID_PACKAGE + "\n"));
179         } finally {
180             getDevice().uninstallPackage(OVERLAY_ANDROID_PACKAGE);
181         }
182     }
183 
184     /**
185      * Overlays that target a pre-Q sdk and that are not signed with the platform signature must not
186      * be installed.
187      **/
188     @Test
189     public void testCannotInstallPieOverlayNotPlatformSigned() throws Exception {
190         try {
191             getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE);
192             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE));
193 
194             // Try to install the overlay, but expect an error.
195             new InstallMultiple().addFile(OVERLAY_ALL_PIE_APK).runExpectingFailure();
196 
197             // The install should have failed.
198             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE));
199 
200             // The package of the installed overlay should not appear in the overlay manager list.
201             assertFalse(getDevice().executeShellCommand("cmd overlay list")
202                     .contains(" " + OVERLAY_ALL_PACKAGE + "\n"));
203         } finally {
204             getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE);
205         }
206     }
207 
208     /**
209      * Overlays that target Q or higher, that do not specify an android:targetName, and that are
210      * not signed with the same signature as the target package must not be installed.
211      **/
212     @Test
213     public void testCannotInstallDifferentSignaturesNoName() throws Exception {
214         try {
215             getDevice().uninstallPackage(TARGET_PACKAGE);
216             getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE);
217             assertFalse(getDevice().getInstalledPackageNames().contains(TARGET_PACKAGE));
218             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE));
219 
220             // Try to install the overlay, but expect an error.
221             new InstallMultiple().addFile(TARGET_NO_OVERLAYABLE_APK).run();
222             new InstallMultiple().addFile(
223                     OVERLAY_ALL_NO_NAME_DIFFERENT_CERT_APK).runExpectingFailure();
224 
225             // The install should have failed.
226             assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE));
227 
228             // The package of the installed overlay should not appear in the overlay manager list.
229             assertFalse(getDevice().executeShellCommand("cmd overlay list --user current")
230                     .contains(" " + OVERLAY_ALL_PACKAGE + "\n"));
231         } finally {
232             getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE);
233             getDevice().uninstallPackage(TARGET_PACKAGE);
234         }
235     }
236 
237     /**
238      * Overlays that target Q or higher, that do not specify an android:targetName, and are
239      * installed before the target must not be allowed to successfully generate an idmap if the
240      * overlay is not signed with the same signature as the target package.
241      **/
242     @Test
243     public void testFailIdmapDifferentSignaturesNoName() throws Exception {
244         assertFailToGenerateIdmap(OVERLAY_ALL_NO_NAME_APK, OVERLAY_ALL_PACKAGE);
245     }
246 
247     /**
248      * Overlays that target Q or higher, that do not specify an android:targetName, and are
249      * installed before the target must be allowed to successfully generate an idmap if the
250      * overlay is signed with the same signature as the target package.
251      **/
252     @Test
253     public void testSameSignatureNoOverlayableSucceeds() throws Exception {
254         String testMethod = "testSameSignatureNoOverlayableSucceeds";
255         runOverlayDeviceTest(TARGET_NO_OVERLAYABLE_APK, OVERLAY_ALL_NO_NAME_APK,
256                 OVERLAY_ALL_PACKAGE, testMethod);
257     }
258 
259     /**
260      * Overlays installed on the data partition may only overlay resources defined under the public
261      * and signature policies if the overlay is signed with the same signature as the target.
262      */
263     @Test
264     public void testOverlayPolicyAll() throws Exception {
265         String testMethod = "testOverlayPolicyAll";
266         runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_APK, OVERLAY_ALL_PACKAGE,
267                 testMethod);
268     }
269 
270     @Test
271     public void testOverlayCodeNotLoaded() throws Exception {
272         String testMethod = "testOverlayCodeNotLoaded";
273         runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_HAS_CODE_APK, OVERLAY_ALL_PACKAGE,
274                 testMethod);
275     }
276 
277     @Test
278     public void testOverlayPolicyAllNoNameFails() throws Exception {
279         assertFailToGenerateIdmap(OVERLAY_ALL_NO_NAME_APK, OVERLAY_ALL_PACKAGE);
280     }
281 
282     @Test
283     public void testOverlayPolicyProductFails() throws Exception {
284         assertFailToGenerateIdmap(OVERLAY_PRODUCT_APK, OVERLAY_PRODUCT_PACKAGE);
285     }
286 
287     @Test
288     public void testOverlayPolicySystemFails() throws Exception {
289         assertFailToGenerateIdmap(OVERLAY_SYSTEM_APK, OVERLAY_SYSTEM_PACKAGE);
290     }
291 
292     @Test
293     public void testOverlayPolicyVendorFails() throws Exception {
294         assertFailToGenerateIdmap(OVERLAY_VENDOR_APK, OVERLAY_VENDOR_PACKAGE);
295     }
296 
297     @Test
298     public void testOverlayPolicyDifferentSignatureFails() throws Exception {
299         assertFailToGenerateIdmap(OVERLAY_DIFFERENT_SIGNATURE_APK,
300                 OVERLAY_DIFFERENT_SIGNATURE_PACKAGE);
301     }
302 
303     @Test
304     public void testFrameworkDoesNotDefineOverlayable() throws Exception {
305         String testMethod = "testFrameworkDoesNotDefineOverlayable";
306         runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
307     }
308 
309     /** Overlays must not overlay assets. */
310     @Test
311     public void testCannotOverlayAssets() throws Exception {
312         String testMethod = "testCannotOverlayAssets";
313         runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_APK, OVERLAY_ALL_PACKAGE,
314                 testMethod);
315     }
316 }
317