blob: 086c607bae8bae7eb7d2efa2f3e139715ede8add [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm.cts;
import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
import static android.content.pm.PackageManager.TRUST_ALL;
import static android.content.pm.PackageManager.TRUST_NONE;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
import android.content.pm.DataLoaderParams;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.Session;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.pm.cts.util.AbandonAllPackageSessionsRule;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.platform.test.annotations.AppModeFull;
import android.util.ExceptionUtils;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.HexDump;
import com.android.server.pm.ApkChecksums;
import com.android.server.pm.PackageManagerShellCommandDataLoader;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import com.android.compatibility.common.util.CpuFeatures;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
@RunWith(AndroidJUnit4.class)
@AppModeFull
public class ChecksumsTest {
private static final String CTS_PACKAGE_NAME = "android.content.cts";
private static final String V2V3_PACKAGE_NAME = "android.content.cts";
private static final String V4_PACKAGE_NAME = "com.example.helloworld";
private static final String FIXED_PACKAGE_NAME = "android.appsecurity.cts.tinyapp";
private static final String FIXED_FSVERITY_PACKAGE_NAME =
"android.appsecurity.cts.apkveritytestapp";
private static final String TEST_APK_PATH = "/data/local/tmp/cts/content/";
private static final String TEST_V4_APK = "HelloWorld5.apk";
private static final String TEST_V4_SPLIT0 = "HelloWorld5_hdpi-v4.apk";
private static final String TEST_V4_SPLIT1 = "HelloWorld5_mdpi-v4.apk";
private static final String TEST_V4_SPLIT2 = "HelloWorld5_xhdpi-v4.apk";
private static final String TEST_V4_SPLIT3 = "HelloWorld5_xxhdpi-v4.apk";
private static final String TEST_V4_SPLIT4 = "HelloWorld5_xxxhdpi-v4.apk";
private static final String TEST_FIXED_APK = "CtsPkgInstallTinyAppV2V3V4.apk";
private static final String TEST_FIXED_APK_DIGESTS_FILE =
"CtsPkgInstallTinyAppV2V3V4.digests";
private static final String TEST_FIXED_APK_DIGESTS_SIGNATURE =
"CtsPkgInstallTinyAppV2V3V4.digests.signature";
private static final String TEST_CERTIFICATE = "test-cert.x509.pem";
private static final String TEST_FIXED_APK_V1 = "CtsPkgInstallTinyAppV1.apk";
private static final String TEST_FIXED_APK_SHA512 =
"CtsPkgInstallTinyAppV2V3V4-Sha512withEC.apk";
private static final String TEST_FIXED_APK_VERITY = "CtsPkgInstallTinyAppV2V3V4-Verity.apk";
private static final String TEST_FIXED_APK_FSVERITY = "CtsApkVerityTestAppPrebuilt.apk";
private static final String TEST_FIXED_APK_FSVERITY_FSVSIG =
"CtsApkVerityTestAppPrebuilt.apk.fsv_sig";
private static final String TEST_FIXED_APK_FSVERITY_SHA256_ARM64 =
"84c9974e74258a9c5abbc8a797358fe8ebf8918edf62383ea621e9e9e6461864";
private static final String TEST_FIXED_APK_FSVERITY_SHA256_X86_64 =
"5073070ee9e9a7821b4044b457d95a2bb81f349ba32d31acd0952e4a2617075a";
private static final String TEST_FIXED_APK_V2_SHA256 =
"1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3";
private static final String TEST_FIXED_APK_SHA256 =
"91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef";
private static final String TEST_FIXED_APK_MD5 = "c19868da017dc01467169f8ea7c5bc57";
private static final Checksum[] TEST_FIXED_APK_DIGESTS = new Checksum[]{
new Checksum(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
hexStringToBytes(TEST_FIXED_APK_V2_SHA256)),
new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(TEST_FIXED_APK_SHA256)),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes(TEST_FIXED_APK_MD5))};
private static final Checksum[] TEST_FIXED_APK_WRONG_DIGESTS = new Checksum[]{
new Checksum(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, hexStringToBytes("850597c6a37bbeb3")),
new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(TEST_FIXED_APK_SHA256)),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes(TEST_FIXED_APK_MD5))};
/** Default is to not use fs-verity since it depends on kernel support. */
private static final int FSVERITY_DISABLED = 0;
/** Standard fs-verity. */
private static final int FSVERITY_ENABLED = 2;
private static final byte[] NO_SIGNATURE = null;
private static final int ALL_CHECKSUMS =
TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256
| TYPE_WHOLE_SHA512
| TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
private static UiAutomation getUiAutomation() {
return InstrumentationRegistry.getInstrumentation().getUiAutomation();
}
private static Context getContext() {
return InstrumentationRegistry.getContext();
}
private static PackageManager getPackageManager() {
return getContext().getPackageManager();
}
private static PackageInstaller getPackageInstaller() {
return getPackageManager().getPackageInstaller();
}
@Rule
public AbandonAllPackageSessionsRule mAbandonSessionsRule = new AbandonAllPackageSessionsRule();
@Before
public void onBefore() throws Exception {
uninstallPackageSilently(V4_PACKAGE_NAME);
assertFalse(isAppInstalled(V4_PACKAGE_NAME));
uninstallPackageSilently(FIXED_PACKAGE_NAME);
assertFalse(isAppInstalled(FIXED_PACKAGE_NAME));
uninstallPackageSilently(FIXED_FSVERITY_PACKAGE_NAME);
assertFalse(isAppInstalled(FIXED_FSVERITY_PACKAGE_NAME));
}
@After
public void onAfter() throws Exception {
uninstallPackageSilently(V4_PACKAGE_NAME);
assertFalse(isAppInstalled(V4_PACKAGE_NAME));
uninstallPackageSilently(FIXED_PACKAGE_NAME);
assertFalse(isAppInstalled(FIXED_PACKAGE_NAME));
uninstallPackageSilently(FIXED_FSVERITY_PACKAGE_NAME);
assertFalse(isAppInstalled(FIXED_FSVERITY_PACKAGE_NAME));
}
@Test
public void testNameNotFound() throws Exception {
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
assertThrows(PackageManager.NameNotFoundException.class,
() -> pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE, receiver));
}
@Test
public void testReadWriteChecksums() throws Exception {
// Read checksums from file and confirm they are the same as hardcoded.
checkStoredChecksums(TEST_FIXED_APK_DIGESTS, TEST_FIXED_APK_DIGESTS_FILE);
// Write checksums and confirm that the file stays the same.
try (ByteArrayOutputStream os = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(os)) {
for (Checksum checksum : TEST_FIXED_APK_DIGESTS) {
Checksum.writeToStream(dos, checksum);
}
final byte[] fileBytes = Files.readAllBytes(
Paths.get(createApkPath(TEST_FIXED_APK_DIGESTS_FILE)));
final byte[] localBytes = os.toByteArray();
Assert.assertArrayEquals(fileBytes, localBytes);
}
}
@Test
public void testDefaultChecksums() throws Exception {
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V2V3_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
}
@Test
public void testSplitsDefaultChecksums() throws Exception {
installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2,
TEST_V4_SPLIT3, TEST_V4_SPLIT4});
assertTrue(isAppInstalled(V4_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 6);
// v2/v3 signature use 1M merkle tree.
assertEquals(checksums[0].getSplitName(), null);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(checksums[1].getSplitName(), "config.hdpi");
assertEquals(checksums[1].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(checksums[2].getSplitName(), "config.mdpi");
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(checksums[3].getSplitName(), "config.xhdpi");
assertEquals(checksums[3].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(checksums[4].getSplitName(), "config.xxhdpi");
assertEquals(checksums[4].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(checksums[5].getSplitName(), "config.xxxhdpi");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
}
@Test
public void testFixedDefaultChecksums() throws Exception {
installPackage(TEST_FIXED_APK);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
// v2/v3 signature use 1M merkle tree.
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testFixedV1DefaultChecksums() throws Exception {
installPackage(TEST_FIXED_APK_V1);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 0);
}
@Test
public void testFixedSha512DefaultChecksums() throws Exception {
installPackage(TEST_FIXED_APK_SHA512);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
// v2/v3 signature use 1M merkle tree.
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
assertEquals(bytesToHexString(checksums[0].getValue()),
"6b866e8a54a3e358dfc20007960fb96123845f6c6d6c45f5fddf88150d71677f"
+ "4c3081a58921c88651f7376118aca312cf764b391cdfb8a18c6710f9f27916a0");
assertNull(checksums[0].getInstallerCertificate());
}
@LargeTest
@Test
public void testFixedFSVerityDefaultChecksums() throws Exception {
Assume.assumeTrue(isApkVerityEnabled());
installApkWithFSVerity(TEST_FIXED_APK_FSVERITY, TEST_FIXED_APK_FSVERITY_FSVSIG);
assertTrue(isAppInstalled(FIXED_FSVERITY_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_FSVERITY_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 2);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
if (CpuFeatures.isArm64Cpu() || CpuFeatures.isArmCpu()) {
assertEquals(bytesToHexString(checksums[0].getValue()),
TEST_FIXED_APK_FSVERITY_SHA256_ARM64);
assertEquals(bytesToHexString(checksums[1].getValue()),
"8c61bc2548521aa0005276af68e42253957e1e24c122f7d8bf10f1832d4014e5");
} else if (CpuFeatures.isX86_64Cpu() || CpuFeatures.isX86Cpu()) {
assertEquals(bytesToHexString(checksums[0].getValue()),
TEST_FIXED_APK_FSVERITY_SHA256_X86_64);
assertEquals(bytesToHexString(checksums[1].getValue()),
"6f7cfa569c4a25d7241e26c1c8ff274badbdefd7854d91b842b1a97a985d5917");
} else {
Assert.fail("Unsupported CPU ABI");
}
assertEquals(checksums[1].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
}
@LargeTest
@Test
public void testFixedFSVerityDefaultChecksumsIncremental() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installFilesIncrementally(
new String[]{TEST_FIXED_APK_FSVERITY, TEST_FIXED_APK_FSVERITY_FSVSIG});
assertTrue(isAppInstalled(FIXED_FSVERITY_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_FSVERITY_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
if (CpuFeatures.isArm64Cpu() || CpuFeatures.isArmCpu()) {
assertEquals(bytesToHexString(checksums[0].getValue()),
TEST_FIXED_APK_FSVERITY_SHA256_ARM64);
} else if (CpuFeatures.isX86_64Cpu() || CpuFeatures.isX86Cpu()) {
assertEquals(bytesToHexString(checksums[0].getValue()),
TEST_FIXED_APK_FSVERITY_SHA256_X86_64);
} else {
Assert.fail("Unsupported CPU ABI");
}
}
@Test
public void testFixedVerityDefaultChecksums() throws Exception {
installPackage(TEST_FIXED_APK_VERITY);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
// No usable hashes as verity-in-v2-signature does not cover the whole file.
assertEquals(checksums.length, 0);
}
@LargeTest
@Test
public void testFixedAllChecksums() throws Exception {
installPackage(TEST_FIXED_APK);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 7);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()),
"759626c33083fbf43215cb5b17156977d963d4c6850c0cb4e73162a665db560b");
assertEquals(checksums[1].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA1);
assertEquals(bytesToHexString(checksums[2].getValue()),
"331eef6bc57671de28cbd7e32089d047285ade6a");
assertEquals(checksums[3].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA512);
assertEquals(bytesToHexString(checksums[4].getValue()),
"b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571"
+ "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256);
assertEquals(checksums[6].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
assertEquals(bytesToHexString(checksums[6].getValue()),
"ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84"
+ "5f2888afcb71524196dda0d6dd16a6a3292bb75b431b8ff74fb60d796e882f80");
}
@LargeTest
@Test
public void testFixedV1AllChecksums() throws Exception {
installPackage(TEST_FIXED_APK_V1);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 5);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()),
"0b9bd6ef683e0c4e8940aba6460382b33e607c0fcf487f3dc6a44b715615d166");
assertEquals(checksums[1].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[1].getValue()), "78e51e8c51e4adc6870cd71389e0f3db");
assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA1);
assertEquals(bytesToHexString(checksums[2].getValue()),
"f6654505f2274fd9bfc098b660cdfdc2e4da6d53");
assertEquals(checksums[3].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[3].getValue()),
"43755d36ec944494f6275ee92662aca95079b3aa6639f2d35208c5af15adff78");
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA512);
assertEquals(bytesToHexString(checksums[4].getValue()),
"030fc815a4957c163af2bc6f30dd5b48ac09c94c25a824a514609e1476f91421"
+ "e2c8b6baa16ef54014ad6c5b90c37b26b0f5c8aeb01b63a1db2eca133091c8d1");
}
@Test
public void testDefaultIncrementalChecksums() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installPackageIncrementally(TEST_V4_APK);
assertTrue(isAppInstalled(V4_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
}
@Test
public void testFixedDefaultIncrementalChecksums() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installPackageIncrementally(TEST_FIXED_APK);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()),
"759626c33083fbf43215cb5b17156977d963d4c6850c0cb4e73162a665db560b");
}
@LargeTest
@Test
public void testFixedAllIncrementalChecksums() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installPackageIncrementally(TEST_FIXED_APK);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 7);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MERKLE_ROOT_4K_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()),
"759626c33083fbf43215cb5b17156977d963d4c6850c0cb4e73162a665db560b");
assertEquals(checksums[1].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA1);
assertEquals(bytesToHexString(checksums[2].getValue()),
"331eef6bc57671de28cbd7e32089d047285ade6a");
assertEquals(checksums[3].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA512);
assertEquals(bytesToHexString(checksums[4].getValue()),
"b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571"
+ "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256);
assertEquals(checksums[6].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
assertEquals(bytesToHexString(checksums[6].getValue()),
"ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84"
+ "5f2888afcb71524196dda0d6dd16a6a3292bb75b431b8ff74fb60d796e882f80");
}
@Test
public void testInstallerChecksumsTrustNone() throws Exception {
installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerPackageName());
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testInstallerWrongChecksumsTrustAll() throws Exception {
installApkWithChecksums(TEST_FIXED_APK_WRONG_DIGESTS);
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerPackageName());
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testInstallerSignedChecksumsInvalidSignature() throws Exception {
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "file", TEST_FIXED_APK);
try {
session.setChecksums("file", Arrays.asList(TEST_FIXED_APK_DIGESTS),
hexStringToBytes("1eec9e86"));
Assert.fail("setChecksums should throw exception.");
} catch (IllegalArgumentException e) {
// expected
}
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
}
@Test
public void testInstallerSignedChecksumsTrustNone() throws Exception {
final byte[] signature = readSignature();
CommitIntentReceiver.checkSuccess(
installApkWithChecksums(TEST_FIXED_APK, "file", "file", TEST_FIXED_APK_DIGESTS,
signature));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerPackageName());
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testInstallerSignedChecksumsTrustAll() throws Exception {
final byte[] signature = readSignature();
final Certificate certificate = readCertificate();
CommitIntentReceiver.checkSuccess(
installApkWithChecksums(TEST_FIXED_APK, "file", "file", TEST_FIXED_APK_DIGESTS,
signature));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
// v2/v3+installer provided.
assertEquals(checksums.length, 3);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), certificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), certificate);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
assertEquals(checksums[2].getSplitName(), null);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
@Test
public void testInstallerChecksumsTrustAll() throws Exception {
installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
final Certificate installerCertificate = getInstallerCertificate();
LocalListener receiver = new LocalListener();
getPackageManager().requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
// v2/v3+installer provided.
assertEquals(checksums.length, 3);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
assertEquals(checksums[2].getSplitName(), null);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
@Test
public void testInstallerChecksumsTrustInstaller() throws Exception {
installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
// Using the installer's certificate(s).
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(CTS_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(GET_SIGNING_CERTIFICATES));
final List<Certificate> signatures = convertSignaturesToCertificates(
packageInfo.signingInfo.getApkContentsSigners());
LocalListener receiver = new LocalListener();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, signatures, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 3);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), signatures.get(0));
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), signatures.get(0));
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
assertEquals(checksums[2].getSplitName(), null);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
@LargeTest
@Test
public void testInstallerFileChecksumsDuringInstall() throws Exception {
Checksum[] digestsBase = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("dd93e23bb8cdab0382fdca0d21a4f1cb"))};
Checksum[] digestsSplit0 = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("f6430e1b795ce2658c49e68d15316b2d"))};
final Certificate installerCertificate = getInstallerCertificate();
getUiAutomation().adoptShellPermissionIdentity();
PackageInstaller installer = null;
int sessionId = -1;
try {
installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
sessionId = installer.createSession(params);
final Session session = installer.openSession(sessionId);
writeFileToSession(session, "hw5.apk", TEST_V4_APK);
session.setChecksums("hw5.apk", Arrays.asList(digestsBase), NO_SIGNATURE);
writeFileToSession(session, "hw5_split0.apk", TEST_V4_SPLIT0);
session.setChecksums("hw5_split0.apk", Arrays.asList(digestsSplit0), NO_SIGNATURE);
// Workaround to emulate .digests file present in installation.
writeChecksumsToSession(session, "hw5.digests", digestsBase);
writeChecksumsToSession(session, "hw5_split0.digests", digestsSplit0);
File dataApp = Environment.getDataAppDirectory(null);
{
LocalListener receiver = new LocalListener();
session.requestChecksums("hw5.apk", 0, TRUST_ALL, getContext().getMainExecutor(),
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 3);
// base
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"dd93e23bb8cdab0382fdca0d21a4f1cb");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
{
LocalListener receiver = new LocalListener();
session.requestChecksums("hw5_split0.apk", 0, TRUST_ALL,
getContext().getMainExecutor(), receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 3);
// split0
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"f6430e1b795ce2658c49e68d15316b2d");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
} finally {
installer.abandonSession(sessionId);
getUiAutomation().dropShellPermissionIdentity();
}
}
@Test
public void testInstallerChecksumsTrustWrongInstaller() throws Exception {
installApkWithChecksums(TEST_FIXED_APK_DIGESTS);
// Using certificates from a security app, not the installer (us).
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(FIXED_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(GET_SIGNING_CERTIFICATES));
final List<Certificate> signatures = convertSignaturesToCertificates(
packageInfo.signingInfo.getApkContentsSigners());
LocalListener receiver = new LocalListener();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, signatures, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerPackageName());
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testInstallerChecksumsTrustAllWrongName() throws Exception {
// NB: "Invalid checksum name(s):" is used in Play to report checksum related failures.
// Please consult with them before changing.
CommitIntentReceiver.checkFailure(
installApkWithChecksums(TEST_FIXED_APK, "apk", "wrong_name",
TEST_FIXED_APK_DIGESTS),
"INSTALL_FAILED_SESSION_INVALID: Invalid checksum name(s): wrong_name");
}
@Test
public void testInstallerChecksumsUpdate() throws Exception {
Checksum[] digestsBase = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("dd93e23bb8cdab0382fdca0d21a4f1cb"))};
Checksum[] digestsSplit0 = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("f6430e1b795ce2658c49e68d15316b2d"))};
Checksum[] digestsSplit1 = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("d1f4b00d034994663e84f907fe4bb664"))};
final Certificate installerCertificate = getInstallerCertificate();
// Original package checksums: base + split0.
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "hw5", TEST_V4_APK);
session.setChecksums("hw5", Arrays.asList(digestsBase), NO_SIGNATURE);
writeFileToSession(session, "hw5_split0", TEST_V4_SPLIT0);
session.setChecksums("hw5_split0", Arrays.asList(digestsSplit0), NO_SIGNATURE);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
{
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 6);
// base
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"dd93e23bb8cdab0382fdca0d21a4f1cb");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
// split0
assertEquals(checksums[3].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[3].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[3].getValue()),
"f6430e1b795ce2658c49e68d15316b2d");
assertEquals(checksums[3].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[3].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[4].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[4].getValue()),
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
assertEquals(checksums[4].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[4].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[5].getSplitName(), "config.hdpi");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[5].getInstallerPackageName());
assertNull(checksums[5].getInstallerCertificate());
}
// Update the package with one split+checksums and another split without checksums.
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
params.setAppPackageName(V4_PACKAGE_NAME);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "hw5_split1", TEST_V4_SPLIT1);
session.setChecksums("hw5_split1", Arrays.asList(digestsSplit1), NO_SIGNATURE);
writeFileToSession(session, "hw5_split2", TEST_V4_SPLIT2);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
{
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 10);
// base
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"dd93e23bb8cdab0382fdca0d21a4f1cb");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
// split0
assertEquals(checksums[3].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[3].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[3].getValue()),
"f6430e1b795ce2658c49e68d15316b2d");
assertEquals(checksums[3].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[3].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[4].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[4].getValue()),
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
assertEquals(checksums[4].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[4].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[5].getSplitName(), "config.hdpi");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[5].getInstallerPackageName());
assertNull(checksums[5].getInstallerCertificate());
// split1
assertEquals(checksums[6].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[6].getSplitName(), "config.mdpi");
assertEquals(bytesToHexString(checksums[6].getValue()),
"d1f4b00d034994663e84f907fe4bb664");
assertEquals(checksums[6].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[6].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[7].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[7].getSplitName(), "config.mdpi");
assertEquals(bytesToHexString(checksums[7].getValue()),
"f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf");
assertEquals(checksums[7].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[7].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[8].getSplitName(), "config.mdpi");
assertEquals(checksums[8].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[8].getInstallerPackageName());
assertNull(checksums[8].getInstallerCertificate());
// split2
assertEquals(checksums[9].getSplitName(), "config.xhdpi");
assertEquals(checksums[9].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[9].getInstallerPackageName());
assertNull(checksums[9].getInstallerCertificate());
}
}
@Test
public void testInstallerSignedChecksumsUpdate() throws Exception {
Checksum[] digestsBase = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("dd93e23bb8cdab0382fdca0d21a4f1cb"))};
Checksum[] digestsSplit0 = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("f6430e1b795ce2658c49e68d15316b2d"))};
Checksum[] digestsSplit1 = new Checksum[]{new Checksum(TYPE_WHOLE_SHA256, hexStringToBytes(
"f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf")),
new Checksum(TYPE_WHOLE_MD5, hexStringToBytes("d1f4b00d034994663e84f907fe4bb664"))};
String digestBaseFile = ApkChecksums.buildDigestsPathForApk(TEST_V4_APK);
String digestSplit0File = ApkChecksums.buildDigestsPathForApk(TEST_V4_SPLIT0);
String digestSplit1File = ApkChecksums.buildDigestsPathForApk(TEST_V4_SPLIT1);
checkStoredChecksums(digestsBase, digestBaseFile);
checkStoredChecksums(digestsSplit0, digestSplit0File);
checkStoredChecksums(digestsSplit1, digestSplit1File);
byte[] digestBaseSignature = readSignature(
ApkChecksums.buildSignaturePathForDigests(digestBaseFile));
byte[] digestSplit0Signature = readSignature(
ApkChecksums.buildSignaturePathForDigests(digestSplit0File));
byte[] digestSplit1Signature = readSignature(
ApkChecksums.buildSignaturePathForDigests(digestSplit1File));
final Certificate certificate = readCertificate();
// Original package checksums: base + split0.
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "hw5", TEST_V4_APK);
session.setChecksums("hw5", Arrays.asList(digestsBase), digestBaseSignature);
writeFileToSession(session, "hw5_split0", TEST_V4_SPLIT0);
session.setChecksums("hw5_split0", Arrays.asList(digestsSplit0), digestSplit0Signature);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
{
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 6);
// base
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"dd93e23bb8cdab0382fdca0d21a4f1cb");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), certificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), certificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
// split0
assertEquals(checksums[3].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[3].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[3].getValue()),
"f6430e1b795ce2658c49e68d15316b2d");
assertEquals(checksums[3].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[3].getInstallerCertificate(), certificate);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[4].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[4].getValue()),
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
assertEquals(checksums[4].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[4].getInstallerCertificate(), certificate);
assertEquals(checksums[5].getSplitName(), "config.hdpi");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[5].getInstallerPackageName());
assertNull(checksums[5].getInstallerCertificate());
}
// Update the package with one split+checksums and another split without checksums.
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
params.setAppPackageName(V4_PACKAGE_NAME);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "hw5_split1", TEST_V4_SPLIT1);
session.setChecksums("hw5_split1", Arrays.asList(digestsSplit1), digestSplit1Signature);
writeFileToSession(session, "hw5_split2", TEST_V4_SPLIT2);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
{
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(V4_PACKAGE_NAME, true, 0, TRUST_ALL, receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 10);
// base
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[0].getSplitName(), null);
assertEquals(bytesToHexString(checksums[0].getValue()),
"dd93e23bb8cdab0382fdca0d21a4f1cb");
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), certificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[1].getSplitName(), null);
assertEquals(bytesToHexString(checksums[1].getValue()),
"ed8c7ae1220fe16d558e00cfc37256e6f7088ab90eb04c1bfcb39922a8a5248e");
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), certificate);
assertEquals(checksums[2].getSplitName(), null);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
// split0
assertEquals(checksums[3].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[3].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[3].getValue()),
"f6430e1b795ce2658c49e68d15316b2d");
assertEquals(checksums[3].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[3].getInstallerCertificate(), certificate);
assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[4].getSplitName(), "config.hdpi");
assertEquals(bytesToHexString(checksums[4].getValue()),
"bd9b095a49a9068498b018ce8cb7cc18d411b13a5a5f7fb417d2ff9808ae838e");
assertEquals(checksums[4].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[4].getInstallerCertificate(), certificate);
assertEquals(checksums[5].getSplitName(), "config.hdpi");
assertEquals(checksums[5].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[5].getInstallerPackageName());
assertNull(checksums[5].getInstallerCertificate());
// split1
assertEquals(checksums[6].getType(), TYPE_WHOLE_MD5);
assertEquals(checksums[6].getSplitName(), "config.mdpi");
assertEquals(bytesToHexString(checksums[6].getValue()),
"d1f4b00d034994663e84f907fe4bb664");
assertEquals(checksums[6].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[6].getInstallerCertificate(), certificate);
assertEquals(checksums[7].getType(), TYPE_WHOLE_SHA256);
assertEquals(checksums[7].getSplitName(), "config.mdpi");
assertEquals(bytesToHexString(checksums[7].getValue()),
"f16898f43990c14585a900eda345c3a236c6224f63920d69cfe8a7afbc0c0ccf");
assertEquals(checksums[7].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[7].getInstallerCertificate(), certificate);
assertEquals(checksums[8].getSplitName(), "config.mdpi");
assertEquals(checksums[8].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[8].getInstallerPackageName());
assertNull(checksums[8].getInstallerCertificate());
// split2
assertEquals(checksums[9].getSplitName(), "config.xhdpi");
assertEquals(checksums[9].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertNull(checksums[9].getInstallerPackageName());
assertNull(checksums[9].getInstallerCertificate());
}
}
@Test
public void testInstallerChecksumsIncremental() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
final Certificate installerCertificate = getInstallerCertificate();
installPackageIncrementally(TEST_FIXED_APK);
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(FIXED_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(0));
final String inPath = packageInfo.applicationInfo.getBaseCodePath();
installApkWithChecksumsIncrementally(inPath);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 3);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), installerCertificate);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
@Test
public void testInstallerSignedChecksumsIncremental() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installPackageIncrementally(TEST_FIXED_APK);
PackageInfo packageInfo = getPackageManager().getPackageInfo(FIXED_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(0));
final String inPath = packageInfo.applicationInfo.getBaseCodePath();
final byte[] signature = readSignature();
final Certificate certificate = readCertificate();
installApkWithChecksumsIncrementally(inPath, TEST_FIXED_APK, TEST_FIXED_APK_DIGESTS,
signature);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 3);
assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5);
assertEquals(checksums[0].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[0].getInstallerCertificate(), certificate);
assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256);
assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256);
assertEquals(checksums[1].getInstallerPackageName(), CTS_PACKAGE_NAME);
assertEquals(checksums[1].getInstallerCertificate(), certificate);
assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[2].getInstallerPackageName());
assertNull(checksums[2].getInstallerCertificate());
}
@Test
public void testInstallerChecksumsIncrementalTrustNone() throws Exception {
if (!checkIncrementalDeliveryFeature()) {
return;
}
installPackageIncrementally(TEST_FIXED_APK);
PackageInfo packageInfo = getPackageManager().getPackageInfo(FIXED_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(0));
final String inPath = packageInfo.applicationInfo.getBaseCodePath();
installApkWithChecksumsIncrementally(inPath);
assertTrue(isAppInstalled(FIXED_PACKAGE_NAME));
LocalListener receiver = new LocalListener();
PackageManager pm = getPackageManager();
pm.requestChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE,
receiver);
ApkChecksum[] checksums = receiver.getResult();
assertNotNull(checksums);
assertEquals(checksums.length, 1);
assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256);
assertNull(checksums[0].getInstallerPackageName());
assertNull(checksums[0].getInstallerCertificate());
}
@Test
public void testInstallerChecksumsDuplicate() throws Exception {
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "file", TEST_FIXED_APK);
session.setChecksums("file", Arrays.asList(TEST_FIXED_APK_DIGESTS), NO_SIGNATURE);
try {
session.setChecksums("file", Arrays.asList(TEST_FIXED_APK_DIGESTS), NO_SIGNATURE);
Assert.fail("setChecksums should throw exception.");
} catch (IllegalStateException e) {
// expected
}
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
}
private List<Certificate> convertSignaturesToCertificates(Signature[] signatures) {
try {
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
ArrayList<Certificate> certs = new ArrayList<>(signatures.length);
for (Signature signature : signatures) {
final InputStream is = new ByteArrayInputStream(signature.toByteArray());
final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
certs.add(cert);
}
return certs;
} catch (CertificateException e) {
throw ExceptionUtils.propagate(e);
}
}
private void installApkWithChecksums(Checksum[] checksums) throws Exception {
installApkWithChecksums("file", "file", checksums);
}
private void installApkWithChecksums(String apkName, String checksumsName, Checksum[] checksums)
throws Exception {
CommitIntentReceiver.checkSuccess(
installApkWithChecksums(TEST_FIXED_APK, apkName, checksumsName, checksums));
}
private Intent installApkWithChecksums(String apk, String apkName,
String checksumsName, Checksum[] checksums) throws Exception {
return installApkWithChecksums(apk, apkName, checksumsName, checksums, NO_SIGNATURE);
}
private Intent installApkWithChecksums(String apk, String apkName,
String checksumsName, Checksum[] checksums, byte[] signature) throws Exception {
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, apkName, apk);
session.setChecksums(checksumsName, Arrays.asList(checksums), signature);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
return receiver.getResult();
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
}
private void installApkWithFSVerity(String apk, String fsv) throws Exception {
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
writeFileToSession(session, "file", apk);
writeFileToSession(session, "file.fsv_sig", fsv);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
}
private void installApkWithChecksumsIncrementally(final String inPath) throws Exception {
installApkWithChecksumsIncrementally(inPath, TEST_FIXED_APK, TEST_FIXED_APK_DIGESTS,
NO_SIGNATURE);
}
private void installApkWithChecksumsIncrementally(final String inPath, final String apk,
final Checksum[] checksums, final byte[] signature) throws Exception {
getUiAutomation().adoptShellPermissionIdentity();
try {
final PackageInstaller installer = getPackageInstaller();
final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
params.setDataLoaderParams(DataLoaderParams.forIncremental(new ComponentName("android",
PackageManagerShellCommandDataLoader.class.getName()), ""));
final int sessionId = installer.createSession(params);
Session session = installer.openSession(sessionId);
final File file = new File(inPath);
final String name = file.getName();
final long size = file.length();
final Metadata metadata = Metadata.forLocalFile(inPath);
session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), null);
session.setChecksums(name, Arrays.asList(checksums), signature);
CommitIntentReceiver receiver = new CommitIntentReceiver();
session.commit(receiver.getIntentSender());
CommitIntentReceiver.checkSuccess(receiver.getResult());
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
}
private static String readFullStream(InputStream inputStream) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
writeFullStream(inputStream, result, -1);
return result.toString("UTF-8");
}
private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
long expected)
throws IOException {
byte[] buffer = new byte[1024];
long total = 0;
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
total += length;
}
if (expected > 0) {
Assert.assertEquals(expected, total);
}
}
private static String executeShellCommand(String command) throws IOException {
final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command);
try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
return readFullStream(inputStream);
}
}
private static String createApkPath(String baseName) {
return TEST_APK_PATH + baseName;
}
private void installPackage(String baseName) throws IOException {
File file = new File(createApkPath(baseName));
Assert.assertEquals("Success\n", executeShellCommand(
"pm install -t -g " + file.getPath()));
}
private void installPackageIncrementally(String baseName) throws IOException {
File file = new File(createApkPath(baseName));
Assert.assertEquals("Success\n", executeShellCommand(
"pm install-incremental -t -g " + file.getPath()));
}
private void installSplits(String[] baseNames) throws IOException {
String[] splits = Arrays.stream(baseNames).map(
baseName -> createApkPath(baseName)).toArray(String[]::new);
Assert.assertEquals("Success\n",
executeShellCommand("pm install -t -g " + String.join(" ", splits)));
}
private void installFilesIncrementally(String[] baseNames) throws IOException {
String[] splits = Arrays.stream(baseNames).map(
baseName -> createApkPath(baseName)).toArray(String[]::new);
Assert.assertEquals("Success\n",
executeShellCommand("pm install-incremental -t -g " + String.join(" ", splits)));
}
private static void writeFileToSession(PackageInstaller.Session session, String name,
String apk) throws IOException {
File file = new File(createApkPath(apk));
try (OutputStream os = session.openWrite(name, 0, file.length());
InputStream is = new FileInputStream(file)) {
writeFullStream(is, os, file.length());
}
}
private static void writeChecksumsToSession(PackageInstaller.Session session, String name,
Checksum[] checksums) throws IOException {
try (DataOutputStream dos = new DataOutputStream(session.openWrite(name, 0, -1))) {
for (Checksum checksum : checksums) {
Checksum.writeToStream(dos, checksum);
}
}
}
private String uninstallPackageSilently(String packageName) throws IOException {
return executeShellCommand("pm uninstall " + packageName);
}
private boolean isAppInstalled(String packageName) throws IOException {
final String commandResult = executeShellCommand("pm list packages");
final int prefixLength = "package:".length();
return Arrays.stream(commandResult.split("\\r?\\n"))
.anyMatch(line -> line.substring(prefixLength).equals(packageName));
}
private String getAppCodePath(String packageName) throws IOException {
final String commandResult = executeShellCommand("pm dump " + packageName);
final String prefix = " codePath=";
final int prefixLength = prefix.length();
return Arrays.stream(commandResult.split("\\r?\\n"))
.filter(line -> line.startsWith(prefix))
.map(line -> line.substring(prefixLength))
.findFirst().get();
}
@Nonnull
private static String bytesToHexString(byte[] bytes) {
return HexDump.toHexString(bytes, 0, bytes.length, /*upperCase=*/ false);
}
@Nonnull
private static byte[] hexStringToBytes(String hexString) {
return HexDump.hexStringToByteArray(hexString);
}
private boolean checkIncrementalDeliveryFeature() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY);
}
// From PackageManagerServiceUtils.
private static boolean isApkVerityEnabled() {
return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.R
|| SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)
== FSVERITY_ENABLED;
}
private byte[] readSignature() throws IOException {
return readSignature(TEST_FIXED_APK_DIGESTS_SIGNATURE);
}
private byte[] readSignature(String file) throws IOException {
return Files.readAllBytes(Paths.get(createApkPath(file)));
}
private Certificate readCertificate() throws Exception {
try (InputStream is = new FileInputStream(createApkPath(TEST_CERTIFICATE))) {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return certFactory.generateCertificate(is);
}
}
private Certificate getInstallerCertificate() throws Exception {
PackageManager pm = getPackageManager();
PackageInfo installerPackageInfo = pm.getPackageInfo(CTS_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(GET_SIGNING_CERTIFICATES));
final List<Certificate> signatures = convertSignaturesToCertificates(
installerPackageInfo.signingInfo.getApkContentsSigners());
return signatures.get(0);
}
private void checkStoredChecksums(Checksum[] checksums, String fileName) throws Exception {
ArrayList<Checksum> storedChecksumsList = new ArrayList<>();
try (InputStream is = new FileInputStream(createApkPath(fileName));
DataInputStream dis = new DataInputStream(is)) {
for (int i = 0; i < 100; ++i) {
try {
storedChecksumsList.add(Checksum.readFromStream(dis));
} catch (EOFException e) {
break;
}
}
}
final Checksum[] storedChecksums = storedChecksumsList.toArray(
new Checksum[storedChecksumsList.size()]);
final String message = fileName + " needs to be updated: ";
Assert.assertEquals(message, storedChecksums.length, checksums.length);
for (int i = 0, size = storedChecksums.length; i < size; ++i) {
Assert.assertEquals(message, storedChecksums[i].getType(), checksums[i].getType());
Assert.assertArrayEquals(message, storedChecksums[i].getValue(),
checksums[i].getValue());
}
}
private static class LocalListener implements PackageManager.OnChecksumsReadyListener {
private final LinkedBlockingQueue<ApkChecksum[]> mResult = new LinkedBlockingQueue<>();
@Override
public void onChecksumsReady(@NonNull List<ApkChecksum> checksumsList) {
ApkChecksum[] checksums = checksumsList.toArray(new ApkChecksum[checksumsList.size()]);
Arrays.sort(checksums, (ApkChecksum lhs, ApkChecksum rhs) -> {
final String lhsSplit = lhs.getSplitName();
final String rhsSplit = rhs.getSplitName();
if (Objects.equals(lhsSplit, rhsSplit)) {
return Integer.signum(lhs.getType() - rhs.getType());
}
if (lhsSplit == null) {
return -1;
}
if (rhsSplit == null) {
return +1;
}
return lhsSplit.compareTo(rhsSplit);
});
mResult.offer(checksums);
}
public ApkChecksum[] getResult() {
try {
return mResult.poll(6, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private static class CommitIntentReceiver {
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
public IntentSender getIntentSender() {
return new IntentSender((IIntentSender) mLocalSender);
}
public Intent getResult() {
try {
return mResult.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void checkSuccess(Intent result) {
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
assertEquals(status, PackageInstaller.STATUS_SUCCESS,
result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + " OR "
+ result.getExtras().get(Intent.EXTRA_INTENT));
}
public static void checkFailure(Intent result, int expectedStatus,
String expectedStatusMessage) {
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
assertEquals(status, expectedStatus);
assertEquals(result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE),
expectedStatusMessage);
}
public static void checkFailure(Intent result, String expectedStatusMessage) {
checkFailure(result, PackageInstaller.STATUS_FAILURE, expectedStatusMessage);
}
}
}