Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92e31e38 authored by Samiul Islam's avatar Samiul Islam
Browse files

Add support for parsing multiple signer for Static Libraries

We have cts that uses mulitple signes for static shared libraries.

Bug: 372862145
Test: atest StaticSharedLibsHostTests
Test: atest ApkLiteParseUtilsTest
FLAG: android.content.pm.sdk_dependency_installer
Change-Id: I901a86b20d9d74318f364f4b8666c933d2321f4c
parent 3b702144
Loading
Loading
Loading
Loading
+53 −13
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.EmptyArray;
import android.util.Pair;
import android.util.Slog;

@@ -565,10 +566,7 @@ public class ApkLiteParseUtils {
                                    usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor,
                                    /*allowDuplicates=*/ true);

                            // We allow ":" delimiters in the SHA declaration as this is the format
                            // emitted by the certtool making it easy for developers to copy/paste.
                            // TODO(372862145): Add test for this replacement
                            usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase();
                            usesSdkCertDigest = normalizeCertDigest(usesSdkCertDigest);

                            if ("".equals(usesSdkCertDigest)) {
                                // Test-only uses-sdk-library empty certificate digest override.
@@ -618,18 +616,23 @@ public class ApkLiteParseUtils {
                                    usesStaticLibrariesVersions, usesStaticLibVersion,
                                    /*allowDuplicates=*/ true);

                            // We allow ":" delimiters in the SHA declaration as this is the format
                            // emitted by the certtool making it easy for developers to copy/paste.
                            // TODO(372862145): Add test for this replacement
                            usesStaticLibCertDigest =
                                    usesStaticLibCertDigest.replace(":", "").toLowerCase();
                            usesStaticLibCertDigest = normalizeCertDigest(usesStaticLibCertDigest);

                            ParseResult<String[]> certResult =
                                    parseAdditionalCertificates(input, parser);
                            if (certResult.isError()) {
                                return input.error(certResult);
                            }
                            String[] additionalCertSha256Digests = certResult.getResult();
                            String[] certSha256Digests =
                                    new String[additionalCertSha256Digests.length + 1];
                            certSha256Digests[0] = usesStaticLibCertDigest;
                            System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
                                    1, additionalCertSha256Digests.length);

                            // TODO(372862145): Add support for multiple signer for app targeting
                            //  O-MR1
                            usesStaticLibrariesCertDigests = ArrayUtils.appendElement(
                                    String[].class, usesStaticLibrariesCertDigests,
                                    new String[]{usesStaticLibCertDigest},
                                    /*allowDuplicates=*/ true);
                                    certSha256Digests, /*allowDuplicates=*/ true);
                            break;
                        case TAG_SDK_LIBRARY:
                            isSdkLibrary = true;
@@ -809,6 +812,43 @@ public class ApkLiteParseUtils {
                        declaredLibraries));
    }

    private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
            XmlResourceParser parser) throws XmlPullParserException, IOException {
        String[] certSha256Digests = EmptyArray.STRING;
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            final String nodeName = parser.getName();
            if (nodeName.equals("additional-certificate")) {
                String certSha256Digest = parser.getAttributeValue(
                        ANDROID_RES_NAMESPACE, "certDigest");
                if (TextUtils.isEmpty(certSha256Digest)) {
                    return input.error("Bad additional-certificate declaration with empty"
                            + " certDigest:" + certSha256Digest);
                }

                certSha256Digest = normalizeCertDigest(certSha256Digest);
                certSha256Digests = ArrayUtils.appendElement(String.class,
                        certSha256Digests, certSha256Digest);
            }
        }

        return input.success(certSha256Digests);
    }

    /**
     * We allow ":" delimiters in the SHA declaration as this is the format emitted by the
     * certtool making it easy for developers to copy/paste.
     */
    private static String normalizeCertDigest(String certDigest) {
        return certDigest.replace(":", "").toLowerCase();
    }

    private static boolean isDeviceAdminReceiver(
            XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)
            throws XmlPullParserException, IOException {
+1 −0
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ android_test {
        ":HelloWorldUsingSdk1And2",
        ":HelloWorldUsingSdkMalformedNegativeVersion",
        ":CtsStaticSharedLibConsumerApp1",
        ":CtsStaticSharedLibConsumerApp3",
    ],
}

