1 /*
2  * Copyright (C) 2020 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 androidx.core.appdigest;
18 
19 import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
20 
21 import static androidx.core.appdigest.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
22 import static androidx.core.appdigest.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
23 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_MD5;
24 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
25 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA1;
26 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA256;
27 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA512;
28 import static androidx.core.appdigest.Checksums.TRUST_ALL;
29 import static androidx.core.appdigest.Checksums.TRUST_NONE;
30 
31 import static org.junit.Assert.assertEquals;
32 import static org.junit.Assert.assertFalse;
33 import static org.junit.Assert.assertNotNull;
34 import static org.junit.Assert.assertNull;
35 import static org.junit.Assert.assertTrue;
36 
37 import android.Manifest;
38 import android.app.PendingIntent;
39 import android.app.UiAutomation;
40 import android.content.BroadcastReceiver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.PackageInfo;
46 import android.content.pm.PackageInstaller;
47 import android.content.pm.PackageManager;
48 import android.content.pm.Signature;
49 import android.os.Build;
50 import android.os.ParcelFileDescriptor;
51 
52 import androidx.annotation.RequiresApi;
53 import androidx.concurrent.futures.ResolvableFuture;
54 import androidx.test.core.app.ApplicationProvider;
55 import androidx.test.ext.junit.runners.AndroidJUnit4;
56 import androidx.test.filters.LargeTest;
57 import androidx.test.filters.SdkSuppress;
58 import androidx.test.filters.SmallTest;
59 import androidx.test.platform.app.InstrumentationRegistry;
60 
61 import org.jspecify.annotations.NonNull;
62 import org.jspecify.annotations.Nullable;
63 import org.junit.After;
64 import org.junit.Assert;
65 import org.junit.Assume;
66 import org.junit.Before;
67 import org.junit.BeforeClass;
68 import org.junit.Test;
69 import org.junit.runner.RunWith;
70 
71 import java.io.ByteArrayInputStream;
72 import java.io.ByteArrayOutputStream;
73 import java.io.DataInputStream;
74 import java.io.DataOutputStream;
75 import java.io.EOFException;
76 import java.io.IOException;
77 import java.io.InputStream;
78 import java.io.OutputStream;
79 import java.security.cert.Certificate;
80 import java.security.cert.CertificateFactory;
81 import java.security.cert.X509Certificate;
82 import java.util.ArrayList;
83 import java.util.Arrays;
84 import java.util.Comparator;
85 import java.util.List;
86 import java.util.concurrent.Executor;
87 import java.util.concurrent.Executors;
88 import java.util.function.Predicate;
89 
90 @SuppressWarnings("deprecation") /* WHOLE_MD5, WHOLE_SHA1 */
91 @RunWith(AndroidJUnit4.class)
92 public class ChecksumsTest {
93     private static final String INSTALLER_PACKAGE_NAME = "androidx.core.appdigest.test";
94     private static final String V2V3_PACKAGE_NAME = "androidx.core.appdigest.test";
95     private static final String V4_PACKAGE_NAME = "com.example.helloworld";
96     private static final String FIXED_PACKAGE_NAME = "android.appsecurity.cts.tinyapp";
97 
98     private static final String TEST_V4_APK = "HelloWorld5.apk";
99     private static final String TEST_V4_SPLIT0 = "HelloWorld5_hdpi-v4.apk";
100     private static final String TEST_V4_SPLIT1 = "HelloWorld5_mdpi-v4.apk";
101     private static final String TEST_V4_SPLIT2 = "HelloWorld5_xhdpi-v4.apk";
102     private static final String TEST_V4_SPLIT3 = "HelloWorld5_xxhdpi-v4.apk";
103     private static final String TEST_V4_SPLIT4 = "HelloWorld5_xxxhdpi-v4.apk";
104 
105     private static final String TEST_FIXED_APK = "CtsPkgInstallTinyAppV2V3V4.apk";
106     private static final String TEST_FIXED_APK_DIGESTS_FILE =
107             "CtsPkgInstallTinyAppV2V3V4.digests";
108     private static final String TEST_FIXED_APK_DIGESTS_SIGNATURE =
109             "CtsPkgInstallTinyAppV2V3V4.digests.signature";
110     private static final String TEST_CERTIFICATE = "test-cert.x509.pem";
111     private static final String TEST_FIXED_APK_V1 = "CtsPkgInstallTinyAppV1.apk";
112     private static final String TEST_FIXED_APK_V2_SHA512 =
113             "CtsPkgInstallTinyAppV2V3V4-Sha512withEC.apk";
114     private static final String TEST_FIXED_APK_VERITY = "CtsPkgInstallTinyAppV2V3V4-Verity.apk";
115 
116     private static final String TEST_FIXED_APK_MD5 = "c19868da017dc01467169f8ea7c5bc57";
117     private static final String TEST_FIXED_APK_V2_SHA256 =
118             "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3";
119     private static final String TEST_FIXED_APK_SHA256 =
120             "91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef";
121     private static final String TEST_FIXED_APK_SHA512 =
122             "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af525357"
123                     + "11e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018";
124 
125     private static final byte[] NO_SIGNATURE = null;
126 
127     private static final int ALL_CHECKSUMS =
128             TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256
129                     | TYPE_WHOLE_SHA512
130                     | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
131     private static final char[] HEX_LOWER_CASE_DIGITS =
132             {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
133     private Context mContext;
134     private Executor mExecutor;
135 
136     @BeforeClass
onBeforeClass()137     public static void onBeforeClass() throws Exception {
138         uninstallPackageSilently(V4_PACKAGE_NAME);
139         uninstallPackageSilently(FIXED_PACKAGE_NAME);
140     }
141 
getChecksums(@onNull Context context, @NonNull Executor executor, @NonNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)142     private static Checksum[] getChecksums(@NonNull Context context, @NonNull Executor executor,
143             @NonNull String packageName, boolean includeSplits, @Checksum.Type int required,
144             @NonNull List<Certificate> trustedInstallers) throws Exception {
145         Checksum[] checksums = Checksums.getChecksums(context, packageName, includeSplits,
146                 required, trustedInstallers, executor).get();
147 
148         Arrays.sort(checksums, new Comparator<Checksum>() {
149             @Override
150             public int compare(Checksum lhs, Checksum rhs) {
151                 final String lhsSplit = lhs.getSplitName();
152                 final String rhsSplit = rhs.getSplitName();
153                 if ((lhsSplit == rhsSplit) || (lhsSplit != null && lhsSplit.equals(rhsSplit))) {
154                     return Integer.signum(lhs.getType() - rhs.getType());
155                 }
156                 if (lhsSplit == null) {
157                     return -1;
158                 }
159                 if (rhsSplit == null) {
160                     return +1;
161                 }
162                 return lhsSplit.compareTo(rhsSplit);
163             }
164         });
165 
166         return checksums;
167     }
168 
getFileChecksums(@onNull Context context, @NonNull Executor executor, @NonNull String filePath, @Checksum.Type int required, @Nullable String installerPackageName, @NonNull List<Certificate> trustedInstallers)169     private static Checksum[] getFileChecksums(@NonNull Context context, @NonNull Executor executor,
170             @NonNull String filePath, @Checksum.Type int required,
171             @Nullable String installerPackageName,
172             @NonNull List<Certificate> trustedInstallers) throws Exception {
173         Checksum[] checksums = Checksums.getFileChecksums(context, filePath,
174                 required, installerPackageName, trustedInstallers, executor).get();
175 
176         Arrays.sort(checksums, new Comparator<Checksum>() {
177             @Override
178             public int compare(Checksum lhs, Checksum rhs) {
179                 return Integer.signum(lhs.getType() - rhs.getType());
180             }
181         });
182 
183         return checksums;
184     }
185 
getResourceAsStream(String name)186     public static InputStream getResourceAsStream(String name) {
187         return Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
188     }
189 
readFullStream(InputStream inputStream)190     private static String readFullStream(InputStream inputStream) throws IOException {
191         ByteArrayOutputStream result = new ByteArrayOutputStream();
192         writeFullStream(inputStream, result, -1);
193         return result.toString("UTF-8");
194     }
195 
readAllBytes(InputStream inputStream)196     private static byte[] readAllBytes(InputStream inputStream) throws IOException {
197         ByteArrayOutputStream result = new ByteArrayOutputStream();
198         writeFullStream(inputStream, result, -1);
199         return result.toByteArray();
200     }
201 
writeFullStream(InputStream inputStream, OutputStream outputStream, long expected)202     private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
203             long expected)
204             throws IOException {
205         byte[] buffer = new byte[1024];
206         long total = 0;
207         int length;
208         while ((length = inputStream.read(buffer)) != -1) {
209             outputStream.write(buffer, 0, length);
210             total += length;
211         }
212         if (expected > 0) {
213             Assert.assertEquals(expected, total);
214         }
215     }
216 
executeShellCommand(String command)217     private static String executeShellCommand(String command) throws IOException {
218         if (Build.VERSION.SDK_INT >= 29) {
219             return InstallerApi29.executeShellCommand(command);
220         }
221         return "";
222     }
223 
uninstallPackageSilently(String packageName)224     private static void uninstallPackageSilently(String packageName) throws IOException {
225         executeShellCommand("pm uninstall " + packageName);
226     }
227 
bytesToHexString(byte[] array)228     private static @NonNull String bytesToHexString(byte[] array) {
229         int offset = 0;
230         int length = array.length;
231 
232         char[] digits = HEX_LOWER_CASE_DIGITS;
233         char[] buf = new char[length * 2];
234 
235         int bufIndex = 0;
236         for (int i = offset; i < offset + length; i++) {
237             byte b = array[i];
238             buf[bufIndex++] = digits[(b >>> 4) & 0x0F];
239             buf[bufIndex++] = digits[b & 0x0F];
240         }
241 
242         return new String(buf);
243     }
244 
hexStringToBytes(String s)245     private static byte @NonNull [] hexStringToBytes(String s) {
246         int len = s.length();
247         byte[] data = new byte[len / 2];
248         for (int i = 0; i < len; i += 2) {
249             data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
250                     + Character.digit(s.charAt(i + 1), 16));
251         }
252         return data;
253     }
254 
255     @Before
onBefore()256     public void onBefore() throws Exception {
257         // b/331664452
258         Assume.assumeFalse(Build.VERSION.SDK_INT >= 35);
259         mContext = ApplicationProvider.getApplicationContext();
260         mExecutor = Executors.newCachedThreadPool();
261     }
262 
263     @After
onAfter()264     public void onAfter() throws Exception {
265         uninstallPackageSilently(V4_PACKAGE_NAME);
266         assertFalse(isAppInstalled(V4_PACKAGE_NAME));
267         uninstallPackageSilently(FIXED_PACKAGE_NAME);
268         assertFalse(isAppInstalled(FIXED_PACKAGE_NAME));
269     }
270 
271     @SmallTest
272     @Test
273     @SdkSuppress(maxSdkVersion = 33) // b/262909049: Failing on SDK 34 // b/262909049: Failing on SDK 34
testDefaultChecksums()274     public void testDefaultChecksums() throws Exception {
275         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
276             return; // b/262909049: Do not run this test on pre-release Android U.
277         }
278 
279         Checksum[] checksums = getChecksums(V2V3_PACKAGE_NAME, true, 0, TRUST_NONE);
280         assertNotNull(checksums);
281         if (Build.VERSION.SDK_INT >= 31) {
282             assertEquals(1, checksums.length);
283             assertEquals(checksums[0].getType(),
284                     android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
285         } else {
286             assertEquals(0, checksums.length);
287         }
288     }
289 
290     @SmallTest
291     @Test
testDefaultFileChecksums()292     public void testDefaultFileChecksums() throws Exception {
293         Checksum[] checksums = getFileChecksums(V2V3_PACKAGE_NAME, 0, TRUST_NONE);
294         assertNotNull(checksums);
295         assertEquals(0, checksums.length);
296     }
297 
298     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
299     @LargeTest
300     @Test
testSplitsDefaultChecksums()301     public void testSplitsDefaultChecksums() throws Exception {
302         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
303             return; // b/262909049: Do not run this test on pre-release Android U.
304         }
305 
306         installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2,
307                 TEST_V4_SPLIT3, TEST_V4_SPLIT4});
308         assertTrue(isAppInstalled(V4_PACKAGE_NAME));
309 
310         Checksum[] checksums = getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE);
311         assertNotNull(checksums);
312         if (Build.VERSION.SDK_INT >= 31) {
313             assertEquals(checksums.length, 6);
314             // v2/v3 signature use 1M merkle tree.
315             assertEquals(null, checksums[0].getSplitName());
316             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[0].getType());
317             assertEquals("config.hdpi", checksums[1].getSplitName());
318             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[1].getType());
319             assertEquals("config.mdpi", checksums[2].getSplitName());
320             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[2].getType());
321             assertEquals("config.xhdpi", checksums[3].getSplitName());
322             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[3].getType());
323             assertEquals("config.xxhdpi", checksums[4].getSplitName());
324             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[4].getType());
325             assertEquals("config.xxxhdpi", checksums[5].getSplitName());
326             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType());
327         } else {
328             assertEquals(0, checksums.length);
329         }
330     }
331 
332     @SdkSuppress(minSdkVersion = 29)
333     @LargeTest
334     @Test
testSplitsDefaultFileChecksums()335     public void testSplitsDefaultFileChecksums() throws Exception {
336         installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2,
337                 TEST_V4_SPLIT3, TEST_V4_SPLIT4});
338         assertTrue(isAppInstalled(V4_PACKAGE_NAME));
339 
340         Checksum[] checksums = getFileChecksums(V4_PACKAGE_NAME, 0, TRUST_NONE);
341         assertNotNull(checksums);
342         assertEquals(0, checksums.length);
343     }
344 
345     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
346     @LargeTest
347     @Test
testSplitsSha256Checksums()348     public void testSplitsSha256Checksums() throws Exception {
349         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
350             return; // b/262909049: Do not run this test on pre-release Android U.
351         }
352 
353         installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2,
354                 TEST_V4_SPLIT3, TEST_V4_SPLIT4});
355         assertTrue(isAppInstalled(V4_PACKAGE_NAME));
356 
357         Checksum[] checksums = getChecksums(V4_PACKAGE_NAME, true, TYPE_WHOLE_SHA256,
358                 TRUST_NONE);
359         assertNotNull(checksums);
360         if (Build.VERSION.SDK_INT >= 31) {
361             assertEquals(checksums.length, 12);
362             // v2/v3 signature use 1M merkle tree.
363             assertEquals(null, checksums[0].getSplitName());
364             assertEquals(TYPE_WHOLE_SHA256, checksums[0].getType());
365             assertEquals(bytesToHexString(checksums[0].getValue()),
366                     "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc");
367             assertEquals(null, checksums[1].getSplitName());
368             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[1].getType());
369             assertEquals(checksums[2].getSplitName(), "config.hdpi");
370             assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA256);
371             assertEquals(bytesToHexString(checksums[2].getValue()),
372                     "336a47c278f6b6c22abffefa6a62971fd0bd718d6947143e6ed1f6f6126a8196");
373             assertEquals("config.hdpi", checksums[3].getSplitName());
374             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[3].getType());
375             assertEquals(checksums[4].getSplitName(), "config.mdpi");
376             assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
377             assertEquals(bytesToHexString(checksums[4].getValue()),
378                     "17fe9f85e6f29a7354932002c8bc4cb829e1f4acf7f30626bd298c810bb13215");
379             assertEquals("config.mdpi", checksums[5].getSplitName());
380             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType());
381             assertEquals(checksums[6].getSplitName(), "config.xhdpi");
382             assertEquals(checksums[6].getType(), TYPE_WHOLE_SHA256);
383             assertEquals(bytesToHexString(checksums[6].getValue()),
384                     "71a0b0ac5970def7ad80071c909be1e446174a9b39ea5cbf3004db05f87bcc4b");
385             assertEquals("config.xhdpi", checksums[7].getSplitName());
386             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[7].getType());
387             assertEquals(checksums[8].getSplitName(), "config.xxhdpi");
388             assertEquals(checksums[8].getType(), TYPE_WHOLE_SHA256);
389             assertEquals(bytesToHexString(checksums[8].getValue()),
390                     "cf6eaee309cf906df5519b9a449ab136841cec62857e283fb4fd20dcd2ea14aa");
391             assertEquals("config.xxhdpi", checksums[9].getSplitName());
392             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[9].getType());
393             assertEquals(checksums[10].getSplitName(), "config.xxxhdpi");
394             assertEquals(checksums[10].getType(), TYPE_WHOLE_SHA256);
395             assertEquals(bytesToHexString(checksums[10].getValue()),
396                     "e7c51a01794d33e13d005b62e5ae96a39215bc588e0a2ef8f6161e1e360a17cc");
397             assertEquals("config.xxxhdpi", checksums[11].getSplitName());
398             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[11].getType());
399         } else {
400             assertEquals(6, checksums.length);
401             assertEquals(checksums[0].getSplitName(), null);
402             assertEquals(checksums[0].getType(), TYPE_WHOLE_SHA256);
403             assertEquals(bytesToHexString(checksums[0].getValue()),
404                     "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc");
405             assertEquals(checksums[1].getSplitName(), "config.hdpi");
406             assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
407             assertEquals(bytesToHexString(checksums[1].getValue()),
408                     "336a47c278f6b6c22abffefa6a62971fd0bd718d6947143e6ed1f6f6126a8196");
409             assertEquals(checksums[2].getSplitName(), "config.mdpi");
410             assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA256);
411             assertEquals(bytesToHexString(checksums[2].getValue()),
412                     "17fe9f85e6f29a7354932002c8bc4cb829e1f4acf7f30626bd298c810bb13215");
413             assertEquals(checksums[3].getSplitName(), "config.xhdpi");
414             assertEquals(checksums[3].getType(), TYPE_WHOLE_SHA256);
415             assertEquals(bytesToHexString(checksums[3].getValue()),
416                     "71a0b0ac5970def7ad80071c909be1e446174a9b39ea5cbf3004db05f87bcc4b");
417             assertEquals(checksums[4].getSplitName(), "config.xxhdpi");
418             assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
419             assertEquals(bytesToHexString(checksums[4].getValue()),
420                     "cf6eaee309cf906df5519b9a449ab136841cec62857e283fb4fd20dcd2ea14aa");
421             assertEquals(checksums[5].getSplitName(), "config.xxxhdpi");
422             assertEquals(checksums[5].getType(), TYPE_WHOLE_SHA256);
423             assertEquals(bytesToHexString(checksums[5].getValue()),
424                     "e7c51a01794d33e13d005b62e5ae96a39215bc588e0a2ef8f6161e1e360a17cc");
425         }
426     }
427 
428     @SdkSuppress(minSdkVersion = 29)
429     @LargeTest
430     @Test
testSplitsSha256FileChecksums()431     public void testSplitsSha256FileChecksums() throws Exception {
432         installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2,
433                 TEST_V4_SPLIT3, TEST_V4_SPLIT4});
434         assertTrue(isAppInstalled(V4_PACKAGE_NAME));
435 
436         Checksum[] checksums = getFileChecksums(V4_PACKAGE_NAME, TYPE_WHOLE_SHA256,
437                 TRUST_NONE);
438         assertNotNull(checksums);
439         assertEquals(1, checksums.length);
440         assertEquals(checksums[0].getSplitName(), null);
441         assertEquals(checksums[0].getType(), TYPE_WHOLE_SHA256);
442         assertEquals(bytesToHexString(checksums[0].getValue()),
443                 "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc");
444     }
445 
446     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
447     @LargeTest
448     @Test
testFixedDefaultChecksums()449     public void testFixedDefaultChecksums() throws Exception {
450         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
451             return; // b/262909049: Do not run this test on pre-release Android U.
452         }
453 
454         installPackage(TEST_FIXED_APK);
455         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
456 
457         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0,
458                 TRUST_NONE);
459         assertNotNull(checksums);
460         if (Build.VERSION.SDK_INT >= 31) {
461             assertEquals(1, checksums.length);
462             // v2/v3 signature use 1M merkle tree.
463             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[0].getType());
464             assertEquals(TEST_FIXED_APK_V2_SHA256, bytesToHexString(checksums[0].getValue()));
465             assertNull(checksums[0].getInstallerCertificate());
466         } else {
467             assertEquals(0, checksums.length);
468         }
469     }
470 
471     @SdkSuppress(minSdkVersion = 29)
472     @LargeTest
473     @Test
testFixedDefaultFileChecksums()474     public void testFixedDefaultFileChecksums() throws Exception {
475         installPackage(TEST_FIXED_APK);
476         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
477 
478         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE);
479         assertNotNull(checksums);
480         assertEquals(0, checksums.length);
481     }
482 
483     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
484     @LargeTest
485     @Test
testFixedV1DefaultChecksums()486     public void testFixedV1DefaultChecksums() throws Exception {
487         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
488             return; // b/262909049: Do not run this test on pre-release Android U.
489         }
490 
491         installPackage(TEST_FIXED_APK_V1);
492         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
493 
494         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0,
495                 TRUST_NONE);
496         assertNotNull(checksums);
497         assertEquals(0, checksums.length);
498     }
499 
500     @SdkSuppress(minSdkVersion = 29)
501     @LargeTest
502     @Test
testFixedV1DefaultFileChecksums()503     public void testFixedV1DefaultFileChecksums() throws Exception {
504         installPackage(TEST_FIXED_APK_V1);
505         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
506 
507         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE);
508         assertNotNull(checksums);
509         assertEquals(0, checksums.length);
510     }
511 
512     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
513     @LargeTest
514     @Test
testFixedSha512DefaultChecksums()515     public void testFixedSha512DefaultChecksums() throws Exception {
516         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
517             return; // b/262909049: Do not run this test on pre-release Android U.
518         }
519 
520         installPackage(TEST_FIXED_APK_V2_SHA512);
521         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
522 
523         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0,
524                 TRUST_NONE);
525         assertNotNull(checksums);
526         if (Build.VERSION.SDK_INT >= 31) {
527             assertEquals(1, checksums.length);
528             // v2/v3 signature use 1M merkle tree.
529             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, checksums[0].getType());
530             assertEquals(bytesToHexString(checksums[0].getValue()),
531                     "6b866e8a54a3e358dfc20007960fb96123845f6c6d6c45f5fddf88150d71677f"
532                             + "4c3081a58921c88651f7376118aca312cf764b391cdfb8a18c6710f9f27916a0");
533             assertNull(checksums[0].getInstallerCertificate());
534         } else {
535             assertEquals(0, checksums.length);
536         }
537     }
538 
539     @SdkSuppress(minSdkVersion = 29)
540     @LargeTest
541     @Test
testFixedSha512DefaultFileChecksums()542     public void testFixedSha512DefaultFileChecksums() throws Exception {
543         installPackage(TEST_FIXED_APK_V2_SHA512);
544         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
545 
546         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE);
547         assertNotNull(checksums);
548         assertEquals(0, checksums.length);
549     }
550 
551     @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
552     @LargeTest
553     @Test
testFixedVerityDefaultChecksums()554     public void testFixedVerityDefaultChecksums() throws Exception {
555         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
556             return; // b/262909049: Do not run this test on pre-release Android U.
557         }
558 
559         installPackage(TEST_FIXED_APK_VERITY);
560         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
561 
562         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0,
563                 TRUST_NONE);
564         assertNotNull(checksums);
565         // No usable hashes as verity-in-v2-signature does not cover the whole file.
566         assertEquals(0, checksums.length);
567     }
568 
569     @SdkSuppress(minSdkVersion = 29)
570     @LargeTest
571     @Test
testFixedVerityDefaultFileChecksums()572     public void testFixedVerityDefaultFileChecksums() throws Exception {
573         installPackage(TEST_FIXED_APK_VERITY);
574         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
575 
576         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE);
577         assertNotNull(checksums);
578         // No usable hashes as verity-in-v2-signature does not cover the whole file.
579         assertEquals(0, checksums.length);
580     }
581 
582     @LargeTest
583     @Test
testAllChecksums()584     public void testAllChecksums() throws Exception {
585         Checksum[] checksums = getChecksums(V2V3_PACKAGE_NAME, true, ALL_CHECKSUMS,
586                 TRUST_NONE);
587         assertNotNull(checksums);
588         if (Build.VERSION.SDK_INT >= 31) {
589             assertEquals(checksums.length, 7);
590             assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
591             assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
592             assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
593             assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
594             assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
595             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType());
596             assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, checksums[6].getType());
597         } else {
598             assertEquals(5, checksums.length);
599             assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
600             assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
601             assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
602             assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
603             assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
604         }
605     }
606 
607     @LargeTest
608     @Test
testAllFileChecksums()609     public void testAllFileChecksums() throws Exception {
610         Checksum[] checksums = getFileChecksums(V2V3_PACKAGE_NAME, ALL_CHECKSUMS,
611                 TRUST_NONE);
612         assertNotNull(checksums);
613         assertEquals(5, checksums.length);
614         assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
615         assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
616         assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
617         assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
618         assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
619     }
620 
621     @SdkSuppress(minSdkVersion = 29)
622     @LargeTest
623     @Test
testFixedAllChecksums()624     public void testFixedAllChecksums() throws Exception {
625         installPackage(TEST_FIXED_APK);
626         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
627 
628         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS,
629                 TRUST_NONE);
630         validateFixedAllChecksums(checksums);
631     }
632 
633     @SdkSuppress(minSdkVersion = 29)
634     @LargeTest
635     @Test
testFixedAllFileChecksums()636     public void testFixedAllFileChecksums() throws Exception {
637         installPackage(TEST_FIXED_APK);
638         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
639 
640         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, ALL_CHECKSUMS,
641                 TRUST_NONE);
642         validateFixedAllChecksumsFallback(checksums);
643     }
644 
645     @SdkSuppress(minSdkVersion = 29)
646     @LargeTest
647     @Test
testFixedAllChecksumsDirectExecutor()648     public void testFixedAllChecksumsDirectExecutor() throws Exception {
649         installPackage(TEST_FIXED_APK);
650         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
651 
652         Checksum[] checksums = getChecksums(mContext, new Executor() {
653             @Override
654             public void execute(Runnable command) {
655                 command.run();
656             }
657         }, FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE);
658         validateFixedAllChecksums(checksums);
659     }
660 
661     @SdkSuppress(minSdkVersion = 29)
662     @LargeTest
663     @Test
664     @SuppressWarnings("deprecation")
testFixedAllFileChecksumsDirectExecutor()665     public void testFixedAllFileChecksumsDirectExecutor() throws Exception {
666         installPackage(TEST_FIXED_APK);
667         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
668 
669         final String apk =
670                 mContext.getPackageManager().getApplicationInfo(FIXED_PACKAGE_NAME, 0).sourceDir;
671         Checksum[] checksums = getFileChecksums(mContext, new Executor() {
672             @Override
673             public void execute(Runnable command) {
674                 command.run();
675             }
676         }, apk, ALL_CHECKSUMS, INSTALLER_PACKAGE_NAME, TRUST_NONE);
677         validateFixedAllChecksumsFallback(checksums);
678     }
679 
680     @SdkSuppress(minSdkVersion = 29)
681     @LargeTest
682     @Test
testFixedAllChecksumsSingleThread()683     public void testFixedAllChecksumsSingleThread() throws Exception {
684         installPackage(TEST_FIXED_APK);
685         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
686 
687         Checksum[] checksums = getChecksums(mContext, Executors.newSingleThreadExecutor(),
688                 FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE);
689         validateFixedAllChecksums(checksums);
690     }
691 
692     @SdkSuppress(minSdkVersion = 29)
693     @LargeTest
694     @Test
695     @SuppressWarnings("deprecation")
testFixedAllFileChecksumsSingleThread()696     public void testFixedAllFileChecksumsSingleThread() throws Exception {
697         installPackage(TEST_FIXED_APK);
698         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
699 
700         final String apk =
701                 mContext.getPackageManager().getApplicationInfo(FIXED_PACKAGE_NAME, 0).sourceDir;
702         Checksum[] checksums = getFileChecksums(mContext, Executors.newSingleThreadExecutor(),
703                 apk, ALL_CHECKSUMS, INSTALLER_PACKAGE_NAME, TRUST_NONE);
704         validateFixedAllChecksumsFallback(checksums);
705     }
706 
707     @SdkSuppress(minSdkVersion = 29)
708     @LargeTest
709     @Test
testFixedV1AllChecksums()710     public void testFixedV1AllChecksums() throws Exception {
711         installPackage(TEST_FIXED_APK_V1);
712         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
713 
714         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS,
715                 TRUST_NONE);
716         assertNotNull(checksums);
717         assertEquals(5, checksums.length);
718         assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
719         if (Build.VERSION.SDK_INT < 33) {
720             assertEquals("1e8f831ef35257ca30d11668520aaafc6da243e853531caabc3b7867986f8886",
721                     bytesToHexString(checksums[0].getValue()));
722         } else {
723             assertEquals("0b9bd6ef683e0c4e8940aba6460382b33e607c0fcf487f3dc6a44b715615d166",
724                     bytesToHexString(checksums[0].getValue()));
725         }
726         assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
727         assertEquals(bytesToHexString(checksums[1].getValue()), "78e51e8c51e4adc6870cd71389e0f3db");
728         assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
729         assertEquals("f6654505f2274fd9bfc098b660cdfdc2e4da6d53",
730                 bytesToHexString(checksums[2].getValue()));
731         assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
732         assertEquals("43755d36ec944494f6275ee92662aca95079b3aa6639f2d35208c5af15adff78",
733                 bytesToHexString(checksums[3].getValue()));
734         assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
735         assertEquals("030fc815a4957c163af2bc6f30dd5b48ac09c94c25a824a514609e1476f91421"
736                         + "e2c8b6baa16ef54014ad6c5b90c37b26b0f5c8aeb01b63a1db2eca133091c8d1",
737                 bytesToHexString(checksums[4].getValue()));
738     }
739 
740     @SdkSuppress(minSdkVersion = 29)
741     @LargeTest
742     @Test
testFixedV1AllFileChecksums()743     public void testFixedV1AllFileChecksums() throws Exception {
744         installPackage(TEST_FIXED_APK_V1);
745         assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
746 
747         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, ALL_CHECKSUMS,
748                 TRUST_NONE);
749         assertNotNull(checksums);
750         assertEquals(5, checksums.length);
751         assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
752         assertEquals("1e8f831ef35257ca30d11668520aaafc6da243e853531caabc3b7867986f8886",
753                 bytesToHexString(checksums[0].getValue()));
754         assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
755         assertEquals(bytesToHexString(checksums[1].getValue()), "78e51e8c51e4adc6870cd71389e0f3db");
756         assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
757         assertEquals("f6654505f2274fd9bfc098b660cdfdc2e4da6d53",
758                 bytesToHexString(checksums[2].getValue()));
759         assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
760         assertEquals("43755d36ec944494f6275ee92662aca95079b3aa6639f2d35208c5af15adff78",
761                 bytesToHexString(checksums[3].getValue()));
762         assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
763         assertEquals("030fc815a4957c163af2bc6f30dd5b48ac09c94c25a824a514609e1476f91421"
764                         + "e2c8b6baa16ef54014ad6c5b90c37b26b0f5c8aeb01b63a1db2eca133091c8d1",
765                 bytesToHexString(checksums[4].getValue()));
766     }
767 
768     @SdkSuppress(minSdkVersion = 31)
769     @SmallTest
770     @Test
testReadWriteChecksums()771     public void testReadWriteChecksums() throws Exception {
772         InstallerApi31.checkStoredChecksums(TEST_FIXED_APK_DIGESTS_FILE);
773         InstallerApi31.checkWrittenChecksums(TEST_FIXED_APK_DIGESTS_FILE);
774     }
775 
776     @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
777     @LargeTest
778     @Test
testInstallerChecksumsTrustNone()779     public void testInstallerChecksumsTrustNone() throws Exception {
780         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
781             return; // b/262909049: Do not run this test on pre-release Android U.
782         }
783 
784         installApkWithChecksums(NO_SIGNATURE);
785 
786         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE);
787         assertNotNull(checksums);
788         assertEquals(1, checksums.length);
789         assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
790         assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
791         assertNull(checksums[0].getInstallerPackageName());
792         assertNull(checksums[0].getInstallerCertificate());
793     }
794 
795     @SdkSuppress(minSdkVersion = 31)
796     @LargeTest
797     @Test
testInstallerFileChecksumsTrustNone()798     public void testInstallerFileChecksumsTrustNone() throws Exception {
799         installApkWithChecksums(NO_SIGNATURE);
800 
801         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE);
802         assertNotNull(checksums);
803         assertEquals(0, checksums.length);
804     }
805 
806     @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
807     @LargeTest
808     @Test
testInstallerChecksumsTrustAll()809     public void testInstallerChecksumsTrustAll() throws Exception {
810         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
811             return; // b/262909049: Do not run this test on pre-release Android U.
812         }
813 
814         installApkWithChecksums(NO_SIGNATURE);
815 
816         final Certificate certificate = InstallerApi31.getInstallerCertificate(mContext);
817 
818         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL);
819 
820         assertNotNull(checksums);
821         // installer provided.
822         assertEquals(3, checksums.length);
823         assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
824         assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
825         assertEquals(checksums[0].getSplitName(), null);
826         assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
827         assertEquals(checksums[0].getInstallerCertificate(), certificate);
828         assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
829         assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
830         assertEquals(checksums[1].getSplitName(), null);
831         assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
832         assertEquals(checksums[1].getInstallerCertificate(), certificate);
833         assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
834         assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
835         assertEquals(checksums[2].getSplitName(), null);
836         assertNull(checksums[2].getInstallerPackageName());
837         assertNull(checksums[2].getInstallerCertificate());
838     }
839 
840     @SdkSuppress(minSdkVersion = 31)
841     @LargeTest
842     @Test
testInstallerFileChecksumsTrustAll()843     public void testInstallerFileChecksumsTrustAll() throws Exception {
844         installApkWithChecksums(NO_SIGNATURE);
845 
846         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME,
847                 TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA256, TRUST_ALL);
848 
849         assertNotNull(checksums);
850         assertEquals(2, checksums.length);
851         assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
852         assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
853         assertEquals(checksums[0].getSplitName(), null);
854         assertNull(checksums[0].getInstallerPackageName());
855         assertNull(checksums[0].getInstallerCertificate());
856         assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
857         assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
858         assertEquals(checksums[1].getSplitName(), null);
859         assertNull(checksums[1].getInstallerPackageName());
860         assertNull(checksums[1].getInstallerCertificate());
861         /* Uncomment when access bug is fixed.
862         final Certificate certificate = InstallerApi31.getInstallerCertificate(mContext);
863         assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
864         assertEquals(checksums[0].getInstallerCertificate(), certificate);
865         assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
866         assertEquals(checksums[1].getInstallerCertificate(), certificate);
867         assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
868         assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
869         assertEquals(checksums[2].getSplitName(), null);
870         assertEquals(checksums[2].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
871         assertEquals(checksums[2].getInstallerCertificate(), certificate);
872         */
873     }
874 
875     @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34
876     @LargeTest
877     @Test
testInstallerSignedChecksums()878     public void testInstallerSignedChecksums() throws Exception {
879         if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) {
880             return; // b/262909049: Do not run this test on pre-release Android U.
881         }
882 
883         final byte[] signature = InstallerApi31.readSignature();
884         final Certificate certificate = InstallerApi31.readCertificate();
885 
886         installApkWithChecksums(signature);
887 
888         Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL);
889 
890         assertNotNull(checksums);
891         assertEquals(3, checksums.length);
892         assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
893         assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
894         assertEquals(checksums[0].getSplitName(), null);
895         assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
896         assertEquals(checksums[0].getInstallerCertificate(), certificate);
897         assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
898         assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
899         assertEquals(checksums[1].getSplitName(), null);
900         assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
901         assertEquals(checksums[1].getInstallerCertificate(), certificate);
902         assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
903         assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
904         assertEquals(checksums[2].getSplitName(), null);
905         assertNull(checksums[2].getInstallerPackageName());
906         assertNull(checksums[2].getInstallerCertificate());
907     }
908 
909     @SdkSuppress(minSdkVersion = 31)
910     @LargeTest
911     @Test
testInstallerSignedFileChecksums()912     public void testInstallerSignedFileChecksums() throws Exception {
913         final byte[] signature = InstallerApi31.readSignature();
914 
915         installApkWithChecksums(signature);
916 
917         Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME,
918                 TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA256, TRUST_ALL);
919 
920         assertNotNull(checksums);
921         assertEquals(2, checksums.length);
922         assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
923         assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
924         assertEquals(checksums[0].getSplitName(), null);
925         assertNull(checksums[0].getInstallerPackageName());
926         assertNull(checksums[0].getInstallerCertificate());
927         assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
928         assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
929         assertEquals(checksums[1].getSplitName(), null);
930         assertNull(checksums[1].getInstallerPackageName());
931         assertNull(checksums[1].getInstallerCertificate());
932         /* Uncomment when access bug is fixed.
933         final Certificate certificate = InstallerApi31.readCertificate();
934         assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
935         assertEquals(checksums[0].getInstallerCertificate(), certificate);
936         assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
937         assertEquals(checksums[1].getInstallerCertificate(), certificate);
938         assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
939         assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
940         assertEquals(checksums[2].getSplitName(), null);
941         assertEquals(checksums[2].getInstallerPackageName(), INSTALLER_PACKAGE_NAME);
942         assertEquals(checksums[2].getInstallerCertificate(), certificate);
943         */
944     }
945 
validateFixedAllChecksums(Checksum[] checksums)946     private void validateFixedAllChecksums(Checksum[] checksums) {
947         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
948             validateFixedAllChecksumsFallback(checksums);
949             return;
950         }
951         assertNotNull(checksums);
952         assertEquals(7, checksums.length);
953         assertEquals(checksums[0].getType(),
954                 android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
955         if (Build.VERSION.SDK_INT < 33) {
956             assertEquals(bytesToHexString(checksums[0].getValue()),
957                     "90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a");
958         } else {
959             assertEquals(bytesToHexString(checksums[0].getValue()),
960                     "759626c33083fbf43215cb5b17156977d963d4c6850c0cb4e73162a665db560b");
961         }
962         assertEquals(checksums[1].getType(), android.content.pm.Checksum.TYPE_WHOLE_MD5);
963         assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5);
964         assertEquals(checksums[2].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA1);
965         assertEquals(bytesToHexString(checksums[2].getValue()),
966                 "331eef6bc57671de28cbd7e32089d047285ade6a");
967         assertEquals(checksums[3].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA256);
968         assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256);
969         assertEquals(checksums[4].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA512);
970         assertEquals(bytesToHexString(checksums[4].getValue()),
971                 "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571"
972                         + "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018");
973         assertEquals(checksums[5].getType(),
974                 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
975         assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256);
976         assertEquals(checksums[6].getType(),
977                 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
978         assertEquals(bytesToHexString(checksums[6].getValue()),
979                 "ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84"
980                         + "5f2888afcb71524196dda0d6dd16a6a3292bb75b431b8ff74fb60d796e882f80");
981     }
982 
validateFixedAllChecksumsFallback(Checksum[] checksums)983     private void validateFixedAllChecksumsFallback(Checksum[] checksums) {
984         assertNotNull(checksums);
985         assertEquals(5, checksums.length);
986         assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType());
987         assertEquals("90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a",
988                 bytesToHexString(checksums[0].getValue()));
989         assertEquals(TYPE_WHOLE_MD5, checksums[1].getType());
990         assertEquals(TEST_FIXED_APK_MD5, bytesToHexString(checksums[1].getValue()));
991         assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType());
992         assertEquals("331eef6bc57671de28cbd7e32089d047285ade6a",
993                 bytesToHexString(checksums[2].getValue()));
994         assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType());
995         assertEquals(TEST_FIXED_APK_SHA256, bytesToHexString(checksums[3].getValue()));
996         assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType());
997         assertEquals(TEST_FIXED_APK_SHA512, bytesToHexString(checksums[4].getValue()));
998     }
999 
getChecksums(@onNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)1000     private Checksum[] getChecksums(@NonNull String packageName, boolean includeSplits,
1001             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)
1002             throws Exception {
1003         return getChecksums(mContext, mExecutor, packageName, includeSplits, required,
1004                 trustedInstallers);
1005     }
1006 
1007     @SuppressWarnings("deprecation")
getFileChecksums(@onNull String packageName, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)1008     private Checksum[] getFileChecksums(@NonNull String packageName,
1009             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)
1010             throws Exception {
1011         final ApplicationInfo applicationInfo =
1012                 mContext.getPackageManager().getApplicationInfo(packageName, 0);
1013         if (applicationInfo == null) {
1014             throw new PackageManager.NameNotFoundException(packageName);
1015         }
1016 
1017         return getFileChecksums(mContext, mExecutor, applicationInfo.sourceDir, required,
1018                 INSTALLER_PACKAGE_NAME, trustedInstallers);
1019     }
1020 
installPackage(String baseName)1021     private void installPackage(String baseName) throws Exception {
1022         installSplits(new String[]{baseName});
1023     }
1024 
installSplits(String[] names)1025     void installSplits(String[] names) throws Exception {
1026         if (Build.VERSION.SDK_INT >= 29) {
1027             new InstallerApi29(mContext).installSplits(names);
1028         }
1029     }
1030 
installApkWithChecksums(byte[] signature)1031     void installApkWithChecksums(byte[] signature) throws Exception {
1032         if (Build.VERSION.SDK_INT >= 31) {
1033             new InstallerApi31(mContext).installApkWithChecksums(TEST_FIXED_APK,
1034                     signature);
1035         }
1036     }
1037 
isAppInstalled(String packageName)1038     private boolean isAppInstalled(String packageName) throws IOException {
1039         if (Build.VERSION.SDK_INT >= 29) {
1040             return InstallerApi29.isAppInstalled(packageName);
1041         }
1042         return false;
1043     }
1044 
1045     @RequiresApi(29)
1046     static class InstallerApi29 {
1047         protected Context mContext;
1048 
InstallerApi29(Context context)1049         InstallerApi29(Context context) {
1050             mContext = context;
1051         }
1052 
writeFileToSession(PackageInstaller.Session session, String name, String apk)1053         static void writeFileToSession(PackageInstaller.Session session, String name,
1054                 String apk) throws IOException {
1055             try (OutputStream os = session.openWrite(name, 0, -1);
1056                  InputStream is = getResourceAsStream(apk)) {
1057                 assertNotNull(name, is);
1058                 writeFullStream(is, os, -1);
1059             }
1060         }
1061 
getUiAutomation()1062         static UiAutomation getUiAutomation() {
1063             return InstrumentationRegistry.getInstrumentation().getUiAutomation();
1064         }
1065 
executeShellCommand(String command)1066         static String executeShellCommand(String command) throws IOException {
1067             final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command);
1068             try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
1069                 return readFullStream(inputStream);
1070             }
1071         }
1072 
isAppInstalled(final String packageName)1073         static boolean isAppInstalled(final String packageName) throws IOException {
1074             final String commandResult = executeShellCommand("pm list packages");
1075             final int prefixLength = "package:".length();
1076             return Arrays.stream(commandResult.split("\\r?\\n"))
1077                     .anyMatch(new Predicate<String>() {
1078                         @Override
1079                         public boolean test(String line) {
1080                             return line.substring(prefixLength).equals(packageName);
1081                         }
1082                     });
1083         }
1084 
installSplits(String[] names)1085         void installSplits(String[] names) throws Exception {
1086             getUiAutomation().adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES);
1087             try {
1088                 final PackageInstaller installer =
1089                         mContext.getPackageManager().getPackageInstaller();
1090                 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
1091                         PackageInstaller.SessionParams.MODE_FULL_INSTALL);
1092 
1093                 final int sessionId = installer.createSession(params);
1094                 PackageInstaller.Session session = installer.openSession(sessionId);
1095 
1096                 for (String name : names) {
1097                     writeFileToSession(session, name, name);
1098                 }
1099 
1100                 commitSession(session);
1101             } finally {
1102                 getUiAutomation().dropShellPermissionIdentity();
1103             }
1104         }
1105 
1106         @SuppressWarnings("deprecation")
commitSession(PackageInstaller.Session session)1107         void commitSession(PackageInstaller.Session session) throws Exception {
1108             final ResolvableFuture<Intent> result = ResolvableFuture.create();
1109 
1110             // Create a single-use broadcast receiver
1111             BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
1112                 @Override
1113                 public void onReceive(Context context, Intent intent) {
1114                     context.unregisterReceiver(this);
1115                     result.set(intent);
1116                 }
1117             };
1118 
1119             // Create a matching intent-filter and register the receiver
1120             final int resultId = result.hashCode();
1121             final String action = "androidx.core.appdigest.COMMIT_COMPLETE." + resultId;
1122             IntentFilter intentFilter = new IntentFilter();
1123             intentFilter.addAction(action);
1124             mContext.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
1125 
1126             Intent intent = new Intent(action);
1127             intent.setPackage(mContext.getPackageName());
1128             PendingIntent sender = PendingIntent.getBroadcast(mContext, resultId, intent,
1129                     PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
1130                             | PendingIntent.FLAG_MUTABLE);
1131 
1132             session.commit(sender.getIntentSender());
1133 
1134             Intent commitResult = result.get();
1135             final int status = commitResult.getIntExtra(PackageInstaller.EXTRA_STATUS,
1136                     PackageInstaller.STATUS_FAILURE);
1137             assertEquals(commitResult.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + " OR "
1138                             + commitResult.getExtras().get(Intent.EXTRA_INTENT),
1139                     PackageInstaller.STATUS_SUCCESS, status);
1140         }
1141     }
1142 
1143     @RequiresApi(31)
1144     static class InstallerApi31 extends InstallerApi29 {
1145         InstallerApi31(Context context) {
1146             super(context);
1147         }
1148 
1149         private static final android.content.pm.Checksum[] TEST_FIXED_APK_DIGESTS =
1150                 new android.content.pm.Checksum[]{
1151                         new android.content.pm.Checksum(
1152                                 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
1153                                 hexStringToBytes(TEST_FIXED_APK_V2_SHA256)),
1154                         new android.content.pm.Checksum(
1155                                 android.content.pm.Checksum.TYPE_WHOLE_SHA256,
1156                                 hexStringToBytes(TEST_FIXED_APK_SHA256)),
1157                         new android.content.pm.Checksum(android.content.pm.Checksum.TYPE_WHOLE_MD5,
1158                                 hexStringToBytes(TEST_FIXED_APK_MD5))};
1159 
1160         static android.content.pm.@NonNull Checksum readFromStream(@NonNull DataInputStream dis)
1161                 throws IOException {
1162             final int type = dis.readInt();
1163 
1164             final byte[] valueBytes = new byte[dis.readInt()];
1165             dis.read(valueBytes);
1166             return new android.content.pm.Checksum(type, valueBytes);
1167         }
1168 
1169         private static void writeToStream(@NonNull DataOutputStream dos,
1170                 android.content.pm.@NonNull Checksum checksum) throws IOException {
1171             dos.writeInt(checksum.getType());
1172 
1173             final byte[] valueBytes = checksum.getValue();
1174             dos.writeInt(valueBytes.length);
1175             dos.write(valueBytes);
1176         }
1177 
1178         static void checkStoredChecksums(String fileName) throws Exception {
1179             android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS;
1180             // Read checksums from file and confirm they are the same as hardcoded.
1181             ArrayList<android.content.pm.Checksum> storedChecksumsList = new ArrayList<>();
1182             try (InputStream is = getResourceAsStream(fileName);
1183                  DataInputStream dis = new DataInputStream(is)) {
1184                 for (int i = 0; i < 100; ++i) {
1185                     try {
1186                         storedChecksumsList.add(readFromStream(dis));
1187                     } catch (EOFException e) {
1188                         break;
1189                     }
1190                 }
1191             }
1192             final android.content.pm.Checksum[] storedChecksums = storedChecksumsList.toArray(
1193                     new android.content.pm.Checksum[storedChecksumsList.size()]);
1194 
1195             final String message = fileName + " needs to be updated: ";
1196             Assert.assertEquals(message, storedChecksums.length, checksums.length);
1197             for (int i = 0, size = storedChecksums.length; i < size; ++i) {
1198                 Assert.assertEquals(message, storedChecksums[i].getType(), checksums[i].getType());
1199                 Assert.assertArrayEquals(message, storedChecksums[i].getValue(),
1200                         checksums[i].getValue());
1201             }
1202         }
1203 
1204         static void checkWrittenChecksums(String fileName) throws Exception {
1205             android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS;
1206             // Write checksums and confirm that the file stays the same.
1207             try (ByteArrayOutputStream os = new ByteArrayOutputStream();
1208                  DataOutputStream dos = new DataOutputStream(os)) {
1209                 for (android.content.pm.Checksum checksum : checksums) {
1210                     writeToStream(dos, checksum);
1211                 }
1212                 final byte[] fileBytes = readAllBytes(getResourceAsStream(fileName));
1213                 final byte[] localBytes = os.toByteArray();
1214                 Assert.assertArrayEquals(fileBytes, localBytes);
1215             }
1216         }
1217 
1218         static List<Certificate> convertSignaturesToCertificates(Signature[] signatures)
1219                 throws Exception {
1220             final CertificateFactory cf = CertificateFactory.getInstance("X.509");
1221             ArrayList<Certificate> certs = new ArrayList<>(signatures.length);
1222             for (Signature signature : signatures) {
1223                 try (InputStream is = new ByteArrayInputStream(signature.toByteArray())) {
1224                     final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
1225                     certs.add(cert);
1226                 }
1227             }
1228             return certs;
1229         }
1230 
1231         static byte[] readSignature() throws IOException {
1232             return readAllBytes(getResourceAsStream(TEST_FIXED_APK_DIGESTS_SIGNATURE));
1233         }
1234 
1235         static Certificate readCertificate() throws Exception {
1236             try (InputStream is = getResourceAsStream(TEST_CERTIFICATE)) {
1237                 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
1238                 return certFactory.generateCertificate(is);
1239             }
1240         }
1241 
1242         @SuppressWarnings("deprecation")
1243         static Certificate getInstallerCertificate(Context context) throws Exception {
1244             PackageManager pm = context.getPackageManager();
1245             PackageInfo installerPackageInfo = pm.getPackageInfo(INSTALLER_PACKAGE_NAME,
1246                     GET_SIGNING_CERTIFICATES);
1247             final List<Certificate> signatures = convertSignaturesToCertificates(
1248                     installerPackageInfo.signingInfo.getApkContentsSigners());
1249             return signatures.get(0);
1250         }
1251 
1252         void installApkWithChecksums(String apk, byte[] signature) throws Exception {
1253             android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS;
1254             getUiAutomation().adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES);
1255             try {
1256                 final PackageInstaller installer =
1257                         mContext.getPackageManager().getPackageInstaller();
1258                 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
1259                         PackageInstaller.SessionParams.MODE_FULL_INSTALL);
1260 
1261                 final int sessionId = installer.createSession(params);
1262                 PackageInstaller.Session session = installer.openSession(sessionId);
1263                 writeFileToSession(session, "file", apk);
1264                 session.setChecksums("file", Arrays.asList(checksums), signature);
1265 
1266                 commitSession(session);
1267             } finally {
1268                 getUiAutomation().dropShellPermissionIdentity();
1269             }
1270         }
1271     }
1272 }
1273