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