1 /*
2  * Copyright (C) 2016 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 android.appsecurity.cts;
18 
19 import android.platform.test.annotations.SecurityTest;
20 
21 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
22 import com.android.tradefed.build.IBuildInfo;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.testtype.DeviceTestCase;
25 import com.android.tradefed.testtype.IBuildReceiver;
26 
27 import java.io.BufferedOutputStream;
28 import java.io.File;
29 import java.io.FileNotFoundException;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.util.Locale;
35 
36 /**
37  * Tests for APK signature verification during installation.
38  */
39 public class PkgInstallSignatureVerificationTest extends DeviceTestCase implements IBuildReceiver {
40 
41     private static final String TEST_PKG = "android.appsecurity.cts.tinyapp";
42     private static final String COMPANION_TEST_PKG = "android.appsecurity.cts.tinyapp_companion";
43     private static final String DEVICE_TESTS_APK = "CtsV3SigningSchemeRotationTest.apk";
44     private static final String DEVICE_TESTS_PKG = "android.appsecurity.cts.v3rotationtests";
45     private static final String DEVICE_TESTS_CLASS = DEVICE_TESTS_PKG + ".V3RotationTest";
46     private static final String TEST_APK_RESOURCE_PREFIX = "/pkgsigverify/";
47 
48     private static final String[] DSA_KEY_NAMES = {"1024", "2048", "3072"};
49     private static final String[] EC_KEY_NAMES = {"p256", "p384", "p521"};
50     private static final String[] RSA_KEY_NAMES = {"1024", "2048", "3072", "4096", "8192", "16384"};
51     private static final String[] RSA_KEY_NAMES_2048_AND_LARGER =
52             {"2048", "3072", "4096", "8192", "16384"};
53 
54     private IBuildInfo mCtsBuild;
55 
56     @Override
setBuild(IBuildInfo buildInfo)57     public void setBuild(IBuildInfo buildInfo) {
58         mCtsBuild = buildInfo;
59     }
60 
61     @Override
setUp()62     protected void setUp() throws Exception {
63         super.setUp();
64 
65         Utils.prepareSingleUser(getDevice());
66         assertNotNull(mCtsBuild);
67         uninstallPackage();
68         uninstallCompanionPackage();
69         installDeviceTestPkg();
70     }
71 
72     @Override
tearDown()73     protected void tearDown() throws Exception {
74         try {
75             uninstallPackages();
76         } catch (DeviceNotAvailableException ignored) {
77         } finally {
78             super.tearDown();
79         }
80     }
81 
testInstallOriginalSucceeds()82     public void testInstallOriginalSucceeds() throws Exception {
83         // APK signed with v1 and v2 schemes. Obtained by building
84         // cts/hostsidetests/appsecurity/test-apps/tinyapp.
85         assertInstallSucceeds("original.apk");
86     }
87 
testInstallV1OneSignerMD5withRSA()88     public void testInstallV1OneSignerMD5withRSA() throws Exception {
89         // APK signed with v1 scheme only, one signer.
90         assertInstallSucceedsForEach(
91                 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
92         assertInstallSucceedsForEach(
93                 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-%s.apk", RSA_KEY_NAMES);
94     }
95 
testInstallV1OneSignerSHA1withRSA()96     public void testInstallV1OneSignerSHA1withRSA() throws Exception {
97         // APK signed with v1 scheme only, one signer.
98         assertInstallSucceedsForEach(
99                 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
100         assertInstallSucceedsForEach(
101                 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-%s.apk", RSA_KEY_NAMES);
102     }
103 
testInstallV1OneSignerSHA224withRSA()104     public void testInstallV1OneSignerSHA224withRSA() throws Exception {
105         // APK signed with v1 scheme only, one signer.
106         assertInstallSucceedsForEach(
107                 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
108         assertInstallSucceedsForEach(
109                 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-%s.apk", RSA_KEY_NAMES);
110     }
111 
testInstallV1OneSignerSHA256withRSA()112     public void testInstallV1OneSignerSHA256withRSA() throws Exception {
113         // APK signed with v1 scheme only, one signer.
114         assertInstallSucceedsForEach(
115                 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
116         assertInstallSucceedsForEach(
117                 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-%s.apk", RSA_KEY_NAMES);
118     }
119 
testInstallV1OneSignerSHA384withRSA()120     public void testInstallV1OneSignerSHA384withRSA() throws Exception {
121         // APK signed with v1 scheme only, one signer.
122         assertInstallSucceedsForEach(
123                 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
124         assertInstallSucceedsForEach(
125                 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-%s.apk", RSA_KEY_NAMES);
126     }
127 
testInstallV1OneSignerSHA512withRSA()128     public void testInstallV1OneSignerSHA512withRSA() throws Exception {
129         // APK signed with v1 scheme only, one signer.
130         assertInstallSucceedsForEach(
131                 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
132         assertInstallSucceedsForEach(
133                 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-%s.apk", RSA_KEY_NAMES);
134     }
135 
testInstallV1OneSignerSHA1withECDSA()136     public void testInstallV1OneSignerSHA1withECDSA() throws Exception {
137         // APK signed with v1 scheme only, one signer.
138         assertInstallSucceedsForEach(
139                 "v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
140         assertInstallSucceedsForEach(
141                 "v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-%s.apk", EC_KEY_NAMES);
142     }
143 
testInstallV1OneSignerSHA224withECDSA()144     public void testInstallV1OneSignerSHA224withECDSA() throws Exception {
145         // APK signed with v1 scheme only, one signer.
146         assertInstallSucceedsForEach(
147                 "v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
148         assertInstallSucceedsForEach(
149                 "v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-%s.apk", EC_KEY_NAMES);
150     }
151 
testInstallV1OneSignerSHA256withECDSA()152     public void testInstallV1OneSignerSHA256withECDSA() throws Exception {
153         // APK signed with v1 scheme only, one signer.
154         assertInstallSucceedsForEach(
155                 "v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
156         assertInstallSucceedsForEach(
157                 "v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-%s.apk", EC_KEY_NAMES);
158     }
159 
testInstallV1OneSignerSHA384withECDSA()160     public void testInstallV1OneSignerSHA384withECDSA() throws Exception {
161         // APK signed with v1 scheme only, one signer.
162         assertInstallSucceedsForEach(
163                 "v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
164         assertInstallSucceedsForEach(
165                 "v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-%s.apk", EC_KEY_NAMES);
166     }
167 
testInstallV1OneSignerSHA512withECDSA()168     public void testInstallV1OneSignerSHA512withECDSA() throws Exception {
169         // APK signed with v1 scheme only, one signer.
170         assertInstallSucceedsForEach(
171                 "v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
172         assertInstallSucceedsForEach(
173                 "v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-%s.apk", EC_KEY_NAMES);
174     }
175 
testInstallV1OneSignerSHA1withDSA()176     public void testInstallV1OneSignerSHA1withDSA() throws Exception {
177         // APK signed with v1 scheme only, one signer.
178         assertInstallSucceedsForEach(
179                 "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES);
180         assertInstallSucceedsForEach(
181                 "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES);
182     }
183 
testInstallV1OneSignerSHA224withDSA()184     public void testInstallV1OneSignerSHA224withDSA() throws Exception {
185         // APK signed with v1 scheme only, one signer.
186         assertInstallSucceedsForEach(
187                 "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES);
188         assertInstallSucceedsForEach(
189                 "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", DSA_KEY_NAMES);
190     }
191 
testInstallV1OneSignerSHA256withDSA()192     public void testInstallV1OneSignerSHA256withDSA() throws Exception {
193         // APK signed with v1 scheme only, one signer.
194         assertInstallSucceedsForEach(
195                 "v1-only-with-dsa-sha256-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES);
196         assertInstallSucceedsForEach(
197                 "v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-%s.apk", DSA_KEY_NAMES);
198     }
199 
200 //  Android platform doesn't support DSA with SHA-384 and SHA-512.
201 //    public void testInstallV1OneSignerSHA384withDSA() throws Exception {
202 //        // APK signed with v1 scheme only, one signer.
203 //        assertInstallSucceedsForEach(
204 //                "v1-only-with-dsa-sha384-2.16.840.1.101.3.4.3.3-%s.apk", DSA_KEY_NAMES);
205 //    }
206 //
207 //    public void testInstallV1OneSignerSHA512withDSA() throws Exception {
208 //        // APK signed with v1 scheme only, one signer.
209 //        assertInstallSucceedsForEach(
210 //                "v1-only-with-dsa-sha512-2.16.840.1.101.3.4.3.3-%s.apk", DSA_KEY_NAMES);
211 //    }
212 
testInstallV2StrippedFails()213     public void testInstallV2StrippedFails() throws Exception {
214         // APK signed with v1 and v2 schemes, but v2 signature was stripped from the file (by using
215         // zipalign).
216         // This should fail because the v1 signature indicates that the APK was supposed to be
217         // signed with v2 scheme as well, making the platform's anti-stripping protections reject
218         // the APK.
219         assertInstallFailsWithError("v2-stripped.apk", "Signature stripped");
220 
221         // Similar to above, but the X-Android-APK-Signed anti-stripping header in v1 signature
222         // lists unknown signature schemes in addition to APK Signature Scheme v2. Unknown schemes
223         // should be ignored.
224         assertInstallFailsWithError(
225                 "v2-stripped-with-ignorable-signing-schemes.apk", "Signature stripped");
226     }
227 
testInstallV2OneSignerOneSignature()228     public void testInstallV2OneSignerOneSignature() throws Exception {
229         // APK signed with v2 scheme only, one signer, one signature.
230         assertInstallSucceedsForEach("v2-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES);
231         assertInstallSucceedsForEach("v2-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES);
232         assertInstallSucceedsForEach("v2-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES);
233         assertInstallSucceedsForEach("v2-only-with-rsa-pss-sha256-%s.apk", RSA_KEY_NAMES);
234 
235         // DSA with SHA-512 is not supported by Android platform and thus APK Signature Scheme v2
236         // does not support that either
237         // assertInstallSucceedsForEach("v2-only-with-dsa-sha512-%s.apk", DSA_KEY_NAMES);
238         assertInstallSucceedsForEach("v2-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES);
239         assertInstallSucceedsForEach("v2-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES);
240         assertInstallSucceedsForEach(
241                 "v2-only-with-rsa-pss-sha512-%s.apk",
242                 RSA_KEY_NAMES_2048_AND_LARGER // 1024-bit key is too short for PSS with SHA-512
243                 );
244     }
245 
testInstallV1SignatureOnlyDoesNotVerify()246     public void testInstallV1SignatureOnlyDoesNotVerify() throws Exception {
247         // APK signed with v1 scheme only, but not all digests match those recorded in
248         // META-INF/MANIFEST.MF.
249         String error = "META-INF/MANIFEST.MF has invalid digest";
250 
251         // Bitflip in classes.dex of otherwise good file.
252         assertInstallFailsWithError(
253                 "v1-only-with-tampered-classes-dex.apk", error);
254     }
255 
testInstallV2SignatureDoesNotVerify()256     public void testInstallV2SignatureDoesNotVerify() throws Exception {
257         // APK signed with v2 scheme only, but the signature over signed-data does not verify.
258         String error = "signature did not verify";
259 
260         // Bitflip in certificate field inside signed-data. Based on
261         // v2-only-with-dsa-sha256-1024.apk.
262         assertInstallFailsWithError("v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", error);
263 
264         // Signature claims to be RSA PKCS#1 v1.5 with SHA-256, but is actually using SHA-512.
265         // Based on v2-only-with-rsa-pkcs1-sha256-2048.apk.
266         assertInstallFailsWithError(
267                 "v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk", error);
268 
269         // Signature claims to be RSA PSS with SHA-256 and 32 bytes of salt, but is actually using 0
270         // bytes of salt. Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. Obtained by modifying APK
271         // signer to use the wrong amount of salt.
272         assertInstallFailsWithError(
273                 "v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk", error);
274 
275         // Bitflip in the ECDSA signature. Based on v2-only-with-ecdsa-sha256-p256.apk.
276         assertInstallFailsWithError(
277                 "v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk", error);
278     }
279 
testInstallV2ContentDigestMismatch()280     public void testInstallV2ContentDigestMismatch() throws Exception {
281         // APK signed with v2 scheme only, but the digest of contents does not match the digest
282         // stored in signed-data.
283         String error = "digest of contents did not verify";
284 
285         // Based on v2-only-with-rsa-pkcs1-sha512-4096.apk. Obtained by modifying APK signer to
286         // flip the leftmost bit in content digest before signing signed-data.
287         assertInstallFailsWithError(
288                 "v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk", error);
289 
290         // Based on v2-only-with-ecdsa-sha256-p256.apk. Obtained by modifying APK signer to flip the
291         // leftmost bit in content digest before signing signed-data.
292         assertInstallFailsWithError(
293                 "v2-only-with-ecdsa-sha256-p256-digest-mismatch.apk", error);
294     }
295 
testInstallNoApkSignatureSchemeBlock()296     public void testInstallNoApkSignatureSchemeBlock() throws Exception {
297         // APK signed with v2 scheme only, but the rules for verifying APK Signature Scheme v2
298         // signatures say that this APK must not be verified using APK Signature Scheme v2.
299 
300         // Obtained from v2-only-with-rsa-pkcs1-sha512-4096.apk by flipping a bit in the magic
301         // field in the footer of APK Signing Block. This makes the APK Signing Block disappear.
302         assertInstallFails("v2-only-wrong-apk-sig-block-magic.apk");
303 
304         // Obtained by modifying APK signer to insert "GARBAGE" between ZIP Central Directory and
305         // End of Central Directory. The APK is otherwise fine and is signed with APK Signature
306         // Scheme v2. Based on v2-only-with-rsa-pkcs1-sha256.apk.
307         assertInstallFails("v2-only-garbage-between-cd-and-eocd.apk");
308 
309         // Obtained by modifying APK signer to truncate the ZIP Central Directory by one byte. The
310         // APK is otherwise fine and is signed with APK Signature Scheme v2. Based on
311         // v2-only-with-rsa-pkcs1-sha256.apk
312         assertInstallFails("v2-only-truncated-cd.apk");
313 
314         // Obtained by modifying the size in APK Signature Block header. Based on
315         // v2-only-with-ecdsa-sha512-p521.apk.
316         assertInstallFails("v2-only-apk-sig-block-size-mismatch.apk");
317 
318         // Obtained by modifying the ID under which APK Signature Scheme v2 Block is stored in
319         // APK Signing Block and by modifying the APK signer to not insert anti-stripping
320         // protections into JAR Signature. The APK should appear as having no APK Signature Scheme
321         // v2 Block and should thus successfully verify using JAR Signature Scheme.
322         assertInstallSucceeds("v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk");
323     }
324 
testInstallV2UnknownPairIgnoredInApkSigningBlock()325     public void testInstallV2UnknownPairIgnoredInApkSigningBlock() throws Exception {
326         // Obtained by modifying APK signer to emit an unknown ID-value pair into APK Signing Block
327         // before the ID-value pair containing the APK Signature Scheme v2 Block. The unknown
328         // ID-value should be ignored.
329         assertInstallSucceeds("v2-only-unknown-pair-in-apk-sig-block.apk");
330     }
331 
testInstallV2IgnoresUnknownSignatureAlgorithms()332     public void testInstallV2IgnoresUnknownSignatureAlgorithms() throws Exception {
333         // APK is signed with a known signature algorithm and with a couple of unknown ones.
334         // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to
335         // known ones.
336         assertInstallSucceeds("v2-only-with-ignorable-unsupported-sig-algs.apk");
337     }
338 
testInstallV2RejectsMismatchBetweenSignaturesAndDigestsBlocks()339     public void testInstallV2RejectsMismatchBetweenSignaturesAndDigestsBlocks() throws Exception {
340         // APK is signed with a single signature algorithm, but the digests block claims that it is
341         // signed with two different signature algorithms. Obtained by modifying APK Signer to
342         // emit an additional digest record with signature algorithm 0x12345678.
343         assertInstallFailsWithError(
344                 "v2-only-signatures-and-digests-block-mismatch.apk",
345                 "Signature algorithms don't match between digests and signatures records");
346     }
347 
testInstallV2RejectsMismatchBetweenPublicKeyAndCertificate()348     public void testInstallV2RejectsMismatchBetweenPublicKeyAndCertificate() throws Exception {
349         // APK is signed with v2 only. The public key field does not match the public key in the
350         // leaf certificate. Obtained by modifying APK signer to write out a modified leaf
351         // certificate where the RSA modulus has a bitflip.
352         assertInstallFailsWithError(
353                 "v2-only-cert-and-public-key-mismatch.apk",
354                 "Public key mismatch between certificate and signature record");
355     }
356 
testInstallV2RejectsSignerBlockWithNoCertificates()357     public void testInstallV2RejectsSignerBlockWithNoCertificates() throws Exception {
358         // APK is signed with v2 only. There are no certificates listed in the signer block.
359         // Obtained by modifying APK signer to output no certificates.
360         assertInstallFailsWithError("v2-only-no-certs-in-sig.apk", "No certificates listed");
361     }
362 
testInstallTwoSigners()363     public void testInstallTwoSigners() throws Exception {
364         // APK signed by two different signers.
365         assertInstallSucceeds("two-signers.apk");
366         // Because the install attempt below is an update, it also tests that the signing
367         // certificates exposed by v2 signatures above are the same as the one exposed by v1
368         // signatures in this APK.
369         assertInstallSucceeds("v1-only-two-signers.apk");
370         assertInstallSucceeds("v2-only-two-signers.apk");
371     }
372 
testInstallNegativeModulus()373     public void testInstallNegativeModulus() throws Exception {
374         // APK signed with a certificate that has a negative RSA modulus.
375         assertInstallSucceeds("v1-only-negative-modulus.apk");
376         assertInstallSucceeds("v2-only-negative-modulus.apk");
377         assertInstallSucceeds("v3-only-negative-modulus.apk");
378     }
379 
testInstallV2TwoSignersRejectsWhenOneBroken()380     public void testInstallV2TwoSignersRejectsWhenOneBroken() throws Exception {
381         // Bitflip in the ECDSA signature of second signer. Based on two-signers.apk.
382         // This asserts that breakage in any signer leads to rejection of the APK.
383         assertInstallFailsWithError(
384                 "two-signers-second-signer-v2-broken.apk", "signature did not verify");
385     }
386 
testInstallV2TwoSignersRejectsWhenOneWithoutSignatures()387     public void testInstallV2TwoSignersRejectsWhenOneWithoutSignatures() throws Exception {
388         // APK v2-signed by two different signers. However, there are no signatures for the second
389         // signer.
390         assertInstallFailsWithError(
391                 "v2-only-two-signers-second-signer-no-sig.apk", "No signatures");
392     }
393 
testInstallV2TwoSignersRejectsWhenOneWithoutSupportedSignatures()394     public void testInstallV2TwoSignersRejectsWhenOneWithoutSupportedSignatures() throws Exception {
395         // APK v2-signed by two different signers. However, there are no supported signatures for
396         // the second signer.
397         assertInstallFailsWithError(
398                 "v2-only-two-signers-second-signer-no-supported-sig.apk",
399                 "No supported signatures");
400     }
401 
testInstallV2RejectsWhenMissingCode()402     public void testInstallV2RejectsWhenMissingCode() throws Exception {
403         // Obtained by removing classes.dex from original.apk and then signing with v2 only.
404         // Although this has nothing to do with v2 signature verification, package manager wants
405         // signature verification / certificate collection to reject APKs with missing code
406         // (classes.dex) unless requested otherwise.
407         assertInstallFailsWithError("v2-only-missing-classes.dex.apk", "code is missing");
408     }
409 
testCorrectCertUsedFromPkcs7SignedDataCertsSet()410     public void testCorrectCertUsedFromPkcs7SignedDataCertsSet() throws Exception {
411         // Obtained by prepending the rsa-1024 certificate to the PKCS#7 SignedData certificates set
412         // of v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk META-INF/CERT.RSA. The certs
413         // (in the order of appearance in the file) are thus: rsa-1024, rsa-2048. The package's
414         // signing cert is rsa-2048.
415         assertInstallSucceeds("v1-only-pkcs7-cert-bag-first-cert-not-used.apk");
416 
417         // Check that rsa-1024 was not used as the previously installed package's signing cert.
418         assertInstallFailsWithError(
419                 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-1024.apk",
420                 "signatures do not match");
421 
422         // Check that rsa-2048 was used as the previously installed package's signing cert.
423         assertInstallSucceeds("v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk");
424     }
425 
testV1SchemeSignatureCertNotReencoded()426     public void testV1SchemeSignatureCertNotReencoded() throws Exception {
427         // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the
428         // original encoded form of signing certificates, bad things happen, such as rejection of
429         // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that
430         // PackageManager started re-encoding signing certs into DER. This normally produces exactly
431         // the original form because X.509 certificates are supposed to be DER-encoded. However, a
432         // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For
433         // such apps, re-encoding into DER changes the serialized form of the certificate, creating
434         // a mismatch with the serialized form stored in the PackageManager database, leading to the
435         // rejection of updates for the app.
436         //
437         // The signing certs of the two APKs differ only in how the cert's signature is encoded.
438         // From Android's perspective, these two APKs are signed by different entities and thus
439         // cannot be used to update one another. If signature verification code re-encodes certs
440         // into DER, both certs will be exactly the same and Android will accept these APKs as
441         // updates of each other. This test is thus asserting that the two APKs are not accepted as
442         // updates of each other.
443         //
444         // * v1-only-with-rsa-1024.apk cert's signature is DER-encoded
445         // * v1-only-with-rsa-1024-cert-not-der.apk cert's signature is not DER-encoded. It is
446         //   BER-encoded, with length encoded as two bytes instead of just one.
447         //   v1-only-with-rsa-1024-cert-not-der.apk META-INF/CERT.RSA was obtained from
448         //   v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure.
449         assertInstallSucceeds("v1-only-with-rsa-1024.apk");
450         assertInstallFailsWithError(
451                 "v1-only-with-rsa-1024-cert-not-der.apk", "signatures do not match");
452 
453         uninstallPackage();
454         assertInstallSucceeds("v1-only-with-rsa-1024-cert-not-der.apk");
455         assertInstallFailsWithError("v1-only-with-rsa-1024.apk", "signatures do not match");
456     }
457 
testV2SchemeSignatureCertNotReencoded()458     public void testV2SchemeSignatureCertNotReencoded() throws Exception {
459         // This test is here to catch something like b/30148997 and b/18228011 happening to the
460         // handling of APK Signature Scheme v2 signatures by PackageManager. When PackageManager
461         // does not preserve the original encoded form of signing certificates, bad things happen,
462         // such as rejection of completely valid updates to apps. The issue in b/30148997 and
463         // b/18228011 was that PackageManager started re-encoding signing certs into DER. This
464         // normally produces exactly the original form because X.509 certificates are supposed to be
465         // DER-encoded. However, a small fraction of Android apps uses X.509 certificates which are
466         // not DER-encoded. For such apps, re-encoding into DER changes the serialized form of the
467         // certificate, creating a mismatch with the serialized form stored in the PackageManager
468         // database, leading to the rejection of updates for the app.
469         //
470         // The signing certs of the two APKs differ only in how the cert's signature is encoded.
471         // From Android's perspective, these two APKs are signed by different entities and thus
472         // cannot be used to update one another. If signature verification code re-encodes certs
473         // into DER, both certs will be exactly the same and Android will accept these APKs as
474         // updates of each other. This test is thus asserting that the two APKs are not accepted as
475         // updates of each other.
476         //
477         // * v2-only-with-rsa-pkcs1-sha256-1024.apk cert's signature is DER-encoded
478         // * v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk cert's signature is not DER-encoded
479         //   It is BER-encoded, with length encoded as two bytes instead of just one.
480         assertInstallSucceeds("v2-only-with-rsa-pkcs1-sha256-1024.apk");
481         assertInstallFailsWithError(
482                 "v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk", "signatures do not match");
483 
484         uninstallPackage();
485         assertInstallSucceeds("v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk");
486         assertInstallFailsWithError(
487                 "v2-only-with-rsa-pkcs1-sha256-1024.apk", "signatures do not match");
488     }
489 
testInstallMaxSizedZipEocdComment()490     public void testInstallMaxSizedZipEocdComment() throws Exception {
491         // Obtained by modifying apksigner to produce a max-sized (0xffff bytes long) ZIP End of
492         // Central Directory comment, and signing the original.apk using the modified apksigner.
493         assertInstallSucceeds("v1-only-max-sized-eocd-comment.apk");
494         assertInstallSucceeds("v2-only-max-sized-eocd-comment.apk");
495     }
496 
testInstallEphemeralRequiresV2Signature()497     public void testInstallEphemeralRequiresV2Signature() throws Exception {
498         assertInstallEphemeralFailsWithError("unsigned-ephemeral.apk",
499                 "Failed to collect certificates");
500         assertInstallEphemeralFailsWithError("v1-only-ephemeral.apk",
501                 "must be signed with APK Signature Scheme v2 or greater");
502         assertInstallEphemeralSucceeds("v2-only-ephemeral.apk");
503         assertInstallEphemeralSucceeds("v1-v2-ephemeral.apk"); // signed with both schemes
504     }
505 
testInstallEmpty()506     public void testInstallEmpty() throws Exception {
507         assertInstallFailsWithError("empty-unsigned.apk", "Unknown failure");
508         assertInstallFailsWithError("v1-only-empty.apk", "Unknown failure");
509         assertInstallFailsWithError("v2-only-empty.apk", "Unknown failure");
510     }
511 
512     @SecurityTest
testInstallApkWhichDoesNotStartWithZipLocalFileHeaderMagic()513     public void testInstallApkWhichDoesNotStartWithZipLocalFileHeaderMagic() throws Exception {
514         // The APKs below are competely fine except they don't start with ZIP Local File Header
515         // magic. Thus, these APKs will install just fine unless Package Manager requires that APKs
516         // start with ZIP Local File Header magic.
517         String error = "Unknown failure";
518 
519         // Obtained by modifying apksigner to output four unused 0x00 bytes at the start of the APK
520         assertInstallFailsWithError("v1-only-starts-with-00000000-magic.apk", error);
521         assertInstallFailsWithError("v2-only-starts-with-00000000-magic.apk", error);
522 
523         // Obtained by modifying apksigner to output 8 unused bytes (DEX magic and version) at the
524         // start of the APK
525         assertInstallFailsWithError("v1-only-starts-with-dex-magic.apk", error);
526         assertInstallFailsWithError("v2-only-starts-with-dex-magic.apk", error);
527     }
528 
testInstallV3KeyRotation()529     public void testInstallV3KeyRotation() throws Exception {
530         // tests that a v3 signed APK with RSA key can rotate to a new key
531         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1.apk");
532         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
533     }
534 
testInstallV3KeyRotationToAncestor()535     public void testInstallV3KeyRotationToAncestor() throws Exception {
536         // tests that a v3 signed APK with RSA key cannot be upgraded by one of its past certs
537         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
538         assertInstallFails("v3-rsa-pkcs1-sha256-2048-1.apk");
539     }
540 
testInstallV3KeyRotationToAncestorWithRollback()541     public void testInstallV3KeyRotationToAncestorWithRollback() throws Exception {
542         // tests that a v3 signed APK with RSA key can be upgraded by one of its past certs if it
543         // has granted that cert the rollback capability
544         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-and-roll-caps.apk");
545         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1.apk");
546     }
547 
testInstallV3KeyRotationMultipleHops()548     public void testInstallV3KeyRotationMultipleHops() throws Exception {
549         // tests that a v3 signed APK with RSA key can rotate to a new key which is the result of
550         // multiple rotations from the original: APK signed with key 1 can be updated by key 3, when
551         // keys were: 1 -> 2 -> 3
552         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1.apk");
553         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-3-with-por_1_2_3-full-caps.apk");
554     }
555 
testInstallV3PorSignerMismatch()556     public void testInstallV3PorSignerMismatch() throws Exception {
557         // tests that an APK with a proof-of-rotation struct that doesn't include the current
558         // signing certificate fails to install
559         assertInstallFails("v3-rsa-pkcs1-sha256-2048-3-with-por_1_2-full-caps.apk");
560     }
561 
testInstallV3KeyRotationWrongPor()562     public void testInstallV3KeyRotationWrongPor() throws Exception {
563         // tests that a valid APK with a proof-of-rotation record can't upgrade an APK with a
564         // signing certificate that isn't in the proof-of-rotation record
565         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1.apk");
566         assertInstallFails("v3-rsa-pkcs1-sha256-2048-3-with-por_2_3-full-caps.apk");
567     }
568 
testInstallV3KeyRotationSharedUid()569     public void testInstallV3KeyRotationSharedUid() throws Exception {
570         // tests that a v3 signed sharedUid APK can still be sharedUid with apps with its older
571         // signing certificate, if it so desires
572         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-sharedUid.apk");
573         assertInstallSucceeds(
574                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps-sharedUid-companion.apk");
575     }
576 
testInstallV3KeyRotationOlderSharedUid()577     public void testInstallV3KeyRotationOlderSharedUid() throws Exception {
578 
579         // tests that a sharedUid APK can still install with another app that is signed by a newer
580         // signing certificate, but which allows sharedUid with the older one
581         assertInstallSucceeds(
582                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps-sharedUid-companion.apk");
583         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-sharedUid.apk");
584     }
585 
testInstallV3KeyRotationSharedUidNoCap()586     public void testInstallV3KeyRotationSharedUidNoCap() throws Exception {
587         // tests that a v3 signed sharedUid APK cannot be sharedUid with apps with its older
588         // signing certificate, when it has not granted that certificate the sharedUid capability
589         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-sharedUid.apk");
590         assertInstallFails(
591                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-shUid-cap-sharedUid-companion.apk");
592     }
593 
testInstallV3KeyRotationOlderSharedUidNoCap()594     public void testInstallV3KeyRotationOlderSharedUidNoCap() throws Exception {
595         // tests that a sharedUid APK signed with an old certificate cannot install with
596         // an app having a proof-of-rotation structure that hasn't granted the older
597         // certificate the sharedUid capability
598         assertInstallSucceeds(
599                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-shUid-cap-sharedUid-companion.apk");
600         assertInstallFails("v3-rsa-pkcs1-sha256-2048-1-sharedUid.apk");
601     }
602 
testInstallV3NoRotationSharedUid()603     public void testInstallV3NoRotationSharedUid() throws Exception {
604         // tests that a sharedUid APK signed with a new certificate installs with
605         // an app having a proof-of-rotation structure that hasn't granted an older
606         // certificate the sharedUid capability
607         assertInstallSucceeds(
608                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-shUid-cap-sharedUid-companion.apk");
609         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-sharedUid.apk");
610     }
611 
testInstallV3KeyRotationSigPerm()612     public void testInstallV3KeyRotationSigPerm() throws Exception {
613         // tests that a v3 signed APK can still get a signature permission from an app with its
614         // older signing certificate.
615         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
616         assertInstallSucceeds(
617                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps-permcli-companion.apk");
618         Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasPerm");
619     }
620 
testInstallV3KeyRotationOlderSigPerm()621     public void testInstallV3KeyRotationOlderSigPerm() throws Exception {
622         // tests that an apk with an older signing certificate than the one which defines a
623         // signature permission it wants gets the permission if the defining APK grants the
624         // capability
625         assertInstallSucceeds(
626                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps-permdef.apk");
627         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permcli-companion.apk");
628         Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasPerm");
629     }
630 
testInstallV3KeyRotationSigPermNoCap()631     public void testInstallV3KeyRotationSigPermNoCap() throws Exception {
632         // tests that an APK signed by an older signing certificate is unable to get a requested
633         // signature permission when the defining APK has rotated to a newer signing certificiate
634         // and does not grant the permission capability to the older cert
635         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-perm-cap-permdef.apk");
636         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permcli-companion.apk");
637         Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasNoPerm");
638     }
639 
testInstallV3KeyRotationOlderSigPermNoCap()640     public void testInstallV3KeyRotationOlderSigPermNoCap() throws Exception {
641         // tests that an APK signed by a newer signing certificate than the APK which defines a
642         // signature permission is able to get that permission, even if the newer APK does not
643         // grant the permission capability to the older signing certificate.
644         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
645         assertInstallSucceeds(
646                 "v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-perm-cap-permcli-companion.apk");
647         Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasPerm");
648     }
649 
testInstallV3NoRotationSigPerm()650     public void testInstallV3NoRotationSigPerm() throws Exception {
651         // make sure that an APK, which wants to use a signature permission defined by an APK, which
652         // has not granted that capability to older signing certificates, can still install
653         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-no-perm-cap-permdef.apk");
654         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-permcli-companion.apk");
655         Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasPerm");
656     }
657 
testInstallV3SigPermDoubleDefNewerSucceeds()658     public void testInstallV3SigPermDoubleDefNewerSucceeds() throws Exception {
659         // make sure that if an app defines a signature permission already defined by another app,
660         // it successfully installs if the other app's signing cert is in its past signing certs and
661         // the signature permission capability is granted
662         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
663         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with_por_1_2-permdef-companion.apk");
664     }
665 
testInstallV3SigPermDoubleDefOlderSucceeds()666     public void testInstallV3SigPermDoubleDefOlderSucceeds() throws Exception {
667         // make sure that if an app defines a signature permission already defined by another app,
668         // it successfully installs if it is in the other app's past signing certs and the signature
669         // permission capability is granted
670         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with_por_1_2-permdef-companion.apk");
671         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
672     }
673 
testInstallV3SigPermDoubleDefNewerNoCapFails()674     public void testInstallV3SigPermDoubleDefNewerNoCapFails() throws Exception {
675         // make sure that if an app defines a signature permission already defined by another app,
676         // it fails to install if the other app's signing cert is in its past signing certs but the
677         // signature permission capability is not granted
678         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
679         assertInstallFails(
680                 "v3-rsa-pkcs1-sha256-2048-2-with_por_1_2-no-perm-cap-permdef-companion.apk");
681     }
682 
testInstallV3SigPermDoubleDefOlderNoCapFails()683     public void testInstallV3SigPermDoubleDefOlderNoCapFails() throws Exception {
684         // make sure that if an app defines a signature permission already defined by another app,
685         // it fails to install if it is in the other app's past signing certs but the signature
686         // permission capability is not granted
687         assertInstallSucceeds(
688                 "v3-rsa-pkcs1-sha256-2048-2-with_por_1_2-no-perm-cap-permdef-companion.apk");
689         assertInstallFails("v3-rsa-pkcs1-sha256-2048-1-permdef.apk");
690     }
691 
testInstallV3SigPermDoubleDefSameNoCapSucceeds()692     public void testInstallV3SigPermDoubleDefSameNoCapSucceeds() throws Exception {
693         // make sure that if an app defines a signature permission already defined by another app,
694         // it installs successfully when signed by the same certificate, even if the original app
695         // does not grant signature capabilities to its past certs
696         assertInstallSucceeds(
697                 "v3-rsa-pkcs1-sha256-2048-2-with_por_1_2-no-perm-cap-permdef-companion.apk");
698         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-permdef.apk");
699     }
700 
testInstallV3KeyRotationGetSignatures()701     public void testInstallV3KeyRotationGetSignatures() throws Exception {
702         // tests that a PackageInfo w/GET_SIGNATURES flag returns the older cert
703         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
704         Utils.runDeviceTests(
705                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testGetSignaturesShowsOld");
706     }
707 
testInstallV3KeyRotationGetSigningCertificates()708     public void testInstallV3KeyRotationGetSigningCertificates() throws Exception {
709         // tests that a PackageInfo w/GET_SIGNING_CERTIFICATES flag returns the old and new certs
710         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
711         Utils.runDeviceTests(
712                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
713                 "testGetSigningCertificatesShowsAll");
714     }
715 
testInstallV3KeyRotationHasSigningCertificate()716     public void testInstallV3KeyRotationHasSigningCertificate() throws Exception {
717         // tests that hasSigningCertificate() recognizes past and current signing certs
718         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
719         Utils.runDeviceTests(
720                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
721                 "testHasSigningCertificate");
722     }
723 
testInstallV3KeyRotationHasSigningCertificateSha256()724     public void testInstallV3KeyRotationHasSigningCertificateSha256() throws Exception {
725         // tests that hasSigningCertificate() recognizes past and current signing certs by sha256
726         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
727         Utils.runDeviceTests(
728                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
729                 "testHasSigningCertificateSha256");
730     }
731 
testInstallV3KeyRotationHasSigningCertificateByUid()732     public void testInstallV3KeyRotationHasSigningCertificateByUid() throws Exception {
733         // tests that hasSigningCertificate() recognizes past and current signing certs by uid
734         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
735         Utils.runDeviceTests(
736                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
737                 "testHasSigningCertificateByUid");
738     }
739 
testInstallV3KeyRotationHasSigningCertificateByUidSha256()740     public void testInstallV3KeyRotationHasSigningCertificateByUidSha256() throws Exception {
741         // tests that hasSigningCertificate() recognizes past and current signing certs by uid
742         // and sha256
743         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
744         Utils.runDeviceTests(
745                 getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
746                 "testHasSigningCertificateByUidSha256");
747     }
748 
testInstallV3KeyRotationHasDuplicateSigningCertificateHistory()749     public void testInstallV3KeyRotationHasDuplicateSigningCertificateHistory() throws Exception {
750         // tests that an app's proof-of-rotation signing history cannot contain the same certificate
751         // more than once.
752         assertInstallFails("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2_2-full-caps.apk");
753     }
754 
testInstallV3HasMultipleSigners()755     public void testInstallV3HasMultipleSigners() throws Exception {
756         // tests that an app can't be signed by multiple signers when using v3 signature scheme
757         assertInstallFails("v3-rsa-pkcs1-sha256-2048-1_and_2.apk");
758     }
759 
testInstallV3HasMultiplePlatformSigners()760     public void testInstallV3HasMultiplePlatformSigners() throws Exception {
761         // tests that an app can be signed by multiple v3 signers if they target different platform
762         // versions
763         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1_P_and_2_Qplus.apk");
764     }
765 
assertInstallSucceeds(String apkFilenameInResources)766     private void assertInstallSucceeds(String apkFilenameInResources) throws Exception {
767         String installResult = installPackageFromResource(apkFilenameInResources);
768         if (installResult != null) {
769             fail("Failed to install " + apkFilenameInResources + ": " + installResult);
770         }
771     }
772 
assertInstallEphemeralSucceeds(String apkFilenameInResources)773     private void assertInstallEphemeralSucceeds(String apkFilenameInResources) throws Exception {
774         String installResult = installEphemeralPackageFromResource(apkFilenameInResources);
775         if (installResult != null) {
776             fail("Failed to install " + apkFilenameInResources + ": " + installResult);
777         }
778     }
779 
assertInstallSucceedsForEach( String apkFilenamePatternInResources, String[] args)780     private void assertInstallSucceedsForEach(
781             String apkFilenamePatternInResources, String[] args) throws Exception {
782         for (String arg : args) {
783             String apkFilenameInResources =
784                     String.format(Locale.US, apkFilenamePatternInResources, arg);
785             String installResult = installPackageFromResource(apkFilenameInResources);
786             if (installResult != null) {
787                 fail("Failed to install " + apkFilenameInResources + ": " + installResult);
788             }
789             try {
790                 uninstallPackage();
791             } catch (Exception e) {
792                 throw new RuntimeException(
793                         "Failed to uninstall after installing " + apkFilenameInResources, e);
794             }
795         }
796     }
797 
assertInstallFailsWithError( String apkFilenameInResources, String errorSubstring)798     private void assertInstallFailsWithError(
799             String apkFilenameInResources, String errorSubstring) throws Exception {
800         String installResult = installPackageFromResource(apkFilenameInResources);
801         if (installResult == null) {
802             fail("Install of " + apkFilenameInResources + " succeeded but was expected to fail"
803                     + " with \"" + errorSubstring + "\"");
804         }
805         assertContains(
806                 "Install failure message of " + apkFilenameInResources,
807                 errorSubstring,
808                 installResult);
809     }
810 
assertInstallEphemeralFailsWithError( String apkFilenameInResources, String errorSubstring)811     private void assertInstallEphemeralFailsWithError(
812             String apkFilenameInResources, String errorSubstring) throws Exception {
813         String installResult = installEphemeralPackageFromResource(apkFilenameInResources);
814         if (installResult == null) {
815             fail("Install of " + apkFilenameInResources + " succeeded but was expected to fail"
816                     + " with \"" + errorSubstring + "\"");
817         }
818         assertContains(
819                 "Install failure message of " + apkFilenameInResources,
820                 errorSubstring,
821                 installResult);
822     }
823 
assertInstallFails(String apkFilenameInResources)824     private void assertInstallFails(String apkFilenameInResources) throws Exception {
825         String installResult = installPackageFromResource(apkFilenameInResources);
826         if (installResult == null) {
827             fail("Install of " + apkFilenameInResources + " succeeded but was expected to fail");
828         }
829     }
830 
assertContains(String message, String expectedSubstring, String actual)831     private static void assertContains(String message, String expectedSubstring, String actual) {
832         String errorPrefix = ((message != null) && (message.length() > 0)) ? (message + ": ") : "";
833         if (actual == null) {
834             fail(errorPrefix + "Expected to contain \"" + expectedSubstring + "\", but was null");
835         }
836         if (!actual.contains(expectedSubstring)) {
837             fail(errorPrefix + "Expected to contain \"" + expectedSubstring + "\", but was \""
838                     + actual + "\"");
839         }
840     }
841 
installDeviceTestPkg()842     private void installDeviceTestPkg() throws Exception {
843         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
844         File apk = buildHelper.getTestFile(DEVICE_TESTS_APK);
845         String result = getDevice().installPackage(apk, true);
846         assertNull("failed to install " + DEVICE_TESTS_APK + ", Reason: " + result, result);
847     }
848 
installPackageFromResource(String apkFilenameInResources, boolean ephemeral)849     private String installPackageFromResource(String apkFilenameInResources, boolean ephemeral)
850             throws IOException, DeviceNotAvailableException {
851         // ITestDevice.installPackage API requires the APK to be install to be a File. We thus
852         // copy the requested resource into a temporary file, attempt to install it, and delete the
853         // file during cleanup.
854 
855         String fullResourceName = TEST_APK_RESOURCE_PREFIX + apkFilenameInResources;
856         File apkFile = File.createTempFile("pkginstalltest", ".apk");
857         try {
858             try (InputStream in = getClass().getResourceAsStream(fullResourceName);
859                     OutputStream out = new BufferedOutputStream(new FileOutputStream(apkFile))) {
860                 if (in == null) {
861                     throw new IllegalArgumentException("Resource not found: " + fullResourceName);
862                 }
863                 byte[] buf = new byte[65536];
864                 int chunkSize;
865                 while ((chunkSize = in.read(buf)) != -1) {
866                     out.write(buf, 0, chunkSize);
867                 }
868             }
869             if (ephemeral) {
870                 return getDevice().installPackage(apkFile, true, "--ephemeral");
871             } else {
872                 return getDevice().installPackage(apkFile, true);
873             }
874         } finally {
875             apkFile.delete();
876         }
877     }
878 
installPackageFromResource(String apkFilenameInResources)879     private String installPackageFromResource(String apkFilenameInResources)
880             throws IOException, DeviceNotAvailableException {
881         return installPackageFromResource(apkFilenameInResources, false);
882     }
883 
installEphemeralPackageFromResource(String apkFilenameInResources)884     private String installEphemeralPackageFromResource(String apkFilenameInResources)
885             throws IOException, DeviceNotAvailableException {
886         return installPackageFromResource(apkFilenameInResources, true);
887     }
888 
uninstallPackage()889     private String uninstallPackage() throws DeviceNotAvailableException {
890         return getDevice().uninstallPackage(TEST_PKG);
891     }
892 
uninstallCompanionPackage()893     private String uninstallCompanionPackage() throws DeviceNotAvailableException {
894         return getDevice().uninstallPackage(COMPANION_TEST_PKG);
895     }
896 
uninstallDeviceTestPackage()897     private String uninstallDeviceTestPackage() throws DeviceNotAvailableException {
898         return getDevice().uninstallPackage(DEVICE_TESTS_PKG);
899     }
900 
uninstallPackages()901     private void uninstallPackages() throws DeviceNotAvailableException {
902         uninstallPackage();
903         uninstallCompanionPackage();
904         uninstallDeviceTestPackage();
905     }
906 }
907