+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@
            value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
        <option name="push-file" key="CtsStaticSharedLibConsumerApp1.apk"
            value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/>
        <option name="push-file" key="CtsStaticSharedLibConsumerApp3.apk"
            value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp3.apk"/>
    </target_preparer>

    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+33 −17
Original line number Diff line number Diff line
@@ -72,6 +72,12 @@ public class ApkLiteParseUtilsTest {
    private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
            "HelloWorldUsingSdkMalformedNegativeVersion.apk";
    private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk";
    private static final String TEST_APP_USING_STATIC_LIB_TWO_CERTS =
            "CtsStaticSharedLibConsumerApp3.apk";
    private static final String STATIC_LIB_CERT_1 =
            "70fbd440503ec0bf41f3f21fcc83ffd39880133c27deb0945ed677c6f31d72fb";
    private static final String STATIC_LIB_CERT_2 =
            "e49582ff3a0aa4c5589fc5feaac6b7d6e757199dd0c6742df7bf37c2ffef95f5";
    private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
    private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
    private static final String TEST_SDK1_NAME = "com.test.sdk1";
@@ -108,9 +114,8 @@ public class ApkLiteParseUtilsTest {
        assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly(
                TEST_SDK1_VERSION, TEST_SDK2_VERSION
        );
        for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) {
            assertThat(certDigests).asList().containsExactly("");
        }
        String[][] expectedCerts = {{""}, {""}};
        assertThat(baseApk.getUsesSdkLibrariesCertDigests()).isEqualTo(expectedCerts);
    }

    @SuppressLint("CheckResult")
@@ -126,18 +131,13 @@ public class ApkLiteParseUtilsTest {
        ApkLite baseApk = result.getResult();

        String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
        assertThat(liteCerts).isNotNull();
        for (String[] certDigests: liteCerts) {
            assertThat(certDigests).asList().containsExactly(certDigest);
        }
        String[][] expectedCerts = {{certDigest}, {certDigest}};
        assertThat(liteCerts).isEqualTo(expectedCerts);

        // Same for package parser
        AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
        String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
        assertThat(pkgCerts).isNotNull();
        for (int i = 0; i < liteCerts.length; i++) {
            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
        }
        assertThat(liteCerts).isEqualTo(pkgCerts);
    }


@@ -160,9 +160,7 @@ public class ApkLiteParseUtilsTest {

        String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
        String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
        for (int i = 0; i < liteCerts.length; i++) {
            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
        }
        assertThat(liteCerts).isEqualTo(pkgCerts);
    }

    @SuppressLint("CheckResult")
@@ -184,9 +182,27 @@ public class ApkLiteParseUtilsTest {

        String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
        String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
        for (int i = 0; i < liteCerts.length; i++) {
            assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
        assertThat(liteCerts).isEqualTo(pkgCerts);
    }

    @Test
    public void testParseApkLite_getUsesStaticLibrary_twoCerts()
            throws Exception {
        File apkFile = copyApkToTmpDir(TEST_APP_USING_STATIC_LIB_TWO_CERTS);
        ParseResult<ApkLite> result = ApkLiteParseUtils
                .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
        assertThat(result.isError()).isFalse();
        ApkLite baseApk = result.getResult();

        // There are two certs.
        String[][] expectedCerts = {{STATIC_LIB_CERT_1, STATIC_LIB_CERT_2}};
        String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
        assertThat(liteCerts).isEqualTo(expectedCerts);

        // And they are same as package parser.
        AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
        String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
        assertThat(liteCerts).isEqualTo(pkgCerts);
    }

    @SuppressLint("CheckResult")